UNPKG

@agile-ts/multieditor

Version:

Simple Form Manager for UI-Frameworks

888 lines (709 loc) 23.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var core = require('@agile-ts/core'); var utils = require('@agile-ts/utils'); function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } 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; } var additionalLogs = { '41:03:00': "A validation method needs to be of the type 'function'!", '41:03:01': "Appending a Validator to itself isn't allowed!" }; var logCodeManager = core.assignAdditionalLogs(core.logCodeManager, process.env.NODE_ENV !== 'production' ? additionalLogs : {}); class Validator { constructor() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _defineProperty(this, "config", {}); _defineProperty(this, "_key", void 0); _defineProperty(this, "validationMethods", []); config = utils.defineConfig(config, { key: utils.generateId() }); this._key = config.key; } set key(value) { this._key = value; } get key() { return this._key; } validate(item, value) { var _this = this; return _asyncToGenerator(function* () { var isValid = true; if (item == null || item._key == null) return false; var editor = item.editor(); item.status.statusTracker.track(); for (var i = 0; i < _this.validationMethods.length; i++) { isValid = (yield _this.validationMethods[i].method(item._key, value, editor)) && isValid; } var trackedStatuses = item.status.statusTracker.getTrackedStatuses(); if (trackedStatuses.length > 0) { item.status.set(trackedStatuses[0]); item.status.lastTrackedValues = utils.copy(trackedStatuses); } else editor.resetStatus(item._key); return isValid; })(); } addValidationMethod(method) { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!utils.isFunction(method)) { logCodeManager.log('41:03:00'); return this; } this.validationMethods.push({ key: config.key, method }); return this; } append(validator) { if (validator === this) { logCodeManager.log('41:03:01'); return this; } this.validationMethods = this.validationMethods.concat(validator.validationMethods); return this; } copy() { var newValidator = new Validator({ key: this._key }); newValidator.validationMethods = utils.copy(this.validationMethods); newValidator.config = utils.copy(this.config); return newValidator; } } function agileResolver() { for (var _len = arguments.length, validationSchemaParts = new Array(_len), _key = 0; _key < _len; _key++) { validationSchemaParts[_key] = arguments[_key]; } var _validationSchemaParts = utils.normalizeArray(validationSchemaParts); var validator = new Validator(); _validationSchemaParts.forEach(validationSchemaPart => { if (typeof validationSchemaPart === 'function') validationSchemaPart = validationSchemaPart(); if (validationSchemaPart != null && utils.isFunction(validationSchemaPart.method)) { var _validationSchemaPart; validator.addValidationMethod(validationSchemaPart.method, { key: "".concat((_validationSchemaPart = validationSchemaPart.key) !== null && _validationSchemaPart !== void 0 ? _validationSchemaPart : utils.generateId(), "_").concat(utils.generateId()) }); } }); return validator; } function yupResolver(schema) { var validator = new Validator(); validator.addValidationMethod(function () { var _ref = _asyncToGenerator(function* (toValidateItemKey, value, editor) { var isValid = true; try { yield schema.validate(value); } catch (e) { isValid = false; if (e.name === 'ValidationError' && e.inner != null) { if (e.inner.length === 0) { editor.setStatus(toValidateItemKey, 'error', e.message.replace('this', toValidateItemKey)); } for (var innerErr of e.inner) { editor.setStatus(toValidateItemKey, 'error', innerErr.message.replace('this', toValidateItemKey)); } } } return isValid; }); return function (_x, _x2, _x3) { return _ref.apply(this, arguments); }; }(), { key: "yup_".concat(utils.generateId()) }); return validator; } function isRequired(errorMessage) { return { key: 'isRequired', method: (toValidateItemKey, value, editor) => { var isValid = !!value; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " is a required field")); } return isValid; } }; } function isNumber(errorMessage) { return { key: 'isNumber', method: (toValidateItemKey, value, editor) => { var isValid = typeof value === 'number'; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be a valid number")); } return isValid; } }; } function maxNumber(maxNumber, errorMessage) { return { key: 'maxNumber', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'number') return false; var isValid = value <= maxNumber; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be smaller than ").concat(maxNumber)); } return isValid; } }; } function minNumber(minNumber, errorMessage) { return { key: 'minNumber', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'number') return false; var isValid = value >= minNumber; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be larger than ").concat(minNumber)); } return isValid; } }; } function isPositiveNumber(errorMessage) { return { key: 'positiveNumber', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'number') return false; var isValid = value >= 0; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be a positive number")); } return isValid; } }; } function isNegativeNumber(errorMessage) { return { key: 'negativeNumber', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'number') return false; var isValid = value < 0; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be a negative number")); } return isValid; } }; } function isString(errorMessage) { return { key: 'isString', method: (toValidateItemKey, value, editor) => { var isValid = typeof value === 'string'; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be a valid string")); } return isValid; } }; } function maxLength(maxLength, errorMessage) { return { key: 'maxLength', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'string') return false; var isValid = value.length <= maxLength; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be at most ").concat(maxLength, " characters")); } return isValid; } }; } function minLength(minLength, errorMessage) { return { key: 'minLength', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'string') return false; var isValid = value.length >= minLength; if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be at least ").concat(minLength, " characters")); } return isValid; } }; } function isEmail(errorMessage) { return { key: 'isEmail', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'string') return false; var emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var isValid = emailRegex.test(value.toLowerCase()); if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be a valid email")); } return isValid; } }; } function isUrl(errorMessage) { return { key: 'isUrl', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'string') return false; var urlRegex = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)?/gi; var isValid = urlRegex.test(value.toLowerCase()); if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must be a valid url")); } return isValid; } }; } function matchesRegex(regex, errorMessage) { return { key: 'matchesRegex', method: (toValidateItemKey, value, editor) => { if (value == null || typeof value !== 'string') return false; var isValid = regex.test(value.toLowerCase()); if (!isValid) { editor.setStatus(toValidateItemKey, 'error', errorMessage || "".concat(toValidateItemKey, " must follow the defined regex")); } return isValid; } }; } class StatusTracker { constructor() { _defineProperty(this, "isTracking", false); _defineProperty(this, "trackedStatuses", new Set()); } track() { this.isTracking = true; } tracked(status) { if (this.isTracking) this.trackedStatuses.add(status); } getTrackedStatuses() { var trackedStatuses = Array.from(this.trackedStatuses); this.isTracking = false; this.trackedStatuses = new Set(); return trackedStatuses; } } class Status extends core.State { constructor(item) { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; super(item.agileInstance(), null, { key: "status_".concat(item._key) }); _defineProperty(this, "config", void 0); _defineProperty(this, "item", void 0); _defineProperty(this, "statusTracker", void 0); _defineProperty(this, "lastTrackedValues", []); config = utils.defineConfig(config, { display: false }); this.item = item; this.statusTracker = new StatusTracker(); this.config = { display: config.display }; } get value() { return this.config.display ? utils.copy(this._value) : null; } set(value) { var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; config = utils.defineConfig(config, { waitForTracking: false, background: this.config != null ? !this.config.display : undefined }); if (value != null) this.statusTracker.tracked(value); if (config.waitForTracking && this.statusTracker.isTracking) return this; this.observers['value'].ingestValue(value, config); return this; } } class Item extends core.State { constructor(editor, initialValue, config) { var _this; super(editor.agileInstance(), initialValue, { key: config.key }); _this = this; _defineProperty(this, "editor", void 0); _defineProperty(this, "config", void 0); _defineProperty(this, "isValid", false); _defineProperty(this, "validator", void 0); _defineProperty(this, "status", void 0); _defineProperty(this, "touched", false); _defineProperty(this, "computeValueMethod", void 0); config = utils.defineConfig(config, { canBeEdited: true, validator: new Validator() }); this.editor = () => editor; this.validator = config.validator; this.config = config; this.status = new Status(this); this.addSideEffect('validateItem', _asyncToGenerator(function* () { if (_this.editor().canAssignStatusToItemOnChange(_this)) _this.status.config.display = true; yield _this.validate(); _this.editor().recomputeValidatedState({ validate: false }); _this.editor().recomputeModifiedState(); })); } validate() { var _this2 = this; return _asyncToGenerator(function* () { var isValid = yield _this2.validator.validate(_this2, _this2.value); _this2.isValid = isValid; return isValid; })(); } reset() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; this.set(this.initialStateValue, config); this.status.config.display = false; this.touched = false; return this; } computeValue(method) { if (!utils.isFunction(method)) { logCodeManager.log('00:03:01', { replacers: ['Compute Value Method', 'function'] }); return this; } this.computeValueMethod = method; this.set(this.nextStateValue); return this; } blur() { this.touched = true; if (this.editor().canAssignStatusToItemOnBlur(this)) { this.status.config.display = true; this.status.ingest({ force: true }); } return this; } } function updateNestedProperty(obj, path, value) { var updatedObject = utils.copy(obj); var schema = updatedObject; var pathParts = Array.isArray(path) ? path : path.split('.'); var pathPartsLength = pathParts.length; if (pathPartsLength <= 0) return obj; for (var i = 0; i < pathPartsLength - 1; i++) { var pathPart = pathParts[i]; if (!schema[pathPart]) schema[pathPart] = {}; schema = schema[pathPart]; } schema[pathParts[pathPartsLength - 1]] = value; return updatedObject; } class Multieditor$1 { constructor(config, agileInstance) { _defineProperty(this, "agileInstance", void 0); _defineProperty(this, "config", void 0); _defineProperty(this, "_key", void 0); _defineProperty(this, "isModified", false); _defineProperty(this, "isValid", false); _defineProperty(this, "submitted", false); _defineProperty(this, "fixedProperties", []); _defineProperty(this, "editableProperties", []); _defineProperty(this, "onSubmit", void 0); _defineProperty(this, "data", {}); this.agileInstance = () => agileInstance; var _config = typeof config === 'function' ? config(this) : config; _config = utils.defineConfig(_config, { key: undefined, fixedProperties: [], editableProperties: Object.keys(_config.initialData), validationSchema: {}, computeMethods: {}, reValidateMode: 'onSubmit', toValidate: 'editable' }); this._key = _config.key; this.onSubmit = _config.onSubmit; this.fixedProperties = _config.fixedProperties; this.editableProperties = _config.editableProperties; this.config = { reValidateMode: _config.reValidateMode, toValidate: _config.toValidate }; var formattedValidators = {}; Object.keys(_config.validationSchema).forEach(key => { var validationMethod = _config.validationSchema[key]; if (validationMethod instanceof Validator) { if (validationMethod.key == null) validationMethod.key = key; formattedValidators[key] = validationMethod; } else { formattedValidators[key] = new Validator({ key }).addValidationMethod(validationMethod); } }); for (var key in _config.initialData) { var data = _config.initialData[key]; var item = new Item(this, data, { key, canBeEdited: this.editableProperties.includes(key), validator: formattedValidators[key] }); if (Object.prototype.hasOwnProperty.call(_config.computeMethods, key)) item.computeValue(_config.computeMethods[key]); this.data[key] = item; item.validate(); } } set key(value) { this._key = value; } get key() { return this._key; } get deps() { var deps = []; for (var key in this.data) { var item = this.data[key]; deps.push(item.observers['value']); deps.push(item.status.observers['value']); } return deps; } itemDeps(itemKey) { var deps = []; var item = this.getItem(itemKey); if (item) { deps.push(item.observers['value']); deps.push(item.status.observers['value']); } return deps; } setValue(itemKey, value) { var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; config = utils.defineConfig(config, { background: true }); var path = itemKey.toString().split('.'); var item = this.getItem(path.shift()); if (item == null) return this; if (path.length > 0) { item.set(updateNestedProperty(item.nextStateValue, path, value), config); } else { item.set(value, config); } return this; } setInitialValue(itemKey, value) { var config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; config = utils.defineConfig(config, { background: true, reset: true }); var path = itemKey.toString().split('.'); var item = this.getItem(path.shift()); if (item == null) return this; if (path.length > 0) { item.initialStateValue = updateNestedProperty(item.initialStateValue, path, value); } else { item.initialStateValue = value; } if (config.reset) { item.reset(config); } return this; } submit() { var _arguments = arguments, _this = this; return _asyncToGenerator(function* () { var config = _arguments.length > 0 && _arguments[0] !== undefined ? _arguments[0] : {}; config = utils.defineConfig(config, { assignToInitial: true, onSubmitConfig: undefined }); for (var key in _this.data) { var item = _this.data[key]; if (_this.canAssignStatusToItemOnSubmit(item)) { item.status.config.display = true; item.status.ingest({ force: true }); } } _this.submitted = true; if (!_this.isValid) return false; var preparedData = {}; for (var _key in _this.data) { var _item = _this.data[_key]; if (_item.isSet && _item.config.canBeEdited) { preparedData[_key] = _item.value; if (config.assignToInitial) _this.setInitialValue(_key, _item.value); } } for (var _key2 of _this.fixedProperties) { var _item2 = _this.getItem(_key2); if (!_item2) continue; preparedData[_key2] = _item2.value; } return yield _this.onSubmit(preparedData, config.onSubmitConfig); })(); } reset() { for (var key in this.data) { this.data[key].reset(); } this.isModified = false; this.submitted = false; return this; } setStatus(itemKey, type, message) { var item = this.getItem(itemKey); if (item == null) return this; item.status.set({ type, message }, { waitForTracking: true }); return this; } resetStatus(itemKey) { var item = this.getItem(itemKey); if (item == null || item.status == null) return this; item.status.set(null); return this; } getStatus(itemKey) { var _this$getItem; return ((_this$getItem = this.getItem(itemKey)) === null || _this$getItem === void 0 ? void 0 : _this$getItem.status.value) || null; } getItem(itemKey) { return this.data[itemKey]; } getItemValue(itemKey) { var _this$getItem2; return (_this$getItem2 = this.getItem(itemKey)) === null || _this$getItem2 === void 0 ? void 0 : _this$getItem2.value; } getItemInitialValue(itemKey) { var _this$getItem3; return (_this$getItem3 = this.getItem(itemKey)) === null || _this$getItem3 === void 0 ? void 0 : _this$getItem3.initialStateValue; } areModified(itemKeys) { var _isModified = false; for (var key of itemKeys) { var item = this.getItem(key); if (item == null) continue; _isModified = _isModified || item.isSet; } return _isModified; } recomputeModifiedState() { this.isModified = this.areModified(this.editableProperties); return this; } recomputeValidatedState() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; config = utils.defineConfig(config, { validate: true }); var isValid = true; for (var key in this.data) { var item = this.data[key]; if (config.validate) item.validate(); if (!item.config.canBeEdited && this.config.toValidate === 'editable') continue; isValid = item.isValid && isValid; } this.isValid = isValid; return isValid; } validate() { return this.recomputeValidatedState({ validate: true }); } canAssignStatusToItemOnChange(item) { return (this.config.reValidateMode === 'onChange' || this.config.reValidateMode === 'afterFirstSubmit' && this.submitted) && (this.config.toValidate === 'all' || this.config.toValidate === 'editable' && item.config.canBeEdited || false); } canAssignStatusToItemOnSubmit(item) { return (this.config.reValidateMode === 'onSubmit' || this.config.reValidateMode === 'afterFirstSubmit' && !this.submitted || this.config.reValidateMode === 'onChange' && !item.status.config.display) && (this.config.toValidate === 'all' || this.config.toValidate === 'editable' && item.config.canBeEdited || false); } canAssignStatusToItemOnBlur(item) { return this.config.reValidateMode === 'onBlur' && (this.config.toValidate === 'all' || this.config.toValidate === 'editable' && item.config.canBeEdited || false); } } function createMultieditor(config) { var agileInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : core.shared; return new Multieditor$1(config, agileInstance); } var Multieditor = Multieditor$1; exports.Item = Item; exports.Multieditor = Multieditor$1; exports.Status = Status; exports.StatusTracker = StatusTracker; exports.Validator = Validator; exports.agileResolver = agileResolver; exports.createMultieditor = createMultieditor; exports["default"] = Multieditor; exports.isEmail = isEmail; exports.isNegativeNumber = isNegativeNumber; exports.isNumber = isNumber; exports.isPositiveNumber = isPositiveNumber; exports.isRequired = isRequired; exports.isString = isString; exports.isUrl = isUrl; exports.matchesRegex = matchesRegex; exports.maxLength = maxLength; exports.maxNumber = maxNumber; exports.minLength = minLength; exports.minNumber = minNumber; exports.yupResolver = yupResolver; //# sourceMappingURL=index.js.map