UEA-PRODEM
This commit is contained in:
+371
-98
@@ -10,7 +10,11 @@ var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => va
|
||||
|
||||
const EVENTS = {
|
||||
BLUR: 'blur',
|
||||
FOCUS_OUT: 'focusout'};
|
||||
FOCUS_OUT: 'focusout',
|
||||
SUBMIT: 'submit',
|
||||
TRIGGER: 'trigger',
|
||||
VALID: 'valid',
|
||||
};
|
||||
const VALIDATION_MODE = {
|
||||
onBlur: 'onBlur',
|
||||
onChange: 'onChange',
|
||||
@@ -27,6 +31,9 @@ const INPUT_VALIDATION_RULES = {
|
||||
required: 'required',
|
||||
validate: 'validate',
|
||||
};
|
||||
const FORM_ERROR_TYPE = 'form';
|
||||
const ROOT_ERROR_TYPE = 'root';
|
||||
const PROTOTYPE_KEYWORDS = ['__proto__', 'constructor', 'prototype'];
|
||||
|
||||
var isDateObject = (value) => value instanceof Date;
|
||||
|
||||
@@ -102,7 +109,11 @@ var createSubject = () => {
|
||||
|
||||
var isPrimitive = (value) => isNullOrUndefined(value) || !isObjectType(value);
|
||||
|
||||
function deepEqual(object1, object2, _internal_visited = new WeakSet()) {
|
||||
const isEmptyObjectWithCustomPrototype = (object, keys) => keys.length === 0 && !Array.isArray(object) && !isPlainObject(object);
|
||||
function deepEqual(object1, object2, visited = new WeakSet()) {
|
||||
if (object1 === object2) {
|
||||
return true;
|
||||
}
|
||||
if (isPrimitive(object1) || isPrimitive(object2)) {
|
||||
return Object.is(object1, object2);
|
||||
}
|
||||
@@ -114,22 +125,26 @@ function deepEqual(object1, object2, _internal_visited = new WeakSet()) {
|
||||
if (keys1.length !== keys2.length) {
|
||||
return false;
|
||||
}
|
||||
if (_internal_visited.has(object1) || _internal_visited.has(object2)) {
|
||||
if (isEmptyObjectWithCustomPrototype(object1, keys1) ||
|
||||
isEmptyObjectWithCustomPrototype(object2, keys2)) {
|
||||
return Object.is(object1, object2);
|
||||
}
|
||||
if (visited.has(object1) || visited.has(object2)) {
|
||||
return true;
|
||||
}
|
||||
_internal_visited.add(object1);
|
||||
_internal_visited.add(object2);
|
||||
visited.add(object1);
|
||||
visited.add(object2);
|
||||
for (const key of keys1) {
|
||||
const val1 = object1[key];
|
||||
if (!keys2.includes(key)) {
|
||||
if (!(key in object2)) {
|
||||
return false;
|
||||
}
|
||||
if (key !== 'ref') {
|
||||
const val2 = object2[key];
|
||||
if ((isDateObject(val1) && isDateObject(val2)) ||
|
||||
(isObject(val1) && isObject(val2)) ||
|
||||
(Array.isArray(val1) && Array.isArray(val2))
|
||||
? !deepEqual(val1, val2, _internal_visited)
|
||||
((isObject(val1) || Array.isArray(val1)) &&
|
||||
(isObject(val2) || Array.isArray(val2)))
|
||||
? !deepEqual(val1, val2, visited)
|
||||
: !Object.is(val1, val2)) {
|
||||
return false;
|
||||
}
|
||||
@@ -162,13 +177,19 @@ var isKey = (value) => /^\w*$/.test(value);
|
||||
|
||||
var isUndefined = (val) => val === undefined;
|
||||
|
||||
var stringToPath = (input) => compact(input.replace(/["|']|\]/g, '').split(/\.|\[/));
|
||||
var stringToPath = (input) => input.split(/[.[\]'"]/g).filter(Boolean);
|
||||
|
||||
var get = (object, path, defaultValue) => {
|
||||
if (!path || !isObject(object)) {
|
||||
return defaultValue;
|
||||
}
|
||||
const result = (isKey(path) ? [path] : stringToPath(path)).reduce((result, key) => isNullOrUndefined(result) ? result : result[key], object);
|
||||
const paths = isKey(path) ? [path] : stringToPath(path);
|
||||
if (paths.some((key) => PROTOTYPE_KEYWORDS.includes(key))) {
|
||||
return defaultValue;
|
||||
}
|
||||
const result = paths.reduce((result, key) => {
|
||||
return isNullOrUndefined(result) ? undefined : result[key];
|
||||
}, object);
|
||||
return isUndefined(result) || result === object
|
||||
? isUndefined(object[path])
|
||||
? defaultValue
|
||||
@@ -222,7 +243,7 @@ var set = (object, path, value) => {
|
||||
? []
|
||||
: {};
|
||||
}
|
||||
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
||||
if (PROTOTYPE_KEYWORDS.includes(key)) {
|
||||
return;
|
||||
}
|
||||
object[key] = newValue;
|
||||
@@ -234,7 +255,12 @@ function baseGet(object, updatePath) {
|
||||
const length = updatePath.slice(0, -1).length;
|
||||
let index = 0;
|
||||
while (index < length) {
|
||||
object = isUndefined(object) ? index++ : object[updatePath[index++]];
|
||||
if (isNullOrUndefined(object)) {
|
||||
object = undefined;
|
||||
break;
|
||||
}
|
||||
object = object[updatePath[index]];
|
||||
index++;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
@@ -247,6 +273,10 @@ function isEmptyArray(obj) {
|
||||
return true;
|
||||
}
|
||||
function unset(object, path) {
|
||||
if (isString(path) && Object.prototype.hasOwnProperty.call(object, path)) {
|
||||
delete object[path];
|
||||
return object;
|
||||
}
|
||||
const paths = Array.isArray(path)
|
||||
? path
|
||||
: isKey(path)
|
||||
@@ -304,6 +334,29 @@ function markFieldsDirty(data, fields = {}) {
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
function pruneDirtyFields(value) {
|
||||
if (value === false) {
|
||||
return undefined;
|
||||
}
|
||||
if (value === true) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const result = value.map((value) => pruneDirtyFields(value));
|
||||
return (result.some((value) => value !== undefined) ? result : undefined);
|
||||
}
|
||||
if (isObject(value)) {
|
||||
const result = {};
|
||||
for (const key in value) {
|
||||
const pruned = pruneDirtyFields(value[key]);
|
||||
if (!isUndefined(pruned)) {
|
||||
result[key] = pruned;
|
||||
}
|
||||
}
|
||||
return (Object.keys(result).length ? result : undefined);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function getDirtyFields(data, formValues, dirtyFieldsFromValues) {
|
||||
if (!dirtyFieldsFromValues) {
|
||||
dirtyFieldsFromValues = markFieldsDirty(formValues);
|
||||
@@ -323,7 +376,7 @@ function getDirtyFields(data, formValues, dirtyFieldsFromValues) {
|
||||
dirtyFieldsFromValues[key] = !deepEqual(value, formValue);
|
||||
}
|
||||
}
|
||||
return dirtyFieldsFromValues;
|
||||
return pruneDirtyFields(dirtyFieldsFromValues) || {};
|
||||
}
|
||||
|
||||
var getEventValue = (event) => isObject(event) && event.target
|
||||
@@ -452,15 +505,14 @@ var hasValidation = (options) => options.mount &&
|
||||
options.pattern ||
|
||||
options.validate);
|
||||
|
||||
var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
|
||||
|
||||
var isNameInFieldArray = (names, name) => names.has(getNodeParentName(name));
|
||||
var isNameInFieldArray = (names, name) => name
|
||||
.split('.')
|
||||
.some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join('.')));
|
||||
|
||||
var isWatched = (name, _names, isBlurEvent) => !isBlurEvent &&
|
||||
(_names.watchAll ||
|
||||
_names.watch.has(name) ||
|
||||
[..._names.watch].some((watchName) => name.startsWith(watchName) &&
|
||||
/^\.\w+/.test(name.slice(watchName.length))));
|
||||
[..._names.watch].some((watchName) => name.startsWith(`${watchName}.`)));
|
||||
|
||||
const iterateFieldsByAction = (fields, action, fieldsNames, abortEarly) => {
|
||||
for (const key of fieldsNames || Object.keys(fields)) {
|
||||
@@ -529,7 +581,8 @@ var shouldRenderFormState = (formStateData, _proxyFormState, updateFormState, is
|
||||
updateFormState(formStateData);
|
||||
const { name, ...formState } = formStateData;
|
||||
return (isEmptyObject(formState) ||
|
||||
Object.keys(formState).length >= Object.keys(_proxyFormState).length ||
|
||||
(isRoot &&
|
||||
Object.keys(formState).length >= Object.keys(_proxyFormState).length) ||
|
||||
Object.keys(formState).find((key) => _proxyFormState[key] ===
|
||||
(!isRoot || VALIDATION_MODE.all)));
|
||||
};
|
||||
@@ -562,8 +615,9 @@ var skipValidation = (isBlurEvent, isTouched, isSubmitted, reValidateMode, mode)
|
||||
var unsetEmptyArray = (ref, name) => !compact(get(ref, name)).length && unset(ref, name);
|
||||
|
||||
var updateFieldArrayRootError = (errors, error, name) => {
|
||||
const fieldArrayErrors = convertToArrayPayload(get(errors, name));
|
||||
set(fieldArrayErrors, 'root', error[name]);
|
||||
const existingErrors = get(errors, name);
|
||||
const fieldArrayErrors = Array.isArray(existingErrors) ? existingErrors : [];
|
||||
set(fieldArrayErrors, ROOT_ERROR_TYPE, error[name]);
|
||||
set(errors, name, fieldArrayErrors);
|
||||
return errors;
|
||||
};
|
||||
@@ -771,24 +825,27 @@ const defaultOptions = {
|
||||
reValidateMode: VALIDATION_MODE.onChange,
|
||||
shouldFocusError: true,
|
||||
};
|
||||
const DEFAULT_FORM_STATE = {
|
||||
submitCount: 0,
|
||||
isDirty: false,
|
||||
isReady: false,
|
||||
isValidating: false,
|
||||
isSubmitted: false,
|
||||
isSubmitting: false,
|
||||
isSubmitSuccessful: false,
|
||||
isValid: false,
|
||||
touchedFields: {},
|
||||
dirtyFields: {},
|
||||
validatingFields: {},
|
||||
};
|
||||
function createFormControl(props = {}) {
|
||||
let _options = {
|
||||
...defaultOptions,
|
||||
...props,
|
||||
};
|
||||
let _formState = {
|
||||
submitCount: 0,
|
||||
isDirty: false,
|
||||
isReady: false,
|
||||
...cloneObject(DEFAULT_FORM_STATE),
|
||||
isLoading: isFunction(_options.defaultValues),
|
||||
isValidating: false,
|
||||
isSubmitted: false,
|
||||
isSubmitting: false,
|
||||
isSubmitSuccessful: false,
|
||||
isValid: false,
|
||||
touchedFields: {},
|
||||
dirtyFields: {},
|
||||
validatingFields: {},
|
||||
errors: _options.errors || {},
|
||||
disabled: _options.disabled || false,
|
||||
};
|
||||
@@ -811,6 +868,7 @@ function createFormControl(props = {}) {
|
||||
unMount: new Set(),
|
||||
array: new Set(),
|
||||
watch: new Set(),
|
||||
registerName: new Set(),
|
||||
};
|
||||
let delayErrorCallback;
|
||||
let timer = 0;
|
||||
@@ -852,7 +910,11 @@ function createFormControl(props = {}) {
|
||||
_updateIsValidating();
|
||||
}
|
||||
else {
|
||||
isValid = await executeBuiltInValidation(_fields, true);
|
||||
isValid = await executeBuiltInValidation({
|
||||
fields: _fields,
|
||||
onlyCheckValid: true,
|
||||
eventType: EVENTS.VALID,
|
||||
});
|
||||
}
|
||||
if (isValid !== _formState.isValid) {
|
||||
_subjects.state.next({
|
||||
@@ -880,6 +942,9 @@ function createFormControl(props = {}) {
|
||||
});
|
||||
}
|
||||
};
|
||||
const _updateDirtyFields = () => {
|
||||
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
|
||||
};
|
||||
const _setFieldArray = (name, values = [], method, args, shouldSetValues = true, shouldUpdateFieldsAndState = true) => {
|
||||
if (args && method && !_options.disabled) {
|
||||
_state.action = true;
|
||||
@@ -901,7 +966,7 @@ function createFormControl(props = {}) {
|
||||
shouldSetValues && set(_formState.touchedFields, name, touchedFields);
|
||||
}
|
||||
if (_proxyFormState.dirtyFields || _proxySubscribeFormState.dirtyFields) {
|
||||
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
|
||||
_updateDirtyFields();
|
||||
}
|
||||
_subjects.state.next({
|
||||
name,
|
||||
@@ -928,16 +993,53 @@ function createFormControl(props = {}) {
|
||||
isValid: false,
|
||||
});
|
||||
};
|
||||
const hasExplicitNullIntermediate = (name) => {
|
||||
const segments = isKey(name) ? [name] : stringToPath(name);
|
||||
let formValues = _formValues;
|
||||
let defaultValues = _defaultValues;
|
||||
for (let i = 0; i < segments.length - 1; i++) {
|
||||
const key = segments[i];
|
||||
formValues = isNullOrUndefined(formValues) ? formValues : formValues[key];
|
||||
defaultValues = isNullOrUndefined(defaultValues)
|
||||
? defaultValues
|
||||
: defaultValues[key];
|
||||
if (formValues === null && defaultValues !== null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const updateValidAndValue = (name, shouldSkipSetValueAs, value, ref) => {
|
||||
const field = get(_fields, name);
|
||||
if (field) {
|
||||
if (hasExplicitNullIntermediate(name)) {
|
||||
return;
|
||||
}
|
||||
const wasUnsetInFormValues = isUndefined(get(_formValues, name));
|
||||
const defaultValue = get(_formValues, name, isUndefined(value) ? get(_defaultValues, name) : value);
|
||||
isUndefined(defaultValue) ||
|
||||
(ref && ref.defaultChecked) ||
|
||||
shouldSkipSetValueAs
|
||||
? set(_formValues, name, shouldSkipSetValueAs ? defaultValue : getFieldValue(field._f))
|
||||
: setFieldValue(name, defaultValue);
|
||||
_state.mount && !_state.action && _setValid();
|
||||
if (_state.mount && !_state.action) {
|
||||
_setValid();
|
||||
// Re-registering a field after a prior unregister puts its key back
|
||||
// into _formValues, which can flip isDirty back to false (#13397).
|
||||
// Only run when we are currently dirty, otherwise an initial register
|
||||
// for a field with no defaultValue would flip isDirty to true. Reset
|
||||
// paths repopulate _formValues before re-register, so the key is
|
||||
// present then and this branch is skipped (preserves keepDirty).
|
||||
if (wasUnsetInFormValues &&
|
||||
_formState.isDirty &&
|
||||
(_proxyFormState.isDirty || _proxySubscribeFormState.isDirty)) {
|
||||
const isDirty = _getDirty();
|
||||
if (!isDirty) {
|
||||
_formState.isDirty = false;
|
||||
_subjects.state.next({ ..._formState });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const updateTouchAndDirty = (name, fieldValue, isBlurEvent, shouldDirty, shouldRender) => {
|
||||
@@ -955,9 +1057,14 @@ function createFormControl(props = {}) {
|
||||
}
|
||||
const isCurrentFieldPristine = deepEqual(get(_defaultValues, name), fieldValue);
|
||||
isPreviousDirty = !!get(_formState.dirtyFields, name);
|
||||
isCurrentFieldPristine
|
||||
? unset(_formState.dirtyFields, name)
|
||||
: set(_formState.dirtyFields, name, true);
|
||||
if (isCurrentFieldPristine !== _formState.isDirty) {
|
||||
_formState.dirtyFields = getDirtyFields(_defaultValues, _formValues);
|
||||
}
|
||||
else {
|
||||
isCurrentFieldPristine
|
||||
? unset(_formState.dirtyFields, name)
|
||||
: set(_formState.dirtyFields, name, true);
|
||||
}
|
||||
output.dirtyFields = _formState.dirtyFields;
|
||||
shouldUpdateField =
|
||||
shouldUpdateField ||
|
||||
@@ -1015,8 +1122,7 @@ function createFormControl(props = {}) {
|
||||
};
|
||||
const _runSchema = async (name) => {
|
||||
_updateIsValidating(name, true);
|
||||
const result = await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
|
||||
return result;
|
||||
return await _options.resolver(_formValues, _options.context, getResolverOptions(name || _names.mount, _fields, _options.criteriaMode, _options.shouldUseNativeValidation));
|
||||
};
|
||||
const executeSchemaAndUpdateState = async (names) => {
|
||||
const { errors } = await _runSchema(names);
|
||||
@@ -1025,7 +1131,11 @@ function createFormControl(props = {}) {
|
||||
for (const name of names) {
|
||||
const error = get(errors, name);
|
||||
error
|
||||
? set(_formState.errors, name, error)
|
||||
? _names.array.has(name) &&
|
||||
isObject(error) &&
|
||||
!Object.keys(error).some((key) => !Number.isNaN(Number(key)))
|
||||
? updateFieldArrayRootError(_formState.errors, { [name]: error }, name)
|
||||
: set(_formState.errors, name, error)
|
||||
: unset(_formState.errors, name);
|
||||
}
|
||||
}
|
||||
@@ -1034,9 +1144,55 @@ function createFormControl(props = {}) {
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
const executeBuiltInValidation = async (fields, shouldOnlyCheckValid, context = {
|
||||
const validateForm = async ({ name, eventType, }) => {
|
||||
if (props.validate) {
|
||||
const result = await props.validate({
|
||||
formValues: _formValues,
|
||||
formState: _formState,
|
||||
name,
|
||||
eventType,
|
||||
});
|
||||
if (isObject(result)) {
|
||||
for (const key in result) {
|
||||
const error = result[key];
|
||||
if (error) {
|
||||
setError(`${FORM_ERROR_TYPE}.${key}`, {
|
||||
message: isString(error.message) ? error.message : '',
|
||||
type: error.type || INPUT_VALIDATION_RULES.validate,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isString(result) || !result) {
|
||||
setError(FORM_ERROR_TYPE, {
|
||||
message: result || '',
|
||||
type: INPUT_VALIDATION_RULES.validate,
|
||||
});
|
||||
}
|
||||
else {
|
||||
clearErrors(FORM_ERROR_TYPE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const executeBuiltInValidation = async ({ fields, onlyCheckValid, name, eventType, context = {
|
||||
valid: true,
|
||||
}) => {
|
||||
runRootValidation: false,
|
||||
}, }) => {
|
||||
if (props.validate) {
|
||||
context.runRootValidation = true;
|
||||
const result = await validateForm({
|
||||
name,
|
||||
eventType,
|
||||
});
|
||||
if (!result) {
|
||||
context.valid = false;
|
||||
if (onlyCheckValid) {
|
||||
return context.valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const name in fields) {
|
||||
const field = fields[name];
|
||||
if (field) {
|
||||
@@ -1044,28 +1200,41 @@ function createFormControl(props = {}) {
|
||||
if (_f) {
|
||||
const isFieldArrayRoot = _names.array.has(_f.name);
|
||||
const isPromiseFunction = field._f && hasPromiseValidation(field._f);
|
||||
if (isPromiseFunction && _proxyFormState.validatingFields) {
|
||||
const shouldTrackIsValidatingState = _proxyFormState.validatingFields ||
|
||||
_proxyFormState.isValidating ||
|
||||
_proxySubscribeFormState.validatingFields ||
|
||||
_proxySubscribeFormState.isValidating;
|
||||
if (isPromiseFunction && shouldTrackIsValidatingState) {
|
||||
_updateIsValidating([_f.name], true);
|
||||
}
|
||||
const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !shouldOnlyCheckValid, isFieldArrayRoot);
|
||||
if (isPromiseFunction && _proxyFormState.validatingFields) {
|
||||
const fieldError = await validateField(field, _names.disabled, _formValues, shouldDisplayAllAssociatedErrors, _options.shouldUseNativeValidation && !onlyCheckValid, isFieldArrayRoot);
|
||||
if (isPromiseFunction && shouldTrackIsValidatingState) {
|
||||
_updateIsValidating([_f.name]);
|
||||
}
|
||||
if (fieldError[_f.name]) {
|
||||
context.valid = false;
|
||||
if (shouldOnlyCheckValid || props.shouldUseNativeValidation) {
|
||||
if (onlyCheckValid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
!shouldOnlyCheckValid &&
|
||||
!onlyCheckValid &&
|
||||
(get(fieldError, _f.name)
|
||||
? isFieldArrayRoot
|
||||
? updateFieldArrayRootError(_formState.errors, fieldError, _f.name)
|
||||
: set(_formState.errors, _f.name, fieldError[_f.name])
|
||||
: unset(_formState.errors, _f.name));
|
||||
if (props.shouldUseNativeValidation && fieldError[_f.name]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
!isEmptyObject(fieldValue) &&
|
||||
(await executeBuiltInValidation(fieldValue, shouldOnlyCheckValid, context));
|
||||
(await executeBuiltInValidation({
|
||||
context,
|
||||
onlyCheckValid,
|
||||
fields: fieldValue,
|
||||
name: name,
|
||||
eventType,
|
||||
}));
|
||||
}
|
||||
}
|
||||
return context.valid;
|
||||
@@ -1094,7 +1263,7 @@ function createFormControl(props = {}) {
|
||||
: defaultValue),
|
||||
}, isGlobal, defaultValue);
|
||||
const _getFieldArray = (name) => compact(get(_state.mount ? _formValues : _defaultValues, name, _options.shouldUnregister ? get(_defaultValues, name, []) : []));
|
||||
const setFieldValue = (name, value, options = {}) => {
|
||||
const setFieldValue = (name, value, options = {}, skipClone = false) => {
|
||||
const field = get(_fields, name);
|
||||
let fieldValue = value;
|
||||
if (field) {
|
||||
@@ -1135,7 +1304,7 @@ function createFormControl(props = {}) {
|
||||
if (!fieldReference.ref.type) {
|
||||
_subjects.state.next({
|
||||
name,
|
||||
values: cloneObject(_formValues),
|
||||
values: skipClone ? _formValues : cloneObject(_formValues),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1145,7 +1314,7 @@ function createFormControl(props = {}) {
|
||||
updateTouchAndDirty(name, fieldValue, options.shouldTouch, options.shouldDirty, true);
|
||||
options.shouldValidate && trigger(name);
|
||||
};
|
||||
const setValues = (name, value, options) => {
|
||||
const setFieldValues = (name, value, options, skipClone = false) => {
|
||||
for (const fieldKey in value) {
|
||||
if (!value.hasOwnProperty(fieldKey)) {
|
||||
return;
|
||||
@@ -1157,49 +1326,79 @@ function createFormControl(props = {}) {
|
||||
isObject(fieldValue) ||
|
||||
(field && !field._f)) &&
|
||||
!isDateObject(fieldValue)
|
||||
? setValues(fieldName, fieldValue, options)
|
||||
: setFieldValue(fieldName, fieldValue, options);
|
||||
? setFieldValues(fieldName, fieldValue, options, skipClone)
|
||||
: setFieldValue(fieldName, fieldValue, options, skipClone);
|
||||
}
|
||||
};
|
||||
const setValue = (name, value, options = {}) => {
|
||||
const _setValue = (name, value, options, skipClone) => {
|
||||
const field = get(_fields, name);
|
||||
const isFieldArray = _names.array.has(name);
|
||||
const cloneValue = cloneObject(value);
|
||||
set(_formValues, name, cloneValue);
|
||||
const cloneValue = skipClone ? value : cloneObject(value);
|
||||
const previousValue = get(_formValues, name);
|
||||
const isValueUnchanged = deepEqual(previousValue, cloneValue);
|
||||
if (!isValueUnchanged) {
|
||||
set(_formValues, name, cloneValue);
|
||||
}
|
||||
if (isFieldArray) {
|
||||
_subjects.array.next({
|
||||
name,
|
||||
values: cloneObject(_formValues),
|
||||
values: skipClone ? _formValues : cloneObject(_formValues),
|
||||
});
|
||||
if ((_proxyFormState.isDirty ||
|
||||
_proxyFormState.dirtyFields ||
|
||||
_proxySubscribeFormState.isDirty ||
|
||||
_proxySubscribeFormState.dirtyFields) &&
|
||||
options.shouldDirty) {
|
||||
_updateDirtyFields();
|
||||
_subjects.state.next({
|
||||
name,
|
||||
dirtyFields: getDirtyFields(_defaultValues, _formValues),
|
||||
dirtyFields: _formState.dirtyFields,
|
||||
isDirty: _getDirty(name, cloneValue),
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
field && !field._f && !isNullOrUndefined(cloneValue)
|
||||
? setValues(name, cloneValue, options)
|
||||
: setFieldValue(name, cloneValue, options);
|
||||
const isEmpty = (Array.isArray(cloneValue) && !cloneValue.length) ||
|
||||
isEmptyObject(cloneValue);
|
||||
if (!field || field._f || isNullOrUndefined(cloneValue) || isEmpty) {
|
||||
setFieldValue(name, cloneValue, options, skipClone);
|
||||
}
|
||||
else {
|
||||
setFieldValues(name, cloneValue, options, skipClone);
|
||||
}
|
||||
}
|
||||
if (isWatched(name, _names)) {
|
||||
if (!isValueUnchanged) {
|
||||
const watched = isWatched(name, _names);
|
||||
const values = skipClone ? _formValues : cloneObject(_formValues);
|
||||
_subjects.state.next({
|
||||
...(watched && _formState),
|
||||
name: _state.mount || watched ? name : undefined,
|
||||
values,
|
||||
});
|
||||
}
|
||||
};
|
||||
const setValue = (name, value, options = {}) => _setValue(name, value, options, false);
|
||||
const setValues = (formValues, options = {}) => {
|
||||
const updatedFormValues = isFunction(formValues)
|
||||
? formValues(_formValues)
|
||||
: formValues;
|
||||
if (!deepEqual(_formValues, updatedFormValues)) {
|
||||
_formValues = {
|
||||
..._formValues,
|
||||
...updatedFormValues,
|
||||
};
|
||||
for (const fieldName of _names.mount) {
|
||||
_setValue(fieldName, get(updatedFormValues, fieldName), options, true);
|
||||
}
|
||||
_subjects.state.next({
|
||||
..._formState,
|
||||
name,
|
||||
values: cloneObject(_formValues),
|
||||
});
|
||||
}
|
||||
else {
|
||||
_subjects.state.next({
|
||||
name: _state.mount ? name : undefined,
|
||||
values: cloneObject(_formValues),
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
values: _formValues,
|
||||
});
|
||||
if (options.shouldValidate) {
|
||||
_setValid();
|
||||
}
|
||||
}
|
||||
};
|
||||
const onChange = async (event) => {
|
||||
@@ -1224,6 +1423,7 @@ function createFormControl(props = {}) {
|
||||
: getEventValue(event);
|
||||
const isBlurEvent = event.type === EVENTS.BLUR || event.type === EVENTS.FOCUS_OUT;
|
||||
const shouldSkipValidation = (!hasValidation(field._f) &&
|
||||
!props.validate &&
|
||||
!_options.resolver &&
|
||||
!get(_formState.errors, name) &&
|
||||
!field._f.deps) ||
|
||||
@@ -1261,18 +1461,26 @@ function createFormControl(props = {}) {
|
||||
return (shouldRender &&
|
||||
_subjects.state.next({ name, ...(watched ? {} : fieldState) }));
|
||||
}
|
||||
if (!_options.resolver && props.validate) {
|
||||
await validateForm({
|
||||
name: name,
|
||||
eventType: event.type,
|
||||
});
|
||||
}
|
||||
!isBlurEvent && watched && _subjects.state.next({ ..._formState });
|
||||
if (_options.resolver) {
|
||||
const { errors } = await _runSchema([name]);
|
||||
_updateIsValidating([name]);
|
||||
_updateIsFieldValueUpdated(fieldValue);
|
||||
if (isFieldValueUpdated) {
|
||||
const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
|
||||
const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
|
||||
error = errorLookupResult.error;
|
||||
name = errorLookupResult.name;
|
||||
isValid = isEmptyObject(errors);
|
||||
if (!isFieldValueUpdated) {
|
||||
!isEmptyObject(fieldState) && _subjects.state.next(fieldState);
|
||||
return;
|
||||
}
|
||||
const previousErrorLookupResult = schemaErrorLookup(_formState.errors, _fields, name);
|
||||
const errorLookupResult = schemaErrorLookup(errors, _fields, previousErrorLookupResult.name || name);
|
||||
error = errorLookupResult.error;
|
||||
name = errorLookupResult.name;
|
||||
isValid = isEmptyObject(errors);
|
||||
}
|
||||
else {
|
||||
_updateIsValidating([name], true);
|
||||
@@ -1285,7 +1493,12 @@ function createFormControl(props = {}) {
|
||||
}
|
||||
else if (_proxyFormState.isValid ||
|
||||
_proxySubscribeFormState.isValid) {
|
||||
isValid = await executeBuiltInValidation(_fields, true);
|
||||
isValid = await executeBuiltInValidation({
|
||||
fields: _fields,
|
||||
onlyCheckValid: true,
|
||||
name: name,
|
||||
eventType: event.type,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1318,12 +1531,19 @@ function createFormControl(props = {}) {
|
||||
else if (name) {
|
||||
validationResult = (await Promise.all(fieldNames.map(async (fieldName) => {
|
||||
const field = get(_fields, fieldName);
|
||||
return await executeBuiltInValidation(field && field._f ? { [fieldName]: field } : field);
|
||||
return await executeBuiltInValidation({
|
||||
fields: field && field._f ? { [fieldName]: field } : field,
|
||||
eventType: EVENTS.TRIGGER,
|
||||
});
|
||||
}))).every(Boolean);
|
||||
!(!validationResult && !_formState.isValid) && _setValid();
|
||||
}
|
||||
else {
|
||||
validationResult = isValid = await executeBuiltInValidation(_fields);
|
||||
validationResult = isValid = await executeBuiltInValidation({
|
||||
fields: _fields,
|
||||
name,
|
||||
eventType: EVENTS.TRIGGER,
|
||||
});
|
||||
}
|
||||
_subjects.state.next({
|
||||
...(!isString(name) ||
|
||||
@@ -1360,11 +1580,24 @@ function createFormControl(props = {}) {
|
||||
isTouched: !!get((formState || _formState).touchedFields, name),
|
||||
});
|
||||
const clearErrors = (name) => {
|
||||
name &&
|
||||
convertToArrayPayload(name).forEach((inputName) => unset(_formState.errors, inputName));
|
||||
_subjects.state.next({
|
||||
errors: name ? _formState.errors : {},
|
||||
});
|
||||
const names = name ? convertToArrayPayload(name) : undefined;
|
||||
names === null || names === void 0 ? void 0 : names.forEach((inputName) => unset(_formState.errors, inputName));
|
||||
if (names) {
|
||||
// Emit for each cleared field with the field name so that
|
||||
// shouldSubscribeByName can filter and avoid broad re-renders
|
||||
names.forEach((inputName) => {
|
||||
_subjects.state.next({
|
||||
name: inputName,
|
||||
errors: _formState.errors,
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Clear all errors - emit without name to notify all subscribers
|
||||
_subjects.state.next({
|
||||
errors: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
const setError = (name, error, options) => {
|
||||
const ref = (get(_fields, name, { _f: {} })._f || {}).ref;
|
||||
@@ -1386,15 +1619,16 @@ function createFormControl(props = {}) {
|
||||
const watch = (name, defaultValue) => isFunction(name)
|
||||
? _subjects.state.subscribe({
|
||||
next: (payload) => 'values' in payload &&
|
||||
name(_getWatch(undefined, defaultValue), payload),
|
||||
name(payload.values || _getWatch(undefined, defaultValue), payload),
|
||||
})
|
||||
: _getWatch(name, defaultValue, true);
|
||||
const _subscribe = (props) => _subjects.state.subscribe({
|
||||
next: (formState) => {
|
||||
if (shouldSubscribeByName(props.name, formState.name, props.exact) &&
|
||||
shouldRenderFormState(formState, props.formState || _proxyFormState, _setFormState, props.reRenderRoot)) {
|
||||
const snapshot = { ..._formValues };
|
||||
props.callback({
|
||||
values: { ..._formValues },
|
||||
values: snapshot,
|
||||
..._formState,
|
||||
...formState,
|
||||
defaultValues: _defaultValues,
|
||||
@@ -1446,12 +1680,17 @@ function createFormControl(props = {}) {
|
||||
if ((isBoolean(disabled) && _state.mount) ||
|
||||
!!disabled ||
|
||||
_names.disabled.has(name)) {
|
||||
const wasDisabled = _names.disabled.has(name);
|
||||
const isDisabled = !!disabled;
|
||||
const disabledStateChanged = wasDisabled !== isDisabled;
|
||||
disabled ? _names.disabled.add(name) : _names.disabled.delete(name);
|
||||
disabledStateChanged && _state.mount && !_state.action && _setValid();
|
||||
}
|
||||
};
|
||||
const register = (name, options = {}) => {
|
||||
let field = get(_fields, name);
|
||||
const disabledIsDefined = isBoolean(options.disabled) || isBoolean(_options.disabled);
|
||||
const shouldRevalidateRemount = !_names.registerName.has(name) && field && field._f && !field._f.mount;
|
||||
set(_fields, name, {
|
||||
...(field || {}),
|
||||
_f: {
|
||||
@@ -1462,7 +1701,7 @@ function createFormControl(props = {}) {
|
||||
},
|
||||
});
|
||||
_names.mount.add(name);
|
||||
if (field) {
|
||||
if (field && !shouldRevalidateRemount) {
|
||||
_setDisabledField({
|
||||
disabled: isBoolean(options.disabled)
|
||||
? options.disabled
|
||||
@@ -1492,7 +1731,9 @@ function createFormControl(props = {}) {
|
||||
onBlur: onChange,
|
||||
ref: (ref) => {
|
||||
if (ref) {
|
||||
_names.registerName.add(name);
|
||||
register(name, options);
|
||||
_names.registerName.delete(name);
|
||||
field = get(_fields, name);
|
||||
const fieldRef = isUndefined(ref.value)
|
||||
? ref.querySelectorAll
|
||||
@@ -1536,6 +1777,7 @@ function createFormControl(props = {}) {
|
||||
};
|
||||
};
|
||||
const _focusError = () => _options.shouldFocusError &&
|
||||
!_options.shouldUseNativeValidation &&
|
||||
iterateFieldsByAction(_fields, _focusInput, _names.mount);
|
||||
const _disableForm = (disabled) => {
|
||||
if (isBoolean(disabled)) {
|
||||
@@ -1571,14 +1813,17 @@ function createFormControl(props = {}) {
|
||||
fieldValues = cloneObject(values);
|
||||
}
|
||||
else {
|
||||
await executeBuiltInValidation(_fields);
|
||||
await executeBuiltInValidation({
|
||||
fields: _fields,
|
||||
eventType: EVENTS.SUBMIT,
|
||||
});
|
||||
}
|
||||
if (_names.disabled.size) {
|
||||
for (const name of _names.disabled) {
|
||||
unset(fieldValues, name);
|
||||
}
|
||||
}
|
||||
unset(_formState.errors, 'root');
|
||||
unset(_formState.errors, ROOT_ERROR_TYPE);
|
||||
if (isEmptyObject(_formState.errors)) {
|
||||
_subjects.state.next({
|
||||
errors: {},
|
||||
@@ -1637,7 +1882,7 @@ function createFormControl(props = {}) {
|
||||
const updatedValues = formValues ? cloneObject(formValues) : _defaultValues;
|
||||
const cloneUpdatedValues = cloneObject(updatedValues);
|
||||
const isEmptyResetValues = isEmptyObject(formValues);
|
||||
const values = isEmptyResetValues ? _defaultValues : cloneUpdatedValues;
|
||||
const values = cloneUpdatedValues;
|
||||
if (!keepStateOptions.keepDefaultValues) {
|
||||
_defaultValues = updatedValues;
|
||||
}
|
||||
@@ -1686,11 +1931,19 @@ function createFormControl(props = {}) {
|
||||
_fields = {};
|
||||
}
|
||||
}
|
||||
_formValues = _options.shouldUnregister
|
||||
? keepStateOptions.keepDefaultValues
|
||||
if (_options.shouldUnregister) {
|
||||
_formValues = keepStateOptions.keepDefaultValues
|
||||
? cloneObject(_defaultValues)
|
||||
: {}
|
||||
: cloneObject(values);
|
||||
: {};
|
||||
if (keepStateOptions.keepFieldsRef) {
|
||||
for (const fieldName of _names.mount) {
|
||||
set(_formValues, fieldName, get(values, fieldName));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_formValues = cloneObject(values);
|
||||
}
|
||||
_subjects.array.next({
|
||||
values: { ...values },
|
||||
});
|
||||
@@ -1702,6 +1955,7 @@ function createFormControl(props = {}) {
|
||||
mount: keepStateOptions.keepDirtyValues ? _names.mount : new Set(),
|
||||
unMount: new Set(),
|
||||
array: new Set(),
|
||||
registerName: new Set(),
|
||||
disabled: new Set(),
|
||||
watch: new Set(),
|
||||
watchAll: false,
|
||||
@@ -1729,8 +1983,10 @@ function createFormControl(props = {}) {
|
||||
? false
|
||||
: keepStateOptions.keepDirty
|
||||
? _formState.isDirty
|
||||
: !!(keepStateOptions.keepDefaultValues &&
|
||||
!deepEqual(formValues, _defaultValues)),
|
||||
: keepStateOptions.keepValues
|
||||
? _getDirty()
|
||||
: !!(keepStateOptions.keepDefaultValues &&
|
||||
!deepEqual(formValues, _defaultValues)),
|
||||
isSubmitted: keepStateOptions.keepIsSubmitted
|
||||
? _formState.isSubmitted
|
||||
: false,
|
||||
@@ -1791,6 +2047,21 @@ function createFormControl(props = {}) {
|
||||
isLoading: false,
|
||||
});
|
||||
});
|
||||
const resetDefaultValues = (values, options = {}) => {
|
||||
_defaultValues = cloneObject(values);
|
||||
if (!options.keepDirty) {
|
||||
const newDirtyFields = getDirtyFields(_defaultValues, _formValues);
|
||||
_formState.dirtyFields = newDirtyFields;
|
||||
_formState.isDirty = !isEmptyObject(newDirtyFields);
|
||||
}
|
||||
if (!options.keepIsValid) {
|
||||
_setValid();
|
||||
}
|
||||
_subjects.state.next({
|
||||
..._formState,
|
||||
defaultValues: _defaultValues,
|
||||
});
|
||||
};
|
||||
const methods = {
|
||||
control: {
|
||||
register,
|
||||
@@ -1855,9 +2126,11 @@ function createFormControl(props = {}) {
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
setValues,
|
||||
getValues,
|
||||
reset,
|
||||
resetField,
|
||||
resetDefaultValues,
|
||||
clearErrors,
|
||||
unregister,
|
||||
setError,
|
||||
|
||||
Reference in New Issue
Block a user