UNPKG

react-formal

Version:

Classy HTML form management for React

144 lines (138 loc) 4.26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = require("react"); var _useField = require("./useField"); var _Errors = require("./Errors"); var _Contexts = require("./Contexts"); /* eslint-disable @typescript-eslint/no-shadow */ /** * Retrieve the values at a given path as well as a set of array helpers * for manipulating list values. * * ```jsx * function ContactList() { * const [values, arrayHelpers, meta] = useFieldArray("contacts") * * return ( * <ul> * {values.map((value, idx) => ( * <li key={value.id}> * <Form.Field name={`contacts[${idx}].name`} /> * * <button onClick={() => arrayHelpers.remove(value)}> * Remove * </button> * </li> * )} * </ul> * ) * } * ``` * * @param name A field path, should point to an array value in the form data */ /** * Retrieve the values at a given path as well as a set of array helpers * for manipulating list values. * * ```jsx * function ContactList() { * const [values, arrayHelpers, meta] = useFieldArray({ * name: 'contacts', * validates: 'otherField' * }) * * return ( * <ul> * {values.map((value, idx) => ( * <li key={value.id}> * <Form.Field name={`contacts[${idx}].name`} /> * * <button onClick={() => arrayHelpers.remove(value)}> * Remove * </button> * </li> * )} * </ul> * ) * } * ``` * * @param name A field path, should point to an array value in the form data */ function useFieldArray(optionsOrName) { var _options$exclusive; let options = typeof optionsOrName === 'string' ? { name: optionsOrName, exclusive: true } : optionsOrName; let { name } = options; const { actions } = (0, _Contexts.useFormContext)(_Contexts.BITS.actions); // TODO: doesn't shallow validate validates const fieldsToValidate = (0, _react.useMemo)(() => [{ path: name, shallow: true }], [name]); const meta = (0, _useField.useFieldMeta)(Object.assign({}, options, { exclusive: (_options$exclusive = options.exclusive) != null ? _options$exclusive : true, validates: fieldsToValidate })); const { errors, onError, value, onChange, update } = meta; const sendErrors = fn => { onError(fn(errors || {}, options.name)); }; const helpers = { unshift: item => helpers.insert(item, 0), add: item => helpers.push(item), push: item => helpers.insert(item, value ? value.length : 0), insert: (item, index) => { const newValue = value == null ? [] : [...value]; newValue.splice(index, 0, item); onChange(newValue); sendErrors((errors, name) => (0, _Errors.unshift)(errors, name, index)); }, move: (item, toIndex) => { const fromIndex = value.indexOf(item); const newValue = value == null ? [] : [...value]; if (fromIndex === -1) throw new Error('`onMove` must be called with an item in the array'); newValue.splice(toIndex, 0, ...newValue.splice(fromIndex, 1)); // FIXME: doesn't handle syncing error state. , { action: 'move', toIndex, fromIndex } onChange(newValue); sendErrors((errors, name) => (0, _Errors.move)(errors, name, fromIndex, toIndex)); }, remove: item => { if (value == null) return; const index = value.indexOf(item); onChange(value.filter(v => v !== item)); sendErrors((errors, name) => (0, _Errors.shift)(errors, name, index)); }, onItemError: (name, errors) => { sendErrors(fieldErrors => Object.assign({}, (0, _Errors.remove)(fieldErrors, name), errors)); }, update: (updatedItem, oldItem) => { const index = value.indexOf(oldItem); const newValue = value == null ? [] : [...value]; newValue.splice(index, 1, updatedItem); update(newValue); // @ts-ignore if (options.noValidate) return; actions == null ? void 0 : actions.onValidate([`${name}[${index}]`], 'onChange', []); } }; return [value, Object.assign((0, _react.useRef)({}).current, helpers), meta]; } var _default = useFieldArray; exports.default = _default;