UNPKG

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