import { objectEntries } from "@homarr/common"; import type { WidgetKind } from "@homarr/definitions"; import type { ZodType } from "@homarr/validation"; import { z } from "@homarr/validation"; import { widgetImports } from "."; import type { inferSelectOptionValue, SelectOption } from "./_inputs/widget-select-input"; interface CommonInput { defaultValue?: TType; withDescription?: boolean; } interface TextInput extends CommonInput { validate?: z.ZodType; } interface MultiSelectInput extends CommonInput[]> { options: TOptions; searchable?: boolean; } interface SelectInput extends CommonInput> { options: TOptions; searchable?: boolean; } interface NumberInput extends CommonInput { validate: z.ZodNumber; step?: number; } interface SliderInput extends CommonInput { validate: z.ZodNumber; step?: number; } export interface OptionLocation { name: string; latitude: number; longitude: number; } const optionsFactory = { switch: (input?: CommonInput) => ({ type: "switch" as const, defaultValue: input?.defaultValue ?? false, withDescription: input?.withDescription ?? false, }), text: (input?: TextInput) => ({ type: "text" as const, defaultValue: input?.defaultValue ?? "", withDescription: input?.withDescription ?? false, validate: input?.validate, }), multiSelect: (input: MultiSelectInput) => ({ type: "multiSelect" as const, defaultValue: input.defaultValue ?? [], options: input.options, searchable: input.searchable ?? false, withDescription: input.withDescription ?? false, }), select: (input: SelectInput) => ({ type: "select" as const, defaultValue: (input.defaultValue ?? input.options[0]) as inferSelectOptionValue, options: input.options, searchable: input.searchable ?? false, withDescription: input.withDescription ?? false, }), number: (input: NumberInput) => ({ type: "number" as const, defaultValue: input.defaultValue ?? ("" as const), step: input.step, withDescription: input.withDescription ?? false, validate: input.validate, }), slider: (input: SliderInput) => ({ type: "slider" as const, defaultValue: input.defaultValue ?? input.validate.minValue ?? 0, step: input.step, withDescription: input.withDescription ?? false, validate: input.validate, }), location: (input?: CommonInput) => ({ type: "location" as const, defaultValue: input?.defaultValue ?? { name: "", latitude: 0, longitude: 0, }, withDescription: input?.withDescription ?? false, validate: z.object({ name: z.string().min(1), latitude: z.number(), longitude: z.number(), }), }), multiText: (input?: CommonInput & { validate?: ZodType }) => ({ type: "multiText" as const, defaultValue: input?.defaultValue ?? [], withDescription: input?.withDescription ?? false, values: [] as string[], validate: input?.validate, }), app: (input?: Omit, "defaultValue">) => ({ type: "app" as const, defaultValue: "", withDescription: input?.withDescription ?? false, }), }; type WidgetOptionFactory = typeof optionsFactory; export type WidgetOptionDefinition = ReturnType; export type WidgetOptionsRecord = Record; export type WidgetOptionType = WidgetOptionDefinition["type"]; export type WidgetOptionOfType = Extract; type inferOptionFromDefinition = TDefinition["defaultValue"]; export type inferOptionsFromDefinition = { [key in keyof TOptions]: inferOptionFromDefinition; }; interface FieldConfiguration { shouldHide: (options: inferOptionsFromDefinition) => boolean; } type ConfigurationInput = Partial< Record> >; const createOptions = ( optionsCallback: (factory: WidgetOptionFactory) => TOptions, configuration?: ConfigurationInput, ) => { const obj = {} as Record; const options = optionsCallback(optionsFactory); for (const key in options) { obj[key] = { ...configuration?.[key], ...options[key], }; } return obj as { [key in keyof TOptions]: TOptions[key] & FieldConfiguration; }; }; type OptionsBuilder = typeof createOptions; export type OptionsBuilderResult = ReturnType; export const optionsBuilder = { from: createOptions, }; export const reduceWidgetOptionsWithDefaultValues = (kind: WidgetKind, currentValue: Record = {}) => { const definition = widgetImports[kind].definition; const options = definition.options as Record; return objectEntries(options).reduce( (prev, [key, value]) => ({ ...prev, [key]: currentValue[key] ?? value.defaultValue, }), {} as Record, ); };