* 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.7 KiB
TypeScript
90 lines
2.7 KiB
TypeScript
import { Card } from "@mantine/core";
|
|
import { useElementSize } from "@mantine/hooks";
|
|
import { QueryErrorResetBoundary } from "@tanstack/react-query";
|
|
import combineClasses from "clsx";
|
|
import { ErrorBoundary } from "react-error-boundary";
|
|
|
|
import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues, useServerDataFor } from "@homarr/widgets";
|
|
import { WidgetError } from "@homarr/widgets/errors";
|
|
|
|
import type { Item } from "~/app/[locale]/boards/_types";
|
|
import { useEditMode, useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
|
import classes from "../sections/item.module.css";
|
|
import { BoardItemMenu } from "./item-menu";
|
|
|
|
interface BoardItemContentProps {
|
|
item: Item;
|
|
}
|
|
|
|
export const BoardItemContent = ({ item }: BoardItemContentProps) => {
|
|
const { ref, width, height } = useElementSize<HTMLDivElement>();
|
|
const board = useRequiredBoard();
|
|
|
|
return (
|
|
<Card
|
|
ref={ref}
|
|
className={combineClasses(
|
|
classes.itemCard,
|
|
`${item.kind}-wrapper`,
|
|
"grid-stack-item-content",
|
|
item.advancedOptions.customCssClasses.join(" "),
|
|
)}
|
|
withBorder
|
|
styles={{
|
|
root: {
|
|
"--opacity": board.opacity / 100,
|
|
containerType: "size",
|
|
},
|
|
}}
|
|
p={0}
|
|
>
|
|
<InnerContent item={item} width={width} height={height} />
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
interface InnerContentProps {
|
|
item: Item;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
|
|
const InnerContent = ({ item, ...dimensions }: InnerContentProps) => {
|
|
const board = useRequiredBoard();
|
|
const [isEditMode] = useEditMode();
|
|
const serverData = useServerDataFor(item.id);
|
|
const Comp = loadWidgetDynamic(item.kind);
|
|
const options = reduceWidgetOptionsWithDefaultValues(item.kind, item.options);
|
|
const newItem = { ...item, options };
|
|
|
|
if (!serverData?.isReady) return null;
|
|
|
|
return (
|
|
<QueryErrorResetBoundary>
|
|
{({ reset }) => (
|
|
<ErrorBoundary
|
|
onReset={reset}
|
|
fallbackRender={({ resetErrorBoundary, error }) => (
|
|
<>
|
|
<BoardItemMenu offset={4} item={newItem} resetErrorBoundary={resetErrorBoundary} />
|
|
<WidgetError kind={item.kind} error={error as unknown} resetErrorBoundary={resetErrorBoundary} />
|
|
</>
|
|
)}
|
|
>
|
|
<BoardItemMenu offset={4} item={newItem} />
|
|
<Comp
|
|
options={options as never}
|
|
integrationIds={item.integrationIds}
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
serverData={serverData?.data as never}
|
|
isEditMode={isEditMode}
|
|
boardId={board.id}
|
|
itemId={item.id}
|
|
{...dimensions}
|
|
/>
|
|
</ErrorBoundary>
|
|
)}
|
|
</QueryErrorResetBoundary>
|
|
);
|
|
};
|