From afe3e2fc398daec4beec8a2c92e1099a3c21abe7 Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Tue, 3 Jan 2023 16:40:15 +0100 Subject: [PATCH 01/15] =?UTF-8?q?=F0=9F=90=9B=20Made=20aspect=20ratio=201?= =?UTF-8?q?=20to=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 294 +++++++++--------- .../Wrappers/gridstack/use-gridstack.ts | 11 +- src/hooks/use-resize.ts | 4 + src/styles/global.scss | 12 +- 4 files changed, 169 insertions(+), 152 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index 2a5cd2b09..7a2b6bb23 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -18,87 +18,12 @@ ], "apps": [ { - "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", - "name": "Small app", + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 0, - "y": 2 - }, - "size": { - "width": 2, - "height": 2 - } - }, - "integration": { - "type": null, - "properties": [] - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", - "name": "Discord", - "url": "https://discord.com/invite/aCsmEV5RgA", - "behaviour": { - "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", - "isOpeningNewTab": true, - "externalUrl": "https://discord.com/invite/aCsmEV5RgA" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 0, - "y": 4 - }, - "size": { - "width": 4, - "height": 3 - } - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", - "name": "Donate", - "url": "https://ko-fi.com/ajnart", - "behaviour": { - "onClickUrl": "https://ko-fi.com/ajnart", - "externalUrl": "https://ko-fi.com/ajnart", + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", "isOpeningNewTab": true }, "network": { @@ -108,26 +33,26 @@ ] }, "appearance": { - "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" + "iconUrl": "/imgs/logo/logo.png" }, "integration": { "type": null, "properties": [] }, "area": { - "type": "category", + "type": "wrapper", "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + "id": "default" } }, "shape": { "location": { - "x": 2, - "y": 2 + "x": 0, + "y": 11 }, "size": { - "width": 2, - "height": 2 + "width": 4, + "height": 3 } } }, @@ -155,7 +80,7 @@ "shape": { "location": { "x": 8, - "y": 12 + "y": 10 }, "size": { "width": 4, @@ -167,6 +92,42 @@ "properties": [] } }, + { + "id": "5df743d9-5cb1-457c-85d2-64ff86855652", + "name": "Your app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "wrapper", + "properties": { + "id": "default" + } + }, + "shape": { + "location": { + "x": 0, + "y": 7 + }, + "size": { + "width": 6, + "height": 4 + } + }, + "integration": { + "type": null, + "properties": [] + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", "name": "Contribute", @@ -198,56 +159,17 @@ "shape": { "location": { "x": 4, - "y": 2 + "y": 0 }, "size": { - "width": 2, - "height": 2 + "width": 1, + "height": 1 } } }, { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "location": { - "x": 8, - "y": 9 - }, - "size": { - "width": 4, - "height": 3 - } - } - }, - { - "id": "5df743d9-5cb1-457c-85d2-64ff86855652", - "name": "Your app", + "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", + "name": "Small app", "url": "https://homarr.dev", "appearance": { "iconUrl": "/imgs/logo/logo.png" @@ -261,32 +183,111 @@ "externalUrl": "https://homarr.dev" }, "area": { - "type": "wrapper", + "type": "category", "properties": { - "id": "default" + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" } }, "shape": { "location": { - "x": 0, - "y": 7 + "x": 5, + "y": 0 }, "size": { - "width": 6, - "height": 4 + "width": 1, + "height": 1 } }, "integration": { "type": null, "properties": [] } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", + "name": "Discord", + "url": "https://discord.com/invite/aCsmEV5RgA", + "behaviour": { + "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", + "isOpeningNewTab": true, + "externalUrl": "https://discord.com/invite/aCsmEV5RgA" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 6, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", + "name": "Donate", + "url": "https://ko-fi.com/ajnart", + "behaviour": { + "onClickUrl": "https://ko-fi.com/ajnart", + "externalUrl": "https://ko-fi.com/ajnart", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 7, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } } ], "widgets": [ { - "id": "date", + "id": "weather", "properties": { - "display24HourFormat": true + "displayInFahrenheit": false, + "location": "Paris" }, "area": { "type": "category", @@ -300,30 +301,29 @@ "y": 0 }, "size": { - "width": 6, + "width": 2, "height": 2 } } }, { - "id": "weather", + "id": "date", "properties": { - "displayInFahrenheit": false, - "location": "Paris" + "display24HourFormat": true }, "area": { - "type": "wrapper", + "type": "category", "properties": { - "id": "default" + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" } }, "shape": { "location": { - "x": 0, + "x": 2, "y": 0 }, "size": { - "width": 4, + "width": 2, "height": 2 } } diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index b6e7fbf64..f56cec96d 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -43,7 +43,8 @@ export const useGridstack = ( // reference of the gridstack object for modifications after initialization const gridRef = useRef(); // width of the wrapper (updating on page resize) - const { width, height } = useResize(wrapperRef); + const { width } = useResize(wrapperRef); + const root: HTMLHtmlElement = useMemo(() => document.querySelector(':root')!, []); const items = useMemo( () => @@ -127,6 +128,14 @@ export const useGridstack = ( ); }, [isLargerThanSm]); + useEffect(() => { + if (width === 0) return; + const widgetWidth = width / (isLargerThanSm ? 12 : 6); + // widget width is used to define sizes of gridstack items within global.scss + root.style.setProperty('--gridstack-widget-width', widgetWidth.toString()); + gridRef.current?.cellHeight(widgetWidth); + }, [width, isLargerThanSm]); + const onChange = isEditMode ? (changedNode: GridStackNode) => { if (!configName) return; diff --git a/src/hooks/use-resize.ts b/src/hooks/use-resize.ts index 68a2d7a52..ca425277a 100644 --- a/src/hooks/use-resize.ts +++ b/src/hooks/use-resize.ts @@ -19,5 +19,9 @@ export const useResize = (myRef: MutableRefObject) => { }; }, [myRef, handleResize]); + useEffect(() => { + handleResize(); + }, [myRef]); + return { width, height }; }; diff --git a/src/styles/global.scss b/src/styles/global.scss index 4db17a0a3..76121cc0e 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -1,5 +1,9 @@ @import 'fily-publish-gridstack/dist/gridstack.min.css'; +:root { + --gridstack-widget-width: 64; +} + .grid-stack-placeholder > .placeholder-content { background-color: rgb(248, 249, 250) !important; border-radius: 12px; @@ -19,9 +23,9 @@ } @for $i from 1 to 96 { - .grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: $i * 64 + "px" } - .grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: $i * 64 + "px" } - .grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: $i * 64 + "px" } + .grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: calc($i * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: calc($i * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc($i * #{var(--gridstack-widget-width)}) } } @for $i from 1 to 13 { @@ -30,7 +34,7 @@ @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 * #{var(--gridstack-widget-width)}) } } .grid-stack>.grid-stack-item>.grid-stack-item-content, From d439ba1842bba6e3097f76b03b9c940f9569051d Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Wed, 4 Jan 2023 19:06:19 +0100 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=90=9B=20Improve=20resposivnes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 316 +++++++++--------- .../Wrappers/gridstack/init-gridstack.ts | 4 +- .../Wrappers/gridstack/use-gridstack.ts | 78 ++++- src/styles/global.scss | 18 +- src/widgets/date/DateTile.tsx | 2 +- src/widgets/weather/WeatherTile.tsx | 2 +- 6 files changed, 244 insertions(+), 176 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index 7a2b6bb23..6e713cc61 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -17,81 +17,6 @@ } ], "apps": [ - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "location": { - "x": 0, - "y": 11 - }, - "size": { - "width": 4, - "height": 3 - } - } - }, - { - "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": 10 - }, - "size": { - "width": 4, - "height": 2 - } - }, - "integration": { - "type": null, - "properties": [] - } - }, { "id": "5df743d9-5cb1-457c-85d2-64ff86855652", "name": "Your app", @@ -128,6 +53,42 @@ "properties": [] } }, + { + "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", + "name": "Small app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 4, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "integration": { + "type": null, + "properties": [] + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", "name": "Contribute", @@ -158,7 +119,7 @@ }, "shape": { "location": { - "x": 4, + "x": 7, "y": 0 }, "size": { @@ -167,42 +128,6 @@ } } }, - { - "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", - "name": "Small app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 5, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "integration": { - "type": null, - "properties": [] - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", "name": "Discord", @@ -242,6 +167,45 @@ } } }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "wrapper", + "properties": { + "id": "default" + } + }, + "shape": { + "location": { + "x": 0, + "y": 11 + }, + "size": { + "width": 4, + "height": 3 + } + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", "name": "Donate", @@ -272,7 +236,7 @@ }, "shape": { "location": { - "x": 7, + "x": 5, "y": 0 }, "size": { @@ -280,54 +244,45 @@ "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": "wrapper", + "properties": { + "id": "default" + } + }, + "shape": { + "location": { + "x": 8, + "y": 10 + }, + "size": { + "width": 4, + "height": 2 + } + }, + "integration": { + "type": null, + "properties": [] + } } ], "widgets": [ - { - "id": "weather", - "properties": { - "displayInFahrenheit": false, - "location": "Paris" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 2, - "height": 2 - } - } - }, - { - "id": "date", - "properties": { - "display24HourFormat": true - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 2, - "y": 0 - }, - "size": { - "width": 2, - "height": 2 - } - } - }, { "id": "calendar", "properties": { @@ -349,6 +304,51 @@ "height": 5 } } + }, + { + "id": "weather", + "properties": { + "displayInFahrenheit": false, + "location": "Paris" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 4, + "height": 1 + } + } + }, + { + "id": "date", + "properties": { + "display24HourFormat": true + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 8, + "y": 0 + }, + "size": { + "width": 4, + "height": 1 + } + } } ], "settings": { diff --git a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts index 8d1bcd0a3..3ae5923cc 100644 --- a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts @@ -12,7 +12,7 @@ export const initializeGridstack = ( items: AppType[], widgets: IWidget[], isEditMode: boolean, - isLargerThanSm: boolean, + wrapperColumnCount: 3 | 6 | 12, events: { onChange: (changedNode: GridStackNode) => void; onAdd: (addedNode: GridStackNode) => void; @@ -20,7 +20,7 @@ export const initializeGridstack = ( ) => { if (!wrapperRef.current) return; // calculates the currently available count of columns - const columnCount = areaType === 'sidebar' ? 4 : isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6; + const columnCount = areaType === 'sidebar' ? 4 : wrapperColumnCount; const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64); // initialize gridstack const newGrid = gridRef; diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index f56cec96d..7b9cfc8fe 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -28,11 +28,18 @@ interface UseGristackReturnType { }; } +const useWrapperColumnCount = () => { + const isLargerThanSm = useScreenLargerThan('sm'); + const isLargerThanXl = useScreenLargerThan('xl'); + + return typeof isLargerThanXl === 'undefined' || isLargerThanXl ? 12 : isLargerThanSm ? 6 : 3; +}; + export const useGridstack = ( areaType: 'wrapper' | 'category' | 'sidebar', areaId: string ): UseGristackReturnType => { - const isLargerThanSm = useScreenLargerThan('sm'); + const wrapperColumnCount = useWrapperColumnCount(); const isEditMode = useEditModeStore((x) => x.enabled); const { config, configVersion, name: configName } = useConfigContext(); const updateConfig = useConfigStore((x) => x.updateConfig); @@ -82,59 +89,104 @@ export const useGridstack = ( useEffect(() => { if (areaType === 'sidebar') return; gridRef.current?.column( - isLargerThanSm || typeof isLargerThanSm === 'undefined' ? 12 : 6, + wrapperColumnCount, (column, prevColumn, newNodes, nodes) => { let nextRow = 0; - let available = 6; + let available = column; if (column === prevColumn) { newNodes.concat(nodes); return; } - nodes.reverse().forEach((node) => { + const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number => parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10); + + const getGridstackAttributes = (node: GridStackNode) => ({ + width: getGridstackAttribute(node, 'w'), + height: getGridstackAttribute(node, 'h'), + x: getGridstackAttribute(node, 'x'), + y: getGridstackAttribute(node, 'y'), + }); + + const sortNodes = (a: GridStackNode, b: GridStackNode) => { + const aAttributes = getGridstackAttributes(a); + const bAttributes = getGridstackAttributes(b); + + const differenceY = aAttributes.y - bAttributes.y; + + return differenceY !== 0 ? differenceY : aAttributes.x - bAttributes.x; + }; + + const sorted = nodes.sort(sortNodes); + + console.log(sorted); + + sorted.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); + const moveYDown = 1; - if (column === 6) { + if (column === 3) { + newnode.x = available >= width ? 3 - available : 0; + newnode.y = available === 3 || available >= width ? nextRow : nextRow += moveYDown; + + if (width > 3) { + newnode.w = 3; + nextRow += moveYDown; + available = 3; + } else if (available >= width) { + available -= width; + if (available === 0) { + nextRow += moveYDown; + available = 3; + } + } else if (available < width) { + newnode.y = newnode.y! + moveYDown; + available = 3 - width; + nextRow += moveYDown; + } + } else if (column === 6) { newnode.x = available >= width ? 6 - available : 0; newnode.y = nextRow; if (width > 6) { newnode.w = 6; - nextRow += 2; + nextRow += moveYDown; available = 6; } else if (available >= width) { available -= width; if (available === 0) { - nextRow += 2; + nextRow += moveYDown; available = 6; } } else if (available < width) { - newnode.y = newnode.y! + 2; + newnode.y = newnode.y! + moveYDown; available = 6 - width; - nextRow += 2; + nextRow += moveYDown; } } else { newnode.x = y % 2 === 1 ? x + 6 : x; newnode.y = Math.floor(y / 2); } + + console.log(newnode); + newNodes.push(newnode); }); } ); - }, [isLargerThanSm]); + }, [wrapperColumnCount]); useEffect(() => { if (width === 0) return; - const widgetWidth = width / (isLargerThanSm ? 12 : 6); + const widgetWidth = width / wrapperColumnCount; // widget width is used to define sizes of gridstack items within global.scss root.style.setProperty('--gridstack-widget-width', widgetWidth.toString()); gridRef.current?.cellHeight(widgetWidth); - }, [width, isLargerThanSm]); + }, [width, wrapperColumnCount]); const onChange = isEditMode ? (changedNode: GridStackNode) => { @@ -291,7 +343,7 @@ export const useGridstack = ( items, widgets ?? [], isEditMode, - isLargerThanSm, + wrapperColumnCount, { onChange, onAdd, diff --git a/src/styles/global.scss b/src/styles/global.scss index 76121cc0e..5c605a390 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -59,7 +59,7 @@ transition: none; } -@media screen and (max-width: 768px) { +@media screen and (max-width: 1400px) { @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 } @@ -73,4 +73,20 @@ .grid-stack>.grid-stack-item { min-width: percentage(1/6) !important; } +} + +@media screen and (max-width: 768px) { + @for $i from 1 to 4 { + .grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: percentage(($i / 3)) !important } + .grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: percentage(($i / 3)) !important } + .grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: percentage(($i / 3)) !important } + } + + @for $i from 1 to 4 { + .grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: percentage(($i / 3)) } + } + + .grid-stack>.grid-stack-item { + min-width: percentage(1/3) !important; + } } \ No newline at end of file diff --git a/src/widgets/date/DateTile.tsx b/src/widgets/date/DateTile.tsx index 653f813c5..843dc2564 100644 --- a/src/widgets/date/DateTile.tsx +++ b/src/widgets/date/DateTile.tsx @@ -17,7 +17,7 @@ const definition = defineWidget({ }, gridstack: { minWidth: 2, - minHeight: 2, + minHeight: 1, maxWidth: 12, maxHeight: 12, }, diff --git a/src/widgets/weather/WeatherTile.tsx b/src/widgets/weather/WeatherTile.tsx index 677d4bd6c..b053a0a2a 100644 --- a/src/widgets/weather/WeatherTile.tsx +++ b/src/widgets/weather/WeatherTile.tsx @@ -20,7 +20,7 @@ const definition = defineWidget({ }, gridstack: { minWidth: 2, - minHeight: 2, + minHeight: 1, maxWidth: 12, maxHeight: 12, }, From 6f8f0e90cb8fba0d0ad6441e1306b56643f7020a Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Thu, 5 Jan 2023 06:01:54 +0100 Subject: [PATCH 03/15] wip: improve responsive scaling --- data/configs/default.json | 318 +++++++++--------- .../Wrappers/gridstack/column-sorting.ts | 88 +++++ .../Wrappers/gridstack/use-gridstack.ts | 91 +++-- 3 files changed, 307 insertions(+), 190 deletions(-) create mode 100644 src/components/Dashboard/Wrappers/gridstack/column-sorting.ts diff --git a/data/configs/default.json b/data/configs/default.json index 6e713cc61..660013340 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -17,156 +17,6 @@ } ], "apps": [ - { - "id": "5df743d9-5cb1-457c-85d2-64ff86855652", - "name": "Your app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "location": { - "x": 0, - "y": 7 - }, - "size": { - "width": 6, - "height": 4 - } - }, - "integration": { - "type": null, - "properties": [] - } - }, - { - "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", - "name": "Small app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 4, - "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": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/github.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 7, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", - "name": "Discord", - "url": "https://discord.com/invite/aCsmEV5RgA", - "behaviour": { - "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", - "isOpeningNewTab": true, - "externalUrl": "https://discord.com/invite/aCsmEV5RgA" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 6, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", "name": "Documentation", @@ -236,13 +86,163 @@ }, "shape": { "location": { - "x": 5, + "x": 6, + "y": 1 + }, + "size": { + "width": 1, + "height": 2 + } + } + }, + { + "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", + "name": "Small app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 4, "y": 0 }, "size": { "width": 1, "height": 1 } + }, + "integration": { + "type": null, + "properties": [] + } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", + "name": "Discord", + "url": "https://discord.com/invite/aCsmEV5RgA", + "behaviour": { + "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", + "isOpeningNewTab": true, + "externalUrl": "https://discord.com/invite/aCsmEV5RgA" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 5, + "y": 2 + }, + "size": { + "width": 1, + "height": 2 + } + } + }, + { + "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/walkxhub/dashboard-icons/png/github.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "location": { + "x": 7, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + }, + { + "id": "5df743d9-5cb1-457c-85d2-64ff86855652", + "name": "Your app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "wrapper", + "properties": { + "id": "default" + } + }, + "shape": { + "location": { + "x": 0, + "y": 7 + }, + "size": { + "width": 6, + "height": 4 + } + }, + "integration": { + "type": null, + "properties": [] } }, { @@ -269,7 +269,7 @@ "shape": { "location": { "x": 8, - "y": 10 + "y": 9 }, "size": { "width": 4, @@ -297,10 +297,10 @@ "shape": { "location": { "x": 0, - "y": 2 + "y": 0 }, "size": { - "width": 5, + "width": 3, "height": 5 } } @@ -323,8 +323,8 @@ "y": 0 }, "size": { - "width": 4, - "height": 1 + "width": 3, + "height": 2 } } }, @@ -341,12 +341,12 @@ }, "shape": { "location": { - "x": 8, + "x": 9, "y": 0 }, "size": { - "width": 4, - "height": 1 + "width": 3, + "height": 2 } } } diff --git a/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts b/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts new file mode 100644 index 000000000..659fa4928 --- /dev/null +++ b/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts @@ -0,0 +1,88 @@ +import { GridStackNode } from 'fily-publish-gridstack'; + +type GridstackColumnSortingFn = ( + column: number, + prevColumn: number, + newNodes: GridStackNode[], + nodes: GridStackNode[] +) => void; + +const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number => + parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10); + +const getGridstackAttributes = (node: GridStackNode) => ({ + width: getGridstackAttribute(node, 'w'), + height: getGridstackAttribute(node, 'h'), + x: getGridstackAttribute(node, 'x'), + y: getGridstackAttribute(node, 'y'), +}); + +type Type = ReturnType & { node: GridStackNode }; + +const nextItem = (start: number, end: number, nodes: Type[]): number => { + const next = nodes + .filter((x) => x.y <= end && x.y + x.height - 1 > end) + .sort((a, b) => a.y + a.height - (b.y + b.height)) + .at(0); + if (!next) return end; + return nextItem(start, next.height - 1 + next.y, nodes); +}; + +const nextRowHeight = (nodes: Type[], values: number[], current = 0) => { + const item = nodes.find((x) => x.y >= current); + if (!item) return; + const next = nextItem(item.y, item.y + item.height - 1, nodes); + values.push(next + 1 - item.y); + nextRowHeight(nodes, values, next); +}; + +const getRowHeights = (nodes: Type[]) => { + const rowHeights: number[] = []; + nextRowHeight( + nodes, + rowHeights + ); + return rowHeights; +}; + +const sortNodesByYAndX = (a: GridStackNode, b: GridStackNode) => { + const aAttributes = getGridstackAttributes(a); + const bAttributes = getGridstackAttributes(b); + + const differenceY = aAttributes.y - bAttributes.y; + + return differenceY !== 0 ? differenceY : aAttributes.x - bAttributes.x; +}; + +export const commonColumnSorting: GridstackColumnSortingFn = ( + column, + prevColumn, + newNodes, + nodes +) => { + if (column === prevColumn) { + newNodes.concat(nodes); + return; + } + + let nextRow = 0; + let available = column; + + const sortedNodes = nodes.sort(sortNodesByYAndX); + const mappedNodes = sortedNodes.map((node) => ({ + ...getGridstackAttributes(node), + node, + })); + const rowHeights = getRowHeights(mappedNodes); + + const rowItems: Type[][] = []; + + // TODO: fix issue with spaces between. + let rowTotal = 0; + rowHeights.forEach(height => { + rowItems.push(mappedNodes.filter(node => node.y >= rowTotal && node.y < rowTotal + height)); + rowTotal += height; + }); + + console.log(rowItems); +}; diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index 7b9cfc8fe..2d05233eb 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -16,8 +16,46 @@ import { AppType } from '../../../../types/app'; import { AreaType } from '../../../../types/area'; import { IWidget } from '../../../../widgets/widgets'; import { useEditModeStore } from '../../Views/useEditModeStore'; +import { commonColumnSorting } from './column-sorting'; import { initializeGridstack } from './init-gridstack'; +const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number => parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10); + + const getGridstackAttributes = (node: GridStackNode) => ({ + width: getGridstackAttribute(node, 'w'), + height: getGridstackAttribute(node, 'h'), + x: getGridstackAttribute(node, 'x'), + y: getGridstackAttribute(node, 'y'), + }); + +type Type = (ReturnType & { node: GridStackNode }); + +const nextItem = (start: number, end: number, nodes: Type[]): number => { + const next = nodes + .filter(x => x.y <= end && x.y + x.height - 1 > end) + .sort((a, b) => (a.y + a.height) - (b.y + b.height)) + .at(0); +if (!next) return end; +return nextItem(start, next.height - 1 + next.y, nodes); +}; + +const nextRowHeight = (nodes: Type[], values: number[], current = 0) => { + const item = nodes.find(x => x.y >= current); + if (!item) return; + const next = nextItem(item.y, item.y + item.height - 1, nodes); + values.push(next + 1 - item.y); + nextRowHeight(nodes, values, next); +}; + +const getRowHeights = (nodes: GridStackNode[]) => { + const rowHeights: number[] = []; + nextRowHeight(nodes.map((node) => ({ + ...getGridstackAttributes(node), + node, + })), rowHeights); + return rowHeights; +}; + interface UseGristackReturnType { apps: AppType[]; widgets: IWidget[]; @@ -90,24 +128,16 @@ export const useGridstack = ( if (areaType === 'sidebar') return; gridRef.current?.column( wrapperColumnCount, - (column, prevColumn, newNodes, nodes) => { + /*(column, prevColumn, newNodes, nodes) => { let nextRow = 0; let available = column; + let maxHeightInRow = 1; if (column === prevColumn) { newNodes.concat(nodes); return; } - const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number => parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10); - - const getGridstackAttributes = (node: GridStackNode) => ({ - width: getGridstackAttribute(node, 'w'), - height: getGridstackAttribute(node, 'h'), - x: getGridstackAttribute(node, 'x'), - y: getGridstackAttribute(node, 'y'), - }); - const sortNodes = (a: GridStackNode, b: GridStackNode) => { const aAttributes = getGridstackAttributes(a); const bAttributes = getGridstackAttributes(b); @@ -117,36 +147,39 @@ export const useGridstack = ( return differenceY !== 0 ? differenceY : aAttributes.x - bAttributes.x; }; - const sorted = nodes.sort(sortNodes); + const sortedNodes = nodes.sort(sortNodes); + const rowHeights = getRowHeights(sortedNodes); - console.log(sorted); - - sorted.forEach((node) => { + sortedNodes.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); - const moveYDown = 1; + maxHeightInRow = height > maxHeightInRow ? height : maxHeightInRow; + + const continueInNextRow = () => { + nextRow += maxHeightInRow; + maxHeightInRow = 1; + available = column; + return nextRow; + }; if (column === 3) { newnode.x = available >= width ? 3 - available : 0; - newnode.y = available === 3 || available >= width ? nextRow : nextRow += moveYDown; + newnode.y = available === 3 || available >= width ? nextRow : continueInNextRow(); if (width > 3) { newnode.w = 3; - nextRow += moveYDown; - available = 3; + continueInNextRow(); } else if (available >= width) { available -= width; if (available === 0) { - nextRow += moveYDown; - available = 3; + continueInNextRow(); } } else if (available < width) { - newnode.y = newnode.y! + moveYDown; + newnode.y = continueInNextRow(); available = 3 - width; - nextRow += moveYDown; } } else if (column === 6) { newnode.x = available >= width ? 6 - available : 0; @@ -154,29 +187,25 @@ export const useGridstack = ( if (width > 6) { newnode.w = 6; - nextRow += moveYDown; - available = 6; + continueInNextRow(); } else if (available >= width) { available -= width; if (available === 0) { - nextRow += moveYDown; - available = 6; + continueInNextRow(); } } else if (available < width) { - newnode.y = newnode.y! + moveYDown; + newnode.y = continueInNextRow(); available = 6 - width; - nextRow += moveYDown; } } else { newnode.x = y % 2 === 1 ? x + 6 : x; newnode.y = Math.floor(y / 2); } - console.log(newnode); - newNodes.push(newnode); }); - } + }*/ + commonColumnSorting ); }, [wrapperColumnCount]); From 26bcb2fc347264bf02b58c112db6f7ff50ccdbab Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Thu, 5 Jan 2023 18:00:10 +0100 Subject: [PATCH 04/15] wip: column sorting --- .../Wrappers/gridstack/column-sorting.ts | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts b/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts index 659fa4928..7d958e7ee 100644 --- a/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts +++ b/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts @@ -28,20 +28,31 @@ const nextItem = (start: number, end: number, nodes: Type[]): number => { return nextItem(start, next.height - 1 + next.y, nodes); }; -const nextRowHeight = (nodes: Type[], values: number[], current = 0) => { +const nextRowHeight = ( + nodes: Type[], + values: { height: number; items: Type[] }[], + maxHeight: number, + current = 0 +) => { const item = nodes.find((x) => x.y >= current); if (!item) return; + if (current < item.y) { + values.push({ height: item.y - current, items: [] }); + } const next = nextItem(item.y, item.y + item.height - 1, nodes); - values.push(next + 1 - item.y); - nextRowHeight(nodes, values, next); + values.push({ + height: next + 1 - item.y, + items: nodes.filter((x) => x.y >= current - 2 && x.y + x.height <= current + next + 1 - item.y), + }); + nextRowHeight(nodes, values, maxHeight, next + 1); }; const getRowHeights = (nodes: Type[]) => { - const rowHeights: number[] = []; - nextRowHeight( - nodes, - rowHeights - ); + const maxHeightElement = nodes.sort((a, b) => a.y + a.height - (b.y + b.height)).at(-1); + if (!maxHeightElement) return []; + const maxHeight = maxHeightElement.height + maxHeightElement.y; + const rowHeights: { height: number; items: Type[] }[] = []; + nextRowHeight(nodes, rowHeights, maxHeight); return rowHeights; }; @@ -79,10 +90,10 @@ export const commonColumnSorting: GridstackColumnSortingFn = ( // TODO: fix issue with spaces between. let rowTotal = 0; - rowHeights.forEach(height => { - rowItems.push(mappedNodes.filter(node => node.y >= rowTotal && node.y < rowTotal + height)); + rowHeights.forEach(({ height }) => { + rowItems.push(mappedNodes.filter((node) => node.y >= rowTotal && node.y < rowTotal + height)); rowTotal += height; }); - console.log(rowItems); + console.log(rowHeights); }; From 9608452bede645dd7746fe551eeafd4acf126372 Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Fri, 6 Jan 2023 22:46:07 +0100 Subject: [PATCH 05/15] =?UTF-8?q?=E2=9C=A8=20Implemented=20different=20lay?= =?UTF-8?q?out=20sizes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 514 +++++++++++++----- .../ChangeWidgetPositionModal.tsx | 12 +- .../WidgetsTab/WidgetElementType.tsx | 34 +- .../Dashboard/Tiles/Widgets/WidgetsMenu.tsx | 6 +- .../Dashboard/Views/DashboardView.tsx | 28 +- .../Dashboard/Wrappers/Category/Category.tsx | 1 - .../Dashboard/Wrappers/WrapperContent.tsx | 13 +- .../Wrappers/gridstack/column-sorting.ts | 99 ---- .../Wrappers/gridstack/init-gridstack.ts | 17 +- .../Dashboard/Wrappers/gridstack/store.tsx | 31 ++ .../Wrappers/gridstack/use-gridstack.ts | 176 +----- .../Actions/Docker/ContainerActionBar.tsx | 34 +- src/hooks/use-resize.ts | 4 +- src/styles/global.scss | 51 +- src/tools/config/migrateConfig.ts | 22 +- src/types/shape.ts | 6 + 16 files changed, 564 insertions(+), 484 deletions(-) delete mode 100644 src/components/Dashboard/Wrappers/gridstack/column-sorting.ts create mode 100644 src/components/Dashboard/Wrappers/gridstack/store.tsx diff --git a/data/configs/default.json b/data/configs/default.json index 660013340..d4f9186a6 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -17,84 +17,6 @@ } ], "apps": [ - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "location": { - "x": 0, - "y": 11 - }, - "size": { - "width": 4, - "height": 3 - } - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", - "name": "Donate", - "url": "https://ko-fi.com/ajnart", - "behaviour": { - "onClickUrl": "https://ko-fi.com/ajnart", - "externalUrl": "https://ko-fi.com/ajnart", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "location": { - "x": 6, - "y": 1 - }, - "size": { - "width": 1, - "height": 2 - } - } - }, { "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", "name": "Small app", @@ -117,13 +39,35 @@ } }, "shape": { - "location": { - "x": 4, - "y": 0 + "md": { + "location": { + "x": 1, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } }, - "size": { - "width": 1, - "height": 1 + "sm": { + "location": { + "x": 0, + "y": 3 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 4, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } } }, "integration": { @@ -160,13 +104,35 @@ } }, "shape": { - "location": { - "x": 5, - "y": 2 + "md": { + "location": { + "x": 0, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } }, - "size": { - "width": 1, - "height": 2 + "sm": { + "location": { + "x": 1, + "y": 2 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 4, + "height": 3 + } } } }, @@ -199,50 +165,158 @@ } }, "shape": { - "location": { - "x": 7, - "y": 0 + "md": { + "location": { + "x": 3, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } }, - "size": { - "width": 1, - "height": 1 + "sm": { + "location": { + "x": 1, + "y": 4 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 4, + "y": 2 + }, + "size": { + "width": 1, + "height": 1 + } } } }, { - "id": "5df743d9-5cb1-457c-85d2-64ff86855652", - "name": "Your app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", + "name": "Donate", + "url": "https://ko-fi.com/ajnart", + "behaviour": { + "onClickUrl": "https://ko-fi.com/ajnart", + "externalUrl": "https://ko-fi.com/ajnart", + "isOpeningNewTab": true }, "network": { "enabledStatusChecker": false, - "okStatus": [] + "okStatus": [ + 200 + ] }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "location": { - "x": 0, - "y": 7 - }, - "size": { - "width": 6, - "height": 4 - } + "appearance": { + "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" }, "integration": { "type": null, "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 2, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 2, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 5, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + } } }, { @@ -267,13 +341,93 @@ } }, "shape": { - "location": { - "x": 8, - "y": 9 + "md": { + "location": { + "x": 0, + "y": 6 + }, + "size": { + "width": 1, + "height": 1 + } }, - "size": { - "width": 4, - "height": 2 + "sm": { + "location": { + "x": 0, + "y": 6 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 6 + }, + "size": { + "width": 1, + "height": 1 + } + } + }, + "integration": { + "type": null, + "properties": [] + } + }, + { + "id": "5df743d9-5cb1-457c-85d2-64ff86855652", + "name": "Your app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 3, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 2, + "y": 3 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 6, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } } }, "integration": { @@ -295,13 +449,35 @@ } }, "shape": { - "location": { - "x": 0, - "y": 0 + "md": { + "location": { + "x": 0, + "y": 1 + }, + "size": { + "width": 3, + "height": 5 + } }, - "size": { - "width": 3, - "height": 5 + "sm": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 5 + } + }, + "lg": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 5 + } } } }, @@ -318,13 +494,35 @@ } }, "shape": { - "location": { - "x": 0, - "y": 0 + "md": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 1 + } }, - "size": { - "width": 3, - "height": 2 + "sm": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "lg": { + "location": { + "x": 4, + "y": 1 + }, + "size": { + "width": 3, + "height": 1 + } } } }, @@ -340,13 +538,35 @@ } }, "shape": { - "location": { - "x": 9, - "y": 0 + "sm": { + "location": { + "x": 0, + "y": 1 + }, + "size": { + "width": 3, + "height": 1 + } }, - "size": { - "width": 3, - "height": 2 + "md": { + "location": { + "x": 4, + "y": 0 + }, + "size": { + "width": 2, + "height": 2 + } + }, + "lg": { + "location": { + "x": 5, + "y": 2 + }, + "size": { + "width": 2, + "height": 1 + } } } } @@ -360,8 +580,8 @@ }, "customization": { "layout": { - "enabledLeftSidebar": false, - "enabledRightSidebar": false, + "enabledLeftSidebar": true, + "enabledRightSidebar": true, "enabledDocker": false, "enabledPing": false, "enabledSearchbar": true diff --git a/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx b/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx index 6e1d92f95..fb384c951 100644 --- a/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx +++ b/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx @@ -4,6 +4,7 @@ import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; import widgets from '../../../../widgets'; import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu'; +import { useGridstackStore } from '../../Wrappers/gridstack/store'; import { ChangePositionModal } from './ChangePositionModal'; export const ChangeWidgetPositionModal = ({ @@ -13,6 +14,7 @@ export const ChangeWidgetPositionModal = ({ }: ContextModalProps) => { const { name: configName } = useConfigContext(); const updateConfig = useConfigStore((x) => x.updateConfig); + const shapeSize = useGridstackStore(x => x.currentShapeSize); const handleSubmit = (x: number, y: number, width: number, height: number) => { if (!configName) { @@ -23,7 +25,7 @@ export const ChangeWidgetPositionModal = ({ configName, (prev) => { const currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId); - currentWidget!.shape = { + currentWidget!.shape[shapeSize] = { location: { x, y, @@ -57,10 +59,10 @@ export const ChangeWidgetPositionModal = ({ onCancel={handleCancel} heightData={heightData} widthData={widthData} - initialX={innerProps.widget.shape.location.x} - initialY={innerProps.widget.shape.location.y} - initialWidth={innerProps.widget.shape.size.width} - initialHeight={innerProps.widget.shape.size.height} + initialX={innerProps.widget.shape[shapeSize].location.x} + initialY={innerProps.widget.shape[shapeSize].location.y} + initialWidth={innerProps.widget.shape[shapeSize].size.width} + initialHeight={innerProps.widget.shape[shapeSize].size.height} /> ); }; diff --git a/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx b/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx index 01caf6c8b..abb890ece 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/WidgetsTab/WidgetElementType.tsx @@ -46,13 +46,35 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement }, }, shape: { - location: { - x: 0, - y: 0, + sm: { + location: { + x: 0, + y: 0, + }, + size: { + width: widget.gridstack.minWidth, + height: widget.gridstack.minHeight, + }, }, - size: { - width: widget.gridstack.minWidth, - height: widget.gridstack.minHeight, + md: { + location: { + 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, + }, }, }, }, diff --git a/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx b/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx index ced815e2f..37b9768b9 100644 --- a/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx +++ b/src/components/Dashboard/Tiles/Widgets/WidgetsMenu.tsx @@ -2,6 +2,7 @@ import { Title } from '@mantine/core'; import { useTranslation } from 'next-i18next'; import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions'; import { IWidget } from '../../../../widgets/widgets'; +import { useWrapperColumnCount } from '../../Wrappers/gridstack/store'; import { GenericTileMenu } from '../GenericTileMenu'; import { WidgetEditModalInnerProps } from './WidgetsEditModal'; import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal'; @@ -9,6 +10,7 @@ import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal'; export type WidgetChangePositionModalInnerProps = { widgetId: string; widget: IWidget; + wrapperColumnCount: number; }; interface WidgetsMenuProps { @@ -18,8 +20,9 @@ interface WidgetsMenuProps { export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => { const { t } = useTranslation(`modules/${integration}`); + const wrapperColumnCount = useWrapperColumnCount(); - if (!widget) return null; + if (!widget || !wrapperColumnCount) return null; const handleDeleteClick = () => { openContextModalGeneric({ @@ -39,6 +42,7 @@ export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => { innerProps: { widgetId: integration, widget, + wrapperColumnCount, }, }); }; diff --git a/src/components/Dashboard/Views/DashboardView.tsx b/src/components/Dashboard/Views/DashboardView.tsx index cacb40263..ae2388017 100644 --- a/src/components/Dashboard/Views/DashboardView.tsx +++ b/src/components/Dashboard/Views/DashboardView.tsx @@ -1,10 +1,12 @@ -import { Group, Stack } from '@mantine/core'; -import { useMemo } from 'react'; +import { Center, Group, Loader, Stack } from '@mantine/core'; +import { useEffect, useMemo, useRef } from 'react'; import { useConfigContext } from '../../../config/provider'; +import { useResize } from '../../../hooks/use-resize'; import { useScreenSmallerThan } from '../../../hooks/useScreenSmallerThan'; import { CategoryType } from '../../../types/category'; import { WrapperType } from '../../../types/wrapper'; import { DashboardCategory } from '../Wrappers/Category/Category'; +import { useGridstackStore } from '../Wrappers/gridstack/store'; import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar'; import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper'; @@ -12,14 +14,26 @@ export const DashboardView = () => { const wrappers = useWrapperItems(); const layoutSettings = useConfigContext()?.config?.settings.customization.layout; const doNotShowSidebar = useScreenSmallerThan('md'); + const notReady = typeof doNotShowSidebar === 'undefined'; + const mainAreaRef = useRef(null); + const { width } = useResize(mainAreaRef, [doNotShowSidebar]); + const setMainAreaWidth = useGridstackStore(x => x.setMainAreaWidth); + const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth); + + useEffect(() => { + setMainAreaWidth(width); + }, [width]); return ( - {layoutSettings?.enabledLeftSidebar && !doNotShowSidebar ? ( + {notReady ?
+ +
: <> + {layoutSettings?.enabledLeftSidebar && !doNotShowSidebar && mainAreaWidth ? ( ) : null} - - {wrappers.map((item) => + + {!mainAreaWidth ? null : wrappers.map((item) => item.type === 'category' ? ( ) : ( @@ -27,9 +41,11 @@ export const DashboardView = () => { ) )} - {layoutSettings?.enabledRightSidebar && !doNotShowSidebar ? ( + {layoutSettings?.enabledRightSidebar && !doNotShowSidebar && mainAreaWidth ? ( ) : null} + +}
); }; diff --git a/src/components/Dashboard/Wrappers/Category/Category.tsx b/src/components/Dashboard/Wrappers/Category/Category.tsx index 89da45c00..9034a2e1a 100644 --- a/src/components/Dashboard/Wrappers/Category/Category.tsx +++ b/src/components/Dashboard/Wrappers/Category/Category.tsx @@ -22,7 +22,6 @@ export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
diff --git a/src/components/Dashboard/Wrappers/WrapperContent.tsx b/src/components/Dashboard/Wrappers/WrapperContent.tsx index 579bbd7c8..b846d2979 100644 --- a/src/components/Dashboard/Wrappers/WrapperContent.tsx +++ b/src/components/Dashboard/Wrappers/WrapperContent.tsx @@ -6,6 +6,7 @@ import { IWidget, IWidgetDefinition } from '../../../widgets/widgets'; import { WidgetWrapper } from '../../../widgets/WidgetWrapper'; import { appTileDefinition } from '../Tiles/Apps/AppTile'; import { GridstackTileWrapper } from '../Tiles/TileWrapper'; +import { useGridstackStore } from './gridstack/store'; interface WrapperContentProps { apps: AppType[]; @@ -18,6 +19,10 @@ interface WrapperContentProps { } export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) { + const shapeSize = useGridstackStore(x => x.currentShapeSize); + + if (!shapeSize) return null; + return ( <> {apps?.map((app) => { @@ -29,8 +34,8 @@ export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) { key={app.id} itemRef={refs.items.current[app.id]} {...tile} - {...app.shape.location} - {...app.shape.size} + {...app.shape[shapeSize]?.location} + {...app.shape[shapeSize]?.size} > @@ -49,8 +54,8 @@ export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) { itemRef={refs.items.current[widget.id]} id={definition.id} {...definition.gridstack} - {...widget.shape.location} - {...widget.shape.size} + {...widget.shape[shapeSize]?.location} + {...widget.shape[shapeSize]?.size} > diff --git a/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts b/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts deleted file mode 100644 index 7d958e7ee..000000000 --- a/src/components/Dashboard/Wrappers/gridstack/column-sorting.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { GridStackNode } from 'fily-publish-gridstack'; - -type GridstackColumnSortingFn = ( - column: number, - prevColumn: number, - newNodes: GridStackNode[], - nodes: GridStackNode[] -) => void; - -const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number => - parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10); - -const getGridstackAttributes = (node: GridStackNode) => ({ - width: getGridstackAttribute(node, 'w'), - height: getGridstackAttribute(node, 'h'), - x: getGridstackAttribute(node, 'x'), - y: getGridstackAttribute(node, 'y'), -}); - -type Type = ReturnType & { node: GridStackNode }; - -const nextItem = (start: number, end: number, nodes: Type[]): number => { - const next = nodes - .filter((x) => x.y <= end && x.y + x.height - 1 > end) - .sort((a, b) => a.y + a.height - (b.y + b.height)) - .at(0); - if (!next) return end; - return nextItem(start, next.height - 1 + next.y, nodes); -}; - -const nextRowHeight = ( - nodes: Type[], - values: { height: number; items: Type[] }[], - maxHeight: number, - current = 0 -) => { - const item = nodes.find((x) => x.y >= current); - if (!item) return; - if (current < item.y) { - values.push({ height: item.y - current, items: [] }); - } - const next = nextItem(item.y, item.y + item.height - 1, nodes); - values.push({ - height: next + 1 - item.y, - items: nodes.filter((x) => x.y >= current - 2 && x.y + x.height <= current + next + 1 - item.y), - }); - nextRowHeight(nodes, values, maxHeight, next + 1); -}; - -const getRowHeights = (nodes: Type[]) => { - const maxHeightElement = nodes.sort((a, b) => a.y + a.height - (b.y + b.height)).at(-1); - if (!maxHeightElement) return []; - const maxHeight = maxHeightElement.height + maxHeightElement.y; - const rowHeights: { height: number; items: Type[] }[] = []; - nextRowHeight(nodes, rowHeights, maxHeight); - return rowHeights; -}; - -const sortNodesByYAndX = (a: GridStackNode, b: GridStackNode) => { - const aAttributes = getGridstackAttributes(a); - const bAttributes = getGridstackAttributes(b); - - const differenceY = aAttributes.y - bAttributes.y; - - return differenceY !== 0 ? differenceY : aAttributes.x - bAttributes.x; -}; - -export const commonColumnSorting: GridstackColumnSortingFn = ( - column, - prevColumn, - newNodes, - nodes -) => { - if (column === prevColumn) { - newNodes.concat(nodes); - return; - } - - let nextRow = 0; - let available = column; - - const sortedNodes = nodes.sort(sortNodesByYAndX); - const mappedNodes = sortedNodes.map((node) => ({ - ...getGridstackAttributes(node), - node, - })); - const rowHeights = getRowHeights(mappedNodes); - - const rowItems: Type[][] = []; - - // TODO: fix issue with spaces between. - let rowTotal = 0; - rowHeights.forEach(({ height }) => { - rowItems.push(mappedNodes.filter((node) => node.y >= rowTotal && node.y < rowTotal + height)); - rowTotal += height; - }); - - console.log(rowHeights); -}; diff --git a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts index 3ae5923cc..c3a739468 100644 --- a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts @@ -20,7 +20,7 @@ export const initializeGridstack = ( ) => { if (!wrapperRef.current) return; // calculates the currently available count of columns - const columnCount = areaType === 'sidebar' ? 4 : wrapperColumnCount; + const columnCount = areaType === 'sidebar' ? 2 : wrapperColumnCount; const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64); // initialize gridstack const newGrid = gridRef; @@ -35,11 +35,14 @@ export const initializeGridstack = ( disableOneColumnMode: true, staticGrid: !isEditMode, minRow, + animate: false, }, // selector of the gridstack item (it's eather category or wrapper) `.grid-stack-${areaType}[data-${areaType}='${areaId}']` ); 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 grid.on('change', (_, el) => { @@ -60,12 +63,16 @@ export const initializeGridstack = ( grid.batchUpdate(); grid.removeAll(false); items.forEach( - ({ id }) => - itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement) + ({ id }) => { + const item = itemRefs.current[id]?.current; + item && grid.makeWidget(item as HTMLDivElement); + } ); widgets.forEach( - ({ id }) => - itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement) + ({ id }) => { + const item = itemRefs.current[id]?.current; + item && grid.makeWidget(item as HTMLDivElement); + } ); grid.batchUpdate(false); }; diff --git a/src/components/Dashboard/Wrappers/gridstack/store.tsx b/src/components/Dashboard/Wrappers/gridstack/store.tsx new file mode 100644 index 000000000..881c0ee39 --- /dev/null +++ b/src/components/Dashboard/Wrappers/gridstack/store.tsx @@ -0,0 +1,31 @@ +import { useMantineTheme } from '@mantine/core'; +import create from 'zustand'; + +export const useGridstackStore = create((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'; +} diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index 2d05233eb..5f5b74a5f 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -3,58 +3,17 @@ import { createRef, MutableRefObject, RefObject, - useEffect, - useLayoutEffect, - useMemo, + useEffect, useMemo, useRef, } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; -import { useResize } from '../../../../hooks/use-resize'; -import { useScreenLargerThan } from '../../../../hooks/useScreenLargerThan'; import { AppType } from '../../../../types/app'; import { AreaType } from '../../../../types/area'; import { IWidget } from '../../../../widgets/widgets'; import { useEditModeStore } from '../../Views/useEditModeStore'; -import { commonColumnSorting } from './column-sorting'; import { initializeGridstack } from './init-gridstack'; - -const getGridstackAttribute = (node: GridStackNode, path: 'x' | 'y' | 'w' | 'h'): number => parseInt(node.el!.getAttribute(`data-gridstack-${path}`)!, 10); - - const getGridstackAttributes = (node: GridStackNode) => ({ - width: getGridstackAttribute(node, 'w'), - height: getGridstackAttribute(node, 'h'), - x: getGridstackAttribute(node, 'x'), - y: getGridstackAttribute(node, 'y'), - }); - -type Type = (ReturnType & { node: GridStackNode }); - -const nextItem = (start: number, end: number, nodes: Type[]): number => { - const next = nodes - .filter(x => x.y <= end && x.y + x.height - 1 > end) - .sort((a, b) => (a.y + a.height) - (b.y + b.height)) - .at(0); -if (!next) return end; -return nextItem(start, next.height - 1 + next.y, nodes); -}; - -const nextRowHeight = (nodes: Type[], values: number[], current = 0) => { - const item = nodes.find(x => x.y >= current); - if (!item) return; - const next = nextItem(item.y, item.y + item.height - 1, nodes); - values.push(next + 1 - item.y); - nextRowHeight(nodes, values, next); -}; - -const getRowHeights = (nodes: GridStackNode[]) => { - const rowHeights: number[] = []; - nextRowHeight(nodes.map((node) => ({ - ...getGridstackAttributes(node), - node, - })), rowHeights); - return rowHeights; -}; +import { useGridstackStore, useWrapperColumnCount } from './store'; interface UseGristackReturnType { apps: AppType[]; @@ -66,18 +25,10 @@ interface UseGristackReturnType { }; } -const useWrapperColumnCount = () => { - const isLargerThanSm = useScreenLargerThan('sm'); - const isLargerThanXl = useScreenLargerThan('xl'); - - return typeof isLargerThanXl === 'undefined' || isLargerThanXl ? 12 : isLargerThanSm ? 6 : 3; -}; - export const useGridstack = ( areaType: 'wrapper' | 'category' | 'sidebar', areaId: string ): UseGristackReturnType => { - const wrapperColumnCount = useWrapperColumnCount(); const isEditMode = useEditModeStore((x) => x.enabled); const { config, configVersion, name: configName } = useConfigContext(); const updateConfig = useConfigStore((x) => x.updateConfig); @@ -87,10 +38,14 @@ export const useGridstack = ( const itemRefs = useRef>>({}); // reference of the gridstack object for modifications after initialization const gridRef = useRef(); + const wrapperColumnCount = useWrapperColumnCount(); + const shapeSize = useGridstackStore(x => x.currentShapeSize); + const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth); // width of the wrapper (updating on page resize) - const { width } = 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( () => config?.apps.filter( @@ -123,99 +78,14 @@ export const useGridstack = ( }); } - // change column count depending on the width and the gridRef useEffect(() => { - if (areaType === 'sidebar') return; - gridRef.current?.column( - wrapperColumnCount, - /*(column, prevColumn, newNodes, nodes) => { - let nextRow = 0; - let available = column; - let maxHeightInRow = 1; - - if (column === prevColumn) { - newNodes.concat(nodes); - return; - } - - const sortNodes = (a: GridStackNode, b: GridStackNode) => { - const aAttributes = getGridstackAttributes(a); - const bAttributes = getGridstackAttributes(b); - - const differenceY = aAttributes.y - bAttributes.y; - - return differenceY !== 0 ? differenceY : aAttributes.x - bAttributes.x; - }; - - const sortedNodes = nodes.sort(sortNodes); - const rowHeights = getRowHeights(sortedNodes); - - sortedNodes.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); - maxHeightInRow = height > maxHeightInRow ? height : maxHeightInRow; - - const continueInNextRow = () => { - nextRow += maxHeightInRow; - maxHeightInRow = 1; - available = column; - return nextRow; - }; - - if (column === 3) { - newnode.x = available >= width ? 3 - available : 0; - newnode.y = available === 3 || available >= width ? nextRow : continueInNextRow(); - - if (width > 3) { - newnode.w = 3; - continueInNextRow(); - } else if (available >= width) { - available -= width; - if (available === 0) { - continueInNextRow(); - } - } else if (available < width) { - newnode.y = continueInNextRow(); - available = 3 - width; - } - } else if (column === 6) { - newnode.x = available >= width ? 6 - available : 0; - newnode.y = nextRow; - - if (width > 6) { - newnode.w = 6; - continueInNextRow(); - } else if (available >= width) { - available -= width; - if (available === 0) { - continueInNextRow(); - } - } else if (available < width) { - newnode.y = continueInNextRow(); - available = 6 - width; - } - } else { - newnode.x = y % 2 === 1 ? x + 6 : x; - newnode.y = Math.floor(y / 2); - } - - newNodes.push(newnode); - }); - }*/ - commonColumnSorting - ); - }, [wrapperColumnCount]); - - useEffect(() => { - if (width === 0) return; - const widgetWidth = width / wrapperColumnCount; + const widgetWidth = mainAreaWidth / wrapperColumnCount; // widget width is used to define sizes of gridstack items within global.scss + // TODO: improve root.style.setProperty('--gridstack-widget-width', widgetWidth.toString()); + root.style.setProperty('--gridstack-column-count', wrapperColumnCount.toString()); gridRef.current?.cellHeight(widgetWidth); - }, [width, wrapperColumnCount]); + }, [mainAreaWidth, wrapperColumnCount]); const onChange = isEditMode ? (changedNode: GridStackNode) => { @@ -233,14 +103,14 @@ export const useGridstack = ( : previous.widgets.find((x) => x.id === itemId); if (!currentItem) return previous; - currentItem.shape = { + currentItem.shape[shapeSize] = { location: { - x: changedNode.x ?? currentItem.shape.location.x, - y: changedNode.y ?? currentItem.shape.location.y, + x: changedNode.x ?? currentItem.shape[shapeSize].location.x, + y: changedNode.y ?? currentItem.shape[shapeSize].location.y, }, size: { - width: changedNode.w ?? currentItem.shape.size.width, - height: changedNode.h ?? currentItem.shape.size.height, + width: changedNode.w ?? currentItem.shape[shapeSize].size.width, + height: changedNode.h ?? currentItem.shape[shapeSize].size.height, }, }; @@ -299,14 +169,14 @@ export const useGridstack = ( }; } - currentItem.shape = { + currentItem.shape[shapeSize] = { location: { - x: addedNode.x ?? currentItem.shape.location.x, - y: addedNode.y ?? currentItem.shape.location.y, + x: addedNode.x ?? currentItem.shape[shapeSize].location.x, + y: addedNode.y ?? currentItem.shape[shapeSize].location.y, }, size: { - width: addedNode.w ?? currentItem.shape.size.width, - height: addedNode.h ?? currentItem.shape.size.height, + width: addedNode.w ?? currentItem.shape[shapeSize].size.width, + height: addedNode.h ?? currentItem.shape[shapeSize].size.height, }, }; @@ -362,7 +232,7 @@ export const useGridstack = ( : () => {}; // initialize the gridstack - useLayoutEffect(() => { + useEffect(() => { initializeGridstack( areaType, wrapperRef, @@ -378,7 +248,7 @@ export const useGridstack = ( onAdd, } ); - }, [items, wrapperRef.current, widgets]); + }, [items, wrapperRef.current, widgets, wrapperColumnCount]); return { apps: items, diff --git a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx b/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx index d24662c89..1b59ebd69 100644 --- a/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx +++ b/src/components/layout/header/Actions/Docker/ContainerActionBar.tsx @@ -184,13 +184,35 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction }, }, shape: { - location: { - x: 0, - y: 0, + lg: { + location: { + x: 0, + y: 0, + }, + size: { + height: 1, + width: 1, + }, }, - size: { - height: 1, - width: 1, + md: { + location: { + x: 0, + y: 0, + }, + size: { + height: 1, + width: 1, + }, + }, + sm: { + location: { + x: 0, + y: 0, + }, + size: { + height: 1, + width: 1, + }, }, }, integration: { diff --git a/src/hooks/use-resize.ts b/src/hooks/use-resize.ts index ca425277a..6f479348d 100644 --- a/src/hooks/use-resize.ts +++ b/src/hooks/use-resize.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState, MutableRefObject } from 'react'; -export const useResize = (myRef: MutableRefObject) => { +export const useResize = (myRef: MutableRefObject, dependencies: any[]) => { const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); @@ -21,7 +21,7 @@ export const useResize = (myRef: MutableRefObject) => { useEffect(() => { handleResize(); - }, [myRef]); + }, [myRef, dependencies]); return { width, height }; }; diff --git a/src/styles/global.scss b/src/styles/global.scss index 5c605a390..0f84d11a9 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -2,6 +2,7 @@ :root { --gridstack-widget-width: 64; + --gridstack-column-count: 12; } .grid-stack-placeholder > .placeholder-content { @@ -17,24 +18,24 @@ } @for $i from 1 to 13 { - .grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: ($i / 12) * 100 + "%" } - .grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: ($i / 12) * 100 + "%" } - .grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-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: calc(100% / #{var(--gridstack-column-count)} * #{$i}) } + .grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: calc(100% / #{var(--gridstack-column-count)} * #{$i}) } } @for $i from 1 to 96 { - .grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: calc($i * #{var(--gridstack-widget-width)}) } - .grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: calc($i * #{var(--gridstack-widget-width)}) } - .grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc($i * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: calc(#{$i} * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: calc(#{$i} * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc(#{$i} * #{var(--gridstack-widget-width)}) } } @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 { - .grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc($i * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc(#{$i} * #{var(--gridstack-widget-width)}) } } .grid-stack>.grid-stack-item>.grid-stack-item-content, @@ -48,7 +49,7 @@ } .grid-stack>.grid-stack-item { - min-width: (1/12)+'%'; + min-width: calc(percentage(1) * #{var(--gridstack-widget-width)}); } .grid-stack > .grid-stack-item > .grid-stack-item-content { @@ -58,35 +59,3 @@ .grid-stack.grid-stack-animate { transition: none; } - -@media screen and (max-width: 1400px) { - @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; - } -} - -@media screen and (max-width: 768px) { - @for $i from 1 to 4 { - .grid-stack>.grid-stack-item[gs-w="#{$i}"] { width: percentage(($i / 3)) !important } - .grid-stack>.grid-stack-item[gs-min-w="#{$i}"] { min-width: percentage(($i / 3)) !important } - .grid-stack>.grid-stack-item[gs-max-w="#{$i}"] { max-width: percentage(($i / 3)) !important } - } - - @for $i from 1 to 4 { - .grid-stack>.grid-stack-item[gs-x="#{$i}"] { left: percentage(($i / 3)) } - } - - .grid-stack>.grid-stack-item { - min-width: percentage(1/3) !important; - } -} \ No newline at end of file diff --git a/src/tools/config/migrateConfig.ts b/src/tools/config/migrateConfig.ts index 9648c1fd1..13d811523 100644 --- a/src/tools/config/migrateConfig.ts +++ b/src/tools/config/migrateConfig.ts @@ -92,6 +92,17 @@ const getConfigAndCreateIfNotExsists = ( return category; }; +const getShapeForColumnCount = (index: number, columnCount: number) => ({ + location: { + x: index % columnCount, + y: Math.floor(index / columnCount), + }, + size: { + width: 1, + height: 1, + }, +}); + const migrateService = ( oldService: serviceItem, serviceIndex: number, @@ -117,13 +128,8 @@ const migrateService = ( }, area: areaType, shape: { - location: { - x: (serviceIndex * 3) % 18, - y: Math.floor(serviceIndex / 6) * 3, - }, - size: { - width: 3, - height: 3, - }, + lg: getShapeForColumnCount(serviceIndex, 12), + md: getShapeForColumnCount(serviceIndex, 6), + sm: getShapeForColumnCount(serviceIndex, 3), }, }); diff --git a/src/types/shape.ts b/src/types/shape.ts index 0bc27df0e..a8a6e6823 100644 --- a/src/types/shape.ts +++ b/src/types/shape.ts @@ -1,4 +1,10 @@ export interface ShapeType { + lg?: SizedShapeType; + md?: SizedShapeType; + sm?: SizedShapeType; +} + +export interface SizedShapeType { location: { x: number; y: number; From d26128af0ef96d46380b9564415b6d8465a1ce8f Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Fri, 6 Jan 2023 23:50:08 +0100 Subject: [PATCH 06/15] =?UTF-8?q?=F0=9F=90=9B=20Sidebars=20not=20working?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 548 +++++++++--------- .../Ribbon/MobileRibbonSidebarDrawer.tsx | 2 +- .../Dashboard/Views/DashboardView.tsx | 9 +- .../Dashboard/Wrappers/Sidebar/Sidebar.tsx | 45 +- .../Wrappers/gridstack/init-gridstack.ts | 20 +- .../Wrappers/gridstack/use-gridstack.ts | 1 + src/hooks/use-resize.ts | 15 +- src/styles/global.scss | 36 +- 8 files changed, 366 insertions(+), 310 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index d4f9186a6..07fe28621 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -18,8 +18,69 @@ ], "apps": [ { - "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", - "name": "Small app", + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "lg": { + "location": { + "x": 3, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "id": "5df743d9-5cb1-457c-85d2-64ff86855652", + "name": "Your app", "url": "https://homarr.dev", "appearance": { "iconUrl": "/imgs/logo/logo.png" @@ -41,7 +102,7 @@ "shape": { "md": { "location": { - "x": 1, + "x": 5, "y": 1 }, "size": { @@ -51,8 +112,8 @@ }, "sm": { "location": { - "x": 0, - "y": 3 + "x": 2, + "y": 0 }, "size": { "width": 1, @@ -61,7 +122,187 @@ }, "lg": { "location": { - "x": 4, + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + }, + "integration": { + "type": null, + "properties": [] + } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", + "name": "Donate", + "url": "https://ko-fi.com/ajnart", + "behaviour": { + "onClickUrl": "https://ko-fi.com/ajnart", + "externalUrl": "https://ko-fi.com/ajnart", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "sidebar", + "properties": { + "location": "left" + } + }, + "shape": { + "md": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "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/walkxhub/dashboard-icons/png/github.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 5, + "y": 3 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 2 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 2, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", + "name": "Small app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "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": { @@ -105,6 +346,16 @@ }, "shape": { "md": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 1, + "height": 4 + } + }, + "sm": { "location": { "x": 0, "y": 1 @@ -114,208 +365,15 @@ "height": 1 } }, - "sm": { - "location": { - "x": 1, - "y": 2 - }, - "size": { - "width": 1, - "height": 1 - } - }, "lg": { "location": { "x": 0, "y": 0 }, - "size": { - "width": 4, - "height": 3 - } - } - } - }, - { - "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/walkxhub/dashboard-icons/png/github.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 3, - "y": 1 - }, "size": { "width": 1, "height": 1 } - }, - "sm": { - "location": { - "x": 1, - "y": 4 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 4, - "y": 2 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", - "name": "Donate", - "url": "https://ko-fi.com/ajnart", - "behaviour": { - "onClickUrl": "https://ko-fi.com/ajnart", - "externalUrl": "https://ko-fi.com/ajnart", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 2, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 2, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 5, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "lg": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 2, - "height": 1 - } } } }, @@ -376,64 +434,6 @@ "type": null, "properties": [] } - }, - { - "id": "5df743d9-5cb1-457c-85d2-64ff86855652", - "name": "Your app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 3, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 2, - "y": 3 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 6, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - }, - "integration": { - "type": null, - "properties": [] - } } ], "widgets": [ @@ -496,11 +496,11 @@ "shape": { "md": { "location": { - "x": 0, + "x": 4, "y": 0 }, "size": { - "width": 3, + "width": 2, "height": 1 } }, @@ -516,11 +516,11 @@ }, "lg": { "location": { - "x": 4, + "x": 0, "y": 1 }, "size": { - "width": 3, + "width": 2, "height": 1 } } @@ -540,28 +540,28 @@ "shape": { "sm": { "location": { - "x": 0, + "x": 1, "y": 1 }, - "size": { - "width": 3, - "height": 1 - } - }, - "md": { - "location": { - "x": 4, - "y": 0 - }, "size": { "width": 2, "height": 2 } }, + "md": { + "location": { + "x": 4, + "y": 2 + }, + "size": { + "width": 2, + "height": 1 + } + }, "lg": { "location": { - "x": 5, - "y": 2 + "x": 2, + "y": 0 }, "size": { "width": 2, diff --git a/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx b/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx index b09749ea2..22fb2df77 100644 --- a/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx +++ b/src/components/Dashboard/Mobile/Ribbon/MobileRibbonSidebarDrawer.tsx @@ -20,6 +20,6 @@ export const MobileRibbonSidebarDrawer = ({ }} {...props} > - + ); diff --git a/src/components/Dashboard/Views/DashboardView.tsx b/src/components/Dashboard/Views/DashboardView.tsx index ae2388017..cce453ad1 100644 --- a/src/components/Dashboard/Views/DashboardView.tsx +++ b/src/components/Dashboard/Views/DashboardView.tsx @@ -21,6 +21,7 @@ export const DashboardView = () => { const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth); useEffect(() => { + if (width === 0) return; setMainAreaWidth(width); }, [width]); @@ -29,8 +30,8 @@ export const DashboardView = () => { {notReady ?
: <> - {layoutSettings?.enabledLeftSidebar && !doNotShowSidebar && mainAreaWidth ? ( - + {layoutSettings?.enabledLeftSidebar && !doNotShowSidebar ? ( + ) : null} {!mainAreaWidth ? null : wrappers.map((item) => @@ -41,8 +42,8 @@ export const DashboardView = () => { ) )} - {layoutSettings?.enabledRightSidebar && !doNotShowSidebar && mainAreaWidth ? ( - + {layoutSettings?.enabledRightSidebar && !doNotShowSidebar ? ( + ) : null} } diff --git a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx index cefbf3538..08a3af69a 100644 --- a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx +++ b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx @@ -3,16 +3,12 @@ import { RefObject } from 'react'; import { useGridstack } from '../gridstack/use-gridstack'; import { WrapperContent } from '../WrapperContent'; -interface DashboardSidebarProps { +interface DashboardSidebarProps extends DashboardSidebarInnerProps { location: 'right' | 'left'; + isGridstackReady: boolean; } -export const DashboardSidebar = ({ location }: DashboardSidebarProps) => { - const { refs, apps, widgets } = useGridstack('sidebar', location); - - const minRow = useMinRowForFullHeight(refs.wrapper); - - return ( +export const DashboardSidebar = ({ location, isGridstackReady }: DashboardSidebarProps) => ( { borderStyle: 'dashed', }} > -
- -
+ {isGridstackReady && }
); + + interface DashboardSidebarInnerProps { + location: 'right' | 'left'; + } + +// Is Required because of the gridstack main area width. +const SidebarInner = ({ location }: DashboardSidebarInnerProps) => { + const { refs, apps, widgets } = useGridstack('sidebar', location); + + const minRow = useMinRowForFullHeight(refs.wrapper); + + return ( +
+ +
); }; const useMinRowForFullHeight = (wrapperRef: RefObject) => - wrapperRef.current ? Math.floor(wrapperRef.current!.offsetHeight / 64) : 2; + wrapperRef.current ? Math.floor(wrapperRef.current!.offsetHeight / 128) : 2; diff --git a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts index c3a739468..f95d01fe1 100644 --- a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts @@ -1,6 +1,7 @@ import { GridStack, GridStackNode } from 'fily-publish-gridstack'; import { MutableRefObject, RefObject } from 'react'; import { AppType } from '../../../../types/app'; +import { ShapeType } from '../../../../types/shape'; import { IWidget } from '../../../../widgets/widgets'; export const initializeGridstack = ( @@ -13,6 +14,7 @@ export const initializeGridstack = ( widgets: IWidget[], isEditMode: boolean, wrapperColumnCount: 3 | 6 | 12, + shapeSize: 'sm' | 'md' | 'lg', events: { onChange: (changedNode: GridStackNode) => void; onAdd: (addedNode: GridStackNode) => void; @@ -21,14 +23,14 @@ export const initializeGridstack = ( if (!wrapperRef.current) return; // calculates the currently available count of columns const columnCount = areaType === 'sidebar' ? 2 : wrapperColumnCount; - const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64); + const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 128); // initialize gridstack const newGrid = gridRef; newGrid.current = GridStack.init( { column: columnCount, margin: 10, - cellHeight: 64, + cellHeight: 128, float: true, alwaysShowResizeHandle: 'mobile', acceptWidgets: true, @@ -63,16 +65,26 @@ export const initializeGridstack = ( grid.batchUpdate(); grid.removeAll(false); items.forEach( - ({ id }) => { + ({ id, shape }) => { const item = itemRefs.current[id]?.current; + setAttributesFromShape(item, shape[shapeSize]); item && grid.makeWidget(item as HTMLDivElement); } ); widgets.forEach( - ({ id }) => { + ({ id, shape }) => { const item = itemRefs.current[id]?.current; + setAttributesFromShape(item, shape[shapeSize]); item && grid.makeWidget(item as HTMLDivElement); } ); 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()); +} diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index 5f5b74a5f..5f0bcf4c4 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -243,6 +243,7 @@ export const useGridstack = ( widgets ?? [], isEditMode, wrapperColumnCount, + shapeSize, { onChange, onAdd, diff --git a/src/hooks/use-resize.ts b/src/hooks/use-resize.ts index 6f479348d..e9c47cf15 100644 --- a/src/hooks/use-resize.ts +++ b/src/hooks/use-resize.ts @@ -1,17 +1,22 @@ -import { useCallback, useEffect, useState, MutableRefObject } from 'react'; +import { MutableRefObject, useCallback, useEffect, useState } from 'react'; export const useResize = (myRef: MutableRefObject, dependencies: any[]) => { const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); const handleResize = useCallback(() => { - setWidth(myRef.current?.offsetWidth ?? 0); - setHeight(myRef.current?.offsetHeight ?? 0); + if (!myRef.current) return; + setWidth(myRef.current.offsetWidth); + setHeight(myRef.current.offsetHeight); }, [myRef]); useEffect(() => { - window.addEventListener('load', handleResize); - window.addEventListener('resize', handleResize); + window.addEventListener('load', () => + handleResize() + ); + window.addEventListener('resize', () => + handleResize() + ); return () => { window.removeEventListener('load', handleResize); diff --git a/src/styles/global.scss b/src/styles/global.scss index 0f84d11a9..026e01066 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -17,6 +17,7 @@ } } +// Styling for grid-stack main area @for $i from 1 to 13 { .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: calc(100% / #{var(--gridstack-column-count)} * #{$i}) } @@ -38,6 +39,37 @@ .grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc(#{$i} * #{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>.placeholder-content { inset: 10px; @@ -48,10 +80,6 @@ right: 10px; } -.grid-stack>.grid-stack-item { - min-width: calc(percentage(1) * #{var(--gridstack-widget-width)}); -} - .grid-stack > .grid-stack-item > .grid-stack-item-content { overflow-y: hidden; } From 81256d10982b934041fdd392feadbf33d5701e75 Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Fri, 6 Jan 2023 23:54:00 +0100 Subject: [PATCH 07/15] =?UTF-8?q?=F0=9F=90=9B=20Styling=20variables=20are?= =?UTF-8?q?=20changed=20to=20frequently=20sometimes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dashboard/Wrappers/gridstack/use-gridstack.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index 5f0bcf4c4..1e72380d3 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -81,12 +81,15 @@ export const useGridstack = ( useEffect(() => { const widgetWidth = mainAreaWidth / wrapperColumnCount; // widget width is used to define sizes of gridstack items within global.scss - // TODO: improve root.style.setProperty('--gridstack-widget-width', widgetWidth.toString()); - root.style.setProperty('--gridstack-column-count', wrapperColumnCount.toString()); gridRef.current?.cellHeight(widgetWidth); }, [mainAreaWidth, wrapperColumnCount]); + useEffect(() => { + // column count is used to define count of columns of gridstack within global.scss + root.style.setProperty('--gridstack-column-count', wrapperColumnCount.toString()); + }, [wrapperColumnCount]); + const onChange = isEditMode ? (changedNode: GridStackNode) => { if (!configName) return; From 48fa81aaadb2b3fb2df6710c71d818f29e1d6fe5 Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Sat, 7 Jan 2023 00:05:07 +0100 Subject: [PATCH 08/15] =?UTF-8?q?=F0=9F=8E=A8=20Refactored=20dashboard-vie?= =?UTF-8?q?w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 244 +++++++++--------- .../Dashboard/Views/DashboardView.tsx | 83 +++--- 2 files changed, 176 insertions(+), 151 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index 07fe28621..9836bed58 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -17,67 +17,6 @@ } ], "apps": [ - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "lg": { - "location": { - "x": 3, - "y": 1 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, { "id": "5df743d9-5cb1-457c-85d2-64ff86855652", "name": "Your app", @@ -136,67 +75,6 @@ "properties": [] } }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", - "name": "Donate", - "url": "https://ko-fi.com/ajnart", - "behaviour": { - "onClickUrl": "https://ko-fi.com/ajnart", - "externalUrl": "https://ko-fi.com/ajnart", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "sidebar", - "properties": { - "location": "left" - } - }, - "shape": { - "md": { - "location": { - "x": 1, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 1, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 1, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", "name": "Contribute", @@ -377,6 +255,128 @@ } } }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", + "name": "Donate", + "url": "https://ko-fi.com/ajnart", + "behaviour": { + "onClickUrl": "https://ko-fi.com/ajnart", + "externalUrl": "https://ko-fi.com/ajnart", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "sidebar", + "properties": { + "location": "left" + } + }, + "shape": { + "md": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "lg": { + "location": { + "x": 3, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, { "id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa", "name": "Your app", diff --git a/src/components/Dashboard/Views/DashboardView.tsx b/src/components/Dashboard/Views/DashboardView.tsx index cce453ad1..dac54a60c 100644 --- a/src/components/Dashboard/Views/DashboardView.tsx +++ b/src/components/Dashboard/Views/DashboardView.tsx @@ -2,7 +2,7 @@ import { Center, Group, Loader, Stack } from '@mantine/core'; import { useEffect, useMemo, useRef } from 'react'; import { useConfigContext } from '../../../config/provider'; import { useResize } from '../../../hooks/use-resize'; -import { useScreenSmallerThan } from '../../../hooks/useScreenSmallerThan'; +import { useScreenLargerThan } from '../../../hooks/useScreenLargerThan'; import { CategoryType } from '../../../types/category'; import { WrapperType } from '../../../types/wrapper'; import { DashboardCategory } from '../Wrappers/Category/Category'; @@ -12,11 +12,43 @@ import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper'; export const DashboardView = () => { const wrappers = useWrapperItems(); - const layoutSettings = useConfigContext()?.config?.settings.customization.layout; - const doNotShowSidebar = useScreenSmallerThan('md'); - const notReady = typeof doNotShowSidebar === 'undefined'; + const sidebarsVisible = useSidebarVisibility(); + const { isReady, mainAreaRef } = usePrepareGridstack(); + + return ( + + {sidebarsVisible.isLoading ? +
+ +
+ : + <> + {sidebarsVisible.left ? ( + + ) : null} + + + {!isReady ? null : wrappers.map((item) => + item.type === 'category' ? ( + + ) : ( + + ) + )} + + + {sidebarsVisible.right ? ( + + ) : null} + +} +
+ ); +}; + +const usePrepareGridstack = () => { const mainAreaRef = useRef(null); - const { width } = useResize(mainAreaRef, [doNotShowSidebar]); + const { width } = useResize(mainAreaRef, []); const setMainAreaWidth = useGridstackStore(x => x.setMainAreaWidth); const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth); @@ -25,30 +57,23 @@ export const DashboardView = () => { setMainAreaWidth(width); }, [width]); - return ( - - {notReady ?
- -
: <> - {layoutSettings?.enabledLeftSidebar && !doNotShowSidebar ? ( - - ) : null} - - {!mainAreaWidth ? null : wrappers.map((item) => - item.type === 'category' ? ( - - ) : ( - - ) - )} - - {layoutSettings?.enabledRightSidebar && !doNotShowSidebar ? ( - - ) : null} - -} -
- ); + 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 = () => { From 2da206d5b0056d1b1144612b850daffc671e0f6e Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Sat, 7 Jan 2023 09:19:02 +0100 Subject: [PATCH 09/15] =?UTF-8?q?=F0=9F=90=9B=20Fix=20issues=20with=20chan?= =?UTF-8?q?ge=20position=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 244 +++++++++--------- .../en/layout/modals/change-position.json | 2 +- .../ChangePosition/ChangeAppPositionModal.tsx | 49 ++-- .../ChangePosition/ChangePositionModal.tsx | 31 ++- .../ChangeWidgetPositionModal.tsx | 17 +- .../Wrappers/gridstack/use-gridstack.ts | 2 +- 6 files changed, 187 insertions(+), 158 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index 9836bed58..64b74e89c 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -194,67 +194,6 @@ "properties": [] } }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", - "name": "Discord", - "url": "https://discord.com/invite/aCsmEV5RgA", - "behaviour": { - "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", - "isOpeningNewTab": true, - "externalUrl": "https://discord.com/invite/aCsmEV5RgA" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 1, - "height": 4 - } - }, - "sm": { - "location": { - "x": 0, - "y": 1 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", "name": "Donate", @@ -316,67 +255,6 @@ } } }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "lg": { - "location": { - "x": 3, - "y": 1 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, { "id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa", "name": "Your app", @@ -434,6 +312,128 @@ "type": null, "properties": [] } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", + "name": "Discord", + "url": "https://discord.com/invite/aCsmEV5RgA", + "behaviour": { + "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", + "isOpeningNewTab": true, + "externalUrl": "https://discord.com/invite/aCsmEV5RgA" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 1, + "height": 3 + } + }, + "sm": { + "location": { + "x": 0, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 3 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "lg": { + "location": { + "x": 3, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + } + } } ], "widgets": [ diff --git a/public/locales/en/layout/modals/change-position.json b/public/locales/en/layout/modals/change-position.json index 49f39e563..464a676de 100644 --- a/public/locales/en/layout/modals/change-position.json +++ b/public/locales/en/layout/modals/change-position.json @@ -4,5 +4,5 @@ "height": "Height", "yPosition": "Y axis position", "zeroOrHigher": "0 or higher", - "betweenXandY": "Between {{mim}} and {{max}}" + "betweenXandY": "Between {{min}} and {{max}}" } \ No newline at end of file diff --git a/src/components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal.tsx b/src/components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal.tsx index 820223405..dc18afe0e 100644 --- a/src/components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal.tsx +++ b/src/components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal.tsx @@ -3,6 +3,7 @@ import { closeModal, ContextModalProps } from '@mantine/modals'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; import { AppType } from '../../../../types/app'; +import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store'; import { ChangePositionModal } from './ChangePositionModal'; type ChangeAppPositionModalInnerProps = { @@ -16,6 +17,9 @@ export const ChangeAppPositionModal = ({ }: ContextModalProps) => { const { name: configName } = useConfigContext(); 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) => { if (!configName) { @@ -28,7 +32,13 @@ export const ChangeAppPositionModal = ({ ...previousConfig, apps: [ ...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 @@ -49,28 +59,35 @@ export const ChangeAppPositionModal = ({ onCancel={handleCancel} widthData={widthData} heightData={heightData} - initialX={innerProps.app.shape.location.x} - initialY={innerProps.app.shape.location.y} - initialWidth={innerProps.app.shape.size.width} - initialHeight={innerProps.app.shape.size.height} + initialX={innerProps.app.shape[shapeSize]?.location.x} + initialY={innerProps.app.shape[shapeSize]?.location.y} + initialWidth={innerProps.app.shape[shapeSize]?.size.width} + initialHeight={innerProps.app.shape[shapeSize]?.size.height} /> ); }; -const useHeightData = (): SelectItem[] => - Array.from(Array(11).keys()).map((n) => { - const index = n + 1; - return { - value: index.toString(), - label: `${64 * index}px`, - }; - }); +const useHeightData = (): SelectItem[] => { + const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth); + const wrapperColumnCount = useWrapperColumnCount(); -const useWidthData = (): SelectItem[] => - Array.from(Array(11).keys()).map((n) => { + return Array.from(Array(11).keys()).map((n) => { const index = n + 1; return { 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)}%`, + }; + }); +}; diff --git a/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx b/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx index 3dfeb55d0..f8e704b51 100644 --- a/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx +++ b/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx @@ -4,10 +4,10 @@ import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../../../config/provider'; interface ChangePositionModalProps { - initialX: number; - initialY: number; - initialWidth: number; - initialHeight: number; + initialX?: number; + initialY?: number; + initialWidth?: number; + initialHeight?: number; widthData: SelectItem[]; heightData: SelectItem[]; onSubmit: (x: number, y: number, width: number, height: number) => void; @@ -28,10 +28,10 @@ export const ChangePositionModal = ({ const form = useForm({ initialValues: { - x: initialX, - y: initialY, - width: initialWidth, - height: initialHeight, + x: initialX ?? null, + y: initialY ?? null, + width: initialWidth?.toString() ?? '', + height: initialHeight?.toString() ?? '', }, validateInputOnChange: true, validateInputOnBlur: true, @@ -42,7 +42,12 @@ export const ChangePositionModal = ({ 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']); @@ -112,8 +117,8 @@ export const ChangePositionModal = ({ }; type FormType = { - x: number; - y: number; - width: number; - height: number; + x: number | null; + y: number | null; + width: string; + height: string; }; diff --git a/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx b/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx index fb384c951..4ad1db3db 100644 --- a/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx +++ b/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx @@ -4,7 +4,7 @@ import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; import widgets from '../../../../widgets'; import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu'; -import { useGridstackStore } from '../../Wrappers/gridstack/store'; +import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store'; import { ChangePositionModal } from './ChangePositionModal'; export const ChangeWidgetPositionModal = ({ @@ -68,23 +68,30 @@ export const ChangeWidgetPositionModal = ({ }; const useWidthData = (integration: string): SelectItem[] => { + const wrapperColumnCount = useWrapperColumnCount(); const currentWidget = widgets[integration as keyof typeof widgets]; if (!currentWidget) return []; const offset = currentWidget.gridstack.minWidth ?? 2; - const length = (currentWidget.gridstack.maxWidth ?? 12) - offset; - return Array.from({ length }, (_, i) => i + offset).map((n) => ({ + const length = (currentWidget.gridstack.maxWidth > wrapperColumnCount! + ? wrapperColumnCount! + : currentWidget.gridstack.maxWidth) - offset; + return Array.from({ length: length + 1 }, (_, i) => i + offset).map((n) => ({ 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 mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth); + const wrapperColumnCount = useWrapperColumnCount(); + const currentWidget = widgets[integration as keyof typeof widgets]; if (!currentWidget) return []; const offset = currentWidget.gridstack.minHeight ?? 2; const length = (currentWidget.gridstack.maxHeight ?? 12) - offset; return Array.from({ length }, (_, i) => i + offset).map((n) => ({ value: n.toString(), - label: `${64 * n}px`, + label: `${(mainAreaWidth! / wrapperColumnCount!) * n}px`, })); }; diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index 1e72380d3..e5227d33f 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -83,7 +83,7 @@ export const useGridstack = ( // widget width is used to define sizes of gridstack items within global.scss root.style.setProperty('--gridstack-widget-width', widgetWidth.toString()); gridRef.current?.cellHeight(widgetWidth); - }, [mainAreaWidth, wrapperColumnCount]); + }, [mainAreaWidth, wrapperColumnCount, gridRef.current]); useEffect(() => { // column count is used to define count of columns of gridstack within global.scss From 6e4938482e24c9a79a34b14f0a0e980b3f17dab9 Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Sat, 7 Jan 2023 09:23:10 +0100 Subject: [PATCH 10/15] =?UTF-8?q?=F0=9F=90=9B=20Fix=20issue=20with=20shape?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 448 ++++++++++-------- .../Overview/AvailableElementsOverview.tsx | 34 +- 2 files changed, 281 insertions(+), 201 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index 64b74e89c..bce515c88 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -18,70 +18,12 @@ ], "apps": [ { - "id": "5df743d9-5cb1-457c-85d2-64ff86855652", - "name": "Your app", + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 5, - "y": 1 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 2, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 1, - "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", + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", "isOpeningNewTab": true }, "network": { @@ -91,7 +33,7 @@ ] }, "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/github.png" + "iconUrl": "/imgs/logo/logo.png" }, "integration": { "type": null, @@ -106,27 +48,27 @@ "shape": { "md": { "location": { - "x": 5, - "y": 3 + "x": 0, + "y": 10 }, "size": { - "width": 1, + "width": 2, "height": 1 } }, "sm": { "location": { "x": 0, - "y": 2 + "y": 10 }, "size": { - "width": 1, + "width": 2, "height": 1 } }, "lg": { "location": { - "x": 2, + "x": 3, "y": 1 }, "size": { @@ -194,6 +136,64 @@ "properties": [] } }, + { + "id": "5df743d9-5cb1-457c-85d2-64ff86855652", + "name": "Your app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 2, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 2, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + }, + "integration": { + "type": null, + "properties": [] + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", "name": "Donate", @@ -255,64 +255,6 @@ } } }, - { - "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": { - "md": { - "location": { - "x": 0, - "y": 6 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 6 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 0, - "y": 6 - }, - "size": { - "width": 1, - "height": 1 - } - } - }, - "integration": { - "type": null, - "properties": [] - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", "name": "Discord", @@ -345,11 +287,11 @@ "md": { "location": { "x": 0, - "y": 0 + "y": 1 }, "size": { "width": 1, - "height": 3 + "height": 1 } }, "sm": { @@ -375,12 +317,70 @@ } }, { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", + "id": "615e43bd-f0aa-4117-ba49-b6495c039f3e", + "name": "Your app", "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "wrapper", + "properties": { + "id": "default" + } + }, + "shape": { + "sm": { + "location": { + "x": 0, + "y": 5 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "md": { + "location": { + "x": 0, + "y": 5 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 5 + }, + "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": { @@ -390,7 +390,7 @@ ] }, "appearance": { - "iconUrl": "/imgs/logo/logo.png" + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/github.png" }, "integration": { "type": null, @@ -402,6 +402,60 @@ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" } }, + "shape": { + "md": { + "location": { + "x": 2, + "y": 3 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 1, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 2, + "y": 3 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa", + "name": "Haha", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, "shape": { "md": { "location": { @@ -415,72 +469,32 @@ }, "sm": { "location": { - "x": 0, - "y": 10 + "x": 2, + "y": 2 }, "size": { - "width": 2, + "width": 1, "height": 1 } }, "lg": { "location": { - "x": 3, - "y": 1 + "x": 0, + "y": 6 }, "size": { "width": 1, "height": 1 } } + }, + "integration": { + "type": null, + "properties": [] } } ], "widgets": [ - { - "id": "calendar", - "properties": { - "sundayStart": false - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 1 - }, - "size": { - "width": 3, - "height": 5 - } - }, - "sm": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 3, - "height": 5 - } - }, - "lg": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 3, - "height": 5 - } - } - } - }, { "id": "weather", "properties": { @@ -496,11 +510,11 @@ "shape": { "md": { "location": { - "x": 4, + "x": 0, "y": 0 }, "size": { - "width": 2, + "width": 3, "height": 1 } }, @@ -510,22 +524,66 @@ "y": 0 }, "size": { - "width": 2, + "width": 3, "height": 1 } }, "lg": { "location": { "x": 0, - "y": 1 + "y": 0 }, "size": { - "width": 2, + "width": 3, "height": 1 } } } }, + { + "id": "calendar", + "properties": { + "sundayStart": false + }, + "area": { + "type": "wrapper", + "properties": { + "id": "default" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 5 + } + }, + "sm": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 5 + } + }, + "lg": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 5 + } + } + } + }, { "id": "date", "properties": { @@ -540,18 +598,18 @@ "shape": { "sm": { "location": { - "x": 1, - "y": 1 + "x": 0, + "y": 2 }, "size": { "width": 2, - "height": 2 + "height": 1 } }, "md": { "location": { - "x": 4, - "y": 2 + "x": 1, + "y": 1 }, "size": { "width": 2, @@ -560,8 +618,8 @@ }, "lg": { "location": { - "x": 2, - "y": 0 + "x": 1, + "y": 1 }, "size": { "width": 2, diff --git a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx index 5c45e4f03..b77669b49 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx @@ -112,13 +112,35 @@ export const AvailableElementTypes = ({ }, }, shape: { - location: { - x: 0, - y: 0, + sm: { + location: { + x: 0, + y: 0, + }, + size: { + width: appTileDefinition.minWidth, + height: appTileDefinition.minHeight, + }, }, - size: { - width: appTileDefinition.minWidth, - height: appTileDefinition.minHeight, + md: { + location: { + 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: { From 6be0779ac30996b34cce49305b25a9d825529ae4 Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Sat, 7 Jan 2023 09:33:14 +0100 Subject: [PATCH 11/15] =?UTF-8?q?=F0=9F=90=9B=20Fix=20styling=20issue=20af?= =?UTF-8?q?ter=20category=20movement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 387 +++++++++++++++++++------------------- src/config/store.ts | 2 +- src/styles/global.scss | 8 +- 3 files changed, 203 insertions(+), 194 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index bce515c88..a697701ac 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -4,6 +4,11 @@ "name": "default" }, "categories": [ + { + "id": "c1c4bec3-1044-4a80-957f-afe7ff49f421", + "name": "Test", + "position": 2 + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f", "position": 0, @@ -12,72 +17,15 @@ ], "wrappers": [ { - "id": "default", + "id": "943f0681-a15b-4576-9a61-a74bd6fdd3ab", "position": 1 + }, + { + "id": "default", + "position": 3 } ], "apps": [ - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", - "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 10 - }, - "size": { - "width": 2, - "height": 1 - } - }, - "lg": { - "location": { - "x": 3, - "y": 1 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, { "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", "name": "Small app", @@ -136,64 +84,6 @@ "properties": [] } }, - { - "id": "5df743d9-5cb1-457c-85d2-64ff86855652", - "name": "Your app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 2, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 2, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 1, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - }, - "integration": { - "type": null, - "properties": [] - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990", "name": "Donate", @@ -255,6 +145,183 @@ } } }, + { + "id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa", + "name": "Haha", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 3 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 2, + "y": 2 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 6 + }, + "size": { + "width": 1, + "height": 1 + } + } + }, + "integration": { + "type": null, + "properties": [] + } + }, + { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "sm": { + "location": { + "x": 0, + "y": 10 + }, + "size": { + "width": 2, + "height": 1 + } + }, + "lg": { + "location": { + "x": 3, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + } + } + }, + { + "id": "5df743d9-5cb1-457c-85d2-64ff86855652", + "name": "Your app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "area": { + "type": "category", + "properties": { + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + } + }, + "shape": { + "md": { + "location": { + "x": 2, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "sm": { + "location": { + "x": 2, + "y": 1 + }, + "size": { + "width": 1, + "height": 1 + } + }, + "lg": { + "location": { + "x": 1, + "y": 0 + }, + "size": { + "width": 1, + "height": 1 + } + } + }, + "integration": { + "type": null, + "properties": [] + } + }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", "name": "Discord", @@ -316,64 +383,6 @@ } } }, - { - "id": "615e43bd-f0aa-4117-ba49-b6495c039f3e", - "name": "Your app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "sm": { - "location": { - "x": 0, - "y": 5 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "md": { - "location": { - "x": 0, - "y": 5 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 0, - "y": 5 - }, - "size": { - "width": 1, - "height": 1 - } - } - }, - "integration": { - "type": null, - "properties": [] - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", "name": "Contribute", @@ -436,8 +445,8 @@ } }, { - "id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa", - "name": "Haha", + "id": "615e43bd-f0aa-4117-ba49-b6495c039f3e", + "name": "Your app", "url": "https://homarr.dev", "appearance": { "iconUrl": "/imgs/logo/logo.png" @@ -448,29 +457,29 @@ }, "behaviour": { "isOpeningNewTab": true, - "externalUrl": "" + "externalUrl": "https://homarr.dev" }, "area": { - "type": "category", + "type": "wrapper", "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + "id": "default" } }, "shape": { - "md": { + "sm": { "location": { "x": 0, - "y": 3 + "y": 5 }, "size": { "width": 1, "height": 1 } }, - "sm": { + "md": { "location": { - "x": 2, - "y": 2 + "x": 0, + "y": 5 }, "size": { "width": 1, @@ -480,7 +489,7 @@ "lg": { "location": { "x": 0, - "y": 6 + "y": 5 }, "size": { "width": 1, diff --git a/src/config/store.ts b/src/config/store.ts index 0839aee60..ecd7ecd58 100644 --- a/src/config/store.ts +++ b/src/config/store.ts @@ -75,7 +75,7 @@ interface UseConfigStoreType { updateConfig: ( name: string, updateCallback: (previous: ConfigType) => ConfigType, - shouldRegenerateGridstace?: + shouldRegenerateGridstack?: | boolean | ((previousConfig: ConfigType, currentConfig: ConfigType) => boolean), shouldSaveConfigToFileSystem?: boolean diff --git a/src/styles/global.scss b/src/styles/global.scss index 026e01066..f77f06bd0 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -25,9 +25,9 @@ } @for $i from 1 to 96 { - .grid-stack>.grid-stack-item[gs-h="#{$i}"] { height: calc(#{$i} * #{var(--gridstack-widget-width)}) } - .grid-stack>.grid-stack-item[gs-min-h="#{$i}"] { min-height: calc(#{$i} * #{var(--gridstack-widget-width)}) } - .grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc(#{$i} * #{var(--gridstack-widget-width)}) } + .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: calc(#{$i}px * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-max-h="#{$i}"] { max-height: calc(#{$i}px * #{var(--gridstack-widget-width)}) } } @for $i from 1 to 13 { @@ -36,7 +36,7 @@ @for $i from 1 to 96 { - .grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc(#{$i} * #{var(--gridstack-widget-width)}) } + .grid-stack>.grid-stack-item[gs-y="#{$i}"] { top: calc(#{$i}px * #{var(--gridstack-widget-width)}) } } .grid-stack>.grid-stack-item { From 1ea4ca21490c95bd734b4740ed0d0c4782dcc43d Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Sat, 7 Jan 2023 09:45:00 +0100 Subject: [PATCH 12/15] =?UTF-8?q?=F0=9F=8E=A8=20Format=20with=20prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/configs/default.json | 418 +++++++++--------- src/components/Config/LoadConfig.tsx | 4 +- .../ChangeWidgetPositionModal.tsx | 11 +- .../Dashboard/Views/DashboardView.tsx | 46 +- .../Dashboard/Wrappers/Sidebar/Sidebar.tsx | 49 +- .../Dashboard/Wrappers/WrapperContent.tsx | 4 +- .../Wrappers/gridstack/init-gridstack.ts | 24 +- .../Wrappers/gridstack/use-gridstack.ts | 17 +- .../Customization/CustomizationSettings.tsx | 4 +- src/config/store.ts | 2 +- src/hooks/use-resize.ts | 8 +- src/hooks/widgets/dashDot/api.ts | 5 +- src/pages/api/modules/torrents.ts | 3 +- src/widgets/WidgetWrapper.tsx | 10 +- src/widgets/useNet/UseNetTile.tsx | 6 +- 15 files changed, 307 insertions(+), 304 deletions(-) diff --git a/data/configs/default.json b/data/configs/default.json index a697701ac..6a7df11ae 100644 --- a/data/configs/default.json +++ b/data/configs/default.json @@ -26,64 +26,6 @@ } ], "apps": [ - { - "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", - "name": "Small app", - "url": "https://homarr.dev", - "appearance": { - "iconUrl": "/imgs/logo/logo.png" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [] - }, - "behaviour": { - "isOpeningNewTab": true, - "externalUrl": "https://homarr.dev" - }, - "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-ad645ee6a990", "name": "Donate", @@ -145,6 +87,64 @@ } } }, + { + "id": "76217a87-7151-42d0-b0cf-1b72aef63f83", + "name": "Small app", + "url": "https://homarr.dev", + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [] + }, + "behaviour": { + "isOpeningNewTab": true, + "externalUrl": "https://homarr.dev" + }, + "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": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa", "name": "Haha", @@ -169,8 +169,8 @@ "shape": { "md": { "location": { - "x": 0, - "y": 3 + "x": 5, + "y": 1 }, "size": { "width": 1, @@ -189,8 +189,8 @@ }, "lg": { "location": { - "x": 0, - "y": 6 + "x": 4, + "y": 2 }, "size": { "width": 1, @@ -204,13 +204,13 @@ } }, { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", - "name": "Documentation", - "url": "https://homarr.dev", + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", + "name": "Discord", + "url": "https://discord.com/invite/aCsmEV5RgA", "behaviour": { - "onClickUrl": "https://homarr.dev", - "externalUrl": "https://homarr.dev", - "isOpeningNewTab": true + "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", + "isOpeningNewTab": true, + "externalUrl": "https://discord.com/invite/aCsmEV5RgA" }, "network": { "enabledStatusChecker": false, @@ -219,7 +219,7 @@ ] }, "appearance": { - "iconUrl": "/imgs/logo/logo.png" + "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" }, "integration": { "type": null, @@ -234,28 +234,28 @@ "shape": { "md": { "location": { - "x": 0, - "y": 10 + "x": 3, + "y": 0 }, "size": { - "width": 2, + "width": 1, "height": 1 } }, "sm": { "location": { "x": 0, - "y": 10 + "y": 1 }, "size": { - "width": 2, + "width": 1, "height": 1 } }, "lg": { "location": { - "x": 3, - "y": 1 + "x": 0, + "y": 0 }, "size": { "width": 1, @@ -308,8 +308,8 @@ }, "lg": { "location": { - "x": 1, - "y": 0 + "x": 2, + "y": 2 }, "size": { "width": 1, @@ -322,67 +322,6 @@ "properties": [] } }, - { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337", - "name": "Discord", - "url": "https://discord.com/invite/aCsmEV5RgA", - "behaviour": { - "onClickUrl": "https://discord.com/invite/aCsmEV5RgA", - "isOpeningNewTab": true, - "externalUrl": "https://discord.com/invite/aCsmEV5RgA" - }, - "network": { - "enabledStatusChecker": false, - "okStatus": [ - 200 - ] - }, - "appearance": { - "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png" - }, - "integration": { - "type": null, - "properties": [] - }, - "area": { - "type": "category", - "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 1 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "sm": { - "location": { - "x": 0, - "y": 1 - }, - "size": { - "width": 1, - "height": 1 - } - }, - "lg": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 1, - "height": 1 - } - } - } - }, { "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a330", "name": "Contribute", @@ -414,8 +353,8 @@ "shape": { "md": { "location": { - "x": 2, - "y": 3 + "x": 0, + "y": 1 }, "size": { "width": 1, @@ -434,8 +373,8 @@ }, "lg": { "location": { - "x": 2, - "y": 3 + "x": 3, + "y": 2 }, "size": { "width": 1, @@ -479,7 +418,7 @@ "md": { "location": { "x": 0, - "y": 5 + "y": 9 }, "size": { "width": 1, @@ -501,29 +440,43 @@ "type": null, "properties": [] } - } - ], - "widgets": [ + }, { - "id": "weather", - "properties": { - "displayInFahrenheit": false, - "location": "Paris" + "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a", + "name": "Documentation", + "url": "https://homarr.dev", + "behaviour": { + "onClickUrl": "https://homarr.dev", + "externalUrl": "https://homarr.dev", + "isOpeningNewTab": true + }, + "network": { + "enabledStatusChecker": false, + "okStatus": [ + 200 + ] + }, + "appearance": { + "iconUrl": "/imgs/logo/logo.png" + }, + "integration": { + "type": null, + "properties": [] }, "area": { "type": "category", "properties": { - "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f" + "id": "c1c4bec3-1044-4a80-957f-afe7ff49f421" } }, "shape": { "md": { "location": { - "x": 0, + "x": 2, "y": 0 }, "size": { - "width": 3, + "width": 2, "height": 1 } }, @@ -533,66 +486,24 @@ "y": 0 }, "size": { - "width": 3, + "width": 2, "height": 1 } }, "lg": { "location": { - "x": 0, - "y": 0 + "x": 1, + "y": 2 }, "size": { - "width": 3, + "width": 1, "height": 1 } } } - }, - { - "id": "calendar", - "properties": { - "sundayStart": false - }, - "area": { - "type": "wrapper", - "properties": { - "id": "default" - } - }, - "shape": { - "md": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 3, - "height": 5 - } - }, - "sm": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 3, - "height": 5 - } - }, - "lg": { - "location": { - "x": 0, - "y": 0 - }, - "size": { - "width": 3, - "height": 5 - } - } - } - }, + } + ], + "widgets": [ { "id": "date", "properties": { @@ -617,8 +528,8 @@ }, "md": { "location": { - "x": 1, - "y": 1 + "x": 4, + "y": 0 }, "size": { "width": 2, @@ -627,13 +538,102 @@ }, "lg": { "location": { - "x": 1, - "y": 1 + "x": 7, + "y": 0 + }, + "size": { + "width": 5, + "height": 2 + } + } + } + }, + { + "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": 3, + "height": 1 + } + }, + "lg": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 7, + "height": 2 + } + } + } + }, + { + "id": "calendar", + "properties": { + "sundayStart": false + }, + "area": { + "type": "wrapper", + "properties": { + "id": "default" + } + }, + "shape": { + "md": { + "location": { + "x": 0, + "y": 4 + }, + "size": { + "width": 3, + "height": 5 + } + }, + "sm": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 5 + } + }, + "lg": { + "location": { + "x": 0, + "y": 0 + }, + "size": { + "width": 3, + "height": 5 + } } } } @@ -647,8 +647,8 @@ }, "customization": { "layout": { - "enabledLeftSidebar": true, - "enabledRightSidebar": true, + "enabledLeftSidebar": false, + "enabledRightSidebar": false, "enabledDocker": false, "enabledPing": false, "enabledSearchbar": true diff --git a/src/components/Config/LoadConfig.tsx b/src/components/Config/LoadConfig.tsx index e653a19ba..0f40c62ff 100644 --- a/src/components/Config/LoadConfig.tsx +++ b/src/components/Config/LoadConfig.tsx @@ -36,7 +36,9 @@ export const LoadConfigComponent = () => { let newConfig: ConfigType = JSON.parse(fileText); if (!newConfig.schemaVersion) { - console.warn('a legacy configuration schema was deteced and migrated to the current schema'); + console.warn( + 'a legacy configuration schema was deteced and migrated to the current schema' + ); const oldConfig = JSON.parse(fileText) as Config; newConfig = migrateConfig(oldConfig); } diff --git a/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx b/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx index 4ad1db3db..60bfdb9b3 100644 --- a/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx +++ b/src/components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal.tsx @@ -14,7 +14,7 @@ export const ChangeWidgetPositionModal = ({ }: ContextModalProps) => { const { name: configName } = useConfigContext(); const updateConfig = useConfigStore((x) => x.updateConfig); - const shapeSize = useGridstackStore(x => x.currentShapeSize); + const shapeSize = useGridstackStore((x) => x.currentShapeSize); const handleSubmit = (x: number, y: number, width: number, height: number) => { if (!configName) { @@ -72,13 +72,14 @@ const useWidthData = (integration: string): SelectItem[] => { const currentWidget = widgets[integration as keyof typeof widgets]; if (!currentWidget) return []; const offset = currentWidget.gridstack.minWidth ?? 2; - const length = (currentWidget.gridstack.maxWidth > wrapperColumnCount! - ? wrapperColumnCount! - : currentWidget.gridstack.maxWidth) - offset; + const length = + (currentWidget.gridstack.maxWidth > wrapperColumnCount! + ? wrapperColumnCount! + : currentWidget.gridstack.maxWidth) - offset; return Array.from({ length: length + 1 }, (_, i) => i + offset).map((n) => ({ value: n.toString(), // eslint-disable-next-line no-mixed-operators - label: `${(100 / wrapperColumnCount! * n).toFixed(2)}%`, + label: `${((100 / wrapperColumnCount!) * n).toFixed(2)}%`, })); }; diff --git a/src/components/Dashboard/Views/DashboardView.tsx b/src/components/Dashboard/Views/DashboardView.tsx index dac54a60c..11f6cb0b4 100644 --- a/src/components/Dashboard/Views/DashboardView.tsx +++ b/src/components/Dashboard/Views/DashboardView.tsx @@ -17,31 +17,33 @@ export const DashboardView = () => { return ( - {sidebarsVisible.isLoading ? + {sidebarsVisible.isLoading ? (
- : - <> - {sidebarsVisible.left ? ( - - ) : null} + ) : ( + <> + {sidebarsVisible.left ? ( + + ) : null} - - {!isReady ? null : wrappers.map((item) => - item.type === 'category' ? ( - - ) : ( - - ) - )} - + + {!isReady + ? null + : wrappers.map((item) => + item.type === 'category' ? ( + + ) : ( + + ) + )} + - {sidebarsVisible.right ? ( - - ) : null} - -} + {sidebarsVisible.right ? ( + + ) : null} + + )}
); }; @@ -49,8 +51,8 @@ export const DashboardView = () => { const usePrepareGridstack = () => { const mainAreaRef = useRef(null); const { width } = useResize(mainAreaRef, []); - const setMainAreaWidth = useGridstackStore(x => x.setMainAreaWidth); - const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth); + const setMainAreaWidth = useGridstackStore((x) => x.setMainAreaWidth); + const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth); useEffect(() => { if (width === 0) return; diff --git a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx index 08a3af69a..501e38e2a 100644 --- a/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx +++ b/src/components/Dashboard/Wrappers/Sidebar/Sidebar.tsx @@ -9,21 +9,21 @@ interface DashboardSidebarProps extends DashboardSidebarInnerProps { } export const DashboardSidebar = ({ location, isGridstackReady }: DashboardSidebarProps) => ( - - {isGridstackReady && } - - ); + + {isGridstackReady && } + +); - interface DashboardSidebarInnerProps { - location: 'right' | 'left'; - } +interface DashboardSidebarInnerProps { + location: 'right' | 'left'; +} // Is Required because of the gridstack main area width. const SidebarInner = ({ location }: DashboardSidebarInnerProps) => { @@ -32,16 +32,17 @@ const SidebarInner = ({ location }: DashboardSidebarInnerProps) => { const minRow = useMinRowForFullHeight(refs.wrapper); return ( -
- -
); +
+ +
+ ); }; const useMinRowForFullHeight = (wrapperRef: RefObject) => diff --git a/src/components/Dashboard/Wrappers/WrapperContent.tsx b/src/components/Dashboard/Wrappers/WrapperContent.tsx index b846d2979..cfb82ae39 100644 --- a/src/components/Dashboard/Wrappers/WrapperContent.tsx +++ b/src/components/Dashboard/Wrappers/WrapperContent.tsx @@ -19,9 +19,9 @@ interface WrapperContentProps { } export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) { - const shapeSize = useGridstackStore(x => x.currentShapeSize); + const shapeSize = useGridstackStore((x) => x.currentShapeSize); - if (!shapeSize) return null; + if (!shapeSize) return null; return ( <> diff --git a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts index f95d01fe1..36836222d 100644 --- a/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/init-gridstack.ts @@ -64,20 +64,16 @@ export const initializeGridstack = ( grid.batchUpdate(); grid.removeAll(false); - items.forEach( - ({ id, shape }) => { - const item = itemRefs.current[id]?.current; - setAttributesFromShape(item, shape[shapeSize]); - item && grid.makeWidget(item as HTMLDivElement); - } - ); - widgets.forEach( - ({ id, shape }) => { - const item = itemRefs.current[id]?.current; - setAttributesFromShape(item, shape[shapeSize]); - item && grid.makeWidget(item as HTMLDivElement); - } - ); + items.forEach(({ id, shape }) => { + const item = itemRefs.current[id]?.current; + setAttributesFromShape(item, shape[shapeSize]); + item && grid.makeWidget(item as HTMLDivElement); + }); + widgets.forEach(({ id, shape }) => { + const item = itemRefs.current[id]?.current; + setAttributesFromShape(item, shape[shapeSize]); + item && grid.makeWidget(item as HTMLDivElement); + }); grid.batchUpdate(false); }; diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index e5227d33f..723eb3e84 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -1,11 +1,5 @@ import { GridStack, GridStackNode } from 'fily-publish-gridstack'; -import { - createRef, - MutableRefObject, - RefObject, - useEffect, useMemo, - useRef, -} from 'react'; +import { createRef, MutableRefObject, RefObject, useEffect, useMemo, useRef } from 'react'; import { useConfigContext } from '../../../../config/provider'; import { useConfigStore } from '../../../../config/store'; import { AppType } from '../../../../types/app'; @@ -39,12 +33,13 @@ export const useGridstack = ( // reference of the gridstack object for modifications after initialization const gridRef = useRef(); const wrapperColumnCount = useWrapperColumnCount(); - const shapeSize = useGridstackStore(x => x.currentShapeSize); - const mainAreaWidth = useGridstackStore(x => x.mainAreaWidth); + const shapeSize = useGridstackStore((x) => x.currentShapeSize); + const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth); // width of the wrapper (updating on page resize) const root: HTMLHtmlElement = useMemo(() => document.querySelector(':root')!, []); - if (!mainAreaWidth || !shapeSize || !wrapperColumnCount) throw new Error('UseGridstack should not be executed before mainAreaWidth has been set!'); + if (!mainAreaWidth || !shapeSize || !wrapperColumnCount) + throw new Error('UseGridstack should not be executed before mainAreaWidth has been set!'); const items = useMemo( () => @@ -86,7 +81,7 @@ export const useGridstack = ( }, [mainAreaWidth, wrapperColumnCount, gridRef.current]); useEffect(() => { - // column count is used to define count of columns of gridstack within global.scss + // column count is used to define count of columns of gridstack within global.scss root.style.setProperty('--gridstack-column-count', wrapperColumnCount.toString()); }, [wrapperColumnCount]); diff --git a/src/components/Settings/Customization/CustomizationSettings.tsx b/src/components/Settings/Customization/CustomizationSettings.tsx index 5b6def2dc..c4b2b2dee 100644 --- a/src/components/Settings/Customization/CustomizationSettings.tsx +++ b/src/components/Settings/Customization/CustomizationSettings.tsx @@ -47,7 +47,9 @@ export default function CustomizationSettings() { - + ); } diff --git a/src/config/store.ts b/src/config/store.ts index ecd7ecd58..4d32ef860 100644 --- a/src/config/store.ts +++ b/src/config/store.ts @@ -70,7 +70,7 @@ interface UseConfigStoreType { addConfig: ( name: string, config: ConfigType, - shouldSaveConfigToFileSystem: boolean, + shouldSaveConfigToFileSystem: boolean ) => Promise; updateConfig: ( name: string, diff --git a/src/hooks/use-resize.ts b/src/hooks/use-resize.ts index e9c47cf15..da6a94c3f 100644 --- a/src/hooks/use-resize.ts +++ b/src/hooks/use-resize.ts @@ -11,12 +11,8 @@ export const useResize = (myRef: MutableRefObject, depend }, [myRef]); useEffect(() => { - window.addEventListener('load', () => - handleResize() - ); - window.addEventListener('resize', () => - handleResize() - ); + window.addEventListener('load', () => handleResize()); + window.addEventListener('resize', () => handleResize()); return () => { window.removeEventListener('load', handleResize); diff --git a/src/hooks/widgets/dashDot/api.ts b/src/hooks/widgets/dashDot/api.ts index 6e8496c85..4032469e5 100644 --- a/src/hooks/widgets/dashDot/api.ts +++ b/src/hooks/widgets/dashDot/api.ts @@ -1,7 +1,10 @@ import { useMutation, useQuery } from '@tanstack/react-query'; import axios from 'axios'; import { Results } from 'sabnzbd-api'; -import { UsenetQueueRequestParams, UsenetQueueResponse } from '../../../pages/api/modules/usenet/queue'; +import { + UsenetQueueRequestParams, + UsenetQueueResponse, +} from '../../../pages/api/modules/usenet/queue'; import { UsenetHistoryRequestParams, UsenetHistoryResponse, diff --git a/src/pages/api/modules/torrents.ts b/src/pages/api/modules/torrents.ts index f47a80191..81600cace 100644 --- a/src/pages/api/modules/torrents.ts +++ b/src/pages/api/modules/torrents.ts @@ -40,7 +40,8 @@ async function Post(req: NextApiRequest, res: NextApiResponse) { ); await Promise.all( delugeApp.map((app) => { - const password = app.integration?.properties.find((x) => x.field === 'password')?.value ?? undefined; + const password = + app.integration?.properties.find((x) => x.field === 'password')?.value ?? undefined; const test = new Deluge({ baseUrl: app.url, password, diff --git a/src/widgets/WidgetWrapper.tsx b/src/widgets/WidgetWrapper.tsx index c321cbc12..da0837307 100644 --- a/src/widgets/WidgetWrapper.tsx +++ b/src/widgets/WidgetWrapper.tsx @@ -11,8 +11,8 @@ interface WidgetWrapperProps { } export const WidgetWrapper = ({ widgetId, widget, className, children }: WidgetWrapperProps) => ( - - - {children} - - ); + + + {children} + +); diff --git a/src/widgets/useNet/UseNetTile.tsx b/src/widgets/useNet/UseNetTile.tsx index c3c8d3272..61182c37e 100644 --- a/src/widgets/useNet/UseNetTile.tsx +++ b/src/widgets/useNet/UseNetTile.tsx @@ -17,7 +17,11 @@ import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../config/provider'; -import { useGetUsenetInfo, usePauseUsenetQueue, useResumeUsenetQueue } from '../../hooks/widgets/dashDot/api'; +import { + useGetUsenetInfo, + usePauseUsenetQueue, + useResumeUsenetQueue, +} from '../../hooks/widgets/dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; import { AppIntegrationType } from '../../types/app'; import { defineWidget } from '../helper'; From 7c0cf6f4ec0fd43396c09c8d2f2d333e25d0f3d4 Mon Sep 17 00:00:00 2001 From: Manuel Ruwe Date: Sat, 7 Jan 2023 17:03:13 +0100 Subject: [PATCH 13/15] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20build=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Config/LoadConfig.tsx | 2 ++ src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Config/LoadConfig.tsx b/src/components/Config/LoadConfig.tsx index 0f40c62ff..d2cd232ff 100644 --- a/src/components/Config/LoadConfig.tsx +++ b/src/components/Config/LoadConfig.tsx @@ -36,6 +36,8 @@ export const LoadConfigComponent = () => { let newConfig: ConfigType = JSON.parse(fileText); if (!newConfig.schemaVersion) { + // client side logging + // eslint-disable-next-line no-console console.warn( 'a legacy configuration schema was deteced and migrated to the current schema' ); diff --git a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts index 723eb3e84..a47f07486 100644 --- a/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts +++ b/src/components/Dashboard/Wrappers/gridstack/use-gridstack.ts @@ -38,8 +38,9 @@ export const useGridstack = ( // width of the wrapper (updating on page resize) const root: HTMLHtmlElement = useMemo(() => document.querySelector(':root')!, []); - if (!mainAreaWidth || !shapeSize || !wrapperColumnCount) + if (!mainAreaWidth || !shapeSize || !wrapperColumnCount) { throw new Error('UseGridstack should not be executed before mainAreaWidth has been set!'); + } const items = useMemo( () => From 14258197244d42e8b23cea2a636df5998e51a04d Mon Sep 17 00:00:00 2001 From: Meierschlumpf Date: Sat, 7 Jan 2023 18:02:56 +0100 Subject: [PATCH 14/15] =?UTF-8?q?=F0=9F=90=9B=20Fix=20build=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/Docker/ContainerActionBar.tsx | 34 +++++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/modules/Docker/ContainerActionBar.tsx b/src/modules/Docker/ContainerActionBar.tsx index 5a0ad3efd..da7388c73 100644 --- a/src/modules/Docker/ContainerActionBar.tsx +++ b/src/modules/Docker/ContainerActionBar.tsx @@ -190,13 +190,35 @@ export default function ContainerActionBar({ selected, reload }: ContainerAction }, }, shape: { - location: { - x: 0, - y: 0, + sm: { + location: { + x: 0, + y: 0, + }, + size: { + width: appTileDefinition.minWidth, + height: appTileDefinition.minHeight, + }, }, - size: { - width: appTileDefinition.minWidth, - height: appTileDefinition.minHeight, + md: { + location: { + 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: { From 6da308e66addcf42bbdf31b975c22d7882765717 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 7 Jan 2023 18:17:56 +0100 Subject: [PATCH 15/15] =?UTF-8?q?=F0=9F=8E=A8Pullrequest=20feedback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com> --- src/hooks/use-resize.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/use-resize.ts b/src/hooks/use-resize.ts index da6a94c3f..e6f632d36 100644 --- a/src/hooks/use-resize.ts +++ b/src/hooks/use-resize.ts @@ -11,8 +11,8 @@ export const useResize = (myRef: MutableRefObject, depend }, [myRef]); useEffect(() => { - window.addEventListener('load', () => handleResize()); - window.addEventListener('resize', () => handleResize()); + window.addEventListener('load', handleResize); + window.addEventListener('resize', handleResize); return () => { window.removeEventListener('load', handleResize);