@rebasepro/forms 0.0.1-canary.4829d6e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Rebase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # @rebasepro/forms
2
+
3
+ Lightweight React form state management with undo/redo support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rebasepro/forms
9
+ ```
10
+
11
+ **Peer dependencies:** `react >= 19.0.0`, `react-dom >= 19.0.0`
12
+
13
+ ## What This Package Does
14
+
15
+ Formex is a minimal, Formik-inspired form library used throughout the Rebase admin panel. It manages form values, validation, touched/dirty state, submission, and provides built-in undo/redo history tracking. It uses `fast-equals` for deep equality checks and avoids unnecessary re-renders.
16
+
17
+ ## Key Exports
18
+
19
+ | Export | Type | Description |
20
+ |---|---|---|
21
+ | `useCreateFormex<T>` | Hook | Creates a `FormexController` — the primary way to initialize a form |
22
+ | `Formex` | Component | Context provider — wraps children to share form state via `useFormex` |
23
+ | `useFormex<T>` | Hook | Consumes the nearest `Formex` context, returns `FormexController<T>` |
24
+ | `Field` | Component | Connects an input to the form by `name`. Supports render-prop children, `as` prop, checkbox/radio/select types |
25
+ | `FormexController<T>` | Type | The full form controller object (values, errors, touched, dirty, submit, undo/redo, etc.) |
26
+ | `FormexResetProps<T>` | Type | Options for `resetForm()` (values, errors, touched, submitCount) |
27
+ | `getIn` | Utility | Deep-get a value from an object by dot/bracket path |
28
+ | `setIn` | Utility | Immutably deep-set a value in an object by path |
29
+
30
+ ### `useCreateFormex` Options
31
+
32
+ | Option | Type | Default | Description |
33
+ |---|---|---|---|
34
+ | `initialValues` | `T` | *required* | Starting form values |
35
+ | `initialErrors` | `Record<string, string>` | `{}` | Pre-set field errors |
36
+ | `initialDirty` | `boolean` | `false` | Initial dirty flag |
37
+ | `initialTouched` | `Record<string, boolean>` | `{}` | Pre-set touched fields |
38
+ | `validation` | `(values: T) => Record<string, string> \| Promise<...> \| void` | — | Sync or async validation function |
39
+ | `validateOnChange` | `boolean` | `false` | Run validation on every field change |
40
+ | `validateOnInitialRender` | `boolean` | `false` | Run validation on mount |
41
+ | `onSubmit` | `(values: T, controller) => void \| Promise<void>` | — | Submit handler |
42
+ | `onReset` | `(controller) => void \| Promise<void>` | — | Reset callback |
43
+ | `onValuesChangeDeferred` | `(values: T, controller) => void` | — | Debounced (300ms) callback on value changes |
44
+ | `debugId` | `string` | — | Optional identifier for debugging |
45
+
46
+ ### `FormexController<T>` Properties
47
+
48
+ | Property | Type | Description |
49
+ |---|---|---|
50
+ | `values` | `T` | Current form values |
51
+ | `initialValues` | `T` | The initial values the form was created with |
52
+ | `errors` | `Record<string, string>` | Current validation errors |
53
+ | `touched` | `Record<string, boolean>` | Which fields have been touched |
54
+ | `dirty` | `boolean` | Whether values differ from initial |
55
+ | `isSubmitting` | `boolean` | Whether the form is currently submitting |
56
+ | `isValidating` | `boolean` | Whether validation is running |
57
+ | `submitCount` | `number` | How many times submit has been called |
58
+ | `version` | `number` | Incremented on submit and reset |
59
+ | `canUndo` / `canRedo` | `boolean` | Whether undo/redo is available |
60
+ | `setValues`, `setFieldValue`, `setFieldError`, `setFieldTouched`, `setDirty`, `setSubmitting`, `setTouched`, `setSubmitCount` | Functions | State setters |
61
+ | `handleChange`, `handleBlur`, `handleSubmit` | Event handlers | Bind to form/input events |
62
+ | `validate`, `resetForm`, `undo`, `redo` | Functions | Form actions |
63
+
64
+ ## Quick Start
65
+
66
+ ```tsx
67
+ import { useCreateFormex, Formex, useFormex, Field } from "@rebasepro/forms";
68
+
69
+ function MyForm() {
70
+ const controller = useCreateFormex({
71
+ initialValues: { name: "", email: "" },
72
+ validation: (values) => {
73
+ const errors: Record<string, string> = {};
74
+ if (!values.name) errors.name = "Required";
75
+ return errors;
76
+ },
77
+ onSubmit: async (values) => {
78
+ await saveToAPI(values);
79
+ }
80
+ });
81
+
82
+ return (
83
+ <Formex value={controller}>
84
+ <form onSubmit={controller.handleSubmit}>
85
+ <Field name="name" />
86
+ <Field name="email" type="email" />
87
+ <button type="submit" disabled={controller.isSubmitting}>
88
+ Save
89
+ </button>
90
+ </form>
91
+ </Formex>
92
+ );
93
+ }
94
+
95
+ // In a child component:
96
+ function SubmitButton() {
97
+ const { isSubmitting, dirty } = useFormex();
98
+ return <button type="submit" disabled={isSubmitting || !dirty}>Save</button>;
99
+ }
100
+ ```
101
+
102
+ ## Related Packages
103
+
104
+ - `@rebasepro/admin` — Uses Formex for all snapshot editing forms
105
+ - `@rebasepro/app` — Core Rebase framework
@@ -0,0 +1,52 @@
1
+ import * as React from "react";
2
+ import { FormexController } from "./types";
3
+ export interface FieldInputProps<Value> {
4
+ /** Value of the field */
5
+ value: Value;
6
+ /** Name of the field */
7
+ name: string;
8
+ /** Multiple select? */
9
+ multiple?: boolean;
10
+ /** Is the field checked? */
11
+ checked?: boolean;
12
+ /** Change event handler */
13
+ onChange: (event: React.SyntheticEvent) => void;
14
+ /** Blur event handler */
15
+ onBlur: (event: React.FocusEvent) => void;
16
+ }
17
+ export interface FormexFieldProps<Value = any, FormValues extends object = object> {
18
+ field: FieldInputProps<Value>;
19
+ form: FormexController<FormValues>;
20
+ }
21
+ export interface FieldConfig<Value, C extends React.ElementType | undefined = undefined> {
22
+ /**
23
+ * Component to render. Can either be a string e.g. 'select', 'input', or 'textarea', or a component.
24
+ */
25
+ as?: C | string | React.ForwardRefExoticComponent<Record<string, unknown>>;
26
+ /**
27
+ * Children render function <Field name>{props => ...}</Field>)
28
+ */
29
+ children?: ((props: FormexFieldProps<Value>) => React.ReactNode) | React.ReactNode;
30
+ /**
31
+ * Validate a single field value independently
32
+ */
33
+ /**
34
+ * Used for 'select' and related input types.
35
+ */
36
+ multiple?: boolean;
37
+ /**
38
+ * Field name
39
+ */
40
+ name: string;
41
+ /** HTML input type */
42
+ type?: string;
43
+ /** Field value */
44
+ value?: unknown;
45
+ /** Inner ref */
46
+ innerRef?: (instance: unknown) => void;
47
+ }
48
+ export type FieldProps<T, C extends React.ElementType | undefined> = {
49
+ as?: C;
50
+ } & (C extends React.ElementType ? (React.ComponentProps<C> & FieldConfig<T, C>) : FieldConfig<T, C>);
51
+ export declare function Field<T, C extends React.ElementType | undefined = undefined>({ validate, name, children, as: is, // `as` is reserved in typescript lol
52
+ className, ...props }: FieldProps<T, C>): any;
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { FormexController } from "./types";
3
+ export declare const useFormex: <T = any>() => FormexController<T>;
4
+ export declare const Formex: <T = any>({ value, children }: {
5
+ value: FormexController<T>;
6
+ children: React.ReactNode;
7
+ }) => React.JSX.Element;
@@ -0,0 +1,5 @@
1
+ export * from "./Field";
2
+ export * from "./Formex";
3
+ export * from "./types";
4
+ export * from "./utils";
5
+ export * from "./useCreateFormex";
@@ -0,0 +1,329 @@
1
+ import * as React$1 from "react";
2
+ import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { deepEqual } from "fast-equals";
5
+ //#region src/Formex.tsx
6
+ var FormexContext = React.createContext(null);
7
+ var useFormex = () => {
8
+ const ctx = useContext(FormexContext);
9
+ if (!ctx) throw new Error("useFormex must be used within a Formex provider");
10
+ return ctx;
11
+ };
12
+ var Formex = ({ value, children }) => {
13
+ return /* @__PURE__ */ jsx(FormexContext.Provider, {
14
+ value,
15
+ children
16
+ });
17
+ };
18
+ //#endregion
19
+ //#region src/utils.ts
20
+ /** @private is the value an empty array? */
21
+ var isEmptyArray = (value) => Array.isArray(value) && value.length === 0;
22
+ /** @private is the given object a Function? */
23
+ var isFunction = (obj) => typeof obj === "function";
24
+ /** @private is the given object an Object? */
25
+ var isObject = (obj) => obj !== null && typeof obj === "object";
26
+ /** @private is the given object an integer? */
27
+ var isInteger = (obj) => String(Math.floor(Number(obj))) === obj;
28
+ /** @private is the given object a NaN? */
29
+ var isNaN = (obj) => obj !== obj;
30
+ /**
31
+ * Deeply get a value from an object via its path.
32
+ */
33
+ function getIn(obj, key, def, p = 0) {
34
+ const path = toPath(key);
35
+ let current = obj;
36
+ while (current && p < path.length) current = current[path[p++]];
37
+ if (p !== path.length && !current) return def;
38
+ return current === void 0 ? def : current;
39
+ }
40
+ function setIn(obj, path, value) {
41
+ const res = clone(obj);
42
+ let resVal = res;
43
+ let i = 0;
44
+ const pathArray = toPath(path);
45
+ for (; i < pathArray.length - 1; i++) {
46
+ const currentPath = pathArray[i];
47
+ const currentObj = getIn(obj, pathArray.slice(0, i + 1));
48
+ if (currentObj && (isObject(currentObj) || Array.isArray(currentObj))) resVal = resVal[currentPath] = clone(currentObj);
49
+ else {
50
+ const nextPath = pathArray[i + 1];
51
+ resVal = resVal[currentPath] = isInteger(nextPath) && Number(nextPath) >= 0 ? [] : {};
52
+ }
53
+ }
54
+ if ((i === 0 ? obj : resVal)[pathArray[i]] === value) return obj;
55
+ if (value === void 0) delete resVal[pathArray[i]];
56
+ else resVal[pathArray[i]] = value;
57
+ if (i === 0 && value === void 0) delete res[pathArray[i]];
58
+ return res;
59
+ }
60
+ function clone(value) {
61
+ if (Array.isArray(value)) return [...value];
62
+ else if (typeof value === "object" && value !== null) {
63
+ if (Object.getPrototypeOf(value) !== Object.prototype) return value;
64
+ return { ...value };
65
+ } else return value;
66
+ }
67
+ function toPath(value) {
68
+ if (Array.isArray(value)) return value;
69
+ return value.replace(/\[(\d+)]/g, ".$1").replace(/^\./, "").replace(/\.$/, "").split(".");
70
+ }
71
+ //#endregion
72
+ //#region src/Field.tsx
73
+ function Field({ validate, name, children, as: is, className, ...props }) {
74
+ const formex = useFormex();
75
+ const field = getFieldProps({
76
+ name,
77
+ ...props
78
+ }, formex);
79
+ if (isFunction(children)) return children({
80
+ field,
81
+ form: formex
82
+ });
83
+ const asElement = is || "input";
84
+ if (typeof asElement === "string") {
85
+ const { innerRef, ...rest } = props;
86
+ return React$1.createElement(asElement, {
87
+ ref: innerRef,
88
+ ...field,
89
+ ...rest,
90
+ className
91
+ }, children);
92
+ }
93
+ return React$1.createElement(asElement, {
94
+ ...field,
95
+ ...props,
96
+ className
97
+ }, children);
98
+ }
99
+ var getFieldProps = (nameOrOptions, formex) => {
100
+ const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions.name;
101
+ const valueState = getIn(formex.values, name);
102
+ const field = {
103
+ name,
104
+ value: valueState,
105
+ onChange: formex.handleChange,
106
+ onBlur: formex.handleBlur
107
+ };
108
+ if (typeof nameOrOptions !== "string") {
109
+ const { type, value: valueProp, as: is, multiple } = nameOrOptions;
110
+ if (type === "checkbox") if (valueProp === void 0) field.checked = !!valueState;
111
+ else {
112
+ field.checked = !!(Array.isArray(valueState) && ~valueState.indexOf(valueProp));
113
+ field.value = valueProp;
114
+ }
115
+ else if (type === "radio") {
116
+ field.checked = valueState === valueProp;
117
+ field.value = valueProp;
118
+ } else if (is === "select" && multiple) {
119
+ field.value = field.value || [];
120
+ field.multiple = true;
121
+ }
122
+ }
123
+ return field;
124
+ };
125
+ //#endregion
126
+ //#region src/useCreateFormex.tsx
127
+ function useCreateFormex({ initialValues, initialErrors, initialDirty, initialTouched, validation, validateOnChange = false, validateOnInitialRender = false, onSubmit, onReset, onValuesChangeDeferred, debugId }) {
128
+ const initialValuesRef = useRef(initialValues);
129
+ const valuesRef = useRef(initialValues);
130
+ const debugIdRef = useRef(debugId);
131
+ const [values, setValuesInner] = useState(initialValues);
132
+ const [touchedState, setTouchedState] = useState(initialTouched ?? {});
133
+ const [errors, setErrors] = useState(initialErrors ?? {});
134
+ const [dirty, setDirty] = useState(initialDirty ?? false);
135
+ const [submitCount, setSubmitCount] = useState(0);
136
+ const [isSubmitting, setIsSubmitting] = useState(false);
137
+ const [isValidating, setIsValidating] = useState(false);
138
+ const [version, setVersion] = useState(0);
139
+ const onValuesChangeRef = useRef(onValuesChangeDeferred);
140
+ onValuesChangeRef.current = onValuesChangeDeferred;
141
+ const debounceTimeoutRef = useRef(void 0);
142
+ const callDebouncedOnValuesChange = useCallback((values) => {
143
+ if (onValuesChangeRef.current) {
144
+ if (debounceTimeoutRef.current) clearTimeout(debounceTimeoutRef.current);
145
+ debounceTimeoutRef.current = setTimeout(() => {
146
+ onValuesChangeRef.current?.(values, controllerRef.current);
147
+ }, 300);
148
+ }
149
+ }, []);
150
+ const historyRef = useRef([initialValues]);
151
+ const historyIndexRef = useRef(0);
152
+ useEffect(() => {
153
+ if (validateOnInitialRender) validate();
154
+ }, []);
155
+ const setValues = useCallback((newValues) => {
156
+ valuesRef.current = newValues;
157
+ setValuesInner(newValues);
158
+ setDirty(!deepEqual(initialValuesRef.current, newValues));
159
+ const newHistory = historyRef.current.slice(0, historyIndexRef.current + 1);
160
+ newHistory.push(newValues);
161
+ historyRef.current = newHistory;
162
+ historyIndexRef.current = newHistory.length - 1;
163
+ callDebouncedOnValuesChange(newValues);
164
+ }, [callDebouncedOnValuesChange]);
165
+ const validate = useCallback(async () => {
166
+ setIsValidating(true);
167
+ const validationErrors = await validation?.(valuesRef.current);
168
+ setErrors(validationErrors ?? {});
169
+ setIsValidating(false);
170
+ return validationErrors;
171
+ }, [validation]);
172
+ const setFieldValue = useCallback((key, value, shouldValidate) => {
173
+ const newValues = setIn(valuesRef.current, key, value);
174
+ valuesRef.current = newValues;
175
+ setValuesInner(newValues);
176
+ if (!deepEqual(getIn(initialValuesRef.current, key), value)) setDirty(true);
177
+ if (shouldValidate) validate();
178
+ const newHistory = historyRef.current.slice(0, historyIndexRef.current + 1);
179
+ newHistory.push(newValues);
180
+ historyRef.current = newHistory;
181
+ historyIndexRef.current = newHistory.length - 1;
182
+ callDebouncedOnValuesChange(newValues);
183
+ }, [validate, callDebouncedOnValuesChange]);
184
+ const setFieldError = useCallback((key, error) => {
185
+ setErrors((prevErrors) => {
186
+ const newErrors = { ...prevErrors };
187
+ if (error) newErrors[key] = error;
188
+ else delete newErrors[key];
189
+ return newErrors;
190
+ });
191
+ }, []);
192
+ const setFieldTouched = useCallback((key, touched, shouldValidate) => {
193
+ setTouchedState((prev) => ({
194
+ ...prev,
195
+ [key]: touched
196
+ }));
197
+ if (shouldValidate) validate();
198
+ }, [validate]);
199
+ const handleChange = useCallback((event) => {
200
+ const target = event.target;
201
+ let value;
202
+ if (target.type === "checkbox") value = target.checked;
203
+ else if (target.type === "number") value = target.valueAsNumber;
204
+ else value = target.value;
205
+ const name = target.name;
206
+ setFieldValue(name, value, validateOnChange);
207
+ setFieldTouched(name, true);
208
+ }, [
209
+ setFieldValue,
210
+ setFieldTouched,
211
+ validateOnChange
212
+ ]);
213
+ const handleBlur = useCallback((event) => {
214
+ const name = event.target.name;
215
+ setFieldTouched(name, true);
216
+ }, [setFieldTouched]);
217
+ const submit = useCallback(async (e) => {
218
+ e?.preventDefault();
219
+ e?.stopPropagation();
220
+ setIsSubmitting(true);
221
+ setSubmitCount((prev) => prev + 1);
222
+ const validationErrors = await validation?.(valuesRef.current);
223
+ if (validationErrors && Object.keys(validationErrors).length > 0) setErrors(validationErrors);
224
+ else {
225
+ setErrors({});
226
+ await onSubmit?.(valuesRef.current, controllerRef.current);
227
+ }
228
+ setIsSubmitting(false);
229
+ setVersion((prev) => prev + 1);
230
+ }, [onSubmit, validation]);
231
+ const resetForm = useCallback((props) => {
232
+ const { submitCount: submitCountProp, values: valuesProp, errors: errorsProp, touched: touchedProp } = props ?? {};
233
+ valuesRef.current = valuesProp ?? initialValuesRef.current;
234
+ initialValuesRef.current = valuesProp ?? initialValuesRef.current;
235
+ setValuesInner(valuesProp ?? initialValuesRef.current);
236
+ setErrors(errorsProp ?? {});
237
+ setTouchedState(touchedProp ?? initialTouched ?? {});
238
+ setDirty(false);
239
+ setSubmitCount(submitCountProp ?? 0);
240
+ setVersion((prev) => prev + 1);
241
+ onReset?.(controllerRef.current);
242
+ historyRef.current = [valuesProp ?? initialValuesRef.current];
243
+ historyIndexRef.current = 0;
244
+ }, [onReset, initialTouched]);
245
+ useEffect(() => {
246
+ if (!deepEqual(initialValuesRef.current, initialValues)) resetForm({ values: initialValues });
247
+ }, [initialValues, resetForm]);
248
+ const undo = useCallback(() => {
249
+ if (historyIndexRef.current > 0) {
250
+ const newIndex = historyIndexRef.current - 1;
251
+ const newValues = historyRef.current[newIndex];
252
+ setValuesInner(newValues);
253
+ valuesRef.current = newValues;
254
+ historyIndexRef.current = newIndex;
255
+ setDirty(!deepEqual(initialValuesRef.current, newValues));
256
+ callDebouncedOnValuesChange(newValues);
257
+ }
258
+ }, [callDebouncedOnValuesChange]);
259
+ const redo = useCallback(() => {
260
+ if (historyIndexRef.current < historyRef.current.length - 1) {
261
+ const newIndex = historyIndexRef.current + 1;
262
+ const newValues = historyRef.current[newIndex];
263
+ setValuesInner(newValues);
264
+ valuesRef.current = newValues;
265
+ historyIndexRef.current = newIndex;
266
+ setDirty(!deepEqual(initialValuesRef.current, newValues));
267
+ callDebouncedOnValuesChange(newValues);
268
+ }
269
+ }, [callDebouncedOnValuesChange]);
270
+ const controllerRef = useRef({});
271
+ const controller = useMemo(() => ({
272
+ values,
273
+ initialValues: initialValuesRef.current,
274
+ handleChange,
275
+ isSubmitting,
276
+ setSubmitting: setIsSubmitting,
277
+ setValues,
278
+ setFieldValue,
279
+ errors,
280
+ setFieldError,
281
+ touched: touchedState,
282
+ setFieldTouched,
283
+ setTouched: setTouchedState,
284
+ dirty,
285
+ setDirty,
286
+ handleSubmit: submit,
287
+ submitCount,
288
+ setSubmitCount,
289
+ handleBlur,
290
+ validate,
291
+ isValidating,
292
+ resetForm,
293
+ version,
294
+ debugId: debugIdRef.current,
295
+ undo,
296
+ redo,
297
+ canUndo: historyIndexRef.current > 0,
298
+ canRedo: historyIndexRef.current < historyRef.current.length - 1
299
+ }), [
300
+ values,
301
+ errors,
302
+ touchedState,
303
+ dirty,
304
+ isSubmitting,
305
+ submitCount,
306
+ isValidating,
307
+ version,
308
+ handleChange,
309
+ handleBlur,
310
+ setValues,
311
+ setFieldValue,
312
+ setFieldTouched,
313
+ setTouchedState,
314
+ setFieldError,
315
+ validate,
316
+ submit,
317
+ resetForm,
318
+ undo,
319
+ redo
320
+ ]);
321
+ useEffect(() => {
322
+ controllerRef.current = controller;
323
+ }, [controller]);
324
+ return controller;
325
+ }
326
+ //#endregion
327
+ export { Field, Formex, clone, getIn, isEmptyArray, isFunction, isInteger, isNaN, isObject, setIn, useCreateFormex, useFormex };
328
+
329
+ //# sourceMappingURL=index.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.es.js","names":[],"sources":["../src/Formex.tsx","../src/utils.ts","../src/Field.tsx","../src/useCreateFormex.tsx"],"sourcesContent":["import React, { useContext } from \"react\";\nimport { FormexController } from \"./types\";\n\n\nconst FormexContext = React.createContext<FormexController<any> | null>(null);\n\nexport const useFormex = <T = any>() => {\n const ctx = useContext(FormexContext);\n if (!ctx) throw new Error(\"useFormex must be used within a Formex provider\");\n return ctx as FormexController<T>;\n};\n\nexport const Formex = <T = any>({ value, children }: { value: FormexController<T>, children: React.ReactNode }) => {\n return <FormexContext.Provider value={value}>{children}</FormexContext.Provider>;\n};\n","/** @private is the value an empty array? */\nexport const isEmptyArray = (value?: unknown) =>\n Array.isArray(value) && value.length === 0;\n\n/** @private is the given object a Function? */\n\nexport const isFunction = (obj: unknown): obj is Function =>\n typeof obj === \"function\";\n\n/** @private is the given object an Object? */\nexport const isObject = (obj: unknown): obj is Record<string, unknown> =>\n obj !== null && typeof obj === \"object\";\n\n/** @private is the given object an integer? */\nexport const isInteger = (obj: unknown): boolean =>\n String(Math.floor(Number(obj))) === obj;\n\n/** @private is the given object a NaN? */\n\nexport const isNaN = (obj: unknown): boolean => obj !== obj;\n\n/**\n * Deeply get a value from an object via its path.\n */\nexport function getIn(\n obj: unknown,\n key: string | string[],\n def?: unknown,\n p = 0\n): unknown {\n const path = toPath(key);\n let current: unknown = obj;\n while (current && p < path.length) {\n current = (current as Record<string, unknown>)[path[p++]];\n }\n\n // check if path is not in the end\n if (p !== path.length && !current) {\n return def;\n }\n\n return current === undefined ? def : current;\n}\n\nexport function setIn(obj: unknown, path: string, value: unknown): unknown {\n const res = clone(obj) as Record<string, unknown>; // this keeps inheritance when obj is a class\n let resVal: Record<string, unknown> = res;\n let i = 0;\n const pathArray = toPath(path);\n\n for (; i < pathArray.length - 1; i++) {\n const currentPath: string = pathArray[i];\n const currentObj = getIn(obj, pathArray.slice(0, i + 1));\n\n if (currentObj && (isObject(currentObj) || Array.isArray(currentObj))) {\n resVal = resVal[currentPath] = clone(currentObj) as Record<string, unknown>;\n } else {\n const nextPath: string = pathArray[i + 1];\n resVal = resVal[currentPath] =\n (isInteger(nextPath) && Number(nextPath) >= 0 ? [] : {}) as Record<string, unknown>;\n }\n }\n\n // Return original object if new value is the same as current\n if ((i === 0 ? (obj as Record<string, unknown>) : resVal)[pathArray[i]] === value) {\n return obj;\n }\n\n if (value === undefined) {\n delete resVal[pathArray[i]];\n } else {\n resVal[pathArray[i]] = value;\n }\n\n // If the path array has a single element, the loop did not run.\n // Deleting on `resVal` had no effect in this scenario, so we delete on the result instead.\n if (i === 0 && value === undefined) {\n delete res[pathArray[i]];\n }\n\n return res;\n}\n\nexport function clone(value: unknown): unknown {\n if (Array.isArray(value)) {\n return [...value];\n } else if (typeof value === \"object\" && value !== null) {\n // Preserve class instances (EntityReference, GeoPoint, etc.) - don't spread them\n if (Object.getPrototypeOf(value) !== Object.prototype) {\n return value;\n }\n return { ...(value as Record<string, unknown>) };\n } else {\n return value; // This is for primitive types which do not need cloning.\n }\n}\n\nfunction toPath(value: string | string[]) {\n if (Array.isArray(value)) return value; // Already in path array form.\n // Replace brackets with dots, remove leading/trailing dots, then split by dot.\n return value.replace(/\\[(\\d+)]/g, \".$1\").replace(/^\\./, \"\").replace(/\\.$/, \"\").split(\".\");\n}\n","import * as React from \"react\";\nimport { useFormex } from \"./Formex\";\nimport { getIn, isFunction, isObject } from \"./utils\";\nimport { FormexController } from \"./types\";\n\nexport interface FieldInputProps<Value> {\n /** Value of the field */\n value: Value;\n /** Name of the field */\n name: string;\n /** Multiple select? */\n multiple?: boolean;\n /** Is the field checked? */\n checked?: boolean;\n /** Change event handler */\n onChange: (event: React.SyntheticEvent) => void,\n /** Blur event handler */\n onBlur: (event: React.FocusEvent) => void,\n}\n\nexport interface FormexFieldProps<Value = any, FormValues extends object = object> {\n field: FieldInputProps<Value>;\n form: FormexController<FormValues>;\n}\n\nexport interface FieldConfig<Value, C extends React.ElementType | undefined = undefined> {\n\n /**\n * Component to render. Can either be a string e.g. 'select', 'input', or 'textarea', or a component.\n */\n as?:\n | C\n | string\n | React.ForwardRefExoticComponent<Record<string, unknown>>;\n\n /**\n * Children render function <Field name>{props => ...}</Field>)\n */\n children?: ((props: FormexFieldProps<Value>) => React.ReactNode) | React.ReactNode;\n\n /**\n * Validate a single field value independently\n */\n // validate?: FieldValidator;\n\n /**\n * Used for 'select' and related input types.\n */\n multiple?: boolean;\n\n /**\n * Field name\n */\n name: string;\n\n /** HTML input type */\n type?: string;\n\n /** Field value */\n value?: unknown;\n\n /** Inner ref */\n innerRef?: (instance: unknown) => void;\n\n}\n\nexport type FieldProps<T, C extends React.ElementType | undefined> = {\n as?: C;\n} & (C extends React.ElementType ? (React.ComponentProps<C> & FieldConfig<T, C>) : FieldConfig<T, C>);\n\nexport function Field<T, C extends React.ElementType | undefined = undefined>({\n validate,\n name,\n children,\n as: is, // `as` is reserved in typescript lol\n // component,\n className,\n ...props\n }: FieldProps<T, C>) {\n const formex = useFormex();\n\n const field = getFieldProps({ name,\n...props }, formex);\n\n if (isFunction(children)) {\n return children({ field,\nform: formex });\n }\n\n // if (component) {\n // if (typeof component === \"string\") {\n // const { innerRef, ...rest } = props;\n // return React.createElement(\n // component,\n // { ref: innerRef, ...field, ...rest, className },\n // children\n // );\n // }\n // return React.createElement(\n // component,\n // { field, form: formex, ...props, className },\n // children\n // );\n // }\n\n // default to input here so we can check for both `as` and `children` above\n const asElement = is || \"input\";\n\n if (typeof asElement === \"string\") {\n const { innerRef, ...rest } = props;\n return React.createElement(\n asElement,\n { ref: innerRef,\n...field,\n...rest,\nclassName },\n children\n );\n }\n\n return React.createElement(asElement, { ...field,\n...props,\nclassName }, children);\n}\n\nconst getFieldProps = (nameOrOptions: string | FieldConfig<unknown>, formex: FormexController<object>): FieldInputProps<unknown> => {\n const name: string = typeof nameOrOptions === \"string\"\n ? nameOrOptions\n : nameOrOptions.name;\n const valueState = getIn(formex.values as Record<string, unknown>, name);\n\n const field: FieldInputProps<unknown> = {\n name: name as string,\n value: valueState,\n onChange: formex.handleChange,\n onBlur: formex.handleBlur\n };\n if (typeof nameOrOptions !== \"string\") {\n const {\n type,\n value: valueProp, // value is special for checkboxes\n as: is,\n multiple\n } = nameOrOptions as FieldConfig<unknown>;\n\n if (type === \"checkbox\") {\n if (valueProp === undefined) {\n field.checked = !!valueState;\n } else {\n field.checked = !!(\n Array.isArray(valueState) && ~valueState.indexOf(valueProp)\n );\n field.value = valueProp;\n }\n } else if (type === \"radio\") {\n field.checked = valueState === valueProp;\n field.value = valueProp;\n } else if (is === \"select\" && multiple) {\n field.value = field.value || [];\n field.multiple = true;\n }\n }\n return field;\n};\n","import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { getIn, setIn } from \"./utils\";\nimport { deepEqual as equal } from \"fast-equals\";\n\nimport { FormexController, FormexResetProps } from \"./types\";\n\nexport function useCreateFormex<T = any>({\n initialValues,\n initialErrors,\n initialDirty,\n initialTouched,\n validation,\n validateOnChange = false,\n validateOnInitialRender = false,\n onSubmit,\n onReset,\n onValuesChangeDeferred,\n debugId\n}: {\n initialValues: T;\n initialErrors?: Record<string, string>;\n initialDirty?: boolean;\n initialTouched?: Record<string, boolean>;\n validateOnChange?: boolean;\n validateOnInitialRender?: boolean;\n validation?: (\n values: T\n ) =>\n | Record<string, string>\n | Promise<Record<string, string>>\n | undefined\n | void;\n onValuesChangeDeferred?: (values: T, controller: FormexController<T>) => void;\n onSubmit?: (values: T, controller: FormexController<T>) => void | Promise<void>;\n onReset?: (controller: FormexController<T>) => void | Promise<void>;\n debugId?: string;\n}): FormexController<T> {\n const initialValuesRef = useRef<T>(initialValues);\n const valuesRef = useRef<T>(initialValues);\n const debugIdRef = useRef<string | undefined>(debugId);\n\n const [values, setValuesInner] = useState<T>(initialValues);\n const [touchedState, setTouchedState] = useState<Record<string, boolean>>(initialTouched ?? {});\n const [errors, setErrors] = useState<Record<string, string>>(initialErrors ?? {});\n const [dirty, setDirty] = useState(initialDirty ?? false);\n const [submitCount, setSubmitCount] = useState(0);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isValidating, setIsValidating] = useState(false);\n const [version, setVersion] = useState(0);\n\n const onValuesChangeRef = useRef(onValuesChangeDeferred);\n onValuesChangeRef.current = onValuesChangeDeferred;\n const debounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const callDebouncedOnValuesChange = useCallback((values: T) => {\n if (onValuesChangeRef.current) {\n if (debounceTimeoutRef.current) {\n clearTimeout(debounceTimeoutRef.current);\n }\n debounceTimeoutRef.current = setTimeout(() => {\n onValuesChangeRef.current?.(values, controllerRef.current);\n }, 300);\n }\n }, []);\n\n // Replace state for history with refs\n const historyRef = useRef<T[]>([initialValues]);\n const historyIndexRef = useRef<number>(0);\n\n useEffect(() => {\n if (validateOnInitialRender) {\n validate();\n }\n }, []);\n\n const setValues = useCallback((newValues: T) => {\n valuesRef.current = newValues;\n setValuesInner(newValues);\n setDirty(!equal(initialValuesRef.current, newValues));\n // Update history using refs\n const newHistory = historyRef.current.slice(0, historyIndexRef.current + 1);\n newHistory.push(newValues);\n historyRef.current = newHistory;\n historyIndexRef.current = newHistory.length - 1;\n callDebouncedOnValuesChange(newValues);\n }, [callDebouncedOnValuesChange]);\n\n const validate = useCallback(async () => {\n setIsValidating(true);\n const validationErrors = await validation?.(valuesRef.current);\n setErrors(validationErrors ?? {});\n setIsValidating(false);\n return validationErrors;\n }, [validation]);\n\n const setFieldValue = useCallback(\n (key: string, value: unknown, shouldValidate?: boolean) => {\n const newValues = setIn(valuesRef.current as Record<string, unknown>, key, value) as T;\n valuesRef.current = newValues;\n setValuesInner(newValues);\n if (!equal(getIn(initialValuesRef.current as Record<string, unknown>, key), value)) {\n setDirty(true);\n }\n if (shouldValidate) {\n validate();\n }\n // Update history using refs\n const newHistory = historyRef.current.slice(0, historyIndexRef.current + 1);\n newHistory.push(newValues);\n historyRef.current = newHistory;\n historyIndexRef.current = newHistory.length - 1;\n callDebouncedOnValuesChange(newValues);\n },\n [validate, callDebouncedOnValuesChange]\n );\n\n const setFieldError = useCallback((key: string, error: string | undefined) => {\n setErrors((prevErrors: Record<string, string>) => {\n const newErrors = { ...prevErrors };\n if (error) {\n newErrors[key] = error;\n } else {\n delete newErrors[key];\n }\n return newErrors;\n });\n }, []);\n\n const setFieldTouched = useCallback(\n (key: string, touched: boolean, shouldValidate?: boolean) => {\n setTouchedState((prev: Record<string, boolean>) => ({\n ...prev,\n [key]: touched\n }));\n if (shouldValidate) {\n validate();\n }\n },\n [validate]\n );\n\n const handleChange = useCallback(\n (event: React.SyntheticEvent) => {\n const target = event.target as HTMLInputElement;\n let value;\n if (target.type === \"checkbox\") {\n value = target.checked;\n } else if (target.type === \"number\") {\n value = target.valueAsNumber;\n } else {\n value = target.value;\n }\n const name = target.name;\n setFieldValue(name, value, validateOnChange);\n setFieldTouched(name, true);\n },\n [setFieldValue, setFieldTouched, validateOnChange]\n );\n\n const handleBlur = useCallback((event: React.FocusEvent) => {\n const target = event.target as HTMLInputElement;\n const name = target.name;\n setFieldTouched(name, true);\n }, [setFieldTouched]);\n\n const submit = useCallback(\n async (e?: React.FormEvent<HTMLFormElement>) => {\n e?.preventDefault();\n e?.stopPropagation();\n setIsSubmitting(true);\n setSubmitCount((prev: number) => prev + 1);\n const validationErrors = await validation?.(valuesRef.current);\n if (validationErrors && Object.keys(validationErrors).length > 0) {\n setErrors(validationErrors);\n } else {\n setErrors({});\n await onSubmit?.(valuesRef.current, controllerRef.current);\n }\n setIsSubmitting(false);\n setVersion((prev: number) => prev + 1);\n },\n [onSubmit, validation]\n );\n\n const resetForm = useCallback((props?: FormexResetProps<T>) => {\n const {\n submitCount: submitCountProp,\n values: valuesProp,\n errors: errorsProp,\n touched: touchedProp\n } = props ?? {};\n valuesRef.current = valuesProp ?? initialValuesRef.current;\n initialValuesRef.current = valuesProp ?? initialValuesRef.current;\n setValuesInner(valuesProp ?? initialValuesRef.current);\n setErrors(errorsProp ?? {});\n setTouchedState(touchedProp ?? initialTouched ?? {});\n setDirty(false);\n setSubmitCount(submitCountProp ?? 0);\n setVersion((prev: number) => prev + 1);\n onReset?.(controllerRef.current);\n // Reset history with refs\n historyRef.current = [valuesProp ?? initialValuesRef.current];\n historyIndexRef.current = 0;\n }, [onReset, initialTouched]);\n\n useEffect(() => {\n if (!equal(initialValuesRef.current, initialValues)) {\n resetForm({ values: initialValues });\n }\n }, [initialValues, resetForm]);\n\n const undo = useCallback(() => {\n if (historyIndexRef.current > 0) {\n const newIndex = historyIndexRef.current - 1;\n const newValues = historyRef.current[newIndex];\n setValuesInner(newValues);\n valuesRef.current = newValues;\n historyIndexRef.current = newIndex;\n setDirty(!equal(initialValuesRef.current, newValues));\n callDebouncedOnValuesChange(newValues);\n }\n }, [callDebouncedOnValuesChange]);\n\n const redo = useCallback(() => {\n if (historyIndexRef.current < historyRef.current.length - 1) {\n const newIndex = historyIndexRef.current + 1;\n const newValues = historyRef.current[newIndex];\n setValuesInner(newValues);\n valuesRef.current = newValues;\n historyIndexRef.current = newIndex;\n setDirty(!equal(initialValuesRef.current, newValues));\n callDebouncedOnValuesChange(newValues);\n }\n }, [callDebouncedOnValuesChange]);\n\n const controllerRef = useRef<FormexController<T>>({} as FormexController<T>);\n\n const controller = useMemo<FormexController<T>>(\n () => ({\n values,\n initialValues: initialValuesRef.current,\n handleChange,\n isSubmitting,\n setSubmitting: setIsSubmitting,\n setValues,\n setFieldValue,\n errors,\n setFieldError,\n touched: touchedState,\n setFieldTouched,\n setTouched: setTouchedState,\n dirty,\n setDirty,\n handleSubmit: submit,\n submitCount,\n setSubmitCount,\n handleBlur,\n validate,\n isValidating,\n resetForm,\n version,\n debugId: debugIdRef.current,\n undo,\n redo,\n canUndo: historyIndexRef.current > 0,\n canRedo: historyIndexRef.current < historyRef.current.length - 1\n }),\n [\n values,\n errors,\n touchedState,\n dirty,\n isSubmitting,\n submitCount,\n isValidating,\n version,\n handleChange,\n handleBlur,\n setValues,\n setFieldValue,\n setFieldTouched,\n setTouchedState,\n setFieldError,\n validate,\n submit,\n resetForm,\n undo,\n redo\n ]\n );\n\n useEffect(() => {\n controllerRef.current = controller;\n }, [controller]);\n\n return controller;\n}\n"],"mappings":"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"}