UNPKG

96.3 kBJavaScriptView Raw
1import React from 'react';
2
3var isCheckBoxInput = (element) => element.type === 'checkbox';
4
5var isDateObject = (value) => value instanceof Date;
6
7var isNullOrUndefined = (value) => value == null;
8
9const isObjectType = (value) => typeof value === 'object';
10var isObject = (value) => !isNullOrUndefined(value) &&
11 !Array.isArray(value) &&
12 isObjectType(value) &&
13 !isDateObject(value);
14
15var getEventValue = (event) => isObject(event) && event.target
16 ? isCheckBoxInput(event.target)
17 ? event.target.checked
18 : event.target.value
19 : event;
20
21var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
22
23var isNameInFieldArray = (names, name) => names.has(getNodeParentName(name));
24
25var isPlainObject = (tempObject) => {
26 const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
27 return (isObject(prototypeCopy) && prototypeCopy.hasOwnProperty('isPrototypeOf'));
28};
29
30var isWeb = typeof window !== 'undefined' &&
31 typeof window.HTMLElement !== 'undefined' &&
32 typeof document !== 'undefined';
33
34function cloneObject(data) {
35 let copy;
36 const isArray = Array.isArray(data);
37 if (data instanceof Date) {
38 copy = new Date(data);
39 }
40 else if (data instanceof Set) {
41 copy = new Set(data);
42 }
43 else if (!(isWeb && (data instanceof Blob || data instanceof FileList)) &&
44 (isArray || isObject(data))) {
45 copy = isArray ? [] : {};
46 if (!isArray && !isPlainObject(data)) {
47 copy = data;
48 }
49 else {
50 for (const key in data) {
51 if (data.hasOwnProperty(key)) {
52 copy[key] = cloneObject(data[key]);
53 }
54 }
55 }
56 }
57 else {
58 return data;
59 }
60 return copy;
61}
62
63var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
64
65var isUndefined = (val) => val === undefined;
66
67var get = (object, path, defaultValue) => {
68 if (!path || !isObject(object)) {
69 return defaultValue;
70 }
71 const result = compact(path.split(/[,[\].]+?/)).reduce((result, key) => isNullOrUndefined(result) ? result : result[key], object);
72 return isUndefined(result) || result === object
73 ? isUndefined(object[path])
74 ? defaultValue
75 : object[path]
76 : result;
77};
78
79var isBoolean = (value) => typeof value === 'boolean';
80
81var isKey = (value) => /^\w*$/.test(value);
82
83var stringToPath = (input) => compact(input.replace(/["|']|\]/g, '').split(/\.|\[/));
84
85var set = (object, path, value) => {
86 let index = -1;
87 const tempPath = isKey(path) ? [path] : stringToPath(path);
88 const length = tempPath.length;
89 const lastIndex = length - 1;
90 while (++index < length) {
91 const key = tempPath[index];
92 let newValue = value;
93 if (index !== lastIndex) {
94 const objValue = object[key];
95 newValue =
96 isObject(objValue) || Array.isArray(objValue)
97 ? objValue
98 : !isNaN(+tempPath[index + 1])
99 ? []
100 : {};
101 }
102 if (key === '__proto__') {
103 return;
104 }
105 object[key] = newValue;
106 object = object[key];
107 }
108 return object;
109};
110
111const EVENTS = {
112 BLUR: 'blur',
113 FOCUS_OUT: 'focusout',
114 CHANGE: 'change',
115};
116const VALIDATION_MODE = {
117 onBlur: 'onBlur',
118 onChange: 'onChange',
119 onSubmit: 'onSubmit',
120 onTouched: 'onTouched',
121 all: 'all',
122};
123const INPUT_VALIDATION_RULES = {
124 max: 'max',
125 min: 'min',
126 maxLength: 'maxLength',
127 minLength: 'minLength',
128 pattern: 'pattern',
129 required: 'required',
130 validate: 'validate',
131};
132
133const HookFormContext = React.createContext(null);
134/**
135 * This custom hook allows you to access the form context. useFormContext is intended to be used in deeply nested structures, where it would become inconvenient to pass the context as a prop. To be used with {@link FormProvider}.
136 *
137 * @remarks
138 * [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
139 *
140 * @returns return all useForm methods
141 *
142 * @example
143 * ```tsx
144 * function App() {
145 * const methods = useForm();
146 * const onSubmit = data => console.log(data);
147 *
148 * return (
149 * <FormProvider {...methods} >
150 * <form onSubmit={methods.handleSubmit(onSubmit)}>
151 * <NestedInput />
152 * <input type="submit" />
153 * </form>
154 * </FormProvider>
155 * );
156 * }
157 *
158 * function NestedInput() {
159 * const { register } = useFormContext(); // retrieve all hook methods
160 * return <input {...register("test")} />;
161 * }
162 * ```
163 */
164const useFormContext = () => React.useContext(HookFormContext);
165/**
166 * A provider component that propagates the `useForm` methods to all children components via [React Context](https://reactjs.org/docs/context.html) API. To be used with {@link useFormContext}.
167 *
168 * @remarks
169 * [API](https://react-hook-form.com/docs/useformcontext) • [Demo](https://codesandbox.io/s/react-hook-form-v7-form-context-ytudi)
170 *
171 * @param props - all useForm methods
172 *
173 * @example
174 * ```tsx
175 * function App() {
176 * const methods = useForm();
177 * const onSubmit = data => console.log(data);
178 *
179 * return (
180 * <FormProvider {...methods} >
181 * <form onSubmit={methods.handleSubmit(onSubmit)}>
182 * <NestedInput />
183 * <input type="submit" />
184 * </form>
185 * </FormProvider>
186 * );
187 * }
188 *
189 * function NestedInput() {
190 * const { register } = useFormContext(); // retrieve all hook methods
191 * return <input {...register("test")} />;
192 * }
193 * ```
194 */
195const FormProvider = (props) => {
196 const { children, ...data } = props;
197 return (React.createElement(HookFormContext.Provider, { value: data }, children));
198};
199
200var getProxyFormState = (formState, control, localProxyFormState, isRoot = true) => {
201 const result = {
202 defaultValues: control._defaultValues,
203 };
204 for (const key in formState) {
205 Object.defineProperty(result, key, {
206 get: () => {
207 const _key = key;
208 if (control._proxyFormState[_key] !== VALIDATION_MODE.all) {
209 control._proxyFormState[_key] = !isRoot || VALIDATION_MODE.all;
210 }
211 localProxyFormState && (localProxyFormState[_key] = true);
212 return formState[_key];
213 },
214 });
215 }
216 return result;
217};
218
219var isEmptyObject = (value) => isObject(value) && !Object.keys(value).length;
220
221var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, isRoot) => {
222 updateFormState(formStateData);
223 const { name, ...formState } = formStateData;
224 return (isEmptyObject(formState) ||
225 Object.keys(formState).length >= Object.keys(_proxyFormState).length ||
226 Object.keys(formState).find((key) => _proxyFormState[key] ===
227 (!isRoot || VALIDATION_MODE.all)));
228};
229
230var convertToArrayPayload = (value) => (Array.isArray(value) ? value : [value]);
231
232var shouldSubscribeByName = (name, signalName, exact) => !name ||
233 !signalName ||
234 name === signalName ||
235 convertToArrayPayload(name).some((currentName) => currentName &&
236 (exact
237 ? currentName === signalName
238 : currentName.startsWith(signalName) ||
239 signalName.startsWith(currentName)));
240
241function useSubscribe(props) {
242 const _props = React.useRef(props);
243 _props.current = props;
244 React.useEffect(() => {
245 const subscription = !props.disabled &&
246 _props.current.subject &&
247 _props.current.subject.subscribe({
248 next: _props.current.next,
249 });
250 return () => {
251 subscription && subscription.unsubscribe();
252 };
253 }, [props.disabled]);
254}
255
256/**
257 * 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.
258 *
259 * @remarks
260 * [API](https://react-hook-form.com/docs/useformstate) • [Demo](https://codesandbox.io/s/useformstate-75xly)
261 *
262 * @param props - include options on specify fields to subscribe. {@link UseFormStateReturn}
263 *
264 * @example
265 * ```tsx
266 * function App() {
267 * const { register, handleSubmit, control } = useForm({
268 * defaultValues: {
269 * firstName: "firstName"
270 * }});
271 * const { dirtyFields } = useFormState({
272 * control
273 * });
274 * const onSubmit = (data) => console.log(data);
275 *
276 * return (
277 * <form onSubmit={handleSubmit(onSubmit)}>
278 * <input {...register("firstName")} placeholder="First Name" />
279 * {dirtyFields.firstName && <p>Field is dirty.</p>}
280 * <input type="submit" />
281 * </form>
282 * );
283 * }
284 * ```
285 */
286function useFormState(props) {
287 const methods = useFormContext();
288 const { control = methods.control, disabled, name, exact } = props || {};
289 const [formState, updateFormState] = React.useState(control._formState);
290 const _mounted = React.useRef(true);
291 const _localProxyFormState = React.useRef({
292 isDirty: false,
293 isLoading: false,
294 dirtyFields: false,
295 touchedFields: false,
296 validatingFields: false,
297 isValidating: false,
298 isValid: false,
299 errors: false,
300 });
301 const _name = React.useRef(name);
302 _name.current = name;
303 useSubscribe({
304 disabled,
305 next: (value) => _mounted.current &&
306 shouldSubscribeByName(_name.current, value.name, exact) &&
307 shouldRenderFormState(value, _localProxyFormState.current, control._updateFormState) &&
308 updateFormState({
309 ...control._formState,
310 ...value,
311 }),
312 subject: control._subjects.state,
313 });
314 React.useEffect(() => {
315 _mounted.current = true;
316 _localProxyFormState.current.isValid && control._updateValid(true);
317 return () => {
318 _mounted.current = false;
319 };
320 }, [control]);
321 return getProxyFormState(formState, control, _localProxyFormState.current, false);
322}
323
324var isString = (value) => typeof value === 'string';
325
326var generateWatchOutput = (names, _names, formValues, isGlobal, defaultValue) => {
327 if (isString(names)) {
328 isGlobal && _names.watch.add(names);
329 return get(formValues, names, defaultValue);
330 }
331 if (Array.isArray(names)) {
332 return names.map((fieldName) => (isGlobal && _names.watch.add(fieldName), get(formValues, fieldName)));
333 }
334 isGlobal && (_names.watchAll = true);
335 return formValues;
336};
337
338/**
339 * Custom hook to subscribe to field change and isolate re-rendering at the component level.
340 *
341 * @remarks
342 *
343 * [API](https://react-hook-form.com/docs/usewatch) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-usewatch-h9i5e)
344 *
345 * @example
346 * ```tsx
347 * const { control } = useForm();
348 * const values = useWatch({
349 * name: "fieldName"
350 * control,
351 * })
352 * ```
353 */
354function useWatch(props) {
355 const methods = useFormContext();
356 const { control = methods.control, name, defaultValue, disabled, exact, } = props || {};
357 const _name = React.useRef(name);
358 _name.current = name;
359 useSubscribe({
360 disabled,
361 subject: control._subjects.values,
362 next: (formState) => {
363 if (shouldSubscribeByName(_name.current, formState.name, exact)) {
364 updateValue(cloneObject(generateWatchOutput(_name.current, control._names, formState.values || control._formValues, false, defaultValue)));
365 }
366 },
367 });
368 const [value, updateValue] = React.useState(control._getWatch(name, defaultValue));
369 React.useEffect(() => control._removeUnmounted());
370 return value;
371}
372
373/**
374 * Custom hook to work with controlled component, this function provide you with both form and field level state. Re-render is isolated at the hook level.
375 *
376 * @remarks
377 * [API](https://react-hook-form.com/docs/usecontroller) • [Demo](https://codesandbox.io/s/usecontroller-0o8px)
378 *
379 * @param props - the path name to the form field value, and validation rules.
380 *
381 * @returns field properties, field and form state. {@link UseControllerReturn}
382 *
383 * @example
384 * ```tsx
385 * function Input(props) {
386 * const { field, fieldState, formState } = useController(props);
387 * return (
388 * <div>
389 * <input {...field} placeholder={props.name} />
390 * <p>{fieldState.isTouched && "Touched"}</p>
391 * <p>{formState.isSubmitted ? "submitted" : ""}</p>
392 * </div>
393 * );
394 * }
395 * ```
396 */
397function useController(props) {
398 const methods = useFormContext();
399 const { name, disabled, control = methods.control, shouldUnregister } = props;
400 const isArrayField = isNameInFieldArray(control._names.array, name);
401 const value = useWatch({
402 control,
403 name,
404 defaultValue: get(control._formValues, name, get(control._defaultValues, name, props.defaultValue)),
405 exact: true,
406 });
407 const formState = useFormState({
408 control,
409 name,
410 });
411 const _registerProps = React.useRef(control.register(name, {
412 ...props.rules,
413 value,
414 ...(isBoolean(props.disabled) ? { disabled: props.disabled } : {}),
415 }));
416 React.useEffect(() => {
417 const _shouldUnregisterField = control._options.shouldUnregister || shouldUnregister;
418 const updateMounted = (name, value) => {
419 const field = get(control._fields, name);
420 if (field && field._f) {
421 field._f.mount = value;
422 }
423 };
424 updateMounted(name, true);
425 if (_shouldUnregisterField) {
426 const value = cloneObject(get(control._options.defaultValues, name));
427 set(control._defaultValues, name, value);
428 if (isUndefined(get(control._formValues, name))) {
429 set(control._formValues, name, value);
430 }
431 }
432 return () => {
433 (isArrayField
434 ? _shouldUnregisterField && !control._state.action
435 : _shouldUnregisterField)
436 ? control.unregister(name)
437 : updateMounted(name, false);
438 };
439 }, [name, control, isArrayField, shouldUnregister]);
440 React.useEffect(() => {
441 if (get(control._fields, name)) {
442 control._updateDisabledField({
443 disabled,
444 fields: control._fields,
445 name,
446 value: get(control._fields, name)._f.value,
447 });
448 }
449 }, [disabled, name, control]);
450 return {
451 field: {
452 name,
453 value,
454 ...(isBoolean(disabled) || formState.disabled
455 ? { disabled: formState.disabled || disabled }
456 : {}),
457 onChange: React.useCallback((event) => _registerProps.current.onChange({
458 target: {
459 value: getEventValue(event),
460 name: name,
461 },
462 type: EVENTS.CHANGE,
463 }), [name]),
464 onBlur: React.useCallback(() => _registerProps.current.onBlur({
465 target: {
466 value: get(control._formValues, name),
467 name: name,
468 },
469 type: EVENTS.BLUR,
470 }), [name, control]),
471 ref: (elm) => {
472 const field = get(control._fields, name);
473 if (field && elm) {
474 field._f.ref = {
475 focus: () => elm.focus(),
476 select: () => elm.select(),
477 setCustomValidity: (message) => elm.setCustomValidity(message),
478 reportValidity: () => elm.reportValidity(),
479 };
480 }
481 },
482 },
483 formState,
484 fieldState: Object.defineProperties({}, {
485 invalid: {
486 enumerable: true,
487 get: () => !!get(formState.errors, name),
488 },
489 isDirty: {
490 enumerable: true,
491 get: () => !!get(formState.dirtyFields, name),
492 },
493 isTouched: {
494 enumerable: true,
495 get: () => !!get(formState.touchedFields, name),
496 },
497 isValidating: {
498 enumerable: true,
499 get: () => !!get(formState.validatingFields, name),
500 },
501 error: {
502 enumerable: true,
503 get: () => get(formState.errors, name),
504 },
505 }),
506 };
507}
508
509/**
510 * Component based on `useController` hook to work with controlled component.
511 *
512 * @remarks
513 * [API](https://react-hook-form.com/docs/usecontroller/controller) • [Demo](https://codesandbox.io/s/react-hook-form-v6-controller-ts-jwyzw) • [Video](https://www.youtube.com/watch?v=N2UNk_UCVyA)
514 *
515 * @param props - the path name to the form field value, and validation rules.
516 *
517 * @returns provide field handler functions, field and form state.
518 *
519 * @example
520 * ```tsx
521 * function App() {
522 * const { control } = useForm<FormValues>({
523 * defaultValues: {
524 * test: ""
525 * }
526 * });
527 *
528 * return (
529 * <form>
530 * <Controller
531 * control={control}
532 * name="test"
533 * render={({ field: { onChange, onBlur, value, ref }, formState, fieldState }) => (
534 * <>
535 * <input
536 * onChange={onChange} // send value to hook form
537 * onBlur={onBlur} // notify when input is touched
538 * value={value} // return updated value
539 * ref={ref} // set ref for focus management
540 * />
541 * <p>{formState.isSubmitted ? "submitted" : ""}</p>
542 * <p>{fieldState.isTouched ? "touched" : ""}</p>
543 * </>
544 * )}
545 * />
546 * </form>
547 * );
548 * }
549 * ```
550 */
551const Controller = (props) => props.render(useController(props));
552
553const POST_REQUEST = 'post';
554/**
555 * Form component to manage submission.
556 *
557 * @param props - to setup submission detail. {@link FormProps}
558 *
559 * @returns form component or headless render prop.
560 *
561 * @example
562 * ```tsx
563 * function App() {
564 * const { control, formState: { errors } } = useForm();
565 *
566 * return (
567 * <Form action="/api" control={control}>
568 * <input {...register("name")} />
569 * <p>{errors?.root?.server && 'Server error'}</p>
570 * <button>Submit</button>
571 * </Form>
572 * );
573 * }
574 * ```
575 */
576function Form(props) {
577 const methods = useFormContext();
578 const [mounted, setMounted] = React.useState(false);
579 const { control = methods.control, onSubmit, children, action, method = POST_REQUEST, headers, encType, onError, render, onSuccess, validateStatus, ...rest } = props;
580 const submit = async (event) => {
581 let hasError = false;
582 let type = '';
583 await control.handleSubmit(async (data) => {
584 const formData = new FormData();
585 let formDataJson = '';
586 try {
587 formDataJson = JSON.stringify(data);
588 }
589 catch (_a) { }
590 for (const name of control._names.mount) {
591 formData.append(name, get(data, name));
592 }
593 if (onSubmit) {
594 await onSubmit({
595 data,
596 event,
597 method,
598 formData,
599 formDataJson,
600 });
601 }
602 if (action) {
603 try {
604 const shouldStringifySubmissionData = [
605 headers && headers['Content-Type'],
606 encType,
607 ].some((value) => value && value.includes('json'));
608 const response = await fetch(action, {
609 method,
610 headers: {
611 ...headers,
612 ...(encType ? { 'Content-Type': encType } : {}),
613 },
614 body: shouldStringifySubmissionData ? formDataJson : formData,
615 });
616 if (response &&
617 (validateStatus
618 ? !validateStatus(response.status)
619 : response.status < 200 || response.status >= 300)) {
620 hasError = true;
621 onError && onError({ response });
622 type = String(response.status);
623 }
624 else {
625 onSuccess && onSuccess({ response });
626 }
627 }
628 catch (error) {
629 hasError = true;
630 onError && onError({ error });
631 }
632 }
633 })(event);
634 if (hasError && props.control) {
635 props.control._subjects.state.next({
636 isSubmitSuccessful: false,
637 });
638 props.control.setError('root.server', {
639 type,
640 });
641 }
642 };
643 React.useEffect(() => {
644 setMounted(true);
645 }, []);
646 return render ? (React.createElement(React.Fragment, null, render({
647 submit,
648 }))) : (React.createElement("form", { noValidate: mounted, action: action, method: method, encType: encType, onSubmit: submit, ...rest }, children));
649}
650
651var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => validateAllFieldCriteria
652 ? {
653 ...errors[name],
654 types: {
655 ...(errors[name] && errors[name].types ? errors[name].types : {}),
656 [type]: message || true,
657 },
658 }
659 : {};
660
661var generateId = () => {
662 const d = typeof performance === 'undefined' ? Date.now() : performance.now() * 1000;
663 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
664 const r = (Math.random() * 16 + d) % 16 | 0;
665 return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
666 });
667};
668
669var getFocusFieldName = (name, index, options = {}) => options.shouldFocus || isUndefined(options.shouldFocus)
670 ? options.focusName ||
671 `${name}.${isUndefined(options.focusIndex) ? index : options.focusIndex}.`
672 : '';
673
674var getValidationModes = (mode) => ({
675 isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit,
676 isOnBlur: mode === VALIDATION_MODE.onBlur,
677 isOnChange: mode === VALIDATION_MODE.onChange,
678 isOnAll: mode === VALIDATION_MODE.all,
679 isOnTouch: mode === VALIDATION_MODE.onTouched,
680});
681
682var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
683 (_names.watchAll ||
684 _names.watch.has(name) ||
685 [..._names.watch].some((watchName) => name.startsWith(watchName) &&
686 /^\.\w+/.test(name.slice(watchName.length))));
687
688const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
689 for (const key of fieldsNames || Object.keys(fields)) {
690 const field = get(fields, key);
691 if (field) {
692 const { _f, ...currentField } = field;
693 if (_f) {
694 if (_f.refs && _f.refs[0] && action(_f.refs[0], key) && !abortEarly) {
695 break;
696 }
697 else if (_f.ref && action(_f.ref, _f.name) && !abortEarly) {
698 break;
699 }
700 else {
701 iterateFieldsByAction(currentField, action);
702 }
703 }
704 else if (isObject(currentField)) {
705 iterateFieldsByAction(currentField, action);
706 }
707 }
708 }
709};
710
711var updateFieldArrayRootError = (errors, error, name) => {
712 const fieldArrayErrors = convertToArrayPayload(get(errors, name));
713 set(fieldArrayErrors, 'root', error[name]);
714 set(errors, name, fieldArrayErrors);
715 return errors;
716};
717
718var isFileInput = (element) => element.type === 'file';
719
720var isFunction = (value) => typeof value === 'function';
721
722var isHTMLElement = (value) => {
723 if (!isWeb) {
724 return false;
725 }
726 const owner = value ? value.ownerDocument : 0;
727 return (value instanceof
728 (owner && owner.defaultView ? owner.defaultView.HTMLElement : HTMLElement));
729};
730
731var isMessage = (value) => isString(value);
732
733var isRadioInput = (element) => element.type === 'radio';
734
735var isRegex = (value) => value instanceof RegExp;
736
737const defaultResult = {
738 value: false,
739 isValid: false,
740};
741const validResult = { value: true, isValid: true };
742var getCheckboxValue = (options) => {
743 if (Array.isArray(options)) {
744 if (options.length > 1) {
745 const values = options
746 .filter((option) => option && option.checked && !option.disabled)
747 .map((option) => option.value);
748 return { value: values, isValid: !!values.length };
749 }
750 return options[0].checked && !options[0].disabled
751 ? // @ts-expect-error expected to work in the browser
752 options[0].attributes && !isUndefined(options[0].attributes.value)
753 ? isUndefined(options[0].value) || options[0].value === ''
754 ? validResult
755 : { value: options[0].value, isValid: true }
756 : validResult
757 : defaultResult;
758 }
759 return defaultResult;
760};
761
762const defaultReturn = {
763 isValid: false,
764 value: null,
765};
766var getRadioValue = (options) => Array.isArray(options)
767 ? options.reduce((previous, option) => option && option.checked && !option.disabled
768 ? {
769 isValid: true,
770 value: option.value,
771 }
772 : previous, defaultReturn)
773 : defaultReturn;
774
775function getValidateError(result, ref, type = 'validate') {
776 if (isMessage(result) ||
777 (Array.isArray(result) && result.every(isMessage)) ||
778 (isBoolean(result) && !result)) {
779 return {
780 type,
781 message: isMessage(result) ? result : '',
782 ref,
783 };
784 }
785}
786
787var getValueAndMessage = (validationData) => isObject(validationData) && !isRegex(validationData)
788 ? validationData
789 : {
790 value: validationData,
791 message: '',
792 };
793
794var validateField = async (field, formValues, validateAllFieldCriteria, shouldUseNativeValidation, isFieldArray) => {
795 const { ref, refs, required, maxLength, minLength, min, max, pattern, validate, name, valueAsNumber, mount, disabled, } = field._f;
796 const inputValue = get(formValues, name);
797 if (!mount || disabled) {
798 return {};
799 }
800 const inputRef = refs ? refs[0] : ref;
801 const setCustomValidity = (message) => {
802 if (shouldUseNativeValidation && inputRef.reportValidity) {
803 inputRef.setCustomValidity(isBoolean(message) ? '' : message || '');
804 inputRef.reportValidity();
805 }
806 };
807 const error = {};
808 const isRadio = isRadioInput(ref);
809 const isCheckBox = isCheckBoxInput(ref);
810 const isRadioOrCheckbox = isRadio || isCheckBox;
811 const isEmpty = ((valueAsNumber || isFileInput(ref)) &&
812 isUndefined(ref.value) &&
813 isUndefined(inputValue)) ||
814 (isHTMLElement(ref) && ref.value === '') ||
815 inputValue === '' ||
816 (Array.isArray(inputValue) && !inputValue.length);
817 const appendErrorsCurry = appendErrors.bind(null, name, validateAllFieldCriteria, error);
818 const getMinMaxMessage = (exceedMax, maxLengthMessage, minLengthMessage, maxType = INPUT_VALIDATION_RULES.maxLength, minType = INPUT_VALIDATION_RULES.minLength) => {
819 const message = exceedMax ? maxLengthMessage : minLengthMessage;
820 error[name] = {
821 type: exceedMax ? maxType : minType,
822 message,
823 ref,
824 ...appendErrorsCurry(exceedMax ? maxType : minType, message),
825 };
826 };
827 if (isFieldArray
828 ? !Array.isArray(inputValue) || !inputValue.length
829 : required &&
830 ((!isRadioOrCheckbox && (isEmpty || isNullOrUndefined(inputValue))) ||
831 (isBoolean(inputValue) && !inputValue) ||
832 (isCheckBox && !getCheckboxValue(refs).isValid) ||
833 (isRadio && !getRadioValue(refs).isValid))) {
834 const { value, message } = isMessage(required)
835 ? { value: !!required, message: required }
836 : getValueAndMessage(required);
837 if (value) {
838 error[name] = {
839 type: INPUT_VALIDATION_RULES.required,
840 message,
841 ref: inputRef,
842 ...appendErrorsCurry(INPUT_VALIDATION_RULES.required, message),
843 };
844 if (!validateAllFieldCriteria) {
845 setCustomValidity(message);
846 return error;
847 }
848 }
849 }
850 if (!isEmpty && (!isNullOrUndefined(min) || !isNullOrUndefined(max))) {
851 let exceedMax;
852 let exceedMin;
853 const maxOutput = getValueAndMessage(max);
854 const minOutput = getValueAndMessage(min);
855 if (!isNullOrUndefined(inputValue) && !isNaN(inputValue)) {
856 const valueNumber = ref.valueAsNumber ||
857 (inputValue ? +inputValue : inputValue);
858 if (!isNullOrUndefined(maxOutput.value)) {
859 exceedMax = valueNumber > maxOutput.value;
860 }
861 if (!isNullOrUndefined(minOutput.value)) {
862 exceedMin = valueNumber < minOutput.value;
863 }
864 }
865 else {
866 const valueDate = ref.valueAsDate || new Date(inputValue);
867 const convertTimeToDate = (time) => new Date(new Date().toDateString() + ' ' + time);
868 const isTime = ref.type == 'time';
869 const isWeek = ref.type == 'week';
870 if (isString(maxOutput.value) && inputValue) {
871 exceedMax = isTime
872 ? convertTimeToDate(inputValue) > convertTimeToDate(maxOutput.value)
873 : isWeek
874 ? inputValue > maxOutput.value
875 : valueDate > new Date(maxOutput.value);
876 }
877 if (isString(minOutput.value) && inputValue) {
878 exceedMin = isTime
879 ? convertTimeToDate(inputValue) < convertTimeToDate(minOutput.value)
880 : isWeek
881 ? inputValue < minOutput.value
882 : valueDate < new Date(minOutput.value);
883 }
884 }
885 if (exceedMax || exceedMin) {
886 getMinMaxMessage(!!exceedMax, maxOutput.message, minOutput.message, INPUT_VALIDATION_RULES.max, INPUT_VALIDATION_RULES.min);
887 if (!validateAllFieldCriteria) {
888 setCustomValidity(error[name].message);
889 return error;
890 }
891 }
892 }
893 if ((maxLength || minLength) &&
894 !isEmpty &&
895 (isString(inputValue) || (isFieldArray && Array.isArray(inputValue)))) {
896 const maxLengthOutput = getValueAndMessage(maxLength);
897 const minLengthOutput = getValueAndMessage(minLength);
898 const exceedMax = !isNullOrUndefined(maxLengthOutput.value) &&
899 inputValue.length > +maxLengthOutput.value;
900 const exceedMin = !isNullOrUndefined(minLengthOutput.value) &&
901 inputValue.length < +minLengthOutput.value;
902 if (exceedMax || exceedMin) {
903 getMinMaxMessage(exceedMax, maxLengthOutput.message, minLengthOutput.message);
904 if (!validateAllFieldCriteria) {
905 setCustomValidity(error[name].message);
906 return error;
907 }
908 }
909 }
910 if (pattern && !isEmpty && isString(inputValue)) {
911 const { value: patternValue, message } = getValueAndMessage(pattern);
912 if (isRegex(patternValue) && !inputValue.match(patternValue)) {
913 error[name] = {
914 type: INPUT_VALIDATION_RULES.pattern,
915 message,
916 ref,
917 ...appendErrorsCurry(INPUT_VALIDATION_RULES.pattern, message),
918 };
919 if (!validateAllFieldCriteria) {
920 setCustomValidity(message);
921 return error;
922 }
923 }
924 }
925 if (validate) {
926 if (isFunction(validate)) {
927 const result = await validate(inputValue, formValues);
928 const validateError = getValidateError(result, inputRef);
929 if (validateError) {
930 error[name] = {
931 ...validateError,
932 ...appendErrorsCurry(INPUT_VALIDATION_RULES.validate, validateError.message),
933 };
934 if (!validateAllFieldCriteria) {
935 setCustomValidity(validateError.message);
936 return error;
937 }
938 }
939 }
940 else if (isObject(validate)) {
941 let validationResult = {};
942 for (const key in validate) {
943 if (!isEmptyObject(validationResult) && !validateAllFieldCriteria) {
944 break;
945 }
946 const validateError = getValidateError(await validate[key](inputValue, formValues), inputRef, key);
947 if (validateError) {
948 validationResult = {
949 ...validateError,
950 ...appendErrorsCurry(key, validateError.message),
951 };
952 setCustomValidity(validateError.message);
953 if (validateAllFieldCriteria) {
954 error[name] = validationResult;
955 }
956 }
957 }
958 if (!isEmptyObject(validationResult)) {
959 error[name] = {
960 ref: inputRef,
961 ...validationResult,
962 };
963 if (!validateAllFieldCriteria) {
964 return error;
965 }
966 }
967 }
968 }
969 setCustomValidity(true);
970 return error;
971};
972
973var appendAt = (data, value) => [
974 ...data,
975 ...convertToArrayPayload(value),
976];
977
978var fillEmptyArray = (value) => Array.isArray(value) ? value.map(() => undefined) : undefined;
979
980function insert(data, index, value) {
981 return [
982 ...data.slice(0, index),
983 ...convertToArrayPayload(value),
984 ...data.slice(index),
985 ];
986}
987
988var moveArrayAt = (data, from, to) => {
989 if (!Array.isArray(data)) {
990 return [];
991 }
992 if (isUndefined(data[to])) {
993 data[to] = undefined;
994 }
995 data.splice(to, 0, data.splice(from, 1)[0]);
996 return data;
997};
998
999var prependAt = (data, value) => [
1000 ...convertToArrayPayload(value),
1001 ...convertToArrayPayload(data),
1002];
1003
1004function removeAtIndexes(data, indexes) {
1005 let i = 0;
1006 const temp = [...data];
1007 for (const index of indexes) {
1008 temp.splice(index - i, 1);
1009 i++;
1010 }
1011 return compact(temp).length ? temp : [];
1012}
1013var removeArrayAt = (data, index) => isUndefined(index)
1014 ? []
1015 : removeAtIndexes(data, convertToArrayPayload(index).sort((a, b) => a - b));
1016
1017var swapArrayAt = (data, indexA, indexB) => {
1018 [data[indexA], data[indexB]] = [data[indexB], data[indexA]];
1019};
1020
1021function baseGet(object, updatePath) {
1022 const length = updatePath.slice(0, -1).length;
1023 let index = 0;
1024 while (index < length) {
1025 object = isUndefined(object) ? index++ : object[updatePath[index++]];
1026 }
1027 return object;
1028}
1029function isEmptyArray(obj) {
1030 for (const key in obj) {
1031 if (obj.hasOwnProperty(key) && !isUndefined(obj[key])) {
1032 return false;
1033 }
1034 }
1035 return true;
1036}
1037function unset(object, path) {
1038 const paths = Array.isArray(path)
1039 ? path
1040 : isKey(path)
1041 ? [path]
1042 : stringToPath(path);
1043 const childObject = paths.length === 1 ? object : baseGet(object, paths);
1044 const index = paths.length - 1;
1045 const key = paths[index];
1046 if (childObject) {
1047 delete childObject[key];
1048 }
1049 if (index !== 0 &&
1050 ((isObject(childObject) && isEmptyObject(childObject)) ||
1051 (Array.isArray(childObject) && isEmptyArray(childObject)))) {
1052 unset(object, paths.slice(0, -1));
1053 }
1054 return object;
1055}
1056
1057var updateAt = (fieldValues, index, value) => {
1058 fieldValues[index] = value;
1059 return fieldValues;
1060};
1061
1062/**
1063 * A custom hook that exposes convenient methods to perform operations with a list of dynamic inputs that need to be appended, updated, removed etc. • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn) • [Video](https://youtu.be/4MrbfGSFY2A)
1064 *
1065 * @remarks
1066 * [API](https://react-hook-form.com/docs/usefieldarray) • [Demo](https://codesandbox.io/s/react-hook-form-usefieldarray-ssugn)
1067 *
1068 * @param props - useFieldArray props
1069 *
1070 * @returns methods - functions to manipulate with the Field Arrays (dynamic inputs) {@link UseFieldArrayReturn}
1071 *
1072 * @example
1073 * ```tsx
1074 * function App() {
1075 * const { register, control, handleSubmit, reset, trigger, setError } = useForm({
1076 * defaultValues: {
1077 * test: []
1078 * }
1079 * });
1080 * const { fields, append } = useFieldArray({
1081 * control,
1082 * name: "test"
1083 * });
1084 *
1085 * return (
1086 * <form onSubmit={handleSubmit(data => console.log(data))}>
1087 * {fields.map((item, index) => (
1088 * <input key={item.id} {...register(`test.${index}.firstName`)} />
1089 * ))}
1090 * <button type="button" onClick={() => append({ firstName: "bill" })}>
1091 * append
1092 * </button>
1093 * <input type="submit" />
1094 * </form>
1095 * );
1096 * }
1097 * ```
1098 */
1099function useFieldArray(props) {
1100 const methods = useFormContext();
1101 const { control = methods.control, name, keyName = 'id', shouldUnregister, } = props;
1102 const [fields, setFields] = React.useState(control._getFieldArray(name));
1103 const ids = React.useRef(control._getFieldArray(name).map(generateId));
1104 const _fieldIds = React.useRef(fields);
1105 const _name = React.useRef(name);
1106 const _actioned = React.useRef(false);
1107 _name.current = name;
1108 _fieldIds.current = fields;
1109 control._names.array.add(name);
1110 props.rules &&
1111 control.register(name, props.rules);
1112 useSubscribe({
1113 next: ({ values, name: fieldArrayName, }) => {
1114 if (fieldArrayName === _name.current || !fieldArrayName) {
1115 const fieldValues = get(values, _name.current);
1116 if (Array.isArray(fieldValues)) {
1117 setFields(fieldValues);
1118 ids.current = fieldValues.map(generateId);
1119 }
1120 }
1121 },
1122 subject: control._subjects.array,
1123 });
1124 const updateValues = React.useCallback((updatedFieldArrayValues) => {
1125 _actioned.current = true;
1126 control._updateFieldArray(name, updatedFieldArrayValues);
1127 }, [control, name]);
1128 const append = (value, options) => {
1129 const appendValue = convertToArrayPayload(cloneObject(value));
1130 const updatedFieldArrayValues = appendAt(control._getFieldArray(name), appendValue);
1131 control._names.focus = getFocusFieldName(name, updatedFieldArrayValues.length - 1, options);
1132 ids.current = appendAt(ids.current, appendValue.map(generateId));
1133 updateValues(updatedFieldArrayValues);
1134 setFields(updatedFieldArrayValues);
1135 control._updateFieldArray(name, updatedFieldArrayValues, appendAt, {
1136 argA: fillEmptyArray(value),
1137 });
1138 };
1139 const prepend = (value, options) => {
1140 const prependValue = convertToArrayPayload(cloneObject(value));
1141 const updatedFieldArrayValues = prependAt(control._getFieldArray(name), prependValue);
1142 control._names.focus = getFocusFieldName(name, 0, options);
1143 ids.current = prependAt(ids.current, prependValue.map(generateId));
1144 updateValues(updatedFieldArrayValues);
1145 setFields(updatedFieldArrayValues);
1146 control._updateFieldArray(name, updatedFieldArrayValues, prependAt, {
1147 argA: fillEmptyArray(value),
1148 });
1149 };
1150 const remove = (index) => {
1151 const updatedFieldArrayValues = removeArrayAt(control._getFieldArray(name), index);
1152 ids.current = removeArrayAt(ids.current, index);
1153 updateValues(updatedFieldArrayValues);
1154 setFields(updatedFieldArrayValues);
1155 control._updateFieldArray(name, updatedFieldArrayValues, removeArrayAt, {
1156 argA: index,
1157 });
1158 };
1159 const insert$1 = (index, value, options) => {
1160 const insertValue = convertToArrayPayload(cloneObject(value));
1161 const updatedFieldArrayValues = insert(control._getFieldArray(name), index, insertValue);
1162 control._names.focus = getFocusFieldName(name, index, options);
1163 ids.current = insert(ids.current, index, insertValue.map(generateId));
1164 updateValues(updatedFieldArrayValues);
1165 setFields(updatedFieldArrayValues);
1166 control._updateFieldArray(name, updatedFieldArrayValues, insert, {
1167 argA: index,
1168 argB: fillEmptyArray(value),
1169 });
1170 };
1171 const swap = (indexA, indexB) => {
1172 const updatedFieldArrayValues = control._getFieldArray(name);
1173 swapArrayAt(updatedFieldArrayValues, indexA, indexB);
1174 swapArrayAt(ids.current, indexA, indexB);
1175 updateValues(updatedFieldArrayValues);
1176 setFields(updatedFieldArrayValues);
1177 control._updateFieldArray(name, updatedFieldArrayValues, swapArrayAt, {
1178 argA: indexA,
1179 argB: indexB,
1180 }, false);
1181 };
1182 const move = (from, to) => {
1183 const updatedFieldArrayValues = control._getFieldArray(name);
1184 moveArrayAt(updatedFieldArrayValues, from, to);
1185 moveArrayAt(ids.current, from, to);
1186 updateValues(updatedFieldArrayValues);
1187 setFields(updatedFieldArrayValues);
1188 control._updateFieldArray(name, updatedFieldArrayValues, moveArrayAt, {
1189 argA: from,
1190 argB: to,
1191 }, false);
1192 };
1193 const update = (index, value) => {
1194 const updateValue = cloneObject(value);
1195 const updatedFieldArrayValues = updateAt(control._getFieldArray(name), index, updateValue);
1196 ids.current = [...updatedFieldArrayValues].map((item, i) => !item || i === index ? generateId() : ids.current[i]);
1197 updateValues(updatedFieldArrayValues);
1198 setFields([...updatedFieldArrayValues]);
1199 control._updateFieldArray(name, updatedFieldArrayValues, updateAt, {
1200 argA: index,
1201 argB: updateValue,
1202 }, true, false);
1203 };
1204 const replace = (value) => {
1205 const updatedFieldArrayValues = convertToArrayPayload(cloneObject(value));
1206 ids.current = updatedFieldArrayValues.map(generateId);
1207 updateValues([...updatedFieldArrayValues]);
1208 setFields([...updatedFieldArrayValues]);
1209 control._updateFieldArray(name, [...updatedFieldArrayValues], (data) => data, {}, true, false);
1210 };
1211 React.useEffect(() => {
1212 control._state.action = false;
1213 isWatched(name, control._names) &&
1214 control._subjects.state.next({
1215 ...control._formState,
1216 });
1217 if (_actioned.current &&
1218 (!getValidationModes(control._options.mode).isOnSubmit ||
1219 control._formState.isSubmitted)) {
1220 if (control._options.resolver) {
1221 control._executeSchema([name]).then((result) => {
1222 const error = get(result.errors, name);
1223 const existingError = get(control._formState.errors, name);
1224 if (existingError
1225 ? (!error && existingError.type) ||
1226 (error &&
1227 (existingError.type !== error.type ||
1228 existingError.message !== error.message))
1229 : error && error.type) {
1230 error
1231 ? set(control._formState.errors, name, error)
1232 : unset(control._formState.errors, name);
1233 control._subjects.state.next({
1234 errors: control._formState.errors,
1235 });
1236 }
1237 });
1238 }
1239 else {
1240 const field = get(control._fields, name);
1241 if (field &&
1242 field._f &&
1243 !(getValidationModes(control._options.reValidateMode).isOnSubmit &&
1244 getValidationModes(control._options.mode).isOnSubmit)) {
1245 validateField(field, control._formValues, control._options.criteriaMode === VALIDATION_MODE.all, control._options.shouldUseNativeValidation, true).then((error) => !isEmptyObject(error) &&
1246 control._subjects.state.next({
1247 errors: updateFieldArrayRootError(control._formState.errors, error, name),
1248 }));
1249 }
1250 }
1251 }
1252 control._subjects.values.next({
1253 name,
1254 values: { ...control._formValues },
1255 });
1256 control._names.focus &&
1257 iterateFieldsByAction(control._fields, (ref, key) => {
1258 if (control._names.focus &&
1259 key.startsWith(control._names.focus) &&
1260 ref.focus) {
1261 ref.focus();
1262 return 1;
1263 }
1264 return;
1265 });
1266 control._names.focus = '';
1267 control._updateValid();
1268 _actioned.current = false;
1269 }, [fields, name, control]);
1270 React.useEffect(() => {
1271 !get(control._formValues, name) && control._updateFieldArray(name);
1272 return () => {
1273 (control._options.shouldUnregister || shouldUnregister) &&
1274 control.unregister(name);
1275 };
1276 }, [name, control, keyName, shouldUnregister]);
1277 return {
1278 swap: React.useCallback(swap, [updateValues, name, control]),
1279 move: React.useCallback(move, [updateValues, name, control]),
1280 prepend: React.useCallback(prepend, [updateValues, name, control]),
1281 append: React.useCallback(append, [updateValues, name, control]),
1282 remove: React.useCallback(remove, [updateValues, name, control]),
1283 insert: React.useCallback(insert$1, [updateValues, name, control]),
1284 update: React.useCallback(update, [updateValues, name, control]),
1285 replace: React.useCallback(replace, [updateValues, name, control]),
1286 fields: React.useMemo(() => fields.map((field, index) => ({
1287 ...field,
1288 [keyName]: ids.current[index] || generateId(),
1289 })), [fields, keyName]),
1290 };
1291}
1292
1293var createSubject = () => {
1294 let _observers = [];
1295 const next = (value) => {
1296 for (const observer of _observers) {
1297 observer.next && observer.next(value);
1298 }
1299 };
1300 const subscribe = (observer) => {
1301 _observers.push(observer);
1302 return {
1303 unsubscribe: () => {
1304 _observers = _observers.filter((o) => o !== observer);
1305 },
1306 };
1307 };
1308 const unsubscribe = () => {
1309 _observers = [];
1310 };
1311 return {
1312 get observers() {
1313 return _observers;
1314 },
1315 next,
1316 subscribe,
1317 unsubscribe,
1318 };
1319};
1320
1321var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
1322
1323function deepEqual(object1, object2) {
1324 if (isPrimitive(object1) || isPrimitive(object2)) {
1325 return object1 === object2;
1326 }
1327 if (isDateObject(object1) && isDateObject(object2)) {
1328 return object1.getTime() === object2.getTime();
1329 }
1330 const keys1 = Object.keys(object1);
1331 const keys2 = Object.keys(object2);
1332 if (keys1.length !== keys2.length) {
1333 return false;
1334 }
1335 for (const key of keys1) {
1336 const val1 = object1[key];
1337 if (!keys2.includes(key)) {
1338 return false;
1339 }
1340 if (key !== 'ref') {
1341 const val2 = object2[key];
1342 if ((isDateObject(val1) && isDateObject(val2)) ||
1343 (isObject(val1) && isObject(val2)) ||
1344 (Array.isArray(val1) && Array.isArray(val2))
1345 ? !deepEqual(val1, val2)
1346 : val1 !== val2) {
1347 return false;
1348 }
1349 }
1350 }
1351 return true;
1352}
1353
1354var isMultipleSelect = (element) => element.type === `select-multiple`;
1355
1356var isRadioOrCheckbox = (ref) => isRadioInput(ref) || isCheckBoxInput(ref);
1357
1358var live = (ref) => isHTMLElement(ref) && ref.isConnected;
1359
1360var objectHasFunction = (data) => {
1361 for (const key in data) {
1362 if (isFunction(data[key])) {
1363 return true;
1364 }
1365 }
1366 return false;
1367};
1368
1369function markFieldsDirty(data, fields = {}) {
1370 const isParentNodeArray = Array.isArray(data);
1371 if (isObject(data) || isParentNodeArray) {
1372 for (const key in data) {
1373 if (Array.isArray(data[key]) ||
1374 (isObject(data[key]) && !objectHasFunction(data[key]))) {
1375 fields[key] = Array.isArray(data[key]) ? [] : {};
1376 markFieldsDirty(data[key], fields[key]);
1377 }
1378 else if (!isNullOrUndefined(data[key])) {
1379 fields[key] = true;
1380 }
1381 }
1382 }
1383 return fields;
1384}
1385function getDirtyFieldsFromDefaultValues(data, formValues, dirtyFieldsFromValues) {
1386 const isParentNodeArray = Array.isArray(data);
1387 if (isObject(data) || isParentNodeArray) {
1388 for (const key in data) {
1389 if (Array.isArray(data[key]) ||
1390 (isObject(data[key]) && !objectHasFunction(data[key]))) {
1391 if (isUndefined(formValues) ||
1392 isPrimitive(dirtyFieldsFromValues[key])) {
1393 dirtyFieldsFromValues[key] = Array.isArray(data[key])
1394 ? markFieldsDirty(data[key], [])
1395 : { ...markFieldsDirty(data[key]) };
1396 }
1397 else {
1398 getDirtyFieldsFromDefaultValues(data[key], isNullOrUndefined(formValues) ? {} : formValues[key], dirtyFieldsFromValues[key]);
1399 }
1400 }
1401 else {
1402 dirtyFieldsFromValues[key] = !deepEqual(data[key], formValues[key]);
1403 }
1404 }
1405 }
1406 return dirtyFieldsFromValues;
1407}
1408var getDirtyFields = (defaultValues, formValues) => getDirtyFieldsFromDefaultValues(defaultValues, formValues, markFieldsDirty(formValues));
1409
1410var getFieldValueAs = (value, { valueAsNumber, valueAsDate, setValueAs }) => isUndefined(value)
1411 ? value
1412 : valueAsNumber
1413 ? value === ''
1414 ? NaN
1415 : value
1416 ? +value
1417 : value
1418 : valueAsDate && isString(value)
1419 ? new Date(value)
1420 : setValueAs
1421 ? setValueAs(value)
1422 : value;
1423
1424function getFieldValue(_f) {
1425 const ref = _f.ref;
1426 if (_f.refs ? _f.refs.every((ref) => ref.disabled) : ref.disabled) {
1427 return;
1428 }
1429 if (isFileInput(ref)) {
1430 return ref.files;
1431 }
1432 if (isRadioInput(ref)) {
1433 return getRadioValue(_f.refs).value;
1434 }
1435 if (isMultipleSelect(ref)) {
1436 return [...ref.selectedOptions].map(({ value }) => value);
1437 }
1438 if (isCheckBoxInput(ref)) {
1439 return getCheckboxValue(_f.refs).value;
1440 }
1441 return getFieldValueAs(isUndefined(ref.value) ? _f.ref.value : ref.value, _f);
1442}
1443
1444var getResolverOptions = (fieldsNames, _fields, criteriaMode, shouldUseNativeValidation) => {
1445 const fields = {};
1446 for (const name of fieldsNames) {
1447 const field = get(_fields, name);
1448 field && set(fields, name, field._f);
1449 }
1450 return {
1451 criteriaMode,
1452 names: [...fieldsNames],
1453 fields,
1454 shouldUseNativeValidation,
1455 };
1456};
1457
1458var getRuleValue = (rule) => isUndefined(rule)
1459 ? rule
1460 : isRegex(rule)
1461 ? rule.source
1462 : isObject(rule)
1463 ? isRegex(rule.value)
1464 ? rule.value.source
1465 : rule.value
1466 : rule;
1467
1468var hasValidation = (options) => options.mount &&
1469 (options.required ||
1470 options.min ||
1471 options.max ||
1472 options.maxLength ||
1473 options.minLength ||
1474 options.pattern ||
1475 options.validate);
1476
1477function schemaErrorLookup(errors, _fields, name) {
1478 const error = get(errors, name);
1479 if (error || isKey(name)) {
1480 return {
1481 error,
1482 name,
1483 };
1484 }
1485 const names = name.split('.');
1486 while (names.length) {
1487 const fieldName = names.join('.');
1488 const field = get(_fields, fieldName);
1489 const foundError = get(errors, fieldName);
1490 if (field && !Array.isArray(field) && name !== fieldName) {
1491 return { name };
1492 }
1493 if (foundError && foundError.type) {
1494 return {
1495 name: fieldName,
1496 error: foundError,
1497 };
1498 }
1499 names.pop();
1500 }
1501 return {
1502 name,
1503 };
1504}
1505
1506var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode) => {
1507 if (mode.isOnAll) {
1508 return false;
1509 }
1510 else if (!isSubmitted && mode.isOnTouch) {
1511 return !(isTouched || isBlurEvent);
1512 }
1513 else if (isSubmitted ? reValidateMode.isOnBlur : mode.isOnBlur) {
1514 return !isBlurEvent;
1515 }
1516 else if (isSubmitted ? reValidateMode.isOnChange : mode.isOnChange) {
1517 return isBlurEvent;
1518 }
1519 return true;
1520};
1521
1522var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
1523
1524const defaultOptions = {
1525 mode: VALIDATION_MODE.onSubmit,
1526 reValidateMode: VALIDATION_MODE.onChange,
1527 shouldFocusError: true,
1528};
1529function createFormControl(props = {}) {
1530 let _options = {
1531 ...defaultOptions,
1532 ...props,
1533 };
1534 let _formState = {
1535 submitCount: 0,
1536 isDirty: false,
1537 isLoading: isFunction(_options.defaultValues),
1538 isValidating: false,
1539 isSubmitted: false,
1540 isSubmitting: false,
1541 isSubmitSuccessful: false,
1542 isValid: false,
1543 touchedFields: {},
1544 dirtyFields: {},
1545 validatingFields: {},
1546 errors: _options.errors || {},
1547 disabled: _options.disabled || false,
1548 };
1549 let _fields = {};
1550 let _defaultValues = isObject(_options.defaultValues) || isObject(_options.values)
1551 ? cloneObject(_options.defaultValues || _options.values) || {}
1552 : {};
1553 let _formValues = _options.shouldUnregister
1554 ? {}
1555 : cloneObject(_defaultValues);
1556 let _state = {
1557 action: false,
1558 mount: false,
1559 watch: false,
1560 };
1561 let _names = {
1562 mount: new Set(),
1563 unMount: new Set(),
1564 array: new Set(),
1565 watch: new Set(),
1566 };
1567 let delayErrorCallback;
1568 let timer = 0;
1569 const _proxyFormState = {
1570 isDirty: false,
1571 dirtyFields: false,
1572 validatingFields: false,
1573 touchedFields: false,
1574 isValidating: false,
1575 isValid: false,
1576 errors: false,
1577 };
1578 const _subjects = {
1579 values: createSubject(),
1580 array: createSubject(),
1581 state: createSubject(),
1582 };
1583 const validationModeBeforeSubmit = getValidationModes(_options.mode);
1584 const validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
1585 const shouldDisplayAllAssociatedErrors = _options.criteriaMode === VALIDATION_MODE.all;
1586 const debounce = (callback) => (wait) => {
1587 clearTimeout(timer);
1588 timer = setTimeout(callback, wait);
1589 };
1590 const _updateValid = async (shouldUpdateValid) => {
1591 if (_proxyFormState.isValid || shouldUpdateValid) {
1592 const isValid = _options.resolver
1593 ? isEmptyObject((await _executeSchema()).errors)
1594 : await executeBuiltInValidation(_fields, true);
1595 if (isValid !== _formState.isValid) {
1596 _subjects.state.next({
1597 isValid,
1598 });
1599 }
1600 }
1601 };
1602 const _updateIsValidating = (names, isValidating) => {
1603 if (_proxyFormState.isValidating || _proxyFormState.validatingFields) {
1604 (names || Array.from(_names.mount)).forEach((name) => {
1605 if (name) {
1606 isValidating
1607 ? set(_formState.validatingFields, name, isValidating)
1608 : unset(_formState.validatingFields, name);
1609 }
1610 });
1611 _subjects.state.next({
1612 validatingFields: _formState.validatingFields,
1613 isValidating: !isEmptyObject(_formState.validatingFields),
1614 });
1615 }
1616 };
1617 const _updateFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
1618 if (args && method) {
1619 _state.action = true;
1620 if (shouldUpdateFieldsAndState && Array.isArray(get(_fields, name))) {
1621 const fieldValues = method(get(_fields, name), args.argA, args.argB);
1622 shouldSetValues && set(_fields, name, fieldValues);
1623 }
1624 if (shouldUpdateFieldsAndState &&
1625 Array.isArray(get(_formState.errors, name))) {
1626 const errors = method(get(_formState.errors, name), args.argA, args.argB);
1627 shouldSetValues && set(_formState.errors, name, errors);
1628 unsetEmptyArray(_formState.errors, name);
1629 }
1630 if (_proxyFormState.touchedFields &&
1631 shouldUpdateFieldsAndState &&
1632 Array.isArray(get(_formState.touchedFields, name))) {
1633 const touchedFields = method(get(_formState.touchedFields, name), args.argA, args.argB);
1634 shouldSetValues && set(_formState.touchedFields, name, touchedFields);
1635 }
1636 if (_proxyFormState.dirtyFields) {
1637 _formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
1638 }
1639 _subjects.state.next({
1640 name,
1641 isDirty: _getDirty(name, values),
1642 dirtyFields: _formState.dirtyFields,
1643 errors: _formState.errors,
1644 isValid: _formState.isValid,
1645 });
1646 }
1647 else {
1648 set(_formValues, name, values);
1649 }
1650 };
1651 const updateErrors = (name, error) => {
1652 set(_formState.errors, name, error);
1653 _subjects.state.next({
1654 errors: _formState.errors,
1655 });
1656 };
1657 const _setErrors = (errors) => {
1658 _formState.errors = errors;
1659 _subjects.state.next({
1660 errors: _formState.errors,
1661 isValid: false,
1662 });
1663 };
1664 const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
1665 const field = get(_fields, name);
1666 if (field) {
1667 const defaultValue = get(_formValues, name, isUndefined(value) ? get(_defaultValues, name) : value);
1668 isUndefined(defaultValue) ||
1669 (ref && ref.defaultChecked) ||
1670 shouldSkipSetValueAs
1671 ? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
1672 : setFieldValue(name, defaultValue);
1673 _state.mount && _updateValid();
1674 }
1675 };
1676 const updateTouchAndDirty = (name, fieldValue, isBlurEvent, shouldDirty, shouldRender) => {
1677 let shouldUpdateField = false;
1678 let isPreviousDirty = false;
1679 const output = {
1680 name,
1681 };
1682 const disabledField = !!(get(_fields, name) &&
1683 get(_fields, name)._f &&
1684 get(_fields, name)._f.disabled);
1685 if (!isBlurEvent || shouldDirty) {
1686 if (_proxyFormState.isDirty) {
1687 isPreviousDirty = _formState.isDirty;
1688 _formState.isDirty = output.isDirty = _getDirty();
1689 shouldUpdateField = isPreviousDirty !== output.isDirty;
1690 }
1691 const isCurrentFieldPristine = disabledField || deepEqual(get(_defaultValues, name), fieldValue);
1692 isPreviousDirty = !!(!disabledField && get(_formState.dirtyFields, name));
1693 isCurrentFieldPristine || disabledField
1694 ? unset(_formState.dirtyFields, name)
1695 : set(_formState.dirtyFields, name, true);
1696 output.dirtyFields = _formState.dirtyFields;
1697 shouldUpdateField =
1698 shouldUpdateField ||
1699 (_proxyFormState.dirtyFields &&
1700 isPreviousDirty !== !isCurrentFieldPristine);
1701 }
1702 if (isBlurEvent) {
1703 const isPreviousFieldTouched = get(_formState.touchedFields, name);
1704 if (!isPreviousFieldTouched) {
1705 set(_formState.touchedFields, name, isBlurEvent);
1706 output.touchedFields = _formState.touchedFields;
1707 shouldUpdateField =
1708 shouldUpdateField ||
1709 (_proxyFormState.touchedFields &&
1710 isPreviousFieldTouched !== isBlurEvent);
1711 }
1712 }
1713 shouldUpdateField && shouldRender && _subjects.state.next(output);
1714 return shouldUpdateField ? output : {};
1715 };
1716 const shouldRenderByError = (name, isValid, error, fieldState) => {
1717 const previousFieldError = get(_formState.errors, name);
1718 const shouldUpdateValid = _proxyFormState.isValid &&
1719 isBoolean(isValid) &&
1720 _formState.isValid !== isValid;
1721 if (props.delayError && error) {
1722 delayErrorCallback = debounce(() => updateErrors(name, error));
1723 delayErrorCallback(props.delayError);
1724 }
1725 else {
1726 clearTimeout(timer);
1727 delayErrorCallback = null;
1728 error
1729 ? set(_formState.errors, name, error)
1730 : unset(_formState.errors, name);
1731 }
1732 if ((error ? !deepEqual(previousFieldError, error) : previousFieldError) ||
1733 !isEmptyObject(fieldState) ||
1734 shouldUpdateValid) {
1735 const updatedFormState = {
1736 ...fieldState,
1737 ...(shouldUpdateValid && isBoolean(isValid) ? { isValid } : {}),
1738 errors: _formState.errors,
1739 name,
1740 };
1741 _formState = {
1742 ..._formState,
1743 ...updatedFormState,
1744 };
1745 _subjects.state.next(updatedFormState);
1746 }
1747 };
1748 const _executeSchema = async (name) => {
1749 _updateIsValidating(name, true);
1750 const result = await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
1751 _updateIsValidating(name);
1752 return result;
1753 };
1754 const executeSchemaAndUpdateState = async (names) => {
1755 const { errors } = await _executeSchema(names);
1756 if (names) {
1757 for (const name of names) {
1758 const error = get(errors, name);
1759 error
1760 ? set(_formState.errors, name, error)
1761 : unset(_formState.errors, name);
1762 }
1763 }
1764 else {
1765 _formState.errors = errors;
1766 }
1767 return errors;
1768 };
1769 const executeBuiltInValidation = async (fields, shouldOnlyCheckValid, context = {
1770 valid: true,
1771 }) => {
1772 for (const name in fields) {
1773 const field = fields[name];
1774 if (field) {
1775 const { _f, ...fieldValue } = field;
1776 if (_f) {
1777 const isFieldArrayRoot = _names.array.has(_f.name);
1778 _updateIsValidating([name], true);
1779 const fieldError = await validateField(field, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
1780 _updateIsValidating([name]);
1781 if (fieldError[_f.name]) {
1782 context.valid = false;
1783 if (shouldOnlyCheckValid) {
1784 break;
1785 }
1786 }
1787 !shouldOnlyCheckValid &&
1788 (get(fieldError, _f.name)
1789 ? isFieldArrayRoot
1790 ? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
1791 : set(_formState.errors, _f.name, fieldError[_f.name])
1792 : unset(_formState.errors, _f.name));
1793 }
1794 fieldValue &&
1795 (await executeBuiltInValidation(fieldValue, shouldOnlyCheckValid, context));
1796 }
1797 }
1798 return context.valid;
1799 };
1800 const _removeUnmounted = () => {
1801 for (const name of _names.unMount) {
1802 const field = get(_fields, name);
1803 field &&
1804 (field._f.refs
1805 ? field._f.refs.every((ref) => !live(ref))
1806 : !live(field._f.ref)) &&
1807 unregister(name);
1808 }
1809 _names.unMount = new Set();
1810 };
1811 const _getDirty = (name, data) => (name && data && set(_formValues, name, data),
1812 !deepEqual(getValues(), _defaultValues));
1813 const _getWatch = (names, defaultValue, isGlobal) => generateWatchOutput(names, _names, {
1814 ...(_state.mount
1815 ? _formValues
1816 : isUndefined(defaultValue)
1817 ? _defaultValues
1818 : isString(names)
1819 ? { [names]: defaultValue }
1820 : defaultValue),
1821 }, isGlobal, defaultValue);
1822 const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, props.shouldUnregister ? get(_defaultValues, name, []) : []));
1823 const setFieldValue = (name, value, options = {}) => {
1824 const field = get(_fields, name);
1825 let fieldValue = value;
1826 if (field) {
1827 const fieldReference = field._f;
1828 if (fieldReference) {
1829 !fieldReference.disabled &&
1830 set(_formValues, name, getFieldValueAs(value, fieldReference));
1831 fieldValue =
1832 isHTMLElement(fieldReference.ref) && isNullOrUndefined(value)
1833 ? ''
1834 : value;
1835 if (isMultipleSelect(fieldReference.ref)) {
1836 [...fieldReference.ref.options].forEach((optionRef) => (optionRef.selected = fieldValue.includes(optionRef.value)));
1837 }
1838 else if (fieldReference.refs) {
1839 if (isCheckBoxInput(fieldReference.ref)) {
1840 fieldReference.refs.length > 1
1841 ? fieldReference.refs.forEach((checkboxRef) => (!checkboxRef.defaultChecked || !checkboxRef.disabled) &&
1842 (checkboxRef.checked = Array.isArray(fieldValue)
1843 ? !!fieldValue.find((data) => data === checkboxRef.value)
1844 : fieldValue === checkboxRef.value))
1845 : fieldReference.refs[0] &&
1846 (fieldReference.refs[0].checked = !!fieldValue);
1847 }
1848 else {
1849 fieldReference.refs.forEach((radioRef) => (radioRef.checked = radioRef.value === fieldValue));
1850 }
1851 }
1852 else if (isFileInput(fieldReference.ref)) {
1853 fieldReference.ref.value = '';
1854 }
1855 else {
1856 fieldReference.ref.value = fieldValue;
1857 if (!fieldReference.ref.type) {
1858 _subjects.values.next({
1859 name,
1860 values: { ..._formValues },
1861 });
1862 }
1863 }
1864 }
1865 }
1866 (options.shouldDirty || options.shouldTouch) &&
1867 updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
1868 options.shouldValidate && trigger(name);
1869 };
1870 const setValues = (name, value, options) => {
1871 for (const fieldKey in value) {
1872 const fieldValue = value[fieldKey];
1873 const fieldName = `${name}.${fieldKey}`;
1874 const field = get(_fields, fieldName);
1875 (_names.array.has(name) ||
1876 !isPrimitive(fieldValue) ||
1877 (field && !field._f)) &&
1878 !isDateObject(fieldValue)
1879 ? setValues(fieldName, fieldValue, options)
1880 : setFieldValue(fieldName, fieldValue, options);
1881 }
1882 };
1883 const setValue = (name, value, options = {}) => {
1884 const field = get(_fields, name);
1885 const isFieldArray = _names.array.has(name);
1886 const cloneValue = cloneObject(value);
1887 set(_formValues, name, cloneValue);
1888 if (isFieldArray) {
1889 _subjects.array.next({
1890 name,
1891 values: { ..._formValues },
1892 });
1893 if ((_proxyFormState.isDirty || _proxyFormState.dirtyFields) &&
1894 options.shouldDirty) {
1895 _subjects.state.next({
1896 name,
1897 dirtyFields: getDirtyFields(_defaultValues, _formValues),
1898 isDirty: _getDirty(name, cloneValue),
1899 });
1900 }
1901 }
1902 else {
1903 field && !field._f && !isNullOrUndefined(cloneValue)
1904 ? setValues(name, cloneValue, options)
1905 : setFieldValue(name, cloneValue, options);
1906 }
1907 isWatched(name, _names) && _subjects.state.next({ ..._formState });
1908 _subjects.values.next({
1909 name: _state.mount ? name : undefined,
1910 values: { ..._formValues },
1911 });
1912 };
1913 const onChange = async (event) => {
1914 _state.mount = true;
1915 const target = event.target;
1916 let name = target.name;
1917 let isFieldValueUpdated = true;
1918 const field = get(_fields, name);
1919 const getCurrentFieldValue = () => target.type ? getFieldValue(field._f) : getEventValue(event);
1920 const _updateIsFieldValueUpdated = (fieldValue) => {
1921 isFieldValueUpdated =
1922 Number.isNaN(fieldValue) ||
1923 fieldValue === get(_formValues, name, fieldValue);
1924 };
1925 if (field) {
1926 let error;
1927 let isValid;
1928 const fieldValue = getCurrentFieldValue();
1929 const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
1930 const shouldSkipValidation = (!hasValidation(field._f) &&
1931 !_options.resolver &&
1932 !get(_formState.errors, name) &&
1933 !field._f.deps) ||
1934 skipValidation(isBlurEvent, get(_formState.touchedFields, name), _formState.isSubmitted, validationModeAfterSubmit, validationModeBeforeSubmit);
1935 const watched = isWatched(name, _names, isBlurEvent);
1936 set(_formValues, name, fieldValue);
1937 if (isBlurEvent) {
1938 field._f.onBlur && field._f.onBlur(event);
1939 delayErrorCallback && delayErrorCallback(0);
1940 }
1941 else if (field._f.onChange) {
1942 field._f.onChange(event);
1943 }
1944 const fieldState = updateTouchAndDirty(name, fieldValue, isBlurEvent, false);
1945 const shouldRender = !isEmptyObject(fieldState) || watched;
1946 !isBlurEvent &&
1947 _subjects.values.next({
1948 name,
1949 type: event.type,
1950 values: { ..._formValues },
1951 });
1952 if (shouldSkipValidation) {
1953 _proxyFormState.isValid && _updateValid();
1954 return (shouldRender &&
1955 _subjects.state.next({ name, ...(watched ? {} : fieldState) }));
1956 }
1957 !isBlurEvent && watched && _subjects.state.next({ ..._formState });
1958 if (_options.resolver) {
1959 const { errors } = await _executeSchema([name]);
1960 _updateIsFieldValueUpdated(fieldValue);
1961 if (isFieldValueUpdated) {
1962 const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
1963 const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
1964 error = errorLookupResult.error;
1965 name = errorLookupResult.name;
1966 isValid = isEmptyObject(errors);
1967 }
1968 }
1969 else {
1970 _updateIsValidating([name], true);
1971 error = (await validateField(field, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation))[name];
1972 _updateIsValidating([name]);
1973 _updateIsFieldValueUpdated(fieldValue);
1974 if (isFieldValueUpdated) {
1975 if (error) {
1976 isValid = false;
1977 }
1978 else if (_proxyFormState.isValid) {
1979 isValid = await executeBuiltInValidation(_fields, true);
1980 }
1981 }
1982 }
1983 if (isFieldValueUpdated) {
1984 field._f.deps &&
1985 trigger(field._f.deps);
1986 shouldRenderByError(name, isValid, error, fieldState);
1987 }
1988 }
1989 };
1990 const _focusInput = (ref, key) => {
1991 if (get(_formState.errors, key) && ref.focus) {
1992 ref.focus();
1993 return 1;
1994 }
1995 return;
1996 };
1997 const trigger = async (name, options = {}) => {
1998 let isValid;
1999 let validationResult;
2000 const fieldNames = convertToArrayPayload(name);
2001 if (_options.resolver) {
2002 const errors = await executeSchemaAndUpdateState(isUndefined(name) ? name : fieldNames);
2003 isValid = isEmptyObject(errors);
2004 validationResult = name
2005 ? !fieldNames.some((name) => get(errors, name))
2006 : isValid;
2007 }
2008 else if (name) {
2009 validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
2010 const field = get(_fields, fieldName);
2011 return await executeBuiltInValidation(field && field._f ? { [fieldName]: field } : field);
2012 }))).every(Boolean);
2013 !(!validationResult && !_formState.isValid) && _updateValid();
2014 }
2015 else {
2016 validationResult = isValid = await executeBuiltInValidation(_fields);
2017 }
2018 _subjects.state.next({
2019 ...(!isString(name) ||
2020 (_proxyFormState.isValid && isValid !== _formState.isValid)
2021 ? {}
2022 : { name }),
2023 ...(_options.resolver || !name ? { isValid } : {}),
2024 errors: _formState.errors,
2025 });
2026 options.shouldFocus &&
2027 !validationResult &&
2028 iterateFieldsByAction(_fields, _focusInput, name ? fieldNames : _names.mount);
2029 return validationResult;
2030 };
2031 const getValues = (fieldNames) => {
2032 const values = {
2033 ...(_state.mount ? _formValues : _defaultValues),
2034 };
2035 return isUndefined(fieldNames)
2036 ? values
2037 : isString(fieldNames)
2038 ? get(values, fieldNames)
2039 : fieldNames.map((name) => get(values, name));
2040 };
2041 const getFieldState = (name, formState) => ({
2042 invalid: !!get((formState || _formState).errors, name),
2043 isDirty: !!get((formState || _formState).dirtyFields, name),
2044 error: get((formState || _formState).errors, name),
2045 isValidating: !!get(_formState.validatingFields, name),
2046 isTouched: !!get((formState || _formState).touchedFields, name),
2047 });
2048 const clearErrors = (name) => {
2049 name &&
2050 convertToArrayPayload(name).forEach((inputName) => unset(_formState.errors, inputName));
2051 _subjects.state.next({
2052 errors: name ? _formState.errors : {},
2053 });
2054 };
2055 const setError = (name, error, options) => {
2056 const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
2057 const currentError = get(_formState.errors, name) || {};
2058 // Don't override existing error messages elsewhere in the object tree.
2059 const { ref: currentRef, message, type, ...restOfErrorTree } = currentError;
2060 set(_formState.errors, name, {
2061 ...restOfErrorTree,
2062 ...error,
2063 ref,
2064 });
2065 _subjects.state.next({
2066 name,
2067 errors: _formState.errors,
2068 isValid: false,
2069 });
2070 options && options.shouldFocus && ref && ref.focus && ref.focus();
2071 };
2072 const watch = (name, defaultValue) => isFunction(name)
2073 ? _subjects.values.subscribe({
2074 next: (payload) => name(_getWatch(undefined, defaultValue), payload),
2075 })
2076 : _getWatch(name, defaultValue, true);
2077 const unregister = (name, options = {}) => {
2078 for (const fieldName of name ? convertToArrayPayload(name) : _names.mount) {
2079 _names.mount.delete(fieldName);
2080 _names.array.delete(fieldName);
2081 if (!options.keepValue) {
2082 unset(_fields, fieldName);
2083 unset(_formValues, fieldName);
2084 }
2085 !options.keepError && unset(_formState.errors, fieldName);
2086 !options.keepDirty && unset(_formState.dirtyFields, fieldName);
2087 !options.keepTouched && unset(_formState.touchedFields, fieldName);
2088 !options.keepIsValidating &&
2089 unset(_formState.validatingFields, fieldName);
2090 !_options.shouldUnregister &&
2091 !options.keepDefaultValue &&
2092 unset(_defaultValues, fieldName);
2093 }
2094 _subjects.values.next({
2095 values: { ..._formValues },
2096 });
2097 _subjects.state.next({
2098 ..._formState,
2099 ...(!options.keepDirty ? {} : { isDirty: _getDirty() }),
2100 });
2101 !options.keepIsValid && _updateValid();
2102 };
2103 const _updateDisabledField = ({ disabled, name, field, fields, value, }) => {
2104 if ((isBoolean(disabled) && _state.mount) || !!disabled) {
2105 const inputValue = disabled
2106 ? undefined
2107 : isUndefined(value)
2108 ? getFieldValue(field ? field._f : get(fields, name)._f)
2109 : value;
2110 set(_formValues, name, inputValue);
2111 updateTouchAndDirty(name, inputValue, false, false, true);
2112 }
2113 };
2114 const register = (name, options = {}) => {
2115 let field = get(_fields, name);
2116 const disabledIsDefined = isBoolean(options.disabled);
2117 set(_fields, name, {
2118 ...(field || {}),
2119 _f: {
2120 ...(field && field._f ? field._f : { ref: { name } }),
2121 name,
2122 mount: true,
2123 ...options,
2124 },
2125 });
2126 _names.mount.add(name);
2127 if (field) {
2128 _updateDisabledField({
2129 field,
2130 disabled: options.disabled,
2131 name,
2132 value: options.value,
2133 });
2134 }
2135 else {
2136 updateValidAndValue(name, true, options.value);
2137 }
2138 return {
2139 ...(disabledIsDefined ? { disabled: options.disabled } : {}),
2140 ...(_options.progressive
2141 ? {
2142 required: !!options.required,
2143 min: getRuleValue(options.min),
2144 max: getRuleValue(options.max),
2145 minLength: getRuleValue(options.minLength),
2146 maxLength: getRuleValue(options.maxLength),
2147 pattern: getRuleValue(options.pattern),
2148 }
2149 : {}),
2150 name,
2151 onChange,
2152 onBlur: onChange,
2153 ref: (ref) => {
2154 if (ref) {
2155 register(name, options);
2156 field = get(_fields, name);
2157 const fieldRef = isUndefined(ref.value)
2158 ? ref.querySelectorAll
2159 ? ref.querySelectorAll('input,select,textarea')[0] || ref
2160 : ref
2161 : ref;
2162 const radioOrCheckbox = isRadioOrCheckbox(fieldRef);
2163 const refs = field._f.refs || [];
2164 if (radioOrCheckbox
2165 ? refs.find((option) => option === fieldRef)
2166 : fieldRef === field._f.ref) {
2167 return;
2168 }
2169 set(_fields, name, {
2170 _f: {
2171 ...field._f,
2172 ...(radioOrCheckbox
2173 ? {
2174 refs: [
2175 ...refs.filter(live),
2176 fieldRef,
2177 ...(Array.isArray(get(_defaultValues, name)) ? [{}] : []),
2178 ],
2179 ref: { type: fieldRef.type, name },
2180 }
2181 : { ref: fieldRef }),
2182 },
2183 });
2184 updateValidAndValue(name, false, undefined, fieldRef);
2185 }
2186 else {
2187 field = get(_fields, name, {});
2188 if (field._f) {
2189 field._f.mount = false;
2190 }
2191 (_options.shouldUnregister || options.shouldUnregister) &&
2192 !(isNameInFieldArray(_names.array, name) && _state.action) &&
2193 _names.unMount.add(name);
2194 }
2195 },
2196 };
2197 };
2198 const _focusError = () => _options.shouldFocusError &&
2199 iterateFieldsByAction(_fields, _focusInput, _names.mount);
2200 const _disableForm = (disabled) => {
2201 if (isBoolean(disabled)) {
2202 _subjects.state.next({ disabled });
2203 iterateFieldsByAction(_fields, (ref, name) => {
2204 const currentField = get(_fields, name);
2205 if (currentField) {
2206 ref.disabled = currentField._f.disabled || disabled;
2207 if (Array.isArray(currentField._f.refs)) {
2208 currentField._f.refs.forEach((inputRef) => {
2209 inputRef.disabled = currentField._f.disabled || disabled;
2210 });
2211 }
2212 }
2213 }, 0, false);
2214 }
2215 };
2216 const handleSubmit = (onValid, onInvalid) => async (e) => {
2217 let onValidError = undefined;
2218 if (e) {
2219 e.preventDefault && e.preventDefault();
2220 e.persist && e.persist();
2221 }
2222 let fieldValues = cloneObject(_formValues);
2223 _subjects.state.next({
2224 isSubmitting: true,
2225 });
2226 if (_options.resolver) {
2227 const { errors, values } = await _executeSchema();
2228 _formState.errors = errors;
2229 fieldValues = values;
2230 }
2231 else {
2232 await executeBuiltInValidation(_fields);
2233 }
2234 unset(_formState.errors, 'root');
2235 if (isEmptyObject(_formState.errors)) {
2236 _subjects.state.next({
2237 errors: {},
2238 });
2239 try {
2240 await onValid(fieldValues, e);
2241 }
2242 catch (error) {
2243 onValidError = error;
2244 }
2245 }
2246 else {
2247 if (onInvalid) {
2248 await onInvalid({ ..._formState.errors }, e);
2249 }
2250 _focusError();
2251 setTimeout(_focusError);
2252 }
2253 _subjects.state.next({
2254 isSubmitted: true,
2255 isSubmitting: false,
2256 isSubmitSuccessful: isEmptyObject(_formState.errors) && !onValidError,
2257 submitCount: _formState.submitCount + 1,
2258 errors: _formState.errors,
2259 });
2260 if (onValidError) {
2261 throw onValidError;
2262 }
2263 };
2264 const resetField = (name, options = {}) => {
2265 if (get(_fields, name)) {
2266 if (isUndefined(options.defaultValue)) {
2267 setValue(name, cloneObject(get(_defaultValues, name)));
2268 }
2269 else {
2270 setValue(name, options.defaultValue);
2271 set(_defaultValues, name, cloneObject(options.defaultValue));
2272 }
2273 if (!options.keepTouched) {
2274 unset(_formState.touchedFields, name);
2275 }
2276 if (!options.keepDirty) {
2277 unset(_formState.dirtyFields, name);
2278 _formState.isDirty = options.defaultValue
2279 ? _getDirty(name, cloneObject(get(_defaultValues, name)))
2280 : _getDirty();
2281 }
2282 if (!options.keepError) {
2283 unset(_formState.errors, name);
2284 _proxyFormState.isValid && _updateValid();
2285 }
2286 _subjects.state.next({ ..._formState });
2287 }
2288 };
2289 const _reset = (formValues, keepStateOptions = {}) => {
2290 const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
2291 const cloneUpdatedValues = cloneObject(updatedValues);
2292 const isEmptyResetValues = isEmptyObject(formValues);
2293 const values = isEmptyResetValues ? _defaultValues : cloneUpdatedValues;
2294 if (!keepStateOptions.keepDefaultValues) {
2295 _defaultValues = updatedValues;
2296 }
2297 if (!keepStateOptions.keepValues) {
2298 if (keepStateOptions.keepDirtyValues) {
2299 for (const fieldName of _names.mount) {
2300 get(_formState.dirtyFields, fieldName)
2301 ? set(values, fieldName, get(_formValues, fieldName))
2302 : setValue(fieldName, get(values, fieldName));
2303 }
2304 }
2305 else {
2306 if (isWeb && isUndefined(formValues)) {
2307 for (const name of _names.mount) {
2308 const field = get(_fields, name);
2309 if (field && field._f) {
2310 const fieldReference = Array.isArray(field._f.refs)
2311 ? field._f.refs[0]
2312 : field._f.ref;
2313 if (isHTMLElement(fieldReference)) {
2314 const form = fieldReference.closest('form');
2315 if (form) {
2316 form.reset();
2317 break;
2318 }
2319 }
2320 }
2321 }
2322 }
2323 _fields = {};
2324 }
2325 _formValues = props.shouldUnregister
2326 ? keepStateOptions.keepDefaultValues
2327 ? cloneObject(_defaultValues)
2328 : {}
2329 : cloneObject(values);
2330 _subjects.array.next({
2331 values: { ...values },
2332 });
2333 _subjects.values.next({
2334 values: { ...values },
2335 });
2336 }
2337 _names = {
2338 mount: keepStateOptions.keepDirtyValues ? _names.mount : new Set(),
2339 unMount: new Set(),
2340 array: new Set(),
2341 watch: new Set(),
2342 watchAll: false,
2343 focus: '',
2344 };
2345 _state.mount =
2346 !_proxyFormState.isValid ||
2347 !!keepStateOptions.keepIsValid ||
2348 !!keepStateOptions.keepDirtyValues;
2349 _state.watch = !!props.shouldUnregister;
2350 _subjects.state.next({
2351 submitCount: keepStateOptions.keepSubmitCount
2352 ? _formState.submitCount
2353 : 0,
2354 isDirty: isEmptyResetValues
2355 ? false
2356 : keepStateOptions.keepDirty
2357 ? _formState.isDirty
2358 : !!(keepStateOptions.keepDefaultValues &&
2359 !deepEqual(formValues, _defaultValues)),
2360 isSubmitted: keepStateOptions.keepIsSubmitted
2361 ? _formState.isSubmitted
2362 : false,
2363 dirtyFields: isEmptyResetValues
2364 ? {}
2365 : keepStateOptions.keepDirtyValues
2366 ? keepStateOptions.keepDefaultValues && _formValues
2367 ? getDirtyFields(_defaultValues, _formValues)
2368 : _formState.dirtyFields
2369 : keepStateOptions.keepDefaultValues && formValues
2370 ? getDirtyFields(_defaultValues, formValues)
2371 : keepStateOptions.keepDirty
2372 ? _formState.dirtyFields
2373 : {},
2374 touchedFields: keepStateOptions.keepTouched
2375 ? _formState.touchedFields
2376 : {},
2377 errors: keepStateOptions.keepErrors ? _formState.errors : {},
2378 isSubmitSuccessful: keepStateOptions.keepIsSubmitSuccessful
2379 ? _formState.isSubmitSuccessful
2380 : false,
2381 isSubmitting: false,
2382 });
2383 };
2384 const reset = (formValues, keepStateOptions) => _reset(isFunction(formValues)
2385 ? formValues(_formValues)
2386 : formValues, keepStateOptions);
2387 const setFocus = (name, options = {}) => {
2388 const field = get(_fields, name);
2389 const fieldReference = field && field._f;
2390 if (fieldReference) {
2391 const fieldRef = fieldReference.refs
2392 ? fieldReference.refs[0]
2393 : fieldReference.ref;
2394 if (fieldRef.focus) {
2395 fieldRef.focus();
2396 options.shouldSelect && fieldRef.select();
2397 }
2398 }
2399 };
2400 const _updateFormState = (updatedFormState) => {
2401 _formState = {
2402 ..._formState,
2403 ...updatedFormState,
2404 };
2405 };
2406 const _resetDefaultValues = () => isFunction(_options.defaultValues) &&
2407 _options.defaultValues().then((values) => {
2408 reset(values, _options.resetOptions);
2409 _subjects.state.next({
2410 isLoading: false,
2411 });
2412 });
2413 return {
2414 control: {
2415 register,
2416 unregister,
2417 getFieldState,
2418 handleSubmit,
2419 setError,
2420 _executeSchema,
2421 _getWatch,
2422 _getDirty,
2423 _updateValid,
2424 _removeUnmounted,
2425 _updateFieldArray,
2426 _updateDisabledField,
2427 _getFieldArray,
2428 _reset,
2429 _resetDefaultValues,
2430 _updateFormState,
2431 _disableForm,
2432 _subjects,
2433 _proxyFormState,
2434 _setErrors,
2435 get _fields() {
2436 return _fields;
2437 },
2438 get _formValues() {
2439 return _formValues;
2440 },
2441 get _state() {
2442 return _state;
2443 },
2444 set _state(value) {
2445 _state = value;
2446 },
2447 get _defaultValues() {
2448 return _defaultValues;
2449 },
2450 get _names() {
2451 return _names;
2452 },
2453 set _names(value) {
2454 _names = value;
2455 },
2456 get _formState() {
2457 return _formState;
2458 },
2459 set _formState(value) {
2460 _formState = value;
2461 },
2462 get _options() {
2463 return _options;
2464 },
2465 set _options(value) {
2466 _options = {
2467 ..._options,
2468 ...value,
2469 };
2470 },
2471 },
2472 trigger,
2473 register,
2474 handleSubmit,
2475 watch,
2476 setValue,
2477 getValues,
2478 reset,
2479 resetField,
2480 clearErrors,
2481 unregister,
2482 setError,
2483 setFocus,
2484 getFieldState,
2485 };
2486}
2487
2488/**
2489 * Custom hook to manage the entire form.
2490 *
2491 * @remarks
2492 * [API](https://react-hook-form.com/docs/useform) • [Demo](https://codesandbox.io/s/react-hook-form-get-started-ts-5ksmm) • [Video](https://www.youtube.com/watch?v=RkXv4AXXC_4)
2493 *
2494 * @param props - form configuration and validation parameters.
2495 *
2496 * @returns methods - individual functions to manage the form state. {@link UseFormReturn}
2497 *
2498 * @example
2499 * ```tsx
2500 * function App() {
2501 * const { register, handleSubmit, watch, formState: { errors } } = useForm();
2502 * const onSubmit = data => console.log(data);
2503 *
2504 * console.log(watch("example"));
2505 *
2506 * return (
2507 * <form onSubmit={handleSubmit(onSubmit)}>
2508 * <input defaultValue="test" {...register("example")} />
2509 * <input {...register("exampleRequired", { required: true })} />
2510 * {errors.exampleRequired && <span>This field is required</span>}
2511 * <button>Submit</button>
2512 * </form>
2513 * );
2514 * }
2515 * ```
2516 */
2517function useForm(props = {}) {
2518 const _formControl = React.useRef();
2519 const _values = React.useRef();
2520 const [formState, updateFormState] = React.useState({
2521 isDirty: false,
2522 isValidating: false,
2523 isLoading: isFunction(props.defaultValues),
2524 isSubmitted: false,
2525 isSubmitting: false,
2526 isSubmitSuccessful: false,
2527 isValid: false,
2528 submitCount: 0,
2529 dirtyFields: {},
2530 touchedFields: {},
2531 validatingFields: {},
2532 errors: props.errors || {},
2533 disabled: props.disabled || false,
2534 defaultValues: isFunction(props.defaultValues)
2535 ? undefined
2536 : props.defaultValues,
2537 });
2538 if (!_formControl.current) {
2539 _formControl.current = {
2540 ...createFormControl(props),
2541 formState,
2542 };
2543 }
2544 const control = _formControl.current.control;
2545 control._options = props;
2546 useSubscribe({
2547 subject: control._subjects.state,
2548 next: (value) => {
2549 if (shouldRenderFormState(value, control._proxyFormState, control._updateFormState, true)) {
2550 updateFormState({ ...control._formState });
2551 }
2552 },
2553 });
2554 React.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
2555 React.useEffect(() => {
2556 if (control._proxyFormState.isDirty) {
2557 const isDirty = control._getDirty();
2558 if (isDirty !== formState.isDirty) {
2559 control._subjects.state.next({
2560 isDirty,
2561 });
2562 }
2563 }
2564 }, [control, formState.isDirty]);
2565 React.useEffect(() => {
2566 if (props.values && !deepEqual(props.values, _values.current)) {
2567 control._reset(props.values, control._options.resetOptions);
2568 _values.current = props.values;
2569 updateFormState((state) => ({ ...state }));
2570 }
2571 else {
2572 control._resetDefaultValues();
2573 }
2574 }, [props.values, control]);
2575 React.useEffect(() => {
2576 if (props.errors) {
2577 control._setErrors(props.errors);
2578 }
2579 }, [props.errors, control]);
2580 React.useEffect(() => {
2581 if (!control._state.mount) {
2582 control._updateValid();
2583 control._state.mount = true;
2584 }
2585 if (control._state.watch) {
2586 control._state.watch = false;
2587 control._subjects.state.next({ ...control._formState });
2588 }
2589 control._removeUnmounted();
2590 });
2591 React.useEffect(() => {
2592 props.shouldUnregister &&
2593 control._subjects.values.next({
2594 values: control._getWatch(),
2595 });
2596 }, [props.shouldUnregister, control]);
2597 _formControl.current.formState = getProxyFormState(formState, control);
2598 return _formControl.current;
2599}
2600
2601export { Controller, Form, FormProvider, appendErrors, get, set, useController, useFieldArray, useForm, useFormContext, useFormState, useWatch };
2602//# sourceMappingURL=index.esm.mjs.map