import { AbsoluteTime, AmountJson, TranslatedString, } from "@gnu-taler/taler-util"; import { ComponentChildren, VNode, createContext, h } from "preact"; import { MutableRef, useState } from "preact/hooks"; export interface FormType { value: MutableRef>; initial?: Partial; readOnly?: boolean; onUpdate?: (v: Partial) => void; computeFormState?: (v: Partial) => FormState; } //@ts-ignore export const FormContext = createContext>({}); /** * Map of {[field]:FieldUIOptions} * 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 ? FieldUIOptions : T[field] extends AmountJson ? FieldUIOptions : T[field] extends Array ? InputArrayFieldState

: T[field] extends (object | undefined) ? FormState : FieldUIOptions; }; /** * Properties that can be defined by design or by computing state */ export type FieldUIOptions = { /* text to be shown next to the field */ error?: TranslatedString; /* instruction to be shown in the field */ placeholder?: TranslatedString; /* long text help to be shown on demand */ tooltip?: TranslatedString; /* short text to be shown next to the field*/ help?: TranslatedString; /* should show as disabled and readonly */ disabled?: boolean; /* should not show */ hidden?: boolean; /* show a mark as required*/ required?: boolean; } /** * properties only to be defined on design time */ export interface UIFormProps extends FieldUIOptions { // property name of the object name: K; // label if the field label: TranslatedString; before?: Addon; after?: Addon; // converter to string and back converter?: StringConverter; } 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; } export interface InputArrayFieldState

extends FieldUIOptions { elements?: FormState

[]; } export type FormProviderProps = Omit, "value"> & { onSubmit?: (v: Partial, s: FormState | undefined) => void; children?: ComponentChildren; } export function FormProvider({ children, initial, onUpdate: notify, onSubmit, computeFormState, readOnly, }: FormProviderProps): VNode { const [state, setState] = useState>(initial ?? {}); 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}
); }