* chore: add parent_section_id and change position to x and y_offset for sqlite section table * chore: rename existing positions to x_offset and y_offset * chore: add related mysql migration * chore: add missing height and width to section table * fix: missing width and height in migration copy script * fix: typecheck issues * fix: test not working caused by unsimilar schemas * wip: add dynamic section * refactor: improve structure of gridstack sections * feat: add rendering of dynamic sections * feat: add saving of moved sections * wip: add static row count, restrict min-width and height * chore: address pull request feedback * fix: format issues * fix: size calculation within dynamic sections * fix: on resize not called when min width or height is reached * fix: size of items while dragging is to big * chore: temporarly remove migration files * chore: readd migrations * fix: format and deepsource issues * chore: remove db_dev.sqlite file * chore: add *.sqlite to .gitignore * chore: address pull request feedback * feat: add dynamic section actions for adding and removing them
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
import { useCallback } from "react";
|
|
|
|
import { createId } from "@homarr/db/client";
|
|
|
|
import type { DynamicSection, EmptySection } from "~/app/[locale]/boards/_types";
|
|
import { useUpdateBoard } from "~/app/[locale]/boards/(content)/_client";
|
|
|
|
interface RemoveDynamicSection {
|
|
id: string;
|
|
}
|
|
|
|
export const useDynamicSectionActions = () => {
|
|
const { updateBoard } = useUpdateBoard();
|
|
|
|
const addDynamicSection = useCallback(() => {
|
|
updateBoard((previous) => {
|
|
const lastSection = previous.sections
|
|
.filter((section): section is EmptySection => section.kind === "empty")
|
|
.sort((sectionA, sectionB) => sectionB.yOffset - sectionA.yOffset)[0];
|
|
|
|
if (!lastSection) return previous;
|
|
|
|
const newSection = {
|
|
id: createId(),
|
|
kind: "dynamic",
|
|
height: 1,
|
|
width: 1,
|
|
items: [],
|
|
parentSectionId: lastSection.id,
|
|
// We omit xOffset and yOffset because gridstack will use the first available position
|
|
} satisfies Omit<DynamicSection, "xOffset" | "yOffset">;
|
|
|
|
return {
|
|
...previous,
|
|
sections: previous.sections.concat(newSection as unknown as DynamicSection),
|
|
};
|
|
});
|
|
}, [updateBoard]);
|
|
|
|
const removeDynamicSection = useCallback(
|
|
({ id }: RemoveDynamicSection) => {
|
|
updateBoard((previous) => {
|
|
const sectionToRemove = previous.sections.find(
|
|
(section): section is DynamicSection => section.id === id && section.kind === "dynamic",
|
|
);
|
|
if (!sectionToRemove) return previous;
|
|
|
|
return {
|
|
...previous,
|
|
sections: previous.sections
|
|
.filter((section) => section.id !== id)
|
|
.map((section) => {
|
|
if (section.id === sectionToRemove.parentSectionId) {
|
|
return {
|
|
...section,
|
|
// Add items from the removed section to the parent section
|
|
items: section.items.concat(
|
|
sectionToRemove.items.map((item) => ({
|
|
...item,
|
|
xOffset: sectionToRemove.xOffset + item.xOffset,
|
|
yOffset: sectionToRemove.yOffset + item.yOffset,
|
|
})),
|
|
),
|
|
};
|
|
}
|
|
|
|
if (section.kind === "dynamic" && section.parentSectionId === sectionToRemove.id) {
|
|
// Change xOffset and yOffset of sections that were below the removed section and set parentSectionId to the parent of the removed section
|
|
return {
|
|
...section,
|
|
parentSectionId: sectionToRemove.parentSectionId,
|
|
yOffset: section.yOffset + sectionToRemove.yOffset,
|
|
xOffset: section.xOffset + sectionToRemove.xOffset,
|
|
};
|
|
}
|
|
|
|
return section;
|
|
}),
|
|
};
|
|
});
|
|
},
|
|
[updateBoard],
|
|
);
|
|
|
|
return {
|
|
addDynamicSection,
|
|
removeDynamicSection,
|
|
};
|
|
};
|