🐛 Issue with moving items arround with change-position-modal

This commit is contained in:
Meier Lukas
2023-10-08 21:00:10 +02:00
parent 4ad87fcf89
commit f8dfc3b23b
12 changed files with 334 additions and 400 deletions

View File

@@ -1,21 +1,19 @@
import { Button } from '@mantine/core';
import { GridItemHTMLElement } from 'fily-publish-gridstack';
import { EmptySection, useRequiredBoard } from '~/components/Board/context';
import { EmptySection } from '~/components/Board/context';
import { GridstackProvider } from '~/components/Board/gridstack/context';
import { useItemActions } from '~/components/Board/item-actions';
import { useEditModeStore } from '../../Views/useEditModeStore';
import { WrapperContent } from '../WrapperContent';
import { useGridstack } from '../gridstack/use-gridstack';
import { useEditModeStore } from '../../Dashboard/Views/useEditModeStore';
import { WrapperContent } from '../../Dashboard/Wrappers/WrapperContent';
import { useGridstack } from '../../Dashboard/Wrappers/gridstack/use-gridstack';
interface DashboardWrapperProps {
interface EmptySectionWrapperProps {
section: EmptySection;
}
export const DashboardWrapper = ({ section }: DashboardWrapperProps) => {
const defaultClasses = 'grid-stack grid-stack-empty min-row';
export const EmptySectionWrapper = ({ section }: EmptySectionWrapperProps) => {
const { refs } = useGridstack({ section });
const isEditMode = useEditModeStore((x) => x.enabled);
const defaultClasses = 'grid-stack grid-stack-empty min-row';
return (
<GridstackProvider gridstackRef={refs.gridstack}>
@@ -29,6 +27,7 @@ export const DashboardWrapper = ({ section }: DashboardWrapperProps) => {
data-empty={section.id}
ref={refs.wrapper}
>
{section.items.length === 0 && <span></span>}
<WrapperContent items={section.items} refs={refs} />
</div>
</GridstackProvider>

View File

@@ -0,0 +1,204 @@
import Consola from 'consola';
import { useCallback } from 'react';
import { v4 } from 'uuid';
import { api } from '~/utils/api';
import { type CategorySection, type EmptySection } from './context';
type AddCategory = {
name: string;
position: number;
};
type RenameCategory = {
id: string;
name: string;
};
type MoveCategory = {
id: string;
direction: 'up' | 'down';
};
type RemoveCategory = {
id: string;
};
export const useCategoryActions = ({ boardName }: { boardName: string }) => {
const utils = api.useContext();
const addCategory = useCallback(
({ name, position }: AddCategory) => {
if (position <= 0) {
Consola.error('Cannot add category before first section');
return;
}
utils.boards.byName.setData({ boardName }, (prev) => {
if (!prev) return prev;
return {
...prev,
sections: [
// Ignore sidebar and hidden sections
...prev.sections.filter(
(section) => section.type === 'sidebar' || section.type === 'hidden'
),
// Place sections before the new category
...prev.sections.filter(
(section) =>
(section.type === 'category' || section.type === 'empty') &&
section.position < position
),
{
id: v4(),
name,
type: 'category',
position,
items: [],
},
{
id: v4(),
type: 'empty',
position: position + 1,
items: [],
},
// Place sections after the new category
...prev.sections
.filter(
(section): section is CategorySection | EmptySection =>
(section.type === 'category' || section.type === 'empty') &&
section.position >= position
)
.map((section) => ({
...section,
position: section.position + 2,
})),
],
};
});
},
[boardName, utils]
);
const renameCategory = useCallback(
({ id: categoryId, name }: RenameCategory) => {
utils.boards.byName.setData({ boardName }, (prev) => {
if (!prev) return prev;
return {
...prev,
sections: prev.sections.map((section) => {
if (section.type !== 'category') return section;
if (section.id !== categoryId) return section;
return {
...section,
name,
};
}),
};
});
},
[boardName, utils]
);
const moveCategory = useCallback(
({ id: categoryId, direction }: MoveCategory) => {
utils.boards.byName.setData({ boardName }, (prev) => {
if (!prev) return prev;
const currentCategory = prev.sections.find(
(section): section is CategorySection =>
section.type === 'category' && section.id === categoryId
);
if (!currentCategory) return prev;
if (currentCategory?.position === 1 && direction === 'up') return prev;
if (currentCategory?.position === prev.sections.length - 2 && direction === 'down')
return prev;
return {
...prev,
sections: prev.sections.map((section) => {
if (section.type !== 'category' && section.type !== 'empty') return section;
const offset = direction === 'up' ? -2 : 2;
// Move category and empty section
if (
section.position === currentCategory.position ||
section.position - 1 === currentCategory.position
) {
return {
...section,
position: section.position + offset,
};
}
// Move all sections behind
if (section.position >= currentCategory.position + offset) {
return {
...section,
position: section.position + 2,
};
}
if (
direction === 'up' &&
(section.position === currentCategory.position + 2 ||
section.position === currentCategory.position + 3)
) {
return {
...section,
position: section.position - 2,
};
}
return section;
}),
};
});
},
[boardName, utils]
);
const removeCategory = useCallback(
({ id: categoryId }: RemoveCategory) => {
utils.boards.byName.setData({ boardName }, (prev) => {
if (!prev) return prev;
const currentCategory = prev.sections.find(
(section): section is CategorySection =>
section.type === 'category' && section.id === categoryId
);
if (!currentCategory) return prev;
return {
...prev,
sections: [
...prev.sections.filter(
(section) => section.type === 'sidebar' || section.type === 'hidden'
),
...prev.sections.filter(
(section) =>
(section.type === 'category' || section.type === 'empty') &&
section.position < currentCategory.position
),
...prev.sections
.filter(
(section): section is CategorySection | EmptySection =>
(section.type === 'category' || section.type === 'empty') &&
section.position >= currentCategory.position + 2
)
.map((section) => ({
...section,
position: section.position - 2,
})),
],
};
});
},
[boardName, utils]
);
return {
addCategory,
renameCategory,
moveCategory,
removeCategory,
};
};

View File

@@ -11,8 +11,6 @@ const GridstackContext = createContext<GridstackContextProps>({
export const useGridstackRef = () => {
const ctx = useContext(GridstackContext);
if (!ctx || !ctx.ref)
throw new Error('useGridstackContext must be used within a GridstackProvider');
return ctx.ref;
};

View File

@@ -13,12 +13,10 @@ export const useResizeGridItem = () => {
const gridstackRef = useGridstackRef();
return ({ height, width, ...options }: ResizeGridItemProps) => {
gridstackRef.current?.batchUpdate();
gridstackRef.current?.update(itemRef.current!, {
gridstackRef?.current?.update(itemRef.current!, {
...options,
h: height,
w: width,
});
gridstackRef.current?.batchUpdate(false);
};
};

View File

@@ -27,6 +27,7 @@ export const useItemActions = ({ boardName }: { boardName: string }) => {
const utils = api.useContext();
const moveAndResizeItem = useCallback(
({ itemId, ...positionProps }: MoveAndResizeItem) => {
console.log(itemId, positionProps);
utils.boards.byName.setData({ boardName }, (prev) => {
if (!prev) return prev;
return {
@@ -54,6 +55,7 @@ export const useItemActions = ({ boardName }: { boardName: string }) => {
const moveItemToSection = useCallback(
({ itemId, sectionId, ...positionProps }: MoveItemToSection) => {
console.log('moveItemToSection', itemId, sectionId, positionProps);
utils.boards.byName.setData({ boardName }, (prev) => {
if (!prev) return prev;
@@ -62,10 +64,16 @@ export const useItemActions = ({ boardName }: { boardName: string }) => {
);
// If item is in the same section (on initial loading) don't do anything
if (!currentSection || currentSection.id === sectionId) return prev;
if (!currentSection || currentSection.id === sectionId) {
console.log('Current section is undefined or same as target section');
return prev;
}
let currentItem = currentSection?.items.find((item) => item.id === itemId);
if (!currentItem) return prev;
if (!currentItem) {
console.log('Current item is undefined');
return prev;
}
return {
...prev,

View File

@@ -9,9 +9,9 @@ import {
import { useResize } from '~/hooks/use-resize';
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
import { EmptySectionWrapper } from '../../Board/Sections/EmptySection';
import { DashboardCategory } from '../Wrappers/Category/Category';
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
import { DashboardWrapper } from '../Wrappers/Wrapper/Wrapper';
import { useGridstackStore } from '../Wrappers/gridstack/store';
export const BoardView = () => {
@@ -43,7 +43,7 @@ export const BoardView = () => {
item.type === 'category' ? (
<DashboardCategory key={item.id} section={item} />
) : (
<DashboardWrapper key={item.id} section={item} />
<EmptySectionWrapper key={item.id} section={item} />
)
)}
</Stack>
@@ -55,17 +55,6 @@ export const BoardView = () => {
</Box>
);
};
// <DashboardCategory key={item.id} category={item as unknown as CategoryType} />
// <DashboardWrapper key={item.id} wrapper={item as WrapperType} />
/*
{sidebarsVisible.left ? (
<DashboardSidebar location="left" isGridstackReady={isReady} />
) : null}
{sidebarsVisible.right ? (
<DashboardSidebar location="right" isGridstackReady={isReady} />
) : null}
*/
const usePrepareGridstack = () => {
const mainAreaRef = useRef<HTMLDivElement>(null);

View File

@@ -1,40 +0,0 @@
import { ActionIcon, Button, Text, Tooltip } from '@mantine/core';
import { IconEdit, IconEditOff } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next';
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
import { useEditModeStore } from './useEditModeStore';
export const ViewToggleButton = () => {
const screenLargerThanMd = useScreenLargerThan('md');
const { enabled: isEditMode, toggleEditMode } = useEditModeStore();
const { t } = useTranslation('layout/header/actions/toggle-edit-mode');
return (
<Tooltip width={100} label={<Text align="center">{t('description')}</Text>}>
{screenLargerThanMd ? (
<Button
variant={isEditMode ? 'filled' : 'default'}
h={44}
w={180}
leftIcon={isEditMode ? <IconEditOff /> : <IconEdit />}
onClick={() => toggleEditMode()}
color={isEditMode ? 'red' : undefined}
radius="md"
>
<Text>{isEditMode ? t('button.enabled') : t('button.disabled')}</Text>
</Button>
) : (
<ActionIcon
onClick={() => toggleEditMode()}
variant="default"
radius="md"
size="xl"
color="blue"
>
{isEditMode ? <IconEditOff /> : <IconEdit />}
</ActionIcon>
)}
</Tooltip>
);
};

View File

@@ -8,11 +8,11 @@ import {
IconTransitionTop,
IconTrash,
} from '@tabler/icons-react';
import { useTranslation } from 'next-i18next';
import { useConfigContext } from '~/config/provider';
import { CategoryType } from '~/types/category';
import { useCategoryActions } from './useCategoryActions';
import { useTranslation } from 'next-i18next';
import { useCategoryActionHelper } from './useCategoryActions';
interface CategoryEditMenuProps {
category: CategoryType;
@@ -21,8 +21,8 @@ interface CategoryEditMenuProps {
export const CategoryEditMenu = ({ category }: CategoryEditMenuProps) => {
const { name: configName } = useConfigContext();
const { addCategoryAbove, addCategoryBelow, moveCategoryUp, moveCategoryDown, edit, remove } =
useCategoryActions(configName, category);
const { t } = useTranslation(['layout/common','common']);
useCategoryActionHelper(configName, category);
const { t } = useTranslation(['layout/common', 'common']);
return (
<Menu withinPortal withArrow>
@@ -33,28 +33,24 @@ export const CategoryEditMenu = ({ category }: CategoryEditMenuProps) => {
</Menu.Target>
<Menu.Dropdown>
<Menu.Item icon={<IconEdit size={20} />} onClick={edit}>
{t('common:edit')}
{t('common:edit')}
</Menu.Item>
<Menu.Item icon={<IconTrash size={20} />} onClick={remove}>
{t('common:remove')}
</Menu.Item>
<Menu.Label>
{t('common:changePosition')}
</Menu.Label>
<Menu.Label>{t('common:changePosition')}</Menu.Label>
<Menu.Item icon={<IconTransitionTop size={20} />} onClick={moveCategoryUp}>
{t('menu.moveUp')}
</Menu.Item>
<Menu.Item icon={<IconTransitionBottom size={20} />} onClick={moveCategoryDown}>
{t('menu.moveDown')}
</Menu.Item>
<Menu.Label>
{t('menu.addCategory',{location: ''})}
</Menu.Label>
<Menu.Label>{t('menu.addCategory', { location: '' })}</Menu.Label>
<Menu.Item icon={<IconRowInsertTop size={20} />} onClick={addCategoryAbove}>
{t('menu.addCategory',{location: t('menu.addAbove')})}
{t('menu.addCategory', { location: t('menu.addAbove') })}
</Menu.Item>
<Menu.Item icon={<IconRowInsertBottom size={20} />} onClick={addCategoryBelow}>
{t('menu.addCategory',{location: t('menu.addBelow')})}
{t('menu.addCategory', { location: t('menu.addBelow') })}
</Menu.Item>
</Menu.Dropdown>
</Menu>

View File

@@ -1,15 +1,20 @@
import { v4 as uuidv4 } from 'uuid';
import { useCategoryActions } from '~/components/Board/category-actions';
import { useRequiredBoard } from '~/components/Board/context';
import { useConfigStore } from '~/config/store';
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
import { AppType } from '~/types/app';
import { CategoryType } from '~/types/category';
import { WrapperType } from '~/types/wrapper';
import { IWidget } from '~/widgets/widgets';
import { CategoryEditModalInnerProps } from './CategoryEditModal';
export const useCategoryActions = (configName: string | undefined, category: CategoryType) => {
const updateConfig = useConfigStore((x) => x.updateConfig);
export const useCategoryActionHelper = (configName: string | undefined, category: CategoryType) => {
const boardName = useRequiredBoard().name;
const { addCategory, moveCategory, removeCategory, renameCategory } = useCategoryActions({
boardName,
});
// creates a new category above the current
const addCategoryAbove = () => {
@@ -24,43 +29,10 @@ export const useCategoryActions = (configName: string | undefined, category: Cat
position: abovePosition + 1,
},
onSuccess: async (category) => {
if (!configName) return;
const newWrapper: WrapperType = {
id: uuidv4(),
position: abovePosition + 2,
};
// Adding category and wrapper and moving other items down
updateConfig(
configName,
(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 belowCategories = previous.categories.filter((x) => x.position > abovePosition);
return {
...previous,
categories: [
...aboveCategories,
category,
// Move categories below down
...belowCategories.map((x) => ({ ...x, position: x.position + 1 })),
],
wrappers: [
...aboveWrappers,
newWrapper,
// Move wrappers below down
...belowWrappers.map((x) => ({ ...x, position: x.position + 1 })),
],
};
},
true
);
addCategory({
name: category.name,
position: category.position,
});
},
},
});
@@ -79,194 +51,35 @@ export const useCategoryActions = (configName: string | undefined, category: Cat
position: belowPosition + 1,
},
onSuccess: async (category) => {
if (!configName) return;
const newWrapper: WrapperType = {
id: uuidv4(),
position: belowPosition,
};
// Adding category and wrapper and moving other items down
updateConfig(
configName,
(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 belowCategories = previous.categories.filter(
(x) => x.position >= belowPosition
);
return {
...previous,
categories: [
...aboveCategories,
category,
// Move categories below down
...belowCategories.map((x) => ({ ...x, position: x.position + 2 })),
],
wrappers: [
...aboveWrappers,
newWrapper,
// Move wrappers below down
...belowWrappers.map((x) => ({ ...x, position: x.position + 2 })),
],
};
},
true
);
addCategory({
name: category.name,
position: category.position,
});
},
},
});
};
const moveCategoryUp = () => {
if (!configName) return;
updateConfig(
configName,
(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 - 1);
if (!upperItem) return previous;
currentItem.position -= 1;
upperItem.position += 1;
return {
...previous,
categories: [
...previous.categories.filter((c) => ![currentItem.id, upperItem.id].includes(c.id)),
{ ...upperItem },
{ ...currentItem },
],
};
},
true
);
moveCategory({
id: category.id,
direction: 'up',
});
};
const moveCategoryDown = () => {
if (!configName) return;
updateConfig(
configName,
(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 + 1);
if (!belowItem) return previous;
currentItem.position += 1;
belowItem.position -= 1;
return {
...previous,
categories: [
...previous.categories.filter((c) => ![currentItem.id, belowItem.id].includes(c.id)),
{ ...currentItem },
{ ...belowItem },
],
};
},
true
);
moveCategory({
id: category.id,
direction: 'down',
});
};
// Removes the current category
const remove = () => {
if (!configName) return;
updateConfig(
configName,
(previous) => {
const currentItem = previous.categories.find((x) => x.id === category.id);
if (!currentItem) return previous;
// Find the main wrapper
const mainWrapper = previous.wrappers.find((x) => x.position === 0);
const mainWrapperId = mainWrapper?.id ?? 'default';
const isAppAffectedFilter = (app: AppType): boolean => {
if (!app.area) {
return false;
}
if (app.area.type !== 'category') {
return false;
}
if (app.area.properties.id === mainWrapperId) {
return false;
}
return app.area.properties.id === currentItem.id;
};
const isWidgetAffectedFilter = (widget: IWidget<string, any>): boolean => {
if (!widget.area) {
return false;
}
if (widget.area.type !== 'category') {
return false;
}
if (widget.area.properties.id === mainWrapperId) {
return false;
}
return widget.area.properties.id === currentItem.id;
};
return {
...previous,
apps: [
...previous.apps.filter((x) => !isAppAffectedFilter(x)),
...previous.apps
.filter((x) => isAppAffectedFilter(x))
.map(
(app): AppType => ({
...app,
area: {
...app.area,
type: 'wrapper',
properties: {
...app.area.properties,
id: mainWrapperId,
},
},
})
),
],
widgets: [
...previous.widgets.filter((widget) => !isWidgetAffectedFilter(widget)),
...previous.widgets
.filter((widget) => isWidgetAffectedFilter(widget))
.map(
(widget): IWidget<string, any> => ({
...widget,
area: {
...widget.area,
type: 'wrapper',
properties: {
...widget.area.properties,
id: mainWrapperId,
},
},
})
),
],
categories: previous.categories.filter((x) => x.id !== category.id),
wrappers: previous.wrappers.filter((x) => x.position !== currentItem.position),
};
},
true
);
// TODO: contained apps are currently just deleted
removeCategory({
id: category.id,
});
};
const edit = async () => {
@@ -276,14 +89,9 @@ export const useCategoryActions = (configName: string | undefined, category: Cat
innerProps: {
category,
onSuccess: async (category) => {
if (!configName) return;
await updateConfig(configName, (prev) => {
const currentCategory = prev.categories.find((c) => c.id === category.id);
if (!currentCategory) return prev;
return {
...prev,
categories: [...prev.categories.filter((c) => c.id !== category.id), { ...category }],
};
renameCategory({
id: category.id,
name: category.name,
});
},
},

View File

@@ -11,10 +11,6 @@ type InitializeGridstackProps = {
};
isEditMode: boolean;
sectionColumnCount: number;
events: {
onChange: (changedNode: GridStackNode) => void;
onAdd: (addedNode: GridStackNode) => void;
};
};
export const initializeGridstack = ({
@@ -22,7 +18,6 @@ export const initializeGridstack = ({
refs,
isEditMode,
sectionColumnCount,
events,
}: InitializeGridstackProps) => {
if (!refs.wrapper.current) return;
// calculates the currently available count of columns
@@ -52,22 +47,6 @@ export const initializeGridstack = ({
// Must be used to update the column count after the initialization
grid.column(columnCount, 'none');
// Add listener for moving items around in a wrapper
grid.on('change', (_, el) => {
const nodes = el as GridStackNode[];
const firstNode = nodes.at(0);
if (!firstNode) return;
events.onChange(firstNode);
});
// Add listener for moving items in config from one wrapper to another
grid.on('added', (_, el) => {
const nodes = el as GridStackNode[];
const firstNode = nodes.at(0);
if (!firstNode) return;
events.onAdd(firstNode);
});
grid.batchUpdate();
grid.removeAll(false);
section.items.forEach((item) => {

View File

@@ -1,19 +1,13 @@
import { type GridItemHTMLElement, GridStack, type GridStackNode } from 'fily-publish-gridstack';
import {
GridItemHTMLElement,
GridStack,
GridStackElement,
GridStackNode,
} from 'fily-publish-gridstack';
import {
MutableRefObject,
RefObject,
type MutableRefObject,
type RefObject,
createRef,
useCallback,
useEffect,
useMemo,
useRef,
} from 'react';
import { Section, useRequiredBoard } from '~/components/Board/context';
import { type Section, useRequiredBoard } from '~/components/Board/context';
import { useItemActions } from '~/components/Board/item-actions';
import { useEditModeStore } from '../../Views/useEditModeStore';
@@ -59,7 +53,7 @@ export const useGridstack = ({ section }: UseGridstackProps): UseGristackReturnT
// define items in itemRefs for easy access and reference to items
if (Object.keys(itemRefs.current).length !== items.length) {
items.forEach(({ id }: { id: keyof typeof itemRefs.current }) => {
itemRefs.current[id] = itemRefs.current[id] || createRef();
itemRefs.current[id] = itemRefs.current[id] ?? createRef();
});
}
@@ -80,53 +74,60 @@ export const useGridstack = ({ section }: UseGridstackProps): UseGristackReturnT
gridRef.current?.setStatic(!isEditMode);
}, [isEditMode]);
const onChange = useCallback(
(changedNode: GridStackNode) => {
if (!isEditMode) return;
const onChange = (changedNode: GridStackNode) => {
const itemType = changedNode.el?.getAttribute('data-type');
const itemId = changedNode.el?.getAttribute('data-id');
if (!itemType || !itemId) return;
const itemType = changedNode.el?.getAttribute('data-type');
const itemId = changedNode.el?.getAttribute('data-id');
if (!itemType || !itemId) return;
// Updates the react-query state
moveAndResizeItem({
itemId,
x: changedNode.x!,
y: changedNode.y!,
width: changedNode.w!,
height: changedNode.h!,
});
};
const onAdd = (addedNode: GridStackNode) => {
const itemType = addedNode.el?.getAttribute('data-type');
const itemId = addedNode.el?.getAttribute('data-id');
if (!itemType || !itemId) return;
// Updates the config and defines the new position of the item
moveAndResizeItem({
itemId,
x: changedNode.x!,
y: changedNode.y!,
width: changedNode.w!,
height: changedNode.h!,
});
},
[isEditMode]
);
// Updates the react-query state
moveItemToSection({
itemId,
sectionId: section.id,
x: addedNode.x!,
y: addedNode.y!,
width: addedNode.w!,
height: addedNode.h!,
});
};
const onAdd = useCallback(
(addedNode: GridStackNode) => {
if (!isEditMode) return;
useEffect(() => {
if (!isEditMode) return;
// Add listener for moving items around in a wrapper
gridRef.current?.on('change', (_, nodes) => {
(nodes as GridStackNode[]).forEach(onChange);
});
const itemType = addedNode.el?.getAttribute('data-type');
const itemId = addedNode.el?.getAttribute('data-id');
if (!itemType || !itemId) return;
// Add listener for moving items in config from one wrapper to another
gridRef.current?.on('added', (_, el) => {
const nodes = el as GridStackNode[];
const firstNode = nodes.at(0);
if (!firstNode) return;
onAdd(firstNode);
});
moveItemToSection({
itemId,
sectionId: section.id,
x: addedNode.x!,
y: addedNode.y!,
width: addedNode.w!,
height: addedNode.h!,
});
},
[isEditMode]
);
return () => {
gridRef.current?.off('change');
gridRef.current?.off('added');
};
}, [isEditMode]);
// initialize the gridstack
useEffect(() => {
initializeGridstack({
events: {
onChange,
onAdd,
},
isEditMode,
section,
refs: {
@@ -136,13 +137,7 @@ export const useGridstack = ({ section }: UseGridstackProps): UseGristackReturnT
},
sectionColumnCount,
});
// Remove event listeners on unmount
return () => {
gridRef.current?.off('change');
gridRef.current?.off('added');
};
}, [items, wrapperRef.current, sectionColumnCount]);
}, [items.length, wrapperRef.current, sectionColumnCount]);
return {
refs: {

View File

@@ -245,8 +245,8 @@ export const boardRouter = createTRPCRouter({
iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/github.png',
width: 2,
height: 1,
x: 3,
y: 1,
x: 2,
y: 0,
});
await addApp({
@@ -257,8 +257,8 @@ export const boardRouter = createTRPCRouter({
iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/discord.png',
width: 2,
height: 1,
x: 5,
y: 1,
x: 4,
y: 0,
});
await addApp({
@@ -269,8 +269,8 @@ export const boardRouter = createTRPCRouter({
iconUrl: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/ko-fi.png',
width: 2,
height: 1,
x: 7,
y: 1,
x: 6,
y: 0,
});
await addApp({
@@ -281,8 +281,8 @@ export const boardRouter = createTRPCRouter({
iconUrl: '/imgs/logo/logo.png',
width: 2,
height: 2,
x: 7,
y: 2,
x: 6,
y: 1,
});
await addWidget({
@@ -291,8 +291,8 @@ export const boardRouter = createTRPCRouter({
type: 'weather',
width: 2,
height: 1,
x: 1,
y: 1,
x: 0,
y: 0,
});
await addWidget({
@@ -301,8 +301,8 @@ export const boardRouter = createTRPCRouter({
type: 'date',
width: 2,
height: 1,
x: 9,
y: 1,
x: 8,
y: 0,
});
await addWidget({
@@ -311,8 +311,8 @@ export const boardRouter = createTRPCRouter({
type: 'date',
width: 2,
height: 1,
x: 9,
y: 2,
x: 8,
y: 1,
});
await addWidget({
@@ -321,8 +321,8 @@ export const boardRouter = createTRPCRouter({
type: 'notebook',
width: 6,
height: 3,
x: 1,
y: 2,
x: 0,
y: 1,
});
}),
});