@bluecateng/auto-forms
Version:
Auto-wired form hooks
134 lines • 3.53 kB
JavaScript
import { forwardRef, useCallback, useReducer } from 'react';
import PropTypes from 'prop-types';
import identity from 'lodash-es/identity';
import isNotNull from '../utils/isNotNull';
import FormContext from './FormContext';
import { jsx as _jsx } from "react/jsx-runtime";
const reducer = (state, {
type,
name,
payload
}) => {
switch (type) {
case 'SET_VALUE':
return {
...state,
values: {
...state.values,
[name]: payload
}
};
case 'SET_ERROR':
return {
...state,
errors: {
...state.errors,
[name]: payload
}
};
case 'SET_EXTRA':
return {
...state,
extras: {
...state.extras,
[name]: payload
}
};
case 'RESET':
return payload;
}
return state;
};
const getInitialState = ({
initialValues,
initialExtras
}) => ({
initialValues,
values: initialValues,
errors: {},
extras: initialExtras
});
const submitForm = ({
values,
extras,
initialValues
}, dispatch, rules, getActiveFields, extraValidation, onSubmit) => {
const setError = (name, payload) => dispatch({
type: 'SET_ERROR',
name,
payload
});
const keys = getActiveFields ? getActiveFields(values, extras) : Object.keys(values);
const errors = keys.reduce((o, key) => {
const rule = rules[key];
if (rule) {
o[key] = rule(values[key]);
}
return o;
}, {});
return Promise.resolve(extraValidation(errors, values, extras, initialValues)).then(errors => {
const passed = Object.entries(errors).reduce((r, [name, payload]) => isNotNull(payload) ? (setError(name, payload), false) : r, true);
if (!passed) {
return null;
}
const setErrors = errors => Object.entries(errors).forEach(([name, payload]) => setError(name, payload));
return onSubmit(values, {
initialValues,
extras,
setErrors
});
});
};
const resetForm = (initialValues, initialExtras, dispatch) => dispatch({
type: 'RESET',
payload: getInitialState({
initialValues,
initialExtras
})
});
const Form = /*#__PURE__*/forwardRef(({
initialValues,
initialExtras = {},
rules,
children,
getActiveFields,
extraValidation = identity,
onSubmit,
...props
}, ref) => {
const [state, dispatch] = useReducer(reducer, {
initialValues,
initialExtras
}, getInitialState);
const submit = useCallback(() => submitForm(state, dispatch, rules, getActiveFields, extraValidation, onSubmit), [state, rules, getActiveFields, extraValidation, onSubmit]);
const reset = useCallback(() => resetForm(initialValues, initialExtras, dispatch), [initialExtras, initialValues, dispatch]);
const handleSubmit = useCallback(event => (event.preventDefault(), submit()), [submit]);
const handleReset = useCallback(event => (event.preventDefault(), reset()), [reset]);
return /*#__PURE__*/_jsx(FormContext.Provider, {
value: {
rules,
state,
dispatch,
submit,
reset
},
children: /*#__PURE__*/_jsx("form", {
...props,
ref: ref,
onSubmit: handleSubmit,
onReset: handleReset,
children: children
})
});
});
Form.displayName = 'Form';
Form.propTypes = {
initialValues: PropTypes.object.isRequired,
initialExtras: PropTypes.object,
rules: PropTypes.object.isRequired,
children: PropTypes.node,
getActiveFields: PropTypes.func,
extraValidation: PropTypes.func,
onSubmit: PropTypes.func
};
export default Form;