import { AbsoluteTime, AmountJson, TranslatedString, } from "@gnu-taler/taler-util"; import { ComponentChildren, VNode, createContext, h } from "preact"; import { MutableRef, StateUpdater, useState } from "preact/hooks"; export interface FormType { value: MutableRef>; initialValue?: Partial; readOnly?: boolean; onUpdate?: StateUpdater; computeFormState?: (v: T) => FormState; } //@ts-ignore export const FormContext = createContext>({}); /** * Map of {[field]:BehaviorResult} * for every field of type * - any native (string, number, etc...) * - absoluteTime * - amountJson * * except for: * - object => recurse into * - array => behavior result and element field */ export type FormState = { [field in keyof T]?: T[field] extends AbsoluteTime ? BehaviorResult : T[field] extends AmountJson ? BehaviorResult : T[field] extends Array ? InputArrayFieldState

: T[field] extends (object | undefined) ? FormState : BehaviorResult; }; export type BehaviorResult = Partial & FieldUIOptions export interface InputFieldState { /* should show the error */ error?: TranslatedString; /* should not allow to edit */ readonly: boolean; /* should show as disable */ disabled: boolean; /* should not show */ hidden: boolean; } export interface IconAddon { type: "icon"; icon: VNode; } export interface ButtonAddon { type: "button"; onClick: () => void; children: ComponentChildren; } export interface TextAddon { type: "text"; text: TranslatedString; } export type Addon = IconAddon | ButtonAddon | TextAddon; export interface StringConverter { toStringUI: (v?: T) => string; fromStringUI: (v?: string) => T; } type FieldUIOptions = { placeholder?: TranslatedString; tooltip?: TranslatedString; help?: TranslatedString; required?: boolean; } export interface UIFormProps extends FieldUIOptions { name: K; label: TranslatedString; before?: Addon; after?: Addon; converter?: StringConverter; } export interface InputArrayFieldState

extends BehaviorResult { elements?: FormState

[]; } export function FormProvider({ children, initialValue, onUpdate: notify, onSubmit, computeFormState, readOnly, }: { initialValue?: Partial; onUpdate?: (v: Partial) => void; onSubmit?: (v: Partial, s: FormState | undefined) => void; computeFormState?: (v: Partial) => FormState; readOnly?: boolean; children: ComponentChildren; }): VNode { const [state, setState] = useState>(initialValue ?? {}); const value = { current: state }; const onUpdate = (v: typeof state) => { setState(v); if (notify) notify(v); }; return (

{ e.preventDefault(); //@ts-ignore if (onSubmit) onSubmit( value.current, !computeFormState ? undefined : computeFormState(value.current), ); }} > {children}
); }