summaryrefslogtreecommitdiff
path: root/packages/web-util/src/forms/useField.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/web-util/src/forms/useField.ts')
-rw-r--r--packages/web-util/src/forms/useField.ts91
1 files changed, 91 insertions, 0 deletions
diff --git a/packages/web-util/src/forms/useField.ts b/packages/web-util/src/forms/useField.ts
new file mode 100644
index 000000000..d30962c6f
--- /dev/null
+++ b/packages/web-util/src/forms/useField.ts
@@ -0,0 +1,91 @@
+import { useContext } from "preact/compat";
+import { FieldUIOptions, FormContext } from "./FormProvider.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
+
+export interface InputFieldHandler<Type> {
+ value: Type;
+ onChange: (s: Type) => void;
+ state: FieldUIOptions;
+ error?: TranslatedString | undefined;
+}
+
+/**
+ * @depreacted removing this so we don't depend on context to create a form
+ * @param name
+ * @returns
+ */
+export function useField<T extends object, K extends keyof T>(
+ name: K,
+): InputFieldHandler<T[K]> | undefined {
+ const ctx = useContext(FormContext);
+ if (!ctx) {
+ //no context, can't be used
+ return undefined;
+ }
+ const {
+ value: formValue,
+ computeFormState,
+ onUpdate: notifyUpdate,
+ readOnly: readOnlyForm,
+ } = ctx
+
+ type P = typeof name;
+ type V = T[P];
+ const formState = computeFormState ? computeFormState(formValue.current) : {};
+
+ const fieldValue = readField(formValue.current, String(name)) as V;
+
+ const fieldState =
+ readField<Partial<FieldUIOptions>>(formState, String(name)) ?? {};
+
+ //compute default state
+ const state = {
+ disabled: readOnlyForm ? true : (fieldState.disabled ?? false),
+ hidden: fieldState.hidden ?? false,
+ help: fieldState.help,
+ elements: "elements" in fieldState ? fieldState.elements ?? [] : [],
+ };
+
+ function onChange(value: V): void {
+ // setCurrentValue(value);
+ formValue.current = setValueDeeper(
+ formValue.current,
+ String(name).split("."),
+ value,
+ );
+ if (notifyUpdate) {
+ notifyUpdate(formValue.current);
+ }
+ }
+
+ return {
+ value: fieldValue,
+ onChange,
+ state,
+ };
+}
+
+/**
+ * read the field of an object an support accessing it using '.'
+ *
+ * @param object
+ * @param name
+ * @returns
+ */
+function readField<T>(
+ object: any,
+ name: string,
+): T | undefined {
+ return name.split(".").reduce((prev, current) => {
+ return prev ? prev[current] : undefined;
+ }, object);
+}
+
+function setValueDeeper(object: any, names: string[], value: any): any {
+ if (names.length === 0) return value;
+ const [head, ...rest] = names;
+ if (object === undefined) {
+ return { [head]: setValueDeeper({}, rest, value) };
+ }
+ return { ...object, [head]: setValueDeeper(object[head] ?? {}, rest, value) };
+}