+12








fcd7c979df
Co-authored-by: Artur <145776561+arturict@users.noreply.github.com> Co-authored-by: arturict <arturict@users.noreply.github.com> Co-authored-by: Thomas <thomas@ajnart.fr> Co-authored-by: Afonso Jorge Ramos <afonsojorgeramos@gmail.com> Co-authored-by: Danura30082 <danura30082+github@gmail.com> Co-authored-by: Kushie <122986913+kushiemoon-dev@users.noreply.github.com> Co-authored-by: ajnart <thomas@ajnart.fr> Co-authored-by: ajnart <thomas@ajnart.dev> Co-authored-by: chenjun <chenjun@foxmail.com> Co-authored-by: Wikibear <wikibear@users.noreply.github.com> Co-authored-by: Bergasha <bergasha@gmail.com> Co-authored-by: leafyelin <43306196+leafyelin@users.noreply.github.com> Co-authored-by: Lucas Araujo <lucas.lra@gmail.com> Co-authored-by: GreenMonito <86085570+GreenMonito@users.noreply.github.com> Co-authored-by: Nerdy-Technician <roffo@nerdy-technician.social> Co-authored-by: Jun <34545896+jcstrong@users.noreply.github.com> Co-authored-by: Edmond Abraham <54119952+gitedmond@users.noreply.github.com> Co-authored-by: thrawnx <tight.bulb9384@fastmail.com> Co-authored-by: Chris <58918656+nordada@users.noreply.github.com> Co-authored-by: yi111 <153097222+Yi-111-a@users.noreply.github.com> Co-authored-by: Yanis <naamaneyanis@gmail.com> Co-authored-by: Wikibear <me@sej.de> Co-authored-by: alessiottavo <93441886+alessiottavo@users.noreply.github.com>
112 lines
4.6 KiB
TypeScript
112 lines
4.6 KiB
TypeScript
import { ActionIcon, Avatar, Group, Stack, Text } from "@mantine/core";
|
|
import { IconBookmark, IconLink, IconX } from "@tabler/icons-react";
|
|
import { z } from "zod/v4";
|
|
|
|
import { clientApi } from "@homarr/api/client";
|
|
|
|
import { createWidgetDefinition } from "../definition";
|
|
import { optionsBuilder } from "../options";
|
|
import { BookmarkAddButton } from "./add-button";
|
|
import type { BookmarkSelectionItem } from "./add-button";
|
|
import { createDirectBookmark, getBookmarkFaviconUrl, getDirectBookmarkUrl } from "./bookmark-item";
|
|
|
|
export const { definition, componentLoader } = createWidgetDefinition("bookmarks", {
|
|
icon: IconBookmark,
|
|
supportsAdvancedFocus: true,
|
|
queryKey: [["app", "byIds"]],
|
|
createOptions() {
|
|
return optionsBuilder.from((factory) => ({
|
|
title: factory.text(),
|
|
showTitle: factory.switch({ defaultValue: true }),
|
|
titleSize: factory.slider({
|
|
defaultValue: 11,
|
|
validate: z.number().min(8).max(32),
|
|
step: 1,
|
|
}),
|
|
layout: factory.select({
|
|
options: (["adaptive", "column", "row", "grid", "gridHorizontal", "icons"] as const).map((value) => ({
|
|
value,
|
|
label: (t) => {
|
|
if (value === "adaptive") return t("widget.bookmarks.option.layout.option.adaptive.label");
|
|
if (value === "grid") return t("widget.common.layout.option.grid");
|
|
if (value === "row") return t("widget.common.layout.option.horizontal");
|
|
if (value === "column") return t("widget.common.layout.option.vertical");
|
|
if (value === "icons") return t("widget.bookmarks.option.layout.option.icons.label");
|
|
return t("widget.bookmarks.option.layout.option.gridHorizontal.label");
|
|
},
|
|
})),
|
|
defaultValue: "adaptive",
|
|
}),
|
|
variant: factory.select({
|
|
options: (["soft", "filled", "outline", "plain"] as const).map((value) => ({
|
|
value,
|
|
label: (t) => t(`widget.bookmarks.option.variant.option.${value}.label`),
|
|
})),
|
|
defaultValue: "soft",
|
|
}),
|
|
spacing: factory.select({
|
|
options: (["xs", "sm", "md", "lg", "xl"] as const).map((value) => ({
|
|
value,
|
|
label: (t) => t(`widget.bookmarks.option.spacing.option.${value}.label`),
|
|
})),
|
|
defaultValue: "xs",
|
|
}),
|
|
hideTitle: factory.switch({ defaultValue: false }),
|
|
hideIcon: factory.switch({ defaultValue: false }),
|
|
hideHostname: factory.switch({ defaultValue: false }),
|
|
openNewTab: factory.switch({ defaultValue: true }),
|
|
withBorder: factory.switch({ defaultValue: false }),
|
|
grow: factory.switch({ defaultValue: false }),
|
|
customUrls: factory.internal({ defaultValue: [] as string[] }),
|
|
items: factory.sortableItemList<BookmarkSelectionItem, string>({
|
|
ItemComponent: ({ item, handle, removeItem, removeLabel, rootAttributes }) => {
|
|
const iconUrl = item.iconUrl ?? getBookmarkFaviconUrl(item.href);
|
|
return (
|
|
<Group {...rootAttributes} tabIndex={0} justify="space-between" wrap="nowrap">
|
|
<Group wrap="nowrap" miw={0}>
|
|
{handle}
|
|
|
|
<Group wrap="nowrap" miw={0}>
|
|
<Avatar src={iconUrl} name={item.name} color="gray" radius="sm">
|
|
<IconLink size={20} />
|
|
</Avatar>
|
|
<Stack gap={0} miw={0}>
|
|
<Text fw={600} truncate>
|
|
{item.name}
|
|
</Text>
|
|
<Text size="xs" c="dimmed" truncate>
|
|
{item.href}
|
|
</Text>
|
|
</Stack>
|
|
</Group>
|
|
</Group>
|
|
|
|
<ActionIcon variant="subtle" color="red" onClick={removeItem} aria-label={removeLabel}>
|
|
<IconX size="var(--mantine-font-size-xl)" />
|
|
</ActionIcon>
|
|
</Group>
|
|
);
|
|
},
|
|
AddButton: BookmarkAddButton,
|
|
uniqueIdentifier: (item) => item.id,
|
|
useData: (initialIds) => {
|
|
const appIds = initialIds.filter((value) => !getDirectBookmarkUrl(value));
|
|
const { data: apps, error, isLoading } = clientApi.app.byIds.useQuery(appIds);
|
|
const appsById = new Map(apps?.map((app) => [app.id, app]));
|
|
const data = initialIds.flatMap((value) => {
|
|
const directUrl = getDirectBookmarkUrl(value);
|
|
if (directUrl) return createDirectBookmark(directUrl) ?? [];
|
|
return appsById.get(value) ?? [];
|
|
});
|
|
|
|
return {
|
|
data,
|
|
error,
|
|
isLoading,
|
|
};
|
|
},
|
|
}),
|
|
}));
|
|
},
|
|
}).withDynamicImport(() => import("./component"));
|