🔀 Merge pull request #553 from ajnart/gridstack-wip-meierschlumpf
This commit is contained in:
@@ -8,90 +8,33 @@
|
|||||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f",
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f",
|
||||||
"position": 0,
|
"position": 0,
|
||||||
"name": "Example Category"
|
"name": "Example Category"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c8407d2c-2353-4775-87c3-602f6f2684d5",
|
||||||
|
"name": "Test",
|
||||||
|
"position": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c1c4bec3-1044-4a80-957f-afe7ff49f421",
|
||||||
|
"name": "Test",
|
||||||
|
"position": 2
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"wrappers": [
|
"wrappers": [
|
||||||
{
|
{
|
||||||
"id": "default",
|
"id": "5823c4d6-6baf-4436-b990-93fe77e1dc62",
|
||||||
"position": 1
|
"position": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "943f0681-a15b-4576-9a61-a74bd6fdd3ab",
|
||||||
|
"position": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "default",
|
||||||
|
"position": 5
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"apps": [
|
"apps": [
|
||||||
{
|
|
||||||
"id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa",
|
|
||||||
"name": "Your app",
|
|
||||||
"url": "https://homarr.dev",
|
|
||||||
"appearance": {
|
|
||||||
"iconUrl": "/imgs/logo/logo.png"
|
|
||||||
},
|
|
||||||
"network": {
|
|
||||||
"enabledStatusChecker": false,
|
|
||||||
"okStatus": []
|
|
||||||
},
|
|
||||||
"behaviour": {
|
|
||||||
"isOpeningNewTab": true,
|
|
||||||
"externalUrl": ""
|
|
||||||
},
|
|
||||||
"area": {
|
|
||||||
"type": "wrapper",
|
|
||||||
"properties": {
|
|
||||||
"id": "default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shape": {
|
|
||||||
"location": {
|
|
||||||
"x": 8,
|
|
||||||
"y": 12
|
|
||||||
},
|
|
||||||
"size": {
|
|
||||||
"width": 4,
|
|
||||||
"height": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"integration": {
|
|
||||||
"type": null,
|
|
||||||
"properties": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330",
|
|
||||||
"name": "Contribute",
|
|
||||||
"url": "https://github.com/ajnart/homarr",
|
|
||||||
"behaviour": {
|
|
||||||
"onClickUrl": "https://github.com/ajnart/homarr",
|
|
||||||
"externalUrl": "https://github.com/ajnart/homarr",
|
|
||||||
"isOpeningNewTab": true
|
|
||||||
},
|
|
||||||
"network": {
|
|
||||||
"enabledStatusChecker": false,
|
|
||||||
"okStatus": [
|
|
||||||
200
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"appearance": {
|
|
||||||
"iconUrl": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/github.png"
|
|
||||||
},
|
|
||||||
"integration": {
|
|
||||||
"type": null,
|
|
||||||
"properties": []
|
|
||||||
},
|
|
||||||
"area": {
|
|
||||||
"type": "category",
|
|
||||||
"properties": {
|
|
||||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shape": {
|
|
||||||
"location": {
|
|
||||||
"x": 4,
|
|
||||||
"y": 2
|
|
||||||
},
|
|
||||||
"size": {
|
|
||||||
"width": 2,
|
|
||||||
"height": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337",
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337",
|
||||||
"name": "Discord",
|
"name": "Discord",
|
||||||
@@ -121,19 +64,41 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shape": {
|
"shape": {
|
||||||
"location": {
|
"md": {
|
||||||
"x": 0,
|
"location": {
|
||||||
"y": 4
|
"x": 2,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"size": {
|
"sm": {
|
||||||
"width": 4,
|
"location": {
|
||||||
"height": 3
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 2,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a",
|
"id": "5df743d9-5cb1-457c-85d2-64ff86855652",
|
||||||
"name": "Documentation",
|
"name": "Your app",
|
||||||
"url": "https://homarr.dev",
|
"url": "https://homarr.dev",
|
||||||
"behaviour": {
|
"behaviour": {
|
||||||
"onClickUrl": "https://homarr.dev",
|
"onClickUrl": "https://homarr.dev",
|
||||||
@@ -154,19 +119,41 @@
|
|||||||
"properties": []
|
"properties": []
|
||||||
},
|
},
|
||||||
"area": {
|
"area": {
|
||||||
"type": "wrapper",
|
"type": "category",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": "default"
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shape": {
|
"shape": {
|
||||||
"location": {
|
"md": {
|
||||||
"x": 8,
|
"location": {
|
||||||
"y": 9
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"size": {
|
"sm": {
|
||||||
"width": 4,
|
"location": {
|
||||||
"height": 3
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -186,7 +173,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"appearance": {
|
"appearance": {
|
||||||
"iconUrl": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/ko-fi.png"
|
"iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png"
|
||||||
},
|
},
|
||||||
"integration": {
|
"integration": {
|
||||||
"type": null,
|
"type": null,
|
||||||
@@ -199,13 +186,152 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shape": {
|
"shape": {
|
||||||
"location": {
|
"md": {
|
||||||
"x": 2,
|
"location": {
|
||||||
"y": 2
|
"x": 3,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"size": {
|
"sm": {
|
||||||
"width": 2,
|
"location": {
|
||||||
"height": 2
|
"x": 3,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 3,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa",
|
||||||
|
"name": "Your app",
|
||||||
|
"url": "https://homarr.dev",
|
||||||
|
"appearance": {
|
||||||
|
"iconUrl": "/imgs/logo/logo.png"
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"enabledStatusChecker": false,
|
||||||
|
"okStatus": []
|
||||||
|
},
|
||||||
|
"behaviour": {
|
||||||
|
"isOpeningNewTab": true,
|
||||||
|
"externalUrl": ""
|
||||||
|
},
|
||||||
|
"area": {
|
||||||
|
"type": "sidebar",
|
||||||
|
"properties": {
|
||||||
|
"location": "left"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shape": {
|
||||||
|
"md": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sm": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"integration": {
|
||||||
|
"type": null,
|
||||||
|
"properties": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330",
|
||||||
|
"name": "Contribute",
|
||||||
|
"url": "https://github.com/ajnart/homarr",
|
||||||
|
"behaviour": {
|
||||||
|
"onClickUrl": "https://github.com/ajnart/homarr",
|
||||||
|
"externalUrl": "https://github.com/ajnart/homarr",
|
||||||
|
"isOpeningNewTab": true
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"enabledStatusChecker": false,
|
||||||
|
"okStatus": []
|
||||||
|
},
|
||||||
|
"appearance": {
|
||||||
|
"iconUrl": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/github.png"
|
||||||
|
},
|
||||||
|
"integration": {
|
||||||
|
"type": null,
|
||||||
|
"properties": []
|
||||||
|
},
|
||||||
|
"area": {
|
||||||
|
"type": "category",
|
||||||
|
"properties": {
|
||||||
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shape": {
|
||||||
|
"md": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 2
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sm": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 2
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 2
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -227,17 +353,39 @@
|
|||||||
"area": {
|
"area": {
|
||||||
"type": "category",
|
"type": "category",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
"id": "c8407d2c-2353-4775-87c3-602f6f2684d5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shape": {
|
"shape": {
|
||||||
"location": {
|
"md": {
|
||||||
"x": 0,
|
"location": {
|
||||||
"y": 2
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 6,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"size": {
|
"sm": {
|
||||||
"width": 2,
|
"location": {
|
||||||
"height": 2
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 8,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 8,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"integration": {
|
"integration": {
|
||||||
@@ -246,7 +394,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5df743d9-5cb1-457c-85d2-64ff86855652",
|
"id": "615e43bd-f0aa-4117-ba49-b6495c039f3e",
|
||||||
"name": "Your app",
|
"name": "Your app",
|
||||||
"url": "https://homarr.dev",
|
"url": "https://homarr.dev",
|
||||||
"appearance": {
|
"appearance": {
|
||||||
@@ -267,13 +415,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shape": {
|
"shape": {
|
||||||
"location": {
|
"sm": {
|
||||||
"x": 0,
|
"location": {
|
||||||
"y": 7
|
"x": 0,
|
||||||
|
"y": 5
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"size": {
|
"md": {
|
||||||
"width": 6,
|
"location": {
|
||||||
"height": 4
|
"x": 0,
|
||||||
|
"y": 5
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 5
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 1,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"integration": {
|
"integration": {
|
||||||
@@ -283,51 +453,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"widgets": [
|
"widgets": [
|
||||||
{
|
|
||||||
"id": "date",
|
|
||||||
"properties": {
|
|
||||||
"display24HourFormat": true
|
|
||||||
},
|
|
||||||
"area": {
|
|
||||||
"type": "category",
|
|
||||||
"properties": {
|
|
||||||
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shape": {
|
|
||||||
"location": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"size": {
|
|
||||||
"width": 6,
|
|
||||||
"height": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "weather",
|
|
||||||
"properties": {
|
|
||||||
"displayInFahrenheit": false,
|
|
||||||
"location": "Paris"
|
|
||||||
},
|
|
||||||
"area": {
|
|
||||||
"type": "wrapper",
|
|
||||||
"properties": {
|
|
||||||
"id": "default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shape": {
|
|
||||||
"location": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"size": {
|
|
||||||
"width": 4,
|
|
||||||
"height": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "calendar",
|
"id": "calendar",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -340,13 +465,124 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shape": {
|
"shape": {
|
||||||
"location": {
|
"md": {
|
||||||
"x": 0,
|
"location": {
|
||||||
"y": 2
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 12,
|
||||||
|
"height": 5
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"size": {
|
"sm": {
|
||||||
"width": 5,
|
"location": {
|
||||||
"height": 5
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 12,
|
||||||
|
"height": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 12,
|
||||||
|
"height": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "weather",
|
||||||
|
"properties": {
|
||||||
|
"displayInFahrenheit": false,
|
||||||
|
"location": "Paris"
|
||||||
|
},
|
||||||
|
"area": {
|
||||||
|
"type": "category",
|
||||||
|
"properties": {
|
||||||
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shape": {
|
||||||
|
"md": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sm": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "date",
|
||||||
|
"properties": {
|
||||||
|
"display24HourFormat": true
|
||||||
|
},
|
||||||
|
"area": {
|
||||||
|
"type": "category",
|
||||||
|
"properties": {
|
||||||
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shape": {
|
||||||
|
"sm": {
|
||||||
|
"location": {
|
||||||
|
"x": 2,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"md": {
|
||||||
|
"location": {
|
||||||
|
"x": 2,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lg": {
|
||||||
|
"location": {
|
||||||
|
"x": 2,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 2,
|
||||||
|
"height": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,8 +596,8 @@
|
|||||||
},
|
},
|
||||||
"customization": {
|
"customization": {
|
||||||
"layout": {
|
"layout": {
|
||||||
"enabledLeftSidebar": false,
|
"enabledLeftSidebar": true,
|
||||||
"enabledRightSidebar": false,
|
"enabledRightSidebar": true,
|
||||||
"enabledDocker": false,
|
"enabledDocker": false,
|
||||||
"enabledPing": false,
|
"enabledPing": false,
|
||||||
"enabledSearchbar": true
|
"enabledSearchbar": true
|
||||||
@@ -372,9 +608,9 @@
|
|||||||
"backgroundImageUrl": "",
|
"backgroundImageUrl": "",
|
||||||
"customCss": "",
|
"customCss": "",
|
||||||
"colors": {
|
"colors": {
|
||||||
"primary": "red",
|
"primary": "pink",
|
||||||
"secondary": "orange",
|
"secondary": "yellow",
|
||||||
"shade": 5
|
"shade": 4
|
||||||
},
|
},
|
||||||
"appOpacity": 100
|
"appOpacity": 100
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
"height": "Height",
|
"height": "Height",
|
||||||
"yPosition": "Y axis position",
|
"yPosition": "Y axis position",
|
||||||
"zeroOrHigher": "0 or higher",
|
"zeroOrHigher": "0 or higher",
|
||||||
"betweenXandY": "Between {{mim}} and {{max}}"
|
"betweenXandY": "Between {{min}} and {{max}}"
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ export const MobileRibbonSidebarDrawer = ({
|
|||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<DashboardSidebar location={location} />
|
<DashboardSidebar location={location} isGridstackReady />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { closeModal, ContextModalProps } from '@mantine/modals';
|
|||||||
import { useConfigContext } from '../../../../config/provider';
|
import { useConfigContext } from '../../../../config/provider';
|
||||||
import { useConfigStore } from '../../../../config/store';
|
import { useConfigStore } from '../../../../config/store';
|
||||||
import { AppType } from '../../../../types/app';
|
import { AppType } from '../../../../types/app';
|
||||||
|
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
import { ChangePositionModal } from './ChangePositionModal';
|
import { ChangePositionModal } from './ChangePositionModal';
|
||||||
|
|
||||||
type ChangeAppPositionModalInnerProps = {
|
type ChangeAppPositionModalInnerProps = {
|
||||||
@@ -16,6 +17,9 @@ export const ChangeAppPositionModal = ({
|
|||||||
}: ContextModalProps<ChangeAppPositionModalInnerProps>) => {
|
}: ContextModalProps<ChangeAppPositionModalInnerProps>) => {
|
||||||
const { name: configName } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||||
|
const shapeSize = useGridstackStore((x) => x.currentShapeSize);
|
||||||
|
|
||||||
|
if (!shapeSize) return null;
|
||||||
|
|
||||||
const handleSubmit = (x: number, y: number, width: number, height: number) => {
|
const handleSubmit = (x: number, y: number, width: number, height: number) => {
|
||||||
if (!configName) {
|
if (!configName) {
|
||||||
@@ -28,7 +32,13 @@ export const ChangeAppPositionModal = ({
|
|||||||
...previousConfig,
|
...previousConfig,
|
||||||
apps: [
|
apps: [
|
||||||
...previousConfig.apps.filter((x) => x.id !== innerProps.app.id),
|
...previousConfig.apps.filter((x) => x.id !== innerProps.app.id),
|
||||||
{ ...innerProps.app, shape: { location: { x, y }, size: { width, height } } },
|
{
|
||||||
|
...innerProps.app,
|
||||||
|
shape: {
|
||||||
|
...innerProps.app.shape,
|
||||||
|
[shapeSize]: { location: { x, y }, size: { width, height } },
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
@@ -49,28 +59,35 @@ export const ChangeAppPositionModal = ({
|
|||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
widthData={widthData}
|
widthData={widthData}
|
||||||
heightData={heightData}
|
heightData={heightData}
|
||||||
initialX={innerProps.app.shape.location.x}
|
initialX={innerProps.app.shape[shapeSize]?.location.x}
|
||||||
initialY={innerProps.app.shape.location.y}
|
initialY={innerProps.app.shape[shapeSize]?.location.y}
|
||||||
initialWidth={innerProps.app.shape.size.width}
|
initialWidth={innerProps.app.shape[shapeSize]?.size.width}
|
||||||
initialHeight={innerProps.app.shape.size.height}
|
initialHeight={innerProps.app.shape[shapeSize]?.size.height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useHeightData = (): SelectItem[] =>
|
const useHeightData = (): SelectItem[] => {
|
||||||
Array.from(Array(11).keys()).map((n) => {
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||||
const index = n + 1;
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
return {
|
|
||||||
value: index.toString(),
|
|
||||||
label: `${64 * index}px`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const useWidthData = (): SelectItem[] =>
|
return Array.from(Array(11).keys()).map((n) => {
|
||||||
Array.from(Array(11).keys()).map((n) => {
|
|
||||||
const index = n + 1;
|
const index = n + 1;
|
||||||
return {
|
return {
|
||||||
value: index.toString(),
|
value: index.toString(),
|
||||||
label: `${64 * index}px`,
|
label: `${Math.floor(index * (mainAreaWidth! / wrapperColumnCount!))}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const useWidthData = (): SelectItem[] => {
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
|
return Array.from(Array(wrapperColumnCount!).keys()).map((n) => {
|
||||||
|
const index = n + 1;
|
||||||
|
return {
|
||||||
|
value: index.toString(),
|
||||||
|
// eslint-disable-next-line no-mixed-operators
|
||||||
|
label: `${((100 / wrapperColumnCount!) * index).toFixed(2)}%`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import { useConfigContext } from '../../../../config/provider';
|
import { useConfigContext } from '../../../../config/provider';
|
||||||
|
|
||||||
interface ChangePositionModalProps {
|
interface ChangePositionModalProps {
|
||||||
initialX: number;
|
initialX?: number;
|
||||||
initialY: number;
|
initialY?: number;
|
||||||
initialWidth: number;
|
initialWidth?: number;
|
||||||
initialHeight: number;
|
initialHeight?: number;
|
||||||
widthData: SelectItem[];
|
widthData: SelectItem[];
|
||||||
heightData: SelectItem[];
|
heightData: SelectItem[];
|
||||||
onSubmit: (x: number, y: number, width: number, height: number) => void;
|
onSubmit: (x: number, y: number, width: number, height: number) => void;
|
||||||
@@ -28,10 +28,10 @@ export const ChangePositionModal = ({
|
|||||||
|
|
||||||
const form = useForm<FormType>({
|
const form = useForm<FormType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
x: initialX,
|
x: initialX ?? null,
|
||||||
y: initialY,
|
y: initialY ?? null,
|
||||||
width: initialWidth,
|
width: initialWidth?.toString() ?? '',
|
||||||
height: initialHeight,
|
height: initialHeight?.toString() ?? '',
|
||||||
},
|
},
|
||||||
validateInputOnChange: true,
|
validateInputOnChange: true,
|
||||||
validateInputOnBlur: true,
|
validateInputOnBlur: true,
|
||||||
@@ -42,7 +42,12 @@ export const ChangePositionModal = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit(form.values.x, form.values.y, form.values.width, form.values.height);
|
const width = parseInt(form.values.width, 10);
|
||||||
|
const height = parseInt(form.values.height, 10);
|
||||||
|
|
||||||
|
if (!form.values.x || !form.values.y || Number.isNaN(width) || Number.isNaN(height)) return;
|
||||||
|
|
||||||
|
onSubmit(form.values.x, form.values.y, width, height);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTranslation(['layout/modals/change-position', 'common']);
|
const { t } = useTranslation(['layout/modals/change-position', 'common']);
|
||||||
@@ -112,8 +117,8 @@ export const ChangePositionModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
type FormType = {
|
type FormType = {
|
||||||
x: number;
|
x: number | null;
|
||||||
y: number;
|
y: number | null;
|
||||||
width: number;
|
width: string;
|
||||||
height: number;
|
height: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useConfigContext } from '../../../../config/provider';
|
|||||||
import { useConfigStore } from '../../../../config/store';
|
import { useConfigStore } from '../../../../config/store';
|
||||||
import widgets from '../../../../widgets';
|
import widgets from '../../../../widgets';
|
||||||
import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu';
|
import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu';
|
||||||
|
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
import { ChangePositionModal } from './ChangePositionModal';
|
import { ChangePositionModal } from './ChangePositionModal';
|
||||||
|
|
||||||
export const ChangeWidgetPositionModal = ({
|
export const ChangeWidgetPositionModal = ({
|
||||||
@@ -13,6 +14,7 @@ export const ChangeWidgetPositionModal = ({
|
|||||||
}: ContextModalProps<WidgetChangePositionModalInnerProps>) => {
|
}: ContextModalProps<WidgetChangePositionModalInnerProps>) => {
|
||||||
const { name: configName } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||||
|
const shapeSize = useGridstackStore((x) => x.currentShapeSize);
|
||||||
|
|
||||||
const handleSubmit = (x: number, y: number, width: number, height: number) => {
|
const handleSubmit = (x: number, y: number, width: number, height: number) => {
|
||||||
if (!configName) {
|
if (!configName) {
|
||||||
@@ -23,7 +25,7 @@ export const ChangeWidgetPositionModal = ({
|
|||||||
configName,
|
configName,
|
||||||
(prev) => {
|
(prev) => {
|
||||||
const currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId);
|
const currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId);
|
||||||
currentWidget!.shape = {
|
currentWidget!.shape[shapeSize] = {
|
||||||
location: {
|
location: {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@@ -57,32 +59,40 @@ export const ChangeWidgetPositionModal = ({
|
|||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
heightData={heightData}
|
heightData={heightData}
|
||||||
widthData={widthData}
|
widthData={widthData}
|
||||||
initialX={innerProps.widget.shape.location.x}
|
initialX={innerProps.widget.shape[shapeSize].location.x}
|
||||||
initialY={innerProps.widget.shape.location.y}
|
initialY={innerProps.widget.shape[shapeSize].location.y}
|
||||||
initialWidth={innerProps.widget.shape.size.width}
|
initialWidth={innerProps.widget.shape[shapeSize].size.width}
|
||||||
initialHeight={innerProps.widget.shape.size.height}
|
initialHeight={innerProps.widget.shape[shapeSize].size.height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useWidthData = (integration: string): SelectItem[] => {
|
const useWidthData = (integration: string): SelectItem[] => {
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
const currentWidget = widgets[integration as keyof typeof widgets];
|
const currentWidget = widgets[integration as keyof typeof widgets];
|
||||||
if (!currentWidget) return [];
|
if (!currentWidget) return [];
|
||||||
const offset = currentWidget.gridstack.minWidth ?? 2;
|
const offset = currentWidget.gridstack.minWidth ?? 2;
|
||||||
const length = (currentWidget.gridstack.maxWidth ?? 12) - offset;
|
const length =
|
||||||
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
|
(currentWidget.gridstack.maxWidth > wrapperColumnCount!
|
||||||
|
? wrapperColumnCount!
|
||||||
|
: currentWidget.gridstack.maxWidth) - offset;
|
||||||
|
return Array.from({ length: length + 1 }, (_, i) => i + offset).map((n) => ({
|
||||||
value: n.toString(),
|
value: n.toString(),
|
||||||
label: `${64 * n}px`,
|
// eslint-disable-next-line no-mixed-operators
|
||||||
|
label: `${((100 / wrapperColumnCount!) * n).toFixed(2)}%`,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const useHeightData = (integration: string): SelectItem[] => {
|
const useHeightData = (integration: string): SelectItem[] => {
|
||||||
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
|
|
||||||
const currentWidget = widgets[integration as keyof typeof widgets];
|
const currentWidget = widgets[integration as keyof typeof widgets];
|
||||||
if (!currentWidget) return [];
|
if (!currentWidget) return [];
|
||||||
const offset = currentWidget.gridstack.minHeight ?? 2;
|
const offset = currentWidget.gridstack.minHeight ?? 2;
|
||||||
const length = (currentWidget.gridstack.maxHeight ?? 12) - offset;
|
const length = (currentWidget.gridstack.maxHeight ?? 12) - offset;
|
||||||
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
|
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
|
||||||
value: n.toString(),
|
value: n.toString(),
|
||||||
label: `${64 * n}px`,
|
label: `${(mainAreaWidth! / wrapperColumnCount!) * n}px`,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -112,13 +112,35 @@ export const AvailableElementTypes = ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
location: {
|
sm: {
|
||||||
x: 0,
|
location: {
|
||||||
y: 0,
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: appTileDefinition.minWidth,
|
||||||
|
height: appTileDefinition.minHeight,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
size: {
|
md: {
|
||||||
width: appTileDefinition.minWidth,
|
location: {
|
||||||
height: appTileDefinition.minHeight,
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: appTileDefinition.minWidth,
|
||||||
|
height: appTileDefinition.minHeight,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lg: {
|
||||||
|
location: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: appTileDefinition.minWidth,
|
||||||
|
height: appTileDefinition.minHeight,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
integration: {
|
integration: {
|
||||||
|
|||||||
@@ -47,13 +47,35 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
location: {
|
sm: {
|
||||||
x: 0,
|
location: {
|
||||||
y: 0,
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: widget.gridstack.minWidth,
|
||||||
|
height: widget.gridstack.minHeight,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
size: {
|
md: {
|
||||||
width: widget.gridstack.minWidth,
|
location: {
|
||||||
height: widget.gridstack.minHeight,
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: widget.gridstack.minWidth,
|
||||||
|
height: widget.gridstack.minHeight,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lg: {
|
||||||
|
location: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: widget.gridstack.minWidth,
|
||||||
|
height: widget.gridstack.minHeight,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Title } from '@mantine/core';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions';
|
import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions';
|
||||||
import { IWidget } from '../../../../widgets/widgets';
|
import { IWidget } from '../../../../widgets/widgets';
|
||||||
|
import { useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
import { GenericTileMenu } from '../GenericTileMenu';
|
import { GenericTileMenu } from '../GenericTileMenu';
|
||||||
import { WidgetEditModalInnerProps } from './WidgetsEditModal';
|
import { WidgetEditModalInnerProps } from './WidgetsEditModal';
|
||||||
import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal';
|
import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal';
|
||||||
@@ -10,6 +11,7 @@ import WidgetsDefinitions from '../../../../widgets';
|
|||||||
export type WidgetChangePositionModalInnerProps = {
|
export type WidgetChangePositionModalInnerProps = {
|
||||||
widgetId: string;
|
widgetId: string;
|
||||||
widget: IWidget<string, any>;
|
widget: IWidget<string, any>;
|
||||||
|
wrapperColumnCount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface WidgetsMenuProps {
|
interface WidgetsMenuProps {
|
||||||
@@ -19,8 +21,9 @@ interface WidgetsMenuProps {
|
|||||||
|
|
||||||
export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => {
|
export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => {
|
||||||
const { t } = useTranslation(`modules/${integration}`);
|
const { t } = useTranslation(`modules/${integration}`);
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
|
|
||||||
if (!widget) return null;
|
if (!widget || !wrapperColumnCount) return null;
|
||||||
// Match widget.id with WidgetsDefinitions
|
// Match widget.id with WidgetsDefinitions
|
||||||
// First get the keys
|
// First get the keys
|
||||||
const keys = Object.keys(WidgetsDefinitions);
|
const keys = Object.keys(WidgetsDefinitions);
|
||||||
@@ -48,6 +51,7 @@ export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => {
|
|||||||
innerProps: {
|
innerProps: {
|
||||||
widgetId: integration,
|
widgetId: integration,
|
||||||
widget,
|
widget,
|
||||||
|
wrapperColumnCount,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,39 +1,83 @@
|
|||||||
import { Group, Stack } from '@mantine/core';
|
import { Center, Group, Loader, Stack } from '@mantine/core';
|
||||||
import { useMemo } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
import { useConfigContext } from '../../../config/provider';
|
import { useConfigContext } from '../../../config/provider';
|
||||||
import { useScreenSmallerThan } from '../../../hooks/useScreenSmallerThan';
|
import { useResize } from '../../../hooks/use-resize';
|
||||||
|
import { useScreenLargerThan } from '../../../hooks/useScreenLargerThan';
|
||||||
import { CategoryType } from '../../../types/category';
|
import { CategoryType } from '../../../types/category';
|
||||||
import { WrapperType } from '../../../types/wrapper';
|
import { WrapperType } from '../../../types/wrapper';
|
||||||
import { DashboardCategory } from '../Wrappers/Category/Category';
|
import { DashboardCategory } from '../Wrappers/Category/Category';
|
||||||
|
import { useGridstackStore } from '../Wrappers/gridstack/store';
|
||||||
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
|
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
|
||||||
import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper';
|
import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper';
|
||||||
|
|
||||||
export const DashboardView = () => {
|
export const DashboardView = () => {
|
||||||
const wrappers = useWrapperItems();
|
const wrappers = useWrapperItems();
|
||||||
const layoutSettings = useConfigContext()?.config?.settings.customization.layout;
|
const sidebarsVisible = useSidebarVisibility();
|
||||||
const doNotShowSidebar = useScreenSmallerThan('md');
|
const { isReady, mainAreaRef } = usePrepareGridstack();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group align="top" h="100%">
|
<Group align="top" h="100%">
|
||||||
{layoutSettings?.enabledLeftSidebar && !doNotShowSidebar ? (
|
{sidebarsVisible.isLoading ? (
|
||||||
<DashboardSidebar location="left" />
|
<Center w="100%">
|
||||||
) : null}
|
<Loader />
|
||||||
<Stack mx={-10} style={{ flexGrow: 1 }}>
|
</Center>
|
||||||
{wrappers.map((item) =>
|
) : (
|
||||||
item.type === 'category' ? (
|
<>
|
||||||
<DashboardCategory key={item.id} category={item as unknown as CategoryType} />
|
{sidebarsVisible.left ? (
|
||||||
) : (
|
<DashboardSidebar location="left" isGridstackReady={isReady} />
|
||||||
<DashboardWrapper key={item.id} wrapper={item as WrapperType} />
|
) : null}
|
||||||
)
|
|
||||||
)}
|
<Stack ref={mainAreaRef} mx={-10} style={{ flexGrow: 1 }}>
|
||||||
</Stack>
|
{!isReady
|
||||||
{layoutSettings?.enabledRightSidebar && !doNotShowSidebar ? (
|
? null
|
||||||
<DashboardSidebar location="right" />
|
: wrappers.map((item) =>
|
||||||
) : null}
|
item.type === 'category' ? (
|
||||||
|
<DashboardCategory key={item.id} category={item as unknown as CategoryType} />
|
||||||
|
) : (
|
||||||
|
<DashboardWrapper key={item.id} wrapper={item as WrapperType} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{sidebarsVisible.right ? (
|
||||||
|
<DashboardSidebar location="right" isGridstackReady={isReady} />
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const usePrepareGridstack = () => {
|
||||||
|
const mainAreaRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { width } = useResize(mainAreaRef, []);
|
||||||
|
const setMainAreaWidth = useGridstackStore((x) => x.setMainAreaWidth);
|
||||||
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (width === 0) return;
|
||||||
|
setMainAreaWidth(width);
|
||||||
|
}, [width]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isReady: !!mainAreaWidth,
|
||||||
|
mainAreaRef,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useSidebarVisibility = () => {
|
||||||
|
const layoutSettings = useConfigContext()?.config?.settings.customization.layout;
|
||||||
|
const screenLargerThanMd = useScreenLargerThan('md'); // For smaller screens mobile ribbons are displayed with drawers
|
||||||
|
|
||||||
|
const isScreenSizeUnknown = typeof screenLargerThanMd === 'undefined';
|
||||||
|
|
||||||
|
return {
|
||||||
|
right: layoutSettings?.enabledRightSidebar && screenLargerThanMd,
|
||||||
|
left: layoutSettings?.enabledLeftSidebar && screenLargerThanMd,
|
||||||
|
isLoading: isScreenSizeUnknown,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const useWrapperItems = () => {
|
const useWrapperItems = () => {
|
||||||
const { config } = useConfigContext();
|
const { config } = useConfigContext();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
|
|||||||
</Group>
|
</Group>
|
||||||
<div
|
<div
|
||||||
className="grid-stack grid-stack-category"
|
className="grid-stack grid-stack-category"
|
||||||
style={{ transitionDuration: '0s' }}
|
|
||||||
data-category={category.id}
|
data-category={category.id}
|
||||||
ref={refs.wrapper}
|
ref={refs.wrapper}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,11 +4,30 @@ import { useCardStyles } from '../../../layout/useCardStyles';
|
|||||||
import { useGridstack } from '../gridstack/use-gridstack';
|
import { useGridstack } from '../gridstack/use-gridstack';
|
||||||
import { WrapperContent } from '../WrapperContent';
|
import { WrapperContent } from '../WrapperContent';
|
||||||
|
|
||||||
interface DashboardSidebarProps {
|
interface DashboardSidebarProps extends DashboardSidebarInnerProps {
|
||||||
|
location: 'right' | 'left';
|
||||||
|
isGridstackReady: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DashboardSidebar = ({ location, isGridstackReady }: DashboardSidebarProps) => (
|
||||||
|
<Card
|
||||||
|
withBorder
|
||||||
|
w={300}
|
||||||
|
style={{
|
||||||
|
background: 'none',
|
||||||
|
borderStyle: 'dashed',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isGridstackReady && <SidebarInner location={location} />}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
|
||||||
|
interface DashboardSidebarInnerProps {
|
||||||
location: 'right' | 'left';
|
location: 'right' | 'left';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
// Is Required because of the gridstack main area width.
|
||||||
|
const SidebarInner = ({ location }: DashboardSidebarInnerProps) => {
|
||||||
const { refs, apps, widgets } = useGridstack('sidebar', location);
|
const { refs, apps, widgets } = useGridstack('sidebar', location);
|
||||||
|
|
||||||
const minRow = useMinRowForFullHeight(refs.wrapper);
|
const minRow = useMinRowForFullHeight(refs.wrapper);
|
||||||
@@ -18,14 +37,13 @@ export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
|||||||
} = useCardStyles(false);
|
} = useCardStyles(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card withBorder w={300} p={0} radius="lg" className={cardClass}>
|
<Card withBorder mih="100%" p={0} radius="lg" className={cardClass} ref={refs.wrapper}>
|
||||||
<div
|
<div
|
||||||
className="grid-stack grid-stack-sidebar"
|
className="grid-stack grid-stack-sidebar"
|
||||||
style={{ transitionDuration: '0s', height: '100%' }}
|
style={{ transitionDuration: '0s', height: '100%' }}
|
||||||
data-sidebar={location}
|
data-sidebar={location}
|
||||||
// eslint-disable-next-line react/no-unknown-property
|
// eslint-disable-next-line react/no-unknown-property
|
||||||
gs-min-row={minRow}
|
gs-min-row={minRow}
|
||||||
ref={refs.wrapper}
|
|
||||||
>
|
>
|
||||||
<WrapperContent apps={apps} refs={refs} widgets={widgets} />
|
<WrapperContent apps={apps} refs={refs} widgets={widgets} />
|
||||||
</div>
|
</div>
|
||||||
@@ -34,4 +52,4 @@ export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useMinRowForFullHeight = (wrapperRef: RefObject<HTMLDivElement>) =>
|
const useMinRowForFullHeight = (wrapperRef: RefObject<HTMLDivElement>) =>
|
||||||
wrapperRef.current ? Math.floor(wrapperRef.current!.offsetHeight / 64) : 2;
|
wrapperRef.current ? Math.floor(wrapperRef.current!.offsetHeight / 128) : 2;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { IWidget, IWidgetDefinition } from '../../../widgets/widgets';
|
|||||||
import { WidgetWrapper } from '../../../widgets/WidgetWrapper';
|
import { WidgetWrapper } from '../../../widgets/WidgetWrapper';
|
||||||
import { appTileDefinition } from '../Tiles/Apps/AppTile';
|
import { appTileDefinition } from '../Tiles/Apps/AppTile';
|
||||||
import { GridstackTileWrapper } from '../Tiles/TileWrapper';
|
import { GridstackTileWrapper } from '../Tiles/TileWrapper';
|
||||||
|
import { useGridstackStore } from './gridstack/store';
|
||||||
|
|
||||||
interface WrapperContentProps {
|
interface WrapperContentProps {
|
||||||
apps: AppType[];
|
apps: AppType[];
|
||||||
@@ -18,6 +19,10 @@ interface WrapperContentProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
||||||
|
const shapeSize = useGridstackStore((x) => x.currentShapeSize);
|
||||||
|
|
||||||
|
if (!shapeSize) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{apps?.map((app) => {
|
{apps?.map((app) => {
|
||||||
@@ -29,8 +34,8 @@ export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
|||||||
key={app.id}
|
key={app.id}
|
||||||
itemRef={refs.items.current[app.id]}
|
itemRef={refs.items.current[app.id]}
|
||||||
{...tile}
|
{...tile}
|
||||||
{...app.shape.location}
|
{...app.shape[shapeSize]?.location}
|
||||||
{...app.shape.size}
|
{...app.shape[shapeSize]?.size}
|
||||||
>
|
>
|
||||||
<TileComponent className="grid-stack-item-content" app={app} />
|
<TileComponent className="grid-stack-item-content" app={app} />
|
||||||
</GridstackTileWrapper>
|
</GridstackTileWrapper>
|
||||||
@@ -49,8 +54,8 @@ export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
|||||||
itemRef={refs.items.current[widget.id]}
|
itemRef={refs.items.current[widget.id]}
|
||||||
id={definition.id}
|
id={definition.id}
|
||||||
{...definition.gridstack}
|
{...definition.gridstack}
|
||||||
{...widget.shape.location}
|
{...widget.shape[shapeSize]?.location}
|
||||||
{...widget.shape.size}
|
{...widget.shape[shapeSize]?.size}
|
||||||
>
|
>
|
||||||
<WidgetWrapper className="grid-stack-item-content" widget={widget} widgetId={widget.id}>
|
<WidgetWrapper className="grid-stack-item-content" widget={widget} widgetId={widget.id}>
|
||||||
<definition.component className="grid-stack-item-content" widget={widget} />
|
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
||||||
import { MutableRefObject, RefObject } from 'react';
|
import { MutableRefObject, RefObject } from 'react';
|
||||||
import { AppType } from '../../../../types/app';
|
import { AppType } from '../../../../types/app';
|
||||||
|
import { ShapeType } from '../../../../types/shape';
|
||||||
import { IWidget } from '../../../../widgets/widgets';
|
import { IWidget } from '../../../../widgets/widgets';
|
||||||
|
|
||||||
export const initializeGridstack = (
|
export const initializeGridstack = (
|
||||||
@@ -12,7 +13,8 @@ export const initializeGridstack = (
|
|||||||
items: AppType[],
|
items: AppType[],
|
||||||
widgets: IWidget<string, any>[],
|
widgets: IWidget<string, any>[],
|
||||||
isEditMode: boolean,
|
isEditMode: boolean,
|
||||||
isLargerThanSm: boolean,
|
wrapperColumnCount: 3 | 6 | 12,
|
||||||
|
shapeSize: 'sm' | 'md' | 'lg',
|
||||||
events: {
|
events: {
|
||||||
onChange: (changedNode: GridStackNode) => void;
|
onChange: (changedNode: GridStackNode) => void;
|
||||||
onAdd: (addedNode: GridStackNode) => void;
|
onAdd: (addedNode: GridStackNode) => void;
|
||||||
@@ -20,27 +22,29 @@ export const initializeGridstack = (
|
|||||||
) => {
|
) => {
|
||||||
if (!wrapperRef.current) return;
|
if (!wrapperRef.current) return;
|
||||||
// calculates the currently available count of columns
|
// calculates the currently available count of columns
|
||||||
const columnCount =
|
const columnCount = areaType === 'sidebar' ? 2 : wrapperColumnCount;
|
||||||
areaType === 'sidebar' ? 1 : isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6;
|
const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 128);
|
||||||
const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64);
|
|
||||||
// initialize gridstack
|
// initialize gridstack
|
||||||
const newGrid = gridRef;
|
const newGrid = gridRef;
|
||||||
newGrid.current = GridStack.init(
|
newGrid.current = GridStack.init(
|
||||||
{
|
{
|
||||||
column: columnCount,
|
column: columnCount,
|
||||||
margin: 10,
|
margin: 10,
|
||||||
cellHeight: 64,
|
cellHeight: 128,
|
||||||
float: true,
|
float: true,
|
||||||
alwaysShowResizeHandle: 'mobile',
|
alwaysShowResizeHandle: 'mobile',
|
||||||
acceptWidgets: true,
|
acceptWidgets: true,
|
||||||
disableOneColumnMode: true,
|
disableOneColumnMode: true,
|
||||||
staticGrid: !isEditMode,
|
staticGrid: !isEditMode,
|
||||||
minRow,
|
minRow,
|
||||||
|
animate: false,
|
||||||
},
|
},
|
||||||
// selector of the gridstack item (it's eather category or wrapper)
|
// selector of the gridstack item (it's eather category or wrapper)
|
||||||
`.grid-stack-${areaType}[data-${areaType}='${areaId}']`
|
`.grid-stack-${areaType}[data-${areaType}='${areaId}']`
|
||||||
);
|
);
|
||||||
const grid = newGrid.current;
|
const grid = newGrid.current;
|
||||||
|
// Must be used to update the column count after the initialization
|
||||||
|
grid.column(columnCount);
|
||||||
|
|
||||||
// Add listener for moving items around in a wrapper
|
// Add listener for moving items around in a wrapper
|
||||||
grid.on('change', (_, el) => {
|
grid.on('change', (_, el) => {
|
||||||
@@ -60,13 +64,23 @@ export const initializeGridstack = (
|
|||||||
|
|
||||||
grid.batchUpdate();
|
grid.batchUpdate();
|
||||||
grid.removeAll(false);
|
grid.removeAll(false);
|
||||||
items.forEach(
|
items.forEach(({ id, shape }) => {
|
||||||
({ id }) =>
|
const item = itemRefs.current[id]?.current;
|
||||||
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
setAttributesFromShape(item, shape[shapeSize]);
|
||||||
);
|
item && grid.makeWidget(item as HTMLDivElement);
|
||||||
widgets.forEach(
|
});
|
||||||
({ id }) =>
|
widgets.forEach(({ id, shape }) => {
|
||||||
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
const item = itemRefs.current[id]?.current;
|
||||||
);
|
setAttributesFromShape(item, shape[shapeSize]);
|
||||||
|
item && grid.makeWidget(item as HTMLDivElement);
|
||||||
|
});
|
||||||
grid.batchUpdate(false);
|
grid.batchUpdate(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function setAttributesFromShape(ref: HTMLDivElement | null, sizedShape: ShapeType['lg']) {
|
||||||
|
if (!sizedShape || !ref) return;
|
||||||
|
ref.setAttribute('gs-x', sizedShape.location.x.toString());
|
||||||
|
ref.setAttribute('gs-y', sizedShape.location.y.toString());
|
||||||
|
ref.setAttribute('gs-w', sizedShape.size.width.toString());
|
||||||
|
ref.setAttribute('gs-h', sizedShape.size.height.toString());
|
||||||
|
}
|
||||||
|
|||||||
31
src/components/Dashboard/Wrappers/gridstack/store.tsx
Normal file
31
src/components/Dashboard/Wrappers/gridstack/store.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { useMantineTheme } from '@mantine/core';
|
||||||
|
import create from 'zustand';
|
||||||
|
|
||||||
|
export const useGridstackStore = create<GridstackStoreType>((set, get) => ({
|
||||||
|
mainAreaWidth: null,
|
||||||
|
currentShapeSize: null,
|
||||||
|
setMainAreaWidth: (w: number) =>
|
||||||
|
set((v) => ({ ...v, mainAreaWidth: w, currentShapeSize: getCurrentShapeSize(w) })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface GridstackStoreType {
|
||||||
|
mainAreaWidth: null | number;
|
||||||
|
currentShapeSize: null | 'sm' | 'md' | 'lg';
|
||||||
|
setMainAreaWidth: (width: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useWrapperColumnCount = () => {
|
||||||
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||||
|
const { sm, xl } = useMantineTheme().breakpoints;
|
||||||
|
if (!mainAreaWidth) return null;
|
||||||
|
|
||||||
|
if (mainAreaWidth >= xl) return 12;
|
||||||
|
|
||||||
|
if (mainAreaWidth >= sm) return 6;
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getCurrentShapeSize(size: number) {
|
||||||
|
return size >= 1400 ? 'lg' : size >= 768 ? 'md' : 'sm';
|
||||||
|
}
|
||||||
@@ -1,22 +1,13 @@
|
|||||||
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
||||||
import {
|
import { createRef, MutableRefObject, RefObject, useEffect, useMemo, useRef } from 'react';
|
||||||
createRef,
|
|
||||||
MutableRefObject,
|
|
||||||
RefObject,
|
|
||||||
useEffect,
|
|
||||||
useLayoutEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
} from 'react';
|
|
||||||
import { useConfigContext } from '../../../../config/provider';
|
import { useConfigContext } from '../../../../config/provider';
|
||||||
import { useConfigStore } from '../../../../config/store';
|
import { useConfigStore } from '../../../../config/store';
|
||||||
import { useResize } from '../../../../hooks/use-resize';
|
|
||||||
import { useScreenLargerThan } from '../../../../hooks/useScreenLargerThan';
|
|
||||||
import { AppType } from '../../../../types/app';
|
import { AppType } from '../../../../types/app';
|
||||||
import { AreaType } from '../../../../types/area';
|
import { AreaType } from '../../../../types/area';
|
||||||
import { IWidget } from '../../../../widgets/widgets';
|
import { IWidget } from '../../../../widgets/widgets';
|
||||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||||
import { initializeGridstack } from './init-gridstack';
|
import { initializeGridstack } from './init-gridstack';
|
||||||
|
import { useGridstackStore, useWrapperColumnCount } from './store';
|
||||||
|
|
||||||
interface UseGristackReturnType {
|
interface UseGristackReturnType {
|
||||||
apps: AppType[];
|
apps: AppType[];
|
||||||
@@ -32,7 +23,6 @@ export const useGridstack = (
|
|||||||
areaType: 'wrapper' | 'category' | 'sidebar',
|
areaType: 'wrapper' | 'category' | 'sidebar',
|
||||||
areaId: string
|
areaId: string
|
||||||
): UseGristackReturnType => {
|
): UseGristackReturnType => {
|
||||||
const isLargerThanSm = useScreenLargerThan('sm');
|
|
||||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||||
const { config, configVersion, name: configName } = useConfigContext();
|
const { config, configVersion, name: configName } = useConfigContext();
|
||||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||||
@@ -42,8 +32,15 @@ export const useGridstack = (
|
|||||||
const itemRefs = useRef<Record<string, RefObject<HTMLDivElement>>>({});
|
const itemRefs = useRef<Record<string, RefObject<HTMLDivElement>>>({});
|
||||||
// reference of the gridstack object for modifications after initialization
|
// reference of the gridstack object for modifications after initialization
|
||||||
const gridRef = useRef<GridStack>();
|
const gridRef = useRef<GridStack>();
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
|
const shapeSize = useGridstackStore((x) => x.currentShapeSize);
|
||||||
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||||
// width of the wrapper (updating on page resize)
|
// width of the wrapper (updating on page resize)
|
||||||
const { width, height } = useResize(wrapperRef);
|
const root: HTMLHtmlElement = useMemo(() => document.querySelector(':root')!, []);
|
||||||
|
|
||||||
|
if (!mainAreaWidth || !shapeSize || !wrapperColumnCount) {
|
||||||
|
throw new Error('UseGridstack should not be executed before mainAreaWidth has been set!');
|
||||||
|
}
|
||||||
|
|
||||||
const items = useMemo(
|
const items = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -77,55 +74,17 @@ export const useGridstack = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// change column count depending on the width and the gridRef
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (areaType === 'sidebar') return;
|
const widgetWidth = mainAreaWidth / wrapperColumnCount;
|
||||||
gridRef.current?.column(
|
// widget width is used to define sizes of gridstack items within global.scss
|
||||||
isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6,
|
root.style.setProperty('--gridstack-widget-width', widgetWidth.toString());
|
||||||
(column, prevColumn, newNodes, nodes) => {
|
gridRef.current?.cellHeight(widgetWidth);
|
||||||
let nextRow = 0;
|
}, [mainAreaWidth, wrapperColumnCount, gridRef.current]);
|
||||||
let available = 6;
|
|
||||||
|
|
||||||
if (column === prevColumn) {
|
useEffect(() => {
|
||||||
newNodes.concat(nodes);
|
// column count is used to define count of columns of gridstack within global.scss
|
||||||
return;
|
root.style.setProperty('--gridstack-column-count', wrapperColumnCount.toString());
|
||||||
}
|
}, [wrapperColumnCount]);
|
||||||
|
|
||||||
nodes.reverse().forEach((node) => {
|
|
||||||
const newnode = node;
|
|
||||||
const width = parseInt(newnode.el!.getAttribute('data-gridstack-w')!, 10);
|
|
||||||
const height = parseInt(newnode.el!.getAttribute('data-gridstack-h')!, 10);
|
|
||||||
const x = parseInt(newnode.el!.getAttribute('data-gridstack-x')!, 10);
|
|
||||||
const y = parseInt(newnode.el!.getAttribute('data-gridstack-y')!, 10);
|
|
||||||
|
|
||||||
if (column === 6) {
|
|
||||||
newnode.x = available >= width ? 6 - available : 0;
|
|
||||||
newnode.y = nextRow;
|
|
||||||
|
|
||||||
if (width > 6) {
|
|
||||||
newnode.w = 6;
|
|
||||||
nextRow += 2;
|
|
||||||
available = 6;
|
|
||||||
} else if (available >= width) {
|
|
||||||
available -= width;
|
|
||||||
if (available === 0) {
|
|
||||||
nextRow += 2;
|
|
||||||
available = 6;
|
|
||||||
}
|
|
||||||
} else if (available < width) {
|
|
||||||
newnode.y = newnode.y! + 2;
|
|
||||||
available = 6 - width;
|
|
||||||
nextRow += 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newnode.x = y % 2 === 1 ? x + 6 : x;
|
|
||||||
newnode.y = Math.floor(y / 2);
|
|
||||||
}
|
|
||||||
newNodes.push(newnode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}, [isLargerThanSm]);
|
|
||||||
|
|
||||||
const onChange = isEditMode
|
const onChange = isEditMode
|
||||||
? (changedNode: GridStackNode) => {
|
? (changedNode: GridStackNode) => {
|
||||||
@@ -143,14 +102,14 @@ export const useGridstack = (
|
|||||||
: previous.widgets.find((x) => x.id === itemId);
|
: previous.widgets.find((x) => x.id === itemId);
|
||||||
if (!currentItem) return previous;
|
if (!currentItem) return previous;
|
||||||
|
|
||||||
currentItem.shape = {
|
currentItem.shape[shapeSize] = {
|
||||||
location: {
|
location: {
|
||||||
x: changedNode.x ?? currentItem.shape.location.x,
|
x: changedNode.x ?? currentItem.shape[shapeSize].location.x,
|
||||||
y: changedNode.y ?? currentItem.shape.location.y,
|
y: changedNode.y ?? currentItem.shape[shapeSize].location.y,
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
width: changedNode.w ?? currentItem.shape.size.width,
|
width: changedNode.w ?? currentItem.shape[shapeSize].size.width,
|
||||||
height: changedNode.h ?? currentItem.shape.size.height,
|
height: changedNode.h ?? currentItem.shape[shapeSize].size.height,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -209,14 +168,14 @@ export const useGridstack = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
currentItem.shape = {
|
currentItem.shape[shapeSize] = {
|
||||||
location: {
|
location: {
|
||||||
x: addedNode.x ?? currentItem.shape.location.x,
|
x: addedNode.x ?? currentItem.shape[shapeSize].location.x,
|
||||||
y: addedNode.y ?? currentItem.shape.location.y,
|
y: addedNode.y ?? currentItem.shape[shapeSize].location.y,
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
width: addedNode.w ?? currentItem.shape.size.width,
|
width: addedNode.w ?? currentItem.shape[shapeSize].size.width,
|
||||||
height: addedNode.h ?? currentItem.shape.size.height,
|
height: addedNode.h ?? currentItem.shape[shapeSize].size.height,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -272,7 +231,7 @@ export const useGridstack = (
|
|||||||
: () => {};
|
: () => {};
|
||||||
|
|
||||||
// initialize the gridstack
|
// initialize the gridstack
|
||||||
useLayoutEffect(() => {
|
useEffect(() => {
|
||||||
initializeGridstack(
|
initializeGridstack(
|
||||||
areaType,
|
areaType,
|
||||||
wrapperRef,
|
wrapperRef,
|
||||||
@@ -282,13 +241,14 @@ export const useGridstack = (
|
|||||||
items,
|
items,
|
||||||
widgets ?? [],
|
widgets ?? [],
|
||||||
isEditMode,
|
isEditMode,
|
||||||
isLargerThanSm,
|
wrapperColumnCount,
|
||||||
|
shapeSize,
|
||||||
{
|
{
|
||||||
onChange,
|
onChange,
|
||||||
onAdd,
|
onAdd,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [items, wrapperRef.current, widgets]);
|
}, [items, wrapperRef.current, widgets, wrapperColumnCount]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
apps: items,
|
apps: items,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ScrollArea, Stack } from '@mantine/core';
|
import { Button, ScrollArea, Stack } from '@mantine/core';
|
||||||
import { useViewportSize } from '@mantine/hooks';
|
import { useViewportSize } from '@mantine/hooks';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useConfigContext } from '../../../config/provider';
|
import { useConfigContext } from '../../../config/provider';
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ interface UseConfigStoreType {
|
|||||||
updateConfig: (
|
updateConfig: (
|
||||||
name: string,
|
name: string,
|
||||||
updateCallback: (previous: ConfigType) => ConfigType,
|
updateCallback: (previous: ConfigType) => ConfigType,
|
||||||
shouldRegenerateGridstace?:
|
shouldRegenerateGridstack?:
|
||||||
| boolean
|
| boolean
|
||||||
| ((previousConfig: ConfigType, currentConfig: ConfigType) => boolean),
|
| ((previousConfig: ConfigType, currentConfig: ConfigType) => boolean),
|
||||||
shouldSaveConfigToFileSystem?: boolean
|
shouldSaveConfigToFileSystem?: boolean
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { useCallback, useEffect, useState, MutableRefObject } from 'react';
|
import { MutableRefObject, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const useResize = (myRef: MutableRefObject<HTMLDivElement | null>) => {
|
export const useResize = (myRef: MutableRefObject<HTMLDivElement | null>, dependencies: any[]) => {
|
||||||
const [width, setWidth] = useState(0);
|
const [width, setWidth] = useState(0);
|
||||||
const [height, setHeight] = useState(0);
|
const [height, setHeight] = useState(0);
|
||||||
|
|
||||||
const handleResize = useCallback(() => {
|
const handleResize = useCallback(() => {
|
||||||
setWidth(myRef.current?.offsetWidth ?? 0);
|
if (!myRef.current) return;
|
||||||
setHeight(myRef.current?.offsetHeight ?? 0);
|
setWidth(myRef.current.offsetWidth);
|
||||||
|
setHeight(myRef.current.offsetHeight);
|
||||||
}, [myRef]);
|
}, [myRef]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -19,5 +20,9 @@ export const useResize = (myRef: MutableRefObject<HTMLDivElement | null>) => {
|
|||||||
};
|
};
|
||||||
}, [myRef, handleResize]);
|
}, [myRef, handleResize]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleResize();
|
||||||
|
}, [myRef, dependencies]);
|
||||||
|
|
||||||
return { width, height };
|
return { width, height };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -190,13 +190,35 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
location: {
|
sm: {
|
||||||
x: 0,
|
location: {
|
||||||
y: 0,
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: appTileDefinition.minWidth,
|
||||||
|
height: appTileDefinition.minHeight,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
size: {
|
md: {
|
||||||
width: appTileDefinition.minWidth,
|
location: {
|
||||||
height: appTileDefinition.minHeight,
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: appTileDefinition.minWidth,
|
||||||
|
height: appTileDefinition.minHeight,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lg: {
|
||||||
|
location: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: appTileDefinition.minWidth,
|
||||||
|
height: appTileDefinition.minHeight,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
integration: {
|
integration: {
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
@import 'fily-publish-gridstack/dist/gridstack.min.css';
|
@import 'fily-publish-gridstack/dist/gridstack.min.css';
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--gridstack-widget-width: 64;
|
||||||
|
--gridstack-column-count: 12;
|
||||||
|
}
|
||||||
|
|
||||||
.grid-stack-placeholder > .placeholder-content {
|
.grid-stack-placeholder > .placeholder-content {
|
||||||
background-color: rgb(248, 249, 250) !important;
|
background-color: rgb(248, 249, 250) !important;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -12,27 +17,59 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Styling for grid-stack main area
|
||||||
@for $i from 1 to 13 {
|
@for $i from 1 to 13 {
|
||||||
.grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: ($i / 12) * 100 + "%" }
|
.grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||||
.grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: ($i / 12) * 100 + "%" }
|
.grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||||
.grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: ($i / 12) * 100 + "%" }
|
.grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@for $i from 1 to 96 {
|
@for $i from 1 to 96 {
|
||||||
.grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: $i * 64 + "px" }
|
.grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: calc(#{$i}px * #{var(--gridstack-widget-width)}) }
|
||||||
.grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: $i * 64 + "px" }
|
.grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: calc(#{$i}px * #{var(--gridstack-widget-width)}) }
|
||||||
.grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: $i * 64 + "px" }
|
.grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc(#{$i}px * #{var(--gridstack-widget-width)}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@for $i from 1 to 13 {
|
@for $i from 1 to 13 {
|
||||||
.grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: ($i / 12) * 100 + "%" }
|
.grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: calc(100% / #{var(--gridstack-column-count)} * #{$i}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@for $i from 1 to 96 {
|
@for $i from 1 to 96 {
|
||||||
.grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: $i * 64 + "px" }
|
.grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc(#{$i}px * #{var(--gridstack-widget-width)}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-stack>.grid-stack-item {
|
||||||
|
min-width: calc(percentage(1) * #{var(--gridstack-widget-width)});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Styling for sidebar grid-stack elements
|
||||||
|
@for $i from 1 to 3 {
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-w="#{$i}"] { width: 128px * $i }
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-min-w="#{$i}"] { min-width: 128px * $i }
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-max-w="#{$i}"] { max-width: 128px * $i }
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 to 96 {
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-h="#{$i}"] { height: 128px * $i }
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-min-h="#{$i}"] { min-height: 128px * $i }
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-max-h="#{$i}"] { max-height: 128px * $i }
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 to 13 {
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-x="#{$i}"] { left: 128px * $i }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@for $i from 1 to 96 {
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item[gs-y="#{$i}"] { top: 128px * $i }
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-stack.grid-stack-sidebar>.grid-stack-item {
|
||||||
|
min-width: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// General gridstack styling
|
||||||
.grid-stack>.grid-stack-item>.grid-stack-item-content,
|
.grid-stack>.grid-stack-item>.grid-stack-item-content,
|
||||||
.grid-stack>.grid-stack-item>.placeholder-content {
|
.grid-stack>.grid-stack-item>.placeholder-content {
|
||||||
inset: 10px;
|
inset: 10px;
|
||||||
@@ -43,10 +80,6 @@
|
|||||||
right: 10px;
|
right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-stack>.grid-stack-item {
|
|
||||||
min-width: (1/12)+'%';
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-stack > .grid-stack-item > .grid-stack-item-content {
|
.grid-stack > .grid-stack-item > .grid-stack-item-content {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
@@ -54,19 +87,3 @@
|
|||||||
.grid-stack.grid-stack-animate {
|
.grid-stack.grid-stack-animate {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
|
||||||
@for $i from 1 to 7 {
|
|
||||||
.grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: percentage(($i / 6)) !important }
|
|
||||||
.grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: percentage(($i / 6)) !important }
|
|
||||||
.grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: percentage(($i / 6)) !important }
|
|
||||||
}
|
|
||||||
|
|
||||||
@for $i from 1 to 7 {
|
|
||||||
.grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: percentage(($i / 6)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-stack>.grid-stack-item {
|
|
||||||
min-width: percentage(1/6) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -92,6 +92,17 @@ const getConfigAndCreateIfNotExsists = (
|
|||||||
return category;
|
return category;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getShapeForColumnCount = (index: number, columnCount: number) => ({
|
||||||
|
location: {
|
||||||
|
x: index % columnCount,
|
||||||
|
y: Math.floor(index / columnCount),
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const migrateService = (
|
const migrateService = (
|
||||||
oldService: serviceItem,
|
oldService: serviceItem,
|
||||||
serviceIndex: number,
|
serviceIndex: number,
|
||||||
@@ -117,13 +128,8 @@ const migrateService = (
|
|||||||
},
|
},
|
||||||
area: areaType,
|
area: areaType,
|
||||||
shape: {
|
shape: {
|
||||||
location: {
|
lg: getShapeForColumnCount(serviceIndex, 12),
|
||||||
x: (serviceIndex * 3) % 18,
|
md: getShapeForColumnCount(serviceIndex, 6),
|
||||||
y: Math.floor(serviceIndex / 6) * 3,
|
sm: getShapeForColumnCount(serviceIndex, 3),
|
||||||
},
|
|
||||||
size: {
|
|
||||||
width: 3,
|
|
||||||
height: 3,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
export interface ShapeType {
|
export interface ShapeType {
|
||||||
|
lg?: SizedShapeType;
|
||||||
|
md?: SizedShapeType;
|
||||||
|
sm?: SizedShapeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SizedShapeType {
|
||||||
location: {
|
location: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const definition = defineWidget({
|
|||||||
},
|
},
|
||||||
gridstack: {
|
gridstack: {
|
||||||
minWidth: 2,
|
minWidth: 2,
|
||||||
minHeight: 2,
|
minHeight: 1,
|
||||||
maxWidth: 12,
|
maxWidth: 12,
|
||||||
maxHeight: 12,
|
maxHeight: 12,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const definition = defineWidget({
|
|||||||
},
|
},
|
||||||
gridstack: {
|
gridstack: {
|
||||||
minWidth: 2,
|
minWidth: 2,
|
||||||
minHeight: 2,
|
minHeight: 1,
|
||||||
maxWidth: 12,
|
maxWidth: 12,
|
||||||
maxHeight: 12,
|
maxHeight: 12,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user