import { TargetedEvent, useContext, useState } from "preact/compat"; import { FormContext, InputFieldState } from "./FormProvider.js"; export interface InputFieldHandler { value: Type; onChange: (s: Type) => void; state: InputFieldState; isDirty: boolean; } export function useField(name: keyof T): InputFieldHandler { const { initialValue, value: formValue, computeFormState, onUpdate, } = useContext(FormContext); type P = typeof name; type V = T[P]; const [isDirty, setDirty] = useState(false); const formState = computeFormState ? computeFormState(formValue) : {}; const fieldValue = readField(formValue, String(name)) as V; const fieldState = readField>( formState, String(name), ); //default state const state: InputFieldState = { disabled: fieldState?.disabled ?? false, readonly: fieldState?.readonly ?? false, hidden: fieldState?.hidden ?? false, error: fieldState?.error, }; function onChange(value: V): void { setDirty(true); return onUpdate((prev: any) => { return setValueDeeper(prev, String(name).split("."), value); }); } return { value: fieldValue, onChange, isDirty, state, }; } /** * read the field of an object an support accessing it using '.' * * @param object * @param name * @returns */ function readField(object: any, name: string): T | undefined { return name .split(".") .reduce((prev, current) => prev && prev[current], object); } function setValueDeeper(object: any, names: string[], value: any): any { if (names.length === 0) return value; const [head, ...rest] = names; if (object === undefined) { return { [head]: setValueDeeper({}, rest, value) }; } return { ...object, [head]: setValueDeeper(object[head] ?? {}, rest, value) }; }