mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 23:45:48 +01:00
✨ Change integration structure to array and rename to widgets in config
This commit is contained in:
@@ -22,10 +22,10 @@ export const ChangeIntegrationPositionModal = ({
|
||||
|
||||
updateConfig(configName, (prev) => ({
|
||||
...prev,
|
||||
integrations: {
|
||||
...prev.integrations,
|
||||
widgets: {
|
||||
...prev.widgets,
|
||||
[innerProps.integration]: {
|
||||
...prev.integrations[innerProps.integration],
|
||||
...prev.widgets[innerProps.integration],
|
||||
shape: {
|
||||
location: {
|
||||
x,
|
||||
@@ -55,10 +55,10 @@ export const ChangeIntegrationPositionModal = ({
|
||||
onCancel={handleCancel}
|
||||
heightData={heightData}
|
||||
widthData={widthData}
|
||||
initialX={innerProps.module.shape.location.x}
|
||||
initialY={innerProps.module.shape.location.y}
|
||||
initialWidth={innerProps.module.shape.size.width}
|
||||
initialHeight={innerProps.module.shape.size.height}
|
||||
initialX={innerProps.widget.shape.location.x}
|
||||
initialY={innerProps.widget.shape.location.y}
|
||||
initialWidth={innerProps.widget.shape.size.width}
|
||||
initialHeight={innerProps.widget.shape.size.height}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ReactNode, RefObject } from 'react';
|
||||
|
||||
interface GridstackTileWrapperProps {
|
||||
id: string;
|
||||
type: 'app' | 'module';
|
||||
type: 'app' | 'widget';
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
|
||||
@@ -47,15 +47,15 @@ export const WidgetsEditModal = ({
|
||||
const handleSave = () => {
|
||||
updateConfig(configName, (prev) => ({
|
||||
...prev,
|
||||
integrations: {
|
||||
...prev.integrations,
|
||||
widgets: {
|
||||
...prev.widgets,
|
||||
[innerProps.integration]:
|
||||
'properties' in (prev.integrations[innerProps.integration] ?? {})
|
||||
'properties' in (prev.widgets[innerProps.integration] ?? {})
|
||||
? {
|
||||
...prev.integrations[innerProps.integration],
|
||||
...prev.widgets[innerProps.integration],
|
||||
properties: moduleProperties,
|
||||
}
|
||||
: prev.integrations[innerProps.integration],
|
||||
: prev.widgets[innerProps.integration],
|
||||
},
|
||||
}));
|
||||
context.closeModal(id);
|
||||
|
||||
@@ -8,18 +8,18 @@ import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal';
|
||||
|
||||
export type WidgetChangePositionModalInnerProps = {
|
||||
integration: string;
|
||||
module: IWidget<string, any>;
|
||||
widget: IWidget<string, any>;
|
||||
};
|
||||
|
||||
interface WidgetsMenuProps {
|
||||
integration: string;
|
||||
module: IWidget<string, any> | undefined;
|
||||
widget: IWidget<string, any> | undefined;
|
||||
}
|
||||
|
||||
export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
||||
export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => {
|
||||
const { t } = useTranslation(`modules/${integration}`);
|
||||
|
||||
if (!module) return null;
|
||||
if (!widget) return null;
|
||||
|
||||
const handleDeleteClick = () => {
|
||||
openContextModalGeneric<WidgetsRemoveModalInnerProps>({
|
||||
@@ -38,7 +38,7 @@ export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
||||
title: null,
|
||||
innerProps: {
|
||||
integration,
|
||||
module,
|
||||
widget: widget,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -49,7 +49,7 @@ export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
||||
title: <Title order={4}>{t('descriptor.settings.title')}</Title>,
|
||||
innerProps: {
|
||||
integration,
|
||||
options: module.properties,
|
||||
options: widget.properties,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -59,7 +59,7 @@ export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
||||
handleClickEdit={handleEditClick}
|
||||
handleClickChangePosition={handleChangeSizeClick}
|
||||
handleClickDelete={handleDeleteClick}
|
||||
displayEdit={module.properties !== undefined}
|
||||
displayEdit={widget.properties !== undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ interface DashboardCategoryProps {
|
||||
}
|
||||
|
||||
export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
|
||||
const { refs, items, integrations } = useGridstack('category', category.id);
|
||||
const { refs, items, widgets } = useGridstack('category', category.id);
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
|
||||
return (
|
||||
@@ -45,21 +45,23 @@ export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
|
||||
</GridstackTileWrapper>
|
||||
);
|
||||
})}
|
||||
{Object.entries(integrations).map(([k, v]) => {
|
||||
const widget = Widgets[k as keyof typeof Widgets] as IWidgetDefinition | undefined;
|
||||
if (!widget) return null;
|
||||
{widgets.map((widget) => {
|
||||
const definition = Widgets[widget.id as keyof typeof Widgets] as
|
||||
| IWidgetDefinition
|
||||
| undefined;
|
||||
if (!definition) return null;
|
||||
|
||||
return (
|
||||
<GridstackTileWrapper
|
||||
type="module"
|
||||
key={k}
|
||||
itemRef={refs.items.current[k]}
|
||||
id={widget.id}
|
||||
{...widget.gridstack}
|
||||
{...v.shape.location}
|
||||
{...v.shape.size}
|
||||
type="widget"
|
||||
key={widget.id}
|
||||
itemRef={refs.items.current[widget.id]}
|
||||
id={definition.id}
|
||||
{...definition.gridstack}
|
||||
{...widget.shape.location}
|
||||
{...widget.shape.size}
|
||||
>
|
||||
<widget.component className="grid-stack-item-content" module={v} />
|
||||
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||
</GridstackTileWrapper>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -11,7 +11,7 @@ interface DashboardSidebarProps {
|
||||
}
|
||||
|
||||
export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
||||
const { refs, items, integrations } = useGridstack('sidebar', location);
|
||||
const { refs, items, widgets } = useGridstack('sidebar', location);
|
||||
|
||||
const minRow = useMinRowForFullHeight(refs.wrapper);
|
||||
|
||||
@@ -47,21 +47,23 @@ export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
||||
</GridstackTileWrapper>
|
||||
);
|
||||
})}
|
||||
{Object.entries(integrations).map(([k, v]) => {
|
||||
const widget = Widgets[k as keyof typeof Widgets] as IWidgetDefinition | undefined;
|
||||
if (!widget) return null;
|
||||
{widgets.map((widget) => {
|
||||
const definition = Widgets[widget.id as keyof typeof Widgets] as
|
||||
| IWidgetDefinition
|
||||
| undefined;
|
||||
if (!definition) return null;
|
||||
|
||||
return (
|
||||
<GridstackTileWrapper
|
||||
type="module"
|
||||
key={k}
|
||||
itemRef={refs.items.current[k]}
|
||||
id={widget.id}
|
||||
{...widget.gridstack}
|
||||
{...v.shape.location}
|
||||
{...v.shape.size}
|
||||
type="widget"
|
||||
key={widget.id}
|
||||
itemRef={refs.items.current[widget.id]}
|
||||
id={definition.id}
|
||||
{...definition.gridstack}
|
||||
{...widget.shape.location}
|
||||
{...widget.shape.size}
|
||||
>
|
||||
<widget.component className="grid-stack-item-content" module={v} />
|
||||
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||
</GridstackTileWrapper>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -10,7 +10,7 @@ interface DashboardWrapperProps {
|
||||
}
|
||||
|
||||
export const DashboardWrapper = ({ wrapper }: DashboardWrapperProps) => {
|
||||
const { refs, items, integrations } = useGridstack('wrapper', wrapper.id);
|
||||
const { refs, items, widgets } = useGridstack('wrapper', wrapper.id);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -35,21 +35,23 @@ export const DashboardWrapper = ({ wrapper }: DashboardWrapperProps) => {
|
||||
</GridstackTileWrapper>
|
||||
);
|
||||
})}
|
||||
{Object.entries(integrations).map(([k, v]) => {
|
||||
const widget = Widgets[k as keyof typeof Widgets] as IWidgetDefinition | undefined;
|
||||
if (!widget) return null;
|
||||
{widgets.map((widget) => {
|
||||
const definition = Widgets[widget.id as keyof typeof Widgets] as
|
||||
| IWidgetDefinition
|
||||
| undefined;
|
||||
if (!definition) return null;
|
||||
|
||||
return (
|
||||
<GridstackTileWrapper
|
||||
type="module"
|
||||
key={k}
|
||||
itemRef={refs.items.current[k]}
|
||||
id={widget.id}
|
||||
{...widget.gridstack}
|
||||
{...v.shape.location}
|
||||
{...v.shape.size}
|
||||
type="widget"
|
||||
key={widget.id}
|
||||
itemRef={refs.items.current[widget.id]}
|
||||
id={definition.id}
|
||||
{...definition.gridstack}
|
||||
{...widget.shape.location}
|
||||
{...widget.shape.size}
|
||||
>
|
||||
<widget.component className="grid-stack-item-content" module={v} />
|
||||
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||
</GridstackTileWrapper>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
||||
import { MutableRefObject, RefObject } from 'react';
|
||||
import { IntegrationsType } from '../../../../types/integration';
|
||||
import { AppType } from '../../../../types/app';
|
||||
import { IWidget } from '../../../../widgets/widgets';
|
||||
|
||||
export const initializeGridstack = (
|
||||
areaType: 'wrapper' | 'category' | 'sidebar',
|
||||
@@ -10,7 +10,7 @@ export const initializeGridstack = (
|
||||
itemRefs: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>,
|
||||
areaId: string,
|
||||
items: AppType[],
|
||||
integrations: IntegrationsType,
|
||||
widgets: IWidget<string, any>[],
|
||||
isEditMode: boolean,
|
||||
events: {
|
||||
onChange: (changedNode: GridStackNode) => void;
|
||||
@@ -60,9 +60,10 @@ export const initializeGridstack = (
|
||||
({ id }) =>
|
||||
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
||||
);
|
||||
Object.keys(integrations).forEach(
|
||||
(key) =>
|
||||
itemRefs.current[key] && grid.makeWidget(itemRefs.current[key].current as HTMLDivElement)
|
||||
console.log('widgets', widgets, itemRefs.current);
|
||||
widgets.forEach(
|
||||
({ id }) =>
|
||||
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
||||
);
|
||||
grid.batchUpdate(false);
|
||||
};
|
||||
|
||||
@@ -11,15 +11,14 @@ import {
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
import { useResize } from '../../../../hooks/use-resize';
|
||||
import { IntegrationsType } from '../../../../types/integration';
|
||||
import { AppType } from '../../../../types/app';
|
||||
import { TileBaseType } from '../../../../types/tile';
|
||||
import { IWidget } from '../../../../widgets/widgets';
|
||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||
import { initializeGridstack } from './init-gridstack';
|
||||
|
||||
interface UseGristackReturnType {
|
||||
items: AppType[];
|
||||
integrations: Partial<IntegrationsType>;
|
||||
widgets: IWidget<string, any>[];
|
||||
refs: {
|
||||
wrapper: RefObject<HTMLDivElement>;
|
||||
items: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>;
|
||||
@@ -54,32 +53,24 @@ export const useGridstack = (
|
||||
) ?? [],
|
||||
[config]
|
||||
);
|
||||
const integrations = useMemo(() => {
|
||||
if (!config) return;
|
||||
return (Object.entries(config.integrations) as [keyof IntegrationsType, TileBaseType][])
|
||||
.filter(
|
||||
([k, v]) =>
|
||||
v.area.type === areaType &&
|
||||
(v.area.type === 'sidebar'
|
||||
? v.area.properties.location === areaId
|
||||
: v.area.properties.id === areaId)
|
||||
)
|
||||
.reduce((prev, [k, v]) => {
|
||||
prev[k] = v as unknown as any;
|
||||
return prev;
|
||||
}, {} as IntegrationsType);
|
||||
const widgets = useMemo(() => {
|
||||
if (!config) return [];
|
||||
return config.widgets.filter(
|
||||
(w) =>
|
||||
w.area.type === areaType &&
|
||||
(w.area.type === 'sidebar'
|
||||
? w.area.properties.location === areaId
|
||||
: w.area.properties.id === areaId)
|
||||
);
|
||||
}, [config]);
|
||||
|
||||
// define items in itemRefs for easy access and reference to items
|
||||
if (
|
||||
Object.keys(itemRefs.current).length !==
|
||||
items.length + Object.keys(integrations ?? {}).length
|
||||
) {
|
||||
if (Object.keys(itemRefs.current).length !== items.length + (widgets ?? []).length) {
|
||||
items.forEach(({ id }: { id: keyof typeof itemRefs.current }) => {
|
||||
itemRefs.current[id] = itemRefs.current[id] || createRef();
|
||||
});
|
||||
Object.keys(integrations ?? {}).forEach((k) => {
|
||||
itemRefs.current[k] = itemRefs.current[k] || createRef();
|
||||
(widgets ?? []).forEach(({ id }) => {
|
||||
itemRefs.current[id] = itemRefs.current[id] || createRef();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,7 +93,7 @@ export const useGridstack = (
|
||||
const currentItem =
|
||||
itemType === 'app'
|
||||
? previous.apps.find((x) => x.id === itemId)
|
||||
: previous.integrations[itemId as keyof typeof previous.integrations];
|
||||
: previous.widgets.find((x) => x.id === itemId);
|
||||
if (!currentItem) return previous;
|
||||
|
||||
currentItem.shape = {
|
||||
@@ -126,11 +117,12 @@ export const useGridstack = (
|
||||
};
|
||||
}
|
||||
|
||||
const integrationsCopy = { ...previous.integrations };
|
||||
integrationsCopy[itemId as keyof typeof integrationsCopy] = currentItem as any;
|
||||
return {
|
||||
...previous,
|
||||
integrations: integrationsCopy,
|
||||
widgets: [
|
||||
...previous.widgets.filter((x) => x.id !== itemId),
|
||||
{ ...(currentItem as IWidget<string, any>) },
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -149,8 +141,7 @@ export const useGridstack = (
|
||||
const currentItem =
|
||||
itemType === 'app'
|
||||
? previous.apps.find((x) => x.id === itemId)
|
||||
: previous.integrations[itemId as keyof typeof previous.integrations];
|
||||
|
||||
: previous.widgets.find((x) => x.id === itemId);
|
||||
if (!currentItem) return previous;
|
||||
|
||||
if (areaType === 'sidebar') {
|
||||
@@ -190,11 +181,12 @@ export const useGridstack = (
|
||||
};
|
||||
}
|
||||
|
||||
const integrationsCopy = { ...previous.integrations };
|
||||
integrationsCopy[itemId as keyof typeof integrationsCopy] = currentItem as any;
|
||||
return {
|
||||
...previous,
|
||||
integrations: integrationsCopy,
|
||||
widgets: [
|
||||
...previous.widgets.filter((x) => x.id !== itemId),
|
||||
{ ...(currentItem as IWidget<string, any>) },
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -209,18 +201,18 @@ export const useGridstack = (
|
||||
itemRefs,
|
||||
areaId,
|
||||
items,
|
||||
integrations ?? {},
|
||||
widgets ?? [],
|
||||
isEditMode,
|
||||
{
|
||||
onChange,
|
||||
onAdd,
|
||||
}
|
||||
);
|
||||
}, [items.length, wrapperRef.current, Object.keys(integrations ?? {}).length]);
|
||||
}, [items.length, wrapperRef.current, (widgets ?? []).length]);
|
||||
|
||||
return {
|
||||
items,
|
||||
integrations: integrations ?? {},
|
||||
widgets: widgets ?? [],
|
||||
refs: {
|
||||
items: itemRefs,
|
||||
wrapper: wrapperRef,
|
||||
|
||||
Reference in New Issue
Block a user