From 0b45f197d9cd73ace2a96671174771f04b6898a2 Mon Sep 17 00:00:00 2001 From: Felix Henninger Date: Tue, 11 Aug 2020 23:29:40 +0200 Subject: [PATCH] Manage array state through custom context --- packages/builder/src/components/Form/array.js | 132 +++++++++++++++--- 1 file changed, 113 insertions(+), 19 deletions(-) diff --git a/packages/builder/src/components/Form/array.js b/packages/builder/src/components/Form/array.js index 5ea62acfc..8f1f25649 100644 --- a/packages/builder/src/components/Form/array.js +++ b/packages/builder/src/components/Form/array.js @@ -1,7 +1,93 @@ -import React, { Fragment } from 'react' +import React, { createContext, useContext, Fragment } from 'react' import { FieldArray, useFormikContext, getIn } from 'formik' +// State management ------------------------------------------------------------ + +const ArrayContext = createContext({}) + +const ArrayContextProvider = ({ name, values, + arrayHelpers, setValues, setFieldValue, ...props +}) => { + const getRows = () => getIn(values, name) ?? [] + + const overwriteRows = (newValue) => + setFieldValue(name, newValue) + + // TODO: A fair amount of these helper functions are grid-specific. + // Maybe it might be worth investigating splitting them out + const overwriteAll = (rows, columns) => { + const baseName = name.replace('.rows', '') + + setValues({ + [baseName]: { + ...getIn(values, baseName), + rows, columns, + } + }) + } + + const dispatch = (f) => { + const columnName = name.replace('.rows', '.columns') + + overwriteAll(...f( + getIn(values, name) ?? [], + getIn(values, columnName) ?? [], + )) + } + + const addRow = () => arrayHelpers.push([]) + + const mapRows = (f) => + overwriteRows(getRows().map(f)) + + const addColumn = (defaultCell, defaultColumn) => + dispatch((rows, columns) => ([ + rows.map(row => [...row, defaultCell]), + [...columns, defaultColumn], + ])) + + const deleteColumn = (index) => + dispatch((rows, columns) => ([ + rows.map(row => row.filter((_, i) => i !== index)), + columns.filter((_, i) => i !== index), + ])) + + const clearColumn = (index) => + mapRows(row => { + const output = [...row] + output[index] = '' + return output + }) + + const fillColumn = (index) => { + // Gather cells with content + const availableCells = getRows() + .map(r => r[index]) + .filter(r => r !== '') + + return mapRows((r, rowIndex) => { + const output = [...r] + output[index] = output[index] || + availableCells[rowIndex % availableCells.length] + return output + }) + } + + return ( + + ) +} + +export const useArrayContext = () => useContext(ArrayContext) + +// Basic form array ------------------------------------------------------------ + export const FormArray = ({ name, item: Item, header: Header, footer: Footer, @@ -10,30 +96,38 @@ export const FormArray = ({ bodyWrapper: BodyWrapper=Fragment, defaultItem={}, }) => { - const { values } = useFormikContext() + const { values, setFieldValue, setValues } = useFormikContext() const rows = getIn(values, name) return ( { arrayHelpers => - { Header &&
} - - { - (rows || []).map( - (data, index) => - - ) - } - - { Footer &&