remix-validated-form 4.6.5 → 4.6.7

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