mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
🐛 Fix issues with change position modal
This commit is contained in:
@@ -194,67 +194,6 @@
|
|||||||
"properties": []
|
"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",
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990",
|
||||||
"name": "Donate",
|
"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",
|
"id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa",
|
||||||
"name": "Your app",
|
"name": "Your app",
|
||||||
@@ -434,6 +312,128 @@
|
|||||||
"type": null,
|
"type": null,
|
||||||
"properties": []
|
"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": [
|
"widgets": [
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
"height": "Height",
|
"height": "Height",
|
||||||
"yPosition": "Y axis position",
|
"yPosition": "Y axis position",
|
||||||
"zeroOrHigher": "0 or higher",
|
"zeroOrHigher": "0 or higher",
|
||||||
"betweenXandY": "Between {{mim}} and {{max}}"
|
"betweenXandY": "Between {{min}} and {{max}}"
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import { closeModal, ContextModalProps } from '@mantine/modals';
|
|||||||
import { useConfigContext } from '../../../../config/provider';
|
import { useConfigContext } from '../../../../config/provider';
|
||||||
import { useConfigStore } from '../../../../config/store';
|
import { useConfigStore } from '../../../../config/store';
|
||||||
import { AppType } from '../../../../types/app';
|
import { AppType } from '../../../../types/app';
|
||||||
|
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
import { ChangePositionModal } from './ChangePositionModal';
|
import { ChangePositionModal } from './ChangePositionModal';
|
||||||
|
|
||||||
type ChangeAppPositionModalInnerProps = {
|
type ChangeAppPositionModalInnerProps = {
|
||||||
@@ -16,6 +17,9 @@ export const ChangeAppPositionModal = ({
|
|||||||
}: ContextModalProps<ChangeAppPositionModalInnerProps>) => {
|
}: ContextModalProps<ChangeAppPositionModalInnerProps>) => {
|
||||||
const { name: configName } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||||
|
const shapeSize = useGridstackStore((x) => x.currentShapeSize);
|
||||||
|
|
||||||
|
if (!shapeSize) return null;
|
||||||
|
|
||||||
const handleSubmit = (x: number, y: number, width: number, height: number) => {
|
const handleSubmit = (x: number, y: number, width: number, height: number) => {
|
||||||
if (!configName) {
|
if (!configName) {
|
||||||
@@ -28,7 +32,13 @@ export const ChangeAppPositionModal = ({
|
|||||||
...previousConfig,
|
...previousConfig,
|
||||||
apps: [
|
apps: [
|
||||||
...previousConfig.apps.filter((x) => x.id !== innerProps.app.id),
|
...previousConfig.apps.filter((x) => x.id !== innerProps.app.id),
|
||||||
{ ...innerProps.app, shape: { location: { x, y }, size: { width, height } } },
|
{
|
||||||
|
...innerProps.app,
|
||||||
|
shape: {
|
||||||
|
...innerProps.app.shape,
|
||||||
|
[shapeSize]: { location: { x, y }, size: { width, height } },
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
true
|
true
|
||||||
@@ -49,28 +59,35 @@ export const ChangeAppPositionModal = ({
|
|||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
widthData={widthData}
|
widthData={widthData}
|
||||||
heightData={heightData}
|
heightData={heightData}
|
||||||
initialX={innerProps.app.shape.location.x}
|
initialX={innerProps.app.shape[shapeSize]?.location.x}
|
||||||
initialY={innerProps.app.shape.location.y}
|
initialY={innerProps.app.shape[shapeSize]?.location.y}
|
||||||
initialWidth={innerProps.app.shape.size.width}
|
initialWidth={innerProps.app.shape[shapeSize]?.size.width}
|
||||||
initialHeight={innerProps.app.shape.size.height}
|
initialHeight={innerProps.app.shape[shapeSize]?.size.height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useHeightData = (): SelectItem[] =>
|
const useHeightData = (): SelectItem[] => {
|
||||||
Array.from(Array(11).keys()).map((n) => {
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||||
const index = n + 1;
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
return {
|
|
||||||
value: index.toString(),
|
|
||||||
label: `${64 * index}px`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const useWidthData = (): SelectItem[] =>
|
return Array.from(Array(11).keys()).map((n) => {
|
||||||
Array.from(Array(11).keys()).map((n) => {
|
|
||||||
const index = n + 1;
|
const index = n + 1;
|
||||||
return {
|
return {
|
||||||
value: index.toString(),
|
value: index.toString(),
|
||||||
label: `${64 * index}px`,
|
label: `${Math.floor(index * (mainAreaWidth! / wrapperColumnCount!))}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const useWidthData = (): SelectItem[] => {
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
|
return Array.from(Array(wrapperColumnCount!).keys()).map((n) => {
|
||||||
|
const index = n + 1;
|
||||||
|
return {
|
||||||
|
value: index.toString(),
|
||||||
|
// eslint-disable-next-line no-mixed-operators
|
||||||
|
label: `${((100 / wrapperColumnCount!) * index).toFixed(2)}%`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import { useConfigContext } from '../../../../config/provider';
|
import { useConfigContext } from '../../../../config/provider';
|
||||||
|
|
||||||
interface ChangePositionModalProps {
|
interface ChangePositionModalProps {
|
||||||
initialX: number;
|
initialX?: number;
|
||||||
initialY: number;
|
initialY?: number;
|
||||||
initialWidth: number;
|
initialWidth?: number;
|
||||||
initialHeight: number;
|
initialHeight?: number;
|
||||||
widthData: SelectItem[];
|
widthData: SelectItem[];
|
||||||
heightData: SelectItem[];
|
heightData: SelectItem[];
|
||||||
onSubmit: (x: number, y: number, width: number, height: number) => void;
|
onSubmit: (x: number, y: number, width: number, height: number) => void;
|
||||||
@@ -28,10 +28,10 @@ export const ChangePositionModal = ({
|
|||||||
|
|
||||||
const form = useForm<FormType>({
|
const form = useForm<FormType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
x: initialX,
|
x: initialX ?? null,
|
||||||
y: initialY,
|
y: initialY ?? null,
|
||||||
width: initialWidth,
|
width: initialWidth?.toString() ?? '',
|
||||||
height: initialHeight,
|
height: initialHeight?.toString() ?? '',
|
||||||
},
|
},
|
||||||
validateInputOnChange: true,
|
validateInputOnChange: true,
|
||||||
validateInputOnBlur: true,
|
validateInputOnBlur: true,
|
||||||
@@ -42,7 +42,12 @@ export const ChangePositionModal = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit(form.values.x, form.values.y, form.values.width, form.values.height);
|
const width = parseInt(form.values.width, 10);
|
||||||
|
const height = parseInt(form.values.height, 10);
|
||||||
|
|
||||||
|
if (!form.values.x || !form.values.y || Number.isNaN(width) || Number.isNaN(height)) return;
|
||||||
|
|
||||||
|
onSubmit(form.values.x, form.values.y, width, height);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTranslation(['layout/modals/change-position', 'common']);
|
const { t } = useTranslation(['layout/modals/change-position', 'common']);
|
||||||
@@ -112,8 +117,8 @@ export const ChangePositionModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
type FormType = {
|
type FormType = {
|
||||||
x: number;
|
x: number | null;
|
||||||
y: number;
|
y: number | null;
|
||||||
width: number;
|
width: string;
|
||||||
height: number;
|
height: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useConfigContext } from '../../../../config/provider';
|
|||||||
import { useConfigStore } from '../../../../config/store';
|
import { useConfigStore } from '../../../../config/store';
|
||||||
import widgets from '../../../../widgets';
|
import widgets from '../../../../widgets';
|
||||||
import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu';
|
import { WidgetChangePositionModalInnerProps } from '../../Tiles/Widgets/WidgetsMenu';
|
||||||
import { useGridstackStore } from '../../Wrappers/gridstack/store';
|
import { useGridstackStore, useWrapperColumnCount } from '../../Wrappers/gridstack/store';
|
||||||
import { ChangePositionModal } from './ChangePositionModal';
|
import { ChangePositionModal } from './ChangePositionModal';
|
||||||
|
|
||||||
export const ChangeWidgetPositionModal = ({
|
export const ChangeWidgetPositionModal = ({
|
||||||
@@ -68,23 +68,30 @@ export const ChangeWidgetPositionModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useWidthData = (integration: string): SelectItem[] => {
|
const useWidthData = (integration: string): SelectItem[] => {
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
const currentWidget = widgets[integration as keyof typeof widgets];
|
const currentWidget = widgets[integration as keyof typeof widgets];
|
||||||
if (!currentWidget) return [];
|
if (!currentWidget) return [];
|
||||||
const offset = currentWidget.gridstack.minWidth ?? 2;
|
const offset = currentWidget.gridstack.minWidth ?? 2;
|
||||||
const length = (currentWidget.gridstack.maxWidth ?? 12) - offset;
|
const length = (currentWidget.gridstack.maxWidth > wrapperColumnCount!
|
||||||
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
|
? wrapperColumnCount!
|
||||||
|
: currentWidget.gridstack.maxWidth) - offset;
|
||||||
|
return Array.from({ length: length + 1 }, (_, i) => i + offset).map((n) => ({
|
||||||
value: n.toString(),
|
value: n.toString(),
|
||||||
label: `${64 * n}px`,
|
// eslint-disable-next-line no-mixed-operators
|
||||||
|
label: `${(100 / wrapperColumnCount! * n).toFixed(2)}%`,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const useHeightData = (integration: string): SelectItem[] => {
|
const useHeightData = (integration: string): SelectItem[] => {
|
||||||
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
||||||
|
const wrapperColumnCount = useWrapperColumnCount();
|
||||||
|
|
||||||
const currentWidget = widgets[integration as keyof typeof widgets];
|
const currentWidget = widgets[integration as keyof typeof widgets];
|
||||||
if (!currentWidget) return [];
|
if (!currentWidget) return [];
|
||||||
const offset = currentWidget.gridstack.minHeight ?? 2;
|
const offset = currentWidget.gridstack.minHeight ?? 2;
|
||||||
const length = (currentWidget.gridstack.maxHeight ?? 12) - offset;
|
const length = (currentWidget.gridstack.maxHeight ?? 12) - offset;
|
||||||
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
|
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
|
||||||
value: n.toString(),
|
value: n.toString(),
|
||||||
label: `${64 * n}px`,
|
label: `${(mainAreaWidth! / wrapperColumnCount!) * n}px`,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export const useGridstack = (
|
|||||||
// widget width is used to define sizes of gridstack items within global.scss
|
// widget width is used to define sizes of gridstack items within global.scss
|
||||||
root.style.setProperty('--gridstack-widget-width', widgetWidth.toString());
|
root.style.setProperty('--gridstack-widget-width', widgetWidth.toString());
|
||||||
gridRef.current?.cellHeight(widgetWidth);
|
gridRef.current?.cellHeight(widgetWidth);
|
||||||
}, [mainAreaWidth, wrapperColumnCount]);
|
}, [mainAreaWidth, wrapperColumnCount, gridRef.current]);
|
||||||
|
|
||||||
useEffect(() => {
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user