remix-validated-form 4.6.5 → 4.6.6

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.
Files changed (43) hide show
  1. package/README.md +10 -9
  2. package/dist/index.cjs.js +1928 -0
  3. package/dist/index.cjs.js.map +1 -0
  4. package/dist/index.d.ts +337 -0
  5. package/dist/{remix-validated-form.es.js → index.esm.js} +573 -877
  6. package/dist/index.esm.js.map +1 -0
  7. package/package.json +12 -10
  8. package/src/ValidatedForm.tsx +17 -5
  9. package/src/hooks.ts +1 -1
  10. package/src/server.ts +1 -1
  11. package/tsup.config.ts +3 -0
  12. package/dist/remix-validated-form.cjs.js +0 -27
  13. package/dist/remix-validated-form.cjs.js.map +0 -1
  14. package/dist/remix-validated-form.es.js.map +0 -1
  15. package/dist/remix-validated-form.umd.js +0 -27
  16. package/dist/remix-validated-form.umd.js.map +0 -1
  17. package/dist/types/ValidatedForm.d.ts +0 -50
  18. package/dist/types/hooks.d.ts +0 -67
  19. package/dist/types/index.d.ts +0 -7
  20. package/dist/types/internal/MultiValueMap.d.ts +0 -11
  21. package/dist/types/internal/constants.d.ts +0 -3
  22. package/dist/types/internal/flatten.d.ts +0 -1
  23. package/dist/types/internal/formContext.d.ts +0 -12
  24. package/dist/types/internal/getInputProps.d.ts +0 -29
  25. package/dist/types/internal/hooks.d.ts +0 -35
  26. package/dist/types/internal/hydratable.d.ts +0 -14
  27. package/dist/types/internal/logic/getCheckboxChecked.d.ts +0 -1
  28. package/dist/types/internal/logic/getRadioChecked.d.ts +0 -1
  29. package/dist/types/internal/logic/requestSubmit.d.ts +0 -5
  30. package/dist/types/internal/state/arrayUtil.d.ts +0 -12
  31. package/dist/types/internal/state/controlledFields.d.ts +0 -7
  32. package/dist/types/internal/state/createFormStore.d.ts +0 -79
  33. package/dist/types/internal/state/fieldArray.d.ts +0 -28
  34. package/dist/types/internal/state/storeHooks.d.ts +0 -3
  35. package/dist/types/internal/state/types.d.ts +0 -1
  36. package/dist/types/internal/submissionCallbacks.d.ts +0 -1
  37. package/dist/types/internal/util.d.ts +0 -5
  38. package/dist/types/server.d.ts +0 -21
  39. package/dist/types/unreleased/formStateHooks.d.ts +0 -64
  40. package/dist/types/userFacingFormContext.d.ts +0 -85
  41. package/dist/types/validation/createValidator.d.ts +0 -7
  42. package/dist/types/validation/types.d.ts +0 -58
  43. 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