From 55a1e8c6e1ab34702525aadf18b1acce2d06e616 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 10 May 2023 15:01:56 -0300 Subject: [PATCH] fix form --- packages/exchange-backoffice-ui/src/App.tsx | 12 +- .../exchange-backoffice-ui/src/Dashborad.tsx | 35 +- packages/exchange-backoffice-ui/src/Form.tsx | 916 +++++++++--------- .../src/HeroSections.tsx | 1 + .../src/forms/FormProvider.tsx | 27 +- .../src/forms/InputArray.tsx | 10 +- .../src/forms/InputChoice.tsx | 4 +- .../src/forms/InputDate.tsx | 3 +- .../src/forms/InputLine.tsx | 4 +- .../src/forms/InputSelectMultiple.tsx | 13 +- .../src/forms/useField.ts | 23 +- 11 files changed, 530 insertions(+), 518 deletions(-) diff --git a/packages/exchange-backoffice-ui/src/App.tsx b/packages/exchange-backoffice-ui/src/App.tsx index 65b58a344..95926e634 100644 --- a/packages/exchange-backoffice-ui/src/App.tsx +++ b/packages/exchange-backoffice-ui/src/App.tsx @@ -1,16 +1,12 @@ -import { h, VNode } from "preact"; -import { HeroSections } from "./HeroSections.js"; -import "./scss/main.css"; -import { Dashboard } from "./Dashborad.js"; -import { Form } from "./Form.js"; import { TranslationProvider } from "@gnu-taler/web-util/browser"; +import { h, VNode } from "preact"; +import { Dashboard } from "./Dashborad.js"; +import "./scss/main.css"; export function App(): VNode { return ( - -
- + ); } diff --git a/packages/exchange-backoffice-ui/src/Dashborad.tsx b/packages/exchange-backoffice-ui/src/Dashborad.tsx index a540caa8d..19ea4a31c 100644 --- a/packages/exchange-backoffice-ui/src/Dashborad.tsx +++ b/packages/exchange-backoffice-ui/src/Dashborad.tsx @@ -16,7 +16,8 @@ import { ChevronDownIcon, MagnifyingGlassIcon, } from "@heroicons/react/20/solid"; -import { useState } from "preact/hooks"; +import { useRef, useState } from "preact/hooks"; +import { Form } from "./Form.js"; const navigation = [ { name: "Dashboard", href: "#", icon: HomeIcon, current: true }, @@ -43,9 +44,10 @@ function classNames(...classes: string[]) { export function Dashboard({ children, }: { - children: ComponentChildren; + children?: ComponentChildren; }): VNode { const [sidebarOpen, setSidebarOpen] = useState(false); + const logRef = useRef(null); return ( <> @@ -53,6 +55,7 @@ export function Dashboard({ @@ -195,9 +198,12 @@ export function Dashboard({ alt="Your Company" /> +
+

+            
{ - initialValue: Partial; - value: Partial; - onUpdate: StateUpdater; + value: MutableRef>; + initialValue?: Partial; + onUpdate?: StateUpdater; computeFormState?: (v: T) => FormState; } //@ts-ignore export const FormContext = createContext>({}); -type FormState = { +export type FormState = { [field in keyof T]?: T[field] extends AbsoluteTime ? Partial : T[field] extends object @@ -33,21 +33,20 @@ export interface InputFieldState { export function FormProvider({ children, - state, + initialValue, + onUpdate, computeFormState, }: { - state: [Partial, StateUpdater]; + initialValue?: Partial; + onUpdate?: (v: Partial) => void; computeFormState?: (v: T) => FormState; 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 ( - + {children} ); 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(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 (
@@ -118,7 +117,7 @@ export function InputArray( })}
{selectedIndex !== undefined && ( - +
@@ -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 ( -
+
{ 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(props: { type: string } & UIFormProps): 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(props: { type: string } & UIFormProps): 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>, ): 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 }>(name); const [filter, setFilter] = useState(undefined); @@ -26,7 +28,7 @@ export function InputSelectMultiple( return regex.test(v.label); }); return ( -
+
{filteredChoices.map((v, idx) => { - let clazz = - "relative flex border p-4 focus:outline-none disabled:text-grey"; return (
  • { + 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(name: keyof T): InputFieldHandler { 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(undefined); const fieldState = readField>( formState, String(name), @@ -35,16 +35,21 @@ export function useField(name: keyof T): InputFieldHandler { }; 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, }; }