import { Controller, DeepPartial, FieldValues, Path, SubmitHandler, useForm } from "react-hook-form"; import { Input } from "./UI/Input.js"; import { Label } from "./UI/Label.js"; import { ErrorMessage } from "@hookform/error-message"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./UI/Select.js"; import { Switch } from "./UI/Switch.js"; import { H4 } from "./UI/Typography/H4.js"; import { Subtle } from "./UI/Typography/Subtle.js"; interface DisabledBy { fieldName: Path; selector?: number; invert?: boolean; } interface BasicFieldProps { name: Path; label: string; description?: string; active?: boolean; required?: boolean; disabledBy?: DisabledBy[]; } interface InputFieldProps extends BasicFieldProps { type: "text" | "number" | "password"; suffix?: string; } interface SelectFieldProps extends BasicFieldProps { type: "select" | "multiSelect"; enumValue: { [s: string]: string | number; }; formatEnumName?: boolean; } interface ToggleFieldProps extends BasicFieldProps { type: "toggle"; } export interface FormProps { onSubmit: SubmitHandler; defaultValues?: DeepPartial; fieldGroups: { label: string; description: string; fields: (InputFieldProps | SelectFieldProps | ToggleFieldProps)[]; }[]; } export function DynamicForm({ fieldGroups, onSubmit, defaultValues }: FormProps) { const { register, handleSubmit, control, getValues } = useForm({ mode: "onChange", defaultValues: defaultValues }); const isDisabled = (disabledBy?: DisabledBy[]): boolean => { if (!disabledBy) return false; return disabledBy.some((field) => { const value = getValues(field.fieldName); if (typeof value === "boolean") return field.invert ? value : !value; if (typeof value === "number") return field.invert ? field.selector !== value : field.selector === value; return false; }); }; return (
{fieldGroups.map((fieldGroup, index) => (

{fieldGroup.label}

{fieldGroup.description}
{fieldGroup.fields.map((field, index) => { const fieldWrapperData: FieldWrapperProps = { label: field.label, description: field.description, disabled: isDisabled(field.disabledBy) }; switch (field.type) { case "text": return ( ); case "number": return ( ( onChange(parseInt(e.target.value))} disabled={fieldWrapperData.disabled} /> )} /> ); case "password": return ( ); case "toggle": return ( ( )} /> ); case "select": const optionsEnumValues = field.enumValue ? Object.entries(field.enumValue).filter( (value) => typeof value[1] === "number" ) : []; return ( ( )} /> ); case "multiSelect": return ( tmp ); } })}
))}
); } interface FieldWrapperProps { label: string; description?: string; disabled?: boolean; children?: React.ReactNode; } const FieldWrapper = ({ label, description, disabled, children }: FieldWrapperProps): JSX.Element => (

{description}

{children}
);