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 &&
}