🐛 Fix Change Position not working with gridstack

This commit is contained in:
Meierschlumpf
2022-12-19 21:27:44 +01:00
parent f240d29f7e
commit a5d31dd3ec
14 changed files with 242 additions and 173 deletions

View File

@@ -22,13 +22,17 @@ export const ChangeAppPositionModal = ({
return; return;
} }
updateConfig(configName, (previousConfig) => ({ updateConfig(
...previousConfig, configName,
apps: [ (previousConfig) => ({
...previousConfig.apps.filter((x) => x.id !== innerProps.app.id), ...previousConfig,
{ ...innerProps.app, shape: { location: { x, y }, size: { width, height } } }, apps: [
], ...previousConfig.apps.filter((x) => x.id !== innerProps.app.id),
})); { ...innerProps.app, shape: { location: { x, y }, size: { width, height } } },
],
}),
true
);
context.closeModal(id); context.closeModal(id);
}; };

View File

@@ -19,25 +19,28 @@ export const ChangeWidgetPositionModal = ({
return; return;
} }
updateConfig(configName, (prev) => { updateConfig(
let currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId); configName,
currentWidget!.shape = { (prev) => {
location: { let currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId);
x, currentWidget!.shape = {
y, location: {
}, x,
size: { y,
height, },
width, size: {
}, height,
}; width,
},
return { };
...prev,
widgets: [...prev.widgets.filter((x) => x.id !== innerProps.widgetId), currentWidget!],
};
});
return {
...prev,
widgets: [...prev.widgets.filter((x) => x.id !== innerProps.widgetId), currentWidget!],
};
},
true
);
context.closeModal(id); context.closeModal(id);
}; };

View File

@@ -83,10 +83,14 @@ export const EditAppModal = ({
return; return;
} }
updateConfig(configName, (previousConfig) => ({ updateConfig(
...previousConfig, configName,
apps: [...previousConfig.apps.filter((x) => x.id !== form.values.id), form.values], (previousConfig) => ({
})); ...previousConfig,
apps: [...previousConfig.apps.filter((x) => x.id !== form.values.id), form.values],
}),
true
);
// also close the parent modal // also close the parent modal
context.closeAll(); context.closeAll();

View File

@@ -26,35 +26,39 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement
}; };
const handleAddition = async () => { const handleAddition = async () => {
updateConfig(configName, (prev) => ({ updateConfig(
...prev, configName,
widgets: [ (prev) => ({
...prev.widgets.filter((w) => w.id !== widget.id), ...prev,
{ widgets: [
id: widget.id, ...prev.widgets.filter((w) => w.id !== widget.id),
properties: Object.entries(widget.options).reduce((prev, [k, v]) => { {
prev[k] = v.defaultValue; id: widget.id,
return prev; properties: Object.entries(widget.options).reduce((prev, [k, v]) => {
}, {} as IWidget<string, any>['properties']), prev[k] = v.defaultValue;
area: { return prev;
type: 'wrapper', }, {} as IWidget<string, any>['properties']),
properties: { area: {
id: getLowestWrapper()?.id ?? '', type: 'wrapper',
properties: {
id: getLowestWrapper()?.id ?? '',
},
},
shape: {
location: {
x: 0,
y: 0,
},
size: {
width: widget.gridstack.minWidth,
height: widget.gridstack.minHeight,
},
}, },
}, },
shape: { ],
location: { }),
x: 0, true
y: 0, );
},
size: {
width: widget.gridstack.minWidth,
height: widget.gridstack.minHeight,
},
},
},
],
}));
// TODO: safe to file system // TODO: safe to file system
closeModal('selectElement'); closeModal('selectElement');
}; };

View File

@@ -45,15 +45,19 @@ export const WidgetsEditModal = ({
}; };
const handleSave = () => { const handleSave = () => {
updateConfig(configName, (prev) => { updateConfig(
let currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId); configName,
currentWidget!.properties = moduleProperties; (prev) => {
let currentWidget = prev.widgets.find((x) => x.id === innerProps.widgetId);
currentWidget!.properties = moduleProperties;
return { return {
...prev, ...prev,
widgets: [...prev.widgets.filter((x) => x.id !== innerProps.widgetId), currentWidget!], widgets: [...prev.widgets.filter((x) => x.id !== innerProps.widgetId), currentWidget!],
}; };
}); },
true
);
context.closeModal(id); context.closeModal(id);
}; };

View File

@@ -19,10 +19,14 @@ export const WidgetsRemoveModal = ({
if (!configName) return null; if (!configName) return null;
const updateConfig = useConfigStore((x) => x.updateConfig); const updateConfig = useConfigStore((x) => x.updateConfig);
const handleDeletion = () => { const handleDeletion = () => {
updateConfig(configName, (prev) => ({ updateConfig(
...prev, configName,
widgets: prev.widgets.filter((w) => w.id !== innerProps.widgetId), (prev) => ({
})); ...prev,
widgets: prev.widgets.filter((w) => w.id !== innerProps.widgetId),
}),
true
);
context.closeModal(id); context.closeModal(id);
}; };

View File

@@ -29,29 +29,35 @@ export const useCategoryActions = (configName: string | undefined, category: Cat
}; };
// Adding category and wrapper and moving other items down // Adding category and wrapper and moving other items down
updateConfig(configName, (previous) => { updateConfig(
const aboveWrappers = previous.wrappers.filter((x) => x.position <= abovePosition); configName,
const aboveCategories = previous.categories.filter((x) => x.position <= abovePosition); (previous) => {
const aboveWrappers = previous.wrappers.filter((x) => x.position <= abovePosition);
const aboveCategories = previous.categories.filter(
(x) => x.position <= abovePosition
);
const belowWrappers = previous.wrappers.filter((x) => x.position > abovePosition); const belowWrappers = previous.wrappers.filter((x) => x.position > abovePosition);
const belowCategories = previous.categories.filter((x) => x.position > abovePosition); const belowCategories = previous.categories.filter((x) => x.position > abovePosition);
return { return {
...previous, ...previous,
categories: [ categories: [
...aboveCategories, ...aboveCategories,
category, category,
// Move categories below down // Move categories below down
...belowCategories.map((x) => ({ ...x, position: x.position + 2 })), ...belowCategories.map((x) => ({ ...x, position: x.position + 2 })),
], ],
wrappers: [ wrappers: [
...aboveWrappers, ...aboveWrappers,
newWrapper, newWrapper,
// Move wrappers below down // Move wrappers below down
...belowWrappers.map((x) => ({ ...x, position: x.position + 2 })), ...belowWrappers.map((x) => ({ ...x, position: x.position + 2 })),
], ],
}; };
}); },
true
);
}, },
}, },
}); });
@@ -78,29 +84,35 @@ export const useCategoryActions = (configName: string | undefined, category: Cat
}; };
// Adding category and wrapper and moving other items down // Adding category and wrapper and moving other items down
updateConfig(configName, (previous) => { updateConfig(
const aboveWrappers = previous.wrappers.filter((x) => x.position < belowPosition); configName,
const aboveCategories = previous.categories.filter((x) => x.position < belowPosition); (previous) => {
const aboveWrappers = previous.wrappers.filter((x) => x.position < belowPosition);
const aboveCategories = previous.categories.filter((x) => x.position < belowPosition);
const belowWrappers = previous.wrappers.filter((x) => x.position >= belowPosition); const belowWrappers = previous.wrappers.filter((x) => x.position >= belowPosition);
const belowCategories = previous.categories.filter((x) => x.position >= belowPosition); const belowCategories = previous.categories.filter(
(x) => x.position >= belowPosition
);
return { return {
...previous, ...previous,
categories: [ categories: [
...aboveCategories, ...aboveCategories,
category, category,
// Move categories below down // Move categories below down
...belowCategories.map((x) => ({ ...x, position: x.position + 2 })), ...belowCategories.map((x) => ({ ...x, position: x.position + 2 })),
], ],
wrappers: [ wrappers: [
...aboveWrappers, ...aboveWrappers,
newWrapper, newWrapper,
// Move wrappers below down // Move wrappers below down
...belowWrappers.map((x) => ({ ...x, position: x.position + 2 })), ...belowWrappers.map((x) => ({ ...x, position: x.position + 2 })),
], ],
}; };
}); },
true
);
}, },
}, },
}); });
@@ -109,51 +121,59 @@ export const useCategoryActions = (configName: string | undefined, category: Cat
const moveCategoryUp = () => { const moveCategoryUp = () => {
if (!configName) return; if (!configName) return;
updateConfig(configName, (previous) => { updateConfig(
const currentItem = previous.categories.find((x) => x.id === category.id); configName,
if (!currentItem) return previous; (previous) => {
const currentItem = previous.categories.find((x) => x.id === category.id);
if (!currentItem) return previous;
const upperItem = previous.categories.find((x) => x.position === currentItem.position - 2); const upperItem = previous.categories.find((x) => x.position === currentItem.position - 2);
if (!upperItem) return previous; if (!upperItem) return previous;
currentItem.position -= 2; currentItem.position -= 2;
upperItem.position += 2; upperItem.position += 2;
return { return {
...previous, ...previous,
categories: [ categories: [
...previous.categories.filter((c) => ![currentItem.id, upperItem.id].includes(c.id)), ...previous.categories.filter((c) => ![currentItem.id, upperItem.id].includes(c.id)),
{ ...upperItem }, { ...upperItem },
{ ...currentItem }, { ...currentItem },
], ],
}; };
}); },
true
);
}; };
const moveCategoryDown = () => { const moveCategoryDown = () => {
if (!configName) return; if (!configName) return;
updateConfig(configName, (previous) => { updateConfig(
const currentItem = previous.categories.find((x) => x.id === category.id); configName,
if (!currentItem) return previous; (previous) => {
const currentItem = previous.categories.find((x) => x.id === category.id);
if (!currentItem) return previous;
const belowItem = previous.categories.find((x) => x.position === currentItem.position + 2); const belowItem = previous.categories.find((x) => x.position === currentItem.position + 2);
if (!belowItem) return previous; if (!belowItem) return previous;
currentItem.position += 2; currentItem.position += 2;
belowItem.position -= 2; belowItem.position -= 2;
return { return {
...previous, ...previous,
categories: [ categories: [
...previous.categories.filter((c) => ![currentItem.id, belowItem.id].includes(c.id)), ...previous.categories.filter((c) => ![currentItem.id, belowItem.id].includes(c.id)),
{ ...currentItem }, { ...currentItem },
{ ...belowItem }, { ...belowItem },
], ],
}; };
}); },
true
);
}; };
const edit = async () => { const edit = async () => {

View File

@@ -14,6 +14,7 @@ interface WrapperContentProps {
wrapper: RefObject<HTMLDivElement>; wrapper: RefObject<HTMLDivElement>;
items: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>; items: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>;
gridstack: MutableRefObject<GridStack | undefined>; gridstack: MutableRefObject<GridStack | undefined>;
updateGridstackRef: MutableRefObject<(() => void) | undefined>;
}; };
} }

View File

@@ -54,6 +54,7 @@ export const initializeGridstack = (
if (!firstNode) return; if (!firstNode) return;
events.onAdd(firstNode); events.onAdd(firstNode);
}); });
grid.batchUpdate(); grid.batchUpdate();
grid.removeAll(false); grid.removeAll(false);
items.forEach( items.forEach(

View File

@@ -31,7 +31,7 @@ export const useGridstack = (
areaId: string areaId: string
): UseGristackReturnType => { ): UseGristackReturnType => {
const isEditMode = useEditModeStore((x) => x.enabled); const isEditMode = useEditModeStore((x) => x.enabled);
const { config, name: configName } = useConfigContext(); const { config, configVersion, name: configName } = useConfigContext();
const updateConfig = useConfigStore((x) => x.updateConfig); const updateConfig = useConfigStore((x) => x.updateConfig);
// define reference for wrapper - is used to calculate the width of the wrapper // define reference for wrapper - is used to calculate the width of the wrapper
const wrapperRef = useRef<HTMLDivElement>(null); const wrapperRef = useRef<HTMLDivElement>(null);
@@ -51,7 +51,7 @@ export const useGridstack = (
? x.area.properties.location === areaId ? x.area.properties.location === areaId
: x.area.properties.id === areaId) : x.area.properties.id === areaId)
) ?? [], ) ?? [],
[config] [configVersion]
); );
const widgets = useMemo(() => { const widgets = useMemo(() => {
if (!config) return []; if (!config) return [];
@@ -62,7 +62,7 @@ export const useGridstack = (
? w.area.properties.location === areaId ? w.area.properties.location === areaId
: w.area.properties.id === areaId) : w.area.properties.id === areaId)
); );
}, [config]); }, [configVersion]);
// define items in itemRefs for easy access and reference to items // define items in itemRefs for easy access and reference to items
if (Object.keys(itemRefs.current).length !== items.length + (widgets ?? []).length) { if (Object.keys(itemRefs.current).length !== items.length + (widgets ?? []).length) {
@@ -208,7 +208,7 @@ export const useGridstack = (
onAdd, onAdd,
} }
); );
}, [items.length, wrapperRef.current, (widgets ?? []).length]); }, [items, wrapperRef.current, widgets]);
return { return {
apps: items, apps: items,

View File

@@ -45,22 +45,26 @@ export const LayoutSelector = ({ defaultLayout }: LayoutSelectorProps) => {
) => { ) => {
const value = event.target.checked; const value = event.target.checked;
setState(value); setState(value);
updateConfig(configName, (prev) => { updateConfig(
const { layout } = prev.settings.customization; configName,
(prev) => {
const { layout } = prev.settings.customization;
layout[key] = value; layout[key] = value;
return { return {
...prev, ...prev,
settings: { settings: {
...prev.settings, ...prev.settings,
customization: { customization: {
...prev.settings.customization, ...prev.settings.customization,
layout, layout,
},
}, },
}, };
}; },
}); true
);
}; };
return ( return (

View File

@@ -4,12 +4,12 @@ import { useConfigContext } from './provider';
import { useConfigStore } from './store'; import { useConfigStore } from './store';
export const useInitConfig = (initialConfig: ConfigType) => { export const useInitConfig = (initialConfig: ConfigType) => {
const { setConfigName } = useConfigContext(); const { setConfigName, increaseVersion } = useConfigContext();
const configName = initialConfig.configProperties?.name ?? 'default'; const configName = initialConfig.configProperties?.name ?? 'default';
const initConfig = useConfigStore((x) => x.initConfig); const initConfig = useConfigStore((x) => x.initConfig);
useEffect(() => { useEffect(() => {
setConfigName(configName); setConfigName(configName);
initConfig(configName, initialConfig); initConfig(configName, initialConfig, increaseVersion);
}, [configName]); }, [configName]);
}; };

View File

@@ -7,21 +7,26 @@ import { useConfigStore } from './store';
export type ConfigContextType = { export type ConfigContextType = {
config: ConfigType | undefined; config: ConfigType | undefined;
name: string | undefined; name: string | undefined;
configVersion: number | undefined;
increaseVersion: () => void;
setConfigName: (name: string) => void; setConfigName: (name: string) => void;
}; };
const ConfigContext = createContext<ConfigContextType>({ const ConfigContext = createContext<ConfigContextType>({
name: 'unknown', name: 'unknown',
config: undefined, config: undefined,
configVersion: undefined,
increaseVersion: () => console.error('Provider not set'),
setConfigName: () => console.error('Provider not set'), setConfigName: () => console.error('Provider not set'),
}); });
export const ConfigProvider = ({ children }: { children: ReactNode }) => { export const ConfigProvider = ({ children }: { children: ReactNode }) => {
const [configName, setConfigName] = useState<string>(); const [configName, setConfigName] = useState<string>();
const [configVersion, setConfigVersion] = useState(0);
const { configs } = useConfigStore((s) => ({ configs: s.configs }), shallow); const { configs } = useConfigStore((s) => ({ configs: s.configs }), shallow);
const { setPrimaryColor, setSecondaryColor, setPrimaryShade } = useColorTheme(); const { setPrimaryColor, setSecondaryColor, setPrimaryShade } = useColorTheme();
const currentConfig = configs.find((c) => c.configProperties.name === configName); const currentConfig = configs.find((c) => c.value.configProperties.name === configName)?.value;
useEffect(() => { useEffect(() => {
setPrimaryColor(currentConfig?.settings.customization.colors.primary || 'red'); setPrimaryColor(currentConfig?.settings.customization.colors.primary || 'red');
@@ -34,6 +39,8 @@ export const ConfigProvider = ({ children }: { children: ReactNode }) => {
value={{ value={{
name: configName, name: configName,
config: currentConfig, config: currentConfig,
configVersion,
increaseVersion: () => setConfigVersion((v) => v + 1),
setConfigName: (name: string) => setConfigName(name), setConfigName: (name: string) => setConfigName(name),
}} }}
> >

View File

@@ -3,33 +3,46 @@ import { ConfigType } from '../types/config';
export const useConfigStore = create<UseConfigStoreType>((set, get) => ({ export const useConfigStore = create<UseConfigStoreType>((set, get) => ({
configs: [], configs: [],
initConfig: (name, config) => { initConfig: (name, config, increaseVersion) => {
set((old) => ({ set((old) => ({
...old, ...old,
configs: [...old.configs.filter((x) => x.configProperties?.name !== name), config], configs: [
...old.configs.filter((x) => x.value.configProperties?.name !== name),
{ increaseVersion, value: config },
],
})); }));
}, },
// TODO: use callback with current config as input updateConfig: async (
updateConfig: async (name, updateCallback: (previous: ConfigType) => ConfigType) => { name,
updateCallback: (previous: ConfigType) => ConfigType,
shouldRegenerateGridstack = false
) => {
const { configs } = get(); const { configs } = get();
const currentConfig = configs.find((x) => x.configProperties.name === name); const currentConfig = configs.find((x) => x.value.configProperties.name === name);
if (!currentConfig) return; if (!currentConfig) return;
// TODO: update config on server // TODO: update config on server
const updatedConfig = updateCallback(currentConfig); const updatedConfig = updateCallback(currentConfig.value);
set((old) => ({ set((old) => ({
...old, ...old,
configs: [...old.configs.filter((x) => x.configProperties.name !== name), updatedConfig], configs: [
...old.configs.filter((x) => x.value.configProperties.name !== name),
{ value: updatedConfig, increaseVersion: currentConfig.increaseVersion },
],
})); }));
if (shouldRegenerateGridstack) {
currentConfig.increaseVersion();
}
}, },
})); }));
interface UseConfigStoreType { interface UseConfigStoreType {
configs: ConfigType[]; configs: { increaseVersion: () => void; value: ConfigType }[];
initConfig: (name: string, config: ConfigType) => void; initConfig: (name: string, config: ConfigType, increaseVersion: () => void) => void;
updateConfig: ( updateConfig: (
name: string, name: string,
updateCallback: (previous: ConfigType) => ConfigType updateCallback: (previous: ConfigType) => ConfigType,
shouldRegenerateGridstack?: boolean
) => Promise<void>; ) => Promise<void>;
} }