@alifd/field
Version:
Fields can be used to manage data when it comes to form data manipulation and validation. After being associated with a component, the form data can be automatically written back, read, and verified.
1,291 lines (1,054 loc) • 39.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _validate2 = _interopRequireDefault(require("@alifd/validate"));
var _utils = require("./utils");
var initMeta = {
state: '',
valueName: 'value',
trigger: 'onChange',
inputValues: []
};
var Field = /*#__PURE__*/function () {
function Field(com) {
var _this = this;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
(0, _classCallCheck2.default)(this, Field);
if (!com) {
(0, _utils.warning)('`this` is missing in `Field`, you should use like `new Field(this)`');
}
this.com = com;
this.fieldsMeta = {};
this.cachedBind = {};
this.instance = {};
this.instanceCount = {}; // holds constructor values. Used for setting field defaults on init if no other value or initValue is passed.
// Also used caching values when using `parseName: true` before a field is initialized
this.values = (0, _extends2.default)({}, options.values);
this.processErrorMessage = options.processErrorMessage;
this.afterValidateRerender = options.afterValidateRerender;
this.options = (0, _extends2.default)({
parseName: false,
forceUpdate: false,
scrollToFirstError: true,
first: false,
onChange: function onChange() {},
autoUnmount: true,
autoValidate: true
}, options);
['init', 'getValue', 'getValues', 'setValue', 'setValues', 'getError', 'getErrors', 'setError', 'setErrors', 'validateCallback', 'validatePromise', 'getState', 'reset', 'resetToDefault', 'remove', 'spliceArray', 'addArrayValue', 'deleteArrayValue', 'getNames'].forEach(function (m) {
_this[m] = _this[m].bind(_this);
});
}
(0, _createClass2.default)(Field, [{
key: "setOptions",
value: function setOptions(options) {
(0, _extends2.default)(this.options, options);
}
/**
* Controlled Component
* @param {String} name
* @param {Object} fieldOption
* @returns {Object} {value, onChange}
*/
}, {
key: "init",
value: function init(name) {
var _this2 = this;
var fieldOption = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var rprops = arguments.length > 2 ? arguments[2] : undefined;
var id = fieldOption.id,
initValue = fieldOption.initValue,
_fieldOption$valueNam = fieldOption.valueName,
valueName = _fieldOption$valueNam === void 0 ? 'value' : _fieldOption$valueNam,
_fieldOption$trigger = fieldOption.trigger,
trigger = _fieldOption$trigger === void 0 ? 'onChange' : _fieldOption$trigger,
_fieldOption$rules = fieldOption.rules,
rules = _fieldOption$rules === void 0 ? [] : _fieldOption$rules,
_fieldOption$props = fieldOption.props,
props = _fieldOption$props === void 0 ? {} : _fieldOption$props,
_fieldOption$getValue = fieldOption.getValueFromEvent,
getValueFromEvent = _fieldOption$getValue === void 0 ? null : _fieldOption$getValue,
_fieldOption$getValue2 = fieldOption.getValueFormatter,
getValueFormatter = _fieldOption$getValue2 === void 0 ? getValueFromEvent : _fieldOption$getValue2,
setValueFormatter = fieldOption.setValueFormatter,
_fieldOption$autoVali = fieldOption.autoValidate,
autoValidate = _fieldOption$autoVali === void 0 ? true : _fieldOption$autoVali;
var parseName = this.options.parseName;
if (getValueFromEvent) {
(0, _utils.warning)('`getValueFromEvent` has been deprecated in `Field`, use `getValueFormatter` instead of it');
}
var originalProps = (0, _extends2.default)({}, props, rprops);
var defaultValueName = "default".concat(valueName[0].toUpperCase()).concat(valueName.slice(1));
var defaultValue;
if (typeof initValue !== 'undefined') {
defaultValue = initValue;
} else if (typeof originalProps[defaultValueName] !== 'undefined') {
// here use typeof, in case of defaultValue={0}
defaultValue = originalProps[defaultValueName];
} // get field from this.fieldsMeta or new one
var field = this._getInitMeta(name);
(0, _extends2.default)(field, {
valueName: valueName,
initValue: defaultValue,
disabled: 'disabled' in originalProps ? originalProps.disabled : false,
getValueFormatter: getValueFormatter,
setValueFormatter: setValueFormatter,
rules: Array.isArray(rules) ? rules : [rules],
ref: originalProps.ref
}); // Controlled Component, should always equal props.value
if (valueName in originalProps) {
field.value = originalProps[valueName]; // When rerendering set the values from props.value
if (parseName) {
this.values = (0, _utils.setIn)(this.values, name, field.value);
} else {
this.values[name] = field.value;
}
}
/**
* first init field (value not in field)
* should get field.value from this.values or defaultValue
*/
if (!('value' in field)) {
if (parseName) {
var cachedValue = (0, _utils.getIn)(this.values, name);
if (typeof cachedValue !== 'undefined') {
field.value = cachedValue;
} else {
// save struct to this.values even defaultValue is undefiend
field.value = defaultValue;
this.values = (0, _utils.setIn)(this.values, name, field.value);
}
} else {
var _cachedValue = this.values[name];
if (typeof _cachedValue !== 'undefined') {
field.value = _cachedValue;
} else if (typeof defaultValue !== 'undefined') {
// should be same with parseName, but compatible with old versions
field.value = defaultValue;
this.values[name] = field.value;
}
}
} // Component props
var inputProps = (0, _defineProperty2.default)({
'data-meta': 'Field',
id: id || name,
ref: this._getCacheBind(name, "".concat(name, "__ref"), this._saveRef)
}, valueName, setValueFormatter ? setValueFormatter(field.value, field.inputValues) : field.value);
var rulesMap = {};
if (this.options.autoValidate && autoValidate !== false) {
// trigger map in rules,
rulesMap = (0, _utils.mapValidateRules)(field.rules, trigger); // step1 : validate hooks
var _loop = function _loop(action) {
// skip default trigger, which will trigger in step2
if (action === trigger) {
return "continue";
}
var actionRule = rulesMap[action];
inputProps[action] = function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this2._callNativePropsEvent.apply(_this2, [action, originalProps].concat(args));
_this2._validate(name, actionRule, action);
};
};
for (var action in rulesMap) {
var _ret = _loop(action);
if (_ret === "continue") continue;
}
} // step2: onChange(trigger=onChange by default) hack
inputProps[trigger] = function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
_this2._updateFieldValue.apply(_this2, [name].concat(args)); // clear validate error
_this2._resetError(name);
_this2._callNativePropsEvent.apply(_this2, [trigger, originalProps].concat(args)); // call global onChange
_this2.options.onChange(name, field.value); // validate while onChange
var rule = rulesMap[trigger];
rule && _this2._validate(name, rule, trigger);
_this2._reRender();
};
delete originalProps[defaultValueName];
return (0, _extends2.default)({}, originalProps, inputProps);
}
/**
* call native event from props.onXx
* eg: props.onChange props.onBlur props.onFocus
*/
}, {
key: "_callNativePropsEvent",
value: function _callNativePropsEvent(action, props) {
for (var _len3 = arguments.length, args = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {
args[_key3 - 2] = arguments[_key3];
}
action in props && typeof props[action] === 'function' && props[action].apply(props, args);
}
}, {
key: "_getInitMeta",
value: function _getInitMeta(name) {
if (!(name in this.fieldsMeta)) {
this.fieldsMeta[name] = (0, _extends2.default)({}, initMeta);
}
return this.fieldsMeta[name];
}
/**
* update field.value and validate
*/
}, {
key: "_updateFieldValue",
value: function _updateFieldValue(name) {
for (var _len4 = arguments.length, others = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
others[_key4 - 1] = arguments[_key4];
}
var e = others[0];
var field = this._get(name);
if (!field) {
return;
}
field.value = field.getValueFormatter ? field.getValueFormatter.apply(this, others) : (0, _utils.getValueFromEvent)(e);
field.inputValues = others;
if (this.options.parseName) {
this.values = (0, _utils.setIn)(this.values, name, field.value);
} else {
this.values[name] = field.value;
}
}
/**
* ref must always be the same function, or if not it will be triggerd every time.
* @param {String} name name of component
* @param {String} action key to find ref
* @param {Function} fn saveRef
*/
}, {
key: "_getCacheBind",
value: function _getCacheBind(name, action, fn) {
var cache = this.cachedBind[name] = this.cachedBind[name] || {};
if (!cache[action]) {
cache[action] = fn.bind(this, name);
}
return cache[action];
}
}, {
key: "_setCache",
value: function _setCache(name, action, hander) {
var cache = this.cachedBind[name] = this.cachedBind[name] || {};
cache[action] = hander;
}
}, {
key: "_getCache",
value: function _getCache(name, action) {
var cache = this.cachedBind[name] || {};
return cache[action];
}
/**
* NOTE: saveRef is async function. it will be called after render
* @param {String} name name of component
* @param {Function} component ref
*/
}, {
key: "_saveRef",
value: function _saveRef(name, component) {
var key = "".concat(name, "_field");
var autoUnmount = this.options.autoUnmount;
if (!component && autoUnmount) {
// more than one component, do nothing
this.instanceCount[name] && this.instanceCount[name]--;
if (this.instanceCount[name] > 0) {
return;
} // component with same name (eg: type ? <A name="n"/>:<B name="n"/>)
// while type changed, B will render before A unmount. so we should cached value for B
// step: render -> B mount -> 1. _saveRef(A, null) -> 2. _saveRef(B, ref) -> render
// 1. _saveRef(A, null)
var cache = this.fieldsMeta[name];
cache && this._setCache(name, key, cache); // after destroy, delete data
delete this.instance[name];
this.remove(name);
return;
} // 2. _saveRef(B, ref) (eg: same name but different compoent may be here)
if (autoUnmount && !this.fieldsMeta[name] && this._getCache(name, key)) {
this.fieldsMeta[name] = this._getCache(name, key);
this.setValue(name, this.fieldsMeta[name] && this.fieldsMeta[name].value, false);
} // only one time here
var field = this._get(name);
if (field) {
var ref = field.ref;
if (ref) {
if (typeof ref === 'string') {
throw new Error("can not set string ref for ".concat(name));
} else if (typeof ref === 'function') {
ref(component);
} else if ((0, _typeof2.default)(ref) === 'object' && 'current' in ref) {
// while ref = React.createRef() ref={ current: null}
ref.current = component;
}
} // mount
if (autoUnmount && component) {
var cnt = this.instanceCount[name];
if (!cnt) {
cnt = 0;
}
this.instanceCount[name] = cnt + 1;
}
this.instance[name] = component;
}
}
/**
* validate one Component
* @param {String} name name of Component
* @param {Array} rule
* @param {String} trigger onChange/onBlur/onItemClick/...
*/
}, {
key: "_validate",
value: function _validate(name, rule, trigger) {
var _this3 = this;
var field = this._get(name);
if (!field) {
return;
}
var value = field.value;
field.state = 'loading';
var validate = this._getCache(name, trigger);
if (validate && typeof validate.abort === 'function') {
validate.abort();
}
validate = new _validate2.default((0, _defineProperty2.default)({}, name, rule), {
messages: this.options.messages
});
this._setCache(name, trigger, validate);
validate.validate((0, _defineProperty2.default)({}, name, value), function (errors) {
var newErrors, newState;
if (errors && errors.length) {
newErrors = (0, _utils.getErrorStrs)(errors, _this3.processErrorMessage);
newState = 'error';
} else {
newErrors = [];
newState = 'success';
}
var reRender = false; // only status or errors changed, Rerender
if (newState !== field.state || !field.errors || newErrors.length !== field.errors.length || newErrors.find(function (e, idx) {
return e !== field.errors[idx];
})) {
reRender = true;
}
field.errors = newErrors;
field.state = newState;
reRender && _this3._reRender();
});
}
}, {
key: "getValue",
value: function getValue(name) {
if (this.options.parseName) {
return (0, _utils.getIn)(this.values, name);
}
return this.values[name];
}
/**
* 1. get values by names.
* 2. If no names passed, return shallow copy of `field.values`
* @param {Array} names
*/
}, {
key: "getValues",
value: function getValues(names) {
var _this4 = this;
var allValues = {};
if (names && names.length) {
names.forEach(function (name) {
allValues[name] = _this4.getValue(name);
});
} else {
(0, _extends2.default)(allValues, this.values);
}
return allValues;
}
}, {
key: "setValue",
value: function setValue(name, value) {
var reRender = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
if (name in this.fieldsMeta) {
this.fieldsMeta[name].value = value;
}
if (this.options.parseName) {
this.values = (0, _utils.setIn)(this.values, name, value);
} else {
this.values[name] = value;
}
reRender && this._reRender();
}
}, {
key: "setValues",
value: function setValues() {
var _this5 = this;
var fieldsValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var reRender = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (!this.options.parseName) {
Object.keys(fieldsValue).forEach(function (name) {
_this5.setValue(name, fieldsValue[name], false);
});
} else {
// NOTE: this is a shallow merge
// Ex. we have two values a.b.c=1 ; a.b.d=2, and use setValues({a:{b:{c:3}}}) , then because of shallow merge a.b.d will be lost, we will get only {a:{b:{c:3}}}
this.values = (0, _extends2.default)({}, this.values, fieldsValue);
var fields = this.getNames();
fields.forEach(function (name) {
var value = (0, _utils.getIn)(_this5.values, name);
if (value !== undefined) {
// copy over values that are in this.values
_this5.fieldsMeta[name].value = value;
} else {
// because of shallow merge
// if no value then copy values from fieldsMeta to keep initialized component data
_this5.values = (0, _utils.setIn)(_this5.values, name, _this5.fieldsMeta[name].value);
}
});
}
reRender && this._reRender();
}
}, {
key: "setError",
value: function setError(name, errors) {
var err = Array.isArray(errors) ? errors : errors ? [errors] : [];
if (name in this.fieldsMeta) {
this.fieldsMeta[name].errors = err;
} else {
this.fieldsMeta[name] = {
errors: err
};
}
if (this.fieldsMeta[name].errors && this.fieldsMeta[name].errors.length > 0) {
this.fieldsMeta[name].state = 'error';
} else {
this.fieldsMeta[name].state = '';
}
this._reRender();
}
}, {
key: "setErrors",
value: function setErrors() {
var _this6 = this;
var fieldsErrors = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
Object.keys(fieldsErrors).forEach(function (name) {
_this6.setError(name, fieldsErrors[name]);
});
}
}, {
key: "getError",
value: function getError(name) {
var field = this._get(name);
if (field && field.errors && field.errors.length) {
return field.errors;
}
return null;
}
}, {
key: "getErrors",
value: function getErrors(names) {
var _this7 = this;
var fields = names || this.getNames();
var allErrors = {};
fields.forEach(function (f) {
allErrors[f] = _this7.getError(f);
});
return allErrors;
}
}, {
key: "getState",
value: function getState(name) {
var field = this._get(name);
if (field && field.state) {
return field.state;
}
return '';
}
/**
* Get errors using `getErrors` and format to match the structure of errors returned in field.validate
* @param {Array} fieldNames
* @return {Object || null} map of inputs and their errors
*/
}, {
key: "formatGetErrors",
value: function formatGetErrors(fieldNames) {
var errors = this.getErrors(fieldNames);
var formattedErrors = null;
for (var field in errors) {
if (errors.hasOwnProperty(field) && errors[field]) {
var errorsObj = errors[field];
if (!formattedErrors) {
formattedErrors = {};
}
formattedErrors[field] = {
errors: errorsObj
};
}
}
return formattedErrors;
}
/**
* validate by trigger
* @param {Array} ns names
* @param {Function} cb callback after validate
*/
}, {
key: "validateCallback",
value: function validateCallback(ns, cb) {
var _this8 = this;
var _getParams = (0, _utils.getParams)(ns, cb),
names = _getParams.names,
callback = _getParams.callback;
var fieldNames = names || this.getNames();
var descriptor = {};
var values = {};
var hasRule = false;
for (var i = 0; i < fieldNames.length; i++) {
var name = fieldNames[i];
var field = this._get(name);
if (!field) {
continue;
}
if (field.rules && field.rules.length) {
descriptor[name] = field.rules;
values[name] = this.getValue(name);
hasRule = true; // clear error
field.errors = [];
field.state = '';
}
}
if (!hasRule) {
var errors = this.formatGetErrors(fieldNames);
callback && callback(errors, this.getValues(names ? fieldNames : []));
return;
}
var validate = new _validate2.default(descriptor, {
first: this.options.first,
messages: this.options.messages
});
validate.validate(values, function (errors) {
var errorsGroup = null;
if (errors && errors.length) {
errorsGroup = {};
errors.forEach(function (e) {
var fieldName = e.field;
if (!errorsGroup[fieldName]) {
errorsGroup[fieldName] = {
errors: []
};
}
var fieldErrors = errorsGroup[fieldName].errors;
fieldErrors.push(e.message);
});
}
if (errorsGroup) {
// update error in every Field
Object.keys(errorsGroup).forEach(function (i) {
var field = _this8._get(i);
if (field) {
field.errors = (0, _utils.getErrorStrs)(errorsGroup[i].errors, _this8.processErrorMessage);
field.state = 'error';
}
});
}
var formattedGetErrors = _this8.formatGetErrors(fieldNames);
if (formattedGetErrors) {
errorsGroup = (0, _extends2.default)({}, formattedGetErrors, errorsGroup);
} // update to success which has no error
for (var _i = 0; _i < fieldNames.length; _i++) {
var _name = fieldNames[_i];
var _field = _this8._get(_name);
if (_field && _field.rules && !(errorsGroup && _name in errorsGroup)) {
_field.state = 'success';
}
} // eslint-disable-next-line callback-return
callback && callback(errorsGroup, _this8.getValues(names ? fieldNames : []));
_this8._reRender();
if (typeof _this8.afterValidateRerender === 'function') {
_this8.afterValidateRerender({
errorsGroup: errorsGroup,
options: _this8.options,
instance: _this8.instance
});
}
});
}
/**
* validate by trigger - Promise version
* NOTES:
* - `afterValidateRerender` is not called in `validatePromise`. The rerender is called just before this function
* returns a promise, so use the returned promise to call any after rerender logic.
*
* @param {Array} ns names
* @param {Function} cb (Optional) callback after validate, must return a promise or a value
* - ({errors, values}) => Promise({errors, values}) | {errors, values}
* @returns {Promise} - resolves with {errors, values}
*/
}, {
key: "validatePromise",
value: function () {
var _validatePromise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(ns, cb) {
var _getParams2, names, callback, fieldNames, descriptor, values, hasRule, i, name, field, _errors, validate, results, errors, errorsGroup, callbackResults;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_getParams2 = (0, _utils.getParams)(ns, cb), names = _getParams2.names, callback = _getParams2.callback;
fieldNames = names || this.getNames();
descriptor = {};
values = {};
hasRule = false;
i = 0;
case 6:
if (!(i < fieldNames.length)) {
_context.next = 15;
break;
}
name = fieldNames[i];
field = this._get(name);
if (field) {
_context.next = 11;
break;
}
return _context.abrupt("continue", 12);
case 11:
if (field.rules && field.rules.length) {
descriptor[name] = field.rules;
values[name] = this.getValue(name);
hasRule = true; // clear error
field.errors = [];
field.state = '';
}
case 12:
i++;
_context.next = 6;
break;
case 15:
if (hasRule) {
_context.next = 22;
break;
}
_errors = this.formatGetErrors(fieldNames);
if (!callback) {
_context.next = 21;
break;
}
return _context.abrupt("return", callback({
errors: _errors,
values: this.getValues(names ? fieldNames : [])
}));
case 21:
return _context.abrupt("return", {
errors: _errors,
values: this.getValues(names ? fieldNames : [])
});
case 22:
validate = new _validate2.default(descriptor, {
first: this.options.first,
messages: this.options.messages
});
_context.next = 25;
return validate.validatePromise(values);
case 25:
results = _context.sent;
errors = results && results.errors || [];
errorsGroup = this._getErrorsGroup({
errors: errors,
fieldNames: fieldNames
});
callbackResults = {
errors: errorsGroup,
values: this.getValues(names ? fieldNames : [])
};
_context.prev = 29;
if (!callback) {
_context.next = 34;
break;
}
_context.next = 33;
return callback(callbackResults);
case 33:
callbackResults = _context.sent;
case 34:
_context.next = 39;
break;
case 36:
_context.prev = 36;
_context.t0 = _context["catch"](29);
return _context.abrupt("return", _context.t0);
case 39:
this._reRender();
return _context.abrupt("return", callbackResults);
case 41:
case "end":
return _context.stop();
}
}
}, _callee, this, [[29, 36]]);
}));
function validatePromise(_x, _x2) {
return _validatePromise.apply(this, arguments);
}
return validatePromise;
}()
}, {
key: "_getErrorsGroup",
value: function _getErrorsGroup(_ref) {
var _this9 = this;
var errors = _ref.errors,
fieldNames = _ref.fieldNames;
var errorsGroup = null;
if (errors && errors.length) {
errorsGroup = {};
errors.forEach(function (e) {
var fieldName = e.field;
if (!errorsGroup[fieldName]) {
errorsGroup[fieldName] = {
errors: []
};
}
var fieldErrors = errorsGroup[fieldName].errors;
fieldErrors.push(e.message);
});
}
if (errorsGroup) {
// update error in every Field
Object.keys(errorsGroup).forEach(function (i) {
var field = _this9._get(i);
if (field) {
field.errors = (0, _utils.getErrorStrs)(errorsGroup[i].errors, _this9.processErrorMessage);
field.state = 'error';
}
});
}
var formattedGetErrors = this.formatGetErrors(fieldNames);
if (formattedGetErrors) {
errorsGroup = (0, _extends2.default)({}, formattedGetErrors, errorsGroup);
} // update to success which has no error
for (var i = 0; i < fieldNames.length; i++) {
var name = fieldNames[i];
var field = this._get(name);
if (field && field.rules && !(errorsGroup && name in errorsGroup)) {
field.state = 'success';
}
}
return errorsGroup;
}
}, {
key: "_reset",
value: function _reset(ns, backToDefault) {
var _this10 = this;
if (typeof ns === 'string') {
ns = [ns];
}
var changed = false;
var names = ns || Object.keys(this.fieldsMeta);
if (!ns) {
this.values = {};
}
names.forEach(function (name) {
var field = _this10._get(name);
if (field) {
changed = true;
field.value = backToDefault ? field.initValue : undefined;
field.state = '';
delete field.errors;
delete field.rules;
delete field.rulesMap;
if (_this10.options.parseName) {
_this10.values = (0, _utils.setIn)(_this10.values, name, field.value);
} else {
_this10.values[name] = field.value;
}
}
});
if (changed) {
this._reRender();
}
}
}, {
key: "reset",
value: function reset(ns) {
this._reset(ns, false);
}
}, {
key: "resetToDefault",
value: function resetToDefault(ns) {
this._reset(ns, true);
}
}, {
key: "getNames",
value: function getNames() {
var fieldsMeta = this.fieldsMeta;
return Object.keys(fieldsMeta).filter(function () {
return true;
});
}
}, {
key: "remove",
value: function remove(ns) {
var _this11 = this;
if (typeof ns === 'string') {
ns = [ns];
}
if (!ns) {
this.values = {};
}
var names = ns || Object.keys(this.fieldsMeta);
names.forEach(function (name) {
if (name in _this11.fieldsMeta) {
delete _this11.fieldsMeta[name];
}
if (_this11.options.parseName) {
_this11.values = (0, _utils.deleteIn)(_this11.values, name);
} else {
delete _this11.values[name];
}
});
}
}, {
key: "addArrayValue",
value: function addArrayValue(key, index) {
for (var _len5 = arguments.length, argv = new Array(_len5 > 2 ? _len5 - 2 : 0), _key5 = 2; _key5 < _len5; _key5++) {
argv[_key5 - 2] = arguments[_key5];
}
return this._spliceArrayValue.apply(this, [key, index, 0].concat(argv));
}
}, {
key: "deleteArrayValue",
value: function deleteArrayValue(key, index) {
var howmany = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
return this._spliceArrayValue(key, index, howmany);
}
/**
* splice array
* @param {String} key
* @param {Number} startIndex
* @param {Number} howmany
* @param {Array} argv
* @param {*} value
*/
}, {
key: "_spliceArrayValue",
value: function _spliceArrayValue(key, index, howmany) {
var _this12 = this;
for (var _len6 = arguments.length, argv = new Array(_len6 > 3 ? _len6 - 3 : 0), _key6 = 3; _key6 < _len6; _key6++) {
argv[_key6 - 3] = arguments[_key6];
}
var argc = argv.length;
var offset = howmany - argc; // how the reset fieldMeta move
var startIndex = index + howmany; // 计算起点
/**
* eg: call _spliceArrayValue('key', 1) to delete 'key.1':
* case 1: names=['key.0', 'key.1']; delete 'key.1';
* case 2: names=['key.0', 'key.1', 'key.2']; key.1= key.2; delete key.2;
* case 3: names=['key.0.name', 'key.0.email', 'key.1.name', 'key.1.email'], should delete 'key.1.name', 'key.1.email'
* eg: call _spliceArrayValue('key', 1, item) to add 'key.1':
* case 1: names=['key.0']; add 'key.1' = item;
* case 2: names=['key.0', 'key.1']; key.2= key.1; delete key.1; add key.1 = item;
*/
var listMap = {}; // eg: {1:[{from: 'key.2.name', to: 'key.1.name'}, {from: 'key.2.email', to: 'key.1.email'}]}
var keyReg = new RegExp("^(".concat(key, ".)(\\d+)"));
var replaceArgv = [];
var names = this.getNames(); // logic of offset fix begin
names.forEach(function (n) {
var ret = keyReg.exec(n);
if (ret) {
var idx = parseInt(ret[2]); // get index of 'key.0.name'
if (idx >= startIndex) {
var l = listMap[idx];
var item = {
from: n,
to: n.replace(keyReg, function (match, p1) {
return "".concat(p1).concat(idx - offset);
})
};
if (!l) {
listMap[idx] = [item];
} else {
l.push(item);
}
} // in case of offsetList.length = 0, eg: delete last element
if (offset > 0 && idx >= index && idx < index + howmany) {
replaceArgv.push(n);
}
}
}); // sort with index eg: [{index:1, list: [{from: 'key.2.name', to: 'key.1.name'}]}, {index:2, list: [...]}]
var offsetList = Object.keys(listMap).map(function (i) {
return {
index: Number(i),
list: listMap[i]
};
}).sort(function (a, b) {
return offset > 0 ? a.index - b.index : b.index - a.index;
});
offsetList.forEach(function (l) {
var list = l.list;
list.forEach(function (i) {
_this12.fieldsMeta[i.to] = _this12.fieldsMeta[i.from];
});
}); // delete copy data
if (offsetList.length > 0) {
var removeList = offsetList.slice(offsetList.length - (offset < 0 ? -offset : offset), offsetList.length);
removeList.forEach(function (item) {
item.list.forEach(function (i) {
delete _this12.fieldsMeta[i.from];
});
});
} else {
// will get from this.values while rerender
replaceArgv.forEach(function (i) {
delete _this12.fieldsMeta[i];
});
}
var p = this.getValue(key);
if (p) {
p.splice.apply(p, [index, howmany].concat(argv));
}
this._reRender();
}
/**
* splice in a Array [deprecated]
* @param {String} keyMatch like name.{index}
* @param {Number} startIndex index
*/
}, {
key: "spliceArray",
value: function spliceArray(keyMatch, startIndex, howmany) {
var _this13 = this;
if (keyMatch.match(/{index}$/) === -1) {
(0, _utils.warning)('key should match /{index}$/');
return;
} // regex to match field names in the same target array
var reg = keyMatch.replace('{index}', '(\\d+)');
var keyReg = new RegExp("^".concat(reg));
var listMap = {};
/**
* keyMatch='key.{index}'
* case 1: names=['key.0', 'key.1'], should delete 'key.1'
* case 2: names=['key.0.name', 'key.0.email', 'key.1.name', 'key.1.email'], should delete 'key.1.name', 'key.1.email'
*/
var names = this.getNames();
names.forEach(function (n) {
// is name in the target array?
var ret = keyReg.exec(n);
if (ret) {
var index = parseInt(ret[1]);
if (index > startIndex) {
var l = listMap[index];
var item = {
from: n,
to: "".concat(keyMatch.replace('{index}', index - 1)).concat(n.replace(ret[0], ''))
};
if (!l) {
listMap[index] = [item];
} else {
l.push(item);
}
}
}
});
var idxList = Object.keys(listMap).map(function (i) {
return {
index: Number(i),
list: listMap[i]
};
}).sort(function (a, b) {
return a.index < b.index;
}); // should be continuous array
if (idxList.length > 0 && idxList[0].index === startIndex + 1) {
idxList.forEach(function (l) {
var list = l.list;
list.forEach(function (i) {
var v = _this13.getValue(i.from); // get index value
_this13.setValue(i.to, v, false); // set value to index - 1
});
});
var lastIdxList = idxList[idxList.length - 1];
lastIdxList.list.forEach(function (i) {
_this13.remove(i.from);
});
var parentName = keyMatch.replace('.{index}', '');
parentName = parentName.replace('[{index}]', '');
var parent = this.getValue(parentName);
if (parent) {
// if parseName=true then parent is an Array object but does not know an element was removed
// this manually decrements the array length
parent.length--;
}
}
}
}, {
key: "_resetError",
value: function _resetError(name) {
var field = this._get(name);
if (field) {
delete field.errors; //清空错误
field.state = '';
}
} //trigger rerender
}, {
key: "_reRender",
value: function _reRender() {
if (this.com) {
if (!this.options.forceUpdate && this.com.setState) {
this.com.setState({});
} else if (this.com.forceUpdate) {
this.com.forceUpdate(); //forceUpdate 对性能有较大的影响,成指数上升
}
}
}
}, {
key: "_get",
value: function _get(name) {
return name in this.fieldsMeta ? this.fieldsMeta[name] : null;
}
}, {
key: "get",
value: function get(name) {
if (name) {
return this._get(name);
} else {
return this.fieldsMeta;
}
}
}], [{
key: "create",
value: function create(com) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return new this(com, options);
}
}, {
key: "getUseField",
value: function getUseField(_ref2) {
var _this14 = this;
var useState = _ref2.useState,
useMemo = _ref2.useMemo;
return function () {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _useState = useState(),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
setState = _useState2[1];
var field = useMemo(function () {
return _this14.create({
setState: setState
}, options);
}, [setState]);
return field;
};
}
}]);
return Field;
}();
var _default = Field;
exports.default = _default;