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:
Manuel
2024-11-02 18:44:36 +01:00
committed by GitHub
parent f8bdd9c5c7
commit 49c0ebea6d
21 changed files with 889 additions and 32 deletions

View File

@@ -1,3 +1,7 @@
import type React from "react";
import type { DraggableAttributes, UniqueIdentifier } from "@dnd-kit/core";
import type { ActionIconProps } from "@mantine/core";
import { objectEntries } from "@homarr/common";
import type { IntegrationKind, WidgetKind } from "@homarr/definitions";
import type { ZodType } from "@homarr/validation";
@@ -21,6 +25,19 @@ interface MultiSelectInput<TOptions extends SelectOption[]>
searchable?: boolean;
}
interface SortableItemListInput<TItem, TOptionValue extends UniqueIdentifier>
extends Omit<CommonInput<TOptionValue[]>, "withDescription"> {
AddButton: (props: { addItem: (item: TItem) => void; values: TOptionValue[] }) => React.ReactNode;
ItemComponent: (props: {
item: TItem;
removeItem: () => void;
rootAttributes: DraggableAttributes;
handle: (props: Partial<Pick<ActionIconProps, "size" | "color" | "variant">>) => React.ReactNode;
}) => React.ReactNode;
uniqueIdentifier: (item: TItem) => TOptionValue;
useData: (values: TOptionValue[]) => { data: TItem[] | undefined; isLoading: boolean; error: unknown };
}
interface SelectInput<TOptions extends readonly SelectOption[]>
extends CommonInput<inferSelectOptionValue<TOptions[number]>> {
options: TOptions;
@@ -109,10 +126,26 @@ const optionsFactory = {
defaultValue: "",
withDescription: false,
}),
sortableItemList: <const TItem, const TOptionValue extends UniqueIdentifier>(
input: SortableItemListInput<TItem, TOptionValue>,
) => ({
type: "sortableItemList" as const,
defaultValue: [] as TOptionValue[],
itemComponent: input.ItemComponent,
addButton: input.AddButton,
uniqueIdentifier: input.uniqueIdentifier,
useData: input.useData,
withDescription: false,
}),
};
type WidgetOptionFactory = typeof optionsFactory;
export type WidgetOptionDefinition = ReturnType<WidgetOptionFactory[keyof WidgetOptionFactory]>;
export type WidgetOptionDefinition =
| ReturnType<WidgetOptionFactory[Exclude<keyof WidgetOptionFactory, "sortableItemList">]>
// We allow any here as it's already type guarded with Record<string, unknown> and it still infers the correct type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
| ReturnType<typeof optionsFactory.sortableItemList<any, any>>;
export type WidgetOptionsRecord = Record<string, WidgetOptionDefinition>;
export type WidgetOptionType = WidgetOptionDefinition["type"];
export type WidgetOptionOfType<TType extends WidgetOptionType> = Extract<WidgetOptionDefinition, { type: TType }>;