@vritti/quantum-ui 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -2,6 +2,8 @@ import React__default from 'react';
2
2
 
3
3
  var isCheckBoxInput = (element) => element.type === 'checkbox';
4
4
 
5
+ var isFileInput = (element) => element.type === 'file';
6
+
5
7
  var isDateObject = (value) => value instanceof Date;
6
8
 
7
9
  var isNullOrUndefined = (value) => value == null;
@@ -15,23 +17,41 @@ var isObject = (value) => !isNullOrUndefined(value) &&
15
17
  var getEventValue = (event) => isObject(event) && event.target
16
18
  ? isCheckBoxInput(event.target)
17
19
  ? event.target.checked
18
- : event.target.value
20
+ : isFileInput(event.target)
21
+ ? event.target.files
22
+ : event.target.value
19
23
  : event;
20
24
 
21
- var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
22
-
23
- var isNameInFieldArray = (names, name) => names.has(getNodeParentName(name));
25
+ const FIELD_PATH_RE = /[.[\]'"]/;
26
+ var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
27
+
28
+ function getNullAncestorValue(control, name) {
29
+ const segments = stringToPath(name);
30
+ let formValues = control._formValues;
31
+ let defaultValues = control._defaultValues;
32
+ for (let i = 0; i < segments.length - 1; i++) {
33
+ const key = segments[i];
34
+ formValues = formValues == null ? formValues : formValues[key];
35
+ defaultValues = defaultValues == null ? defaultValues : defaultValues[key];
36
+ if (formValues === null || defaultValues === null) {
37
+ return null;
38
+ }
39
+ }
40
+ return undefined;
41
+ }
24
42
 
25
- var isPlainObject = (tempObject) => {
26
- const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
27
- return (isObject(prototypeCopy) && prototypeCopy.hasOwnProperty('isPrototypeOf'));
28
- };
43
+ var isNameInFieldArray = (names, name) => name
44
+ .split('.')
45
+ .some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
29
46
 
30
47
  var isWeb = typeof window !== 'undefined' &&
31
48
  typeof window.HTMLElement !== 'undefined' &&
32
49
  typeof document !== 'undefined';
33
50
 
34
51
  function cloneObject(data) {
52
+ if (data === null || typeof data !== 'object') {
53
+ return data;
54
+ }
35
55
  if (data instanceof Date) {
36
56
  return new Date(data);
37
57
  }
@@ -40,7 +60,7 @@ function cloneObject(data) {
40
60
  return data;
41
61
  }
42
62
  const isArray = Array.isArray(data);
43
- if (!isArray && !(isObject(data) && isPlainObject(data))) {
63
+ if (!isArray && data.constructor !== Object) {
44
64
  return data;
45
65
  }
46
66
  const copy = isArray ? [] : Object.create(Object.getPrototypeOf(data));
@@ -52,19 +72,49 @@ function cloneObject(data) {
52
72
  return copy;
53
73
  }
54
74
 
55
- var isKey = (value) => /^\w*$/.test(value);
56
-
57
- var isUndefined = (val) => val === undefined;
75
+ const EVENTS = {
76
+ BLUR: 'blur',
77
+ FOCUS_OUT: 'focusout',
78
+ CHANGE: 'change',
79
+ SUBMIT: 'submit',
80
+ TRIGGER: 'trigger',
81
+ VALID: 'valid',
82
+ };
83
+ const VALIDATION_MODE = {
84
+ onBlur: 'onBlur',
85
+ onChange: 'onChange',
86
+ onSubmit: 'onSubmit',
87
+ onTouched: 'onTouched',
88
+ all: 'all',
89
+ };
90
+ const INPUT_VALIDATION_RULES = {
91
+ max: 'max',
92
+ min: 'min',
93
+ maxLength: 'maxLength',
94
+ minLength: 'minLength',
95
+ pattern: 'pattern',
96
+ required: 'required',
97
+ validate: 'validate',
98
+ };
99
+ const ROOT_ERROR_TYPE = 'root';
100
+ const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
58
101
 
59
- var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
102
+ const IS_KEY_RE = /^\w*$/;
103
+ var isKey = (value) => IS_KEY_RE.test(value);
60
104
 
61
- var stringToPath = (input) => compact(input.replace(/["|']|\]/g, '').split(/\.|\[/));
105
+ var isUndefined = (val) => val === undefined;
62
106
 
63
107
  var get = (object, path, defaultValue) => {
64
108
  if (!path || !isObject(object)) {
65
109
  return defaultValue;
66
110
  }
67
- const result = (isKey(path) ? [path] : stringToPath(path)).reduce((result, key) => isNullOrUndefined(result) ? result : result[key], object);
111
+ const paths = isKey(path) ? [path] : stringToPath(path);
112
+ if (paths.some((key) => PROTOTYPE_KEYWORDS.includes(key))) {
113
+ return defaultValue;
114
+ }
115
+ const result = paths.reduce((result, key) => {
116
+ return isNullOrUndefined(result) ? undefined : result[key];
117
+ }, object);
68
118
  return isUndefined(result) || result === object
69
119
  ? isUndefined(object[path])
70
120
  ? defaultValue
@@ -93,7 +143,7 @@ var set = (object, path, value) => {
93
143
  ? []
94
144
  : {};
95
145
  }
96
- if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
146
+ if (PROTOTYPE_KEYWORDS.includes(key)) {
97
147
  return;
98
148
  }
99
149
  object[key] = newValue;
@@ -101,28 +151,6 @@ var set = (object, path, value) => {
101
151
  }
102
152
  };
103
153
 
104
- const EVENTS = {
105
- BLUR: 'blur',
106
- FOCUS_OUT: 'focusout',
107
- CHANGE: 'change',
108
- };
109
- const VALIDATION_MODE = {
110
- onBlur: 'onBlur',
111
- onChange: 'onChange',
112
- onSubmit: 'onSubmit',
113
- onTouched: 'onTouched',
114
- all: 'all',
115
- };
116
- const INPUT_VALIDATION_RULES = {
117
- max: 'max',
118
- min: 'min',
119
- maxLength: 'maxLength',
120
- minLength: 'minLength',
121
- pattern: 'pattern',
122
- required: 'required',
123
- validate: 'validate',
124
- };
125
-
126
154
  /**
127
155
  * Separate context for `control` to prevent unnecessary rerenders.
128
156
  * Internal hooks that only need control use this instead of full form context.
@@ -135,9 +163,7 @@ HookFormControlContext.displayName = 'HookFormControlContext';
135
163
  const useFormControlContext = () => React__default.useContext(HookFormControlContext);
136
164
 
137
165
  var getProxyFormState = (formState, control, localProxyFormState, isRoot = true) => {
138
- const result = {
139
- defaultValues: control._defaultValues,
140
- };
166
+ const result = {};
141
167
  for (const key in formState) {
142
168
  Object.defineProperty(result, key, {
143
169
  get: () => {
@@ -153,15 +179,107 @@ var getProxyFormState = (formState, control, localProxyFormState, isRoot = true)
153
179
  return result;
154
180
  };
155
181
 
156
- const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React__default.useLayoutEffect : React__default.useEffect;
182
+ const useIsomorphicLayoutEffect = isWeb
183
+ ? React__default.useLayoutEffect
184
+ : React__default.useEffect;
185
+
186
+ var isPlainObject = (tempObject) => {
187
+ const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
188
+ return (isObject(prototypeCopy) && prototypeCopy.hasOwnProperty('isPrototypeOf'));
189
+ };
190
+
191
+ var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
192
+
193
+ const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
194
+ function deepEqual(object1, object2, visited = new WeakMap()) {
195
+ if (object1 === object2) {
196
+ return true;
197
+ }
198
+ if (isPrimitive(object1) || isPrimitive(object2)) {
199
+ return Object.is(object1, object2);
200
+ }
201
+ if (isDateObject(object1) && isDateObject(object2)) {
202
+ return Object.is(object1.getTime(), object2.getTime());
203
+ }
204
+ const keys1 = Object.keys(object1);
205
+ const keys2 = Object.keys(object2);
206
+ if (keys1.length !== keys2.length) {
207
+ return false;
208
+ }
209
+ if (isEmptyObjectWithCustomPrototype(object1, keys1) ||
210
+ isEmptyObjectWithCustomPrototype(object2, keys2)) {
211
+ return Object.is(object1, object2);
212
+ }
213
+ if (!keys1.length && Array.isArray(object1) !== Array.isArray(object2)) {
214
+ return false;
215
+ }
216
+ const visitedPairs = visited.get(object1);
217
+ if (visitedPairs && visitedPairs.has(object2)) {
218
+ return true;
219
+ }
220
+ if (visitedPairs) {
221
+ visitedPairs.add(object2);
222
+ }
223
+ else {
224
+ const ws = new WeakSet();
225
+ ws.add(object2);
226
+ visited.set(object1, ws);
227
+ }
228
+ for (const key of keys1) {
229
+ const val1 = object1[key];
230
+ if (!(key in object2)) {
231
+ return false;
232
+ }
233
+ if (key !== 'ref') {
234
+ const val2 = object2[key];
235
+ if ((isDateObject(val1) && isDateObject(val2)) ||
236
+ ((isObject(val1) || Array.isArray(val1)) &&
237
+ (isObject(val2) || Array.isArray(val2)))
238
+ ? !deepEqual(val1, val2, visited)
239
+ : !Object.is(val1, val2)) {
240
+ return false;
241
+ }
242
+ }
243
+ }
244
+ return true;
245
+ }
246
+
247
+ function useResyncOnReconnect(getInitialValue) {
248
+ const _connected = React__default.useRef(false);
249
+ const _initialized = React__default.useRef(false);
250
+ const _prevValue = React__default.useRef(undefined);
251
+ const _renderCount = React__default.useRef(0);
252
+ _renderCount.current++;
253
+ if (!_initialized.current && getInitialValue) {
254
+ _initialized.current = true;
255
+ _prevValue.current = cloneObject(getInitialValue());
256
+ }
257
+ const resyncIfNeeded = React__default.useCallback((enabled, getCurrentValue, setValue) => {
258
+ if (enabled &&
259
+ (_connected.current ||
260
+ (_initialized.current && _renderCount.current > 1))) {
261
+ const currentValue = getCurrentValue();
262
+ if (!deepEqual(_prevValue.current, currentValue)) {
263
+ setValue(currentValue);
264
+ }
265
+ }
266
+ _connected.current = true;
267
+ }, []);
268
+ const snapshot = React__default.useCallback((enabled, getCurrentValue) => {
269
+ if (enabled) {
270
+ _prevValue.current = cloneObject(getCurrentValue());
271
+ }
272
+ }, []);
273
+ return { resyncIfNeeded, snapshot };
274
+ }
157
275
 
158
276
  /**
159
- * This custom hook allows you to subscribe to each form state, and isolate the re-render at the custom hook level. It has its scope in terms of form state subscription, so it would not affect other useFormState and useForm. Using this hook can reduce the re-render impact on large and complex form application.
277
+ * Subscribes to each form state and isolates re-renders at the custom hook level. It has its own scope for form state subscriptions, so it will not affect other useFormState or useForm instances. Using this hook can reduce the re-render impact on large and complex form applications.
160
278
  *
161
279
  * @remarks
162
280
  * [API](https://react-hook-form.com/docs/useformstate) • [Demo](https://codesandbox.io/s/useformstate-75xly)
163
281
  *
164
- * @param props - include options on specify fields to subscribe. {@link UseFormStateReturn}
282
+ * @param props - Include options to specify fields to subscribe to. {@link UseFormStateReturn}
165
283
  *
166
284
  * @example
167
285
  * ```tsx
@@ -188,7 +306,11 @@ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React__default
188
306
  function useFormState(props) {
189
307
  const formControl = useFormControlContext();
190
308
  const { control = formControl, disabled, name, exact } = props || {};
191
- const [formState, updateFormState] = React__default.useState(control._formState);
309
+ const getCurrentFormState = () => ({
310
+ ...control._formState,
311
+ defaultValues: control._defaultValues,
312
+ });
313
+ const [formState, updateFormState] = React__default.useState(getCurrentFormState);
192
314
  const _localProxyFormState = React__default.useRef({
193
315
  isDirty: false,
194
316
  isLoading: false,
@@ -199,18 +321,27 @@ function useFormState(props) {
199
321
  isValid: false,
200
322
  errors: false,
201
323
  });
202
- useIsomorphicLayoutEffect(() => control._subscribe({
203
- name,
204
- formState: _localProxyFormState.current,
205
- exact,
206
- callback: (formState) => {
207
- !disabled &&
208
- updateFormState({
209
- ...control._formState,
210
- ...formState,
211
- });
212
- },
213
- }), [name, disabled, exact]);
324
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getCurrentFormState);
325
+ useIsomorphicLayoutEffect(() => {
326
+ resyncIfNeeded(!disabled, getCurrentFormState, updateFormState);
327
+ const unsubscribe = control._subscribe({
328
+ name,
329
+ formState: _localProxyFormState.current,
330
+ exact,
331
+ callback: (formState) => {
332
+ !disabled &&
333
+ updateFormState({
334
+ ...control._formState,
335
+ ...formState,
336
+ defaultValues: control._defaultValues,
337
+ });
338
+ },
339
+ });
340
+ return () => {
341
+ unsubscribe();
342
+ snapshot(!disabled, getCurrentFormState);
343
+ };
344
+ }, [name, disabled, exact, resyncIfNeeded, snapshot]);
214
345
  React__default.useEffect(() => {
215
346
  _localProxyFormState.current.isValid && control._setValid(true);
216
347
  }, [control]);
@@ -226,52 +357,14 @@ var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) =>
226
357
  }
227
358
  if (Array.isArray(names)) {
228
359
  return names.map((fieldName) => (isGlobal && _names.watch.add(fieldName),
229
- get(formValues, fieldName)));
360
+ get(formValues, fieldName, get(defaultValue, fieldName))));
230
361
  }
231
362
  isGlobal && (_names.watchAll = true);
232
363
  return formValues;
233
364
  };
234
365
 
235
- var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
236
-
237
- function deepEqual(object1, object2, _internal_visited = new WeakSet()) {
238
- if (isPrimitive(object1) || isPrimitive(object2)) {
239
- return Object.is(object1, object2);
240
- }
241
- if (isDateObject(object1) && isDateObject(object2)) {
242
- return Object.is(object1.getTime(), object2.getTime());
243
- }
244
- const keys1 = Object.keys(object1);
245
- const keys2 = Object.keys(object2);
246
- if (keys1.length !== keys2.length) {
247
- return false;
248
- }
249
- if (_internal_visited.has(object1) || _internal_visited.has(object2)) {
250
- return true;
251
- }
252
- _internal_visited.add(object1);
253
- _internal_visited.add(object2);
254
- for (const key of keys1) {
255
- const val1 = object1[key];
256
- if (!keys2.includes(key)) {
257
- return false;
258
- }
259
- if (key !== 'ref') {
260
- const val2 = object2[key];
261
- if ((isDateObject(val1) && isDateObject(val2)) ||
262
- (isObject(val1) && isObject(val2)) ||
263
- (Array.isArray(val1) && Array.isArray(val2))
264
- ? !deepEqual(val1, val2, _internal_visited)
265
- : !Object.is(val1, val2)) {
266
- return false;
267
- }
268
- }
269
- }
270
- return true;
271
- }
272
-
273
366
  /**
274
- * Custom hook to subscribe to field change and isolate re-rendering at the component level.
367
+ * Custom hook to subscribe to field changes and isolate re-rendering at the component level.
275
368
  *
276
369
  * @remarks
277
370
  *
@@ -281,7 +374,7 @@ function deepEqual(object1, object2, _internal_visited = new WeakSet()) {
281
374
  * ```tsx
282
375
  * const { control } = useForm();
283
376
  * const values = useWatch({
284
- * name: "fieldName"
377
+ * name: "fieldName",
285
378
  * control,
286
379
  * })
287
380
  * ```
@@ -295,10 +388,11 @@ function useWatch(props) {
295
388
  const _prevControl = React__default.useRef(control);
296
389
  const _prevName = React__default.useRef(name);
297
390
  _compute.current = compute;
298
- const [value, updateValue] = React__default.useState(() => {
391
+ const getInitialOutput = () => {
299
392
  const defaultValue = control._getWatch(name, _defaultValue.current);
300
393
  return _compute.current ? _compute.current(defaultValue) : defaultValue;
301
- });
394
+ };
395
+ const [value, updateValue] = React__default.useState(getInitialOutput);
302
396
  const getCurrentOutput = React__default.useCallback((values) => {
303
397
  const formValues = generateWatchOutput(name, control._names, values || control._formValues, false, _defaultValue.current);
304
398
  return _compute.current ? _compute.current(formValues) : formValues;
@@ -318,24 +412,39 @@ function useWatch(props) {
318
412
  }
319
413
  }
320
414
  }, [control._formValues, control._names, disabled, name]);
415
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect(getInitialOutput);
416
+ const _refreshValue = React__default.useRef(refreshValue);
417
+ _refreshValue.current = refreshValue;
418
+ const _getCurrentOutput = React__default.useRef(getCurrentOutput);
419
+ _getCurrentOutput.current = getCurrentOutput;
321
420
  useIsomorphicLayoutEffect(() => {
322
421
  if (_prevControl.current !== control ||
323
422
  !deepEqual(_prevName.current, name)) {
324
423
  _prevControl.current = control;
325
424
  _prevName.current = name;
326
- refreshValue();
425
+ _refreshValue.current();
327
426
  }
328
- return control._subscribe({
427
+ else {
428
+ resyncIfNeeded(!disabled, () => _getCurrentOutput.current(), (currentValue) => {
429
+ updateValue(currentValue);
430
+ _computeFormValues.current = currentValue;
431
+ });
432
+ }
433
+ const unsubscribe = control._subscribe({
329
434
  name,
330
435
  formState: {
331
436
  values: true,
332
437
  },
333
438
  exact,
334
439
  callback: (formState) => {
335
- refreshValue(formState.values);
440
+ _refreshValue.current(formState.values);
336
441
  },
337
442
  });
338
- }, [control, exact, name, refreshValue]);
443
+ return () => {
444
+ unsubscribe();
445
+ snapshot(!disabled, () => _getCurrentOutput.current());
446
+ };
447
+ }, [control, exact, name, disabled, resyncIfNeeded, snapshot]);
339
448
  React__default.useEffect(() => control._removeUnmounted());
340
449
  // If name or control changed for this render, synchronously reflect the
341
450
  // latest value so callers (like useController) see the correct value
@@ -343,17 +452,17 @@ function useWatch(props) {
343
452
  // Optimize: Check control reference first before expensive deepEqual
344
453
  const controlChanged = _prevControl.current !== control;
345
454
  const prevName = _prevName.current;
346
- // Cache the computed output to avoid duplicate calls within the same render
347
- // We include shouldReturnImmediate in deps to ensure proper recomputation
348
- const computedOutput = React__default.useMemo(() => {
455
+ // `null`/`undefined` are valid watched values, so a boolean flag (rather
456
+ // than a sentinel return value) decides whether to return the freshly
457
+ // computed output instead of the possibly-stale state value.
458
+ const shouldReturnImmediate = React__default.useMemo(() => {
349
459
  if (disabled) {
350
- return null;
460
+ return false;
351
461
  }
352
462
  const nameChanged = !controlChanged && !deepEqual(prevName, name);
353
- const shouldReturnImmediate = controlChanged || nameChanged;
354
- return shouldReturnImmediate ? getCurrentOutput() : null;
355
- }, [disabled, controlChanged, name, prevName, getCurrentOutput]);
356
- return computedOutput !== null ? computedOutput : value;
463
+ return controlChanged || nameChanged;
464
+ }, [disabled, controlChanged, name, prevName]);
465
+ return shouldReturnImmediate ? getCurrentOutput() : value;
357
466
  }
358
467
 
359
468
  /**
@@ -384,7 +493,12 @@ function useController(props) {
384
493
  const formControl = useFormControlContext();
385
494
  const { name, disabled, control = formControl, shouldUnregister, defaultValue, exact = true, } = props;
386
495
  const isArrayField = isNameInFieldArray(control._names.array, name);
387
- const defaultValueMemo = React__default.useMemo(() => get(control._formValues, name, get(control._defaultValues, name, defaultValue)), [control, name, defaultValue]);
496
+ const defaultValueMemo = React__default.useMemo(() => {
497
+ const resolved = get(control._formValues, name, get(control._defaultValues, name, defaultValue));
498
+ return isUndefined(resolved)
499
+ ? getNullAncestorValue(control, name)
500
+ : resolved;
501
+ }, [control, name, defaultValue]);
388
502
  const value = useWatch({
389
503
  control,
390
504
  name,
@@ -397,7 +511,7 @@ function useController(props) {
397
511
  exact,
398
512
  });
399
513
  const _props = React__default.useRef(props);
400
- const _previousNameRef = React__default.useRef(undefined);
514
+ const _proxyRef = React__default.useRef(null);
401
515
  const _registerProps = React__default.useRef(control.register(name, {
402
516
  ...props.rules,
403
517
  value,
@@ -426,13 +540,22 @@ function useController(props) {
426
540
  get: () => get(formState.errors, name),
427
541
  },
428
542
  }), [formState, name]);
429
- const onChange = React__default.useCallback((event) => _registerProps.current.onChange({
430
- target: {
431
- value: getEventValue(event),
432
- name: name,
433
- },
434
- type: EVENTS.CHANGE,
435
- }), [name]);
543
+ const onChange = React__default.useCallback((event) => {
544
+ const value = getEventValue(event);
545
+ if (!get(control._fields, name)) {
546
+ _registerProps.current = control.register(name, {
547
+ ..._props.current.rules,
548
+ value,
549
+ });
550
+ }
551
+ return _registerProps.current.onChange({
552
+ target: {
553
+ value: getEventValue(event),
554
+ name: name,
555
+ },
556
+ type: EVENTS.CHANGE,
557
+ });
558
+ }, [name, control]);
436
559
  const onBlur = React__default.useCallback(() => _registerProps.current.onBlur({
437
560
  target: {
438
561
  value: get(control._formValues, name),
@@ -441,15 +564,18 @@ function useController(props) {
441
564
  type: EVENTS.BLUR,
442
565
  }), [name, control._formValues]);
443
566
  const ref = React__default.useCallback((elm) => {
444
- const field = get(control._fields, name);
445
- if (field && field._f && elm) {
446
- field._f.ref = {
567
+ if (elm) {
568
+ _proxyRef.current = {
447
569
  focus: () => isFunction(elm.focus) && elm.focus(),
448
570
  select: () => isFunction(elm.select) && elm.select(),
449
571
  setCustomValidity: (message) => isFunction(elm.setCustomValidity) && elm.setCustomValidity(message),
450
572
  reportValidity: () => isFunction(elm.reportValidity) && elm.reportValidity(),
451
573
  };
452
574
  }
575
+ const field = get(control._fields, name);
576
+ if (field && field._f && elm) {
577
+ field._f.ref = _proxyRef.current;
578
+ }
453
579
  }, [control._fields, name]);
454
580
  const field = React__default.useMemo(() => ({
455
581
  name,
@@ -463,11 +589,7 @@ function useController(props) {
463
589
  }), [name, disabled, formState.disabled, onChange, onBlur, ref, value]);
464
590
  React__default.useEffect(() => {
465
591
  const _shouldUnregisterField = control._options.shouldUnregister || shouldUnregister;
466
- const previousName = _previousNameRef.current;
467
- if (previousName && previousName !== name && !isArrayField) {
468
- control.unregister(previousName);
469
- }
470
- control.register(name, {
592
+ _registerProps.current = control.register(name, {
471
593
  ..._props.current.rules,
472
594
  ...(isBoolean(_props.current.disabled)
473
595
  ? { disabled: _props.current.disabled }
@@ -481,14 +603,21 @@ function useController(props) {
481
603
  };
482
604
  updateMounted(name, true);
483
605
  if (_shouldUnregisterField) {
484
- const value = cloneObject(get(control._options.defaultValues, name, _props.current.defaultValue));
606
+ const value = cloneObject(get(shouldUnregister
607
+ ? control._defaultValues
608
+ : control._options.values || control._defaultValues, name, get(control._options.defaultValues, name, _props.current.defaultValue)));
485
609
  set(control._defaultValues, name, value);
486
610
  if (isUndefined(get(control._formValues, name))) {
487
611
  set(control._formValues, name, value);
488
612
  }
489
613
  }
490
614
  !isArrayField && control.register(name);
491
- _previousNameRef.current = name;
615
+ if (_proxyRef.current) {
616
+ const field = get(control._fields, name);
617
+ if (field && field._f) {
618
+ field._f.ref = _proxyRef.current;
619
+ }
620
+ }
492
621
  return () => {
493
622
  (isArrayField
494
623
  ? _shouldUnregisterField && !control._state.action
@@ -554,95 +683,472 @@ function useController(props) {
554
683
  */
555
684
  const Controller = (props) => props.render(useController(props));
556
685
 
557
- const HookFormContext = React__default.createContext(null);
558
- HookFormContext.displayName = 'HookFormContext';
559
- /**
560
- * A provider component that propagates the `useForm` methods to all children components via [React Context](https://react.dev/reference/react/useContext) API. To be used with {@link useFormContext}.
561
- *
562
- * @remarks
563
- * [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
564
- *
565
- * @param props - all useForm methods
566
- *
567
- * @example
568
- * ```tsx
569
- * function App() {
570
- * const methods = useForm();
571
- * const onSubmit = data => console.log(data);
572
- *
573
- * return (
574
- * <FormProvider {...methods} >
575
- * <form onSubmit={methods.handleSubmit(onSubmit)}>
576
- * <NestedInput />
577
- * <input type="submit" />
578
- * </form>
579
- * </FormProvider>
580
- * );
581
- * }
582
- *
583
- * function NestedInput() {
584
- * const { register } = useFormContext(); // retrieve all hook methods
585
- * return <input {...register("test")} />;
586
- * }
587
- * ```
588
- */
589
- const FormProvider = (props) => {
590
- const { children, watch, getValues, getFieldState, setError, clearErrors, setValue, trigger, formState, resetField, reset, handleSubmit, unregister, control, register, setFocus, subscribe, } = props;
591
- return (React__default.createElement(HookFormContext.Provider, { value: React__default.useMemo(() => ({
592
- watch,
593
- getValues,
594
- getFieldState,
595
- setError,
596
- clearErrors,
597
- setValue,
598
- trigger,
599
- formState,
600
- resetField,
601
- reset,
602
- handleSubmit,
603
- unregister,
604
- control,
605
- register,
606
- setFocus,
607
- subscribe,
608
- }), [
609
- clearErrors,
610
- control,
611
- formState,
612
- getFieldState,
613
- getValues,
614
- handleSubmit,
615
- register,
616
- reset,
617
- resetField,
618
- setError,
619
- setFocus,
620
- setValue,
621
- subscribe,
622
- trigger,
623
- unregister,
624
- watch,
625
- ]) },
626
- React__default.createElement(HookFormControlContext.Provider, { value: control }, children)));
627
- };
686
+ var getValidationModes = (mode) => ({
687
+ isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
688
+ isOnBlur: mode === VALIDATION_MODE.onBlur,
689
+ isOnChange: mode === VALIDATION_MODE.onChange,
690
+ isOnAll: mode === VALIDATION_MODE.all,
691
+ isOnTouch: mode === VALIDATION_MODE.onTouched,
692
+ });
628
693
 
629
- var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => validateAllFieldCriteria
630
- ? {
631
- ...errors[name],
632
- types: {
633
- ...(errors[name] && errors[name].types ? errors[name].types : {}),
634
- [type]: message || true,
635
- },
694
+ var isWatched = (name, _names, isBlurEvent) => {
695
+ if (isBlurEvent)
696
+ return false;
697
+ if (_names.watchAll || _names.watch.has(name))
698
+ return true;
699
+ for (const watchName of _names.watch) {
700
+ if (name.startsWith(watchName) && name.charAt(watchName.length) === '.')
701
+ return true;
636
702
  }
637
- : {};
638
-
639
- var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
703
+ return false;
704
+ };
640
705
 
641
- var createSubject = () => {
642
- let _observers = [];
643
- const next = (value) => {
644
- for (const observer of _observers) {
645
- observer.next && observer.next(value);
706
+ const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
707
+ for (const key of fieldsNames || Object.keys(fields)) {
708
+ if (key === '_f') {
709
+ continue;
710
+ }
711
+ const field = fieldsNames ? get(fields, key) : fields[key];
712
+ if (field) {
713
+ const { _f } = field;
714
+ if (_f) {
715
+ if (_f.refs && _f.refs[0] && action(_f.refs[0], key) && !abortEarly) {
716
+ return true;
717
+ }
718
+ else if (_f.ref && action(_f.ref, _f.name) && !abortEarly) {
719
+ return true;
720
+ }
721
+ else {
722
+ if (iterateFieldsByAction(field, action)) {
723
+ break;
724
+ }
725
+ }
726
+ }
727
+ else if (isObject(field) || Array.isArray(field)) {
728
+ if (iterateFieldsByAction(field, action)) {
729
+ break;
730
+ }
731
+ }
732
+ }
733
+ }
734
+ return;
735
+ };
736
+
737
+ var updateFieldArrayRootError = (errors, error, name) => {
738
+ const existingErrors = get(errors, name);
739
+ const fieldArrayErrors = Array.isArray(existingErrors) ? existingErrors : [];
740
+ set(fieldArrayErrors, ROOT_ERROR_TYPE, error[name]);
741
+ set(errors, name, fieldArrayErrors);
742
+ return errors;
743
+ };
744
+
745
+ var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
746
+
747
+ var isHTMLElement = (value) => {
748
+ if (!isWeb) {
749
+ return false;
750
+ }
751
+ const owner = value ? value.ownerDocument : 0;
752
+ return (value instanceof
753
+ (owner && owner.defaultView ? owner.defaultView.HTMLElement : HTMLElement));
754
+ };
755
+
756
+ var isRadioInput = (element) => element.type === 'radio';
757
+
758
+ var isRegex = (value) => value instanceof RegExp;
759
+
760
+ var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => validateAllFieldCriteria
761
+ ? {
762
+ ...errors[name],
763
+ types: {
764
+ ...(errors[name] && errors[name].types ? errors[name].types : {}),
765
+ [type]: message || true,
766
+ },
767
+ }
768
+ : {};
769
+
770
+ const defaultResult = {
771
+ value: false,
772
+ isValid: false,
773
+ };
774
+ const validResult = { value: true, isValid: true };
775
+ var getCheckboxValue = (options) => {
776
+ if (!Array.isArray(options)) {
777
+ return defaultResult;
778
+ }
779
+ if (options.length > 1) {
780
+ const values = options
781
+ .filter((option) => option && option.checked && !option.disabled)
782
+ .map((option) => option.value);
783
+ return { value: values, isValid: !!values.length };
784
+ }
785
+ const option = options[0];
786
+ if (!option || !option.checked || option.disabled) {
787
+ return defaultResult;
788
+ }
789
+ if (!option.attributes || !('value' in option.attributes)) {
790
+ return validResult;
791
+ }
792
+ return isUndefined(option.value) || option.value === ''
793
+ ? validResult
794
+ : { value: option.value, isValid: true };
795
+ };
796
+
797
+ const defaultReturn = {
798
+ isValid: false,
799
+ value: null,
800
+ };
801
+ var getRadioValue = (options) => Array.isArray(options)
802
+ ? options.reduce((previous, option) => option && option.checked && !option.disabled
803
+ ? {
804
+ isValid: true,
805
+ value: option.value,
806
+ }
807
+ : previous, defaultReturn)
808
+ : defaultReturn;
809
+
810
+ function getValidateError(result, ref, type = 'validate') {
811
+ if (isString(result) ||
812
+ (Array.isArray(result) && result.every(isString)) ||
813
+ (isBoolean(result) && !result)) {
814
+ return {
815
+ type,
816
+ message: isString(result) ? result : '',
817
+ ref,
818
+ };
819
+ }
820
+ }
821
+
822
+ var getValueAndMessage = (validationData) => isObject(validationData) && !isRegex(validationData)
823
+ ? validationData
824
+ : {
825
+ value: validationData,
826
+ message: '',
827
+ };
828
+
829
+ var validateField = async (field, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
830
+ const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
831
+ const inputValue = get(formValues, name);
832
+ if (!mount || disabledFieldNames.has(name)) {
833
+ return {};
834
+ }
835
+ const inputRef = refs ? refs[0] : ref;
836
+ const setCustomValidity = (message) => {
837
+ if (shouldUseNativeValidation && inputRef.reportValidity) {
838
+ const validityMessage = isBoolean(message) ? '' : message || '';
839
+ if (refs) {
840
+ refs.forEach((ref) => ref.setCustomValidity(validityMessage));
841
+ }
842
+ else {
843
+ inputRef.setCustomValidity(validityMessage);
844
+ }
845
+ inputRef.reportValidity();
846
+ }
847
+ };
848
+ const error = {};
849
+ const isRadio = isRadioInput(ref);
850
+ const isCheckBox = isCheckBoxInput(ref);
851
+ const isRadioOrCheckbox = isRadio || isCheckBox;
852
+ const isEmpty = ((valueAsNumber || isFileInput(ref)) &&
853
+ isUndefined(ref.value) &&
854
+ isUndefined(inputValue)) ||
855
+ (isHTMLElement(ref) && ref.value === '') ||
856
+ inputValue === '' ||
857
+ (Array.isArray(inputValue) && !inputValue.length);
858
+ const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
859
+ const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
860
+ const message = exceedMax ? maxLengthMessage : minLengthMessage;
861
+ error[name] = {
862
+ type: exceedMax ? maxType : minType,
863
+ message,
864
+ ref,
865
+ ...appendErrorsCurry(exceedMax ? maxType : minType, message),
866
+ };
867
+ };
868
+ if (isFieldArray
869
+ ? !Array.isArray(inputValue) || !inputValue.length
870
+ : required &&
871
+ ((!isRadioOrCheckbox && (isEmpty || isNullOrUndefined(inputValue))) ||
872
+ (isBoolean(inputValue) && !inputValue) ||
873
+ (isCheckBox && !getCheckboxValue(refs).isValid) ||
874
+ (isRadio && !getRadioValue(refs).isValid))) {
875
+ const { value, message } = isString(required)
876
+ ? { value: !!required, message: required }
877
+ : getValueAndMessage(required);
878
+ if (value) {
879
+ error[name] = {
880
+ type: INPUT_VALIDATION_RULES.required,
881
+ message,
882
+ ref: inputRef,
883
+ ...appendErrorsCurry(INPUT_VALIDATION_RULES.required, message),
884
+ };
885
+ if (!validateAllFieldCriteria) {
886
+ setCustomValidity(message);
887
+ return error;
888
+ }
889
+ }
890
+ }
891
+ if (!isEmpty && (!isNullOrUndefined(min) || !isNullOrUndefined(max))) {
892
+ let exceedMax;
893
+ let exceedMin;
894
+ const maxOutput = getValueAndMessage(max);
895
+ const minOutput = getValueAndMessage(min);
896
+ if (!isNullOrUndefined(inputValue) &&
897
+ !isDateObject(inputValue) &&
898
+ !isNaN(inputValue)) {
899
+ const valueNumber = ref.valueAsNumber ||
900
+ (inputValue ? +inputValue : inputValue);
901
+ if (!isNullOrUndefined(maxOutput.value)) {
902
+ exceedMax = valueNumber > maxOutput.value;
903
+ }
904
+ if (!isNullOrUndefined(minOutput.value)) {
905
+ exceedMin = valueNumber < minOutput.value;
906
+ }
907
+ }
908
+ else {
909
+ const valueDate = ref.valueAsDate || new Date(inputValue);
910
+ const convertTimeToDate = (time) => new Date(new Date().toDateString() + ' ' + time);
911
+ const isTime = ref.type == 'time';
912
+ const isWeek = ref.type == 'week';
913
+ if (isString(maxOutput.value) && inputValue) {
914
+ exceedMax = isTime
915
+ ? convertTimeToDate(inputValue) > convertTimeToDate(maxOutput.value)
916
+ : isWeek
917
+ ? inputValue > maxOutput.value
918
+ : valueDate > new Date(maxOutput.value);
919
+ }
920
+ if (isString(minOutput.value) && inputValue) {
921
+ exceedMin = isTime
922
+ ? convertTimeToDate(inputValue) < convertTimeToDate(minOutput.value)
923
+ : isWeek
924
+ ? inputValue < minOutput.value
925
+ : valueDate < new Date(minOutput.value);
926
+ }
927
+ }
928
+ if (exceedMax || exceedMin) {
929
+ getMinMaxMessage(!!exceedMax, maxOutput.message, minOutput.message, INPUT_VALIDATION_RULES.max, INPUT_VALIDATION_RULES.min);
930
+ if (!validateAllFieldCriteria) {
931
+ setCustomValidity(error[name].message);
932
+ return error;
933
+ }
934
+ }
935
+ }
936
+ if ((maxLength || minLength) &&
937
+ !isEmpty &&
938
+ (isString(inputValue) || (isFieldArray && Array.isArray(inputValue)))) {
939
+ const maxLengthOutput = getValueAndMessage(maxLength);
940
+ const minLengthOutput = getValueAndMessage(minLength);
941
+ const exceedMax = !isNullOrUndefined(maxLengthOutput.value) &&
942
+ inputValue.length > +maxLengthOutput.value;
943
+ const exceedMin = !isNullOrUndefined(minLengthOutput.value) &&
944
+ inputValue.length < +minLengthOutput.value;
945
+ if (exceedMax || exceedMin) {
946
+ getMinMaxMessage(exceedMax, maxLengthOutput.message, minLengthOutput.message);
947
+ if (!validateAllFieldCriteria) {
948
+ setCustomValidity(error[name].message);
949
+ return error;
950
+ }
951
+ }
952
+ }
953
+ if (pattern && !isEmpty && isString(inputValue)) {
954
+ const { value: patternValue, message } = getValueAndMessage(pattern);
955
+ if (isRegex(patternValue) && !inputValue.match(patternValue)) {
956
+ error[name] = {
957
+ type: INPUT_VALIDATION_RULES.pattern,
958
+ message,
959
+ ref,
960
+ ...appendErrorsCurry(INPUT_VALIDATION_RULES.pattern, message),
961
+ };
962
+ if (!validateAllFieldCriteria) {
963
+ setCustomValidity(message);
964
+ return error;
965
+ }
966
+ }
967
+ }
968
+ if (validate) {
969
+ if (isFunction(validate)) {
970
+ const result = await validate(inputValue, formValues);
971
+ const validateError = getValidateError(result, inputRef);
972
+ if (validateError) {
973
+ error[name] = {
974
+ ...validateError,
975
+ ...appendErrorsCurry(INPUT_VALIDATION_RULES.validate, validateError.message),
976
+ };
977
+ if (!validateAllFieldCriteria) {
978
+ setCustomValidity(validateError.message);
979
+ return error;
980
+ }
981
+ }
982
+ }
983
+ else if (isObject(validate)) {
984
+ let validationResult = {};
985
+ for (const key in validate) {
986
+ if (!isEmptyObject(validationResult) && !validateAllFieldCriteria) {
987
+ break;
988
+ }
989
+ const validateError = getValidateError(await validate[key](inputValue, formValues), inputRef, key);
990
+ if (validateError) {
991
+ validationResult = {
992
+ ...validateError,
993
+ ...appendErrorsCurry(key, validateError.message),
994
+ };
995
+ setCustomValidity(validateError.message);
996
+ if (validateAllFieldCriteria) {
997
+ error[name] = validationResult;
998
+ }
999
+ }
1000
+ }
1001
+ if (!isEmptyObject(validationResult)) {
1002
+ error[name] = {
1003
+ ref: inputRef,
1004
+ ...validationResult,
1005
+ };
1006
+ if (!validateAllFieldCriteria) {
1007
+ return error;
1008
+ }
1009
+ }
1010
+ }
1011
+ }
1012
+ const fieldError = error[name];
1013
+ setCustomValidity(fieldError ? fieldError.message : true);
1014
+ return error;
1015
+ };
1016
+
1017
+ var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
1018
+
1019
+ var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
1020
+
1021
+ function baseGet(object, updatePath) {
1022
+ const length = updatePath.length - 1;
1023
+ let index = 0;
1024
+ while (index < length) {
1025
+ if (isNullOrUndefined(object)) {
1026
+ object = undefined;
1027
+ break;
1028
+ }
1029
+ object = object[updatePath[index]];
1030
+ index++;
1031
+ }
1032
+ return object;
1033
+ }
1034
+ function isEmptyArray(obj) {
1035
+ for (const key in obj) {
1036
+ if (obj.hasOwnProperty(key) && !isUndefined(obj[key])) {
1037
+ return false;
1038
+ }
1039
+ }
1040
+ return true;
1041
+ }
1042
+ function unset(object, path) {
1043
+ if (isString(path) && Object.prototype.hasOwnProperty.call(object, path)) {
1044
+ delete object[path];
1045
+ return object;
1046
+ }
1047
+ const paths = Array.isArray(path)
1048
+ ? path
1049
+ : isKey(path)
1050
+ ? [path]
1051
+ : stringToPath(path);
1052
+ if (paths.some((segment) => PROTOTYPE_KEYWORDS.includes(String(segment)))) {
1053
+ return object;
1054
+ }
1055
+ const childObject = paths.length === 1 ? object : baseGet(object, paths);
1056
+ const index = paths.length - 1;
1057
+ const key = paths[index];
1058
+ if (childObject) {
1059
+ delete childObject[key];
1060
+ }
1061
+ if (index !== 0 &&
1062
+ ((isObject(childObject) && isEmptyObject(childObject)) ||
1063
+ (Array.isArray(childObject) && isEmptyArray(childObject)))) {
1064
+ unset(object, paths.slice(0, -1));
1065
+ }
1066
+ return object;
1067
+ }
1068
+
1069
+ const HookFormContext = React__default.createContext(null);
1070
+ HookFormContext.displayName = 'HookFormContext';
1071
+ /**
1072
+ * A provider component that propagates the `useForm` methods to all children components via [React Context](https://react.dev/reference/react/useContext) API. To be used with {@link useFormContext}.
1073
+ *
1074
+ * @remarks
1075
+ * [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
1076
+ *
1077
+ * @param props - all useForm methods
1078
+ *
1079
+ * @example
1080
+ * ```tsx
1081
+ * function App() {
1082
+ * const methods = useForm();
1083
+ * const onSubmit = data => console.log(data);
1084
+ *
1085
+ * return (
1086
+ * <FormProvider {...methods} >
1087
+ * <form onSubmit={methods.handleSubmit(onSubmit)}>
1088
+ * <NestedInput />
1089
+ * <input type="submit" />
1090
+ * </form>
1091
+ * </FormProvider>
1092
+ * );
1093
+ * }
1094
+ *
1095
+ * function NestedInput() {
1096
+ * const { register } = useFormContext(); // retrieve all hook methods
1097
+ * return <input {...register("test")} />;
1098
+ * }
1099
+ * ```
1100
+ */
1101
+ const FormProvider = ({ children, watch, getValues, getErrors, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }) => {
1102
+ const memoizedValue = React__default.useMemo(() => ({
1103
+ watch,
1104
+ getValues,
1105
+ getErrors,
1106
+ getFieldState,
1107
+ setError,
1108
+ clearErrors,
1109
+ setValue,
1110
+ setValues,
1111
+ trigger,
1112
+ formState,
1113
+ resetField,
1114
+ reset,
1115
+ resetDefaultValues,
1116
+ handleSubmit,
1117
+ unregister,
1118
+ control,
1119
+ register,
1120
+ setFocus,
1121
+ subscribe,
1122
+ }), [
1123
+ clearErrors,
1124
+ control,
1125
+ formState,
1126
+ getErrors,
1127
+ getFieldState,
1128
+ getValues,
1129
+ handleSubmit,
1130
+ register,
1131
+ reset,
1132
+ resetDefaultValues,
1133
+ resetField,
1134
+ setError,
1135
+ setFocus,
1136
+ setValue,
1137
+ setValues,
1138
+ subscribe,
1139
+ trigger,
1140
+ unregister,
1141
+ watch,
1142
+ ]);
1143
+ return (React__default.createElement(HookFormContext.Provider, { value: memoizedValue },
1144
+ React__default.createElement(HookFormControlContext.Provider, { value: memoizedValue.control }, children)));
1145
+ };
1146
+
1147
+ var createSubject = () => {
1148
+ let _observers = [];
1149
+ const next = (value) => {
1150
+ for (const observer of _observers) {
1151
+ observer.next && observer.next(value);
646
1152
  }
647
1153
  };
648
1154
  const subscribe = (observer) => {
@@ -686,61 +1192,46 @@ function extractFormValues(fieldsState, formValues) {
686
1192
  return values;
687
1193
  }
688
1194
 
689
- var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
690
-
691
- var isFileInput = (element) => element.type === 'file';
692
-
693
- var isHTMLElement = (value) => {
694
- if (!isWeb) {
1195
+ const hasOwn = (value, key) => value !== null &&
1196
+ isObjectType(value) &&
1197
+ Object.prototype.hasOwnProperty.call(value, key);
1198
+ var has = (object, path) => {
1199
+ if (!path) {
695
1200
  return false;
696
1201
  }
697
- const owner = value ? value.ownerDocument : 0;
698
- return (value instanceof
699
- (owner && owner.defaultView ? owner.defaultView.HTMLElement : HTMLElement));
1202
+ let result = object;
1203
+ for (const key of isKey(path) ? [path] : stringToPath(path)) {
1204
+ if (!hasOwn(result, key)) {
1205
+ // `get` also resolves a path held as a single literal key, eg `{ 'a.b': 1 }`
1206
+ return hasOwn(object, path);
1207
+ }
1208
+ result = result[key];
1209
+ }
1210
+ return true;
700
1211
  };
701
1212
 
702
1213
  var isMultipleSelect = (element) => element.type === `select-multiple`;
703
1214
 
704
- var isRadioInput = (element) => element.type === 'radio';
705
-
706
1215
  var isRadioOrCheckbox = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
707
1216
 
708
1217
  var live = (ref) => isHTMLElement(ref) && ref.isConnected;
709
1218
 
710
- function baseGet(object, updatePath) {
711
- const length = updatePath.slice(0, -1).length;
712
- let index = 0;
713
- while (index < length) {
714
- object = isUndefined(object) ? index++ : object[updatePath[index++]];
715
- }
716
- return object;
1219
+ function isDirtyContainer(value) {
1220
+ return Array.isArray(value) || isObject(value);
717
1221
  }
718
- function isEmptyArray(obj) {
719
- for (const key in obj) {
720
- if (obj.hasOwnProperty(key) && !isUndefined(obj[key])) {
721
- return false;
1222
+ function collectDirtyFieldNames(dirtyTree, cachedDirtyFields, prefix = '', names = []) {
1223
+ for (const key in dirtyTree) {
1224
+ const path = prefix ? `${prefix}.${key}` : key;
1225
+ const value = dirtyTree[key];
1226
+ if (isDirtyContainer(value) &&
1227
+ isDirtyContainer(get(cachedDirtyFields, path))) {
1228
+ collectDirtyFieldNames(value, cachedDirtyFields, path, names);
1229
+ }
1230
+ else {
1231
+ names.push(path);
722
1232
  }
723
1233
  }
724
- return true;
725
- }
726
- function unset(object, path) {
727
- const paths = Array.isArray(path)
728
- ? path
729
- : isKey(path)
730
- ? [path]
731
- : stringToPath(path);
732
- const childObject = paths.length === 1 ? object : baseGet(object, paths);
733
- const index = paths.length - 1;
734
- const key = paths[index];
735
- if (childObject) {
736
- delete childObject[key];
737
- }
738
- if (index !== 0 &&
739
- ((isObject(childObject) && isEmptyObject(childObject)) ||
740
- (Array.isArray(childObject) && isEmptyArray(childObject)))) {
741
- unset(object, paths.slice(0, -1));
742
- }
743
- return object;
1234
+ return names;
744
1235
  }
745
1236
 
746
1237
  var objectHasFunction = (data) => {
@@ -755,12 +1246,33 @@ var objectHasFunction = (data) => {
755
1246
  function isTraversable(value) {
756
1247
  return Array.isArray(value) || (isObject(value) && !objectHasFunction(value));
757
1248
  }
758
- function markFieldsDirty(data, fields = {}) {
1249
+ function isRegisteredLeaf(fieldRef) {
1250
+ return !!(fieldRef && '_f' in fieldRef);
1251
+ }
1252
+ function isEmptyDirtyContainer(value) {
1253
+ return Array.isArray(value)
1254
+ ? !value.some((item) => !isUndefined(item))
1255
+ : !Object.keys(value).length;
1256
+ }
1257
+ function clearDirtyField(container, key) {
1258
+ if (Array.isArray(container)) {
1259
+ container[key] = undefined;
1260
+ }
1261
+ else {
1262
+ delete container[key];
1263
+ }
1264
+ }
1265
+ function markFieldsDirty(data, fields = {}, fieldRefs) {
759
1266
  for (const key in data) {
760
1267
  const value = data[key];
761
- if (isTraversable(value)) {
1268
+ const fieldRef = fieldRefs && fieldRefs[key];
1269
+ if (isTraversable(value) &&
1270
+ (!Array.isArray(value) || !isRegisteredLeaf(fieldRef))) {
762
1271
  fields[key] = Array.isArray(value) ? [] : {};
763
- markFieldsDirty(value, fields[key]);
1272
+ markFieldsDirty(value, fields[key], fieldRef);
1273
+ if (isEmptyDirtyContainer(fields[key])) {
1274
+ clearDirtyField(fields, key);
1275
+ }
764
1276
  }
765
1277
  else if (!isUndefined(value)) {
766
1278
  fields[key] = true;
@@ -768,51 +1280,45 @@ function markFieldsDirty(data, fields = {}) {
768
1280
  }
769
1281
  return fields;
770
1282
  }
771
- function getDirtyFields(data, formValues, dirtyFieldsFromValues) {
1283
+ function getDirtyFields(data, formValues, dirtyFieldsFromValues, fieldRefs) {
772
1284
  if (!dirtyFieldsFromValues) {
773
- dirtyFieldsFromValues = markFieldsDirty(formValues);
1285
+ dirtyFieldsFromValues = markFieldsDirty(formValues, {}, fieldRefs);
774
1286
  }
775
1287
  for (const key in data) {
776
1288
  const value = data[key];
777
- if (isTraversable(value)) {
1289
+ const fieldRef = fieldRefs && fieldRefs[key];
1290
+ if (isTraversable(value) &&
1291
+ (!Array.isArray(value) || !isRegisteredLeaf(fieldRef))) {
778
1292
  if (isUndefined(formValues) || isPrimitive(dirtyFieldsFromValues[key])) {
779
- dirtyFieldsFromValues[key] = markFieldsDirty(value, Array.isArray(value) ? [] : {});
1293
+ dirtyFieldsFromValues[key] = markFieldsDirty(value, Array.isArray(value) ? [] : {}, fieldRef);
780
1294
  }
781
1295
  else {
782
- getDirtyFields(value, isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
1296
+ getDirtyFields(value, isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key], fieldRef);
783
1297
  }
1298
+ if (isEmptyDirtyContainer(dirtyFieldsFromValues[key])) {
1299
+ clearDirtyField(dirtyFieldsFromValues, key);
1300
+ }
1301
+ }
1302
+ else if (deepEqual(value, formValues[key])) {
1303
+ clearDirtyField(dirtyFieldsFromValues, key);
784
1304
  }
785
1305
  else {
786
- const formValue = formValues[key];
787
- dirtyFieldsFromValues[key] = !deepEqual(value, formValue);
1306
+ dirtyFieldsFromValues[key] = true;
788
1307
  }
789
1308
  }
790
1309
  return dirtyFieldsFromValues;
791
1310
  }
792
1311
 
793
- const defaultResult = {
794
- value: false,
795
- isValid: false,
796
- };
797
- const validResult = { value: true, isValid: true };
798
- var getCheckboxValue = (options) => {
799
- if (Array.isArray(options)) {
800
- if (options.length > 1) {
801
- const values = options
802
- .filter((option) => option && option.checked && !option.disabled)
803
- .map((option) => option.value);
804
- return { value: values, isValid: !!values.length };
805
- }
806
- return options[0].checked && !options[0].disabled
807
- ? // @ts-expect-error expected to work in the browser
808
- options[0].attributes && !isUndefined(options[0].attributes.value)
809
- ? isUndefined(options[0].value) || options[0].value === ''
810
- ? validResult
811
- : { value: options[0].value, isValid: true }
812
- : validResult
813
- : defaultResult;
814
- }
815
- return defaultResult;
1312
+ var getFieldArrayItemNames = (names, name) => {
1313
+ const segments = name.split('.');
1314
+ const matches = [];
1315
+ let prefix = segments[0];
1316
+ for (let i = 1; i < segments.length; prefix += '.' + segments[i++]) {
1317
+ if (!isNaN(+segments[i]) && names.has(prefix)) {
1318
+ matches.push(`${prefix}.${segments[i]}`);
1319
+ }
1320
+ }
1321
+ return matches;
816
1322
  };
817
1323
 
818
1324
  var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isUndefined(value)
@@ -829,19 +1335,6 @@ var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isU
829
1335
  ? setValueAs(value)
830
1336
  : value;
831
1337
 
832
- const defaultReturn = {
833
- isValid: false,
834
- value: null,
835
- };
836
- var getRadioValue = (options) => Array.isArray(options)
837
- ? options.reduce((previous, option) => option && option.checked && !option.disabled
838
- ? {
839
- isValid: true,
840
- value: option.value,
841
- }
842
- : previous, defaultReturn)
843
- : defaultReturn;
844
-
845
1338
  function getFieldValue(_f) {
846
1339
  const ref = _f.ref;
847
1340
  if (isFileInput(ref)) {
@@ -856,7 +1349,7 @@ function getFieldValue(_f) {
856
1349
  if (isCheckBoxInput(ref)) {
857
1350
  return getCheckboxValue(_f.refs).value;
858
1351
  }
859
- return getFieldValueAs(isUndefined(ref.value) ? _f.ref.value : ref.value, _f);
1352
+ return getFieldValueAs(ref.value, _f);
860
1353
  }
861
1354
 
862
1355
  var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeValidation) => {
@@ -873,8 +1366,6 @@ var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeVal
873
1366
  };
874
1367
  };
875
1368
 
876
- var isRegex = (value) => value instanceof RegExp;
877
-
878
1369
  var getRuleValue = (rule) => isUndefined(rule)
879
1370
  ? rule
880
1371
  : isRegex(rule)
@@ -885,65 +1376,34 @@ var getRuleValue = (rule) => isUndefined(rule)
885
1376
  : rule.value
886
1377
  : rule;
887
1378
 
888
- var getValidationModes = (mode) => ({
889
- isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
890
- isOnBlur: mode === VALIDATION_MODE.onBlur,
891
- isOnChange: mode === VALIDATION_MODE.onChange,
892
- isOnAll: mode === VALIDATION_MODE.all,
893
- isOnTouch: mode === VALIDATION_MODE.onTouched,
894
- });
895
-
896
1379
  const ASYNC_FUNCTION = 'AsyncFunction';
897
- var hasPromiseValidation = (fieldReference) => !!fieldReference &&
898
- !!fieldReference.validate &&
899
- !!((isFunction(fieldReference.validate) &&
900
- fieldReference.validate.constructor.name === ASYNC_FUNCTION) ||
901
- (isObject(fieldReference.validate) &&
902
- Object.values(fieldReference.validate).find((validateFunction) => validateFunction.constructor.name === ASYNC_FUNCTION)));
1380
+ var hasPromiseValidation = (fieldReference) => {
1381
+ if (!fieldReference || !fieldReference.validate)
1382
+ return false;
1383
+ if (isFunction(fieldReference.validate)) {
1384
+ return fieldReference.validate.constructor.name === ASYNC_FUNCTION;
1385
+ }
1386
+ if (isObject(fieldReference.validate)) {
1387
+ for (const key in fieldReference.validate) {
1388
+ if (fieldReference.validate[key].constructor
1389
+ .name === ASYNC_FUNCTION) {
1390
+ return true;
1391
+ }
1392
+ }
1393
+ }
1394
+ return false;
1395
+ };
903
1396
 
904
1397
  var hasValidation = (options) => options.mount &&
905
1398
  (options.required ||
906
- options.min ||
907
- options.max ||
908
- options.maxLength ||
909
- options.minLength ||
1399
+ (!isUndefined(options.required) && options.required !== false) ||
1400
+ !isUndefined(options.min) ||
1401
+ !isUndefined(options.max) ||
1402
+ !isUndefined(options.maxLength) ||
1403
+ !isUndefined(options.minLength) ||
910
1404
  options.pattern ||
911
1405
  options.validate);
912
1406
 
913
- var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
914
- (_names.watchAll ||
915
- _names.watch.has(name) ||
916
- [..._names.watch].some((watchName) => name.startsWith(watchName) &&
917
- /^\.\w+/.test(name.slice(watchName.length))));
918
-
919
- const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
920
- for (const key of fieldsNames || Object.keys(fields)) {
921
- const field = get(fields, key);
922
- if (field) {
923
- const { _f, ...currentField } = field;
924
- if (_f) {
925
- if (_f.refs && _f.refs[0] && action(_f.refs[0], key) && !abortEarly) {
926
- return true;
927
- }
928
- else if (_f.ref && action(_f.ref, _f.name) && !abortEarly) {
929
- return true;
930
- }
931
- else {
932
- if (iterateFieldsByAction(currentField, action)) {
933
- break;
934
- }
935
- }
936
- }
937
- else if (isObject(currentField)) {
938
- if (iterateFieldsByAction(currentField, action)) {
939
- break;
940
- }
941
- }
942
- }
943
- }
944
- return;
945
- };
946
-
947
1407
  function schemaErrorLookup(errors, _fields, name) {
948
1408
  const error = get(errors, name);
949
1409
  if (error || isKey(name)) {
@@ -958,266 +1418,66 @@ function schemaErrorLookup(errors, _fields, name) {
958
1418
  const field = get(_fields, fieldName);
959
1419
  const foundError = get(errors, fieldName);
960
1420
  if (field && !Array.isArray(field) && name !== fieldName) {
961
- return { name };
962
- }
963
- if (foundError && foundError.type) {
964
- return {
965
- name: fieldName,
966
- error: foundError,
967
- };
968
- }
969
- if (foundError && foundError.root && foundError.root.type) {
970
- return {
971
- name: `${fieldName}.root`,
972
- error: foundError.root,
973
- };
974
- }
975
- names.pop();
976
- }
977
- return {
978
- name,
979
- };
980
- }
981
-
982
- var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
983
- updateFormState(formStateData);
984
- const { name, ...formState } = formStateData;
985
- return (isEmptyObject(formState) ||
986
- Object.keys(formState).length >= Object.keys(_proxyFormState).length ||
987
- Object.keys(formState).find((key) => _proxyFormState[key] ===
988
- (!isRoot || VALIDATION_MODE.all)));
989
- };
990
-
991
- var shouldSubscribeByName = (name, signalName, exact) => !name ||
992
- !signalName ||
993
- name === signalName ||
994
- convertToArrayPayload(name).some((currentName) => currentName &&
995
- (exact
996
- ? currentName === signalName
997
- : currentName.startsWith(signalName) ||
998
- signalName.startsWith(currentName)));
999
-
1000
- var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode) => {
1001
- if (mode.isOnAll) {
1002
- return false;
1003
- }
1004
- else if (!isSubmitted && mode.isOnTouch) {
1005
- return !(isTouched || isBlurEvent);
1006
- }
1007
- else if (isSubmitted ? reValidateMode.isOnBlur : mode.isOnBlur) {
1008
- return !isBlurEvent;
1009
- }
1010
- else if (isSubmitted ? reValidateMode.isOnChange : mode.isOnChange) {
1011
- return isBlurEvent;
1012
- }
1013
- return true;
1014
- };
1015
-
1016
- var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
1017
-
1018
- var updateFieldArrayRootError = (errors, error, name) => {
1019
- const fieldArrayErrors = convertToArrayPayload(get(errors, name));
1020
- set(fieldArrayErrors, 'root', error[name]);
1021
- set(errors, name, fieldArrayErrors);
1022
- return errors;
1023
- };
1024
-
1025
- function getValidateError(result, ref, type = 'validate') {
1026
- if (isString(result) ||
1027
- (Array.isArray(result) && result.every(isString)) ||
1028
- (isBoolean(result) && !result)) {
1029
- return {
1030
- type,
1031
- message: isString(result) ? result : '',
1032
- ref,
1033
- };
1034
- }
1035
- }
1036
-
1037
- var getValueAndMessage = (validationData) => isObject(validationData) && !isRegex(validationData)
1038
- ? validationData
1039
- : {
1040
- value: validationData,
1041
- message: '',
1042
- };
1043
-
1044
- var validateField = async (field, disabledFieldNames, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
1045
- const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, } = field._f;
1046
- const inputValue = get(formValues, name);
1047
- if (!mount || disabledFieldNames.has(name)) {
1048
- return {};
1049
- }
1050
- const inputRef = refs ? refs[0] : ref;
1051
- const setCustomValidity = (message) => {
1052
- if (shouldUseNativeValidation && inputRef.reportValidity) {
1053
- inputRef.setCustomValidity(isBoolean(message) ? '' : message || '');
1054
- inputRef.reportValidity();
1055
- }
1056
- };
1057
- const error = {};
1058
- const isRadio = isRadioInput(ref);
1059
- const isCheckBox = isCheckBoxInput(ref);
1060
- const isRadioOrCheckbox = isRadio || isCheckBox;
1061
- const isEmpty = ((valueAsNumber || isFileInput(ref)) &&
1062
- isUndefined(ref.value) &&
1063
- isUndefined(inputValue)) ||
1064
- (isHTMLElement(ref) && ref.value === '') ||
1065
- inputValue === '' ||
1066
- (Array.isArray(inputValue) && !inputValue.length);
1067
- const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
1068
- const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
1069
- const message = exceedMax ? maxLengthMessage : minLengthMessage;
1070
- error[name] = {
1071
- type: exceedMax ? maxType : minType,
1072
- message,
1073
- ref,
1074
- ...appendErrorsCurry(exceedMax ? maxType : minType, message),
1075
- };
1076
- };
1077
- if (isFieldArray
1078
- ? !Array.isArray(inputValue) || !inputValue.length
1079
- : required &&
1080
- ((!isRadioOrCheckbox && (isEmpty || isNullOrUndefined(inputValue))) ||
1081
- (isBoolean(inputValue) && !inputValue) ||
1082
- (isCheckBox && !getCheckboxValue(refs).isValid) ||
1083
- (isRadio && !getRadioValue(refs).isValid))) {
1084
- const { value, message } = isString(required)
1085
- ? { value: !!required, message: required }
1086
- : getValueAndMessage(required);
1087
- if (value) {
1088
- error[name] = {
1089
- type: INPUT_VALIDATION_RULES.required,
1090
- message,
1091
- ref: inputRef,
1092
- ...appendErrorsCurry(INPUT_VALIDATION_RULES.required, message),
1093
- };
1094
- if (!validateAllFieldCriteria) {
1095
- setCustomValidity(message);
1096
- return error;
1097
- }
1098
- }
1099
- }
1100
- if (!isEmpty && (!isNullOrUndefined(min) || !isNullOrUndefined(max))) {
1101
- let exceedMax;
1102
- let exceedMin;
1103
- const maxOutput = getValueAndMessage(max);
1104
- const minOutput = getValueAndMessage(min);
1105
- if (!isNullOrUndefined(inputValue) && !isNaN(inputValue)) {
1106
- const valueNumber = ref.valueAsNumber ||
1107
- (inputValue ? +inputValue : inputValue);
1108
- if (!isNullOrUndefined(maxOutput.value)) {
1109
- exceedMax = valueNumber > maxOutput.value;
1110
- }
1111
- if (!isNullOrUndefined(minOutput.value)) {
1112
- exceedMin = valueNumber < minOutput.value;
1113
- }
1114
- }
1115
- else {
1116
- const valueDate = ref.valueAsDate || new Date(inputValue);
1117
- const convertTimeToDate = (time) => new Date(new Date().toDateString() + ' ' + time);
1118
- const isTime = ref.type == 'time';
1119
- const isWeek = ref.type == 'week';
1120
- if (isString(maxOutput.value) && inputValue) {
1121
- exceedMax = isTime
1122
- ? convertTimeToDate(inputValue) > convertTimeToDate(maxOutput.value)
1123
- : isWeek
1124
- ? inputValue > maxOutput.value
1125
- : valueDate > new Date(maxOutput.value);
1126
- }
1127
- if (isString(minOutput.value) && inputValue) {
1128
- exceedMin = isTime
1129
- ? convertTimeToDate(inputValue) < convertTimeToDate(minOutput.value)
1130
- : isWeek
1131
- ? inputValue < minOutput.value
1132
- : valueDate < new Date(minOutput.value);
1133
- }
1134
- }
1135
- if (exceedMax || exceedMin) {
1136
- getMinMaxMessage(!!exceedMax, maxOutput.message, minOutput.message, INPUT_VALIDATION_RULES.max, INPUT_VALIDATION_RULES.min);
1137
- if (!validateAllFieldCriteria) {
1138
- setCustomValidity(error[name].message);
1139
- return error;
1140
- }
1141
- }
1142
- }
1143
- if ((maxLength || minLength) &&
1144
- !isEmpty &&
1145
- (isString(inputValue) || (isFieldArray && Array.isArray(inputValue)))) {
1146
- const maxLengthOutput = getValueAndMessage(maxLength);
1147
- const minLengthOutput = getValueAndMessage(minLength);
1148
- const exceedMax = !isNullOrUndefined(maxLengthOutput.value) &&
1149
- inputValue.length > +maxLengthOutput.value;
1150
- const exceedMin = !isNullOrUndefined(minLengthOutput.value) &&
1151
- inputValue.length < +minLengthOutput.value;
1152
- if (exceedMax || exceedMin) {
1153
- getMinMaxMessage(exceedMax, maxLengthOutput.message, minLengthOutput.message);
1154
- if (!validateAllFieldCriteria) {
1155
- setCustomValidity(error[name].message);
1156
- return error;
1157
- }
1158
- }
1159
- }
1160
- if (pattern && !isEmpty && isString(inputValue)) {
1161
- const { value: patternValue, message } = getValueAndMessage(pattern);
1162
- if (isRegex(patternValue) && !inputValue.match(patternValue)) {
1163
- error[name] = {
1164
- type: INPUT_VALIDATION_RULES.pattern,
1165
- message,
1166
- ref,
1167
- ...appendErrorsCurry(INPUT_VALIDATION_RULES.pattern, message),
1168
- };
1169
- if (!validateAllFieldCriteria) {
1170
- setCustomValidity(message);
1171
- return error;
1172
- }
1421
+ return { name };
1173
1422
  }
1174
- }
1175
- if (validate) {
1176
- if (isFunction(validate)) {
1177
- const result = await validate(inputValue, formValues);
1178
- const validateError = getValidateError(result, inputRef);
1179
- if (validateError) {
1180
- error[name] = {
1181
- ...validateError,
1182
- ...appendErrorsCurry(INPUT_VALIDATION_RULES.validate, validateError.message),
1183
- };
1184
- if (!validateAllFieldCriteria) {
1185
- setCustomValidity(validateError.message);
1186
- return error;
1187
- }
1188
- }
1423
+ if (foundError && foundError.type) {
1424
+ return {
1425
+ name: fieldName,
1426
+ error: foundError,
1427
+ };
1189
1428
  }
1190
- else if (isObject(validate)) {
1191
- let validationResult = {};
1192
- for (const key in validate) {
1193
- if (!isEmptyObject(validationResult) && !validateAllFieldCriteria) {
1194
- break;
1195
- }
1196
- const validateError = getValidateError(await validate[key](inputValue, formValues), inputRef, key);
1197
- if (validateError) {
1198
- validationResult = {
1199
- ...validateError,
1200
- ...appendErrorsCurry(key, validateError.message),
1201
- };
1202
- setCustomValidity(validateError.message);
1203
- if (validateAllFieldCriteria) {
1204
- error[name] = validationResult;
1205
- }
1206
- }
1207
- }
1208
- if (!isEmptyObject(validationResult)) {
1209
- error[name] = {
1210
- ref: inputRef,
1211
- ...validationResult,
1212
- };
1213
- if (!validateAllFieldCriteria) {
1214
- return error;
1215
- }
1216
- }
1429
+ if (foundError && foundError.root && foundError.root.type) {
1430
+ return {
1431
+ name: `${fieldName}.root`,
1432
+ error: foundError.root,
1433
+ };
1217
1434
  }
1435
+ names.pop();
1218
1436
  }
1219
- setCustomValidity(true);
1220
- return error;
1437
+ return {
1438
+ name,
1439
+ };
1440
+ }
1441
+
1442
+ var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
1443
+ updateFormState(formStateData);
1444
+ const keys = Object.keys(formStateData).filter((key) => key !== 'name');
1445
+ return (!keys.length ||
1446
+ (isRoot && keys.length >= Object.keys(_proxyFormState).length) ||
1447
+ keys.find((key) => _proxyFormState[key] ===
1448
+ (!isRoot || VALIDATION_MODE.all)));
1449
+ };
1450
+
1451
+ var shouldSubscribeByName = (name, signalName, exact) => !name ||
1452
+ !signalName ||
1453
+ name === signalName ||
1454
+ convertToArrayPayload(name).some((currentName) => currentName &&
1455
+ (exact
1456
+ ? currentName === signalName || currentName.startsWith(signalName + '.')
1457
+ : currentName.startsWith(signalName) ||
1458
+ signalName.startsWith(currentName)));
1459
+
1460
+ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode) => {
1461
+ if (mode.isOnAll) {
1462
+ return false;
1463
+ }
1464
+ else if (!isSubmitted && mode.isOnTouch) {
1465
+ return !(isTouched || isBlurEvent);
1466
+ }
1467
+ else if (isSubmitted ? reValidateMode.isOnBlur : mode.isOnBlur) {
1468
+ return !isBlurEvent;
1469
+ }
1470
+ else if (isSubmitted ? reValidateMode.isOnChange : mode.isOnChange) {
1471
+ return isBlurEvent;
1472
+ }
1473
+ return true;
1474
+ };
1475
+
1476
+ var unsetEmptyArray = (ref, name) => {
1477
+ const array = get(ref, name);
1478
+ !compact(array).length &&
1479
+ !(array === null || array === void 0 ? void 0 : array.root) &&
1480
+ unset(ref, name);
1221
1481
  };
1222
1482
 
1223
1483
  const defaultOptions = {
@@ -1225,24 +1485,36 @@ const defaultOptions = {
1225
1485
  reValidateMode: VALIDATION_MODE.onChange,
1226
1486
  shouldFocusError: true,
1227
1487
  };
1488
+ const FORM_ERROR_TYPE = 'form';
1489
+ const updateDirtyFields = (dirtyFields, nextDirtyFields) => {
1490
+ for (const key in dirtyFields) {
1491
+ if (!(key in nextDirtyFields)) {
1492
+ delete dirtyFields[key];
1493
+ }
1494
+ }
1495
+ Object.assign(dirtyFields, nextDirtyFields);
1496
+ };
1497
+ const DEFAULT_FORM_STATE = {
1498
+ submitCount: 0,
1499
+ isDirty: false,
1500
+ isReady: false,
1501
+ isValidating: false,
1502
+ isSubmitted: false,
1503
+ isSubmitting: false,
1504
+ isSubmitSuccessful: false,
1505
+ isValid: false,
1506
+ touchedFields: {},
1507
+ dirtyFields: {},
1508
+ validatingFields: {},
1509
+ };
1228
1510
  function createFormControl(props = {}) {
1229
1511
  let _options = {
1230
1512
  ...defaultOptions,
1231
1513
  ...props,
1232
1514
  };
1233
1515
  let _formState = {
1234
- submitCount: 0,
1235
- isDirty: false,
1236
- isReady: false,
1516
+ ...cloneObject(DEFAULT_FORM_STATE),
1237
1517
  isLoading: isFunction(_options.defaultValues),
1238
- isValidating: false,
1239
- isSubmitted: false,
1240
- isSubmitting: false,
1241
- isSubmitSuccessful: false,
1242
- isValid: false,
1243
- touchedFields: {},
1244
- dirtyFields: {},
1245
- validatingFields: {},
1246
1518
  errors: _options.errors || {},
1247
1519
  disabled: _options.disabled || false,
1248
1520
  };
@@ -1255,6 +1527,7 @@ function createFormControl(props = {}) {
1255
1527
  : cloneObject(_defaultValues);
1256
1528
  let _state = {
1257
1529
  action: false,
1530
+ actionArrayLengths: new Map(),
1258
1531
  mount: false,
1259
1532
  watch: false,
1260
1533
  keepIsValid: false,
@@ -1265,9 +1538,13 @@ function createFormControl(props = {}) {
1265
1538
  unMount: new Set(),
1266
1539
  array: new Set(),
1267
1540
  watch: new Set(),
1541
+ registerName: new Set(),
1268
1542
  };
1269
- let delayErrorCallback;
1270
- let timer = 0;
1543
+ const delayErrorCallbacks = {};
1544
+ const timers = {};
1545
+ let _valuesSubscriberCount = 0;
1546
+ let _validationModeBeforeSubmit = getValidationModes(_options.mode);
1547
+ let _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
1271
1548
  const defaultProxyFormState = {
1272
1549
  isDirty: false,
1273
1550
  dirtyFields: false,
@@ -1287,10 +1564,11 @@ function createFormControl(props = {}) {
1287
1564
  array: createSubject(),
1288
1565
  state: createSubject(),
1289
1566
  };
1567
+ let _setValidCallId = 0;
1290
1568
  const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
1291
- const debounce = (callback) => (wait) => {
1292
- clearTimeout(timer);
1293
- timer = setTimeout(callback, wait);
1569
+ const debounce = (name, callback) => (wait) => {
1570
+ clearTimeout(timers[name]);
1571
+ timers[name] = setTimeout(callback, wait);
1294
1572
  };
1295
1573
  const _setValid = async (shouldUpdateValid) => {
1296
1574
  if (_state.keepIsValid) {
@@ -1300,15 +1578,20 @@ function createFormControl(props = {}) {
1300
1578
  (_proxyFormState.isValid ||
1301
1579
  _proxySubscribeFormState.isValid ||
1302
1580
  shouldUpdateValid)) {
1581
+ const callId = ++_setValidCallId;
1303
1582
  let isValid;
1304
1583
  if (_options.resolver) {
1305
1584
  isValid = isEmptyObject((await _runSchema()).errors);
1306
- _updateIsValidating();
1585
+ callId === _setValidCallId && _updateIsValidating();
1307
1586
  }
1308
1587
  else {
1309
- isValid = await executeBuiltInValidation(_fields, true);
1588
+ isValid = await executeBuiltInValidation({
1589
+ fields: _fields,
1590
+ onlyCheckValid: true,
1591
+ eventType: EVENTS.VALID,
1592
+ });
1310
1593
  }
1311
- if (isValid !== _formState.isValid) {
1594
+ if (callId === _setValidCallId && isValid !== _formState.isValid) {
1312
1595
  _subjects.state.next({
1313
1596
  isValid,
1314
1597
  });
@@ -1321,7 +1604,7 @@ function createFormControl(props = {}) {
1321
1604
  _proxyFormState.validatingFields ||
1322
1605
  _proxySubscribeFormState.isValidating ||
1323
1606
  _proxySubscribeFormState.validatingFields)) {
1324
- (names || Array.from(_names.mount)).forEach((name) => {
1607
+ (names || _names.mount).forEach((name) => {
1325
1608
  if (name) {
1326
1609
  isValidating
1327
1610
  ? set(_formState.validatingFields, name, isValidating)
@@ -1334,28 +1617,40 @@ function createFormControl(props = {}) {
1334
1617
  });
1335
1618
  }
1336
1619
  };
1620
+ const _updateDirtyFields = () => {
1621
+ _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues, undefined, _fields);
1622
+ };
1337
1623
  const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
1338
1624
  if (args && method && !_options.disabled) {
1339
1625
  _state.action = true;
1340
- if (shouldUpdateFieldsAndState && Array.isArray(get(_fields, name))) {
1341
- const fieldValues = method(get(_fields, name), args.argA, args.argB);
1626
+ const fields = get(_fields, name);
1627
+ if (!_state.actionArrayLengths.has(name)) {
1628
+ _state.actionArrayLengths.set(name, Array.isArray(fields) ? fields.length : 0);
1629
+ }
1630
+ if (shouldUpdateFieldsAndState && Array.isArray(fields)) {
1631
+ const fieldValues = method(fields, args.argA, args.argB);
1342
1632
  shouldSetValues && set(_fields, name, fieldValues);
1343
1633
  }
1344
- if (shouldUpdateFieldsAndState &&
1345
- Array.isArray(get(_formState.errors, name))) {
1346
- const errors = method(get(_formState.errors, name), args.argA, args.argB);
1634
+ const fieldArrayErrors = get(_formState.errors, name);
1635
+ if (shouldUpdateFieldsAndState && Array.isArray(fieldArrayErrors)) {
1636
+ const rootError = fieldArrayErrors.root;
1637
+ const errors = method(fieldArrayErrors, args.argA, args.argB) || fieldArrayErrors;
1638
+ if (rootError) {
1639
+ errors.root = rootError;
1640
+ }
1347
1641
  shouldSetValues && set(_formState.errors, name, errors);
1348
1642
  unsetEmptyArray(_formState.errors, name);
1349
1643
  }
1644
+ const touchedFieldsArray = get(_formState.touchedFields, name);
1350
1645
  if ((_proxyFormState.touchedFields ||
1351
1646
  _proxySubscribeFormState.touchedFields) &&
1352
1647
  shouldUpdateFieldsAndState &&
1353
- Array.isArray(get(_formState.touchedFields, name))) {
1354
- const touchedFields = method(get(_formState.touchedFields, name), args.argA, args.argB);
1648
+ Array.isArray(touchedFieldsArray)) {
1649
+ const touchedFields = method(touchedFieldsArray, args.argA, args.argB);
1355
1650
  shouldSetValues && set(_formState.touchedFields, name, touchedFields);
1356
1651
  }
1357
1652
  if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
1358
- _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
1653
+ _updateDirtyFields();
1359
1654
  }
1360
1655
  _subjects.state.next({
1361
1656
  name,
@@ -1371,6 +1666,7 @@ function createFormControl(props = {}) {
1371
1666
  };
1372
1667
  const updateErrors = (name, error) => {
1373
1668
  set(_formState.errors, name, error);
1669
+ _formState.errors = { ..._formState.errors };
1374
1670
  _subjects.state.next({
1375
1671
  errors: _formState.errors,
1376
1672
  });
@@ -1382,16 +1678,83 @@ function createFormControl(props = {}) {
1382
1678
  isValid: false,
1383
1679
  });
1384
1680
  };
1681
+ const hasExplicitNullIntermediate = (name) => {
1682
+ const segments = isKey(name) ? [name] : stringToPath(name);
1683
+ let formValues = _formValues;
1684
+ let defaultValues = _defaultValues;
1685
+ for (let i = 0; i < segments.length - 1; i++) {
1686
+ const key = segments[i];
1687
+ formValues = isNullOrUndefined(formValues) ? formValues : formValues[key];
1688
+ defaultValues = isNullOrUndefined(defaultValues)
1689
+ ? defaultValues
1690
+ : defaultValues[key];
1691
+ if (formValues === null && defaultValues !== null) {
1692
+ return true;
1693
+ }
1694
+ }
1695
+ return false;
1696
+ };
1697
+ const isStaleArrayIndex = (name) => {
1698
+ if (!_state.actionArrayLengths.size) {
1699
+ return false;
1700
+ }
1701
+ const segments = isKey(name) ? [name] : stringToPath(name);
1702
+ let node = _formValues;
1703
+ let path = '';
1704
+ let ownerDepth = -1;
1705
+ let ownerPreActionLength = 0;
1706
+ for (let i = 0; i < segments.length; i++) {
1707
+ if (isNullOrUndefined(node)) {
1708
+ return false;
1709
+ }
1710
+ const key = segments[i];
1711
+ path = path ? `${path}.${key}` : key;
1712
+ if (Array.isArray(node) && +key >= node.length) {
1713
+ return ownerDepth === -1
1714
+ ? false
1715
+ : i === ownerDepth
1716
+ ? +key < ownerPreActionLength
1717
+ : true;
1718
+ }
1719
+ if (_state.actionArrayLengths.has(path)) {
1720
+ ownerDepth = i + 1;
1721
+ ownerPreActionLength = _state.actionArrayLengths.get(path);
1722
+ }
1723
+ node = node[key];
1724
+ }
1725
+ return false;
1726
+ };
1385
1727
  const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
1386
1728
  const field = get(_fields, name);
1387
1729
  if (field) {
1730
+ if (hasExplicitNullIntermediate(name) || isStaleArrayIndex(name)) {
1731
+ return;
1732
+ }
1733
+ const wasUnsetInFormValues = isUndefined(get(_formValues, name));
1388
1734
  const defaultValue = get(_formValues, name, isUndefined(value) ? get(_defaultValues, name) : value);
1389
1735
  isUndefined(defaultValue) ||
1390
1736
  (ref && ref.defaultChecked) ||
1391
1737
  shouldSkipSetValueAs
1392
1738
  ? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
1393
1739
  : setFieldValue(name, defaultValue);
1394
- _state.mount && !_state.action && _setValid();
1740
+ if (_state.mount && !_state.action) {
1741
+ _setValid();
1742
+ if (wasUnsetInFormValues &&
1743
+ _formState.isDirty &&
1744
+ (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
1745
+ const isDirty = _getDirty();
1746
+ if (!isDirty) {
1747
+ _formState.isDirty = false;
1748
+ _subjects.state.next({ ..._formState });
1749
+ }
1750
+ }
1751
+ if (props.shouldUnregister &&
1752
+ wasUnsetInFormValues &&
1753
+ !isUndefined(get(_formValues, name)) &&
1754
+ isWatched(name, _names)) {
1755
+ _state.watch = true;
1756
+ }
1757
+ }
1395
1758
  }
1396
1759
  };
1397
1760
  const updateTouchAndDirty = (name, fieldValue, isBlurEvent, shouldDirty, shouldRender) => {
@@ -1400,18 +1763,26 @@ function createFormControl(props = {}) {
1400
1763
  const output = {
1401
1764
  name,
1402
1765
  };
1403
- if (!_options.disabled) {
1766
+ // an explicit programmatic update (e.g. setValue with shouldDirty: true)
1767
+ // opts into dirty tracking even when the form is disabled
1768
+ if (!_options.disabled || shouldDirty === true) {
1404
1769
  if (!isBlurEvent || shouldDirty) {
1770
+ const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
1405
1771
  if (_proxyFormState.isDirty || _proxySubscribeFormState.isDirty) {
1406
1772
  isPreviousDirty = _formState.isDirty;
1407
- _formState.isDirty = output.isDirty = _getDirty();
1773
+ _formState.isDirty = output.isDirty =
1774
+ !isCurrentFieldPristine || _getDirty();
1408
1775
  shouldUpdateField = isPreviousDirty !== output.isDirty;
1409
1776
  }
1410
- const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
1411
1777
  isPreviousDirty = !!get(_formState.dirtyFields, name);
1412
- isCurrentFieldPristine
1413
- ? unset(_formState.dirtyFields, name)
1414
- : set(_formState.dirtyFields, name, true);
1778
+ if (isCurrentFieldPristine !== _formState.isDirty) {
1779
+ updateDirtyFields(_formState.dirtyFields, getDirtyFields(_defaultValues, _formValues, undefined, _fields));
1780
+ }
1781
+ else {
1782
+ isCurrentFieldPristine
1783
+ ? unset(_formState.dirtyFields, name)
1784
+ : set(_formState.dirtyFields, name, true);
1785
+ }
1415
1786
  output.dirtyFields = _formState.dirtyFields;
1416
1787
  shouldUpdateField =
1417
1788
  shouldUpdateField ||
@@ -1441,15 +1812,16 @@ function createFormControl(props = {}) {
1441
1812
  isBoolean(isValid) &&
1442
1813
  _formState.isValid !== isValid;
1443
1814
  if (_options.delayError && error) {
1444
- delayErrorCallback = debounce(() => updateErrors(name, error));
1445
- delayErrorCallback(_options.delayError);
1815
+ delayErrorCallbacks[name] = debounce(name, () => updateErrors(name, error));
1816
+ delayErrorCallbacks[name](_options.delayError);
1446
1817
  }
1447
1818
  else {
1448
- clearTimeout(timer);
1449
- delayErrorCallback = null;
1819
+ clearTimeout(timers[name]);
1820
+ delete delayErrorCallbacks[name];
1450
1821
  error
1451
1822
  ? set(_formState.errors, name, error)
1452
1823
  : unset(_formState.errors, name);
1824
+ _formState.errors = { ..._formState.errors };
1453
1825
  }
1454
1826
  if ((error ? !deepEqual(previousFieldError, error) : previousFieldError) ||
1455
1827
  !isEmptyObject(fieldState) ||
@@ -1460,17 +1832,12 @@ function createFormControl(props = {}) {
1460
1832
  errors: _formState.errors,
1461
1833
  name,
1462
1834
  };
1463
- _formState = {
1464
- ..._formState,
1465
- ...updatedFormState,
1466
- };
1467
1835
  _subjects.state.next(updatedFormState);
1468
1836
  }
1469
1837
  };
1470
1838
  const _runSchema = async (name) => {
1471
1839
  _updateIsValidating(name, true);
1472
- const result = await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
1473
- return result;
1840
+ return await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
1474
1841
  };
1475
1842
  const executeSchemaAndUpdateState = async (names) => {
1476
1843
  const { errors } = await _runSchema(names);
@@ -1479,18 +1846,69 @@ function createFormControl(props = {}) {
1479
1846
  for (const name of names) {
1480
1847
  const error = get(errors, name);
1481
1848
  error
1482
- ? set(_formState.errors, name, error)
1849
+ ? _names.array.has(name) &&
1850
+ isObject(error) &&
1851
+ !Object.keys(error).some((key) => !Number.isNaN(Number(key)))
1852
+ ? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
1853
+ : set(_formState.errors, name, error)
1483
1854
  : unset(_formState.errors, name);
1484
1855
  }
1856
+ _formState.errors = { ..._formState.errors };
1485
1857
  }
1486
1858
  else {
1487
1859
  _formState.errors = errors;
1488
1860
  }
1489
1861
  return errors;
1490
1862
  };
1491
- const executeBuiltInValidation = async (fields, shouldOnlyCheckValid, context = {
1863
+ const validateForm = async ({ name, eventType, }) => {
1864
+ if (props.validate) {
1865
+ const result = await props.validate({
1866
+ formValues: _formValues,
1867
+ formState: _formState,
1868
+ name,
1869
+ eventType,
1870
+ });
1871
+ if (isObject(result)) {
1872
+ for (const key in result) {
1873
+ const error = result[key];
1874
+ if (error) {
1875
+ setError(`${FORM_ERROR_TYPE}.${key}`, {
1876
+ message: isString(error.message) ? error.message : '',
1877
+ type: error.type || INPUT_VALIDATION_RULES.validate,
1878
+ });
1879
+ }
1880
+ }
1881
+ }
1882
+ else if (isString(result) || !result) {
1883
+ setError(FORM_ERROR_TYPE, {
1884
+ message: result || '',
1885
+ type: INPUT_VALIDATION_RULES.validate,
1886
+ });
1887
+ }
1888
+ else {
1889
+ clearErrors(FORM_ERROR_TYPE);
1890
+ }
1891
+ return result;
1892
+ }
1893
+ return true;
1894
+ };
1895
+ const executeBuiltInValidation = async ({ fields, onlyCheckValid, name, eventType, context = {
1492
1896
  valid: true,
1493
- }) => {
1897
+ runRootValidation: false,
1898
+ }, }) => {
1899
+ if (props.validate) {
1900
+ context.runRootValidation = true;
1901
+ const result = await validateForm({
1902
+ name,
1903
+ eventType,
1904
+ });
1905
+ if (!result) {
1906
+ context.valid = false;
1907
+ if (onlyCheckValid) {
1908
+ return context.valid;
1909
+ }
1910
+ }
1911
+ }
1494
1912
  for (const name in fields) {
1495
1913
  const field = fields[name];
1496
1914
  if (field) {
@@ -1498,28 +1916,41 @@ function createFormControl(props = {}) {
1498
1916
  if (_f) {
1499
1917
  const isFieldArrayRoot = _names.array.has(_f.name);
1500
1918
  const isPromiseFunction = field._f && hasPromiseValidation(field._f);
1501
- if (isPromiseFunction && _proxyFormState.validatingFields) {
1919
+ const shouldTrackIsValidatingState = _proxyFormState.validatingFields ||
1920
+ _proxyFormState.isValidating ||
1921
+ _proxySubscribeFormState.validatingFields ||
1922
+ _proxySubscribeFormState.isValidating;
1923
+ if (isPromiseFunction && shouldTrackIsValidatingState) {
1502
1924
  _updateIsValidating([_f.name], true);
1503
1925
  }
1504
- const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
1505
- if (isPromiseFunction && _proxyFormState.validatingFields) {
1926
+ const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !onlyCheckValid, isFieldArrayRoot);
1927
+ if (isPromiseFunction && shouldTrackIsValidatingState) {
1506
1928
  _updateIsValidating([_f.name]);
1507
1929
  }
1508
1930
  if (fieldError[_f.name]) {
1509
1931
  context.valid = false;
1510
- if (shouldOnlyCheckValid || props.shouldUseNativeValidation) {
1932
+ if (onlyCheckValid) {
1511
1933
  break;
1512
1934
  }
1513
1935
  }
1514
- !shouldOnlyCheckValid &&
1936
+ !onlyCheckValid &&
1515
1937
  (get(fieldError, _f.name)
1516
1938
  ? isFieldArrayRoot
1517
1939
  ? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
1518
1940
  : set(_formState.errors, _f.name, fieldError[_f.name])
1519
1941
  : unset(_formState.errors, _f.name));
1942
+ if (props.shouldUseNativeValidation && fieldError[_f.name]) {
1943
+ break;
1944
+ }
1520
1945
  }
1521
1946
  !isEmptyObject(fieldValue) &&
1522
- (await executeBuiltInValidation(fieldValue, shouldOnlyCheckValid, context));
1947
+ (await executeBuiltInValidation({
1948
+ context,
1949
+ onlyCheckValid,
1950
+ fields: fieldValue,
1951
+ name: name,
1952
+ eventType,
1953
+ }));
1523
1954
  }
1524
1955
  }
1525
1956
  return context.valid;
@@ -1535,20 +1966,17 @@ function createFormControl(props = {}) {
1535
1966
  }
1536
1967
  _names.unMount = new Set();
1537
1968
  };
1538
- const _getDirty = (name, data) => !_options.disabled &&
1539
- (name && data && set(_formValues, name, data),
1540
- !deepEqual(getValues(), _defaultValues));
1969
+ const _getDirty = (name, data) => (name && data && set(_formValues, name, data),
1970
+ !deepEqual(_state.mount ? _formValues : _defaultValues, _defaultValues));
1541
1971
  const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
1542
1972
  ...(_state.mount
1543
1973
  ? _formValues
1544
- : isUndefined(defaultValue)
1974
+ : isUndefined(defaultValue) || isString(names)
1545
1975
  ? _defaultValues
1546
- : isString(names)
1547
- ? { [names]: defaultValue }
1548
- : defaultValue),
1976
+ : defaultValue),
1549
1977
  }, isGlobal, defaultValue);
1550
1978
  const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
1551
- const setFieldValue = (name, value, options = {}) => {
1979
+ const setFieldValue = (name, value, options = {}, skipClone = false, skipRender = false, skipValueRender = false) => {
1552
1980
  const field = get(_fields, name);
1553
1981
  let fieldValue = value;
1554
1982
  if (field) {
@@ -1586,23 +2014,32 @@ function createFormControl(props = {}) {
1586
2014
  }
1587
2015
  else {
1588
2016
  fieldReference.ref.value = fieldValue;
1589
- if (!fieldReference.ref.type) {
2017
+ if (!fieldReference.ref.type && !skipRender && !skipValueRender) {
1590
2018
  _subjects.state.next({
1591
2019
  name,
1592
- values: cloneObject(_formValues),
2020
+ values: skipClone ? _formValues : cloneObject(_formValues),
1593
2021
  });
1594
2022
  }
1595
2023
  }
1596
2024
  }
1597
2025
  }
1598
2026
  (options.shouldDirty || options.shouldTouch) &&
1599
- updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
1600
- options.shouldValidate && trigger(name);
2027
+ updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, !skipRender);
2028
+ options.shouldValidate &&
2029
+ trigger(name, {
2030
+ delayError: options.delayError,
2031
+ });
1601
2032
  };
1602
- const setValues = (name, value, options) => {
2033
+ const setFieldValues = (name, value, options, skipClone = false, skipRender = false, skipValueRender = false) => {
2034
+ if (_names.array.has(name)) {
2035
+ _subjects.array.next({
2036
+ name,
2037
+ values: skipClone ? _formValues : cloneObject(_formValues),
2038
+ });
2039
+ }
1603
2040
  for (const fieldKey in value) {
1604
2041
  if (!value.hasOwnProperty(fieldKey)) {
1605
- return;
2042
+ continue;
1606
2043
  }
1607
2044
  const fieldValue = value[fieldKey];
1608
2045
  const fieldName = name + '.' + fieldKey;
@@ -1611,49 +2048,89 @@ function createFormControl(props = {}) {
1611
2048
  isObject(fieldValue) ||
1612
2049
  (field && !field._f)) &&
1613
2050
  !isDateObject(fieldValue)
1614
- ? setValues(fieldName, fieldValue, options)
1615
- : setFieldValue(fieldName, fieldValue, options);
2051
+ ? setFieldValues(fieldName, fieldValue, options, skipClone, skipRender, skipValueRender)
2052
+ : setFieldValue(fieldName, fieldValue, options, skipClone, skipRender, skipValueRender);
1616
2053
  }
1617
2054
  };
1618
- const setValue = (name, value, options = {}) => {
2055
+ const _setValue = (name, value, options, skipClone, skipStateEmit = false) => {
1619
2056
  const field = get(_fields, name);
1620
2057
  const isFieldArray = _names.array.has(name);
1621
- const cloneValue = cloneObject(value);
1622
- set(_formValues, name, cloneValue);
2058
+ const cloneValue = skipClone ? value : cloneObject(value);
2059
+ const previousValue = get(_formValues, name);
2060
+ const isValueUnchanged = deepEqual(previousValue, cloneValue);
2061
+ if (!isValueUnchanged) {
2062
+ set(_formValues, name, cloneValue);
2063
+ }
1623
2064
  if (isFieldArray) {
1624
2065
  _subjects.array.next({
1625
2066
  name,
1626
- values: cloneObject(_formValues),
2067
+ values: skipClone ? _formValues : cloneObject(_formValues),
1627
2068
  });
1628
2069
  if ((_proxyFormState.isDirty ||
1629
2070
  _proxyFormState.dirtyFields ||
1630
2071
  _proxySubscribeFormState.isDirty ||
1631
2072
  _proxySubscribeFormState.dirtyFields) &&
1632
2073
  options.shouldDirty) {
1633
- _subjects.state.next({
1634
- name,
1635
- dirtyFields: getDirtyFields(_defaultValues, _formValues),
1636
- isDirty: _getDirty(name, cloneValue),
1637
- });
2074
+ _updateDirtyFields();
2075
+ if (!skipStateEmit) {
2076
+ _subjects.state.next({
2077
+ name,
2078
+ dirtyFields: _formState.dirtyFields,
2079
+ isDirty: _getDirty(name, cloneValue),
2080
+ });
2081
+ }
1638
2082
  }
1639
2083
  }
1640
2084
  else {
1641
- field && !field._f && !isNullOrUndefined(cloneValue)
1642
- ? setValues(name, cloneValue, options)
1643
- : setFieldValue(name, cloneValue, options);
2085
+ const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
2086
+ isEmptyObject(cloneValue);
2087
+ const skipValueRender = !isValueUnchanged && !skipStateEmit;
2088
+ if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
2089
+ setFieldValue(name, cloneValue, options, skipClone, skipStateEmit, skipValueRender);
2090
+ }
2091
+ else {
2092
+ setFieldValues(name, cloneValue, options, skipClone, skipStateEmit, skipValueRender);
2093
+ }
1644
2094
  }
1645
- if (isWatched(name, _names)) {
2095
+ if (!isValueUnchanged && !skipStateEmit) {
2096
+ const watched = isWatched(name, _names);
2097
+ const values = skipClone ? _formValues : cloneObject(_formValues);
1646
2098
  _subjects.state.next({
1647
- ..._formState,
1648
- name,
1649
- values: cloneObject(_formValues),
2099
+ ...(watched && _formState),
2100
+ name: _state.mount || watched ? name : undefined,
2101
+ values,
1650
2102
  });
2103
+ if (!isFieldArray) {
2104
+ for (const itemName of getFieldArrayItemNames(_names.array, name)) {
2105
+ _subjects.state.next({ name: itemName, values });
2106
+ }
2107
+ }
1651
2108
  }
1652
- else {
2109
+ };
2110
+ const setValue = (name, value, options = {}) => _setValue(name, value, options, false);
2111
+ const setValues = (formValues, options = {}) => {
2112
+ const updatedFormValues = isFunction(formValues)
2113
+ ? formValues(_formValues)
2114
+ : formValues;
2115
+ if (!deepEqual(_formValues, updatedFormValues)) {
2116
+ _formValues = {
2117
+ ..._formValues,
2118
+ ...updatedFormValues,
2119
+ };
2120
+ for (const fieldName of _names.mount) {
2121
+ if (has(updatedFormValues, fieldName)) {
2122
+ _setValue(fieldName, get(updatedFormValues, fieldName), options, true, true);
2123
+ }
2124
+ }
1653
2125
  _subjects.state.next({
1654
- name: _state.mount ? name : undefined,
1655
- values: cloneObject(_formValues),
2126
+ ..._formState,
2127
+ name: undefined,
2128
+ type: undefined,
2129
+ ...(_valuesSubscriberCount ? { values: _formValues } : {}),
1656
2130
  });
2131
+ if (options.shouldValidate) {
2132
+ _setValid();
2133
+ }
1657
2134
  }
1658
2135
  };
1659
2136
  const onChange = async (event) => {
@@ -1668,8 +2145,6 @@ function createFormControl(props = {}) {
1668
2145
  (isDateObject(fieldValue) && isNaN(fieldValue.getTime())) ||
1669
2146
  deepEqual(fieldValue, get(_formValues, name, fieldValue));
1670
2147
  };
1671
- const validationModeBeforeSubmit = getValidationModes(_options.mode);
1672
- const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
1673
2148
  if (field) {
1674
2149
  let error;
1675
2150
  let isValid;
@@ -1677,17 +2152,20 @@ function createFormControl(props = {}) {
1677
2152
  ? getFieldValue(field._f)
1678
2153
  : getEventValue(event);
1679
2154
  const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
1680
- const shouldSkipValidation = (!hasValidation(field._f) &&
2155
+ const hasNoValidationEffect = !hasValidation(field._f) &&
2156
+ !props.validate &&
1681
2157
  !_options.resolver &&
1682
2158
  !get(_formState.errors, name) &&
1683
- !field._f.deps) ||
1684
- skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, validationModeAfterSubmit, validationModeBeforeSubmit);
2159
+ !field._f.deps;
2160
+ const shouldSkipValidation = hasNoValidationEffect ||
2161
+ skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, _validationModeAfterSubmit, _validationModeBeforeSubmit);
1685
2162
  const watched = isWatched(name, _names, isBlurEvent);
1686
- set(_formValues, name, fieldValue);
2163
+ set(_formValues, name, cloneObject(fieldValue));
1687
2164
  if (isBlurEvent) {
1688
2165
  if (!target || !target.readOnly) {
1689
2166
  field._f.onBlur && field._f.onBlur(event);
1690
- delayErrorCallback && delayErrorCallback(0);
2167
+ const pendingDelayError = delayErrorCallbacks[name];
2168
+ pendingDelayError && pendingDelayError(0);
1691
2169
  }
1692
2170
  }
1693
2171
  else if (field._f.onChange) {
@@ -1699,10 +2177,13 @@ function createFormControl(props = {}) {
1699
2177
  _subjects.state.next({
1700
2178
  name,
1701
2179
  type: event.type,
1702
- values: cloneObject(_formValues),
2180
+ ...(_valuesSubscriberCount
2181
+ ? { values: cloneObject(_formValues) }
2182
+ : {}),
1703
2183
  });
1704
2184
  if (shouldSkipValidation) {
1705
- if (_proxyFormState.isValid || _proxySubscribeFormState.isValid) {
2185
+ if ((!hasNoValidationEffect || !_formState.isValid) &&
2186
+ (_proxyFormState.isValid || _proxySubscribeFormState.isValid)) {
1706
2187
  if (_options.mode === 'onBlur') {
1707
2188
  if (isBlurEvent) {
1708
2189
  _setValid();
@@ -1715,18 +2196,26 @@ function createFormControl(props = {}) {
1715
2196
  return (shouldRender &&
1716
2197
  _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
1717
2198
  }
2199
+ if (!_options.resolver && props.validate) {
2200
+ await validateForm({
2201
+ name: name,
2202
+ eventType: event.type,
2203
+ });
2204
+ }
1718
2205
  !isBlurEvent && watched && _subjects.state.next({ ..._formState });
1719
2206
  if (_options.resolver) {
1720
2207
  const { errors } = await _runSchema([name]);
1721
2208
  _updateIsValidating([name]);
1722
2209
  _updateIsFieldValueUpdated(fieldValue);
1723
- if (isFieldValueUpdated) {
1724
- const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
1725
- const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
1726
- error = errorLookupResult.error;
1727
- name = errorLookupResult.name;
1728
- isValid = isEmptyObject(errors);
2210
+ if (!isFieldValueUpdated) {
2211
+ !isEmptyObject(fieldState) && _subjects.state.next(fieldState);
2212
+ return;
1729
2213
  }
2214
+ const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
2215
+ const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
2216
+ error = errorLookupResult.error;
2217
+ name = errorLookupResult.name;
2218
+ isValid = isEmptyObject(errors);
1730
2219
  }
1731
2220
  else {
1732
2221
  _updateIsValidating([name], true);
@@ -1739,7 +2228,12 @@ function createFormControl(props = {}) {
1739
2228
  }
1740
2229
  else if (_proxyFormState.isValid ||
1741
2230
  _proxySubscribeFormState.isValid) {
1742
- isValid = await executeBuiltInValidation(_fields, true);
2231
+ isValid = await executeBuiltInValidation({
2232
+ fields: _fields,
2233
+ onlyCheckValid: true,
2234
+ name: name,
2235
+ eventType: event.type,
2236
+ });
1743
2237
  }
1744
2238
  }
1745
2239
  }
@@ -1772,12 +2266,37 @@ function createFormControl(props = {}) {
1772
2266
  else if (name) {
1773
2267
  validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
1774
2268
  const field = get(_fields, fieldName);
1775
- return await executeBuiltInValidation(field && field._f ? { [fieldName]: field } : field);
2269
+ return await executeBuiltInValidation({
2270
+ fields: field && field._f ? { [fieldName]: field } : field,
2271
+ eventType: EVENTS.TRIGGER,
2272
+ });
1776
2273
  }))).every(Boolean);
1777
2274
  !(!validationResult && !_formState.isValid) && _setValid();
1778
2275
  }
1779
2276
  else {
1780
- validationResult = isValid = await executeBuiltInValidation(_fields);
2277
+ validationResult = isValid = await executeBuiltInValidation({
2278
+ fields: _fields,
2279
+ name,
2280
+ eventType: EVENTS.TRIGGER,
2281
+ });
2282
+ }
2283
+ if (options.delayError && _options.delayError && isString(name)) {
2284
+ const error = get(_formState.errors, name);
2285
+ if (error) {
2286
+ unset(_formState.errors, name);
2287
+ delayErrorCallbacks[name] = debounce(name, () => updateErrors(name, error));
2288
+ delayErrorCallbacks[name](_options.delayError);
2289
+ }
2290
+ else {
2291
+ clearTimeout(timers[name]);
2292
+ delete delayErrorCallbacks[name];
2293
+ }
2294
+ }
2295
+ if (options.shouldTouch) {
2296
+ for (const fieldName of name ? fieldNames : _names.mount) {
2297
+ !_names.array.has(fieldName) &&
2298
+ set(_formState.touchedFields, fieldName, true);
2299
+ }
1781
2300
  }
1782
2301
  _subjects.state.next({
1783
2302
  ...(!isString(name) ||
@@ -1786,6 +2305,10 @@ function createFormControl(props = {}) {
1786
2305
  ? {}
1787
2306
  : { name }),
1788
2307
  ...(_options.resolver || !name ? { isValid } : {}),
2308
+ ...(options.shouldTouch &&
2309
+ (_proxyFormState.touchedFields || _proxySubscribeFormState.touchedFields)
2310
+ ? { touchedFields: _formState.touchedFields }
2311
+ : {}),
1789
2312
  errors: _formState.errors,
1790
2313
  });
1791
2314
  options.shouldFocus &&
@@ -1806,19 +2329,26 @@ function createFormControl(props = {}) {
1806
2329
  ? get(values, fieldNames)
1807
2330
  : fieldNames.map((name) => get(values, name));
1808
2331
  };
1809
- const getFieldState = (name, formState) => ({
1810
- invalid: !!get((formState || _formState).errors, name),
1811
- isDirty: !!get((formState || _formState).dirtyFields, name),
1812
- error: get((formState || _formState).errors, name),
1813
- isValidating: !!get(_formState.validatingFields, name),
1814
- isTouched: !!get((formState || _formState).touchedFields, name),
1815
- });
2332
+ const getErrors = (fieldNames) => isUndefined(fieldNames)
2333
+ ? { ..._formState.errors }
2334
+ : isString(fieldNames)
2335
+ ? get(_formState.errors, fieldNames)
2336
+ : fieldNames.map((name) => get(_formState.errors, name));
2337
+ const getFieldState = (name, formState) => {
2338
+ const targetFormState = formState || _formState;
2339
+ const error = get(targetFormState.errors, name);
2340
+ return {
2341
+ invalid: !!error,
2342
+ isDirty: !!get(targetFormState.dirtyFields, name),
2343
+ error,
2344
+ isValidating: !!get(_formState.validatingFields, name),
2345
+ isTouched: !!get(targetFormState.touchedFields, name),
2346
+ };
2347
+ };
1816
2348
  const clearErrors = (name) => {
1817
2349
  const names = name ? convertToArrayPayload(name) : undefined;
1818
2350
  names === null || names === void 0 ? void 0 : names.forEach((inputName) => unset(_formState.errors, inputName));
1819
2351
  if (names) {
1820
- // Emit for each cleared field with the field name so that
1821
- // shouldSubscribeByName can filter and avoid broad re-renders
1822
2352
  names.forEach((inputName) => {
1823
2353
  _subjects.state.next({
1824
2354
  name: inputName,
@@ -1827,16 +2357,15 @@ function createFormControl(props = {}) {
1827
2357
  });
1828
2358
  }
1829
2359
  else {
1830
- // Clear all errors - emit without name to notify all subscribers
2360
+ _formState.errors = {};
1831
2361
  _subjects.state.next({
1832
- errors: {},
2362
+ errors: _formState.errors,
1833
2363
  });
1834
2364
  }
1835
2365
  };
1836
2366
  const setError = (name, error, options) => {
1837
2367
  const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
1838
2368
  const currentError = get(_formState.errors, name) || {};
1839
- // Don't override existing error messages elsewhere in the object tree.
1840
2369
  const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
1841
2370
  set(_formState.errors, name, {
1842
2371
  ...restOfErrorTree,
@@ -1850,25 +2379,60 @@ function createFormControl(props = {}) {
1850
2379
  });
1851
2380
  options && options.shouldFocus && ref && ref.focus && ref.focus();
1852
2381
  };
1853
- const watch = (name, defaultValue) => isFunction(name)
1854
- ? _subjects.state.subscribe({
1855
- next: (payload) => 'values' in payload &&
1856
- name(_getWatch(undefined, defaultValue), payload),
1857
- })
1858
- : _getWatch(name, defaultValue, true);
1859
- const _subscribe = (props) => _subjects.state.subscribe({
1860
- next: (formState) => {
1861
- if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
1862
- shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
1863
- props.callback({
1864
- values: { ..._formValues },
1865
- ..._formState,
1866
- ...formState,
1867
- defaultValues: _defaultValues,
1868
- });
2382
+ const watch = (name, defaultValue) => {
2383
+ if (isFunction(name)) {
2384
+ _valuesSubscriberCount++;
2385
+ const { unsubscribe } = _subjects.state.subscribe({
2386
+ next: (payload) => 'values' in payload &&
2387
+ name(payload.values || _getWatch(undefined, defaultValue), payload),
2388
+ });
2389
+ let called = false;
2390
+ return {
2391
+ unsubscribe: () => {
2392
+ if (called) {
2393
+ return;
2394
+ }
2395
+ called = true;
2396
+ _valuesSubscriberCount--;
2397
+ unsubscribe();
2398
+ },
2399
+ };
2400
+ }
2401
+ return _getWatch(name, defaultValue, true);
2402
+ };
2403
+ const _subscribe = (props) => {
2404
+ var _a;
2405
+ const needsValues = !!((_a = props.formState) === null || _a === void 0 ? void 0 : _a.values);
2406
+ if (needsValues) {
2407
+ _valuesSubscriberCount++;
2408
+ }
2409
+ const { unsubscribe } = _subjects.state.subscribe({
2410
+ next: (formState) => {
2411
+ if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
2412
+ shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
2413
+ const snapshot = { ..._formValues };
2414
+ props.callback({
2415
+ values: snapshot,
2416
+ ..._formState,
2417
+ ...formState,
2418
+ defaultValues: _defaultValues,
2419
+ });
2420
+ }
2421
+ },
2422
+ });
2423
+ if (!needsValues) {
2424
+ return unsubscribe;
2425
+ }
2426
+ let called = false;
2427
+ return () => {
2428
+ if (called) {
2429
+ return;
1869
2430
  }
1870
- },
1871
- }).unsubscribe;
2431
+ called = true;
2432
+ _valuesSubscriberCount--;
2433
+ unsubscribe();
2434
+ };
2435
+ };
1872
2436
  const subscribe = (props) => {
1873
2437
  _state.mount = true;
1874
2438
  _proxySubscribeFormState = {
@@ -1900,12 +2464,13 @@ function createFormControl(props = {}) {
1900
2464
  !options.keepDefaultValue &&
1901
2465
  unset(_defaultValues, fieldName);
1902
2466
  }
1903
- _subjects.state.next({
1904
- values: cloneObject(_formValues),
1905
- });
2467
+ _valuesSubscriberCount &&
2468
+ _subjects.state.next({
2469
+ values: cloneObject(_formValues),
2470
+ });
1906
2471
  _subjects.state.next({
1907
2472
  ..._formState,
1908
- ...(!options.keepDirty ? {} : { isDirty: _getDirty() }),
2473
+ ...(options.keepDirty ? {} : { isDirty: _getDirty() }),
1909
2474
  });
1910
2475
  !options.keepIsValid && _setValid();
1911
2476
  };
@@ -1923,6 +2488,7 @@ function createFormControl(props = {}) {
1923
2488
  const register = (name, options = {}) => {
1924
2489
  let field = get(_fields, name);
1925
2490
  const disabledIsDefined = isBoolean(options.disabled) || isBoolean(_options.disabled);
2491
+ const shouldRevalidateRemount = !_names.registerName.has(name) && field && field._f && !field._f.mount;
1926
2492
  set(_fields, name, {
1927
2493
  ...(field || {}),
1928
2494
  _f: {
@@ -1933,7 +2499,7 @@ function createFormControl(props = {}) {
1933
2499
  },
1934
2500
  });
1935
2501
  _names.mount.add(name);
1936
- if (field) {
2502
+ if (field && !shouldRevalidateRemount) {
1937
2503
  _setDisabledField({
1938
2504
  disabled: isBoolean(options.disabled)
1939
2505
  ? options.disabled
@@ -1963,7 +2529,9 @@ function createFormControl(props = {}) {
1963
2529
  onBlur: onChange,
1964
2530
  ref: (ref) => {
1965
2531
  if (ref) {
2532
+ _names.registerName.add(name);
1966
2533
  register(name, options);
2534
+ _names.registerName.delete(name);
1967
2535
  field = get(_fields, name);
1968
2536
  const fieldRef = isUndefined(ref.value)
1969
2537
  ? ref.querySelectorAll
@@ -1977,20 +2545,23 @@ function createFormControl(props = {}) {
1977
2545
  : fieldRef === field._f.ref) {
1978
2546
  return;
1979
2547
  }
2548
+ const newField = {
2549
+ ...field._f,
2550
+ };
2551
+ if (radioOrCheckbox) {
2552
+ newField.refs = [
2553
+ ...refs.filter(live),
2554
+ fieldRef,
2555
+ ...(Array.isArray(get(_defaultValues, name)) ? [{}] : []),
2556
+ ];
2557
+ newField.ref = { type: fieldRef.type, name };
2558
+ }
2559
+ else {
2560
+ newField.ref = fieldRef;
2561
+ delete newField.refs;
2562
+ }
1980
2563
  set(_fields, name, {
1981
- _f: {
1982
- ...field._f,
1983
- ...(radioOrCheckbox
1984
- ? {
1985
- refs: [
1986
- ...refs.filter(live),
1987
- fieldRef,
1988
- ...(Array.isArray(get(_defaultValues, name)) ? [{}] : []),
1989
- ],
1990
- ref: { type: fieldRef.type, name },
1991
- }
1992
- : { ref: fieldRef }),
1993
- },
2564
+ _f: newField,
1994
2565
  });
1995
2566
  updateValidAndValue(name, false, undefined, fieldRef);
1996
2567
  }
@@ -2007,6 +2578,7 @@ function createFormControl(props = {}) {
2007
2578
  };
2008
2579
  };
2009
2580
  const _focusError = () => _options.shouldFocusError &&
2581
+ !_options.shouldUseNativeValidation &&
2010
2582
  iterateFieldsByAction(_fields, _focusInput, _names.mount);
2011
2583
  const _disableForm = (disabled) => {
2012
2584
  if (isBoolean(disabled)) {
@@ -2025,6 +2597,7 @@ function createFormControl(props = {}) {
2025
2597
  }
2026
2598
  };
2027
2599
  const handleSubmit = (onValid, onInvalid) => async (e) => {
2600
+ let result = undefined;
2028
2601
  let onValidError = undefined;
2029
2602
  if (e) {
2030
2603
  e.preventDefault && e.preventDefault();
@@ -2042,20 +2615,23 @@ function createFormControl(props = {}) {
2042
2615
  fieldValues = cloneObject(values);
2043
2616
  }
2044
2617
  else {
2045
- await executeBuiltInValidation(_fields);
2618
+ await executeBuiltInValidation({
2619
+ fields: _fields,
2620
+ eventType: EVENTS.SUBMIT,
2621
+ });
2046
2622
  }
2047
2623
  if (_names.disabled.size) {
2048
2624
  for (const name of _names.disabled) {
2049
2625
  unset(fieldValues, name);
2050
2626
  }
2051
2627
  }
2052
- unset(_formState.errors, 'root');
2628
+ unset(_formState.errors, ROOT_ERROR_TYPE);
2053
2629
  if (isEmptyObject(_formState.errors)) {
2054
2630
  _subjects.state.next({
2055
2631
  errors: {},
2056
2632
  });
2057
2633
  try {
2058
- await onValid(fieldValues, e);
2634
+ result = await onValid(fieldValues, e);
2059
2635
  }
2060
2636
  catch (error) {
2061
2637
  onValidError = error;
@@ -2078,6 +2654,7 @@ function createFormControl(props = {}) {
2078
2654
  if (onValidError) {
2079
2655
  throw onValidError;
2080
2656
  }
2657
+ return result;
2081
2658
  };
2082
2659
  const resetField = (name, options = {}) => {
2083
2660
  if (get(_fields, name)) {
@@ -2099,7 +2676,7 @@ function createFormControl(props = {}) {
2099
2676
  }
2100
2677
  if (!options.keepError) {
2101
2678
  unset(_formState.errors, name);
2102
- _proxyFormState.isValid && _setValid();
2679
+ _setValid();
2103
2680
  }
2104
2681
  _subjects.state.next({ ..._formState });
2105
2682
  }
@@ -2108,7 +2685,8 @@ function createFormControl(props = {}) {
2108
2685
  const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
2109
2686
  const cloneUpdatedValues = cloneObject(updatedValues);
2110
2687
  const isEmptyResetValues = isEmptyObject(formValues);
2111
- const values = isEmptyResetValues ? _defaultValues : cloneUpdatedValues;
2688
+ const values = cloneUpdatedValues;
2689
+ const fieldRefs = _fields;
2112
2690
  if (!keepStateOptions.keepDefaultValues) {
2113
2691
  _defaultValues = updatedValues;
2114
2692
  }
@@ -2116,9 +2694,9 @@ function createFormControl(props = {}) {
2116
2694
  if (keepStateOptions.keepDirtyValues) {
2117
2695
  const fieldsToCheck = new Set([
2118
2696
  ..._names.mount,
2119
- ...Object.keys(getDirtyFields(_defaultValues, _formValues)),
2697
+ ...collectDirtyFieldNames(getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs), _formState.dirtyFields),
2120
2698
  ]);
2121
- for (const fieldName of Array.from(fieldsToCheck)) {
2699
+ for (const fieldName of fieldsToCheck) {
2122
2700
  const isDirty = get(_formState.dirtyFields, fieldName);
2123
2701
  const existingValue = get(_formValues, fieldName);
2124
2702
  const newValue = get(values, fieldName);
@@ -2157,15 +2735,25 @@ function createFormControl(props = {}) {
2157
2735
  _fields = {};
2158
2736
  }
2159
2737
  }
2160
- _formValues = _options.shouldUnregister
2161
- ? keepStateOptions.keepDefaultValues
2738
+ if (_options.shouldUnregister) {
2739
+ _formValues = keepStateOptions.keepDefaultValues
2162
2740
  ? cloneObject(_defaultValues)
2163
- : {}
2164
- : cloneObject(values);
2741
+ : {};
2742
+ if (keepStateOptions.keepFieldsRef) {
2743
+ for (const fieldName of _names.mount) {
2744
+ set(_formValues, fieldName, get(values, fieldName));
2745
+ }
2746
+ }
2747
+ }
2748
+ else {
2749
+ _formValues = cloneObject(values);
2750
+ }
2165
2751
  _subjects.array.next({
2166
2752
  values: { ...values },
2167
2753
  });
2168
2754
  _subjects.state.next({
2755
+ name: undefined,
2756
+ type: undefined,
2169
2757
  values: { ...values },
2170
2758
  });
2171
2759
  }
@@ -2173,6 +2761,7 @@ function createFormControl(props = {}) {
2173
2761
  mount: keepStateOptions.keepDirtyValues ? _names.mount : new Set(),
2174
2762
  unMount: new Set(),
2175
2763
  array: new Set(),
2764
+ registerName: new Set(),
2176
2765
  disabled: new Set(),
2177
2766
  watch: new Set(),
2178
2767
  watchAll: false,
@@ -2186,9 +2775,7 @@ function createFormControl(props = {}) {
2186
2775
  _state.watch = !!_options.shouldUnregister;
2187
2776
  _state.keepIsValid = !!keepStateOptions.keepIsValid;
2188
2777
  _state.action = false;
2189
- // Clear errors synchronously to prevent validation errors on subsequent submissions
2190
- // This fixes the issue where form.reset() causes validation errors on subsequent
2191
- // submissions in Next.js 16 with Server Actions
2778
+ _state.actionArrayLengths.clear();
2192
2779
  if (!keepStateOptions.keepErrors) {
2193
2780
  _formState.errors = {};
2194
2781
  }
@@ -2200,8 +2787,10 @@ function createFormControl(props = {}) {
2200
2787
  ? false
2201
2788
  : keepStateOptions.keepDirty
2202
2789
  ? _formState.isDirty
2203
- : !!(keepStateOptions.keepDefaultValues &&
2204
- !deepEqual(formValues, _defaultValues)),
2790
+ : keepStateOptions.keepValues
2791
+ ? _getDirty()
2792
+ : !!(keepStateOptions.keepDefaultValues &&
2793
+ !deepEqual(formValues, _defaultValues)),
2205
2794
  isSubmitted: keepStateOptions.keepIsSubmitted
2206
2795
  ? _formState.isSubmitted
2207
2796
  : false,
@@ -2209,10 +2798,10 @@ function createFormControl(props = {}) {
2209
2798
  ? {}
2210
2799
  : keepStateOptions.keepDirtyValues
2211
2800
  ? keepStateOptions.keepDefaultValues && _formValues
2212
- ? getDirtyFields(_defaultValues, _formValues)
2801
+ ? getDirtyFields(_defaultValues, _formValues, undefined, fieldRefs)
2213
2802
  : _formState.dirtyFields
2214
2803
  : keepStateOptions.keepDefaultValues && formValues
2215
- ? getDirtyFields(_defaultValues, formValues)
2804
+ ? getDirtyFields(_defaultValues, formValues, undefined, fieldRefs)
2216
2805
  : keepStateOptions.keepDirty
2217
2806
  ? _formState.dirtyFields
2218
2807
  : {},
@@ -2238,8 +2827,6 @@ function createFormControl(props = {}) {
2238
2827
  ? fieldReference.refs[0]
2239
2828
  : fieldReference.ref;
2240
2829
  if (fieldRef.focus) {
2241
- // Use setTimeout to ensure focus happens after any pending state updates
2242
- // This fixes the issue where setFocus doesn't work immediately after setError
2243
2830
  setTimeout(() => {
2244
2831
  fieldRef.focus();
2245
2832
  options.shouldSelect &&
@@ -2250,11 +2837,18 @@ function createFormControl(props = {}) {
2250
2837
  }
2251
2838
  };
2252
2839
  const _setFormState = (updatedFormState) => {
2840
+ // `name`, `type`, and `values` describe the event that produced this
2841
+ // update, not the form's persisted state (they aren't part of
2842
+ // `FormState`). Merging them in would leak a stale `name`/`type` from
2843
+ // one event into a later, unrelated notification that doesn't specify
2844
+ // its own.
2845
+ const { name, type, values, ...formState } = updatedFormState;
2253
2846
  _formState = {
2254
2847
  ..._formState,
2255
- ...updatedFormState,
2848
+ ...formState,
2256
2849
  };
2257
2850
  };
2851
+ _subjects.state.subscribe({ next: _setFormState });
2258
2852
  const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
2259
2853
  _options.defaultValues().then((values) => {
2260
2854
  reset(values, _options.resetOptions);
@@ -2262,6 +2856,21 @@ function createFormControl(props = {}) {
2262
2856
  isLoading: false,
2263
2857
  });
2264
2858
  });
2859
+ const resetDefaultValues = (values, options = {}) => {
2860
+ _defaultValues = cloneObject(values);
2861
+ if (!options.keepDirty) {
2862
+ const newDirtyFields = getDirtyFields(_defaultValues, _formValues, undefined, _fields);
2863
+ _formState.dirtyFields = newDirtyFields;
2864
+ _formState.isDirty = !isEmptyObject(newDirtyFields);
2865
+ }
2866
+ if (!options.keepIsValid) {
2867
+ _setValid();
2868
+ }
2869
+ _subjects.state.next({
2870
+ ..._formState,
2871
+ defaultValues: _defaultValues,
2872
+ });
2873
+ };
2265
2874
  const methods = {
2266
2875
  control: {
2267
2876
  register,
@@ -2318,6 +2927,8 @@ function createFormControl(props = {}) {
2318
2927
  ..._options,
2319
2928
  ...value,
2320
2929
  };
2930
+ _validationModeBeforeSubmit = getValidationModes(_options.mode);
2931
+ _validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
2321
2932
  },
2322
2933
  },
2323
2934
  subscribe,
@@ -2326,9 +2937,12 @@ function createFormControl(props = {}) {
2326
2937
  handleSubmit,
2327
2938
  watch,
2328
2939
  setValue,
2940
+ setValues,
2329
2941
  getValues,
2942
+ getErrors,
2330
2943
  reset,
2331
2944
  resetField,
2945
+ resetDefaultValues,
2332
2946
  clearErrors,
2333
2947
  unregister,
2334
2948
  setError,
@@ -2373,26 +2987,19 @@ function createFormControl(props = {}) {
2373
2987
  function useForm(props = {}) {
2374
2988
  const _formControl = React__default.useRef(undefined);
2375
2989
  const _values = React__default.useRef(undefined);
2376
- const [formState, updateFormState] = React__default.useState({
2377
- isDirty: false,
2378
- isValidating: false,
2990
+ const _formControlProp = React__default.useRef(props.formControl);
2991
+ const [formState, updateFormState] = React__default.useState(() => ({
2992
+ ...cloneObject(DEFAULT_FORM_STATE),
2379
2993
  isLoading: isFunction(props.defaultValues),
2380
- isSubmitted: false,
2381
- isSubmitting: false,
2382
- isSubmitSuccessful: false,
2383
- isValid: false,
2384
- submitCount: 0,
2385
- dirtyFields: {},
2386
- touchedFields: {},
2387
- validatingFields: {},
2388
2994
  errors: props.errors || {},
2389
2995
  disabled: props.disabled || false,
2390
- isReady: false,
2391
2996
  defaultValues: isFunction(props.defaultValues)
2392
2997
  ? undefined
2393
2998
  : props.defaultValues,
2394
- });
2395
- if (!_formControl.current) {
2999
+ }));
3000
+ if (!_formControl.current ||
3001
+ (props.formControl && _formControlProp.current !== props.formControl)) {
3002
+ _formControlProp.current = props.formControl;
2396
3003
  if (props.formControl) {
2397
3004
  _formControl.current = {
2398
3005
  ...props.formControl,
@@ -2412,10 +3019,19 @@ function useForm(props = {}) {
2412
3019
  }
2413
3020
  const control = _formControl.current.control;
2414
3021
  control._options = props;
3022
+ const { resyncIfNeeded, snapshot } = useResyncOnReconnect();
2415
3023
  useIsomorphicLayoutEffect(() => {
2416
- const sub = control._subscribe({
3024
+ const getCurrentFormState = () => ({
3025
+ ...control._formState,
3026
+ defaultValues: control._defaultValues,
3027
+ });
3028
+ resyncIfNeeded(true, getCurrentFormState, updateFormState);
3029
+ const unsubscribe = control._subscribe({
2417
3030
  formState: control._proxyFormState,
2418
- callback: () => updateFormState({ ...control._formState }),
3031
+ callback: () => updateFormState({
3032
+ ...control._formState,
3033
+ defaultValues: control._defaultValues,
3034
+ }),
2419
3035
  reRenderRoot: true,
2420
3036
  });
2421
3037
  updateFormState((data) => ({
@@ -2423,8 +3039,11 @@ function useForm(props = {}) {
2423
3039
  isReady: true,
2424
3040
  }));
2425
3041
  control._formState.isReady = true;
2426
- return sub;
2427
- }, [control]);
3042
+ return () => {
3043
+ unsubscribe();
3044
+ snapshot(true, getCurrentFormState);
3045
+ };
3046
+ }, [control, resyncIfNeeded, snapshot]);
2428
3047
  React__default.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
2429
3048
  React__default.useEffect(() => {
2430
3049
  if (props.mode) {