@mantine/form
Version:
Mantine form management library
252 lines (248 loc) • 9.49 kB
JavaScript
'use client';
'use strict';
var react = require('react');
var actions = require('./actions/actions.cjs');
var getInputOnChange = require('./get-input-on-change/get-input-on-change.cjs');
var useFormErrors = require('./hooks/use-form-errors/use-form-errors.cjs');
var useFormList = require('./hooks/use-form-list/use-form-list.cjs');
var useFormStatus = require('./hooks/use-form-status/use-form-status.cjs');
var useFormValues = require('./hooks/use-form-values/use-form-values.cjs');
var useFormWatch = require('./hooks/use-form-watch/use-form-watch.cjs');
var getPath = require('./paths/get-path.cjs');
require('klona/full');
var getDataPath = require('./paths/get-data-path.cjs');
var validateValues = require('./validate/validate-values.cjs');
var validateFieldValue = require('./validate/validate-field-value.cjs');
var shouldValidateOnChange = require('./validate/should-validate-on-change.cjs');
function useForm({
name,
mode = "controlled",
initialValues,
initialErrors = {},
initialDirty = {},
initialTouched = {},
clearInputErrorOnChange = true,
validateInputOnChange = false,
validateInputOnBlur = false,
onValuesChange,
transformValues = ((values) => values),
enhanceGetInputProps,
validate: rules,
onSubmitPreventDefault = "always",
touchTrigger = "change",
cascadeUpdates = false
} = {}) {
const $errors = useFormErrors.useFormErrors(initialErrors);
const $values = useFormValues.useFormValues({ initialValues, onValuesChange, mode });
const $status = useFormStatus.useFormStatus({ initialDirty, initialTouched, $values, mode });
const $list = useFormList.useFormList({ $values, $errors, $status });
const $watch = useFormWatch.useFormWatch({ $status, cascadeUpdates });
const [formKey, setFormKey] = react.useState(0);
const [fieldKeys, setFieldKeys] = react.useState({});
const [submitting, setSubmitting] = react.useState(false);
const reset = react.useCallback(() => {
$values.resetValues();
$errors.clearErrors();
$status.resetDirty();
$status.resetTouched();
mode === "uncontrolled" && setFormKey((key2) => key2 + 1);
}, []);
const handleValuesChanges = react.useCallback(
(previousValues) => {
clearInputErrorOnChange && $errors.clearErrors();
mode === "uncontrolled" && setFormKey((key2) => key2 + 1);
Object.keys($watch.subscribers.current).forEach((path) => {
const value = getPath.getPath(path, $values.refValues.current);
const previousValue = getPath.getPath(path, previousValues);
if (value !== previousValue) {
$watch.getFieldSubscribers(path).forEach((cb) => cb({ previousValues, updatedValues: $values.refValues.current }));
}
});
},
[clearInputErrorOnChange]
);
const initialize = react.useCallback(
(values) => {
const previousValues = $values.refValues.current;
$values.initialize(values, () => mode === "uncontrolled" && setFormKey((key2) => key2 + 1));
handleValuesChanges(previousValues);
},
[handleValuesChanges]
);
const setFieldValue = react.useCallback(
(path, value, options) => {
const shouldValidate = shouldValidateOnChange.shouldValidateOnChange(path, validateInputOnChange);
const resolvedValue = value instanceof Function ? value(getPath.getPath(path, $values.refValues.current)) : value;
$status.setCalculatedFieldDirty(path, resolvedValue);
touchTrigger === "change" && $status.setFieldTouched(path, true);
!shouldValidate && clearInputErrorOnChange && $errors.clearFieldError(path);
$values.setFieldValue({
path,
value,
updateState: mode === "controlled",
subscribers: [
...$watch.getFieldSubscribers(path),
shouldValidate ? (payload) => {
const validationResults = validateFieldValue.validateFieldValue(path, rules, payload.updatedValues);
validationResults.hasError ? $errors.setFieldError(path, validationResults.error) : $errors.clearFieldError(path);
} : null,
options?.forceUpdate !== false && mode !== "controlled" ? () => setFieldKeys((keys) => ({
...keys,
[path]: (keys[path] || 0) + 1
})) : null
]
});
},
[onValuesChange, rules]
);
const setValues = react.useCallback(
(values) => {
const previousValues = $values.refValues.current;
$values.setValues({ values, updateState: mode === "controlled" });
handleValuesChanges(previousValues);
},
[onValuesChange, handleValuesChanges]
);
const validate = react.useCallback(() => {
const results = validateValues.validateValues(rules, $values.refValues.current);
$errors.setErrors(results.errors);
return results;
}, [rules]);
const validateField = react.useCallback(
(path) => {
const results = validateFieldValue.validateFieldValue(path, rules, $values.refValues.current);
results.hasError ? $errors.setFieldError(path, results.error) : $errors.clearFieldError(path);
return results;
},
[rules]
);
const getInputProps = (path, { type = "input", withError = true, withFocus = true, ...otherOptions } = {}) => {
const onChange = getInputOnChange.getInputOnChange(
(value) => setFieldValue(path, value, { forceUpdate: false })
);
const payload = { onChange, "data-path": getDataPath.getDataPath(name, path) };
if (withError) {
payload.error = $errors.errorsState[path];
}
if (type === "checkbox") {
payload[mode === "controlled" ? "checked" : "defaultChecked"] = getPath.getPath(
path,
$values.refValues.current
);
} else {
payload[mode === "controlled" ? "value" : "defaultValue"] = getPath.getPath(
path,
$values.refValues.current
);
}
if (withFocus) {
payload.onFocus = () => $status.setFieldTouched(path, true);
payload.onBlur = () => {
if (shouldValidateOnChange.shouldValidateOnChange(path, validateInputOnBlur)) {
const validationResults = validateFieldValue.validateFieldValue(path, rules, $values.refValues.current);
validationResults.hasError ? $errors.setFieldError(path, validationResults.error) : $errors.clearFieldError(path);
}
};
}
return Object.assign(
payload,
enhanceGetInputProps?.({
inputProps: payload,
field: path,
options: { type, withError, withFocus, ...otherOptions },
form
})
);
};
const onSubmit = (handleSubmit, handleValidationFailure) => (event) => {
if (onSubmitPreventDefault === "always") {
event?.preventDefault();
}
const results = validate();
if (results.hasErrors) {
if (onSubmitPreventDefault === "validation-failed") {
event?.preventDefault();
}
handleValidationFailure?.(results.errors, $values.refValues.current, event);
} else {
const submitResult = handleSubmit?.(
transformValues($values.refValues.current),
event
);
if (submitResult instanceof Promise) {
setSubmitting(true);
submitResult.finally(() => setSubmitting(false));
}
}
};
const getTransformedValues = (input) => transformValues(input || $values.refValues.current);
const onReset = react.useCallback((event) => {
event.preventDefault();
reset();
}, []);
const isValid = react.useCallback(
(path) => path ? !validateFieldValue.validateFieldValue(path, rules, $values.refValues.current).hasError : !validateValues.validateValues(rules, $values.refValues.current).hasErrors,
[rules]
);
const key = (path) => `${formKey}-${String(path)}-${fieldKeys[String(path)] || 0}`;
const getInputNode = react.useCallback(
(path) => document.querySelector(`[data-path="${getDataPath.getDataPath(name, path)}"]`),
[]
);
const resetField = react.useCallback(
(path) => {
$values.resetField(path, [
mode !== "controlled" ? () => setFieldKeys((keys) => ({
...keys,
[path]: (keys[path] || 0) + 1
})) : null
]);
},
[$values.resetField, mode, setFieldKeys]
);
const form = {
watch: $watch.watch,
initialized: $values.initialized.current,
values: mode === "uncontrolled" ? $values.refValues.current : $values.stateValues,
getValues: $values.getValues,
getInitialValues: $values.getValuesSnapshot,
setInitialValues: $values.setValuesSnapshot,
resetField,
initialize,
setValues,
setFieldValue,
submitting,
setSubmitting,
errors: $errors.errorsState,
setErrors: $errors.setErrors,
setFieldError: $errors.setFieldError,
clearFieldError: $errors.clearFieldError,
clearErrors: $errors.clearErrors,
resetDirty: $status.resetDirty,
setTouched: $status.setTouched,
setDirty: $status.setDirty,
isTouched: $status.isTouched,
resetTouched: $status.resetTouched,
isDirty: $status.isDirty,
getTouched: $status.getTouched,
getDirty: $status.getDirty,
reorderListItem: $list.reorderListItem,
insertListItem: $list.insertListItem,
removeListItem: $list.removeListItem,
replaceListItem: $list.replaceListItem,
reset,
validate,
validateField,
getInputProps,
onSubmit,
onReset,
isValid,
getTransformedValues,
key,
getInputNode
};
actions.useFormActions(name, form);
return form;
}
exports.useForm = useForm;
//# sourceMappingURL=use-form.cjs.map