@agile-ts/multieditor
Version:
Simple Form Manager for UI-Frameworks
888 lines (709 loc) • 23.9 kB
JavaScript
'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