aboutsummaryrefslogtreecommitdiff
path: root/packages/exchange-backoffice-ui/src/forms
diff options
context:
space:
mode:
Diffstat (limited to 'packages/exchange-backoffice-ui/src/forms')
-rw-r--r--packages/exchange-backoffice-ui/src/forms/FormProvider.tsx27
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputArray.tsx10
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputChoice.tsx4
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputDate.tsx3
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputLine.tsx4
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx13
-rw-r--r--packages/exchange-backoffice-ui/src/forms/useField.ts23
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,
};
}