* 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
71 lines
2.6 KiB
TypeScript
71 lines
2.6 KiB
TypeScript
import { useMemo } from "react";
|
|
|
|
import type { DynamicSection, Item, Section } from "~/app/[locale]/boards/_types";
|
|
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
|
import { BoardItemContent } from "../items/item-content";
|
|
import { BoardDynamicSection } from "./dynamic-section";
|
|
import { GridStackItem } from "./gridstack/gridstack-item";
|
|
import { useSectionContext } from "./section-context";
|
|
|
|
export const SectionContent = () => {
|
|
const { section, innerSections, refs } = useSectionContext();
|
|
const board = useRequiredBoard();
|
|
const sortedItems = useMemo(() => {
|
|
return [
|
|
...section.items.map((item) => ({ ...item, type: "item" as const })),
|
|
...innerSections.map((section) => ({ ...section, type: "section" as const })),
|
|
].sort((itemA, itemB) => {
|
|
if (itemA.yOffset === itemB.yOffset) {
|
|
return itemA.xOffset - itemB.xOffset;
|
|
}
|
|
|
|
return itemA.yOffset - itemB.xOffset;
|
|
});
|
|
}, [section.items, innerSections]);
|
|
|
|
return (
|
|
<>
|
|
{sortedItems.map((item) => (
|
|
<GridStackItem
|
|
key={item.id}
|
|
innerRef={refs.items.current[item.id]}
|
|
width={item.width}
|
|
height={item.height}
|
|
xOffset={item.xOffset}
|
|
yOffset={item.yOffset}
|
|
kind={item.kind}
|
|
id={item.id}
|
|
type={item.type}
|
|
minWidth={item.type === "section" ? getMinSize("x", item.items, board.sections, item.id) : undefined}
|
|
minHeight={item.type === "section" ? getMinSize("y", item.items, board.sections, item.id) : undefined}
|
|
>
|
|
{item.type === "item" ? <BoardItemContent item={item} /> : <BoardDynamicSection section={item} />}
|
|
</GridStackItem>
|
|
))}
|
|
</>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Calculates the min width / height of a section by taking the maximum of
|
|
* the sum of the offset and size of all items and dynamic sections inside.
|
|
* @param direction either "x" or "y"
|
|
* @param items items of the section
|
|
* @param sections sections of the board to look for dynamic sections
|
|
* @param parentSectionId the id of the section we want to calculate the min size for
|
|
* @returns the min size
|
|
*/
|
|
const getMinSize = (direction: "x" | "y", items: Item[], sections: Section[], parentSectionId: string) => {
|
|
const size = direction === "x" ? "width" : "height";
|
|
return Math.max(
|
|
...items.map((item) => item[`${direction}Offset`] + item[size]),
|
|
...sections
|
|
.filter(
|
|
(section): section is DynamicSection =>
|
|
section.kind === "dynamic" && section.parentSectionId === parentSectionId,
|
|
)
|
|
.map((item) => item[`${direction}Offset`] + item[size]),
|
|
1, // Minimum size
|
|
);
|
|
};
|