import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime"; import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; 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"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; var _excluded = ["name"]; import toChildrenArray from "rc-util/es/Children/toArray"; import isEqual from "rc-util/es/isEqual"; import warning from "rc-util/es/warning"; import * as React from 'react'; import FieldContext, { HOOK_MARK } from "./FieldContext"; import ListContext from "./ListContext"; 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; } // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style // 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); // ============================== Subscriptions ============================== function Field(props) { var _this; _classCallCheck(this, Field); _this = _super.call(this, props); // Register on init _defineProperty(_assertThisInitialized(_this), "state", { resetCount: 0 }); _defineProperty(_assertThisInitialized(_this), "cancelRegisterFunc", null); _defineProperty(_assertThisInitialized(_this), "mounted", false); /** * 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. */ _defineProperty(_assertThisInitialized(_this), "touched", false); /** * 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. */ _defineProperty(_assertThisInitialized(_this), "dirty", false); _defineProperty(_assertThisInitialized(_this), "validatePromise", void 0); _defineProperty(_assertThisInitialized(_this), "prevValidating", void 0); _defineProperty(_assertThisInitialized(_this), "errors", EMPTY_ERRORS); _defineProperty(_assertThisInitialized(_this), "warnings", EMPTY_ERRORS); _defineProperty(_assertThisInitialized(_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; }); // ================================== Utils ================================== _defineProperty(_assertThisInitialized(_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)) : []; }); _defineProperty(_assertThisInitialized(_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; }); }); _defineProperty(_assertThisInitialized(_this), "refresh", function () { if (!_this.mounted) return; /** * Clean up current node. */ _this.setState(function (_ref) { var resetCount = _ref.resetCount; return { resetCount: resetCount + 1 }; }); }); // Event should only trigger when meta changed _defineProperty(_assertThisInitialized(_this), "metaCache", null); _defineProperty(_assertThisInitialized(_this), "triggerMetaEvent", function (destroy) { var onMetaChange = _this.props.onMetaChange; if (onMetaChange) { var _meta = _objectSpread(_objectSpread({}, _this.getMeta()), {}, { destroy: destroy }); if (!isEqual(_this.metaCache, _meta)) { onMetaChange(_meta); } _this.metaCache = _meta; } else { _this.metaCache = null; } }); // ========================= Field Entity Interfaces ========================= // Trigger by store update. Check if need update the component _defineProperty(_assertThisInitialized(_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 = undefined; _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': { var data = info.data; if (namePathMatch) { 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; } else if ('value' in data && containsNamePath(namePathList, namePath, true)) { // Contains path with value should also check _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(); } }); _defineProperty(_assertThisInitialized(_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(); var _ref2 = options || {}, triggerName = _ref2.triggerName, _ref2$validateOnly = _ref2.validateOnly, validateOnly = _ref2$validateOnly === void 0 ? false : _ref2$validateOnly; // Force change to async to avoid rule OOD under renderProps field var rootPromise = Promise.resolve().then( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() { var _this$props5, _this$props5$validate, validateFirst, messageVariables, validateDebounce, filteredRules, promise; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: if (_this.mounted) { _context.next = 2; break; } return _context.abrupt("return", []); case 2: _this$props5 = _this.props, _this$props5$validate = _this$props5.validateFirst, validateFirst = _this$props5$validate === void 0 ? false : _this$props5$validate, messageVariables = _this$props5.messageVariables, validateDebounce = _this$props5.validateDebounce; // Start validate filteredRules = _this.getRules(); if (triggerName) { filteredRules = filteredRules.filter(function (rule) { return rule; }).filter(function (rule) { var validateTrigger = rule.validateTrigger; if (!validateTrigger) { return true; } var triggerList = toArray(validateTrigger); return triggerList.includes(triggerName); }); } // Wait for debounce. Skip if no `triggerName` since its from `validateFields / submit` if (!(validateDebounce && triggerName)) { _context.next = 10; break; } _context.next = 8; return new Promise(function (resolve) { setTimeout(resolve, validateDebounce); }); case 8: if (!(_this.validatePromise !== rootPromise)) { _context.next = 10; break; } return _context.abrupt("return", []); case 10: 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) { var _ruleErrors$forEach; _this.validatePromise = null; // Get errors & warnings var nextErrors = []; var nextWarnings = []; (_ruleErrors$forEach = ruleErrors.forEach) === null || _ruleErrors$forEach === void 0 ? void 0 : _ruleErrors$forEach.call(ruleErrors, function (_ref4) { var warningOnly = _ref4.rule.warningOnly, _ref4$errors = _ref4.errors, errors = _ref4$errors === void 0 ? EMPTY_ERRORS : _ref4$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 _context.abrupt("return", promise); case 13: case "end": return _context.stop(); } }, _callee); }))); if (validateOnly) { return rootPromise; } _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; }); _defineProperty(_assertThisInitialized(_this), "isFieldValidating", function () { return !!_this.validatePromise; }); _defineProperty(_assertThisInitialized(_this), "isFieldTouched", function () { return _this.touched; }); _defineProperty(_assertThisInitialized(_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; }); _defineProperty(_assertThisInitialized(_this), "getErrors", function () { return _this.errors; }); _defineProperty(_assertThisInitialized(_this), "getWarnings", function () { return _this.warnings; }); _defineProperty(_assertThisInitialized(_this), "isListField", function () { return _this.props.isListField; }); _defineProperty(_assertThisInitialized(_this), "isList", function () { return _this.props.isList; }); _defineProperty(_assertThisInitialized(_this), "isPreserve", function () { return _this.props.preserve; }); // ============================= Child Component ============================= _defineProperty(_assertThisInitialized(_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(), validated: _this.validatePromise === null }; return meta; }); // Only return validate child node. If invalidate, will do nothing about field. _defineProperty(_assertThisInitialized(_this), "getOnlyChild", function (children) { // Support render props if (typeof children === 'function') { var _meta2 = _this.getMeta(); return _objectSpread(_objectSpread({}, _this.getOnlyChild(children(_this.getControlled(), _meta2, _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 }; }); // ============================== Field Control ============================== _defineProperty(_assertThisInitialized(_this), "getValue", function (store) { var getFieldsValue = _this.props.fieldContext.getFieldsValue; var namePath = _this.getNamePath(); return getValue(store || getFieldsValue(true), namePath); }); _defineProperty(_assertThisInitialized(_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); _defineProperty(Field, "contextType", FieldContext); _defineProperty(Field, "defaultProps", { trigger: 'onChange', valuePropName: 'value' }); function WrapperField(_ref6) { var name = _ref6.name, restProps = _objectWithoutProperties(_ref6, _excluded); var fieldContext = React.useContext(FieldContext); var listContext = React.useContext(ListContext); 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, isListField: !!listContext }, restProps, { fieldContext: fieldContext })); } export default WrapperField;