UNPKG

mobx-react-form

Version:
1,131 lines (1,118 loc) 145 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("_"), require("mobx")); else if(typeof define === 'function' && define.amd) define(["_", "mobx"], factory); else if(typeof exports === 'object') exports["MobxReactForm"] = factory(require("_"), require("mobx")); else root["MobxReactForm"] = factory(root["_"], root["mobx"]); })(self, (__WEBPACK_EXTERNAL_MODULE_lodash__, __WEBPACK_EXTERNAL_MODULE_mobx__) => { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/Base.ts": /*!*********************!*\ !*** ./src/Base.ts ***! \*********************/ /***/ (function(__unused_webpack_module, exports, __webpack_require__) { var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); const mobx_1 = __webpack_require__(/*! mobx */ "mobx"); const lodash_1 = __importDefault(__webpack_require__(/*! lodash */ "lodash")); const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts"); const parser_1 = __webpack_require__(/*! ./parser */ "./src/parser.ts"); const FieldProps_1 = __webpack_require__(/*! ./models/FieldProps */ "./src/models/FieldProps.ts"); const OptionsModel_1 = __webpack_require__(/*! ./models/OptionsModel */ "./src/models/OptionsModel.ts"); const ValidatorInterface_1 = __webpack_require__(/*! ./models/ValidatorInterface */ "./src/models/ValidatorInterface.ts"); class Base { constructor() { Object.defineProperty(this, "noop", { enumerable: true, configurable: true, writable: true, value: () => { } }); Object.defineProperty(this, "state", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "fields", { enumerable: true, configurable: true, writable: true, value: mobx_1.observable.map({}) }); Object.defineProperty(this, "path", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "$submitted", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "$submitting", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "$validated", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "$validating", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "$clearing", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "$resetting", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "$touched", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "$changed", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "$hooks", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "$handlers", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "execHook", { enumerable: true, configurable: true, writable: true, value: (name, fallback = {}) => (0, utils_1.$try)(fallback[name], this.$hooks[name], this.noop).apply(this, [this]) }); Object.defineProperty(this, "execHandler", { enumerable: true, configurable: true, writable: true, value: (name, args, fallback = undefined, hook = null, execHook = true) => [ (0, utils_1.$try)(this.$handlers[name] && this.$handlers[name].apply(this, [this]), fallback, this.noop).apply(this, [...args]), execHook && this.execHook(hook || name), ] }); /** Interceptor */ Object.defineProperty(this, "intercept", { enumerable: true, configurable: true, writable: true, value: (opt) => this.MOBXEvent((typeof opt === 'function') ? { type: "interceptor", call: opt } : Object.assign({ type: "interceptor" }, opt)) }); /** Observer */ Object.defineProperty(this, "observe", { enumerable: true, configurable: true, writable: true, value: (opt) => this.MOBXEvent((typeof opt === 'function') ? { type: "observer", call: opt } : Object.assign({ type: "observer" }, opt)) }); /** Event Handler: On Clear */ Object.defineProperty(this, "onClear", { enumerable: true, configurable: true, writable: true, value: (...args) => this.execHandler(FieldProps_1.FieldPropsEnum.onClear, args, (e) => { (0, utils_1.isEvent)(e) && e.preventDefault(); this.clear(true, false); }) }); /** Event Handler: On Reset */ Object.defineProperty(this, "onReset", { enumerable: true, configurable: true, writable: true, value: (...args) => this.execHandler(FieldProps_1.FieldPropsEnum.onReset, args, (e) => { (0, utils_1.isEvent)(e) && e.preventDefault(); this.reset(true, false); }) }); /** Event Handler: On Submit */ Object.defineProperty(this, "onSubmit", { enumerable: true, configurable: true, writable: true, value: (...args) => this.execHandler(FieldProps_1.FieldPropsEnum.onSubmit, args, (e, o = {}) => { (0, utils_1.isEvent)(e) && e.preventDefault(); this.submit(o); }, null, false) }); /** Event Handler: On Add */ Object.defineProperty(this, "onAdd", { enumerable: true, configurable: true, writable: true, value: (...args) => this.execHandler(FieldProps_1.FieldPropsEnum.onAdd, args, (e, val) => { (0, utils_1.isEvent)(e) && e.preventDefault(); this.add((0, utils_1.isEvent)(val) ? null : val, false); }) }); /** Event Handler: On Del */ Object.defineProperty(this, "onDel", { enumerable: true, configurable: true, writable: true, value: (...args) => this.execHandler(FieldProps_1.FieldPropsEnum.onDel, args, (e, path) => { (0, utils_1.isEvent)(e) && e.preventDefault(); this.del((0, utils_1.isEvent)(path) ? this.path : path, false); }) }); (0, mobx_1.makeObservable)(this, { $submitted: mobx_1.observable, $submitting: mobx_1.observable, $validated: mobx_1.observable, $validating: mobx_1.observable, $clearing: mobx_1.observable, $resetting: mobx_1.observable, $touched: mobx_1.observable, $changed: mobx_1.observable, $hooks: mobx_1.observable, $handlers: mobx_1.observable, changed: mobx_1.computed, submitted: mobx_1.computed, submitting: mobx_1.computed, validated: mobx_1.computed, validating: mobx_1.computed, clearing: mobx_1.computed, resetting: mobx_1.computed, hasIncrementalKeys: mobx_1.computed, hasNestedFields: mobx_1.computed, size: mobx_1.computed, // initialization initField: mobx_1.action, // actions submit: mobx_1.action, deepUpdate: mobx_1.action, set: mobx_1.action, add: mobx_1.action, del: mobx_1.action, }); } get resetting() { return this.hasNestedFields ? this.check(FieldProps_1.FieldPropsEnum.resetting, true) : this.$resetting; } get clearing() { return this.hasNestedFields ? this.check(FieldProps_1.FieldPropsEnum.clearing, true) : this.$clearing; } get submitted() { return (0, mobx_1.toJS)(this.$submitted); } get submitting() { return (0, mobx_1.toJS)(this.$submitting); } get validated() { return (0, mobx_1.toJS)(this.$validated); } get validating() { return (0, mobx_1.toJS)(this.$validating); } get hasIncrementalKeys() { return !!this.fields.size && (0, utils_1.hasIntKeys)(this.fields); } get hasNestedFields() { return this.fields.size !== 0; } get size() { return this.fields.size; } get changed() { return !lodash_1.default.isNil(this.path) && this.hasNestedFields ? (this.reduce((acc, field) => (acc + field.changed), 0) + this.$changed) : this.$changed; } /****************************************************************** Initializer */ initFields(initial, update = false) { const fallback = this.state.options.get(OptionsModel_1.OptionsEnum.fallback); const $path = (key) => lodash_1.default.trimStart([this.path, key].join("."), "."); let fields; fields = (0, parser_1.prepareFieldsData)(initial, this.state.strict, fallback); fields = (0, parser_1.mergeSchemaDefaults)(fields, this.validator); // create fields lodash_1.default.forIn(fields, (field, key) => { const path = $path(key); const $f = this.select(path, null, false); if (lodash_1.default.isNil($f)) { if (fallback) { this.initField(key, path, field, update); } else { const structPath = (0, utils_1.pathToStruct)(path); const struct = this.state.struct(); const found = struct .filter((s) => s.startsWith(structPath)) .find((s) => s.charAt(structPath.length) === "." || s.substring(structPath.length, structPath.length + 2) === "[]" || s === structPath); if (found) this.initField(key, path, field, update); } } }); } initField(key, path, data, update = false) { const initial = this.state.get("current", "props"); const struct = (0, utils_1.pathToStruct)(path); // try to get props from separated objects const _try = (prop) => { const t = lodash_1.default.get(initial[prop], struct); if ([ FieldProps_1.FieldPropsEnum.input, FieldProps_1.FieldPropsEnum.output, FieldProps_1.FieldPropsEnum.converter, ].includes(prop) && typeof t !== "function") return undefined; return t; }; const props = { $value: lodash_1.default.get(initial[FieldProps_1.SeparatedPropsMode.values], path), $computed: _try(FieldProps_1.SeparatedPropsMode.computed), $label: _try(FieldProps_1.SeparatedPropsMode.labels), $placeholder: _try(FieldProps_1.SeparatedPropsMode.placeholders), $default: _try(FieldProps_1.SeparatedPropsMode.defaults), $initial: _try(FieldProps_1.SeparatedPropsMode.initials), $disabled: _try(FieldProps_1.SeparatedPropsMode.disabled), $deleted: _try(FieldProps_1.SeparatedPropsMode.deleted), $type: _try(FieldProps_1.SeparatedPropsMode.types), $related: _try(FieldProps_1.SeparatedPropsMode.related), $rules: _try(FieldProps_1.SeparatedPropsMode.rules), $options: _try(FieldProps_1.SeparatedPropsMode.options), $bindings: _try(FieldProps_1.SeparatedPropsMode.bindings), $extra: _try(FieldProps_1.SeparatedPropsMode.extra), $hooks: _try(FieldProps_1.SeparatedPropsMode.hooks), $handlers: _try(FieldProps_1.SeparatedPropsMode.handlers), $validatedWith: _try(FieldProps_1.SeparatedPropsMode.validatedWith), $validators: _try(FieldProps_1.SeparatedPropsMode.validators), $observers: _try(FieldProps_1.SeparatedPropsMode.observers), $interceptors: _try(FieldProps_1.SeparatedPropsMode.interceptors), $converters: _try(FieldProps_1.SeparatedPropsMode.converters), $input: _try(FieldProps_1.SeparatedPropsMode.input), $output: _try(FieldProps_1.SeparatedPropsMode.output), $autoFocus: _try(FieldProps_1.SeparatedPropsMode.autoFocus), $ref: _try(FieldProps_1.SeparatedPropsMode.refs), $nullable: _try(FieldProps_1.SeparatedPropsMode.nullable), $autoComplete: _try(FieldProps_1.SeparatedPropsMode.autoComplete), }; const field = this.state.form.makeField({ key, path, struct, data, props, update, state: this.state, }, data && data[FieldProps_1.FieldPropsEnum.class] || _try(FieldProps_1.SeparatedPropsMode.classes)); this.fields.merge({ [key]: field }); return field; } /****************************************************************** Actions */ validate(opt, obj) { const $opt = lodash_1.default.merge(opt, { path: this.path }); return this.state.form.validator.validate($opt, obj); } /** Submit */ submit(hooks = {}, { execOnSubmitHook = true, execValidationHooks = true, validate = true } = {}) { const execOnSubmit = () => this.execHook(FieldProps_1.FieldPropsEnum.onSubmit, hooks); const submit = execOnSubmitHook ? execOnSubmit() : undefined; this.$submitting = true; this.$submitted += 1; if (!validate || !this.state.options.get(OptionsModel_1.OptionsEnum.validateOnSubmit, this)) { return Promise .resolve(submit) .then((0, mobx_1.action)(() => (this.$submitting = false))) .catch((0, mobx_1.action)((err) => { this.$submitting = false; throw err; })) .then(() => this); } const exec = (isValid) => isValid ? this.execHook(ValidatorInterface_1.ValidationHooks.onSuccess, hooks) : this.execHook(ValidatorInterface_1.ValidationHooks.onError, hooks); return (this.validate({ showErrors: this.state.options.get(OptionsModel_1.OptionsEnum.showErrorsOnSubmit, this), }) .then(({ isValid }) => { const handler = execValidationHooks ? exec(isValid) : undefined; if (isValid) return Promise.all([submit, handler]); const $err = this.state.options.get(OptionsModel_1.OptionsEnum.defaultGenericError, this); const $throw = this.state.options.get(OptionsModel_1.OptionsEnum.submitThrowsError, this); if ($throw && $err) this.invalidate(); return Promise.all([submit, handler]); }) .then((0, mobx_1.action)(() => (this.$submitting = false))) .catch((0, mobx_1.action)((err) => { this.$submitting = false; throw err; })) .then(() => this)); } /** Check Field Computed Values */ check(prop, deep = false) { (0, utils_1.allowedProps)(FieldProps_1.AllowedFieldPropsTypes.computed, [prop]); return deep ? (0, utils_1.checkPropOccurrence)({ type: utils_1.props.occurrences[prop], data: this.deepCheck(utils_1.props.occurrences[prop], prop, this.fields), }) : this[prop]; } deepCheck(type, prop, fields) { const $fields = (0, utils_1.getObservableMapValues)(fields); return lodash_1.default.transform($fields, (check, field) => { if (!field.fields.size || !Array.isArray(field.initial)) { check.push(field[prop]); } check.push((0, utils_1.checkPropOccurrence)({ data: this.deepCheck(type, prop, field.fields), type, })); return check; }, []); } /** Update Field Values recurisvely OR Create Field if 'undefined' */ update(fields) { if (!lodash_1.default.isPlainObject(fields)) { throw new Error("The update() method accepts only plain objects."); } this.deepUpdate((0, parser_1.prepareFieldsData)({ fields }, this.state.strict), undefined, undefined, fields); } deepUpdate(fields, path = "", recursion = true, raw) { lodash_1.default.each(fields, (field, key) => { var _a; const $key = lodash_1.default.has(field, FieldProps_1.FieldPropsEnum.name) ? field.name : key; const $path = lodash_1.default.trimStart(`${path}.${$key}`, "."); const strictUpdate = this.state.options.get(OptionsModel_1.OptionsEnum.strictUpdate, this); const $field = this.select($path, null, strictUpdate); const $container = this.select(path, null, false) || this.state.form.select(this.path, null, false); const applyInputConverterOnUpdate = this.state.options.get(OptionsModel_1.OptionsEnum.applyInputConverterOnUpdate, this); if (!lodash_1.default.isNil($field) && !lodash_1.default.isUndefined(field)) { if (Array.isArray($field.values())) { const n = (_a = lodash_1.default.max(lodash_1.default.map(field.fields, (f, i) => Number(i)))) !== null && _a !== void 0 ? _a : -1; (0, utils_1.getObservableMapValues)($field.fields).forEach(($f) => { if (Number($f.name) > n) { $field.$changed++; $field.state.form.$changed++; $field.fields.delete($f.name); } }); } if (field === null || field === void 0 ? void 0 : field.fields) { const fallback = this.state.options.get(OptionsModel_1.OptionsEnum.fallback); const x = this.state.struct().findIndex(s => s.startsWith($field.path.replace(/\.\d+\./, '[].') + '[]')); if (!fallback && $field.fields.size === 0 && x < 0) { $field.value = (0, parser_1.parseInput)(applyInputConverterOnUpdate ? $field.$input : (val) => val, { fallbackValueOption: this.state.options.get(OptionsModel_1.OptionsEnum.fallbackValue, this), separated: lodash_1.default.get(raw, $path), }); return; } } if (lodash_1.default.isNull(field) || lodash_1.default.isNil(field.fields)) { $field.value = (0, parser_1.parseInput)(applyInputConverterOnUpdate ? $field.$input : (val) => val, { fallbackValueOption: this.state.options.get(OptionsModel_1.OptionsEnum.fallbackValue, this), separated: field, }); return; } } if (!lodash_1.default.isNil($container) && lodash_1.default.isNil($field)) { // get full path when using update() with select() - FIX: #179 const $newFieldPath = lodash_1.default.trimStart([this.path, $path].join("."), "."); // init field into the container field $container.$changed++; $container.state.form.$changed++; $container.initField($key, $newFieldPath, field, true); } else if (recursion) { if (lodash_1.default.has(field, FieldProps_1.FieldPropsEnum.fields) && !lodash_1.default.isNil(field.fields)) { // handle nested fields if defined this.deepUpdate(field.fields, $path); } else { // handle nested fields if undefined or null const $fields = (0, parser_1.pathToFieldsTree)(this.state.struct(), $path); this.deepUpdate($fields, $path, false); } } }); } /** Get Fields Props */ get(prop = null, strict = true) { if (lodash_1.default.isNil(prop)) { return this.deepGet([...utils_1.props.computed, ...utils_1.props.editable, ...utils_1.props.validation], this.fields, strict); } (0, utils_1.allowedProps)(FieldProps_1.AllowedFieldPropsTypes.all, Array.isArray(prop) ? prop : [prop]); if (lodash_1.default.isString(prop)) { if ([ FieldProps_1.FieldPropsEnum.hooks, FieldProps_1.FieldPropsEnum.handlers ].includes(prop)) { return this[`$${prop}`]; } if (strict && this.fields.size === 0) { const retrieveNullifiedEmptyStrings = this.state.options.get(OptionsModel_1.OptionsEnum.retrieveNullifiedEmptyStrings, this); return (0, parser_1.parseCheckOutput)(this, prop, strict ? retrieveNullifiedEmptyStrings : false); } const value = this.deepGet(prop, this.fields, strict); const removeNullishValuesInArrays = this.state.options.get(OptionsModel_1.OptionsEnum.removeNullishValuesInArrays, this); return (0, parser_1.parseCheckArray)(this, value, prop, strict ? removeNullishValuesInArrays : false); } return this.deepGet(prop, this.fields, strict); } /** Get Fields Props Recursively */ deepGet(prop, fields, strict = true) { return lodash_1.default.transform((0, utils_1.getObservableMapValues)(fields), (obj, field) => { const $nested = ($fields) => $fields.size !== 0 ? this.deepGet(prop, $fields, strict) : undefined; Object.assign(obj, { [field.key]: { fields: $nested(field.fields) }, }); if (lodash_1.default.isString(prop)) { const opt = this.state.options; const removeProp = ((opt.get(OptionsModel_1.OptionsEnum.retrieveOnlyDirtyFieldsValues, this) && prop === FieldProps_1.FieldPropsEnum.value && field.isPristine) || (opt.get(OptionsModel_1.OptionsEnum.retrieveOnlyEnabledFieldsValues, this) && prop === FieldProps_1.FieldPropsEnum.value && field.disabled) || (opt.get(OptionsModel_1.OptionsEnum.retrieveOnlyEnabledFieldsErrors, this) && prop === FieldProps_1.FieldPropsEnum.error && field.disabled && field.isValid && (!field.error || !field.hasError)) || (opt.get(OptionsModel_1.OptionsEnum.softDelete, this) && prop === FieldProps_1.FieldPropsEnum.value && field.deleted)); if (field.fields.size === 0) { delete obj[field.key]; if (removeProp) return obj; const retrieveNullifiedEmptyStrings = this.state.options.get(OptionsModel_1.OptionsEnum.retrieveNullifiedEmptyStrings, this); return Object.assign(obj, { [field.key]: (0, parser_1.parseCheckOutput)(field, prop, strict ? retrieveNullifiedEmptyStrings : false), }); } let value = this.deepGet(prop, field.fields, strict); if (prop === FieldProps_1.FieldPropsEnum.value) value = field.$output(value); delete obj[field.key]; if (removeProp) return obj; const removeNullishValuesInArrays = this.state.options.get(OptionsModel_1.OptionsEnum.removeNullishValuesInArrays, this); return Object.assign(obj, { [field.key]: (0, parser_1.parseCheckArray)(field, value, prop, strict ? removeNullishValuesInArrays : false), }); } lodash_1.default.each(prop, ($prop) => Object.assign(obj[field.key], { [$prop]: field[$prop], })); return obj; }, {}); } /** Set Fields Props */ set(prop, data) { // UPDATE CUSTOM PROP if (lodash_1.default.isString(prop) && !lodash_1.default.isUndefined(data)) { (0, utils_1.allowedProps)(FieldProps_1.AllowedFieldPropsTypes.editable, [prop]); const isPlain = [ FieldProps_1.FieldPropsEnum.hooks, FieldProps_1.FieldPropsEnum.handlers, ].includes(prop); const deep = (lodash_1.default.isObject(data) && prop === FieldProps_1.FieldPropsEnum.value) || (lodash_1.default.isPlainObject(data) && !isPlain); if (deep && this.hasNestedFields) return this.deepSet(prop, data, "", true); if (prop === FieldProps_1.FieldPropsEnum.value) { const applyInputConverterOnSet = this.state.options.get(OptionsModel_1.OptionsEnum.applyInputConverterOnSet, this); this.value = (0, parser_1.parseInput)(applyInputConverterOnSet ? this.$input : (val) => val, { fallbackValueOption: this.state.options.get(OptionsModel_1.OptionsEnum.fallbackValue, this), separated: data, }); } else if (isPlain) { Object.assign(this[`$${prop}`], data); } else { lodash_1.default.set(this, `$${prop}`, data); } return; } // NO PROP NAME PROVIDED ("prop" is value) if (lodash_1.default.isNil(data)) { if (this.hasNestedFields) this.deepSet(FieldProps_1.FieldPropsEnum.value, prop, "", true); else this.set(FieldProps_1.FieldPropsEnum.value, prop); } } /** Set Fields Props Recursively */ deepSet(prop, data, path = "", recursion = false) { const err = "You are updating a not existent field:"; const isStrict = this.state.options.get(OptionsModel_1.OptionsEnum.strictSet, this); if (lodash_1.default.isNil(data)) { this.each((field) => field.$value = (0, parser_1.defaultValue)({ fallbackValueOption: this.state.options.get(OptionsModel_1.OptionsEnum.fallbackValue, this), value: field.$value, nullable: field.$nullable, type: field.type, })); return; } lodash_1.default.each(data, ($val, $key) => { const $path = lodash_1.default.trimStart(`${path}.${$key}`, "."); // get the field by path joining keys recursively const field = this.select($path, null, isStrict); // if no field found when is strict update, throw error if (isStrict) (0, utils_1.throwError)($path, field, err); // update the field/fields if defined if (!lodash_1.default.isUndefined(field)) { // update field values or others props if (!lodash_1.default.isUndefined($val)) { field.set(prop, $val, recursion); } // update values recursively only if field has nested if (field.fields.size && lodash_1.default.isObject($val)) { this.deepSet(prop, $val, $path, recursion); } } }); } /** Add Field */ add(obj, execEvent = true) { if ((0, utils_1.isArrayOfObjects)(obj)) { lodash_1.default.each(obj, (values) => this.update({ [(0, utils_1.maxKey)(this.fields)]: values, })); this.$changed++; this.state.form.$changed++; execEvent && this.execHook(FieldProps_1.FieldPropsEnum.onAdd); return this; } let key; if (lodash_1.default.has(obj, FieldProps_1.FieldPropsEnum.key)) key = obj.key; if (lodash_1.default.has(obj, FieldProps_1.FieldPropsEnum.name)) key = obj.name; if (!key) key = (0, utils_1.maxKey)(this.fields); const $path = ($key) => lodash_1.default.trimStart([this.path, $key].join("."), "."); const tree = (0, parser_1.pathToFieldsTree)(this.state.struct(), this.path, 0, true); const field = this.initField(key, $path(key), lodash_1.default.merge(tree[0], obj)); const hasValues = lodash_1.default.has(obj, FieldProps_1.FieldPropsEnum.value) || lodash_1.default.has(obj, FieldProps_1.FieldPropsEnum.fields); if (!hasValues && !this.state.options.get(OptionsModel_1.OptionsEnum.preserveDeletedFieldsValues, this)) { const fallbackValueOption = this.state.options.get(OptionsModel_1.OptionsEnum.fallbackValue, this); field.$value = (0, parser_1.defaultValue)({ fallbackValueOption, value: field.$value, nullable: field.$nullable, type: field.type }); field.each((field) => field.$value = (0, parser_1.defaultValue)({ fallbackValueOption, value: field.$value, nullable: field.$nullable, type: field.type })); } this.$changed++; this.state.form.$changed++; execEvent && this.execHook(FieldProps_1.FieldPropsEnum.onAdd); return field; } /** Del Field */ del($path = null, execEvent = true) { const isStrict = this.state.options.get(OptionsModel_1.OptionsEnum.strictDelete, this); const path = (0, parser_1.parsePath)($path !== null && $path !== void 0 ? $path : this.path); const fullpath = lodash_1.default.trim([this.path, path].join("."), "."); const container = this.container($path); const keys = lodash_1.default.split(path, "."); const last = lodash_1.default.last(keys); if (isStrict && !container.fields.has(last)) { const msg = `Key "${last}" not found when trying to delete field`; (0, utils_1.throwError)(fullpath, null, msg); } container.$changed++; container.state.form.$changed++; if (this.state.options.get(OptionsModel_1.OptionsEnum.softDelete, this)) { return this.select(fullpath).set(FieldProps_1.FieldPropsEnum.deleted, true); } container.each((field) => field.debouncedValidation.cancel()); execEvent && this.execHook(FieldProps_1.FieldPropsEnum.onDel); return container.fields.delete(last); } /****************************************************************** Events */ /** MobX Event (observe/intercept) */ MOBXEvent({ prop = FieldProps_1.FieldPropsEnum.value, key = null, path = null, call, type }) { let $prop = key || prop; (0, utils_1.allowedProps)(FieldProps_1.AllowedFieldPropsTypes.observable, [$prop]); const $instance = this.select(path || this.path, null, null) || this; const $call = (change) => call.apply(null, [ { change, form: this.state.form, path: $instance.path || null, field: $instance.path ? $instance : null, }, ]); let fn; let ffn; if (type === "observer") { fn = mobx_1.observe; ffn = (cb) => (0, mobx_1.observe)($instance.fields, cb); // fields } if (type === "interceptor") { $prop = `$${prop}`; fn = mobx_1.intercept; ffn = $instance.fields.intercept; // fields } const $dkey = $instance.path ? `${$prop}@${$instance.path}` : $prop; lodash_1.default.merge(this.state.disposers[type], { [$dkey]: $prop === FieldProps_1.FieldPropsEnum.fields ? ffn.apply((change) => $call(change)) : fn($instance, $prop, (change) => $call(change)), }); } /** Dispose MOBX Events */ dispose(opt = null) { if (this.path && opt) return this.disposeSingle(opt); return this.disposeAll(); } /** Dispose All Events (observe/intercept) */ disposeAll() { const dispose = (disposer) => disposer.apply(); lodash_1.default.each(this.state.disposers.interceptor, dispose); lodash_1.default.each(this.state.disposers.observer, dispose); this.state.disposers = { interceptor: {}, observer: {} }; return null; } /** Dispose Single Event (observe/intercept) */ disposeSingle({ type, key = FieldProps_1.FieldPropsEnum.value, path = null }) { const $path = (0, parser_1.parsePath)(path !== null && path !== void 0 ? path : this.path); // eslint-disable-next-line if (type === "interceptor") key = `$${key}`; // target observables this.state.disposers[type][`${key}@${$path}`].apply(); delete this.state.disposers[type][`${key}@${$path}`]; } /****************************************************************** Utils */ /** Fields Selector */ select(path, fields = null, isStrict = true) { const $path = (0, parser_1.parsePath)(path); const keys = lodash_1.default.split($path, "."); const head = lodash_1.default.head(keys); keys.shift(); let $fields = lodash_1.default.isNil(fields) ? this.fields.get(head) : fields.get(head); let stop = false; lodash_1.default.each(keys, ($key) => { if (stop) return; if (lodash_1.default.isNil($fields)) { $fields = undefined; stop = true; } else { $fields = $fields.fields.get($key); } }); if (isStrict && this.state.options.get(OptionsModel_1.OptionsEnum.strictSelect, this)) { (0, utils_1.throwError)(path, $fields); } return $fields; } /** Get Container */ container($path) { const path = (0, parser_1.parsePath)($path !== null && $path !== void 0 ? $path : this.path); const cpath = lodash_1.default.trim(path.replace(new RegExp("[^./]+$"), ""), "."); if (!!this.path && lodash_1.default.isNil($path)) { return cpath !== "" ? this.state.form.select(cpath, null, false) : this.state.form; } return cpath !== "" ? this.select(cpath, null, false) : this; } /** Has Field */ has(path) { return this.fields.has(path); } /** Map Fields */ map(cb) { return (0, utils_1.getObservableMapValues)(this.fields).map(cb); } /** * Iterates deeply over fields and invokes `iteratee` for each element. * The iteratee is invoked with three arguments: (value, index|key, depth). * * @param {Function} iteratee The function invoked per iteration. * @param {Array|Object} [fields=form.fields] fields to iterate over. * @param {number} [depth=1] The recursion depth for internal use. * @returns {Array} Returns [fields.values()] of input [fields] parameter. * @example * * JSON.stringify(form) * // => { * "fields": { * "state": { * "fields": { * "city": { * "fields": { "places": { * "fields": {}, * "key": "places", "path": "state.city.places", "$value": "NY Places" * } * }, * "key": "city", "path": "state.city", "$value": "New York" * } * }, * "key": "state", "path": "state", "$value": "USA" * } * } * } * * const data = {}; * form.each(field => data[field.path] = field.value); * // => { * "state": "USA", * "state.city": "New York", * "state.city.places": "NY Places" * } * */ each(iteratee, fields = null, depth = 0) { const $fields = fields || this.fields; (0, utils_1.getObservableMapValues)($fields).forEach((field, index) => { iteratee(field, index, depth); if (field.fields.size !== 0) { this.each(iteratee, field.fields, depth + 1); } }); } reduce(iteratee, acc) { return (0, utils_1.getObservableMapValues)(this.fields).reduce(iteratee, acc); } /****************************************************************** Helpers */ /** Fields Selector (alias of select) */ $(key) { return this.select(key); } /** Fields Values (recursive with Nested Fields) */ values() { return this.get(FieldProps_1.FieldPropsEnum.value); } /** Fields Errors (recursive with Nested Fields) */ errors() { return this.get(FieldProps_1.FieldPropsEnum.error); } /** Fields Labels (recursive with Nested Fields) */ labels() { return this.get(FieldProps_1.FieldPropsEnum.label); } /** Fields Placeholders (recursive with Nested Fields) */ placeholders() { return this.get(FieldProps_1.FieldPropsEnum.placeholder); } /** Fields Default Values (recursive with Nested Fields) */ defaults() { return this.get(FieldProps_1.FieldPropsEnum.default); } /** Fields Initial Values (recursive with Nested Fields) */ initials() { return this.get(FieldProps_1.FieldPropsEnum.initial); } /** Fields Types (recursive with Nested Fields) */ types() { return this.get(FieldProps_1.FieldPropsEnum.type); } } exports["default"] = Base; /***/ }), /***/ "./src/Bindings.ts": /*!*************************!*\ !*** ./src/Bindings.ts ***! \*************************/ /***/ (function(__unused_webpack_module, exports, __webpack_require__) { var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); const lodash_1 = __importDefault(__webpack_require__(/*! lodash */ "lodash")); const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts"); const FieldProps_1 = __webpack_require__(/*! ./models/FieldProps */ "./src/models/FieldProps.ts"); class Bindings { constructor() { Object.defineProperty(this, "templates", { enumerable: true, configurable: true, writable: true, value: { // default: ({ field, form, props, keys, $try }) => ({ // [keys.id]: $try(props.id, field.id), // }), } }); Object.defineProperty(this, "rewriters", { enumerable: true, configurable: true, writable: true, value: { default: { id: FieldProps_1.FieldPropsEnum.id, name: FieldProps_1.FieldPropsEnum.name, type: FieldProps_1.FieldPropsEnum.type, value: FieldProps_1.FieldPropsEnum.value, checked: FieldProps_1.FieldPropsEnum.checked, label: FieldProps_1.FieldPropsEnum.label, placeholder: FieldProps_1.FieldPropsEnum.placeholder, disabled: FieldProps_1.FieldPropsEnum.disabled, autoComplete: FieldProps_1.FieldPropsEnum.autoComplete, onChange: FieldProps_1.FieldPropsEnum.onChange, onBlur: FieldProps_1.FieldPropsEnum.onBlur, onFocus: FieldProps_1.FieldPropsEnum.onFocus, autoFocus: FieldProps_1.FieldPropsEnum.autoFocus, inputMode: FieldProps_1.FieldPropsEnum.inputMode, onKeyUp: FieldProps_1.FieldPropsEnum.onKeyUp, onKeyDown: FieldProps_1.FieldPropsEnum.onKeyDown, }, } }); } register(bindings) { lodash_1.default.each(bindings, (val, key) => { if ((typeof val === 'function')) lodash_1.default.merge(this.templates, { [key]: val }); if (lodash_1.default.isPlainObject(val)) lodash_1.default.merge(this.rewriters, { [key]: val }); }); return this; } load(field, name = FieldProps_1.FieldPropsEnum.default, props) { const args = ({ keys: lodash_1.default.get(this.rewriters, FieldProps_1.FieldPropsEnum.default), form: field.state.form, field, props, $try: utils_1.$try, }); if (lodash_1.default.has(this.templates, FieldProps_1.FieldPropsEnum.default)) { return lodash_1.default.get(this.templates, name)(args); } if (lodash_1.default.has(this.rewriters, name)) { const $bindings = {}; lodash_1.default.each(lodash_1.default.get(this.rewriters, name), ($v, $k) => lodash_1.default.merge($bindings, { [$v]: (0, utils_1.$try)(props[$k], field[$k]) })); return $bindings; } return lodash_1.default.get(this.templates, name)(args); } } exports["default"] = Bindings; /***/ }), /***/ "./src/Field.ts": /*!**********************!*\ !*** ./src/Field.ts ***! \**********************/ /***/ (function(__unused_webpack_module, exports, __webpack_require__) { var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); const mobx_1 = __webpack_require__(/*! mobx */ "mobx"); const lodash_1 = __importDefault(__webpack_require__(/*! lodash */ "lodash")); const Base_1 = __importDefault(__webpack_require__(/*! ./Base */ "./src/Base.ts")); const utils_1 = __webpack_require__(/*! ./utils */ "./src/utils.ts"); const parser_1 = __webpack_require__(/*! ./parser */ "./src/parser.ts"); const OptionsModel_1 = __webpack_require__(/*! ./models/OptionsModel */ "./src/models/OptionsModel.ts"); const FieldProps_1 = __webpack_require__(/*! ./models/FieldProps */ "./src/models/FieldProps.ts"); const applyFieldPropFunc = (instance, prop) => { if (typeof prop !== 'function') return prop; return prop.apply(instance, [{ field: instance, form: instance.state.form }]); }; const retrieveFieldPropFunc = (prop) => (typeof prop === 'function') ? prop : undefined; const propGetter = (instance, prop) => (typeof instance[`_${prop}`] === 'function') ? instance[`_${prop}`].apply(instance, [{ form: instance.state.form, field: instance, }]) : instance[`$${prop}`]; const setupFieldProps = (instance, props, data) => Object.assign(instance, { // retrieve functions _label: retrieveFieldPropFunc(props.$label || (data === null || data === void 0 ? void 0 : data.label)), _placeholder: retrieveFieldPropFunc(props.$placeholder || (data === null || data === void 0 ? void 0 : data.placeholder)), _disabled: retrieveFieldPropFunc(props.$disabled || (data === null || data === void 0 ? void 0 : data.disabled)), _rules: retrieveFieldPropFunc(props.$rules || (data === null || data === void 0 ? void 0 : data.rules)), _related: retrieveFieldPropFunc(props.$related || (data === null || data === void 0 ? void 0 : data.related)), _deleted: retrieveFieldPropFunc(props.$deleted || (data === null || data === void 0 ? void 0 : data.deleted)), _validators: retrieveFieldPropFunc(props.$validators || (data === null || data === void 0 ? void 0 : data.validators)), _validatedWith: retrieveFieldPropFunc(props.$validatedWith || (data === null || data === void 0 ? void 0 : data.validatedWith)), _bindings: retrieveFieldPropFunc(props.$bindings || (data === null || data === void 0 ? void 0 : data.bindings)), _extra: retrieveFieldPropFunc(props.$extra || (data === null || data === void 0 ? void 0 : data.extra)), _options: retrieveFieldPropFunc(props.$options || (data === null || data === void 0 ? void 0 : data.options)), _autoFocus: retrieveFieldPropFunc(props.$autoFocus || (data === null || data === void 0 ? void 0 : data.autoFocus)), _inputMode: retrieveFieldPropFunc(props.$inputMode || (data === null || data === void 0 ? void 0 : data.inputMode)), // apply functions or value $label: applyFieldPropFunc(instance, props.$label || (data === null || data === void 0 ? void 0 : data.label) || ""), $placeholder: applyFieldPropFunc(instance, props.$placeholder || (data === null || data === void 0 ? void 0 : data.placeholder) || ""), $disabled: applyFieldPropFunc(instance, props.$disabled || (data === null || data === void 0 ? void 0 : data.disabled) || false), $rules: applyFieldPropFunc(instance, props.$rules || (data === null || data === void 0 ? void 0 : data.rules) || null), $related: applyFieldPropFunc(instance, props.$related || (data === null || data === void 0 ? void 0 : data.related) || []), $deleted: applyFieldPropFunc(instance, props.$deleted || (data === null || data === void 0 ? void 0 : data.deleted) || false), $validatedWith: applyFieldPropFunc(instance, props.$validatedWith || (data === null || data === void 0 ? void 0 : data.validatedWith) || FieldProps_1.FieldPropsEnum.value), $bindings: applyFieldPropFunc(instance, props.$bindings || (data === null || data === void 0 ? void 0 : data.bindings) || FieldProps_1.FieldPropsEnum.default), $extra: applyFieldPropFunc(instance, props.$extra || (data === null || data === void 0 ? void 0 : data.extra) || null), $options: applyFieldPropFunc(instance, props.$options || (data === null || data === void 0 ? void 0 : data.options) || {}), $autoFocus: applyFieldPropFunc(instance, props.$autoFocus || (data === null || data === void 0 ? void 0 : data.autoFocus) || false), $inputMode: applyFieldPropFunc(instance, props.$inputMode || (data === null || data === void 0 ? void 0 : data.inputMode) || undefined), $validators: applyFieldPropFunc(instance, props.$validators || (data === null || data === void 0 ? void 0 : data.validators) || null), // other props $hooks: props.$hooks || (data === null || data === void 0 ? void 0 : data.hooks) || {}, $handlers: props.$handlers || (data === null || data === void 0 ? void 0 : data.handlers) || {}, $observers: props.$observers || (data === null || data === void 0 ? void 0 : data.observers) || null, $interceptors: props.$interceptors || (data === null || data === void 0 ? void 0 : data.interceptors) || null, $ref: props.$ref || (data === null || data === void 0 ? void 0 : data.ref) || undefined, $nullable: props.$nullable || (data === null || data === void 0 ? void 0 : data.nullable) || false, $autoComplete: props.$autoComplete || (data === null || data === void 0 ? void 0 : data.autoComplete) || undefined, }); const setupDefaultProp = (instance, data, props, update, { isEmptyArray, fallbackValueOption }) => (0, parser_1.parseInput)((val) => val, { isEmptyArray, type: instance.type, unified: update ? (0, parser_1.defaultValue)({ fallbackValueOption, type: instance.type, value: instance.value }) : data === null || data === void 0 ? void 0 : data.default, separated: props.$default, fallback: instance.$initial, }); class Field extends Base_1.default { constructor({ key, path, struct, data = {}, props = {}, update = false, state, }) { super(); Object.defineProperty(this, "hasInitialNestedFields", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "incremental", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "id", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "key", { enumerable: true, configurable: true, writable: true, value: v