/* eslint-disable react/no-unknown-property */ // Ignored because of gridstack attributes import { useMemo } from "react"; import type { RefObject } from "react"; import { ActionIcon, Card, Menu } from "@mantine/core"; import { useElementSize } from "@mantine/hooks"; import { IconDotsVertical, IconLayoutKanban, IconPencil, IconTrash } from "@tabler/icons-react"; import combineClasses from "clsx"; import { useAtomValue } from "jotai"; import { clientApi } from "@homarr/api/client"; import { useConfirmModal, useModalAction } from "@homarr/modals"; import { useI18n, useScopedI18n } from "@homarr/translation/client"; import { loadWidgetDynamic, reduceWidgetOptionsWithDefaultValues, useServerDataFor, WidgetEditModal, widgetImports, } from "@homarr/widgets"; import type { Item } from "~/app/[locale]/boards/_types"; import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context"; import { editModeAtom } from "../editMode"; import { useItemActions } from "../items/item-actions"; import type { UseGridstackRefs } from "./gridstack/use-gridstack"; import classes from "./item.module.css"; interface Props { items: Item[]; refs: UseGridstackRefs; } export const SectionContent = ({ items, refs }: Props) => { const board = useRequiredBoard(); return ( <> {items.map((item) => ( ))} ); }; interface ItemProps { item: Item; refs: UseGridstackRefs; opacity: number; } const BoardItem = ({ refs, item, opacity }: ItemProps) => { const { ref, width, height } = useElementSize(); return (
} >
); }; interface ItemContentProps { item: Item; width: number; height: number; } const BoardItemContent = ({ item, ...dimensions }: ItemContentProps) => { const board = useRequiredBoard(); const editMode = useAtomValue(editModeAtom); 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 ( <> ); }; const ItemMenu = ({ offset, item }: { offset: number; item: Item }) => { const tItem = useScopedI18n("item"); const t = useI18n(); const { openModal } = useModalAction(WidgetEditModal); const { openConfirmModal } = useConfirmModal(); const isEditMode = useAtomValue(editModeAtom); const { updateItemOptions, updateItemAdvancedOptions, updateItemIntegrations, removeItem } = useItemActions(); const { data: integrationData, isPending } = clientApi.integration.all.useQuery(); const currentDefinition = useMemo(() => widgetImports[item.kind].definition, [item.kind]); if (!isEditMode || isPending) return null; const openEditModal = () => { openModal({ kind: item.kind, value: { advancedOptions: item.advancedOptions, options: item.options, integrations: item.integrations, }, onSuccessfulEdit: ({ options, integrations, advancedOptions }) => { updateItemOptions({ itemId: item.id, newOptions: options, }); updateItemAdvancedOptions({ itemId: item.id, newAdvancedOptions: advancedOptions, }); updateItemIntegrations({ itemId: item.id, newIntegrations: integrations, }); }, integrationData: (integrationData ?? []).filter( (integration) => "supportedIntegrations" in currentDefinition && (currentDefinition.supportedIntegrations as string[]).some((kind) => kind === integration.kind), ), integrationSupport: "supportedIntegrations" in currentDefinition, }); }; const openRemoveModal = () => { openConfirmModal({ title: tItem("remove.title"), children: tItem("remove.message"), onConfirm: () => { removeItem({ itemId: item.id }); }, }); }; return ( {tItem("menu.label.settings")} } onClick={openEditModal}> {tItem("action.edit")} }>{tItem("action.move")} {t("common.dangerZone")} } onClick={openRemoveModal}> {tItem("action.remove")} ); };