@modular-forms/react
Version:
The modular and type-safe form library for React
109 lines (108 loc) • 5.73 kB
JavaScript
import { batch } from '@preact/signals-react';
import { getFieldArrayNames, getFieldArrayState, getFieldArrayStore, getFieldNames, getFieldState, getPathIndex, setFieldArrayState, setFieldState, sortArrayPathIndex, updateFieldArrayDirty, } from '../utils';
/**
* Moves a field of the field array to a new position and rearranges all fields
* in between.
*
* @param form The form of the field array.
* @param name The name of the field array.
* @param options The move options.
*/
export function move(form, name, { from: fromIndex, to: toIndex }) {
// Get store of specified field array
const fieldArray = getFieldArrayStore(form, name);
// Continue if specified field array exists
if (fieldArray) {
// Get last index of field array
const lastIndex = fieldArray.items.peek().length - 1;
// Continue if specified indexes are valid
if (fromIndex >= 0 &&
fromIndex <= lastIndex &&
toIndex >= 0 &&
toIndex <= lastIndex &&
fromIndex !== toIndex) {
// Create function to filter a name
const filterName = (value) => {
if (value.startsWith(name)) {
const fieldIndex = getPathIndex(name, value);
return ((fieldIndex >= fromIndex && fieldIndex <= toIndex) ||
(fieldIndex <= fromIndex && fieldIndex >= toIndex));
}
};
// Create function to get previous index name
const getPrevIndexName = (fieldOrFieldArrayName, fieldOrFieldArrayIndex) => fieldOrFieldArrayName.replace(`${name}.${fieldOrFieldArrayIndex}`, fromIndex < toIndex
? `${name}.${fieldOrFieldArrayIndex - 1}`
: `${name}.${fieldOrFieldArrayIndex + 1}`);
// Create function to get "to" index name
const getToIndexName = (fieldOrFieldArrayName) => fieldOrFieldArrayName.replace(`${name}.${fromIndex}`, `${name}.${toIndex}`);
// Create list of all affected field and field array names
const fieldNames = getFieldNames(form)
.filter(filterName)
.sort(sortArrayPathIndex(name));
const fieldArrayNames = getFieldArrayNames(form)
.filter(filterName)
.sort(sortArrayPathIndex(name));
// Reverse names if "from" index is greater than "to" index
if (fromIndex > toIndex) {
fieldNames.reverse();
fieldArrayNames.reverse();
}
// Create field and field array state map
const fieldStateMap = new Map();
const fieldArrayStateMap = new Map();
batch(() => {
// Add state of "from" fields to map and move all fields in between forward
// or backward
fieldNames.forEach((fieldName) => {
// Get state of current field
const fieldState = getFieldState(form, fieldName);
// Get index of current field
const fieldIndex = getPathIndex(name, fieldName);
// Add state of field to map if it is "from" index
if (fieldIndex === fromIndex) {
fieldStateMap.set(fieldName, fieldState);
// Otherwise replace state of previous field with state of current
// field
}
else {
setFieldState(form, getPrevIndexName(fieldName, fieldIndex), fieldState);
}
});
// Finally move fields with "from" index to "to" index
fieldStateMap.forEach((fieldState, fieldName) => {
setFieldState(form, getToIndexName(fieldName), fieldState);
});
// Add state of "from" field arrays to map and move all field arrays in
// between forward or backward
fieldArrayNames.forEach((fieldArrayName) => {
// Get state of current field array
const fieldArrayState = getFieldArrayState(form, fieldArrayName);
// Get index of current field array
const fieldArrayIndex = getPathIndex(name, fieldArrayName);
// Add state of field to map if it is "from" index
if (fieldArrayIndex === fromIndex) {
fieldArrayStateMap.set(fieldArrayName, fieldArrayState);
// Otherwise replace state of previous field array with state of
// current field array
}
else {
setFieldArrayState(form, getPrevIndexName(fieldArrayName, fieldArrayIndex), fieldArrayState);
}
});
// Finally move field arrays with "from" index to "to" index
fieldArrayStateMap.forEach((fieldArrayState, fieldArrayName) => {
setFieldArrayState(form, getToIndexName(fieldArrayName), fieldArrayState);
});
// Swap items of field array
const nextItems = [...fieldArray.items.peek()];
nextItems.splice(toIndex, 0, nextItems.splice(fromIndex, 1)[0]);
fieldArray.items.value = nextItems;
// Set touched at field array and form to true
fieldArray.touched.value = true;
form.touched.value = true;
// Update dirty state at field array and form
updateFieldArrayDirty(form, fieldArray);
});
}
}
}