diff options
Diffstat (limited to 'packages/exchange-backoffice-ui/src/forms')
7 files changed, 44 insertions, 40 deletions
diff --git a/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx b/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx index c9b6783e6..ebb8e3dce 100644 --- a/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx +++ b/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx @@ -1,18 +1,18 @@ import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; import { ComponentChildren, VNode, createContext, h } from "preact"; -import { StateUpdater, useMemo } from "preact/hooks"; +import { MutableRef, StateUpdater, useRef } from "preact/hooks"; export interface FormType<T> { - initialValue: Partial<T>; - value: Partial<T>; - onUpdate: StateUpdater<T>; + value: MutableRef<Partial<T>>; + initialValue?: Partial<T>; + onUpdate?: StateUpdater<T>; computeFormState?: (v: T) => FormState<T>; } //@ts-ignore export const FormContext = createContext<FormType<any>>({}); -type FormState<T> = { +export type FormState<T> = { [field in keyof T]?: T[field] extends AbsoluteTime ? Partial<InputFieldState> : T[field] extends object @@ -33,21 +33,20 @@ export interface InputFieldState { export function FormProvider<T>({ children, - state, + initialValue, + onUpdate, computeFormState, }: { - state: [Partial<T>, StateUpdater<T>]; + initialValue?: Partial<T>; + onUpdate?: (v: Partial<T>) => void; computeFormState?: (v: T) => FormState<T>; children: ComponentChildren; }): VNode { - const [value, onUpdate] = state; - const initialValue = useMemo(() => value, []); - const contextValue = useMemo( - () => ({ initialValue, value, onUpdate, computeFormState }), - [value, onUpdate, computeFormState], - ); + const value = useRef(initialValue ?? {}); return ( - <FormContext.Provider value={contextValue}> + <FormContext.Provider + value={{ initialValue, value, onUpdate, computeFormState }} + > <form>{children}</form> </FormContext.Provider> ); diff --git a/packages/exchange-backoffice-ui/src/forms/InputArray.tsx b/packages/exchange-backoffice-ui/src/forms/InputArray.tsx index f60ed4160..2447c9989 100644 --- a/packages/exchange-backoffice-ui/src/forms/InputArray.tsx +++ b/packages/exchange-backoffice-ui/src/forms/InputArray.tsx @@ -17,10 +17,9 @@ export function InputArray( const [selectedIndex, setSelected] = useState<number | undefined>(undefined); const selected = selectedIndex === undefined ? undefined : list[selectedIndex]; - const formState = useState(selected ?? {}); + const [subForm, updateSubForm] = useState(selected ?? {}); useEffect(() => { - const [, update] = formState; - update(selected); + updateSubForm(selected); }, [selected]); return ( <div class="sm:col-span-6"> @@ -118,7 +117,7 @@ export function InputArray( })} </div> {selectedIndex !== undefined && ( - <FormProvider state={formState}> + <FormProvider initialValue={subForm} onUpdate={updateSubForm}> <div class="px-4 py-6"> <div class="grid grid-cols-1 gap-y-8 "> <RenderAllFieldsByUiConfig fields={fields} /> @@ -149,8 +148,7 @@ export function InputArray( type="button" onClick={() => { const newValue = [...list]; - const [confirmed] = formState; - newValue.splice(selectedIndex, 1, confirmed); + newValue.splice(selectedIndex, 1, subForm); onChange(newValue); setSelected(undefined); }} diff --git a/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx b/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx index dae5ff34a..0e1b32cba 100644 --- a/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx +++ b/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx @@ -43,7 +43,7 @@ export function InputChoiceStacked( } return ( - <div class="sm:col-span-4"> + <div class="sm:col-span-6"> <LabelWithTooltipMaybeRequired label={label} required={required} @@ -81,7 +81,7 @@ export function InputChoiceStacked( <input type="radio" name="server-size" - value={choice.value} + defaultValue={choice.value} onClick={(e) => { onChange(choice.value); }} diff --git a/packages/exchange-backoffice-ui/src/forms/InputDate.tsx b/packages/exchange-backoffice-ui/src/forms/InputDate.tsx index c9e1421f8..00dd59996 100644 --- a/packages/exchange-backoffice-ui/src/forms/InputDate.tsx +++ b/packages/exchange-backoffice-ui/src/forms/InputDate.tsx @@ -18,12 +18,11 @@ export function InputDate( converter={{ fromStringUI: (v) => { if (!v) return { t_ms: "never" }; - console.log("from", v); const t_ms = parse(v, pattern, Date.now()).getTime(); return { t_ms }; }, toStringUI: (v) => { - return v === undefined + return !v || !v.t_ms ? "" : v.t_ms === "never" ? "never" diff --git a/packages/exchange-backoffice-ui/src/forms/InputLine.tsx b/packages/exchange-backoffice-ui/src/forms/InputLine.tsx index 0870e885a..2a5245ddf 100644 --- a/packages/exchange-backoffice-ui/src/forms/InputLine.tsx +++ b/packages/exchange-backoffice-ui/src/forms/InputLine.tsx @@ -243,7 +243,7 @@ export function InputLine<T>(props: { type: string } & UIFormProps<T>): VNode { onChange(fromString(e.currentTarget.value)); }} placeholder={placeholder ? placeholder : undefined} - value={toString(value)} + defaultValue={toString(value)} disabled={state.disabled} aria-invalid={showError} // aria-describedby="email-error" @@ -262,7 +262,7 @@ export function InputLine<T>(props: { type: string } & UIFormProps<T>): VNode { onChange(fromString(e.currentTarget.value)); }} placeholder={placeholder ? placeholder : undefined} - value={toString(value)} + defaultValue={toString(value)} disabled={state.disabled} aria-invalid={showError} // aria-describedby="email-error" diff --git a/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx b/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx index 375f8da93..05733fe19 100644 --- a/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx +++ b/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx @@ -7,9 +7,11 @@ import { useState } from "preact/hooks"; export function InputSelectMultiple( props: { choices: Choice[]; + unique?: boolean; } & UIFormProps<Array<string>>, ): VNode { - const { name, label, choices, placeholder, tooltip, required } = props; + const { name, label, choices, placeholder, tooltip, required, unique } = + props; const { value, onChange } = useField<{ [s: string]: Array<string> }>(name); const [filter, setFilter] = useState<string | undefined>(undefined); @@ -26,7 +28,7 @@ export function InputSelectMultiple( return regex.test(v.label); }); return ( - <div class="sm:col-span-4"> + <div class="sm:col-span-6"> <LabelWithTooltipMaybeRequired label={label} required={required} @@ -101,18 +103,19 @@ export function InputSelectMultiple( role="listbox" > {filteredChoices.map((v, idx) => { - let clazz = - "relative flex border p-4 focus:outline-none disabled:text-grey"; return ( <li class="relative cursor-pointer select-none py-2 pl-3 pr-9 text-gray-900 hover:text-white hover:bg-indigo-600" id="option-0" role="option" onClick={() => { + setFilter(undefined); + if (unique && list.indexOf(v.value) !== -1) { + return; + } const newValue = [...list]; newValue.splice(0, 0, v.value); onChange(newValue); - setFilter(undefined); }} // tabindex="-1" diff --git a/packages/exchange-backoffice-ui/src/forms/useField.ts b/packages/exchange-backoffice-ui/src/forms/useField.ts index 6f7b84112..f54dc7465 100644 --- a/packages/exchange-backoffice-ui/src/forms/useField.ts +++ b/packages/exchange-backoffice-ui/src/forms/useField.ts @@ -13,14 +13,14 @@ export function useField<T>(name: keyof T): InputFieldHandler<T[keyof T]> { initialValue, value: formValue, computeFormState, - onUpdate, + onUpdate: notifyUpdate, } = useContext(FormContext); type P = typeof name; type V = T[P]; - const [isDirty, setDirty] = useState(false); - const formState = computeFormState ? computeFormState(formValue) : {}; + const formState = computeFormState ? computeFormState(formValue.current) : {}; - const fieldValue = readField(formValue, String(name)) as V; + const fieldValue = readField(formValue.current, String(name)) as V; + const [currentValue, setCurrentValue] = useState<any | undefined>(undefined); const fieldState = readField<Partial<InputFieldState>>( formState, String(name), @@ -35,16 +35,21 @@ export function useField<T>(name: keyof T): InputFieldHandler<T[keyof T]> { }; function onChange(value: V): void { - setDirty(true); - return onUpdate((prev: any) => { - return setValueDeeper(prev, String(name).split("."), value); - }); + setCurrentValue(value); + formValue.current = setValueDeeper( + formValue.current, + String(name).split("."), + value, + ); + if (notifyUpdate) { + notifyUpdate(formValue.current); + } } return { value: fieldValue, onChange, - isDirty, + isDirty: currentValue !== undefined, state, }; } |