UNPKG

informed

Version:

A lightweight framework and utility for building powerful forms in React applications

200 lines (190 loc) 7.81 kB
import { objectWithoutProperties as _objectWithoutProperties, extends as _extends, objectSpread2 as _objectSpread2, slicedToArray as _slicedToArray } from '../_virtual/_rollupPluginBabelHelpers.js'; import React, { useContext, useState, useMemo, useCallback } from 'react'; import { Relevant } from './Relevant.js'; import { useArrayField } from '../hooks/useArrayField.js'; import { ArrayFieldStateContext, FormControllerContext, ArrayFieldItemApiContext, ArrayFieldItemStateContext, ScopeContext } from '../Context.js'; import { useFormController } from '../hooks/useFormController.js'; import { useFieldState } from '../hooks/useFieldState.js'; import { Debug } from '../debug.js'; import { useScopedApi } from '../hooks/useScopedApi.js'; var _excluded = ["relevant", "relevanceWhen", "relevanceDeps", "name"], _excluded2 = ["children"]; var debug = Debug('informed:ArrayField' + '\t'); var ArrayField = function ArrayField(_ref) { var relevant = _ref.relevant, relevanceWhen = _ref.relevanceWhen, relevanceDeps = _ref.relevanceDeps, name = _ref.name, props = _objectWithoutProperties(_ref, _excluded); if (relevant) { return /*#__PURE__*/React.createElement(Relevant, { when: relevant, relevanceWhen: relevanceWhen, relevanceDeps: relevanceDeps }, /*#__PURE__*/React.createElement(ArrayFieldWrapper, _extends({ name: name }, props))); } else { return /*#__PURE__*/React.createElement(ArrayFieldWrapper, _extends({ name: name }, props)); } }; var ArrayFieldWrapper = function ArrayFieldWrapper(_ref2) { var children = _ref2.children, props = _objectWithoutProperties(_ref2, _excluded2); var _useArrayField = useArrayField(props), render = _useArrayField.render, arrayFieldState = _useArrayField.arrayFieldState, arrayFieldApi = _useArrayField.arrayFieldApi; if (typeof children === 'function') { return render(children(_objectSpread2(_objectSpread2({ arrayFieldApi: arrayFieldApi, arrayFieldState: arrayFieldState }, arrayFieldApi), arrayFieldState))); } return render(children); }; var ArrayFieldItem = function ArrayFieldItem(_ref3) { var arrayFieldItemState = _ref3.arrayFieldItemState, arrayFieldItemApi = _ref3.arrayFieldItemApi, children = _ref3.children; var formController = useFormController(); // Map will store all fields by name // Key => name // Val => fieldMetaRef // Why? so the array knows about all its field meta var _useState = useState(function () { return new Map(); }), _useState2 = _slicedToArray(_useState, 1), fieldsMap = _useState2[0]; // Register for child field updates var subState = useFieldState(arrayFieldItemState.name); // Get scoped api for item api var itemApi = useScopedApi(arrayFieldItemState.name); // Example evaluateWhen = ["name", "age"] // TODO maybe add this // useFieldSubscription( // 'field-modified', // [arrayFieldItemState.name], // target => { // debug(`updating hidden field ${hidden} for ${name} because of ${target}`); // formController.setModifiedValue( // `${arrayFieldItemState.name}.${hidden}`, // formController.getValue(`${arrayFieldItemState.name}.${hidden}`) // ); // console.log( // `updating hidden field ${hidden} for ${name} because of ${target}` // ); // }, // false // ); // Need to memoize to prevent re renders var wrappedController = useMemo(function () { return _objectSpread2(_objectSpread2({}, formController), {}, { register: function register(n, m) { fieldsMap.set(n, m); formController.register(n, m); }, deregister: function deregister(n, m) { fieldsMap["delete"](n); formController.deregister(n, m); // When the very last field from the array is removed unlock var lockedUntil = formController.getRemovalLocked(); debug( // fieldsMap, 'DEREGISTER', n, 'SIZE', fieldsMap.size, 'INDEX', arrayFieldItemState.index, 'LOCKEDUNTIL', lockedUntil); if (lockedUntil != null && lockedUntil.index === arrayFieldItemState.index && lockedUntil.name === arrayFieldItemState.parent && // We are the last field in this item // 1. Example fieldsMap.keys() ==> [ 'friends[0].name' ] // 2. We are de registering friends[1].age // 3. We look to see if friends[1] is in the field map // 4. If its not, we are done and can unlock!! !Array.from(fieldsMap.keys()).some(function (k) { // debug( // 'CHECKING', // k, // `${arrayFieldItemState.parent}[${lockedUntil.index}]` // ); return k.includes("".concat(arrayFieldItemState.parent, "[").concat(lockedUntil.index, "]")); })) { debug('UNLOCKING'); formController.unlockRemoval(); } } }); }, // WHATEVER YOU DO... DONT REMOVE THIS... need updated controller when index changes [arrayFieldItemState.index]); var reset = useCallback(function () { fieldsMap.forEach(function (fieldMeta) { fieldMeta.current.fieldApi.reset(); }); }, [arrayFieldItemState.name, arrayFieldItemState.index]); var arrayFieldStateValue = _objectSpread2(_objectSpread2({}, arrayFieldItemState), {}, { values: subState.value, errors: subState.error, touched: subState.touched }); var arrayFieldItemApiValue = useMemo(function () { return _objectSpread2(_objectSpread2(_objectSpread2({}, arrayFieldItemApi), itemApi), {}, { reset: reset }); }, [arrayFieldItemState.name, arrayFieldItemState.index]); // const memoizedChildren = useMemo( // () => { // debug('Rendering'); // return children({ // ...arrayFieldItemApiValue, // name: arrayFieldItemState.name, // index: arrayFieldItemState.index // }); // }, // [arrayFieldItemState.name, arrayFieldItemState.index] // ); if (typeof children === 'function') { return /*#__PURE__*/React.createElement(FormControllerContext.Provider, { value: wrappedController }, /*#__PURE__*/React.createElement(ArrayFieldItemApiContext.Provider, { value: arrayFieldItemApiValue }, /*#__PURE__*/React.createElement(ArrayFieldItemStateContext.Provider, { value: arrayFieldStateValue }, /*#__PURE__*/React.createElement(ScopeContext.Provider, { value: arrayFieldItemState.name }, children(_objectSpread2(_objectSpread2({}, arrayFieldItemApiValue), {}, { name: arrayFieldItemState.name, index: arrayFieldItemState.index })))))); } return /*#__PURE__*/React.createElement(FormControllerContext.Provider, { value: wrappedController }, /*#__PURE__*/React.createElement(ArrayFieldItemApiContext.Provider, { value: arrayFieldItemApi }, /*#__PURE__*/React.createElement(ArrayFieldItemStateContext.Provider, { value: arrayFieldItemState }, /*#__PURE__*/React.createElement(ScopeContext.Provider, { value: arrayFieldItemState.name }, children)))); }; ArrayField.Items = function (_ref4) { var children = _ref4.children; // TODO maybe add this { hidden, fields } = var _useContext = useContext(ArrayFieldStateContext), fields = _useContext.fields; // console.log("FIELDS", fields); return fields.map(function (_ref5) { var arrayFieldItemState = _ref5.arrayFieldItemState, arrayFieldItemApi = _ref5.arrayFieldItemApi; var key = arrayFieldItemState.key; return /*#__PURE__*/React.createElement(ArrayFieldItem, { key: key // hidden={hidden} , arrayFieldItemApi: arrayFieldItemApi, arrayFieldItemState: arrayFieldItemState }, children); }); }; ArrayField.Items.displayName = 'ArrayField.Items'; export { ArrayField };