informed
Version:
A lightweight framework and utility for building powerful forms in React applications
253 lines (240 loc) • 10.3 kB
JavaScript
import { objectWithoutProperties as _objectWithoutProperties, slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.js';
import React, { useMemo, useRef, useState, useEffect } from 'react';
import { FormController } from '../FormController.js';
import { FormControllerContext, FormApiContext, FormStateContext } from '../Context.js';
import { useUpdateEffect } from './useUpdateEffect.js';
import { useInformed } from './useInformed.js';
import { Debug } from '../debug.js';
var _excluded = ["onSubmit", "onReset", "onChange", "onSubmitFailure", "onValueChange", "onValueSet", "onValid", "onInvalid", "onValueModified", "initialValues", "validateFields", "autocomplete", "showErrorIfError", "showErrorIfTouched", "showErrorIfDirty", "validateOn", "validateOnMount", "formApiRef", "dontPreventDefault", "yupSchema", "allowEmptyStrings", "disabled", "preventEnter", "validateModified", "schema", "ajv", "ajvErrors", "components", "errorMessage", "fieldMap", "adapter", "name", "keepState", "keepStateIfRelevant", "focusOnInvalid", "scrollOnInvalid", "resetOnlyOnscreen", "debounceGather", "debounceError"];
var logger = Debug('informed:useForm' + '\t');
var useForm = function useForm(_ref) {
var _window;
var onSubmit = _ref.onSubmit,
onReset = _ref.onReset,
onChange = _ref.onChange,
onSubmitFailure = _ref.onSubmitFailure,
onValueChange = _ref.onValueChange,
onValueSet = _ref.onValueSet,
onValid = _ref.onValid,
onInvalid = _ref.onInvalid,
onValueModified = _ref.onValueModified,
userInitialValues = _ref.initialValues,
validateFields = _ref.validateFields,
autocomplete = _ref.autocomplete,
showErrorIfError = _ref.showErrorIfError,
showErrorIfTouched = _ref.showErrorIfTouched,
showErrorIfDirty = _ref.showErrorIfDirty,
validateOn = _ref.validateOn,
validateOnMount = _ref.validateOnMount,
formApiRef = _ref.formApiRef,
dontPreventDefault = _ref.dontPreventDefault,
yupSchema = _ref.yupSchema,
allowEmptyStrings = _ref.allowEmptyStrings,
disabled = _ref.disabled,
preventEnter = _ref.preventEnter,
validateModified = _ref.validateModified,
schema = _ref.schema,
ajv = _ref.ajv,
ajvErrors = _ref.ajvErrors,
components = _ref.components,
errorMessage = _ref.errorMessage,
fieldMap = _ref.fieldMap,
adapter = _ref.adapter,
name = _ref.name,
keepState = _ref.keepState,
keepStateIfRelevant = _ref.keepStateIfRelevant,
focusOnInvalid = _ref.focusOnInvalid,
scrollOnInvalid = _ref.scrollOnInvalid,
resetOnlyOnscreen = _ref.resetOnlyOnscreen,
debounceGather = _ref.debounceGather,
debounceError = _ref.debounceError,
userProps = _objectWithoutProperties(_ref, _excluded);
// Register this controller by name if we are in global context
var informed = useInformed();
var initialValues = useMemo(function () {
if (informed && name) {
var _informed$getSavedVal;
logger('Checking for saved values', informed.getSavedValues(name));
return (_informed$getSavedVal = informed.getSavedValues(name)) !== null && _informed$getSavedVal !== void 0 ? _informed$getSavedVal : userInitialValues;
}
return userInitialValues;
}, [userInitialValues]);
// If we have a schema allow user to add magic validation to the schema before passing it in!!
if (typeof window !== 'undefined' && (_window = window) !== null && _window !== void 0 && (_window = _window.informed) !== null && _window !== void 0 && _window.properties) {
var _window2;
var properties = (_window2 = window) === null || _window2 === void 0 || (_window2 = _window2.informed) === null || _window2 === void 0 ? void 0 : _window2.properties;
Object.keys(properties).forEach(function (key) {
if (schema.properties[key]) {
schema.properties[key]['ui:props'] = properties[key];
}
});
}
var formControllerOptions = {
initialValues: initialValues,
validateFields: validateFields,
autocomplete: autocomplete,
showErrorIfError: showErrorIfError,
showErrorIfTouched: showErrorIfTouched !== null && showErrorIfTouched !== void 0 ? showErrorIfTouched : true,
showErrorIfDirty: showErrorIfDirty,
validateOn: validateOn,
validateOnMount: validateOnMount,
// NEW STUFF
dontPreventDefault: dontPreventDefault,
yupSchema: yupSchema,
allowEmptyStrings: allowEmptyStrings,
disabled: disabled,
preventEnter: preventEnter,
schema: schema,
ajv: ajv,
ajvErrors: ajvErrors,
components: components,
errorMessage: errorMessage,
fieldMap: fieldMap,
adapter: adapter,
keepState: keepState,
keepStateIfRelevant: keepStateIfRelevant,
validateModified: validateModified,
focusOnInvalid: focusOnInvalid,
scrollOnInvalid: scrollOnInvalid,
resetOnlyOnscreen: resetOnlyOnscreen,
debounceGather: debounceGather,
debounceError: debounceError
};
var optionsRef = useRef();
optionsRef.current = formControllerOptions;
// Create form controller
var _useState = useState(function () {
return new FormController(optionsRef);
}),
_useState2 = _slicedToArray(_useState, 1),
formController = _useState2[0];
// Register for events
useEffect(function () {
var onChangeHandler = function onChangeHandler() {
return onChange && onChange(formController.getFormState());
};
var onResetHandler = function onResetHandler() {
return onReset && onReset(formController.getFormState());
};
var onSubmitHandler = function onSubmitHandler() {
return onSubmit && onSubmit(formController.getFormState());
};
var onFailureHandler = function onFailureHandler() {
return onSubmitFailure && onSubmitFailure(formController.getFormState());
};
var onValueChangeHandler = function onValueChangeHandler(n) {
return onValueChange && onValueChange(formController.getFormState(), n);
};
var onValueModifiedHandler = function onValueModifiedHandler(n) {
return onValueModified && onValueModified(formController.getFormState(), n);
};
var onValueSetHandler = function onValueSetHandler(n) {
return onValueSet && onValueSet(formController.getFormState(), n);
};
var onValidHandler = function onValidHandler() {
return onValid && onValid(formController.getFormState());
};
var onInvalidHandler = function onInvalidHandler() {
return onInvalid && onInvalid(formController.getFormState());
};
// Register for events
formController.on('field', onChangeHandler);
formController.on('reset', onResetHandler);
formController.on('submit', onSubmitHandler);
formController.on('failure', onFailureHandler);
formController.on('field-value', onValueChangeHandler);
formController.on('field-modified', onValueModifiedHandler);
formController.on('field-value-set', onValueSetHandler);
formController.on('valid', onValidHandler);
formController.on('invalid', onInvalidHandler);
// Unregister events
return function () {
formController.removeListener('field', onChangeHandler);
formController.removeListener('reset', onResetHandler);
formController.removeListener('submit', onSubmitHandler);
formController.removeListener('failure', onFailureHandler);
formController.removeListener('field-value', onValueChangeHandler);
formController.removeListener('field-modified', onValueModifiedHandler);
};
}, [onChange, onReset, onSubmit, onSubmitFailure, onValueChange, onValueModified]);
// Form state will be used to trigger rerenders
var _useState3 = useState(function () {
return formController.getFormState();
}),
_useState4 = _slicedToArray(_useState3, 2),
formState = _useState4[0],
setFormState = _useState4[1];
// Register for events for ALL fields!
useEffect(function () {
var listener = function listener(target) {
setFormState(_objectSpread2({}, formController.getFormState()));
if (informed) {
informed.inform(name, target);
}
};
formController.emitter.on('field', listener);
// Need initial state
setFormState(_objectSpread2({}, formController.getFormState()));
//Register this form if we need to
if (name && informed) {
informed.register(name, formController);
}
return function () {
formController.emitter.removeListener('field', listener);
if (name && informed) {
// informed.deregister(name);
informed.setSavedValues(name, formController.getFormState().values);
}
};
}, []);
// YES! this is important! Otherwise it would get a new formApi object every render
/// That would cause unessissarry re-renders! so do not remove useMemeo!
var formApi = useMemo(function () {
if (formApiRef) {
formApiRef.current = formController.getFormApi();
}
return formController.getFormApi();
}, []);
useUpdateEffect(function () {
// If the form is pristine then reset it when we get new initial values !
var _formApi$getFormState = formApi.getFormState(),
pristine = _formApi$getFormState.pristine;
if (pristine) {
logger('Resetting entire form as form is pristine and we got new initial values');
formApi.reset();
}
}, [initialValues]);
useUpdateEffect(function () {
if (disabled) {
formApi.disable();
} else {
formApi.enable();
}
}, [disabled]);
useEffect(function () {
logger('Mount');
formController.lockRemoval(false);
return function () {
// Important so we dont clear values!!!
formController.lockRemoval(true);
logger('Un-Mount');
};
}, []);
var render = function render(children) {
return /*#__PURE__*/React.createElement(FormControllerContext.Provider, {
value: formController
}, /*#__PURE__*/React.createElement(FormApiContext.Provider, {
value: formApi
}, /*#__PURE__*/React.createElement(FormStateContext.Provider, {
value: formState
}, children)));
};
return {
formApi: formApi,
formState: formState,
formController: formController,
render: render,
userProps: userProps
};
};
export { useForm };