re-use-form
Version:
Easy-to-use React form hooks with built-in validation support
645 lines (526 loc) • 23.2 kB
JavaScript
;
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
};
}