"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.FormStore = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var React = _interopRequireWildcard(require("react")); var _warning = _interopRequireDefault(require("rc-util/lib/warning")); var _FieldContext = require("./FieldContext"); var _asyncUtil = require("./utils/asyncUtil"); var _NameMap = _interopRequireDefault(require("./utils/NameMap")); var _messages = require("./utils/messages"); var _valueUtil = require("./utils/valueUtil"); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } var FormStore = function FormStore(forceRootUpdate) { var _this = this; (0, _classCallCheck2.default)(this, FormStore); this.formHooked = false; this.subscribable = true; this.store = {}; this.fieldEntities = []; this.initialValues = {}; this.callbacks = {}; this.validateMessages = null; this.preserve = null; this.lastValidatePromise = null; this.getForm = function () { return { getFieldValue: _this.getFieldValue, getFieldsValue: _this.getFieldsValue, getFieldError: _this.getFieldError, getFieldsError: _this.getFieldsError, isFieldsTouched: _this.isFieldsTouched, isFieldTouched: _this.isFieldTouched, isFieldValidating: _this.isFieldValidating, isFieldsValidating: _this.isFieldsValidating, resetFields: _this.resetFields, setFields: _this.setFields, setFieldsValue: _this.setFieldsValue, validateFields: _this.validateFields, submit: _this.submit, getInternalHooks: _this.getInternalHooks }; }; // ======================== Internal Hooks ======================== this.getInternalHooks = function (key) { if (key === _FieldContext.HOOK_MARK) { _this.formHooked = true; return { dispatch: _this.dispatch, registerField: _this.registerField, useSubscribe: _this.useSubscribe, setInitialValues: _this.setInitialValues, setCallbacks: _this.setCallbacks, setValidateMessages: _this.setValidateMessages, getFields: _this.getFields, setPreserve: _this.setPreserve }; } (0, _warning.default)(false, '`getInternalHooks` is internal usage. Should not call directly.'); return null; }; this.useSubscribe = function (subscribable) { _this.subscribable = subscribable; }; /** * First time `setInitialValues` should update store with initial value */ this.setInitialValues = function (initialValues, init) { _this.initialValues = initialValues || {}; if (init) { _this.store = (0, _valueUtil.setValues)({}, initialValues, _this.store); } }; this.getInitialValue = function (namePath) { return (0, _valueUtil.getValue)(_this.initialValues, namePath); }; this.setCallbacks = function (callbacks) { _this.callbacks = callbacks; }; this.setValidateMessages = function (validateMessages) { _this.validateMessages = validateMessages; }; this.setPreserve = function (preserve) { _this.preserve = preserve; }; // ========================== Dev Warning ========================= this.timeoutId = null; this.warningUnhooked = function () { if (process.env.NODE_ENV !== 'production' && !_this.timeoutId && typeof window !== 'undefined') { _this.timeoutId = window.setTimeout(function () { _this.timeoutId = null; if (!_this.formHooked) { (0, _warning.default)(false, 'Instance created by `useForm` is not connected to any Form element. Forget to pass `form` prop?'); } }); } }; // ============================ Fields ============================ /** * Get registered field entities. * @param pure Only return field which has a `name`. Default: false */ this.getFieldEntities = function () { var pure = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; if (!pure) { return _this.fieldEntities; } return _this.fieldEntities.filter(function (field) { return field.getNamePath().length; }); }; this.getFieldsMap = function () { var pure = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var cache = new _NameMap.default(); _this.getFieldEntities(pure).forEach(function (field) { var namePath = field.getNamePath(); cache.set(namePath, field); }); return cache; }; this.getFieldEntitiesForNamePathList = function (nameList) { if (!nameList) { return _this.getFieldEntities(true); } var cache = _this.getFieldsMap(true); return nameList.map(function (name) { var namePath = (0, _valueUtil.getNamePath)(name); return cache.get(namePath) || { INVALIDATE_NAME_PATH: (0, _valueUtil.getNamePath)(name) }; }); }; this.getFieldsValue = function (nameList, filterFunc) { _this.warningUnhooked(); if (nameList === true && !filterFunc) { return _this.store; } var fieldEntities = _this.getFieldEntitiesForNamePathList(Array.isArray(nameList) ? nameList : null); var filteredNameList = []; fieldEntities.forEach(function (entity) { var namePath = 'INVALIDATE_NAME_PATH' in entity ? entity.INVALIDATE_NAME_PATH : entity.getNamePath(); if (!filterFunc) { filteredNameList.push(namePath); } else { var meta = 'getMeta' in entity ? entity.getMeta() : null; if (filterFunc(meta)) { filteredNameList.push(namePath); } } }); return (0, _valueUtil.cloneByNamePathList)(_this.store, filteredNameList.map(_valueUtil.getNamePath)); }; this.getFieldValue = function (name) { _this.warningUnhooked(); var namePath = (0, _valueUtil.getNamePath)(name); return (0, _valueUtil.getValue)(_this.store, namePath); }; this.getFieldsError = function (nameList) { _this.warningUnhooked(); var fieldEntities = _this.getFieldEntitiesForNamePathList(nameList); return fieldEntities.map(function (entity, index) { if (entity && !('INVALIDATE_NAME_PATH' in entity)) { return { name: entity.getNamePath(), errors: entity.getErrors() }; } return { name: (0, _valueUtil.getNamePath)(nameList[index]), errors: [] }; }); }; this.getFieldError = function (name) { _this.warningUnhooked(); var namePath = (0, _valueUtil.getNamePath)(name); var fieldError = _this.getFieldsError([namePath])[0]; return fieldError.errors; }; this.isFieldsTouched = function () { _this.warningUnhooked(); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var arg0 = args[0], arg1 = args[1]; var namePathList; var isAllFieldsTouched = false; if (args.length === 0) { namePathList = null; } else if (args.length === 1) { if (Array.isArray(arg0)) { namePathList = arg0.map(_valueUtil.getNamePath); isAllFieldsTouched = false; } else { namePathList = null; isAllFieldsTouched = arg0; } } else { namePathList = arg0.map(_valueUtil.getNamePath); isAllFieldsTouched = arg1; } var testTouched = function testTouched(field) { // Not provide `nameList` will check all the fields if (!namePathList) { return field.isFieldTouched(); } var fieldNamePath = field.getNamePath(); if ((0, _valueUtil.containsNamePath)(namePathList, fieldNamePath)) { return field.isFieldTouched(); } return isAllFieldsTouched; }; return isAllFieldsTouched ? _this.getFieldEntities(true).every(testTouched) : _this.getFieldEntities(true).some(testTouched); }; this.isFieldTouched = function (name) { _this.warningUnhooked(); return _this.isFieldsTouched([name]); }; this.isFieldsValidating = function (nameList) { _this.warningUnhooked(); var fieldEntities = _this.getFieldEntities(); if (!nameList) { return fieldEntities.some(function (testField) { return testField.isFieldValidating(); }); } var namePathList = nameList.map(_valueUtil.getNamePath); return fieldEntities.some(function (testField) { var fieldNamePath = testField.getNamePath(); return (0, _valueUtil.containsNamePath)(namePathList, fieldNamePath) && testField.isFieldValidating(); }); }; this.isFieldValidating = function (name) { _this.warningUnhooked(); return _this.isFieldsValidating([name]); }; /** * Reset Field with field `initialValue` prop. * Can pass `entities` or `namePathList` or just nothing. */ this.resetWithFieldInitialValue = function () { var info = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // Create cache var cache = new _NameMap.default(); var fieldEntities = _this.getFieldEntities(true); fieldEntities.forEach(function (field) { var initialValue = field.props.initialValue; var namePath = field.getNamePath(); // Record only if has `initialValue` if (initialValue !== undefined) { var records = cache.get(namePath) || new Set(); records.add({ entity: field, value: initialValue }); cache.set(namePath, records); } }); // Reset var resetWithFields = function resetWithFields(entities) { entities.forEach(function (field) { var initialValue = field.props.initialValue; if (initialValue !== undefined) { var namePath = field.getNamePath(); var formInitialValue = _this.getInitialValue(namePath); if (formInitialValue !== undefined) { // Warning if conflict with form initialValues and do not modify value (0, _warning.default)(false, "Form already set 'initialValues' with path '".concat(namePath.join('.'), "'. Field can not overwrite it.")); } else { var records = cache.get(namePath); if (records && records.size > 1) { // Warning if multiple field set `initialValue`and do not modify value (0, _warning.default)(false, "Multiple Field with path '".concat(namePath.join('.'), "' set 'initialValue'. Can not decide which one to pick.")); } else if (records) { var originValue = _this.getFieldValue(namePath); // Set `initialValue` if (!info.skipExist || originValue === undefined) { _this.store = (0, _valueUtil.setValue)(_this.store, namePath, (0, _toConsumableArray2.default)(records)[0].value); } } } } }); }; var requiredFieldEntities; if (info.entities) { requiredFieldEntities = info.entities; } else if (info.namePathList) { requiredFieldEntities = []; info.namePathList.forEach(function (namePath) { var records = cache.get(namePath); if (records) { var _requiredFieldEntitie; (_requiredFieldEntitie = requiredFieldEntities).push.apply(_requiredFieldEntitie, (0, _toConsumableArray2.default)((0, _toConsumableArray2.default)(records).map(function (r) { return r.entity; }))); } }); } else { requiredFieldEntities = fieldEntities; } resetWithFields(requiredFieldEntities); }; this.resetFields = function (nameList) { _this.warningUnhooked(); var prevStore = _this.store; if (!nameList) { _this.store = (0, _valueUtil.setValues)({}, _this.initialValues); _this.resetWithFieldInitialValue(); _this.notifyObservers(prevStore, null, { type: 'reset' }); return; } // Reset by `nameList` var namePathList = nameList.map(_valueUtil.getNamePath); namePathList.forEach(function (namePath) { var initialValue = _this.getInitialValue(namePath); _this.store = (0, _valueUtil.setValue)(_this.store, namePath, initialValue); }); _this.resetWithFieldInitialValue({ namePathList: namePathList }); _this.notifyObservers(prevStore, namePathList, { type: 'reset' }); }; this.setFields = function (fields) { _this.warningUnhooked(); var prevStore = _this.store; fields.forEach(function (fieldData) { var name = fieldData.name, errors = fieldData.errors, data = (0, _objectWithoutProperties2.default)(fieldData, ["name", "errors"]); var namePath = (0, _valueUtil.getNamePath)(name); // Value if ('value' in data) { _this.store = (0, _valueUtil.setValue)(_this.store, namePath, data.value); } _this.notifyObservers(prevStore, [namePath], { type: 'setField', data: fieldData }); }); }; this.getFields = function () { var entities = _this.getFieldEntities(true); var fields = entities.map(function (field) { var namePath = field.getNamePath(); var meta = field.getMeta(); var fieldData = _objectSpread({}, meta, { name: namePath, value: _this.getFieldValue(namePath) }); Object.defineProperty(fieldData, 'originRCField', { value: true }); return fieldData; }); return fields; }; // =========================== Observer =========================== this.registerField = function (entity) { _this.fieldEntities.push(entity); // Set initial values if (entity.props.initialValue !== undefined) { var prevStore = _this.store; _this.resetWithFieldInitialValue({ entities: [entity], skipExist: true }); _this.notifyObservers(prevStore, [entity.getNamePath()], { type: 'valueUpdate', source: 'internal' }); } // un-register field callback return function (preserve) { _this.fieldEntities = _this.fieldEntities.filter(function (item) { return item !== entity; }); // Clean up store value if preserve var mergedPreserve = preserve !== undefined ? preserve : _this.preserve; if (mergedPreserve === false) { var namePath = entity.getNamePath(); if (_this.getFieldValue(namePath) !== undefined) { _this.store = (0, _valueUtil.setValue)(_this.store, namePath, undefined); } } }; }; this.dispatch = function (action) { switch (action.type) { case 'updateValue': { var namePath = action.namePath, value = action.value; _this.updateValue(namePath, value); break; } case 'validateField': { var _namePath = action.namePath, triggerName = action.triggerName; _this.validateFields([_namePath], { triggerName: triggerName }); break; } default: // Currently we don't have other action. Do nothing. } }; this.notifyObservers = function (prevStore, namePathList, info) { if (_this.subscribable) { var mergedInfo = _objectSpread({}, info, { store: _this.getFieldsValue(true) }); _this.getFieldEntities().forEach(function (_ref) { var onStoreChange = _ref.onStoreChange; onStoreChange(prevStore, namePathList, mergedInfo); }); } else { _this.forceRootUpdate(); } }; this.updateValue = function (name, value) { var namePath = (0, _valueUtil.getNamePath)(name); var prevStore = _this.store; _this.store = (0, _valueUtil.setValue)(_this.store, namePath, value); _this.notifyObservers(prevStore, [namePath], { type: 'valueUpdate', source: 'internal' }); // Notify dependencies children with parent update var childrenFields = _this.getDependencyChildrenFields(namePath); _this.validateFields(childrenFields); _this.notifyObservers(prevStore, childrenFields, { type: 'dependenciesUpdate', relatedFields: [namePath].concat((0, _toConsumableArray2.default)(childrenFields)) }); // trigger callback function var onValuesChange = _this.callbacks.onValuesChange; if (onValuesChange) { var changedValues = (0, _valueUtil.cloneByNamePathList)(_this.store, [namePath]); onValuesChange(changedValues, _this.store); } _this.triggerOnFieldsChange([namePath].concat((0, _toConsumableArray2.default)(childrenFields))); }; // Let all child Field get update. this.setFieldsValue = function (store) { _this.warningUnhooked(); var prevStore = _this.store; if (store) { _this.store = (0, _valueUtil.setValues)(_this.store, store); } _this.notifyObservers(prevStore, null, { type: 'valueUpdate', source: 'external' }); }; this.getDependencyChildrenFields = function (rootNamePath) { var children = new Set(); var childrenFields = []; var dependencies2fields = new _NameMap.default(); /** * Generate maps * Can use cache to save perf if user report performance issue with this */ _this.getFieldEntities().forEach(function (field) { var dependencies = field.props.dependencies; (dependencies || []).forEach(function (dependency) { var dependencyNamePath = (0, _valueUtil.getNamePath)(dependency); dependencies2fields.update(dependencyNamePath, function () { var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Set(); fields.add(field); return fields; }); }); }); var fillChildren = function fillChildren(namePath) { var fields = dependencies2fields.get(namePath) || new Set(); fields.forEach(function (field) { if (!children.has(field)) { children.add(field); var fieldNamePath = field.getNamePath(); if (field.isFieldDirty() && fieldNamePath.length) { childrenFields.push(fieldNamePath); fillChildren(fieldNamePath); } } }); }; fillChildren(rootNamePath); return childrenFields; }; this.triggerOnFieldsChange = function (namePathList, filedErrors) { var onFieldsChange = _this.callbacks.onFieldsChange; if (onFieldsChange) { var fields = _this.getFields(); /** * Fill errors since `fields` may be replaced by controlled fields */ if (filedErrors) { var cache = new _NameMap.default(); filedErrors.forEach(function (_ref2) { var name = _ref2.name, errors = _ref2.errors; cache.set(name, errors); }); fields.forEach(function (field) { // eslint-disable-next-line no-param-reassign field.errors = cache.get(field.name) || field.errors; }); } var changedFields = fields.filter(function (_ref3) { var fieldName = _ref3.name; return (0, _valueUtil.containsNamePath)(namePathList, fieldName); }); onFieldsChange(changedFields, fields); } }; // =========================== Validate =========================== this.validateFields = function (nameList, options) { _this.warningUnhooked(); var provideNameList = !!nameList; var namePathList = provideNameList ? nameList.map(_valueUtil.getNamePath) : []; // Collect result in promise list var promiseList = []; _this.getFieldEntities(true).forEach(function (field) { // Add field if not provide `nameList` if (!provideNameList) { namePathList.push(field.getNamePath()); } // Skip if without rule if (!field.props.rules || !field.props.rules.length) { return; } var fieldNamePath = field.getNamePath(); // Add field validate rule in to promise list if (!provideNameList || (0, _valueUtil.containsNamePath)(namePathList, fieldNamePath)) { var promise = field.validateRules(_objectSpread({ validateMessages: _objectSpread({}, _messages.defaultValidateMessages, {}, _this.validateMessages) }, options)); // Wrap promise with field promiseList.push(promise.then(function () { return { name: fieldNamePath, errors: [] }; }).catch(function (errors) { return Promise.reject({ name: fieldNamePath, errors: errors }); })); } }); var summaryPromise = (0, _asyncUtil.allPromiseFinish)(promiseList); _this.lastValidatePromise = summaryPromise; // Notify fields with rule that validate has finished and need update summaryPromise.catch(function (results) { return results; }).then(function (results) { var resultNamePathList = results.map(function (_ref4) { var name = _ref4.name; return name; }); _this.notifyObservers(_this.store, resultNamePathList, { type: 'validateFinish' }); _this.triggerOnFieldsChange(resultNamePathList, results); }); var returnPromise = summaryPromise.then(function () { if (_this.lastValidatePromise === summaryPromise) { return Promise.resolve(_this.getFieldsValue(namePathList)); } return Promise.reject([]); }).catch(function (results) { var errorList = results.filter(function (result) { return result && result.errors.length; }); return Promise.reject({ values: _this.getFieldsValue(namePathList), errorFields: errorList, outOfDate: _this.lastValidatePromise !== summaryPromise }); }); // Do not throw in console returnPromise.catch(function (e) { return e; }); return returnPromise; }; // ============================ Submit ============================ this.submit = function () { _this.warningUnhooked(); _this.validateFields().then(function (values) { var onFinish = _this.callbacks.onFinish; if (onFinish) { try { onFinish(values); } catch (err) { // Should print error if user `onFinish` callback failed console.error(err); } } }).catch(function (e) { var onFinishFailed = _this.callbacks.onFinishFailed; if (onFinishFailed) { onFinishFailed(e); } }); }; this.forceRootUpdate = forceRootUpdate; }; exports.FormStore = FormStore; function useForm(form) { var formRef = React.useRef(); var _React$useState = React.useState(), _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2), forceUpdate = _React$useState2[1]; if (!formRef.current) { if (form) { formRef.current = form; } else { // Create a new FormStore if not provided var forceReRender = function forceReRender() { forceUpdate({}); }; var formStore = new FormStore(forceReRender); formRef.current = formStore.getForm(); } } return [formRef.current]; } var _default = useForm; exports.default = _default;