UNPKG

re-use-form

Version:

Easy-to-use React form hooks with built-in validation support

645 lines (526 loc) 23.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = reducer; exports.init = init; exports.addConfig = addConfig; exports.removeConfig = removeConfig; exports.amendInitialConfig = amendInitialConfig; exports.setAttr = setAttr; exports.setAttrs = setAttrs; exports.setFullAttrs = setFullAttrs; exports.validate = validate; exports.validateAsync = validateAsync; exports.finishValidateAsync = finishValidateAsync; exports.setError = setError; exports.setErrors = setErrors; exports.reset = reset; exports.setState = setState; var _validations4 = require("./validations"); var _config = require("./config"); var _updateJs = _interopRequireDefault(require("update-js")); var _getLookup = _interopRequireDefault(require("get-lookup")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function reducer(state, action) { var attrs = state.attrs, errors = state.errors, validations = state.validations, validationOptions = state.validationOptions, validationDeps = state.validationDeps, validationStrategy = state.validationStrategy, asyncValidations = state.asyncValidations, asyncErrorsStrategy = state.asyncErrorsStrategy, isValidated = state.isValidated, configs = state.configs; var shouldValidateOnChange = false; var justDropError = false; switch (validationStrategy) { case 'always': case 'onAnyChange': shouldValidateOnChange = action.type === 'setAttr' || action.type === 'setAttrs' || action.type === 'setFullAttrs'; break; case 'onAnyError': shouldValidateOnChange = Object.values(errors).some(Boolean); break; case 'onAfterValidate': shouldValidateOnChange = isValidated; if (!isValidated) { justDropError = true; } break; case 'none': justDropError = true; break; default: throw new Error("Invalid validate on change validation strategy '".concat(validationStrategy, "'")); } switch (action.type) { case 'addConfig': { if (!action.resolvedConfig) return state; var nextConfigs = [].concat(_toConsumableArray(configs), [action.resolvedConfig]); var _nextConfigs$reduce = nextConfigs.reduce(_config.mergeConfigs), _validations = _nextConfigs$reduce.validations, _validationOptions = _nextConfigs$reduce.validationOptions, _validationDeps = _nextConfigs$reduce.validationDeps, helpers = _nextConfigs$reduce.helpers; var nextErrors = errors; if (shouldValidateOnChange) { var fullOpts = _objectSpread(_objectSpread({}, _validationOptions), {}, { attrs: attrs }); nextErrors = {}; Object.keys(_validations).forEach(function (rule) { (0, _validations4.validateRule)(_validations, fullOpts, rule, nextErrors); }); } return _objectSpread(_objectSpread({}, state), {}, { configs: nextConfigs, errors: (0, _validations4.compact)(nextErrors), validations: _validations, validationOptions: _validationOptions, validationDeps: _validationDeps, helpers: helpers, action: action }); } case 'removeConfig': { if (!action.resolvedConfig) return state; var index = configs.indexOf(action.resolvedConfig); var _nextConfigs = _toConsumableArray(configs); _nextConfigs.splice(index, 1); var _nextConfigs$reduce2 = _nextConfigs.reduce(_config.mergeConfigs), _validations2 = _nextConfigs$reduce2.validations, _validationOptions2 = _nextConfigs$reduce2.validationOptions, _validationDeps2 = _nextConfigs$reduce2.validationDeps, _helpers = _nextConfigs$reduce2.helpers; var _nextErrors = errors; if (shouldValidateOnChange) { var _fullOpts = _objectSpread(_objectSpread({}, _validationOptions2), {}, { attrs: attrs }); _nextErrors = {}; Object.keys(_validations2).forEach(function (rule) { (0, _validations4.validateRule)(_validations2, _fullOpts, rule, _nextErrors); }); } return _objectSpread(_objectSpread({}, state), {}, { configs: _nextConfigs, errors: (0, _validations4.compact)(_nextErrors), validations: _validations2, validationOptions: _validationOptions2, validationDeps: _validationDeps2, helpers: _helpers, action: action }); } case 'amendInitialConfig': { if (!action.resolvedConfig) return state; var _nextConfigs2 = _toConsumableArray(configs); _nextConfigs2[0] = (0, _config.mergeConfigs)(_nextConfigs2[0], action.resolvedConfig); _nextConfigs2[0].helpers = action.resolvedConfig.helpers; var _nextConfigs2$reduce = _nextConfigs2.reduce(_config.mergeConfigs), _validations3 = _nextConfigs2$reduce.validations, _validationOptions3 = _nextConfigs2$reduce.validationOptions, _validationDeps3 = _nextConfigs2$reduce.validationDeps, _validationStrategy = _nextConfigs2$reduce.validationStrategy, _helpers2 = _nextConfigs2$reduce.helpers; return _objectSpread(_objectSpread({}, state), {}, { configs: _nextConfigs2, validations: _validations3, validationOptions: _validationOptions3, validationDeps: _validationDeps3, validationStrategy: _validationStrategy, helpers: _helpers2, action: action }); } case 'setAttr': { var path = action.path, value = action.value; if (shouldValidateOnChange || justDropError) { var nextAttrs = (0, _updateJs["default"])(attrs, path, value); var _fullOpts2 = _objectSpread(_objectSpread({}, validationOptions), {}, { attrs: nextAttrs }); var _nextErrors2 = _defineProperty({}, path, (0, _validations4.validateAttr)(validations, _fullOpts2, path, value, justDropError)); var depsToValidate = (0, _validations4.getValidationDeps)(validationDeps, path); if (depsToValidate.length) { depsToValidate.forEach(function (name) { return (0, _validations4.validateRule)(validations, _fullOpts2, name, _nextErrors2, justDropError); }); } if (Array.isArray(value)) { Object.keys(validations).forEach(function (rule) { var escapedPath = (0, _validations4.escapePath)(path); var match = rule.match(new RegExp("^".concat(escapedPath, "\\.(\\*|\\d+)"))); if (match && (match[1] === '*' || (0, _getLookup["default"])(nextAttrs, "".concat(path, ".").concat(match[1])) !== undefined)) { (0, _validations4.validateRule)(validations, _fullOpts2, rule, _nextErrors2, justDropError); } }); } return _objectSpread(_objectSpread({}, state), {}, { attrs: nextAttrs, errors: (0, _validations4.compact)(_objectSpread(_objectSpread({}, errors), _nextErrors2)), isPristine: false, action: action }); } else { var _update; return (0, _updateJs["default"])(state, (_update = {}, _defineProperty(_update, "attrs.".concat(path), value), _defineProperty(_update, "isPristine", false), _defineProperty(_update, "action", action), _update)); } } case 'setAttrs': { var _ret = function () { var actionAttrs = action.attrs, prefix = action.prefix; var nextAttrs = _objectSpread({}, attrs); var nextErrors = shouldValidateOnChange ? _objectSpread({}, errors) : errors; var attrsObj = prefix ? (0, _getLookup["default"])(attrs, prefix) : attrs; var actionAttrsObj = typeof actionAttrs === 'function' ? actionAttrs(attrsObj) : actionAttrs; for (var _path in actionAttrsObj) { var fullPath = prefix ? "".concat(prefix, ".").concat(_path) : _path; _updateJs["default"]["in"](nextAttrs, fullPath, actionAttrsObj[_path]); if (shouldValidateOnChange || justDropError) { (function () { var fullOpts = _objectSpread(_objectSpread({}, validationOptions), {}, { attrs: nextAttrs }); var depsToValidate = (0, _validations4.getValidationDeps)(validationDeps, fullPath); if (depsToValidate.length) { depsToValidate.forEach(function (name) { return (0, _validations4.validateRule)(validations, fullOpts, name, nextErrors, justDropError); }); } nextErrors[fullPath] = (0, _validations4.validateAttr)(validations, fullOpts, fullPath, actionAttrsObj[_path], justDropError); })(); } } return { v: _objectSpread(_objectSpread({}, state), {}, { attrs: nextAttrs, errors: (0, _validations4.compact)(nextErrors), isPristine: false, action: action }) }; }(); if (_typeof(_ret) === "object") return _ret.v; } case 'setFullAttrs': { var _attrs = action.attrs, _action$options = action.options, options = _action$options === void 0 ? {} : _action$options; if (shouldValidateOnChange && options.validate !== false) { // When all attributes are set at once and validation should be // executed on set, run all validation routines. var _nextErrors4 = {}; var _fullOpts3 = _objectSpread(_objectSpread({}, validationOptions), {}, { attrs: _attrs }); Object.keys(validations).forEach(function (rule) { (0, _validations4.validateRule)(validations, _fullOpts3, rule, _nextErrors4, justDropError); }); return _objectSpread(_objectSpread({}, state), {}, { attrs: _attrs, errors: (0, _validations4.compact)(_nextErrors4), isPristine: false, action: action }); } return _objectSpread(_objectSpread({}, state), {}, { attrs: _attrs, isPristine: false, action: action }); } case 'validate': { var resolve = action.resolve, reject = action.reject; var _nextErrors5 = {}; var _fullOpts4 = _objectSpread(_objectSpread({}, validationOptions), {}, { attrs: attrs }); Object.keys(validations).forEach(function (ruleName) { (0, _validations4.validateRule)(validations, _fullOpts4, ruleName, _nextErrors5); }); var isValid = !Object.values(_nextErrors5).some(Boolean); if (isValid) { resolve(attrs); } else { reject(_nextErrors5); } return _objectSpread(_objectSpread({}, state), {}, { errors: (0, _validations4.compact)(_nextErrors5), isValidated: true, action: action }); } case 'validatePath': { var _path2 = action.path, _resolve = action.resolve, _reject = action.reject; var _value = (0, _getLookup["default"])(attrs, _path2); var _fullOpts5 = _objectSpread(_objectSpread({}, validationOptions), {}, { attrs: attrs }); var error = (0, _validations4.validateAttr)(validations, _fullOpts5, _path2, _value); if (error) { _reject(_defineProperty({}, _path2, error)); } else { _resolve(_value); } return _objectSpread(_objectSpread({}, state), {}, { errors: (0, _validations4.compact)(_objectSpread(_objectSpread({}, errors), {}, _defineProperty({}, _path2, error))), action: action }); } case 'validateAsync': { var _path3 = action.path, _resolve2 = action.resolve, _reject3 = action.reject, skipInputs = action.skipInputs; var toValidate = _path3 ? [_path3] : Object.keys(asyncValidations); var _fullOpts6 = _objectSpread(_objectSpread({}, validationOptions), {}, { attrs: attrs }); var validationPromises = {}; toValidate.forEach(function (ruleName) { (0, _validations4.validateRule)(asyncValidations, _fullOpts6, ruleName, validationPromises, false, true, skipInputs); }); validationPromises = (0, _validations4.compact)(validationPromises); var entries = []; Object.entries(validationPromises).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), name = _ref2[0], promises = _ref2[1]; promises.forEach(function (p) { return entries.push([name, p]); }); }); Promise.allSettled(entries.map(function (e) { return e[1]; }).flat()).then(function (results) { if (results.every(function (res) { return res.status === 'fulfilled'; })) { _resolve2(_path3 ? (0, _getLookup["default"])(attrs, _path3) : attrs); } else { var _nextErrors6 = {}; results.forEach(function (res, i) { if (res.status !== 'rejected') return; var name = entries[i][0]; if (!_nextErrors6[name]) { _nextErrors6[name] = []; } _nextErrors6[name].push(res.reason); }); for (var name in _nextErrors6) { if (asyncErrorsStrategy === 'takeFirst') { _nextErrors6[name] = _nextErrors6[name][0]; } else if (asyncErrorsStrategy === 'join') { _nextErrors6[name] = _nextErrors6[name].join('; '); } else if (typeof asyncErrorsStrategy === 'function') { _nextErrors6[name] = asyncErrorsStrategy(_nextErrors6[name]); } else { throw new Error("Invalid async errors handling strategy '".concat(asyncErrorsStrategy, "'")); } } _reject3(_objectSpread(_objectSpread({}, errors), _nextErrors6)); } }); return _objectSpread(_objectSpread({}, state), {}, { validating: validationPromises, action: action }); } case 'finishValidateAsync': { var validationErrors = action.errors; return _objectSpread(_objectSpread({}, state), {}, { validating: null, errors: _objectSpread(_objectSpread({}, errors), validationErrors) }); } case 'setError': { var _objectSpread3; var name = action.name, _error = action.error; if (!_error && !errors[name]) return state; return _objectSpread(_objectSpread({}, state), {}, { errors: (0, _validations4.compact)(_objectSpread(_objectSpread({}, state.errors), {}, (_objectSpread3 = {}, _defineProperty(_objectSpread3, name, _error), _defineProperty(_objectSpread3, "action", action), _objectSpread3))) }); } case 'setErrors': { return _objectSpread(_objectSpread({}, state), {}, { errors: (0, _validations4.compact)(action.errors), action: action }); } case 'reset': { var actionAttrs = action.attrs || state.initialAttrs; var _nextAttrs = typeof actionAttrs === 'function' ? actionAttrs(attrs) : actionAttrs; return _objectSpread(_objectSpread({}, state), {}, { errors: {}, attrs: _nextAttrs, isPristine: true, isValidated: false, action: action }); } case 'setState': { var setter = action.setter; var nextState = setter(state); action.isAttrUpdate = attrs !== nextState.attrs; return _objectSpread(_objectSpread({}, nextState), {}, { action: action }); } default: throw new Error("unrecognized action ".concat(action.type)); } } function init(config, secondaryConfig) { var resolved = (0, _config.resolveConfig)(config); var attrs = resolved.attrs, rest = _objectWithoutProperties(resolved, ["attrs"]); var fullConfig = secondaryConfig ? (0, _config.mergeConfigs)(rest, secondaryConfig) : rest; return _objectSpread({ initialAttrs: attrs, attrs: attrs, errors: {}, configs: [fullConfig], isPristine: true, isValidated: false, validating: null }, fullConfig); } function addConfig(resolvedConfig) { return { type: 'addConfig', resolvedConfig: resolvedConfig }; } function removeConfig(resolvedConfig) { return { type: 'removeConfig', resolvedConfig: resolvedConfig }; } function amendInitialConfig(resolvedConfig) { return { type: 'amendInitialConfig', resolvedConfig: resolvedConfig }; } function setAttr(path, value) { return { type: 'setAttr', path: path, value: value, isAttrUpdate: true }; } function setAttrs(attrs, prefix) { return { type: 'setAttrs', attrs: attrs, prefix: prefix, isAttrUpdate: true }; } function setFullAttrs(attrs, options) { return { type: 'setFullAttrs', attrs: attrs, options: options }; } function validate(path, resolve, reject) { if (typeof path === 'string') { return { type: 'validatePath', path: path, resolve: resolve, reject: reject }; } else { return { type: 'validate', resolve: resolve, reject: reject }; } } function validateAsync(path, resolve, reject) { var skipInputs = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; return { type: 'validateAsync', path: path, resolve: resolve, reject: reject, skipInputs: skipInputs }; } function finishValidateAsync() { var errors = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { type: 'finishValidateAsync', errors: errors }; } function setError(name, error) { return { type: 'setError', name: name, error: error }; } function setErrors(errors) { return { type: 'setErrors', errors: errors }; } function reset(attrs) { return { type: 'reset', attrs: attrs, isAttrUpdate: true }; } function setState(setter) { return { type: 'setState', setter: setter }; }