remix-validated-form 4.6.5 → 4.6.7

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 +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