remix-validated-form 4.6.4 → 4.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/.turbo/turbo-build.log +12 -12
  2. package/.turbo/turbo-typecheck.log +1 -0
  3. package/README.md +10 -9
  4. package/browser/ValidatedForm.d.ts +3 -3
  5. package/browser/hooks.d.ts +1 -1
  6. package/browser/internal/formContext.d.ts +3 -3
  7. package/browser/internal/getInputProps.d.ts +7 -7
  8. package/browser/internal/hydratable.d.ts +1 -1
  9. package/browser/internal/state/createFormStore.d.ts +3 -3
  10. package/browser/internal/state/fieldArray.d.ts +5 -5
  11. package/browser/internal/state/types.d.ts +1 -1
  12. package/browser/server.d.ts +1 -1
  13. package/browser/unreleased/formStateHooks.d.ts +2 -2
  14. package/browser/userFacingFormContext.d.ts +1 -1
  15. package/browser/validation/types.d.ts +15 -15
  16. package/dist/index.cjs.js +1928 -0
  17. package/dist/index.cjs.js.map +1 -0
  18. package/dist/index.d.ts +337 -0
  19. package/dist/{remix-validated-form.es.js → index.esm.js} +573 -869
  20. package/dist/index.esm.js.map +1 -0
  21. package/package.json +14 -11
  22. package/src/ValidatedForm.tsx +19 -6
  23. package/src/hooks.ts +1 -1
  24. package/src/internal/formContext.ts +2 -2
  25. package/src/server.ts +1 -1
  26. package/stats.html +1 -1
  27. package/tsup.config.ts +3 -0
  28. package/dist/remix-validated-form.cjs.js +0 -27
  29. package/dist/remix-validated-form.cjs.js.map +0 -1
  30. package/dist/remix-validated-form.es.js.map +0 -1
  31. package/dist/remix-validated-form.umd.js +0 -27
  32. package/dist/remix-validated-form.umd.js.map +0 -1
  33. package/dist/types/ValidatedForm.d.ts +0 -50
  34. package/dist/types/hooks.d.ts +0 -67
  35. package/dist/types/index.d.ts +0 -7
  36. package/dist/types/internal/MultiValueMap.d.ts +0 -11
  37. package/dist/types/internal/constants.d.ts +0 -3
  38. package/dist/types/internal/flatten.d.ts +0 -1
  39. package/dist/types/internal/formContext.d.ts +0 -12
  40. package/dist/types/internal/getInputProps.d.ts +0 -29
  41. package/dist/types/internal/hooks.d.ts +0 -35
  42. package/dist/types/internal/hydratable.d.ts +0 -14
  43. package/dist/types/internal/logic/getCheckboxChecked.d.ts +0 -1
  44. package/dist/types/internal/logic/getRadioChecked.d.ts +0 -1
  45. package/dist/types/internal/logic/requestSubmit.d.ts +0 -5
  46. package/dist/types/internal/state/arrayUtil.d.ts +0 -12
  47. package/dist/types/internal/state/controlledFields.d.ts +0 -7
  48. package/dist/types/internal/state/createFormStore.d.ts +0 -79
  49. package/dist/types/internal/state/fieldArray.d.ts +0 -28
  50. package/dist/types/internal/state/storeHooks.d.ts +0 -3
  51. package/dist/types/internal/state/types.d.ts +0 -1
  52. package/dist/types/internal/submissionCallbacks.d.ts +0 -1
  53. package/dist/types/internal/util.d.ts +0 -5
  54. package/dist/types/server.d.ts +0 -21
  55. package/dist/types/unreleased/formStateHooks.d.ts +0 -64
  56. package/dist/types/userFacingFormContext.d.ts +0 -85
  57. package/dist/types/validation/createValidator.d.ts +0 -7
  58. package/dist/types/validation/types.d.ts +0 -58
  59. package/vite.config.ts +0 -7
@@ -0,0 +1,1928 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from2, except, desc) => {
13
+ if (from2 && typeof from2 === "object" || typeof from2 === "function") {
14
+ for (let key of __getOwnPropNames(from2))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
25
+
26
+ // src/index.ts
27
+ var src_exports = {};
28
+ __export(src_exports, {
29
+ FieldArray: () => FieldArray,
30
+ ValidatedForm: () => ValidatedForm,
31
+ createValidator: () => createValidator,
32
+ setFormDefaults: () => setFormDefaults,
33
+ useControlField: () => useControlField,
34
+ useField: () => useField,
35
+ useFieldArray: () => useFieldArray,
36
+ useFormContext: () => useFormContext,
37
+ useIsSubmitting: () => useIsSubmitting,
38
+ useIsValid: () => useIsValid,
39
+ useUpdateControlledField: () => useUpdateControlledField,
40
+ validationError: () => validationError
41
+ });
42
+ module.exports = __toCommonJS(src_exports);
43
+
44
+ // src/hooks.ts
45
+ var import_react5 = require("react");
46
+
47
+ // src/internal/getInputProps.ts
48
+ var R = __toESM(require("remeda"));
49
+
50
+ // src/internal/logic/getCheckboxChecked.ts
51
+ var getCheckboxChecked = (checkboxValue = "on", newValue) => {
52
+ if (Array.isArray(newValue))
53
+ return newValue.some((val) => val === true || val === checkboxValue);
54
+ if (typeof newValue === "boolean")
55
+ return newValue;
56
+ if (typeof newValue === "string")
57
+ return newValue === checkboxValue;
58
+ return void 0;
59
+ };
60
+
61
+ // src/internal/logic/getRadioChecked.ts
62
+ var getRadioChecked = (radioValue = "on", newValue) => {
63
+ if (typeof newValue === "string")
64
+ return newValue === radioValue;
65
+ return void 0;
66
+ };
67
+ if (void 0) {
68
+ const { it, expect } = void 0;
69
+ it("getRadioChecked", () => {
70
+ expect(getRadioChecked("on", "on")).toBe(true);
71
+ expect(getRadioChecked("on", void 0)).toBe(void 0);
72
+ expect(getRadioChecked("trueValue", void 0)).toBe(void 0);
73
+ expect(getRadioChecked("trueValue", "bob")).toBe(false);
74
+ expect(getRadioChecked("trueValue", "trueValue")).toBe(true);
75
+ });
76
+ }
77
+
78
+ // src/internal/getInputProps.ts
79
+ var defaultValidationBehavior = {
80
+ initial: "onBlur",
81
+ whenTouched: "onChange",
82
+ whenSubmitted: "onChange"
83
+ };
84
+ var createGetInputProps = ({
85
+ clearError,
86
+ validate,
87
+ defaultValue,
88
+ touched,
89
+ setTouched,
90
+ hasBeenSubmitted,
91
+ validationBehavior,
92
+ name
93
+ }) => {
94
+ const validationBehaviors = {
95
+ ...defaultValidationBehavior,
96
+ ...validationBehavior
97
+ };
98
+ return (props = {}) => {
99
+ const behavior = hasBeenSubmitted ? validationBehaviors.whenSubmitted : touched ? validationBehaviors.whenTouched : validationBehaviors.initial;
100
+ const inputProps = {
101
+ ...props,
102
+ onChange: (...args) => {
103
+ var _a;
104
+ if (behavior === "onChange")
105
+ validate();
106
+ else
107
+ clearError();
108
+ return (_a = props == null ? void 0 : props.onChange) == null ? void 0 : _a.call(props, ...args);
109
+ },
110
+ onBlur: (...args) => {
111
+ var _a;
112
+ if (behavior === "onBlur")
113
+ validate();
114
+ setTouched(true);
115
+ return (_a = props == null ? void 0 : props.onBlur) == null ? void 0 : _a.call(props, ...args);
116
+ },
117
+ name
118
+ };
119
+ if (props.type === "checkbox") {
120
+ inputProps.defaultChecked = getCheckboxChecked(props.value, defaultValue);
121
+ } else if (props.type === "radio") {
122
+ inputProps.defaultChecked = getRadioChecked(props.value, defaultValue);
123
+ } else if (props.value === void 0) {
124
+ inputProps.defaultValue = defaultValue;
125
+ }
126
+ return R.omitBy(inputProps, (value) => value === void 0);
127
+ };
128
+ };
129
+
130
+ // src/internal/hooks.ts
131
+ var import_react2 = require("@remix-run/react");
132
+ var import_react3 = require("react");
133
+
134
+ // ../set-get/src/stringToPathArray.ts
135
+ var stringToPathArray = (path) => {
136
+ if (path.length === 0)
137
+ return [];
138
+ const match = path.match(/^\[(.+?)\](.*)$/) || path.match(/^\.?([^\.\[\]]+)(.*)$/);
139
+ if (match) {
140
+ const [_, key, rest] = match;
141
+ return [/^\d+$/.test(key) ? Number(key) : key, ...stringToPathArray(rest)];
142
+ }
143
+ return [path];
144
+ };
145
+
146
+ // ../set-get/src/setPath.ts
147
+ function setPath(object, path, defaultValue) {
148
+ return _setPathNormalized(object, stringToPathArray(path), defaultValue);
149
+ }
150
+ function _setPathNormalized(object, path, value) {
151
+ var _a;
152
+ const leadingSegments = path.slice(0, -1);
153
+ const lastSegment = path[path.length - 1];
154
+ let obj = object;
155
+ for (let i = 0; i < leadingSegments.length; i++) {
156
+ const segment = leadingSegments[i];
157
+ if (obj[segment] === void 0) {
158
+ const nextSegment = (_a = leadingSegments[i + 1]) != null ? _a : lastSegment;
159
+ obj[segment] = typeof nextSegment === "number" ? [] : {};
160
+ }
161
+ obj = obj[segment];
162
+ }
163
+ obj[lastSegment] = value;
164
+ return object;
165
+ }
166
+
167
+ // ../set-get/src/getPath.ts
168
+ var R2 = __toESM(require("remeda"));
169
+ var getPath = (object, path) => R2.pathOr(object, stringToPathArray(path), void 0);
170
+
171
+ // src/internal/hooks.ts
172
+ var import_tiny_invariant3 = __toESM(require("tiny-invariant"));
173
+
174
+ // src/internal/constants.ts
175
+ var FORM_ID_FIELD = "__rvfInternalFormId";
176
+ var FORM_DEFAULTS_FIELD = "__rvfInternalFormDefaults";
177
+ var formDefaultValuesKey = (formId) => `${FORM_DEFAULTS_FIELD}_${formId}`;
178
+
179
+ // src/internal/formContext.ts
180
+ var import_react = require("react");
181
+ var InternalFormContext = (0, import_react.createContext)(null);
182
+
183
+ // src/internal/hydratable.ts
184
+ var serverData = (data) => ({
185
+ hydrateTo: () => data,
186
+ map: (fn) => serverData(fn(data))
187
+ });
188
+ var hydratedData = () => ({
189
+ hydrateTo: (hydratedData2) => hydratedData2,
190
+ map: () => hydratedData()
191
+ });
192
+ var from = (data, hydrated) => hydrated ? hydratedData() : serverData(data);
193
+ var hydratable = {
194
+ serverData,
195
+ hydratedData,
196
+ from
197
+ };
198
+
199
+ // src/internal/state/createFormStore.ts
200
+ var import_tiny_invariant2 = __toESM(require("tiny-invariant"));
201
+ var import_zustand = __toESM(require("zustand"));
202
+ var import_immer = require("zustand/middleware/immer");
203
+
204
+ // src/internal/logic/requestSubmit.ts
205
+ var requestSubmit = (element, submitter) => {
206
+ if (typeof Object.getPrototypeOf(element).requestSubmit === "function" && true) {
207
+ element.requestSubmit(submitter);
208
+ return;
209
+ }
210
+ if (submitter) {
211
+ validateSubmitter(element, submitter);
212
+ submitter.click();
213
+ return;
214
+ }
215
+ const dummySubmitter = document.createElement("input");
216
+ dummySubmitter.type = "submit";
217
+ dummySubmitter.hidden = true;
218
+ element.appendChild(dummySubmitter);
219
+ dummySubmitter.click();
220
+ element.removeChild(dummySubmitter);
221
+ };
222
+ function validateSubmitter(element, submitter) {
223
+ const isHtmlElement = submitter instanceof HTMLElement;
224
+ if (!isHtmlElement) {
225
+ raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
226
+ }
227
+ const hasSubmitType = "type" in submitter && submitter.type === "submit";
228
+ if (!hasSubmitType)
229
+ raise(TypeError, "The specified element is not a submit button");
230
+ const isForCorrectForm = "form" in submitter && submitter.form === element;
231
+ if (!isForCorrectForm)
232
+ raise(
233
+ DOMException,
234
+ "The specified element is not owned by this form element",
235
+ "NotFoundError"
236
+ );
237
+ }
238
+ function raise(errorConstructor, message, name) {
239
+ throw new errorConstructor(
240
+ "Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".",
241
+ name
242
+ );
243
+ }
244
+ if (void 0) {
245
+ const { it, expect } = void 0;
246
+ it("should validate the submitter", () => {
247
+ const form = document.createElement("form");
248
+ document.body.appendChild(form);
249
+ const submitter = document.createElement("input");
250
+ expect(() => validateSubmitter(null, null)).toThrow();
251
+ expect(() => validateSubmitter(form, null)).toThrow();
252
+ expect(() => validateSubmitter(form, submitter)).toThrow();
253
+ expect(
254
+ () => validateSubmitter(form, document.createElement("div"))
255
+ ).toThrow();
256
+ submitter.type = "submit";
257
+ expect(() => validateSubmitter(form, submitter)).toThrow();
258
+ form.appendChild(submitter);
259
+ expect(() => validateSubmitter(form, submitter)).not.toThrow();
260
+ form.removeChild(submitter);
261
+ expect(() => validateSubmitter(form, submitter)).toThrow();
262
+ document.body.appendChild(submitter);
263
+ form.id = "test-form";
264
+ submitter.setAttribute("form", "test-form");
265
+ expect(() => validateSubmitter(form, submitter)).not.toThrow();
266
+ const button = document.createElement("button");
267
+ button.type = "submit";
268
+ form.appendChild(button);
269
+ expect(() => validateSubmitter(form, button)).not.toThrow();
270
+ });
271
+ }
272
+
273
+ // src/internal/state/arrayUtil.ts
274
+ var import_tiny_invariant = __toESM(require("tiny-invariant"));
275
+ var getArray = (values, field) => {
276
+ const value = getPath(values, field);
277
+ if (value === void 0 || value === null) {
278
+ const newValue = [];
279
+ setPath(values, field, newValue);
280
+ return newValue;
281
+ }
282
+ (0, import_tiny_invariant.default)(
283
+ Array.isArray(value),
284
+ `FieldArray: defaultValue value for ${field} must be an array, null, or undefined`
285
+ );
286
+ return value;
287
+ };
288
+ var swap = (array, indexA, indexB) => {
289
+ const itemA = array[indexA];
290
+ const itemB = array[indexB];
291
+ const hasItemA = indexA in array;
292
+ const hasItemB = indexB in array;
293
+ if (hasItemA) {
294
+ array[indexB] = itemA;
295
+ } else {
296
+ delete array[indexB];
297
+ }
298
+ if (hasItemB) {
299
+ array[indexA] = itemB;
300
+ } else {
301
+ delete array[indexA];
302
+ }
303
+ };
304
+ function sparseSplice(array, start, deleteCount, item) {
305
+ if (array.length < start && item) {
306
+ array.length = start;
307
+ }
308
+ if (arguments.length === 4)
309
+ return array.splice(start, deleteCount, item);
310
+ return array.splice(start, deleteCount);
311
+ }
312
+ var move = (array, from2, to) => {
313
+ const [item] = sparseSplice(array, from2, 1);
314
+ sparseSplice(array, to, 0, item);
315
+ };
316
+ var insert = (array, index, value) => {
317
+ sparseSplice(array, index, 0, value);
318
+ };
319
+ var remove = (array, index) => {
320
+ sparseSplice(array, index, 1);
321
+ };
322
+ var replace = (array, index, value) => {
323
+ sparseSplice(array, index, 1, value);
324
+ };
325
+ var mutateAsArray = (field, obj, mutate) => {
326
+ const beforeKeys = /* @__PURE__ */ new Set();
327
+ const arr = [];
328
+ for (const [key, value] of Object.entries(obj)) {
329
+ if (key.startsWith(field) && key !== field) {
330
+ beforeKeys.add(key);
331
+ setPath(arr, key.substring(field.length), value);
332
+ }
333
+ }
334
+ mutate(arr);
335
+ for (const key of beforeKeys) {
336
+ delete obj[key];
337
+ }
338
+ const newKeys = getDeepArrayPaths(arr);
339
+ for (const key of newKeys) {
340
+ const val = getPath(arr, key);
341
+ if (val !== void 0) {
342
+ obj[`${field}${key}`] = val;
343
+ }
344
+ }
345
+ };
346
+ var getDeepArrayPaths = (obj, basePath = "") => {
347
+ if (Array.isArray(obj)) {
348
+ return obj.flatMap(
349
+ (item, index) => getDeepArrayPaths(item, `${basePath}[${index}]`)
350
+ );
351
+ }
352
+ if (typeof obj === "object") {
353
+ return Object.keys(obj).flatMap(
354
+ (key) => getDeepArrayPaths(obj[key], `${basePath}.${key}`)
355
+ );
356
+ }
357
+ return [basePath];
358
+ };
359
+ if (void 0) {
360
+ const { describe, expect, it } = void 0;
361
+ const countArrayItems = (arr) => {
362
+ let count = 0;
363
+ arr.forEach(() => count++);
364
+ return count;
365
+ };
366
+ describe("getArray", () => {
367
+ it("shoud get a deeply nested array that can be mutated to update the nested value", () => {
368
+ const values = {
369
+ d: [
370
+ { foo: "bar", baz: [true, false] },
371
+ { e: true, f: "hi" }
372
+ ]
373
+ };
374
+ const result = getArray(values, "d[0].baz");
375
+ const finalValues = {
376
+ d: [
377
+ { foo: "bar", baz: [true, false, true] },
378
+ { e: true, f: "hi" }
379
+ ]
380
+ };
381
+ expect(result).toEqual([true, false]);
382
+ result.push(true);
383
+ expect(values).toEqual(finalValues);
384
+ });
385
+ it("should return an empty array that can be mutated if result is null or undefined", () => {
386
+ const values = {};
387
+ const result = getArray(values, "a.foo[0].bar");
388
+ const finalValues = {
389
+ a: { foo: [{ bar: ["Bob ross"] }] }
390
+ };
391
+ expect(result).toEqual([]);
392
+ result.push("Bob ross");
393
+ expect(values).toEqual(finalValues);
394
+ });
395
+ it("should throw if the value is defined and not an array", () => {
396
+ const values = { foo: "foo" };
397
+ expect(() => getArray(values, "foo")).toThrow();
398
+ });
399
+ });
400
+ describe("swap", () => {
401
+ it("should swap two items", () => {
402
+ const array = [1, 2, 3];
403
+ swap(array, 0, 1);
404
+ expect(array).toEqual([2, 1, 3]);
405
+ });
406
+ it("should work for sparse arrays", () => {
407
+ const arr = [];
408
+ arr[0] = true;
409
+ swap(arr, 0, 2);
410
+ expect(countArrayItems(arr)).toEqual(1);
411
+ expect(0 in arr).toBe(false);
412
+ expect(2 in arr).toBe(true);
413
+ expect(arr[2]).toEqual(true);
414
+ });
415
+ });
416
+ describe("move", () => {
417
+ it("should move an item to a new index", () => {
418
+ const array = [1, 2, 3];
419
+ move(array, 0, 1);
420
+ expect(array).toEqual([2, 1, 3]);
421
+ });
422
+ it("should work with sparse arrays", () => {
423
+ const array = [1];
424
+ move(array, 0, 2);
425
+ expect(countArrayItems(array)).toEqual(1);
426
+ expect(array).toEqual([void 0, void 0, 1]);
427
+ });
428
+ });
429
+ describe("insert", () => {
430
+ it("should insert an item at a new index", () => {
431
+ const array = [1, 2, 3];
432
+ insert(array, 1, 4);
433
+ expect(array).toEqual([1, 4, 2, 3]);
434
+ });
435
+ it("should be able to insert falsey values", () => {
436
+ const array = [1, 2, 3];
437
+ insert(array, 1, null);
438
+ expect(array).toEqual([1, null, 2, 3]);
439
+ });
440
+ it("should handle sparse arrays", () => {
441
+ const array = [];
442
+ array[2] = true;
443
+ insert(array, 0, true);
444
+ expect(countArrayItems(array)).toEqual(2);
445
+ expect(array).toEqual([true, void 0, void 0, true]);
446
+ });
447
+ });
448
+ describe("remove", () => {
449
+ it("should remove an item at a given index", () => {
450
+ const array = [1, 2, 3];
451
+ remove(array, 1);
452
+ expect(array).toEqual([1, 3]);
453
+ });
454
+ it("should handle sparse arrays", () => {
455
+ const array = [];
456
+ array[2] = true;
457
+ remove(array, 0);
458
+ expect(countArrayItems(array)).toEqual(1);
459
+ expect(array).toEqual([void 0, true]);
460
+ });
461
+ });
462
+ describe("replace", () => {
463
+ it("should replace an item at a given index", () => {
464
+ const array = [1, 2, 3];
465
+ replace(array, 1, 4);
466
+ expect(array).toEqual([1, 4, 3]);
467
+ });
468
+ it("should handle sparse arrays", () => {
469
+ const array = [];
470
+ array[2] = true;
471
+ replace(array, 0, true);
472
+ expect(countArrayItems(array)).toEqual(2);
473
+ expect(array).toEqual([true, void 0, true]);
474
+ });
475
+ });
476
+ describe("mutateAsArray", () => {
477
+ it("should handle swap", () => {
478
+ const values = {
479
+ myField: "something",
480
+ "myField[0]": "foo",
481
+ "myField[2]": "bar",
482
+ otherField: "baz",
483
+ "otherField[0]": "something else"
484
+ };
485
+ mutateAsArray("myField", values, (arr) => {
486
+ swap(arr, 0, 2);
487
+ });
488
+ expect(values).toEqual({
489
+ myField: "something",
490
+ "myField[0]": "bar",
491
+ "myField[2]": "foo",
492
+ otherField: "baz",
493
+ "otherField[0]": "something else"
494
+ });
495
+ });
496
+ it("should swap sparse arrays", () => {
497
+ const values = {
498
+ myField: "something",
499
+ "myField[0]": "foo",
500
+ otherField: "baz",
501
+ "otherField[0]": "something else"
502
+ };
503
+ mutateAsArray("myField", values, (arr) => {
504
+ swap(arr, 0, 2);
505
+ });
506
+ expect(values).toEqual({
507
+ myField: "something",
508
+ "myField[2]": "foo",
509
+ otherField: "baz",
510
+ "otherField[0]": "something else"
511
+ });
512
+ });
513
+ it("should handle arrays with nested values", () => {
514
+ const values = {
515
+ myField: "something",
516
+ "myField[0].title": "foo",
517
+ "myField[0].note": "bar",
518
+ "myField[2].title": "other",
519
+ "myField[2].note": "other",
520
+ otherField: "baz",
521
+ "otherField[0]": "something else"
522
+ };
523
+ mutateAsArray("myField", values, (arr) => {
524
+ swap(arr, 0, 2);
525
+ });
526
+ expect(values).toEqual({
527
+ myField: "something",
528
+ "myField[0].title": "other",
529
+ "myField[0].note": "other",
530
+ "myField[2].title": "foo",
531
+ "myField[2].note": "bar",
532
+ otherField: "baz",
533
+ "otherField[0]": "something else"
534
+ });
535
+ });
536
+ it("should handle move", () => {
537
+ const values = {
538
+ myField: "something",
539
+ "myField[0]": "foo",
540
+ "myField[1]": "bar",
541
+ "myField[2]": "baz",
542
+ "otherField[0]": "something else"
543
+ };
544
+ mutateAsArray("myField", values, (arr) => {
545
+ move(arr, 0, 2);
546
+ });
547
+ expect(values).toEqual({
548
+ myField: "something",
549
+ "myField[0]": "bar",
550
+ "myField[1]": "baz",
551
+ "myField[2]": "foo",
552
+ "otherField[0]": "something else"
553
+ });
554
+ });
555
+ it("should not create keys for `undefined`", () => {
556
+ const values = {
557
+ "myField[0]": "foo"
558
+ };
559
+ mutateAsArray("myField", values, (arr) => {
560
+ arr.unshift(void 0);
561
+ });
562
+ expect(Object.keys(values)).toHaveLength(1);
563
+ expect(values).toEqual({
564
+ "myField[1]": "foo"
565
+ });
566
+ });
567
+ it("should handle remove", () => {
568
+ const values = {
569
+ myField: "something",
570
+ "myField[0]": "foo",
571
+ "myField[1]": "bar",
572
+ "myField[2]": "baz",
573
+ "otherField[0]": "something else"
574
+ };
575
+ mutateAsArray("myField", values, (arr) => {
576
+ remove(arr, 1);
577
+ });
578
+ expect(values).toEqual({
579
+ myField: "something",
580
+ "myField[0]": "foo",
581
+ "myField[1]": "baz",
582
+ "otherField[0]": "something else"
583
+ });
584
+ expect("myField[2]" in values).toBe(false);
585
+ });
586
+ });
587
+ describe("getDeepArrayPaths", () => {
588
+ it("should return all paths recursively", () => {
589
+ const obj = [
590
+ true,
591
+ true,
592
+ [true, true],
593
+ { foo: true, bar: { baz: true, test: [true] } }
594
+ ];
595
+ expect(getDeepArrayPaths(obj, "myField")).toEqual([
596
+ "myField[0]",
597
+ "myField[1]",
598
+ "myField[2][0]",
599
+ "myField[2][1]",
600
+ "myField[3].foo",
601
+ "myField[3].bar.baz",
602
+ "myField[3].bar.test[0]"
603
+ ]);
604
+ });
605
+ });
606
+ }
607
+
608
+ // src/internal/state/createFormStore.ts
609
+ var noOp = () => {
610
+ };
611
+ var defaultFormState = {
612
+ isHydrated: false,
613
+ isSubmitting: false,
614
+ hasBeenSubmitted: false,
615
+ touchedFields: {},
616
+ fieldErrors: {},
617
+ formElement: null,
618
+ isValid: () => true,
619
+ startSubmit: noOp,
620
+ endSubmit: noOp,
621
+ setTouched: noOp,
622
+ setFieldError: noOp,
623
+ setFieldErrors: noOp,
624
+ clearFieldError: noOp,
625
+ currentDefaultValues: {},
626
+ reset: () => noOp,
627
+ syncFormProps: noOp,
628
+ setFormElement: noOp,
629
+ validateField: async () => null,
630
+ validate: async () => {
631
+ throw new Error("Validate called before form was initialized.");
632
+ },
633
+ submit: async () => {
634
+ throw new Error("Submit called before form was initialized.");
635
+ },
636
+ resetFormElement: noOp,
637
+ getValues: () => new FormData(),
638
+ controlledFields: {
639
+ values: {},
640
+ refCounts: {},
641
+ valueUpdatePromises: {},
642
+ valueUpdateResolvers: {},
643
+ register: noOp,
644
+ unregister: noOp,
645
+ setValue: noOp,
646
+ getValue: noOp,
647
+ kickoffValueUpdate: noOp,
648
+ awaitValueUpdate: async () => {
649
+ throw new Error("AwaitValueUpdate called before form was initialized.");
650
+ },
651
+ array: {
652
+ push: noOp,
653
+ swap: noOp,
654
+ move: noOp,
655
+ insert: noOp,
656
+ unshift: noOp,
657
+ remove: noOp,
658
+ pop: noOp,
659
+ replace: noOp
660
+ }
661
+ }
662
+ };
663
+ var createFormState = (set, get) => ({
664
+ isHydrated: false,
665
+ isSubmitting: false,
666
+ hasBeenSubmitted: false,
667
+ touchedFields: {},
668
+ fieldErrors: {},
669
+ formElement: null,
670
+ currentDefaultValues: {},
671
+ isValid: () => Object.keys(get().fieldErrors).length === 0,
672
+ startSubmit: () => set((state) => {
673
+ state.isSubmitting = true;
674
+ state.hasBeenSubmitted = true;
675
+ }),
676
+ endSubmit: () => set((state) => {
677
+ state.isSubmitting = false;
678
+ }),
679
+ setTouched: (fieldName, touched) => set((state) => {
680
+ state.touchedFields[fieldName] = touched;
681
+ }),
682
+ setFieldError: (fieldName, error) => set((state) => {
683
+ state.fieldErrors[fieldName] = error;
684
+ }),
685
+ setFieldErrors: (errors) => set((state) => {
686
+ state.fieldErrors = errors;
687
+ }),
688
+ clearFieldError: (fieldName) => set((state) => {
689
+ delete state.fieldErrors[fieldName];
690
+ }),
691
+ reset: () => set((state) => {
692
+ var _a, _b;
693
+ state.fieldErrors = {};
694
+ state.touchedFields = {};
695
+ state.hasBeenSubmitted = false;
696
+ const nextDefaults = (_b = (_a = state.formProps) == null ? void 0 : _a.defaultValues) != null ? _b : {};
697
+ state.controlledFields.values = nextDefaults;
698
+ state.currentDefaultValues = nextDefaults;
699
+ }),
700
+ syncFormProps: (props) => set((state) => {
701
+ if (!state.isHydrated) {
702
+ state.controlledFields.values = props.defaultValues;
703
+ state.currentDefaultValues = props.defaultValues;
704
+ }
705
+ state.formProps = props;
706
+ state.isHydrated = true;
707
+ }),
708
+ setFormElement: (formElement) => {
709
+ if (get().formElement === formElement)
710
+ return;
711
+ set((state) => {
712
+ state.formElement = formElement;
713
+ });
714
+ },
715
+ validateField: async (field) => {
716
+ var _a, _b, _c;
717
+ const formElement = get().formElement;
718
+ (0, import_tiny_invariant2.default)(
719
+ formElement,
720
+ "Cannot find reference to form. This is probably a bug in remix-validated-form."
721
+ );
722
+ const validator = (_a = get().formProps) == null ? void 0 : _a.validator;
723
+ (0, import_tiny_invariant2.default)(
724
+ validator,
725
+ "Cannot validator. This is probably a bug in remix-validated-form."
726
+ );
727
+ await ((_c = (_b = get().controlledFields).awaitValueUpdate) == null ? void 0 : _c.call(_b, field));
728
+ const { error } = await validator.validateField(
729
+ new FormData(formElement),
730
+ field
731
+ );
732
+ if (error) {
733
+ get().setFieldError(field, error);
734
+ return error;
735
+ } else {
736
+ get().clearFieldError(field);
737
+ return null;
738
+ }
739
+ },
740
+ validate: async () => {
741
+ var _a;
742
+ const formElement = get().formElement;
743
+ (0, import_tiny_invariant2.default)(
744
+ formElement,
745
+ "Cannot find reference to form. This is probably a bug in remix-validated-form."
746
+ );
747
+ const validator = (_a = get().formProps) == null ? void 0 : _a.validator;
748
+ (0, import_tiny_invariant2.default)(
749
+ validator,
750
+ "Cannot validator. This is probably a bug in remix-validated-form."
751
+ );
752
+ const result = await validator.validate(new FormData(formElement));
753
+ if (result.error)
754
+ get().setFieldErrors(result.error.fieldErrors);
755
+ return result;
756
+ },
757
+ submit: () => {
758
+ const formElement = get().formElement;
759
+ (0, import_tiny_invariant2.default)(
760
+ formElement,
761
+ "Cannot find reference to form. This is probably a bug in remix-validated-form."
762
+ );
763
+ requestSubmit(formElement);
764
+ },
765
+ getValues: () => {
766
+ var _a;
767
+ return new FormData((_a = get().formElement) != null ? _a : void 0);
768
+ },
769
+ resetFormElement: () => {
770
+ var _a;
771
+ return (_a = get().formElement) == null ? void 0 : _a.reset();
772
+ },
773
+ controlledFields: {
774
+ values: {},
775
+ refCounts: {},
776
+ valueUpdatePromises: {},
777
+ valueUpdateResolvers: {},
778
+ register: (fieldName) => {
779
+ set((state) => {
780
+ var _a;
781
+ const current = (_a = state.controlledFields.refCounts[fieldName]) != null ? _a : 0;
782
+ state.controlledFields.refCounts[fieldName] = current + 1;
783
+ });
784
+ },
785
+ unregister: (fieldName) => {
786
+ if (get() === null || get() === void 0)
787
+ return;
788
+ set((state) => {
789
+ var _a, _b, _c;
790
+ const current = (_a = state.controlledFields.refCounts[fieldName]) != null ? _a : 0;
791
+ if (current > 1) {
792
+ state.controlledFields.refCounts[fieldName] = current - 1;
793
+ return;
794
+ }
795
+ const isNested = Object.keys(state.controlledFields.refCounts).some(
796
+ (key) => fieldName.startsWith(key) && key !== fieldName
797
+ );
798
+ if (!isNested) {
799
+ setPath(
800
+ state.controlledFields.values,
801
+ fieldName,
802
+ getPath((_b = state.formProps) == null ? void 0 : _b.defaultValues, fieldName)
803
+ );
804
+ setPath(
805
+ state.currentDefaultValues,
806
+ fieldName,
807
+ getPath((_c = state.formProps) == null ? void 0 : _c.defaultValues, fieldName)
808
+ );
809
+ }
810
+ delete state.controlledFields.refCounts[fieldName];
811
+ });
812
+ },
813
+ getValue: (fieldName) => getPath(get().controlledFields.values, fieldName),
814
+ setValue: (fieldName, value) => {
815
+ set((state) => {
816
+ setPath(state.controlledFields.values, fieldName, value);
817
+ });
818
+ get().controlledFields.kickoffValueUpdate(fieldName);
819
+ },
820
+ kickoffValueUpdate: (fieldName) => {
821
+ const clear = () => set((state) => {
822
+ delete state.controlledFields.valueUpdateResolvers[fieldName];
823
+ delete state.controlledFields.valueUpdatePromises[fieldName];
824
+ });
825
+ set((state) => {
826
+ const promise = new Promise((resolve) => {
827
+ state.controlledFields.valueUpdateResolvers[fieldName] = resolve;
828
+ }).then(clear);
829
+ state.controlledFields.valueUpdatePromises[fieldName] = promise;
830
+ });
831
+ },
832
+ awaitValueUpdate: async (fieldName) => {
833
+ await get().controlledFields.valueUpdatePromises[fieldName];
834
+ },
835
+ array: {
836
+ push: (fieldName, item) => {
837
+ set((state) => {
838
+ getArray(state.controlledFields.values, fieldName).push(item);
839
+ getArray(state.currentDefaultValues, fieldName).push(item);
840
+ });
841
+ get().controlledFields.kickoffValueUpdate(fieldName);
842
+ },
843
+ swap: (fieldName, indexA, indexB) => {
844
+ set((state) => {
845
+ swap(
846
+ getArray(state.controlledFields.values, fieldName),
847
+ indexA,
848
+ indexB
849
+ );
850
+ swap(
851
+ getArray(state.currentDefaultValues, fieldName),
852
+ indexA,
853
+ indexB
854
+ );
855
+ mutateAsArray(
856
+ fieldName,
857
+ state.touchedFields,
858
+ (array) => swap(array, indexA, indexB)
859
+ );
860
+ mutateAsArray(
861
+ fieldName,
862
+ state.fieldErrors,
863
+ (array) => swap(array, indexA, indexB)
864
+ );
865
+ });
866
+ get().controlledFields.kickoffValueUpdate(fieldName);
867
+ },
868
+ move: (fieldName, from2, to) => {
869
+ set((state) => {
870
+ move(
871
+ getArray(state.controlledFields.values, fieldName),
872
+ from2,
873
+ to
874
+ );
875
+ move(
876
+ getArray(state.currentDefaultValues, fieldName),
877
+ from2,
878
+ to
879
+ );
880
+ mutateAsArray(
881
+ fieldName,
882
+ state.touchedFields,
883
+ (array) => move(array, from2, to)
884
+ );
885
+ mutateAsArray(
886
+ fieldName,
887
+ state.fieldErrors,
888
+ (array) => move(array, from2, to)
889
+ );
890
+ });
891
+ get().controlledFields.kickoffValueUpdate(fieldName);
892
+ },
893
+ insert: (fieldName, index, item) => {
894
+ set((state) => {
895
+ insert(
896
+ getArray(state.controlledFields.values, fieldName),
897
+ index,
898
+ item
899
+ );
900
+ insert(
901
+ getArray(state.currentDefaultValues, fieldName),
902
+ index,
903
+ item
904
+ );
905
+ mutateAsArray(
906
+ fieldName,
907
+ state.touchedFields,
908
+ (array) => insert(array, index, false)
909
+ );
910
+ mutateAsArray(
911
+ fieldName,
912
+ state.fieldErrors,
913
+ (array) => insert(array, index, void 0)
914
+ );
915
+ });
916
+ get().controlledFields.kickoffValueUpdate(fieldName);
917
+ },
918
+ remove: (fieldName, index) => {
919
+ set((state) => {
920
+ remove(
921
+ getArray(state.controlledFields.values, fieldName),
922
+ index
923
+ );
924
+ remove(
925
+ getArray(state.currentDefaultValues, fieldName),
926
+ index
927
+ );
928
+ mutateAsArray(
929
+ fieldName,
930
+ state.touchedFields,
931
+ (array) => remove(array, index)
932
+ );
933
+ mutateAsArray(
934
+ fieldName,
935
+ state.fieldErrors,
936
+ (array) => remove(array, index)
937
+ );
938
+ });
939
+ get().controlledFields.kickoffValueUpdate(fieldName);
940
+ },
941
+ pop: (fieldName) => {
942
+ set((state) => {
943
+ getArray(state.controlledFields.values, fieldName).pop();
944
+ getArray(state.currentDefaultValues, fieldName).pop();
945
+ mutateAsArray(
946
+ fieldName,
947
+ state.touchedFields,
948
+ (array) => array.pop()
949
+ );
950
+ mutateAsArray(
951
+ fieldName,
952
+ state.fieldErrors,
953
+ (array) => array.pop()
954
+ );
955
+ });
956
+ get().controlledFields.kickoffValueUpdate(fieldName);
957
+ },
958
+ unshift: (fieldName, value) => {
959
+ set((state) => {
960
+ getArray(state.controlledFields.values, fieldName).unshift(value);
961
+ getArray(state.currentDefaultValues, fieldName).unshift(value);
962
+ mutateAsArray(
963
+ fieldName,
964
+ state.touchedFields,
965
+ (array) => array.unshift(false)
966
+ );
967
+ mutateAsArray(
968
+ fieldName,
969
+ state.fieldErrors,
970
+ (array) => array.unshift(void 0)
971
+ );
972
+ });
973
+ },
974
+ replace: (fieldName, index, item) => {
975
+ set((state) => {
976
+ replace(
977
+ getArray(state.controlledFields.values, fieldName),
978
+ index,
979
+ item
980
+ );
981
+ replace(
982
+ getArray(state.currentDefaultValues, fieldName),
983
+ index,
984
+ item
985
+ );
986
+ mutateAsArray(
987
+ fieldName,
988
+ state.touchedFields,
989
+ (array) => replace(array, index, item)
990
+ );
991
+ mutateAsArray(
992
+ fieldName,
993
+ state.fieldErrors,
994
+ (array) => replace(array, index, item)
995
+ );
996
+ });
997
+ get().controlledFields.kickoffValueUpdate(fieldName);
998
+ }
999
+ }
1000
+ }
1001
+ });
1002
+ var useRootFormStore = (0, import_zustand.default)()(
1003
+ (0, import_immer.immer)((set, get) => ({
1004
+ forms: {},
1005
+ form: (formId) => {
1006
+ var _a;
1007
+ return (_a = get().forms[formId]) != null ? _a : defaultFormState;
1008
+ },
1009
+ cleanupForm: (formId) => {
1010
+ set((state) => {
1011
+ delete state.forms[formId];
1012
+ });
1013
+ },
1014
+ registerForm: (formId) => {
1015
+ if (get().forms[formId])
1016
+ return;
1017
+ set((state) => {
1018
+ state.forms[formId] = createFormState(
1019
+ (setter) => set((state2) => setter(state2.forms[formId])),
1020
+ () => get().forms[formId]
1021
+ );
1022
+ });
1023
+ }
1024
+ }))
1025
+ );
1026
+
1027
+ // src/internal/state/storeHooks.ts
1028
+ var useFormStore = (formId, selector) => {
1029
+ return useRootFormStore((state) => selector(state.form(formId)));
1030
+ };
1031
+
1032
+ // src/internal/hooks.ts
1033
+ var useInternalFormContext = (formId, hookName) => {
1034
+ const formContext = (0, import_react3.useContext)(InternalFormContext);
1035
+ if (formId)
1036
+ return { formId };
1037
+ if (formContext)
1038
+ return formContext;
1039
+ throw new Error(
1040
+ `Unable to determine form for ${hookName}. Please use it inside a ValidatedForm or pass a 'formId'.`
1041
+ );
1042
+ };
1043
+ function useErrorResponseForForm({
1044
+ fetcher,
1045
+ subaction,
1046
+ formId
1047
+ }) {
1048
+ var _a;
1049
+ const actionData = (0, import_react2.useActionData)();
1050
+ if (fetcher) {
1051
+ if ((_a = fetcher.data) == null ? void 0 : _a.fieldErrors)
1052
+ return fetcher.data;
1053
+ return null;
1054
+ }
1055
+ if (!(actionData == null ? void 0 : actionData.fieldErrors))
1056
+ return null;
1057
+ if (typeof formId === "string" && actionData.formId)
1058
+ return actionData.formId === formId ? actionData : null;
1059
+ if (!subaction && !actionData.subaction || actionData.subaction === subaction)
1060
+ return actionData;
1061
+ return null;
1062
+ }
1063
+ var useFieldErrorsForForm = (context) => {
1064
+ const response = useErrorResponseForForm(context);
1065
+ const hydrated = useFormStore(context.formId, (state) => state.isHydrated);
1066
+ return hydratable.from(response == null ? void 0 : response.fieldErrors, hydrated);
1067
+ };
1068
+ var useDefaultValuesFromLoader = ({
1069
+ formId
1070
+ }) => {
1071
+ const matches = (0, import_react2.useMatches)();
1072
+ if (typeof formId === "string") {
1073
+ const dataKey = formDefaultValuesKey(formId);
1074
+ const match = matches.reverse().find((match2) => match2.data && dataKey in match2.data);
1075
+ return match == null ? void 0 : match.data[dataKey];
1076
+ }
1077
+ return null;
1078
+ };
1079
+ var useDefaultValuesForForm = (context) => {
1080
+ const { formId, defaultValuesProp } = context;
1081
+ const hydrated = useFormStore(formId, (state) => state.isHydrated);
1082
+ const errorResponse = useErrorResponseForForm(context);
1083
+ const defaultValuesFromLoader = useDefaultValuesFromLoader(context);
1084
+ if (hydrated)
1085
+ return hydratable.hydratedData();
1086
+ if (errorResponse == null ? void 0 : errorResponse.repopulateFields) {
1087
+ (0, import_tiny_invariant3.default)(
1088
+ typeof errorResponse.repopulateFields === "object",
1089
+ "repopulateFields returned something other than an object"
1090
+ );
1091
+ return hydratable.serverData(errorResponse.repopulateFields);
1092
+ }
1093
+ if (defaultValuesProp)
1094
+ return hydratable.serverData(defaultValuesProp);
1095
+ return hydratable.serverData(defaultValuesFromLoader);
1096
+ };
1097
+ var useHasActiveFormSubmit = ({
1098
+ fetcher
1099
+ }) => {
1100
+ const transition = (0, import_react2.useTransition)();
1101
+ const hasActiveSubmission = fetcher ? fetcher.state === "submitting" : !!transition.submission;
1102
+ return hasActiveSubmission;
1103
+ };
1104
+ var useFieldTouched = (field, { formId }) => {
1105
+ const touched = useFormStore(formId, (state) => state.touchedFields[field]);
1106
+ const setFieldTouched = useFormStore(formId, (state) => state.setTouched);
1107
+ const setTouched = (0, import_react3.useCallback)(
1108
+ (touched2) => setFieldTouched(field, touched2),
1109
+ [field, setFieldTouched]
1110
+ );
1111
+ return [touched, setTouched];
1112
+ };
1113
+ var useFieldError = (name, context) => {
1114
+ const fieldErrors = useFieldErrorsForForm(context);
1115
+ const state = useFormStore(
1116
+ context.formId,
1117
+ (state2) => state2.fieldErrors[name]
1118
+ );
1119
+ return fieldErrors.map((fieldErrors2) => fieldErrors2 == null ? void 0 : fieldErrors2[name]).hydrateTo(state);
1120
+ };
1121
+ var useClearError = (context) => {
1122
+ const { formId } = context;
1123
+ return useFormStore(formId, (state) => state.clearFieldError);
1124
+ };
1125
+ var useCurrentDefaultValueForField = (formId, field) => useFormStore(formId, (state) => getPath(state.currentDefaultValues, field));
1126
+ var useFieldDefaultValue = (name, context) => {
1127
+ const defaultValues = useDefaultValuesForForm(context);
1128
+ const state = useCurrentDefaultValueForField(context.formId, name);
1129
+ return defaultValues.map((val) => getPath(val, name)).hydrateTo(state);
1130
+ };
1131
+ var useInternalIsSubmitting = (formId) => useFormStore(formId, (state) => state.isSubmitting);
1132
+ var useInternalIsValid = (formId) => useFormStore(formId, (state) => state.isValid());
1133
+ var useInternalHasBeenSubmitted = (formId) => useFormStore(formId, (state) => state.hasBeenSubmitted);
1134
+ var useValidateField = (formId) => useFormStore(formId, (state) => state.validateField);
1135
+ var useValidate = (formId) => useFormStore(formId, (state) => state.validate);
1136
+ var noOpReceiver = () => () => {
1137
+ };
1138
+ var useRegisterReceiveFocus = (formId) => useFormStore(
1139
+ formId,
1140
+ (state) => {
1141
+ var _a, _b;
1142
+ return (_b = (_a = state.formProps) == null ? void 0 : _a.registerReceiveFocus) != null ? _b : noOpReceiver;
1143
+ }
1144
+ );
1145
+ var defaultDefaultValues = {};
1146
+ var useSyncedDefaultValues = (formId) => useFormStore(
1147
+ formId,
1148
+ (state) => {
1149
+ var _a, _b;
1150
+ return (_b = (_a = state.formProps) == null ? void 0 : _a.defaultValues) != null ? _b : defaultDefaultValues;
1151
+ }
1152
+ );
1153
+ var useSetTouched = ({ formId }) => useFormStore(formId, (state) => state.setTouched);
1154
+ var useTouchedFields = (formId) => useFormStore(formId, (state) => state.touchedFields);
1155
+ var useFieldErrors = (formId) => useFormStore(formId, (state) => state.fieldErrors);
1156
+ var useSetFieldErrors = (formId) => useFormStore(formId, (state) => state.setFieldErrors);
1157
+ var useResetFormElement = (formId) => useFormStore(formId, (state) => state.resetFormElement);
1158
+ var useSubmitForm = (formId) => useFormStore(formId, (state) => state.submit);
1159
+ var useFormActionProp = (formId) => useFormStore(formId, (state) => {
1160
+ var _a;
1161
+ return (_a = state.formProps) == null ? void 0 : _a.action;
1162
+ });
1163
+ var useFormSubactionProp = (formId) => useFormStore(formId, (state) => {
1164
+ var _a;
1165
+ return (_a = state.formProps) == null ? void 0 : _a.subaction;
1166
+ });
1167
+ var useFormValues = (formId) => useFormStore(formId, (state) => state.getValues);
1168
+
1169
+ // src/internal/state/controlledFields.ts
1170
+ var import_react4 = require("react");
1171
+ var useControlledFieldValue = (context, field) => {
1172
+ const value = useFormStore(
1173
+ context.formId,
1174
+ (state) => state.controlledFields.getValue(field)
1175
+ );
1176
+ const isFormHydrated = useFormStore(
1177
+ context.formId,
1178
+ (state) => state.isHydrated
1179
+ );
1180
+ const defaultValue = useFieldDefaultValue(field, context);
1181
+ return isFormHydrated ? value : defaultValue;
1182
+ };
1183
+ var useRegisterControlledField = (context, field) => {
1184
+ const resolveUpdate = useFormStore(
1185
+ context.formId,
1186
+ (state) => state.controlledFields.valueUpdateResolvers[field]
1187
+ );
1188
+ (0, import_react4.useEffect)(() => {
1189
+ resolveUpdate == null ? void 0 : resolveUpdate();
1190
+ }, [resolveUpdate]);
1191
+ const register = useFormStore(
1192
+ context.formId,
1193
+ (state) => state.controlledFields.register
1194
+ );
1195
+ const unregister = useFormStore(
1196
+ context.formId,
1197
+ (state) => state.controlledFields.unregister
1198
+ );
1199
+ (0, import_react4.useEffect)(() => {
1200
+ register(field);
1201
+ return () => unregister(field);
1202
+ }, [context.formId, field, register, unregister]);
1203
+ };
1204
+ var useControllableValue = (context, field) => {
1205
+ useRegisterControlledField(context, field);
1206
+ const setControlledFieldValue = useFormStore(
1207
+ context.formId,
1208
+ (state) => state.controlledFields.setValue
1209
+ );
1210
+ const setValue = (0, import_react4.useCallback)(
1211
+ (value2) => setControlledFieldValue(field, value2),
1212
+ [field, setControlledFieldValue]
1213
+ );
1214
+ const value = useControlledFieldValue(context, field);
1215
+ return [value, setValue];
1216
+ };
1217
+ var useUpdateControllableValue = (formId) => {
1218
+ const setValue = useFormStore(
1219
+ formId,
1220
+ (state) => state.controlledFields.setValue
1221
+ );
1222
+ return (0, import_react4.useCallback)(
1223
+ (field, value) => setValue(field, value),
1224
+ [setValue]
1225
+ );
1226
+ };
1227
+
1228
+ // src/hooks.ts
1229
+ var useIsSubmitting = (formId) => {
1230
+ const formContext = useInternalFormContext(formId, "useIsSubmitting");
1231
+ return useInternalIsSubmitting(formContext.formId);
1232
+ };
1233
+ var useIsValid = (formId) => {
1234
+ const formContext = useInternalFormContext(formId, "useIsValid");
1235
+ return useInternalIsValid(formContext.formId);
1236
+ };
1237
+ var useField = (name, options) => {
1238
+ const { formId: providedFormId, handleReceiveFocus } = options != null ? options : {};
1239
+ const formContext = useInternalFormContext(providedFormId, "useField");
1240
+ const defaultValue = useFieldDefaultValue(name, formContext);
1241
+ const [touched, setTouched] = useFieldTouched(name, formContext);
1242
+ const error = useFieldError(name, formContext);
1243
+ const clearError = useClearError(formContext);
1244
+ const hasBeenSubmitted = useInternalHasBeenSubmitted(formContext.formId);
1245
+ const validateField = useValidateField(formContext.formId);
1246
+ const registerReceiveFocus = useRegisterReceiveFocus(formContext.formId);
1247
+ (0, import_react5.useEffect)(() => {
1248
+ if (handleReceiveFocus)
1249
+ return registerReceiveFocus(name, handleReceiveFocus);
1250
+ }, [handleReceiveFocus, name, registerReceiveFocus]);
1251
+ const field = (0, import_react5.useMemo)(() => {
1252
+ const helpers = {
1253
+ error,
1254
+ clearError: () => clearError(name),
1255
+ validate: () => {
1256
+ validateField(name);
1257
+ },
1258
+ defaultValue,
1259
+ touched,
1260
+ setTouched
1261
+ };
1262
+ const getInputProps = createGetInputProps({
1263
+ ...helpers,
1264
+ name,
1265
+ hasBeenSubmitted,
1266
+ validationBehavior: options == null ? void 0 : options.validationBehavior
1267
+ });
1268
+ return {
1269
+ ...helpers,
1270
+ getInputProps
1271
+ };
1272
+ }, [
1273
+ error,
1274
+ clearError,
1275
+ defaultValue,
1276
+ touched,
1277
+ setTouched,
1278
+ name,
1279
+ hasBeenSubmitted,
1280
+ options == null ? void 0 : options.validationBehavior,
1281
+ validateField
1282
+ ]);
1283
+ return field;
1284
+ };
1285
+ var useControlField = (name, formId) => {
1286
+ const context = useInternalFormContext(formId, "useControlField");
1287
+ const [value, setValue] = useControllableValue(context, name);
1288
+ return [value, setValue];
1289
+ };
1290
+ var useUpdateControlledField = (formId) => {
1291
+ const context = useInternalFormContext(formId, "useControlField");
1292
+ return useUpdateControllableValue(context.formId);
1293
+ };
1294
+
1295
+ // src/server.ts
1296
+ var import_server_runtime = require("@remix-run/server-runtime");
1297
+ function validationError(error, repopulateFields, init) {
1298
+ return (0, import_server_runtime.json)(
1299
+ {
1300
+ fieldErrors: error.fieldErrors,
1301
+ subaction: error.subaction,
1302
+ repopulateFields,
1303
+ formId: error.formId
1304
+ },
1305
+ { status: 422, ...init }
1306
+ );
1307
+ }
1308
+ var setFormDefaults = (formId, defaultValues) => ({
1309
+ [formDefaultValuesKey(formId)]: defaultValues
1310
+ });
1311
+
1312
+ // src/ValidatedForm.tsx
1313
+ var import_react9 = require("@remix-run/react");
1314
+ var import_react10 = require("react");
1315
+ var R4 = __toESM(require("remeda"));
1316
+
1317
+ // src/internal/MultiValueMap.ts
1318
+ var import_react6 = require("react");
1319
+ var MultiValueMap = class {
1320
+ constructor() {
1321
+ this.dict = /* @__PURE__ */ new Map();
1322
+ this.add = (key, value) => {
1323
+ if (this.dict.has(key)) {
1324
+ this.dict.get(key).push(value);
1325
+ } else {
1326
+ this.dict.set(key, [value]);
1327
+ }
1328
+ };
1329
+ this.delete = (key) => {
1330
+ this.dict.delete(key);
1331
+ };
1332
+ this.remove = (key, value) => {
1333
+ if (!this.dict.has(key))
1334
+ return;
1335
+ const array = this.dict.get(key);
1336
+ const index = array.indexOf(value);
1337
+ if (index !== -1)
1338
+ array.splice(index, 1);
1339
+ if (array.length === 0)
1340
+ this.dict.delete(key);
1341
+ };
1342
+ this.getAll = (key) => {
1343
+ var _a;
1344
+ return (_a = this.dict.get(key)) != null ? _a : [];
1345
+ };
1346
+ this.entries = () => this.dict.entries();
1347
+ this.values = () => this.dict.values();
1348
+ this.has = (key) => this.dict.has(key);
1349
+ }
1350
+ };
1351
+ var useMultiValueMap = () => {
1352
+ const ref = (0, import_react6.useRef)(null);
1353
+ return (0, import_react6.useCallback)(() => {
1354
+ if (ref.current)
1355
+ return ref.current;
1356
+ ref.current = new MultiValueMap();
1357
+ return ref.current;
1358
+ }, []);
1359
+ };
1360
+
1361
+ // src/internal/submissionCallbacks.ts
1362
+ var import_react7 = require("react");
1363
+ function useSubmitComplete(isSubmitting, callback) {
1364
+ const isPending = (0, import_react7.useRef)(false);
1365
+ (0, import_react7.useEffect)(() => {
1366
+ if (isSubmitting) {
1367
+ isPending.current = true;
1368
+ }
1369
+ if (!isSubmitting && isPending.current) {
1370
+ isPending.current = false;
1371
+ callback();
1372
+ }
1373
+ });
1374
+ }
1375
+
1376
+ // src/internal/util.ts
1377
+ var import_react8 = require("react");
1378
+ var R3 = __toESM(require("remeda"));
1379
+ var mergeRefs = (refs) => {
1380
+ return (value) => {
1381
+ refs.filter(Boolean).forEach((ref) => {
1382
+ if (typeof ref === "function") {
1383
+ ref(value);
1384
+ } else if (ref != null) {
1385
+ ref.current = value;
1386
+ }
1387
+ });
1388
+ };
1389
+ };
1390
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react8.useLayoutEffect : import_react8.useEffect;
1391
+ var useDeepEqualsMemo = (item) => {
1392
+ const ref = (0, import_react8.useRef)(item);
1393
+ const areEqual = ref.current === item || R3.equals(ref.current, item);
1394
+ (0, import_react8.useEffect)(() => {
1395
+ if (!areEqual) {
1396
+ ref.current = item;
1397
+ }
1398
+ });
1399
+ return areEqual ? ref.current : item;
1400
+ };
1401
+
1402
+ // src/ValidatedForm.tsx
1403
+ var import_jsx_runtime = require("react/jsx-runtime");
1404
+ var getDataFromForm = (el) => new FormData(el);
1405
+ function nonNull(value) {
1406
+ return value !== null;
1407
+ }
1408
+ var focusFirstInvalidInput = (fieldErrors, customFocusHandlers, formElement) => {
1409
+ var _a;
1410
+ const namesInOrder = [...formElement.elements].map((el) => {
1411
+ const input = el instanceof RadioNodeList ? el[0] : el;
1412
+ if (input instanceof HTMLElement && "name" in input)
1413
+ return input.name;
1414
+ return null;
1415
+ }).filter(nonNull).filter((name) => name in fieldErrors);
1416
+ const uniqueNamesInOrder = R4.uniq(namesInOrder);
1417
+ for (const fieldName of uniqueNamesInOrder) {
1418
+ if (customFocusHandlers.has(fieldName)) {
1419
+ customFocusHandlers.getAll(fieldName).forEach((handler) => {
1420
+ handler();
1421
+ });
1422
+ break;
1423
+ }
1424
+ const elem = formElement.elements.namedItem(fieldName);
1425
+ if (!elem)
1426
+ continue;
1427
+ if (elem instanceof RadioNodeList) {
1428
+ const selectedRadio = (_a = [...elem].filter(
1429
+ (item) => item instanceof HTMLInputElement
1430
+ ).find((item) => item.value === elem.value)) != null ? _a : elem[0];
1431
+ if (selectedRadio && selectedRadio instanceof HTMLInputElement) {
1432
+ selectedRadio.focus();
1433
+ break;
1434
+ }
1435
+ }
1436
+ if (elem instanceof HTMLElement) {
1437
+ if (elem instanceof HTMLInputElement && elem.type === "hidden") {
1438
+ continue;
1439
+ }
1440
+ elem.focus();
1441
+ break;
1442
+ }
1443
+ }
1444
+ };
1445
+ var useFormId = (providedId) => {
1446
+ const [symbolId] = (0, import_react10.useState)(() => Symbol("remix-validated-form-id"));
1447
+ return providedId != null ? providedId : symbolId;
1448
+ };
1449
+ var FormResetter = ({
1450
+ resetAfterSubmit,
1451
+ formRef
1452
+ }) => {
1453
+ const isSubmitting = useIsSubmitting();
1454
+ const isValid = useIsValid();
1455
+ useSubmitComplete(isSubmitting, () => {
1456
+ var _a;
1457
+ if (isValid && resetAfterSubmit) {
1458
+ (_a = formRef.current) == null ? void 0 : _a.reset();
1459
+ }
1460
+ });
1461
+ return null;
1462
+ };
1463
+ function formEventProxy(event) {
1464
+ let defaultPrevented = false;
1465
+ return new Proxy(event, {
1466
+ get: (target, prop) => {
1467
+ if (prop === "preventDefault") {
1468
+ return () => {
1469
+ defaultPrevented = true;
1470
+ };
1471
+ }
1472
+ if (prop === "defaultPrevented") {
1473
+ return defaultPrevented;
1474
+ }
1475
+ return target[prop];
1476
+ }
1477
+ });
1478
+ }
1479
+ function ValidatedForm({
1480
+ validator,
1481
+ onSubmit,
1482
+ children,
1483
+ fetcher,
1484
+ action,
1485
+ defaultValues: unMemoizedDefaults,
1486
+ formRef: formRefProp,
1487
+ onReset,
1488
+ subaction,
1489
+ resetAfterSubmit = false,
1490
+ disableFocusOnError,
1491
+ method,
1492
+ replace: replace2,
1493
+ id,
1494
+ ...rest
1495
+ }) {
1496
+ var _a;
1497
+ const formId = useFormId(id);
1498
+ const providedDefaultValues = useDeepEqualsMemo(unMemoizedDefaults);
1499
+ const contextValue = (0, import_react10.useMemo)(
1500
+ () => ({
1501
+ formId,
1502
+ action,
1503
+ subaction,
1504
+ defaultValuesProp: providedDefaultValues,
1505
+ fetcher
1506
+ }),
1507
+ [action, fetcher, formId, providedDefaultValues, subaction]
1508
+ );
1509
+ const backendError = useErrorResponseForForm(contextValue);
1510
+ const backendDefaultValues = useDefaultValuesFromLoader(contextValue);
1511
+ const hasActiveSubmission = useHasActiveFormSubmit(contextValue);
1512
+ const formRef = (0, import_react10.useRef)(null);
1513
+ const Form = (_a = fetcher == null ? void 0 : fetcher.Form) != null ? _a : import_react9.Form;
1514
+ const submit = (0, import_react9.useSubmit)();
1515
+ const setFieldErrors = useSetFieldErrors(formId);
1516
+ const setFieldError = useFormStore(formId, (state) => state.setFieldError);
1517
+ const reset = useFormStore(formId, (state) => state.reset);
1518
+ const startSubmit = useFormStore(formId, (state) => state.startSubmit);
1519
+ const endSubmit = useFormStore(formId, (state) => state.endSubmit);
1520
+ const syncFormProps = useFormStore(formId, (state) => state.syncFormProps);
1521
+ const setFormElementInState = useFormStore(
1522
+ formId,
1523
+ (state) => state.setFormElement
1524
+ );
1525
+ const cleanupForm = useRootFormStore((state) => state.cleanupForm);
1526
+ const registerForm = useRootFormStore((state) => state.registerForm);
1527
+ const customFocusHandlers = useMultiValueMap();
1528
+ const registerReceiveFocus = (0, import_react10.useCallback)(
1529
+ (fieldName, handler) => {
1530
+ customFocusHandlers().add(fieldName, handler);
1531
+ return () => {
1532
+ customFocusHandlers().remove(fieldName, handler);
1533
+ };
1534
+ },
1535
+ [customFocusHandlers]
1536
+ );
1537
+ useIsomorphicLayoutEffect(() => {
1538
+ registerForm(formId);
1539
+ return () => cleanupForm(formId);
1540
+ }, [cleanupForm, formId, registerForm]);
1541
+ useIsomorphicLayoutEffect(() => {
1542
+ var _a2;
1543
+ syncFormProps({
1544
+ action,
1545
+ defaultValues: (_a2 = providedDefaultValues != null ? providedDefaultValues : backendDefaultValues) != null ? _a2 : {},
1546
+ subaction,
1547
+ registerReceiveFocus,
1548
+ validator
1549
+ });
1550
+ }, [
1551
+ action,
1552
+ providedDefaultValues,
1553
+ registerReceiveFocus,
1554
+ subaction,
1555
+ syncFormProps,
1556
+ backendDefaultValues,
1557
+ validator
1558
+ ]);
1559
+ useIsomorphicLayoutEffect(() => {
1560
+ setFormElementInState(formRef.current);
1561
+ }, [setFormElementInState]);
1562
+ (0, import_react10.useEffect)(() => {
1563
+ var _a2;
1564
+ setFieldErrors((_a2 = backendError == null ? void 0 : backendError.fieldErrors) != null ? _a2 : {});
1565
+ if (!disableFocusOnError && (backendError == null ? void 0 : backendError.fieldErrors)) {
1566
+ focusFirstInvalidInput(
1567
+ backendError.fieldErrors,
1568
+ customFocusHandlers(),
1569
+ formRef.current
1570
+ );
1571
+ }
1572
+ }, [
1573
+ backendError == null ? void 0 : backendError.fieldErrors,
1574
+ customFocusHandlers,
1575
+ disableFocusOnError,
1576
+ setFieldErrors,
1577
+ setFieldError
1578
+ ]);
1579
+ useSubmitComplete(hasActiveSubmission, () => {
1580
+ endSubmit();
1581
+ });
1582
+ const handleSubmit = async (e, target, nativeEvent) => {
1583
+ startSubmit();
1584
+ const submitter = nativeEvent.submitter;
1585
+ const formDataToValidate = getDataFromForm(e.currentTarget);
1586
+ if (submitter == null ? void 0 : submitter.name) {
1587
+ formDataToValidate.append(submitter.name, submitter.value);
1588
+ }
1589
+ const result = await validator.validate(formDataToValidate);
1590
+ if (result.error) {
1591
+ setFieldErrors(result.error.fieldErrors);
1592
+ endSubmit();
1593
+ if (!disableFocusOnError) {
1594
+ focusFirstInvalidInput(
1595
+ result.error.fieldErrors,
1596
+ customFocusHandlers(),
1597
+ formRef.current
1598
+ );
1599
+ }
1600
+ } else {
1601
+ setFieldErrors({});
1602
+ const eventProxy = formEventProxy(e);
1603
+ await (onSubmit == null ? void 0 : onSubmit(result.data, eventProxy));
1604
+ if (eventProxy.defaultPrevented) {
1605
+ endSubmit();
1606
+ return;
1607
+ }
1608
+ if (fetcher)
1609
+ fetcher.submit(submitter || e.currentTarget);
1610
+ else
1611
+ submit(submitter || target, {
1612
+ replace: replace2,
1613
+ method: (submitter == null ? void 0 : submitter.formMethod) || method
1614
+ });
1615
+ }
1616
+ };
1617
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1618
+ Form,
1619
+ {
1620
+ ref: mergeRefs([formRef, formRefProp]),
1621
+ ...rest,
1622
+ id,
1623
+ action,
1624
+ method,
1625
+ replace: replace2,
1626
+ onSubmit: (e) => {
1627
+ e.preventDefault();
1628
+ handleSubmit(
1629
+ e,
1630
+ e.currentTarget,
1631
+ e.nativeEvent
1632
+ );
1633
+ },
1634
+ onReset: (event) => {
1635
+ onReset == null ? void 0 : onReset(event);
1636
+ if (event.defaultPrevented)
1637
+ return;
1638
+ reset();
1639
+ },
1640
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InternalFormContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormResetter, { formRef, resetAfterSubmit }),
1642
+ subaction && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "hidden", value: subaction, name: "subaction" }),
1643
+ id && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "hidden", value: id, name: FORM_ID_FIELD }),
1644
+ children
1645
+ ] }) })
1646
+ }
1647
+ );
1648
+ }
1649
+
1650
+ // src/validation/createValidator.ts
1651
+ var R5 = __toESM(require("remeda"));
1652
+
1653
+ // src/internal/flatten.ts
1654
+ var objectFromPathEntries = (entries) => {
1655
+ const map = new MultiValueMap();
1656
+ entries.forEach(([key, value]) => map.add(key, value));
1657
+ return [...map.entries()].reduce(
1658
+ (acc, [key, value]) => setPath(acc, key, value.length === 1 ? value[0] : value),
1659
+ {}
1660
+ );
1661
+ };
1662
+
1663
+ // src/validation/createValidator.ts
1664
+ var preprocessFormData = (data) => {
1665
+ if ("entries" in data && typeof data.entries === "function")
1666
+ return objectFromPathEntries([...data.entries()]);
1667
+ return objectFromPathEntries(Object.entries(data));
1668
+ };
1669
+ var omitInternalFields = (data) => R5.omit(data, [FORM_ID_FIELD]);
1670
+ function createValidator(validator) {
1671
+ return {
1672
+ validate: async (value) => {
1673
+ const data = preprocessFormData(value);
1674
+ const result = await validator.validate(omitInternalFields(data));
1675
+ if (result.error) {
1676
+ return {
1677
+ data: void 0,
1678
+ error: {
1679
+ fieldErrors: result.error,
1680
+ subaction: data.subaction,
1681
+ formId: data[FORM_ID_FIELD]
1682
+ },
1683
+ submittedData: data,
1684
+ formId: data[FORM_ID_FIELD]
1685
+ };
1686
+ }
1687
+ return {
1688
+ data: result.data,
1689
+ error: void 0,
1690
+ submittedData: data,
1691
+ formId: data[FORM_ID_FIELD]
1692
+ };
1693
+ },
1694
+ validateField: (data, field) => validator.validateField(preprocessFormData(data), field)
1695
+ };
1696
+ }
1697
+
1698
+ // src/userFacingFormContext.ts
1699
+ var import_react12 = require("react");
1700
+
1701
+ // src/unreleased/formStateHooks.ts
1702
+ var import_react11 = require("react");
1703
+ var useFormState = (formId) => {
1704
+ const formContext = useInternalFormContext(formId, "useFormState");
1705
+ const isSubmitting = useInternalIsSubmitting(formContext.formId);
1706
+ const hasBeenSubmitted = useInternalHasBeenSubmitted(formContext.formId);
1707
+ const touchedFields = useTouchedFields(formContext.formId);
1708
+ const isValid = useInternalIsValid(formContext.formId);
1709
+ const action = useFormActionProp(formContext.formId);
1710
+ const subaction = useFormSubactionProp(formContext.formId);
1711
+ const syncedDefaultValues = useSyncedDefaultValues(formContext.formId);
1712
+ const defaultValuesToUse = useDefaultValuesForForm(formContext);
1713
+ const hydratedDefaultValues = defaultValuesToUse.hydrateTo(syncedDefaultValues);
1714
+ const fieldErrorsFromState = useFieldErrors(formContext.formId);
1715
+ const fieldErrorsToUse = useFieldErrorsForForm(formContext);
1716
+ const hydratedFieldErrors = fieldErrorsToUse.hydrateTo(fieldErrorsFromState);
1717
+ return (0, import_react11.useMemo)(
1718
+ () => ({
1719
+ action,
1720
+ subaction,
1721
+ defaultValues: hydratedDefaultValues,
1722
+ fieldErrors: hydratedFieldErrors != null ? hydratedFieldErrors : {},
1723
+ hasBeenSubmitted,
1724
+ isSubmitting,
1725
+ touchedFields,
1726
+ isValid
1727
+ }),
1728
+ [
1729
+ action,
1730
+ hasBeenSubmitted,
1731
+ hydratedDefaultValues,
1732
+ hydratedFieldErrors,
1733
+ isSubmitting,
1734
+ isValid,
1735
+ subaction,
1736
+ touchedFields
1737
+ ]
1738
+ );
1739
+ };
1740
+ var useFormHelpers = (formId) => {
1741
+ const formContext = useInternalFormContext(formId, "useFormHelpers");
1742
+ const setTouched = useSetTouched(formContext);
1743
+ const validateField = useValidateField(formContext.formId);
1744
+ const validate = useValidate(formContext.formId);
1745
+ const clearError = useClearError(formContext);
1746
+ const setFieldErrors = useSetFieldErrors(formContext.formId);
1747
+ const reset = useResetFormElement(formContext.formId);
1748
+ const submit = useSubmitForm(formContext.formId);
1749
+ const getValues = useFormValues(formContext.formId);
1750
+ return (0, import_react11.useMemo)(
1751
+ () => ({
1752
+ setTouched,
1753
+ validateField,
1754
+ clearError,
1755
+ validate,
1756
+ clearAllErrors: () => setFieldErrors({}),
1757
+ reset,
1758
+ submit,
1759
+ getValues
1760
+ }),
1761
+ [
1762
+ clearError,
1763
+ reset,
1764
+ setFieldErrors,
1765
+ setTouched,
1766
+ submit,
1767
+ validate,
1768
+ validateField,
1769
+ getValues
1770
+ ]
1771
+ );
1772
+ };
1773
+
1774
+ // src/userFacingFormContext.ts
1775
+ var useFormContext = (formId) => {
1776
+ const context = useInternalFormContext(formId, "useFormContext");
1777
+ const state = useFormState(formId);
1778
+ const {
1779
+ clearError: internalClearError,
1780
+ setTouched,
1781
+ validateField,
1782
+ clearAllErrors,
1783
+ validate,
1784
+ reset,
1785
+ submit,
1786
+ getValues
1787
+ } = useFormHelpers(formId);
1788
+ const registerReceiveFocus = useRegisterReceiveFocus(context.formId);
1789
+ const clearError = (0, import_react12.useCallback)(
1790
+ (...names) => {
1791
+ names.forEach((name) => {
1792
+ internalClearError(name);
1793
+ });
1794
+ },
1795
+ [internalClearError]
1796
+ );
1797
+ return (0, import_react12.useMemo)(
1798
+ () => ({
1799
+ ...state,
1800
+ setFieldTouched: setTouched,
1801
+ validateField,
1802
+ clearError,
1803
+ registerReceiveFocus,
1804
+ clearAllErrors,
1805
+ validate,
1806
+ reset,
1807
+ submit,
1808
+ getValues
1809
+ }),
1810
+ [
1811
+ clearAllErrors,
1812
+ clearError,
1813
+ registerReceiveFocus,
1814
+ reset,
1815
+ setTouched,
1816
+ state,
1817
+ submit,
1818
+ validate,
1819
+ validateField,
1820
+ getValues
1821
+ ]
1822
+ );
1823
+ };
1824
+
1825
+ // src/internal/state/fieldArray.tsx
1826
+ var import_react13 = require("react");
1827
+ var import_react14 = require("react");
1828
+ var import_tiny_invariant4 = __toESM(require("tiny-invariant"));
1829
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1830
+ var useInternalFieldArray = (context, field, validationBehavior) => {
1831
+ const value = useFieldDefaultValue(field, context);
1832
+ useRegisterControlledField(context, field);
1833
+ const hasBeenSubmitted = useInternalHasBeenSubmitted(context.formId);
1834
+ const validateField = useValidateField(context.formId);
1835
+ const error = useFieldError(field, context);
1836
+ const resolvedValidationBehavior = {
1837
+ initial: "onSubmit",
1838
+ whenSubmitted: "onChange",
1839
+ ...validationBehavior
1840
+ };
1841
+ const behavior = hasBeenSubmitted ? resolvedValidationBehavior.whenSubmitted : resolvedValidationBehavior.initial;
1842
+ const maybeValidate = (0, import_react14.useCallback)(() => {
1843
+ if (behavior === "onChange") {
1844
+ validateField(field);
1845
+ }
1846
+ }, [behavior, field, validateField]);
1847
+ (0, import_tiny_invariant4.default)(
1848
+ value === void 0 || value === null || Array.isArray(value),
1849
+ `FieldArray: defaultValue value for ${field} must be an array, null, or undefined`
1850
+ );
1851
+ const arr = useFormStore(
1852
+ context.formId,
1853
+ (state) => state.controlledFields.array
1854
+ );
1855
+ const helpers = (0, import_react13.useMemo)(
1856
+ () => ({
1857
+ push: (item) => {
1858
+ arr.push(field, item);
1859
+ maybeValidate();
1860
+ },
1861
+ swap: (indexA, indexB) => {
1862
+ arr.swap(field, indexA, indexB);
1863
+ maybeValidate();
1864
+ },
1865
+ move: (from2, to) => {
1866
+ arr.move(field, from2, to);
1867
+ maybeValidate();
1868
+ },
1869
+ insert: (index, value2) => {
1870
+ arr.insert(field, index, value2);
1871
+ maybeValidate();
1872
+ },
1873
+ unshift: (value2) => {
1874
+ arr.unshift(field, value2);
1875
+ maybeValidate();
1876
+ },
1877
+ remove: (index) => {
1878
+ arr.remove(field, index);
1879
+ maybeValidate();
1880
+ },
1881
+ pop: () => {
1882
+ arr.pop(field);
1883
+ maybeValidate();
1884
+ },
1885
+ replace: (index, value2) => {
1886
+ arr.replace(field, index, value2);
1887
+ maybeValidate();
1888
+ }
1889
+ }),
1890
+ [arr, field, maybeValidate]
1891
+ );
1892
+ const arrayValue = (0, import_react13.useMemo)(() => value != null ? value : [], [value]);
1893
+ return [arrayValue, helpers, error];
1894
+ };
1895
+ function useFieldArray(name, { formId, validationBehavior } = {}) {
1896
+ const context = useInternalFormContext(formId, "FieldArray");
1897
+ return useInternalFieldArray(context, name, validationBehavior);
1898
+ }
1899
+ var FieldArray = ({
1900
+ name,
1901
+ children,
1902
+ formId,
1903
+ validationBehavior
1904
+ }) => {
1905
+ const context = useInternalFormContext(formId, "FieldArray");
1906
+ const [value, helpers, error] = useInternalFieldArray(
1907
+ context,
1908
+ name,
1909
+ validationBehavior
1910
+ );
1911
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: children(value, helpers, error) });
1912
+ };
1913
+ // Annotate the CommonJS export names for ESM import in node:
1914
+ 0 && (module.exports = {
1915
+ FieldArray,
1916
+ ValidatedForm,
1917
+ createValidator,
1918
+ setFormDefaults,
1919
+ useControlField,
1920
+ useField,
1921
+ useFieldArray,
1922
+ useFormContext,
1923
+ useIsSubmitting,
1924
+ useIsValid,
1925
+ useUpdateControlledField,
1926
+ validationError
1927
+ });
1928
+ //# sourceMappingURL=index.cjs.js.map