diff options
Diffstat (limited to 'packages/web-util/src/forms/useField.ts')
-rw-r--r-- | packages/web-util/src/forms/useField.ts | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/packages/web-util/src/forms/useField.ts b/packages/web-util/src/forms/useField.ts new file mode 100644 index 000000000..d30962c6f --- /dev/null +++ b/packages/web-util/src/forms/useField.ts @@ -0,0 +1,91 @@ +import { useContext } from "preact/compat"; +import { FieldUIOptions, FormContext } from "./FormProvider.js"; +import { TranslatedString } from "@gnu-taler/taler-util"; + +export interface InputFieldHandler<Type> { + value: Type; + onChange: (s: Type) => void; + state: FieldUIOptions; + error?: TranslatedString | undefined; +} + +/** + * @depreacted removing this so we don't depend on context to create a form + * @param name + * @returns + */ +export function useField<T extends object, K extends keyof T>( + name: K, +): InputFieldHandler<T[K]> | undefined { + const ctx = useContext(FormContext); + if (!ctx) { + //no context, can't be used + return undefined; + } + const { + value: formValue, + computeFormState, + onUpdate: notifyUpdate, + readOnly: readOnlyForm, + } = ctx + + type P = typeof name; + type V = T[P]; + const formState = computeFormState ? computeFormState(formValue.current) : {}; + + const fieldValue = readField(formValue.current, String(name)) as V; + + const fieldState = + readField<Partial<FieldUIOptions>>(formState, String(name)) ?? {}; + + //compute default state + const state = { + disabled: readOnlyForm ? true : (fieldState.disabled ?? false), + hidden: fieldState.hidden ?? false, + help: fieldState.help, + elements: "elements" in fieldState ? fieldState.elements ?? [] : [], + }; + + function onChange(value: V): void { + // setCurrentValue(value); + formValue.current = setValueDeeper( + formValue.current, + String(name).split("."), + value, + ); + if (notifyUpdate) { + notifyUpdate(formValue.current); + } + } + + return { + value: fieldValue, + onChange, + state, + }; +} + +/** + * read the field of an object an support accessing it using '.' + * + * @param object + * @param name + * @returns + */ +function readField<T>( + object: any, + name: string, +): T | undefined { + return name.split(".").reduce((prev, current) => { + return prev ? prev[current] : undefined; + }, 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) }; +} |