@octanejs/hook-form 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/package.json +46 -0
- package/src/FormProvider.tsrx +76 -0
- package/src/FormProvider.tsrx.d.ts +10 -0
- package/src/constants.ts +31 -0
- package/src/controller.tsrx +8 -0
- package/src/controller.tsrx.d.ts +52 -0
- package/src/form.tsrx +119 -0
- package/src/form.tsrx.d.ts +6 -0
- package/src/formStateSubscribe.tsrx +10 -0
- package/src/formStateSubscribe.tsrx.d.ts +16 -0
- package/src/index.ts +19 -0
- package/src/logic/appendErrors.ts +19 -0
- package/src/logic/createFormControl.ts +1841 -0
- package/src/logic/generateId.ts +14 -0
- package/src/logic/generateWatchOutput.ts +27 -0
- package/src/logic/getCheckboxValue.ts +36 -0
- package/src/logic/getDirtyFields.ts +120 -0
- package/src/logic/getEventValue.ts +12 -0
- package/src/logic/getFieldValue.ts +33 -0
- package/src/logic/getFieldValueAs.ts +22 -0
- package/src/logic/getFocusFieldName.ts +13 -0
- package/src/logic/getNodeParentName.ts +4 -0
- package/src/logic/getProxyFormState.ts +33 -0
- package/src/logic/getRadioValue.ts +24 -0
- package/src/logic/getResolverOptions.ts +33 -0
- package/src/logic/getRuleValue.ts +16 -0
- package/src/logic/getValidateError.ts +22 -0
- package/src/logic/getValidationModes.ts +11 -0
- package/src/logic/getValueAndMessage.ts +12 -0
- package/src/logic/hasPromiseValidation.ts +27 -0
- package/src/logic/hasValidation.ts +12 -0
- package/src/logic/index.ts +3 -0
- package/src/logic/isNameInFieldArray.ts +7 -0
- package/src/logic/isWatched.ts +11 -0
- package/src/logic/iterateFieldsByAction.ts +37 -0
- package/src/logic/schemaErrorLookup.ts +54 -0
- package/src/logic/shouldRenderFormState.ts +25 -0
- package/src/logic/shouldSubscribeByName.ts +18 -0
- package/src/logic/skipValidation.ts +21 -0
- package/src/logic/unsetEmptyArray.ts +6 -0
- package/src/logic/updateFieldArrayRootError.ts +17 -0
- package/src/logic/validateField.ts +273 -0
- package/src/types/controller.ts +98 -0
- package/src/types/errors.ts +56 -0
- package/src/types/events.ts +26 -0
- package/src/types/fieldArray.ts +288 -0
- package/src/types/fields.ts +46 -0
- package/src/types/form.ts +961 -0
- package/src/types/index.ts +12 -0
- package/src/types/path/common.ts +391 -0
- package/src/types/path/eager.ts +224 -0
- package/src/types/path/index.ts +16 -0
- package/src/types/resolvers.ts +38 -0
- package/src/types/utils.ts +125 -0
- package/src/types/validator.ts +94 -0
- package/src/types/watch.ts +66 -0
- package/src/useController.ts +268 -0
- package/src/useFieldArray.ts +490 -0
- package/src/useForm.ts +182 -0
- package/src/useFormContext.ts +47 -0
- package/src/useFormControlContext.ts +25 -0
- package/src/useFormState.ts +86 -0
- package/src/useIsomorphicLayoutEffect.ts +6 -0
- package/src/useWatch.ts +356 -0
- package/src/utils/append.ts +4 -0
- package/src/utils/cloneObject.ts +32 -0
- package/src/utils/compact.ts +2 -0
- package/src/utils/convertToArrayPayload.ts +2 -0
- package/src/utils/createSubject.ts +48 -0
- package/src/utils/deepEqual.ts +81 -0
- package/src/utils/deepMerge.ts +27 -0
- package/src/utils/extractFormValues.ts +28 -0
- package/src/utils/fillEmptyArray.ts +3 -0
- package/src/utils/flatten.ts +23 -0
- package/src/utils/get.ts +29 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/insert.ts +8 -0
- package/src/utils/isBoolean.ts +2 -0
- package/src/utils/isCheckBoxInput.ts +4 -0
- package/src/utils/isDateObject.ts +2 -0
- package/src/utils/isEmptyObject.ts +7 -0
- package/src/utils/isFileInput.ts +4 -0
- package/src/utils/isFunction.ts +2 -0
- package/src/utils/isHTMLElement.ts +13 -0
- package/src/utils/isKey.ts +4 -0
- package/src/utils/isMultipleSelect.ts +5 -0
- package/src/utils/isNullOrUndefined.ts +2 -0
- package/src/utils/isObject.ts +8 -0
- package/src/utils/isPlainObject.ts +8 -0
- package/src/utils/isPrimitive.ts +8 -0
- package/src/utils/isRadioInput.ts +4 -0
- package/src/utils/isRadioOrCheckbox.ts +8 -0
- package/src/utils/isRegex.ts +2 -0
- package/src/utils/isString.ts +2 -0
- package/src/utils/isUndefined.ts +2 -0
- package/src/utils/isWeb.ts +4 -0
- package/src/utils/live.ts +6 -0
- package/src/utils/move.ts +15 -0
- package/src/utils/noop.ts +2 -0
- package/src/utils/objectHasFunction.ts +11 -0
- package/src/utils/prepend.ts +7 -0
- package/src/utils/remove.ts +24 -0
- package/src/utils/set.ts +36 -0
- package/src/utils/sleep.ts +2 -0
- package/src/utils/stringToPath.ts +4 -0
- package/src/utils/swap.ts +4 -0
- package/src/utils/unset.ts +65 -0
- package/src/utils/update.ts +5 -0
- package/src/watch.tsrx +7 -0
- package/src/watch.tsrx.d.ts +42 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Vendored from react-hook-form@7.81.0 src/types/utils.ts (octane port).
|
|
2
|
+
import type { NestedValue } from './form';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
Projects that React Hook Form installed don't include the DOM library need these interfaces to compile.
|
|
6
|
+
React Native applications is no DOM available. The JavaScript runtime is ES6/ES2015 only.
|
|
7
|
+
These definitions allow such projects to compile with only --lib ES6.
|
|
8
|
+
|
|
9
|
+
Warning: all of these interfaces are empty.
|
|
10
|
+
If you want type definitions for various properties, you need to add `--lib DOM` (via command line or tsconfig.json).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type Noop = () => void;
|
|
14
|
+
|
|
15
|
+
interface File extends Blob {
|
|
16
|
+
readonly lastModified: number;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface FileList {
|
|
21
|
+
readonly length: number;
|
|
22
|
+
item(index: number): File | null;
|
|
23
|
+
[index: number]: File;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type Primitive = null | undefined | string | number | boolean | symbol | bigint;
|
|
27
|
+
|
|
28
|
+
export type BrowserNativeObject = Date | FileList | File;
|
|
29
|
+
|
|
30
|
+
export type EmptyObject = { [K in string | number]: never };
|
|
31
|
+
|
|
32
|
+
export type NonUndefined<T> = T extends undefined ? never : T;
|
|
33
|
+
|
|
34
|
+
export type LiteralUnion<T extends U, U extends Primitive> = T | (U & { _?: never });
|
|
35
|
+
|
|
36
|
+
export type ExtractObjects<T> = T extends infer U ? (U extends object ? U : never) : never;
|
|
37
|
+
|
|
38
|
+
type IsPrimitiveLike<T> = T extends Primitive ? true : T extends Primitive & object ? true : false;
|
|
39
|
+
|
|
40
|
+
export type DeepPartial<T> =
|
|
41
|
+
IsPrimitiveLike<T> extends true
|
|
42
|
+
? T
|
|
43
|
+
: T extends BrowserNativeObject | NestedValue
|
|
44
|
+
? T
|
|
45
|
+
: {
|
|
46
|
+
[K in keyof T]?: ExtractObjects<T[K]> extends never ? T[K] : DeepPartial<T[K]>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type DeepPartialSkipArrayKey<T> =
|
|
50
|
+
IsPrimitiveLike<T> extends true
|
|
51
|
+
? T
|
|
52
|
+
: T extends BrowserNativeObject | NestedValue
|
|
53
|
+
? T
|
|
54
|
+
: T extends ReadonlyArray<any>
|
|
55
|
+
? { [K in keyof T]: DeepPartialSkipArrayKey<T[K]> }
|
|
56
|
+
: { [K in keyof T]?: DeepPartialSkipArrayKey<T[K]> };
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks whether the type is any
|
|
60
|
+
* See {@link https://stackoverflow.com/a/49928360/3406963}
|
|
61
|
+
* @typeParam T - type which may be any
|
|
62
|
+
* ```
|
|
63
|
+
* IsAny<any> = true
|
|
64
|
+
* IsAny<string> = false
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Checks whether the type is never
|
|
71
|
+
* @typeParam T - type which may be never
|
|
72
|
+
* ```
|
|
73
|
+
* IsNever<never> = true
|
|
74
|
+
* IsNever<string> = false
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export type IsNever<T> = [T] extends [never] ? true : false;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Checks whether T1 can be exactly (mutually) assigned to T2
|
|
81
|
+
* @typeParam T1 - type to check
|
|
82
|
+
* @typeParam T2 - type to check against
|
|
83
|
+
* ```
|
|
84
|
+
* IsEqual<string, string> = true
|
|
85
|
+
* IsEqual<'foo', 'foo'> = true
|
|
86
|
+
* IsEqual<string, number> = false
|
|
87
|
+
* IsEqual<string, number> = false
|
|
88
|
+
* IsEqual<string, 'foo'> = false
|
|
89
|
+
* IsEqual<'foo', string> = false
|
|
90
|
+
* IsEqual<'foo' | 'bar', 'foo'> = boolean // 'foo' is assignable, but 'bar' is not (true | false) -> boolean
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export type IsEqual<T1, T2> = T1 extends T2
|
|
94
|
+
? (<G>() => G extends T1 ? 1 : 2) extends <G>() => G extends T2 ? 1 : 2
|
|
95
|
+
? true
|
|
96
|
+
: false
|
|
97
|
+
: false;
|
|
98
|
+
|
|
99
|
+
export type DeepMap<T, TValue> =
|
|
100
|
+
IsAny<T> extends true
|
|
101
|
+
? any
|
|
102
|
+
: T extends BrowserNativeObject | NestedValue
|
|
103
|
+
? TValue
|
|
104
|
+
: T extends ReadonlyArray<infer U>
|
|
105
|
+
? Array<DeepMap<NonUndefined<U>, TValue> | undefined>
|
|
106
|
+
: T extends object
|
|
107
|
+
? { [K in keyof T]: DeepMap<NonUndefined<T[K]>, TValue> }
|
|
108
|
+
: TValue;
|
|
109
|
+
|
|
110
|
+
export type IsFlatObject<T extends object> =
|
|
111
|
+
Extract<Exclude<T[keyof T], NestedValue | Date | FileList>, any[] | object> extends never
|
|
112
|
+
? true
|
|
113
|
+
: false;
|
|
114
|
+
|
|
115
|
+
export type Merge<A, B> = {
|
|
116
|
+
[K in keyof A | keyof B]?: K extends keyof A & keyof B
|
|
117
|
+
? [A[K], B[K]] extends [object, object]
|
|
118
|
+
? Merge<A[K], B[K]>
|
|
119
|
+
: B[K]
|
|
120
|
+
: K extends keyof A
|
|
121
|
+
? A[K]
|
|
122
|
+
: K extends keyof B
|
|
123
|
+
? B[K]
|
|
124
|
+
: never;
|
|
125
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Vendored from react-hook-form@7.81.0 src/types/validator.ts (octane port).
|
|
2
|
+
import type { EVENTS, INPUT_VALIDATION_RULES } from '../constants';
|
|
3
|
+
|
|
4
|
+
import type { Message } from './errors';
|
|
5
|
+
import type { FieldValues } from './fields';
|
|
6
|
+
import type { FormState } from './form';
|
|
7
|
+
import type { FieldPath, FieldPathValue } from './path';
|
|
8
|
+
|
|
9
|
+
export type ValidationValue = boolean | number | string | RegExp;
|
|
10
|
+
|
|
11
|
+
export type ValidationRule<TValidationValue extends ValidationValue = ValidationValue> =
|
|
12
|
+
| TValidationValue
|
|
13
|
+
| ValidationValueMessage<TValidationValue>;
|
|
14
|
+
|
|
15
|
+
export type ValidationValueMessage<TValidationValue extends ValidationValue = ValidationValue> = {
|
|
16
|
+
value: TValidationValue;
|
|
17
|
+
message: Message;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ValidateResult = Message | Message[] | boolean | undefined;
|
|
21
|
+
|
|
22
|
+
export type FormValidateResult<T> =
|
|
23
|
+
| Partial<
|
|
24
|
+
Record<
|
|
25
|
+
keyof T,
|
|
26
|
+
{
|
|
27
|
+
message: Message | Message[] | boolean | undefined;
|
|
28
|
+
type: string;
|
|
29
|
+
}
|
|
30
|
+
>
|
|
31
|
+
>
|
|
32
|
+
| string
|
|
33
|
+
| boolean;
|
|
34
|
+
|
|
35
|
+
export type Validate<TFieldValue, TFormValues> = (
|
|
36
|
+
value: TFieldValue,
|
|
37
|
+
formValues: TFormValues,
|
|
38
|
+
) => ValidateResult | Promise<ValidateResult>;
|
|
39
|
+
|
|
40
|
+
export type ValidateFormEventType = (typeof EVENTS)[keyof typeof EVENTS];
|
|
41
|
+
|
|
42
|
+
export type ValidateForm<
|
|
43
|
+
TFormValues extends FieldValues,
|
|
44
|
+
TFieldName extends FieldPath<TFormValues> = FieldPath<TFormValues>,
|
|
45
|
+
> = (props: {
|
|
46
|
+
formValues: TFormValues;
|
|
47
|
+
formState: FormState<TFormValues>;
|
|
48
|
+
eventType?: ValidateFormEventType;
|
|
49
|
+
name?: TFieldName | TFieldName[];
|
|
50
|
+
}) => FormValidateResult<TFormValues> | Promise<FormValidateResult<TFormValues>>;
|
|
51
|
+
|
|
52
|
+
export type RegisterOptions<
|
|
53
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
54
|
+
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
55
|
+
> = Partial<{
|
|
56
|
+
required: Message | ValidationRule<boolean>;
|
|
57
|
+
min: ValidationRule<number | string>;
|
|
58
|
+
max: ValidationRule<number | string>;
|
|
59
|
+
maxLength: ValidationRule<number>;
|
|
60
|
+
minLength: ValidationRule<number>;
|
|
61
|
+
validate:
|
|
62
|
+
| Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>
|
|
63
|
+
| Record<string, Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>>;
|
|
64
|
+
value: FieldPathValue<TFieldValues, TFieldName>;
|
|
65
|
+
setValueAs: (value: any) => any;
|
|
66
|
+
shouldUnregister?: boolean;
|
|
67
|
+
onChange?: (event: any) => void;
|
|
68
|
+
onBlur?: (event: any) => void;
|
|
69
|
+
disabled: boolean;
|
|
70
|
+
deps: FieldPath<TFieldValues> | FieldPath<TFieldValues>[];
|
|
71
|
+
}> &
|
|
72
|
+
(
|
|
73
|
+
| {
|
|
74
|
+
pattern?: ValidationRule<RegExp>;
|
|
75
|
+
valueAsNumber?: false;
|
|
76
|
+
valueAsDate?: false;
|
|
77
|
+
}
|
|
78
|
+
| {
|
|
79
|
+
pattern?: undefined;
|
|
80
|
+
valueAsNumber?: false;
|
|
81
|
+
valueAsDate?: true;
|
|
82
|
+
}
|
|
83
|
+
| {
|
|
84
|
+
pattern?: undefined;
|
|
85
|
+
valueAsNumber?: true;
|
|
86
|
+
valueAsDate?: false;
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
export type InputValidationRules = typeof INPUT_VALIDATION_RULES;
|
|
91
|
+
|
|
92
|
+
export type MaxType = InputValidationRules['max'] | InputValidationRules['maxLength'];
|
|
93
|
+
|
|
94
|
+
export type MinType = InputValidationRules['min'] | InputValidationRules['minLength'];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Vendored from react-hook-form@7.81.0 src/types/watch.ts.
|
|
2
|
+
// octane: upstream's ReactNode becomes `unknown` — any octane renderable.
|
|
3
|
+
type ReactNode = unknown;
|
|
4
|
+
|
|
5
|
+
import type { FieldValues } from './fields';
|
|
6
|
+
import type { Control } from './form';
|
|
7
|
+
import type { FieldPath, FieldPathValue, FieldPathValues } from './path';
|
|
8
|
+
import type { DeepPartialSkipArrayKey } from './utils';
|
|
9
|
+
|
|
10
|
+
export type UseWatchProps<TFieldValues extends FieldValues = FieldValues> = {
|
|
11
|
+
defaultValue?: unknown;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
name?: FieldPath<TFieldValues> | FieldPath<TFieldValues>[] | readonly FieldPath<TFieldValues>[];
|
|
14
|
+
control?: Control<TFieldValues>;
|
|
15
|
+
exact?: boolean;
|
|
16
|
+
compute?: (formValues: TFieldValues) => TFieldValues;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type WatchDefaultValue<TFieldName, TFieldValues extends FieldValues = FieldValues> =
|
|
20
|
+
TFieldName extends FieldPath<TFieldValues>
|
|
21
|
+
? FieldPathValue<TFieldValues, TFieldName>
|
|
22
|
+
: DeepPartialSkipArrayKey<TFieldValues>;
|
|
23
|
+
|
|
24
|
+
export type WatchName<TFieldValues extends FieldValues> =
|
|
25
|
+
| FieldPath<TFieldValues>
|
|
26
|
+
| FieldPath<TFieldValues>[]
|
|
27
|
+
| readonly FieldPath<TFieldValues>[]
|
|
28
|
+
| undefined;
|
|
29
|
+
|
|
30
|
+
export type WatchValue<
|
|
31
|
+
TFieldName,
|
|
32
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
33
|
+
> = TFieldName extends FieldPath<TFieldValues>[] | readonly FieldPath<TFieldValues>[]
|
|
34
|
+
? FieldPathValues<TFieldValues, TFieldName>
|
|
35
|
+
: TFieldName extends FieldPath<TFieldValues>
|
|
36
|
+
? FieldPathValue<TFieldValues, TFieldName>
|
|
37
|
+
: TFieldValues;
|
|
38
|
+
|
|
39
|
+
export type WatchRenderValue<
|
|
40
|
+
TFieldName,
|
|
41
|
+
TFieldValues extends FieldValues,
|
|
42
|
+
TComputeValue,
|
|
43
|
+
> = TComputeValue extends undefined ? WatchValue<TFieldName, TFieldValues> : TComputeValue;
|
|
44
|
+
|
|
45
|
+
export type WatchProps<
|
|
46
|
+
TFieldName extends WatchName<TFieldValues>,
|
|
47
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
48
|
+
TContext = any,
|
|
49
|
+
TTransformedValues = TFieldValues,
|
|
50
|
+
TComputeValue = undefined,
|
|
51
|
+
> = {
|
|
52
|
+
control?: Control<TFieldValues, TContext, TTransformedValues>;
|
|
53
|
+
/**
|
|
54
|
+
* @deprecated This prop will be renamed to `name` in the next major release.
|
|
55
|
+
* Use `name` instead.
|
|
56
|
+
*/
|
|
57
|
+
names?: TFieldName;
|
|
58
|
+
name?: TFieldName;
|
|
59
|
+
disabled?: boolean;
|
|
60
|
+
exact?: boolean;
|
|
61
|
+
defaultValue?: WatchDefaultValue<TFieldName, TFieldValues>;
|
|
62
|
+
compute?: (value: WatchValue<TFieldName, TFieldValues>) => TComputeValue;
|
|
63
|
+
render: (
|
|
64
|
+
value: WatchRenderValue<TFieldName, TFieldValues, TComputeValue>,
|
|
65
|
+
) => ReactNode | ReactNode[];
|
|
66
|
+
};
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// Vendored from react-hook-form@7.81.0 src/useController.ts (octane port).
|
|
2
|
+
import { useRef, useEffect, useMemo, useCallback } from 'octane';
|
|
3
|
+
|
|
4
|
+
import getEventValue from './logic/getEventValue';
|
|
5
|
+
import isNameInFieldArray from './logic/isNameInFieldArray';
|
|
6
|
+
import cloneObject from './utils/cloneObject';
|
|
7
|
+
import get from './utils/get';
|
|
8
|
+
import isBoolean from './utils/isBoolean';
|
|
9
|
+
import isFunction from './utils/isFunction';
|
|
10
|
+
import isUndefined from './utils/isUndefined';
|
|
11
|
+
import set from './utils/set';
|
|
12
|
+
import { EVENTS } from './constants';
|
|
13
|
+
import type {
|
|
14
|
+
ControllerFieldState,
|
|
15
|
+
Field,
|
|
16
|
+
FieldPath,
|
|
17
|
+
FieldPathValue,
|
|
18
|
+
FieldValues,
|
|
19
|
+
InternalFieldName,
|
|
20
|
+
UseControllerProps,
|
|
21
|
+
UseControllerReturn,
|
|
22
|
+
} from './types';
|
|
23
|
+
import { useFormControlContext } from './useFormControlContext';
|
|
24
|
+
import { useFormState } from './useFormState';
|
|
25
|
+
import { useWatch } from './useWatch';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 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.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* [API](https://react-hook-form.com/docs/usecontroller) • [Demo](https://codesandbox.io/s/usecontroller-0o8px)
|
|
32
|
+
*
|
|
33
|
+
* @param props - the path name to the form field value, and validation rules.
|
|
34
|
+
*
|
|
35
|
+
* @returns field properties, field and form state. {@link UseControllerReturn}
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* function Input(props) {
|
|
40
|
+
* const { field, fieldState, formState } = useController(props);
|
|
41
|
+
* return (
|
|
42
|
+
* <div>
|
|
43
|
+
* <input {...field} placeholder={props.name} />
|
|
44
|
+
* <p>{fieldState.isTouched && "Touched"}</p>
|
|
45
|
+
* <p>{formState.isSubmitted ? "submitted" : ""}</p>
|
|
46
|
+
* </div>
|
|
47
|
+
* );
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function useController<
|
|
52
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
53
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
54
|
+
TTransformedValues = TFieldValues,
|
|
55
|
+
>(
|
|
56
|
+
props: UseControllerProps<TFieldValues, TName, TTransformedValues>,
|
|
57
|
+
): UseControllerReturn<TFieldValues, TName> {
|
|
58
|
+
const formControl = useFormControlContext<TFieldValues, any, TTransformedValues>();
|
|
59
|
+
const {
|
|
60
|
+
name,
|
|
61
|
+
disabled,
|
|
62
|
+
control = formControl,
|
|
63
|
+
shouldUnregister,
|
|
64
|
+
defaultValue,
|
|
65
|
+
exact = true,
|
|
66
|
+
} = props;
|
|
67
|
+
const isArrayField = isNameInFieldArray(control._names.array, name);
|
|
68
|
+
|
|
69
|
+
const defaultValueMemo = useMemo(
|
|
70
|
+
() => get(control._formValues, name, get(control._defaultValues, name, defaultValue)),
|
|
71
|
+
[control, name, defaultValue],
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const value = useWatch({
|
|
75
|
+
control,
|
|
76
|
+
name,
|
|
77
|
+
defaultValue: defaultValueMemo,
|
|
78
|
+
exact,
|
|
79
|
+
}) as FieldPathValue<TFieldValues, TName>;
|
|
80
|
+
|
|
81
|
+
const formState = useFormState({
|
|
82
|
+
control,
|
|
83
|
+
name,
|
|
84
|
+
exact,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const _props = useRef(props);
|
|
88
|
+
const _proxyRef = useRef<any>(null);
|
|
89
|
+
|
|
90
|
+
const _registerProps = useRef(
|
|
91
|
+
control.register(name, {
|
|
92
|
+
...props.rules,
|
|
93
|
+
value,
|
|
94
|
+
...(isBoolean(props.disabled) ? { disabled: props.disabled } : {}),
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
_props.current = props;
|
|
99
|
+
|
|
100
|
+
const fieldState = useMemo(
|
|
101
|
+
() =>
|
|
102
|
+
Object.defineProperties(
|
|
103
|
+
{},
|
|
104
|
+
{
|
|
105
|
+
invalid: {
|
|
106
|
+
enumerable: true,
|
|
107
|
+
get: () => !!get(formState.errors, name),
|
|
108
|
+
},
|
|
109
|
+
isDirty: {
|
|
110
|
+
enumerable: true,
|
|
111
|
+
get: () => !!get(formState.dirtyFields, name),
|
|
112
|
+
},
|
|
113
|
+
isTouched: {
|
|
114
|
+
enumerable: true,
|
|
115
|
+
get: () => !!get(formState.touchedFields, name),
|
|
116
|
+
},
|
|
117
|
+
isValidating: {
|
|
118
|
+
enumerable: true,
|
|
119
|
+
get: () => !!get(formState.validatingFields, name),
|
|
120
|
+
},
|
|
121
|
+
error: {
|
|
122
|
+
enumerable: true,
|
|
123
|
+
get: () => get(formState.errors, name),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
) as ControllerFieldState,
|
|
127
|
+
[formState, name],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// octane: upstream names this `onChange` — exposed as `onInput` because
|
|
131
|
+
// octane events are native (input = per-keystroke). Still accepts an event
|
|
132
|
+
// OR a raw value (getEventValue unwraps), and programmatic calls keep the
|
|
133
|
+
// upstream 'change' event type.
|
|
134
|
+
const onInput = useCallback(
|
|
135
|
+
(event: any) => {
|
|
136
|
+
const value = getEventValue(event);
|
|
137
|
+
|
|
138
|
+
if (!get(control._fields, name)) {
|
|
139
|
+
_registerProps.current = control.register(name, {
|
|
140
|
+
..._props.current.rules,
|
|
141
|
+
value,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return _registerProps.current.onInput({
|
|
146
|
+
target: {
|
|
147
|
+
value: getEventValue(event),
|
|
148
|
+
name: name as InternalFieldName,
|
|
149
|
+
},
|
|
150
|
+
type: EVENTS.CHANGE,
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
[name, control],
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const onBlur = useCallback(
|
|
157
|
+
() =>
|
|
158
|
+
_registerProps.current.onBlur({
|
|
159
|
+
target: {
|
|
160
|
+
value: get(control._formValues, name),
|
|
161
|
+
name: name as InternalFieldName,
|
|
162
|
+
},
|
|
163
|
+
type: EVENTS.BLUR,
|
|
164
|
+
}),
|
|
165
|
+
[name, control._formValues],
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const ref = useCallback(
|
|
169
|
+
(elm: any) => {
|
|
170
|
+
if (elm) {
|
|
171
|
+
_proxyRef.current = {
|
|
172
|
+
focus: () => isFunction(elm.focus) && elm.focus(),
|
|
173
|
+
select: () => isFunction(elm.select) && elm.select(),
|
|
174
|
+
setCustomValidity: (message: string) =>
|
|
175
|
+
isFunction(elm.setCustomValidity) && elm.setCustomValidity(message),
|
|
176
|
+
reportValidity: () => isFunction(elm.reportValidity) && elm.reportValidity(),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const field = get(control._fields, name);
|
|
181
|
+
|
|
182
|
+
if (field && field._f && elm) {
|
|
183
|
+
field._f.ref = _proxyRef.current;
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
[control._fields, name],
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const field = useMemo(
|
|
190
|
+
() => ({
|
|
191
|
+
name,
|
|
192
|
+
value,
|
|
193
|
+
...(isBoolean(disabled) || formState.disabled
|
|
194
|
+
? { disabled: formState.disabled || disabled }
|
|
195
|
+
: {}),
|
|
196
|
+
onInput,
|
|
197
|
+
onBlur,
|
|
198
|
+
ref,
|
|
199
|
+
}),
|
|
200
|
+
[name, disabled, formState.disabled, onInput, onBlur, ref, value],
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
const _shouldUnregisterField = control._options.shouldUnregister || shouldUnregister;
|
|
205
|
+
|
|
206
|
+
control.register(name, {
|
|
207
|
+
..._props.current.rules,
|
|
208
|
+
...(isBoolean(_props.current.disabled) ? { disabled: _props.current.disabled } : {}),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const updateMounted = (name: InternalFieldName, value: boolean) => {
|
|
212
|
+
const field: Field = get(control._fields, name);
|
|
213
|
+
|
|
214
|
+
if (field && field._f) {
|
|
215
|
+
field._f.mount = value;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
updateMounted(name, true);
|
|
220
|
+
|
|
221
|
+
if (_shouldUnregisterField) {
|
|
222
|
+
const value = cloneObject(
|
|
223
|
+
get(
|
|
224
|
+
shouldUnregister
|
|
225
|
+
? control._defaultValues
|
|
226
|
+
: control._options.values || control._defaultValues,
|
|
227
|
+
name,
|
|
228
|
+
get(control._options.defaultValues, name, _props.current.defaultValue),
|
|
229
|
+
),
|
|
230
|
+
);
|
|
231
|
+
set(control._defaultValues, name, value);
|
|
232
|
+
if (isUndefined(get(control._formValues, name))) {
|
|
233
|
+
set(control._formValues, name, value);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
!isArrayField && control.register(name);
|
|
238
|
+
|
|
239
|
+
if (_proxyRef.current) {
|
|
240
|
+
const field: Field = get(control._fields, name);
|
|
241
|
+
if (field && field._f) {
|
|
242
|
+
field._f.ref = _proxyRef.current;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return () => {
|
|
247
|
+
(isArrayField ? _shouldUnregisterField && !control._state.action : _shouldUnregisterField)
|
|
248
|
+
? control.unregister(name)
|
|
249
|
+
: updateMounted(name, false);
|
|
250
|
+
};
|
|
251
|
+
}, [name, control, isArrayField, shouldUnregister]);
|
|
252
|
+
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
control._setDisabledField({
|
|
255
|
+
disabled,
|
|
256
|
+
name,
|
|
257
|
+
});
|
|
258
|
+
}, [disabled, name, control]);
|
|
259
|
+
|
|
260
|
+
return useMemo(
|
|
261
|
+
() => ({
|
|
262
|
+
field,
|
|
263
|
+
formState,
|
|
264
|
+
fieldState,
|
|
265
|
+
}),
|
|
266
|
+
[field, formState, fieldState],
|
|
267
|
+
);
|
|
268
|
+
}
|