@zensen/form-service
Version:
A reactive form service framework
521 lines (520 loc) • 23.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _error = require("./error");
var _utils = require("./utils");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _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(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
var errCb = function errCb(selector) {
return Array.isArray(selector) || selector.validators;
};
var pristineCb = function pristineCb(selector) {
return selector.clipPristine;
};
function pathToKeyPath(path) {
var str = "".concat(path);
return str ? str.split('.') : [];
}
var Service = exports["default"] = /*#__PURE__*/function () {
function Service(model, selectors, onChange) {
_classCallCheck(this, Service);
this.__state = {};
this.__errors = {};
this.__pristine = {};
this.__selectors = selectors;
this.__onChange = onChange;
this.refresh(model);
this.__verifySelectors();
}
return _createClass(Service, [{
key: "isDirty",
get: function get() {
return !(0, _utils.deepEqual)(this.__state, this.__initialState);
}
}, {
key: "isPristine",
get: function get() {
var _fn = function fn(obj) {
return !Object.values(obj).filter(function (v) {
return _typeof(v) === 'object' ? _fn(v) : v;
}).length;
};
return _typeof(this.__pristine) === 'object' ? _fn(this.__pristine) : this.__pristine;
}
}, {
key: "hasErrors",
get: function get() {
var _fn2 = function fn(obj) {
return Object.values(obj).filter(function (v) {
return _typeof(v) === 'object' ? _fn2(v) : v;
}).length > 0;
};
return _typeof(this.__errors) === 'object' ? _fn2(this.__errors) : Boolean(this.__errors);
}
}, {
key: "state",
get: function get() {
return this.__state;
}
}, {
key: "errors",
get: function get() {
return this.__errors;
}
}, {
key: "refresh",
value: function refresh(model) {
this.__state = (0, _utils.deepCopy)(model);
this.__state = this.convert(model, 'format');
this.__initialState = (0, _utils.deepCopy)(this.__state);
this.__refreshErrors();
this.__refreshPristine();
this.__change();
}
}, {
key: "reset",
value: function reset() {
this.__state = (0, _utils.deepCopy)(this.__initialState);
this.__refreshErrors();
this.__refreshPristine();
this.__change();
}
}, {
key: "apply",
value: function apply(path, value) {
var keyPath = pathToKeyPath(path);
if (path && value === this.__state) {
throw new _error.MutationError(keyPath, value, this.__state);
}
var pristine = (0, _utils.getValueByPath)(this.__pristine, keyPath);
if (_typeof(pristine) === 'object') {
throw new _error.PristineError(keyPath);
}
this.__verifyValue(keyPath, value);
(0, _utils.setValueByPath)(this.__state, keyPath, value);
this.validateKey(keyPath);
(0, _utils.setValueByPath)(this.__pristine, keyPath, false);
this.__spreadSchema('__state', keyPath);
this.__modify(keyPath);
}
}, {
key: "addItem",
value: function addItem(path) {
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
var keyPath = pathToKeyPath(path);
var items = (0, _utils.getValueByPath)(this.__state, keyPath);
var shiftedIndex = index !== -1 ? index : items.length;
var selector = this.getSelector(keyPath);
var model = this.convert(this.__state, 'unformat');
var rawItem = selector.createItem(keyPath, shiftedIndex, model, this);
var item = this.__convertItem(rawItem, keyPath);
items.splice(shiftedIndex, 0, item);
this.__spreadSchema('__state', [].concat(_toConsumableArray(keyPath), [shiftedIndex]));
this.__addItemToSchema('__errors', keyPath, shiftedIndex, item, '', errCb);
this.__addItemToSchema('__pristine', keyPath, shiftedIndex, item, true, pristineCb);
this.__modifyPristineItem([].concat(_toConsumableArray(keyPath), [shiftedIndex]));
this.__modify(keyPath);
this.__change();
}
}, {
key: "removeItem",
value: function removeItem(path) {
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
var keyPath = pathToKeyPath(path);
var items = (0, _utils.getValueByPath)(this.__state, keyPath);
var shiftedIndex = index === -1 ? items.length - 1 : index;
items.splice(shiftedIndex, 1);
this.__spreadSchema('__state', [].concat(_toConsumableArray(keyPath), ["".concat(shiftedIndex)]));
this.__removeItemFromSchema('__errors', keyPath, shiftedIndex);
this.__removeItemFromSchema('__pristine', keyPath, shiftedIndex);
this.__modify(keyPath);
this.__change();
}
}, {
key: "moveItem",
value: function moveItem(path, fromIndex, toIndex) {
var keyPath = pathToKeyPath(path);
this.__moveItemInSchema('__state', keyPath, fromIndex, toIndex);
this.__moveItemInSchema('__errors', keyPath, fromIndex, toIndex);
this.__moveItemInSchema('__pristine', keyPath, fromIndex, toIndex);
this.__change();
}
}, {
key: "swapItems",
value: function swapItems(path, index1, index2) {
var keyPath = pathToKeyPath(path);
this.__swapItemsInSchema('__state', keyPath, index1, index2);
this.__swapItemsInSchema('__errors', keyPath, index1, index2);
this.__swapItemsInSchema('__pristine', keyPath, index1, index2);
(0, _utils.setValueByPath)(this.__pristine, [].concat(_toConsumableArray(keyPath), [index1]), false);
(0, _utils.setValueByPath)(this.__pristine, [].concat(_toConsumableArray(keyPath), [index2]), false);
this.__change();
}
}, {
key: "convert",
value: function convert(data, op) {
var _this = this;
var rootPath = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var rootSelector = this.getSelector([], true);
var action = rootSelector && rootSelector[op];
var copy = _typeof(data) === 'object' ? (0, _utils.deepCopy)(data) : data;
var result = action ? action(copy, rootPath, data) : copy;
(0, _utils.traverse)(result, function (keyPath, value) {
var fullPath = [].concat(_toConsumableArray(rootPath), _toConsumableArray(keyPath));
var selector = _this.getSelector(fullPath, true);
if (selector && selector[op]) {
var selVal = selector[op](value, fullPath, data);
if (selVal !== null && _typeof(selVal) === 'object') {
var _copy = selVal instanceof Date ? new Date(selVal.getTime()) : (0, _utils.deepCopy)(selVal);
(0, _utils.setValueByPath)(result, keyPath, _copy);
} else {
(0, _utils.setValueByPath)(result, keyPath, selVal);
}
}
});
return result;
}
}, {
key: "build",
value: function build() {
return this.convert(this.__state, 'unformat');
}
}, {
key: "validate",
value: function validate() {
var _this2 = this;
this.__pristine = (0, _utils.map)(this.__pristine, function () {
return false;
});
(0, _utils.traverse)(this.__state, function (keyPath) {
var pristine = (0, _utils.getValueByPath)(_this2.__pristine, keyPath);
if (pristine !== undefined && _typeof(pristine) !== 'object') {
_this2.validateKey(keyPath, true);
}
});
return !this.hasErrors;
}
}, {
key: "validateKey",
value: function validateKey(keyPath) {
var _this3 = this;
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var clippedPathIndex = keyPath.findIndex(function (_, index) {
var subPath = keyPath.slice(0, index);
return _this3.getValidators(subPath);
});
var validatorPathLength = clippedPathIndex !== -1 ? clippedPathIndex : keyPath.length;
var validatorPath = keyPath.slice(0, validatorPathLength);
var validators = this.getValidators(validatorPath);
var pristine = (0, _utils.getValueByPath)(this.__pristine, keyPath);
var prevErrors = this.__errors;
if (validators && (!pristine || force)) {
var selector = this.getSelector(validatorPath);
var useRaw = selector.validateRaw || false;
if (!selector.validateManually || force) {
this.__processValidator(validatorPath, validators, useRaw);
}
}
if (prevErrors !== this.__errors) {
this.__change();
}
}
}, {
key: "unsetPristine",
value: function unsetPristine(keyPath) {
if (typeof (0, _utils.getValueByPath)(this.__pristine, keyPath) !== 'boolean') {
throw new TypeError("Invalid path: ".concat(keyPath.join('.')));
}
(0, _utils.setValueByPath)(this.__pristine, keyPath, false);
}
}, {
key: "getSelectorPath",
value: function getSelectorPath(keyPath) {
var _this4 = this;
var ignoreCheck = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!ignoreCheck) {
var value = (0, _utils.getValueByPath)(this.__state, keyPath);
if (value === undefined) {
throw new _error.PathError(keyPath);
}
}
var initialValue = keyPath.length ? ['children'] : [];
return keyPath.reduce(function (accum, curr, index) {
var parentPath = keyPath.slice(0, index);
var parent = (0, _utils.getValueByPath)(_this4.__state, parentPath);
var key = Array.isArray(parent) ? '$' : curr;
return index < keyPath.length - 1 ? [].concat(_toConsumableArray(accum), [key, 'children']) : [].concat(_toConsumableArray(accum), [key]);
}, initialValue);
}
}, {
key: "getSelector",
value: function getSelector(keyPath) {
var ignoreCheck = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var selectorPath = this.getSelectorPath(keyPath, ignoreCheck);
return (0, _utils.getValueByPath)(this.__selectors, selectorPath);
}
}, {
key: "getValidators",
value: function getValidators(keyPath) {
var selector = this.getSelector(keyPath);
if (selector) {
return Array.isArray(selector) ? selector : selector.validators;
}
return null;
}
}, {
key: "__change",
value: function __change() {
this.__onChange(this.isDirty, this.__state, this.__errors, this.__pristine);
}
}, {
key: "__verifySelectors",
value: function __verifySelectors() {
var _this5 = this;
(0, _utils.traverse)(this.__state, function (keyPath, v) {
var validators = _this5.getValidators(keyPath);
if (validators) {
var parentPath = keyPath.slice(0, keyPath.length - 1);
parentPath.forEach(function (_, index) {
var ancestorPath = parentPath.slice(0, index + 1);
var ancestorValidators = _this5.getValidators(ancestorPath);
if (ancestorValidators) {
throw new _error.VerificationError("Selector (".concat(keyPath.join('.'), ") has ancestor selector with validators: ").concat(ancestorPath.join('.')));
}
});
}
if (_typeof(v) === 'object') {
var selectors = _this5.getSelector(keyPath);
if (selectors && selectors.ignorePristine && !selectors.clipPristine) {
var msg = "ignorePristine set object-type key for path: ".concat(keyPath.join('.'), ". Perhaps you meant to use clipPristine?");
throw new _error.VerificationError(msg);
}
}
});
}
}, {
key: "__verifyValue",
value: function __verifyValue(keyPath, value) {
var selector = this.getSelector(keyPath, true);
var oldValue = (0, _utils.getValueByPath)(this.__state, keyPath);
if (!selector || !selector.unsafe) {
if (oldValue === undefined) {
throw new TypeError("Invalid path: ".concat(keyPath.join('.')));
}
if (oldValue !== null && value !== null) {
if (_typeof(oldValue) === 'object') {
var oldPathMap = (0, _utils.getKeyPaths)(oldValue);
if (_typeof(value) === 'object') {
var pathMap = (0, _utils.getKeyPaths)(value);
if (!(0, _utils.deepEqual)(oldPathMap, pathMap)) {
throw new _error.MutationError(keyPath, oldValue, value);
}
} else {
throw new _error.MutationError(keyPath, oldValue, value);
}
}
}
}
}
}, {
key: "__buildSchema",
value: function __buildSchema(refSchema, initialValue, fn) {
var _this6 = this;
var rootPath = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
var result = Array.isArray(refSchema) ? [] : {};
var rootSelector = this.getSelector(rootPath);
if (rootSelector && fn(rootSelector)) {
return initialValue;
}
(0, _utils.traverse)(refSchema, function (keyPath, value) {
var dateType = value instanceof Date;
if (!dateType && value !== null && _typeof(value) === 'object') {
var fullPath = [].concat(_toConsumableArray(rootPath), _toConsumableArray(keyPath));
var selector = _this6.getSelector(fullPath);
if (selector && fn(selector)) {
(0, _utils.setValueByPath)(result, keyPath, initialValue);
return false;
}
(0, _utils.setValueByPath)(result, keyPath, Array.isArray(value) ? [] : {});
} else {
(0, _utils.setValueByPath)(result, keyPath, initialValue);
}
}, true);
return result;
}
}, {
key: "__convertItem",
value: function __convertItem(data) {
var rootPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var item = _typeof(data) === 'object' ? (0, _utils.deepCopy)(data) : data;
var result = this.convert([item], 'format', rootPath);
return result[0];
}
}, {
key: "__spreadSchema",
value: function __spreadSchema(schemaKey, keyPath) {
var _this7 = this;
var ref = this[schemaKey];
this[schemaKey] = Array.isArray(ref) ? _toConsumableArray(ref) : _objectSpread({}, ref);
if (keyPath.length > 1) {
keyPath.slice(0, keyPath.length - 1).forEach(function (_, index) {
var subPath = keyPath.slice(0, index + 1);
var subObj = (0, _utils.getValueByPath)(_this7[schemaKey], subPath);
var result = Array.isArray(subObj) ? _toConsumableArray(subObj) : _objectSpread({}, subObj);
(0, _utils.setValueByPath)(_this7[schemaKey], subPath, result);
});
}
}
}, {
key: "__addItemToSchema",
value: function __addItemToSchema(schemaKey, keyPath, index, item, defaultValue, fn) {
var value = (0, _utils.getValueByPath)(this[schemaKey], keyPath);
if (_typeof(value) === 'object') {
var selector = this.getSelector(keyPath);
var clipElement = selector && selector.children && selector.children.$ && fn(selector.children.$);
var subObj = !clipElement && _typeof(item) === 'object' ? this.__buildSchema(item, defaultValue, fn, [].concat(_toConsumableArray(keyPath), ["".concat(index)])) : defaultValue;
value.splice(index, 0, subObj);
this.__spreadSchema(schemaKey, [].concat(_toConsumableArray(keyPath), ["".concat(index)]));
}
}
}, {
key: "__removeItemFromSchema",
value: function __removeItemFromSchema(schemaKey, keyPath, index) {
var item = (0, _utils.getValueByPath)(this[schemaKey], keyPath);
if (Array.isArray(item)) {
item.splice(index, 1);
this.__spreadSchema(schemaKey, [].concat(_toConsumableArray(keyPath), ["".concat(index)]));
}
}
}, {
key: "__moveItemInSchema",
value: function __moveItemInSchema(schemaKey, keyPath, fromIndex, toIndex) {
var items = (0, _utils.getValueByPath)(this[schemaKey], keyPath);
if (Array.isArray(items)) {
var result = (0, _utils.moveItem)(items, fromIndex, toIndex).map(function (item) {
if (_typeof(item) === 'object') {
return Array.isArray(item) ? _toConsumableArray(item) : _objectSpread({}, item);
}
return item;
});
if (keyPath.length) {
(0, _utils.setValueByPath)(this[schemaKey], keyPath, result);
} else {
this[schemaKey] = result;
}
this.__spreadSchema(schemaKey, keyPath);
}
}
}, {
key: "__swapItemsInSchema",
value: function __swapItemsInSchema(schemaKey, keyPath, index1, index2) {
var items = (0, _utils.getValueByPath)(this[schemaKey], keyPath);
if (Array.isArray(items)) {
var result = (0, _utils.swap)(items, index1, index2);
if (keyPath.length) {
(0, _utils.setValueByPath)(this[schemaKey], keyPath, result);
} else {
this[schemaKey] = result;
}
this.__spreadSchema(schemaKey, [].concat(_toConsumableArray(keyPath), [index1]));
this.__spreadSchema(schemaKey, [].concat(_toConsumableArray(keyPath), [index2]));
}
}
}, {
key: "__modify",
value: function __modify(keyPath) {
var pristine = (0, _utils.getValueByPath)(this.__pristine, keyPath);
if (pristine && _typeof(pristine) !== 'object') {
(0, _utils.setValueByPath)(this.__pristine, keyPath, false);
}
this.__change();
}
}, {
key: "__processValidator",
value: function __processValidator(keyPath, validators, useRaw) {
var _this8 = this;
var data = useRaw ? this.convert(this.__state, 'unformat') : this.__state;
var value = (0, _utils.getValueByPath)(data, keyPath);
try {
validators.forEach(function (validator) {
if (!validator.validate(value, keyPath, data, _this8)) {
throw new _error.ValidationError(validator.error);
}
});
this.__setError(keyPath, '');
} catch (e) {
if (e instanceof _error.ValidationError) {
this.__setError(keyPath, e.message);
} else {
throw e;
}
}
}
}, {
key: "__refreshErrors",
value: function __refreshErrors() {
this.__errors = this.__buildSchema(this.__state, '', function (selector) {
return Array.isArray(selector) || selector.validators;
});
}
}, {
key: "__setError",
value: function __setError(keyPath, message) {
if (keyPath.length) {
(0, _utils.setValueByPath)(this.__errors, keyPath, message);
this.__spreadSchema('__errors', keyPath);
} else {
this.__errors = message;
}
}
}, {
key: "__refreshPristine",
value: function __refreshPristine() {
var _this9 = this;
this.__pristine = this.__buildSchema(this.__state, true, function (selector) {
return selector.clipPristine;
});
(0, _utils.traverse)(this.__pristine, function (keyPath) {
var selector = _this9.getSelector(keyPath);
if (selector && selector.ignorePristine) {
(0, _utils.setValueByPath)(_this9.__pristine, keyPath, false);
}
});
}
}, {
key: "__modifyPristineItem",
value: function __modifyPristineItem(keyPath) {
var _this0 = this;
var pristine = (0, _utils.getValueByPath)(this.__pristine, keyPath);
if (_typeof(pristine) === 'object') {
(0, _utils.traverse)(pristine, function (subPath) {
var fullPath = [].concat(_toConsumableArray(keyPath), _toConsumableArray(subPath));
var selector = _this0.getSelector(fullPath);
if (selector && selector.ignorePristine) {
(0, _utils.setValueByPath)(_this0.__pristine, fullPath, false);
}
});
} else {
var selector = this.getSelector(keyPath);
if (selector && selector.ignorePristine) {
(0, _utils.setValueByPath)(this.__pristine, keyPath, false);
}
}
}
}]);
}();