react-formal
Version:
Classy HTML form management for React
144 lines (138 loc) • 4.26 kB
JavaScript
;
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;