"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _createSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/createSuper")); var _toArray = _interopRequireDefault(require("rc-util/lib/Children/toArray")); var _warning = _interopRequireDefault(require("rc-util/lib/warning")); var React = _interopRequireWildcard(require("react")); var _FieldContext = _interopRequireWildcard(require("./FieldContext")); var _typeUtil = require("./utils/typeUtil"); var _validateUtil = require("./utils/validateUtil"); var _valueUtil = require("./utils/valueUtil"); var _excluded = ["name"]; var EMPTY_ERRORS = []; function requireUpdate(shouldUpdate, prev, next, prevValue, nextValue, info) { if (typeof shouldUpdate === 'function') { return shouldUpdate(prev, next, 'source' in info ? { source: info.source } : {}); } return prevValue !== nextValue; } // We use Class instead of Hooks here since it will cost much code by using Hooks. var Field = /*#__PURE__*/function (_React$Component) { (0, _inherits2.default)(Field, _React$Component); var _super = (0, _createSuper2.default)(Field); /** * Follow state should not management in State since it will async update by React. * This makes first render of form can not get correct state value. */ /** * Mark when touched & validated. Currently only used for `dependencies`. * Note that we do not think field with `initialValue` is dirty * but this will be by `isFieldDirty` func. */ // ============================== Subscriptions ============================== function Field(props) { var _this; (0, _classCallCheck2.default)(this, Field); _this = _super.call(this, props); // Register on init _this.state = { resetCount: 0 }; _this.cancelRegisterFunc = null; _this.mounted = false; _this.touched = false; _this.dirty = false; _this.validatePromise = null; _this.prevValidating = void 0; _this.errors = EMPTY_ERRORS; _this.warnings = EMPTY_ERRORS; _this.cancelRegister = function () { var _this$props = _this.props, preserve = _this$props.preserve, isListField = _this$props.isListField, name = _this$props.name; if (_this.cancelRegisterFunc) { _this.cancelRegisterFunc(isListField, preserve, (0, _valueUtil.getNamePath)(name)); } _this.cancelRegisterFunc = null; }; _this.getNamePath = function () { var _this$props2 = _this.props, name = _this$props2.name, fieldContext = _this$props2.fieldContext; var _fieldContext$prefixN = fieldContext.prefixName, prefixName = _fieldContext$prefixN === void 0 ? [] : _fieldContext$prefixN; return name !== undefined ? [].concat((0, _toConsumableArray2.default)(prefixName), (0, _toConsumableArray2.default)(name)) : []; }; _this.getRules = function () { var _this$props3 = _this.props, _this$props3$rules = _this$props3.rules, rules = _this$props3$rules === void 0 ? [] : _this$props3$rules, fieldContext = _this$props3.fieldContext; return rules.map(function (rule) { if (typeof rule === 'function') { return rule(fieldContext); } return rule; }); }; _this.refresh = function () { if (!_this.mounted) return; /** * Clean up current node. */ _this.setState(function (_ref) { var resetCount = _ref.resetCount; return { resetCount: resetCount + 1 }; }); }; _this.triggerMetaEvent = function (destroy) { var onMetaChange = _this.props.onMetaChange; onMetaChange === null || onMetaChange === void 0 ? void 0 : onMetaChange((0, _objectSpread2.default)((0, _objectSpread2.default)({}, _this.getMeta()), {}, { destroy: destroy })); }; _this.onStoreChange = function (prevStore, namePathList, info) { var _this$props4 = _this.props, shouldUpdate = _this$props4.shouldUpdate, _this$props4$dependen = _this$props4.dependencies, dependencies = _this$props4$dependen === void 0 ? [] : _this$props4$dependen, onReset = _this$props4.onReset; var store = info.store; var namePath = _this.getNamePath(); var prevValue = _this.getValue(prevStore); var curValue = _this.getValue(store); var namePathMatch = namePathList && (0, _valueUtil.containsNamePath)(namePathList, namePath); // `setFieldsValue` is a quick access to update related status if (info.type === 'valueUpdate' && info.source === 'external' && prevValue !== curValue) { _this.touched = true; _this.dirty = true; _this.validatePromise = null; _this.errors = EMPTY_ERRORS; _this.warnings = EMPTY_ERRORS; _this.triggerMetaEvent(); } switch (info.type) { case 'reset': if (!namePathList || namePathMatch) { // Clean up state _this.touched = false; _this.dirty = false; _this.validatePromise = null; _this.errors = EMPTY_ERRORS; _this.warnings = EMPTY_ERRORS; _this.triggerMetaEvent(); onReset === null || onReset === void 0 ? void 0 : onReset(); _this.refresh(); return; } break; /** * In case field with `preserve = false` nest deps like: * - A = 1 => show B * - B = 1 => show C * - Reset A, need clean B, C */ case 'remove': { if (shouldUpdate) { _this.reRender(); return; } break; } case 'setField': { if (namePathMatch) { var data = info.data; if ('touched' in data) { _this.touched = data.touched; } if ('validating' in data && !('originRCField' in data)) { _this.validatePromise = data.validating ? Promise.resolve([]) : null; } if ('errors' in data) { _this.errors = data.errors || EMPTY_ERRORS; } if ('warnings' in data) { _this.warnings = data.warnings || EMPTY_ERRORS; } _this.dirty = true; _this.triggerMetaEvent(); _this.reRender(); return; } // Handle update by `setField` with `shouldUpdate` if (shouldUpdate && !namePath.length && requireUpdate(shouldUpdate, prevStore, store, prevValue, curValue, info)) { _this.reRender(); return; } break; } case 'dependenciesUpdate': { /** * Trigger when marked `dependencies` updated. Related fields will all update */ var dependencyList = dependencies.map(_valueUtil.getNamePath); // No need for `namePathMath` check and `shouldUpdate` check, since `valueUpdate` will be // emitted earlier and they will work there // If set it may cause unnecessary twice rerendering if (dependencyList.some(function (dependency) { return (0, _valueUtil.containsNamePath)(info.relatedFields, dependency); })) { _this.reRender(); return; } break; } default: // 1. If `namePath` exists in `namePathList`, means it's related value and should update // For example // If `namePathList` is [['list']] (List value update), Field should be updated // If `namePathList` is [['list', 0]] (Field value update), List shouldn't be updated // 2. // 2.1 If `dependencies` is set, `name` is not set and `shouldUpdate` is not set, // don't use `shouldUpdate`. `dependencies` is view as a shortcut if `shouldUpdate` // is not provided // 2.2 If `shouldUpdate` provided, use customize logic to update the field // else to check if value changed if (namePathMatch || (!dependencies.length || namePath.length || shouldUpdate) && requireUpdate(shouldUpdate, prevStore, store, prevValue, curValue, info)) { _this.reRender(); return; } break; } if (shouldUpdate === true) { _this.reRender(); } }; _this.validateRules = function (options) { // We should fixed namePath & value to avoid developer change then by form function var namePath = _this.getNamePath(); var currentValue = _this.getValue(); // Force change to async to avoid rule OOD under renderProps field var rootPromise = Promise.resolve().then(function () { if (!_this.mounted) { return []; } var _this$props5 = _this.props, _this$props5$validate = _this$props5.validateFirst, validateFirst = _this$props5$validate === void 0 ? false : _this$props5$validate, messageVariables = _this$props5.messageVariables; var _ref2 = options || {}, triggerName = _ref2.triggerName; var filteredRules = _this.getRules(); if (triggerName) { filteredRules = filteredRules.filter(function (rule) { var validateTrigger = rule.validateTrigger; if (!validateTrigger) { return true; } var triggerList = (0, _typeUtil.toArray)(validateTrigger); return triggerList.includes(triggerName); }); } var promise = (0, _validateUtil.validateRules)(namePath, currentValue, filteredRules, options, validateFirst, messageVariables); promise.catch(function (e) { return e; }).then(function () { var ruleErrors = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : EMPTY_ERRORS; if (_this.validatePromise === rootPromise) { _this.validatePromise = null; // Get errors & warnings var nextErrors = []; var nextWarnings = []; ruleErrors.forEach(function (_ref3) { var warningOnly = _ref3.rule.warningOnly, _ref3$errors = _ref3.errors, errors = _ref3$errors === void 0 ? EMPTY_ERRORS : _ref3$errors; if (warningOnly) { nextWarnings.push.apply(nextWarnings, (0, _toConsumableArray2.default)(errors)); } else { nextErrors.push.apply(nextErrors, (0, _toConsumableArray2.default)(errors)); } }); _this.errors = nextErrors; _this.warnings = nextWarnings; _this.triggerMetaEvent(); _this.reRender(); } }); return promise; }); _this.validatePromise = rootPromise; _this.dirty = true; _this.errors = EMPTY_ERRORS; _this.warnings = EMPTY_ERRORS; _this.triggerMetaEvent(); // Force trigger re-render since we need sync renderProps with new meta _this.reRender(); return rootPromise; }; _this.isFieldValidating = function () { return !!_this.validatePromise; }; _this.isFieldTouched = function () { return _this.touched; }; _this.isFieldDirty = function () { // Touched or validate or has initialValue if (_this.dirty || _this.props.initialValue !== undefined) { return true; } // Form set initialValue var fieldContext = _this.props.fieldContext; var _fieldContext$getInte = fieldContext.getInternalHooks(_FieldContext.HOOK_MARK), getInitialValue = _fieldContext$getInte.getInitialValue; if (getInitialValue(_this.getNamePath()) !== undefined) { return true; } return false; }; _this.getErrors = function () { return _this.errors; }; _this.getWarnings = function () { return _this.warnings; }; _this.isListField = function () { return _this.props.isListField; }; _this.isList = function () { return _this.props.isList; }; _this.isPreserve = function () { return _this.props.preserve; }; _this.getMeta = function () { // Make error & validating in cache to save perf _this.prevValidating = _this.isFieldValidating(); var meta = { touched: _this.isFieldTouched(), validating: _this.prevValidating, errors: _this.errors, warnings: _this.warnings, name: _this.getNamePath() }; return meta; }; _this.getOnlyChild = function (children) { // Support render props if (typeof children === 'function') { var meta = _this.getMeta(); return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, _this.getOnlyChild(children(_this.getControlled(), meta, _this.props.fieldContext))), {}, { isFunction: true }); } // Filed element only var childList = (0, _toArray.default)(children); if (childList.length !== 1 || ! /*#__PURE__*/React.isValidElement(childList[0])) { return { child: childList, isFunction: false }; } return { child: childList[0], isFunction: false }; }; _this.getValue = function (store) { var getFieldsValue = _this.props.fieldContext.getFieldsValue; var namePath = _this.getNamePath(); return (0, _valueUtil.getValue)(store || getFieldsValue(true), namePath); }; _this.getControlled = function () { var childProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _this$props6 = _this.props, trigger = _this$props6.trigger, validateTrigger = _this$props6.validateTrigger, getValueFromEvent = _this$props6.getValueFromEvent, normalize = _this$props6.normalize, valuePropName = _this$props6.valuePropName, getValueProps = _this$props6.getValueProps, fieldContext = _this$props6.fieldContext; var mergedValidateTrigger = validateTrigger !== undefined ? validateTrigger : fieldContext.validateTrigger; var namePath = _this.getNamePath(); var getInternalHooks = fieldContext.getInternalHooks, getFieldsValue = fieldContext.getFieldsValue; var _getInternalHooks = getInternalHooks(_FieldContext.HOOK_MARK), dispatch = _getInternalHooks.dispatch; var value = _this.getValue(); var mergedGetValueProps = getValueProps || function (val) { return (0, _defineProperty2.default)({}, valuePropName, val); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any var originTriggerFunc = childProps[trigger]; var control = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, childProps), mergedGetValueProps(value)); // Add trigger control[trigger] = function () { // Mark as touched _this.touched = true; _this.dirty = true; _this.triggerMetaEvent(); var newValue; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (getValueFromEvent) { newValue = getValueFromEvent.apply(void 0, args); } else { newValue = _valueUtil.defaultGetValueFromEvent.apply(void 0, [valuePropName].concat(args)); } if (normalize) { newValue = normalize(newValue, value, getFieldsValue(true)); } dispatch({ type: 'updateValue', namePath: namePath, value: newValue }); if (originTriggerFunc) { originTriggerFunc.apply(void 0, args); } }; // Add validateTrigger var validateTriggerList = (0, _typeUtil.toArray)(mergedValidateTrigger || []); validateTriggerList.forEach(function (triggerName) { // Wrap additional function of component, so that we can get latest value from store var originTrigger = control[triggerName]; control[triggerName] = function () { if (originTrigger) { originTrigger.apply(void 0, arguments); } // Always use latest rules var rules = _this.props.rules; if (rules && rules.length) { // We dispatch validate to root, // since it will update related data with other field with same name dispatch({ type: 'validateField', namePath: namePath, triggerName: triggerName }); } }; }); return control; }; if (props.fieldContext) { var getInternalHooks = props.fieldContext.getInternalHooks; var _getInternalHooks2 = getInternalHooks(_FieldContext.HOOK_MARK), initEntityValue = _getInternalHooks2.initEntityValue; initEntityValue((0, _assertThisInitialized2.default)(_this)); } return _this; } (0, _createClass2.default)(Field, [{ key: "componentDidMount", value: function componentDidMount() { var _this$props7 = this.props, shouldUpdate = _this$props7.shouldUpdate, fieldContext = _this$props7.fieldContext; this.mounted = true; // Register on init if (fieldContext) { var getInternalHooks = fieldContext.getInternalHooks; var _getInternalHooks3 = getInternalHooks(_FieldContext.HOOK_MARK), registerField = _getInternalHooks3.registerField; this.cancelRegisterFunc = registerField(this); } // One more render for component in case fields not ready if (shouldUpdate === true) { this.reRender(); } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { this.cancelRegister(); this.triggerMetaEvent(true); this.mounted = false; } }, { key: "reRender", value: function reRender() { if (!this.mounted) return; this.forceUpdate(); } }, { key: "render", value: function render() { var resetCount = this.state.resetCount; var children = this.props.children; var _this$getOnlyChild = this.getOnlyChild(children), child = _this$getOnlyChild.child, isFunction = _this$getOnlyChild.isFunction; // Not need to `cloneElement` since user can handle this in render function self var returnChildNode; if (isFunction) { returnChildNode = child; } else if ( /*#__PURE__*/React.isValidElement(child)) { returnChildNode = /*#__PURE__*/React.cloneElement(child, this.getControlled(child.props)); } else { (0, _warning.default)(!child, '`children` of Field is not validate ReactElement.'); returnChildNode = child; } return /*#__PURE__*/React.createElement(React.Fragment, { key: resetCount }, returnChildNode); } }]); return Field; }(React.Component); Field.contextType = _FieldContext.default; Field.defaultProps = { trigger: 'onChange', valuePropName: 'value' }; function WrapperField(_ref5) { var name = _ref5.name, restProps = (0, _objectWithoutProperties2.default)(_ref5, _excluded); var fieldContext = React.useContext(_FieldContext.default); var namePath = name !== undefined ? (0, _valueUtil.getNamePath)(name) : undefined; var key = 'keep'; if (!restProps.isListField) { key = "_".concat((namePath || []).join('_')); } // Warning if it's a directly list field. // We can still support multiple level field preserve. if (process.env.NODE_ENV !== 'production' && restProps.preserve === false && restProps.isListField && namePath.length <= 1) { (0, _warning.default)(false, '`preserve` should not apply on Form.List fields.'); } return /*#__PURE__*/React.createElement(Field, (0, _extends2.default)({ key: key, name: namePath }, restProps, { fieldContext: fieldContext })); } var _default = WrapperField; exports.default = _default;