From 12800f816b872d614ed50c9fcf3179f41dbbbfb2 Mon Sep 17 00:00:00 2001 From: Chris Villa Date: Tue, 10 Oct 2023 15:40:33 +0100 Subject: [PATCH] feat: enable drag-and-drop of array items --- .../InputOrGroup/fields/ArrayField/index.tsx | 168 ++++++++++++------ .../components/InputOrGroup/styles.module.css | 21 +-- 2 files changed, 122 insertions(+), 67 deletions(-) diff --git a/packages/core/components/InputOrGroup/fields/ArrayField/index.tsx b/packages/core/components/InputOrGroup/fields/ArrayField/index.tsx index 7c713496a6..28c7eaed71 100644 --- a/packages/core/components/InputOrGroup/fields/ArrayField/index.tsx +++ b/packages/core/components/InputOrGroup/fields/ArrayField/index.tsx @@ -3,10 +3,20 @@ import styles from "../../styles.module.css"; import { List, Trash } from "react-feather"; import { InputOrGroup, type InputProps } from "../.."; import { IconButton } from "../../../IconButton"; -import { replace } from "../../../../lib"; +import { reorder, replace } from "../../../../lib"; +import DroppableStrictMode from "../../../DroppableStrictMode"; +import { DragDropContext } from "react-beautiful-dnd"; +import { Draggable } from "../../../Draggable"; +import { generateId } from "../../../../lib/generate-id"; +import { useEffect, useState } from "react"; const getClassName = getClassNameFactory("Input", styles); +type ItemWithId = { + _arrayId: string; + data: any; +}; + export const ArrayField = ({ field, onChange, @@ -14,6 +24,18 @@ export const ArrayField = ({ name, label, }: InputProps) => { + const [valueWithIds, setValueWithIds] = useState(value); + + // Create a mirror of value with IDs added for drag and drop + useEffect(() => { + const newValueWithIds = value.map((item, idx) => ({ + _arrayId: valueWithIds[idx]?._arrayId || generateId("ArrayItem"), + data: item, + })); + + setValueWithIds(newValueWithIds); + }, [value]); + if (!field.arrayFields) { return null; } @@ -26,65 +48,101 @@ export const ArrayField = ({ {label || name} -
- {Array.isArray(value) ? ( - value.map((item, i) => ( -
- - {field.getItemSummary - ? field.getItemSummary(item, i) - : `Item #${i}`} + { + if (event.destination) { + const newValue: ItemWithId[] = reorder( + valueWithIds, + event.source.index, + event.destination?.index + ); + + setValueWithIds(newValue); + onChange(newValue.map(({ _arrayId, data }) => data)); + } + }} + > + + {(provided) => { + return ( +
+ {Array.isArray(value) ? ( + valueWithIds.map(({ _arrayId, data }, i) => ( + +
+ + {field.getItemSummary + ? field.getItemSummary(data, i) + : `Item #${i}`} + +
+ { + const existingValue = value || []; + const existingValueWithIds = valueWithIds || []; + + existingValue.splice(i, 1); + existingValueWithIds.splice(i, 1); -
- { - const existingValue = value || []; + onChange(existingValue); + setValueWithIds(existingValueWithIds); + }} + title="Delete" + > + + +
+
+
+ {Object.keys(field.arrayFields!).map((fieldName) => { + const subField = field.arrayFields![fieldName]; - existingValue.splice(i, 1); - onChange(existingValue); - }} - title="Delete" - > - - -
-
-
- {Object.keys(field.arrayFields!).map((fieldName) => { - const subField = field.arrayFields![fieldName]; + return ( + + onChange( + replace(value, i, { + ...data, + [fieldName]: val, + }) + ) + } + /> + ); + })} +
+
+ + )) + ) : ( +
+ )} - return ( - - onChange( - replace(value, i, { ...item, [fieldName]: val }) - ) - } - /> - ); - })} - - - )) - ) : ( -
- )} + {provided.placeholder} - +
+ ); }} - > - + Add item - -
+ +
); }; diff --git a/packages/core/components/InputOrGroup/styles.module.css b/packages/core/components/InputOrGroup/styles.module.css index 330fee868a..35f4cb66b4 100644 --- a/packages/core/components/InputOrGroup/styles.module.css +++ b/packages/core/components/InputOrGroup/styles.module.css @@ -100,13 +100,15 @@ padding: 16px; } -.Input-arrayItem - > .Input-fieldset - .Input - + .Input-arrayItem - > .Input-fieldset - .Input { - margin-top: 16px; +.Input-array { + display: flex; + flex-direction: column; +} + +.Input-arrayItem { + display: block; + overflow: hidden; + margin-bottom: 12px; } .Input-arrayItem > .Input-fieldset .Input-label { @@ -128,10 +130,6 @@ cursor: pointer; } -.Input-arrayItem + .Input-arrayItem { - margin-top: 12px; -} - .Input-addButton { background-color: white; border: none; @@ -140,7 +138,6 @@ cursor: pointer; width: 100%; margin: 0; - margin-top: 12px; padding: 12px 16px; text-align: left; }