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