@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
213 lines (212 loc) • 7.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined"));
var _react = _interopRequireWildcard(require("react"));
var _uuid = require("@douyinfe/semi-foundation/lib/cjs/utils/uuid");
var _context = require("./context");
var _warning = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/utils/warning"));
var _fastCopy = _interopRequireDefault(require("fast-copy"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const filterArrayByIndex = (array, index) => array.filter((item, i) => i !== index);
const getUuidByArray = array => array.map(() => (0, _uuid.getUuidv4)());
const getUpdateKey = arrayField => {
if (!arrayField) {
return undefined;
}
if (arrayField && arrayField.updateKey) {
return arrayField.updateKey;
}
return undefined;
};
const initValueAdapter = initValue => {
const iv = [];
if (Array.isArray(initValue)) {
return initValue;
} else {
(0, _warning.default)(!(0, _isUndefined2.default)(initValue), '[Semi Form ArrayField] initValue of ArrayField must be an array. Please check the type of your props');
return iv;
}
};
/**
*
* @param {any[]} value
* @param {string[]} oldKeys
* @returns string[]
*/
const generateKeys = (value, oldKeys) => {
const val = initValueAdapter(value);
const newKeys = getUuidByArray(val);
// return newKeys;
const keys = newKeys.map((key, i) => oldKeys && oldKeys[i] ? oldKeys[i] : key);
return keys;
};
class ArrayFieldComponent extends _react.Component {
constructor(props, context) {
super(props, context);
const initValueInProps = this.props.initValue;
const {
field
} = this.props;
const initValueInForm = context.getValue(field);
const initValue = initValueInProps || initValueInForm;
this.state = {
keys: generateKeys(initValue)
};
this.add = this.add.bind(this);
this.addWithInitValue = this.addWithInitValue.bind(this);
this.remove = this.remove.bind(this);
this.cacheFieldValues = null;
this.cacheUpdateKey = null;
/*
If updateKey exists, it means that the arrayField (usually a nested ArrayField not at the first level) is only re-mounted due to setValues,
and the fields it contains do not need to consume initValue
*/
// whether the fields inside arrayField should use props.initValue in current render process
this.shouldUseInitValue = !context.getArrayField(field);
// Separate the arrays that reset and the usual add and remove modify, otherwise they will affect each other
const initValueCopyForFormState = (0, _fastCopy.default)(initValue);
const initValueCopyForReset = (0, _fastCopy.default)(initValue);
context.registerArrayField(field, initValueCopyForReset);
// register ArrayField will update state.updateKey to render, So there is no need to execute forceUpdate here
context.updateStateValue(field, initValueCopyForFormState, {
notNotify: true,
notUpdate: true
});
}
componentWillUnmount() {
const updater = this.context;
const {
field
} = this.props;
updater.unRegisterArrayField(field);
}
componentDidUpdate() {
const updater = this.context;
const {
field
} = this.props;
const {
keys
} = this.state;
const fieldValues = updater.getValue(field);
const updateKey = getUpdateKey(updater.getArrayField(field));
// when update form outside, like use formApi.setValue('field', [{newItem1, newItem2}]), formApi.setValues
// re generate keys to update arrayField;
if (updateKey !== this.cacheUpdateKey) {
const newKeys = generateKeys(fieldValues, keys);
// eslint-disable-next-line
this.setState({
keys: newKeys
});
this.cacheUpdateKey = updateKey;
if (this.cacheUpdateKey !== null) {
this.shouldUseInitValue = false;
}
}
}
add() {
const {
keys
} = this.state;
const {
field
} = this.props;
const updater = this.context;
keys.push((0, _uuid.getUuidv4)());
this.shouldUseInitValue = true;
this.setState({
keys
});
let updateKey = new Date().valueOf();
updater.updateArrayField(field, {
updateKey
});
this.cacheUpdateKey = updateKey;
}
addWithInitValue(rowVal) {
const updater = this.context;
const {
field
} = this.props;
const newArrayFieldVal = updater.getValue(field) ? updater.getValue(field).slice() : [];
const cloneRowVal = (0, _fastCopy.default)(rowVal);
newArrayFieldVal.push(cloneRowVal);
updater.updateStateValue(field, newArrayFieldVal, {});
updater.updateArrayField(field, {
updateKey: new Date().valueOf()
});
}
remove(i) {
const updater = this.context;
const {
keys
} = this.state;
const {
field
} = this.props;
const newKeys = filterArrayByIndex(keys, i);
// Make sure that all the keys in the line are removed, because some keys are not taken over by the field, only set in the initValue
let newArrayFieldError = updater.getError(field);
const opts = {
notNotify: true,
notUpdate: true
};
if (Array.isArray(newArrayFieldError)) {
newArrayFieldError = newArrayFieldError.slice();
newArrayFieldError.splice(i, 1);
updater.updateStateError(field, newArrayFieldError, opts);
}
// if (Array.isArray(newArrayFieldTouched)) {
// newArrayFieldTouched = newArrayFieldTouched.slice();
// newArrayFieldTouched.splice(i, 1);
// updater.updateStateTouched(field, newArrayFieldTouched, opts);
// }
let newArrayFieldValue = updater.getValue(field);
if (Array.isArray(newArrayFieldValue)) {
newArrayFieldValue = newArrayFieldValue.slice();
newArrayFieldValue.splice(i, 1);
updater.updateStateValue(field, newArrayFieldValue);
}
this.setState({
keys: newKeys
});
}
render() {
const {
children,
field
} = this.props;
const {
keys
} = this.state;
const arrayFields = keys.map((key, i) => ({
// key: i,
key,
field: `${field}[${i}]`,
remove: () => this.remove(i)
}));
const {
add
} = this;
const {
addWithInitValue
} = this;
const contextVal = {
shouldUseInitValue: this.shouldUseInitValue
};
return /*#__PURE__*/_react.default.createElement(_context.ArrayFieldContext.Provider, {
value: contextVal
}, children({
arrayFields,
add,
addWithInitValue
}));
}
}
ArrayFieldComponent.contextType = _context.FormUpdaterContext;
var _default = exports.default = ArrayFieldComponent;