feat: add bookmark widget (#964)
* feat: add bookmark widget * fix: item component type issue, widget-ordered-object-list-input item component issue * feat: add button in items list * wip * wip: bookmark options dnd * wip: improve widget sortable item list * feat: add sortable item list input to widget edit modal * feat: implement bookmark widget * chore: address pull request feedback * fix: format issues * fix: lockfile not up to date * fix: import configuration missing and apps not imported * fix: bookmark items not sorted * feat: add flex layouts to bookmark widget * fix: deepsource issue * fix: add missing layout bookmarks old-import options mapping --------- Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
This commit is contained in:
67
packages/widgets/src/bookmarks/index.tsx
Normal file
67
packages/widgets/src/bookmarks/index.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { ActionIcon, Avatar, Button, Group, Stack, Text } from "@mantine/core";
|
||||
import { IconClock, IconX } from "@tabler/icons-react";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import { createWidgetDefinition } from "../definition";
|
||||
import { optionsBuilder } from "../options";
|
||||
import { AppSelectModal } from "./app-select-modal";
|
||||
|
||||
export const { definition, componentLoader } = createWidgetDefinition("bookmarks", {
|
||||
icon: IconClock,
|
||||
options: optionsBuilder.from((factory) => ({
|
||||
title: factory.text(),
|
||||
layout: factory.select({
|
||||
options: (["grid", "row", "column"] as const).map((value) => ({
|
||||
value,
|
||||
label: (t) => t(`widget.bookmarks.option.layout.option.${value}.label`),
|
||||
})),
|
||||
defaultValue: "column",
|
||||
}),
|
||||
items: factory.sortableItemList<RouterOutputs["app"]["all"][number], string>({
|
||||
ItemComponent: ({ item, handle: Handle, removeItem, rootAttributes }) => {
|
||||
return (
|
||||
<Group {...rootAttributes} tabIndex={0} justify="space-between" wrap="nowrap">
|
||||
<Group wrap="nowrap">
|
||||
<Handle />
|
||||
|
||||
<Group>
|
||||
<Avatar src={item.iconUrl} alt={item.name} />
|
||||
<Stack gap={0}>
|
||||
<Text>{item.name}</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<ActionIcon variant="transparent" color="red" onClick={removeItem}>
|
||||
<IconX size={20} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
);
|
||||
},
|
||||
AddButton({ addItem, values }) {
|
||||
const { openModal } = useModalAction(AppSelectModal);
|
||||
const t = useI18n();
|
||||
|
||||
return (
|
||||
<Button onClick={() => openModal({ onSelect: addItem, presentAppIds: values })}>
|
||||
{t("widget.bookmarks.option.items.add")}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
uniqueIdentifier: (item) => item.id,
|
||||
useData: (initialIds) => {
|
||||
const { data, error, isLoading } = clientApi.app.byIds.useQuery(initialIds);
|
||||
|
||||
return {
|
||||
data,
|
||||
error,
|
||||
isLoading,
|
||||
};
|
||||
},
|
||||
}),
|
||||
})),
|
||||
}).withDynamicImport(() => import("./component"));
|
||||
Reference in New Issue
Block a user