import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; import _createClass from "@babel/runtime/helpers/esm/createClass"; import _assertThisInitialized from "@babel/runtime/helpers/esm/assertThisInitialized"; import _inherits from "@babel/runtime/helpers/esm/inherits"; import _createSuper from "@babel/runtime/helpers/esm/createSuper"; var _excluded = ["name"]; import toChildrenArray from "rc-util/es/Children/toArray"; import warning from "rc-util/es/warning"; import * as React from 'react'; import FieldContext, { HOOK_MARK } from './FieldContext'; import { toArray } from './utils/typeUtil'; import { validateRules } from './utils/validateUtil'; import { containsNamePath, defaultGetValueFromEvent, getNamePath, getValue } from './utils/valueUtil'; 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) { _inherits(Field, _React$Component); var _super = _createSuper(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; _classCallCheck(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, 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(_toConsumableArray(prefixName), _toConsumableArray(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(_objectSpread(_objectSpread({}, _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 && 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(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 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 = toArray(validateTrigger); return triggerList.includes(triggerName); }); } var promise = 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, _toConsumableArray(errors)); } else { nextErrors.push.apply(nextErrors, _toConsumableArray(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(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 _objectSpread(_objectSpread({}, _this.getOnlyChild(children(_this.getControlled(), meta, _this.props.fieldContext))), {}, { isFunction: true }); } // Filed element only var childList = toChildrenArray(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 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(HOOK_MARK), dispatch = _getInternalHooks.dispatch; var value = _this.getValue(); var mergedGetValueProps = getValueProps || function (val) { return _defineProperty({}, valuePropName, val); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any var originTriggerFunc = childProps[trigger]; var control = _objectSpread(_objectSpread({}, 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 = 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 = 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(HOOK_MARK), initEntityValue = _getInternalHooks2.initEntityValue; initEntityValue(_assertThisInitialized(_this)); } return _this; } _createClass(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(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 { warning(!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; Field.defaultProps = { trigger: 'onChange', valuePropName: 'value' }; function WrapperField(_ref5) { var name = _ref5.name, restProps = _objectWithoutProperties(_ref5, _excluded); var fieldContext = React.useContext(FieldContext); var namePath = name !== undefined ? 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) { warning(false, '`preserve` should not apply on Form.List fields.'); } return /*#__PURE__*/React.createElement(Field, _extends({ key: key, name: namePath }, restProps, { fieldContext: fieldContext })); } export default WrapperField;