@firecms/core 3.0.0-canary.242 → 3.0.0-canary.244

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,17 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from "react";
2
-
3
- import { DragDropContext, Draggable, DraggableProvided, Droppable } from "@hello-pangea/dnd";
4
-
2
+ import {
3
+ closestCenter,
4
+ DndContext,
5
+ DragEndEvent,
6
+ KeyboardSensor,
7
+ PointerSensor,
8
+ useSensor,
9
+ useSensors
10
+ } from "@dnd-kit/core";
11
+ import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
12
+
13
+ import { SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
14
+ import { CSS } from "@dnd-kit/utilities";
5
15
  import { getHashValue } from "../util";
6
16
  import {
7
17
  AddIcon,
@@ -20,11 +30,11 @@ import {
20
30
  } from "@firecms/ui";
21
31
 
22
32
  export type ArrayEntryParams = {
23
- index: number,
24
- internalId: number,
25
- isDragging: boolean,
26
- storedProps?: object,
27
- storeProps: (props: object) => void
33
+ index: number;
34
+ internalId: number;
35
+ isDragging: boolean;
36
+ storedProps?: object;
37
+ storeProps: (props: object) => void;
28
38
  };
29
39
 
30
40
  export type ArrayEntryBuilder = (params: ArrayEntryParams) => React.ReactNode;
@@ -37,14 +47,11 @@ export interface ArrayContainerProps<T> {
37
47
  disabled?: boolean;
38
48
  size?: "small" | "medium";
39
49
  onInternalIdAdded?: (id: number) => void;
40
- /**
41
- * @deprecated Use `canAddElements` instead
42
- */
43
50
  includeAddButton?: boolean;
44
51
  canAddElements?: boolean;
45
52
  sortable?: boolean;
46
53
  newDefaultEntry: T;
47
- onValueChange: (value: T[]) => void,
54
+ onValueChange: (value: T[]) => void;
48
55
  className?: string;
49
56
  min?: number;
50
57
  max?: number;
@@ -52,17 +59,285 @@ export interface ArrayContainerProps<T> {
52
59
 
53
60
  const buildIdsMap = (value: any[]) =>
54
61
  value && Array.isArray(value) && value.length > 0
55
- ? value.map((v, index) => {
56
- if (!v) return {};
57
- return ({
58
- [getHashValue(v) + index]: getRandomId()
59
- });
60
- }).reduce((a, b) => ({ ...a, ...b }), {})
61
- : {}
62
+ ? value
63
+ .map((v, index) => {
64
+ if (!v) return {};
65
+ return {
66
+ [getHashValue(v) + index]: getRandomId()
67
+ };
68
+ })
69
+ .reduce((a, b) => ({ ...a, ...b }), {})
70
+ : {};
71
+
72
+ type SortableItemProps = {
73
+ id: number;
74
+ index: number;
75
+ size?: "small" | "medium";
76
+ disabled: boolean;
77
+ buildEntry: ArrayEntryBuilder;
78
+ remove: (index: number) => void;
79
+ copy: (index: number) => void;
80
+ addInIndex?: (index: number) => void;
81
+ canAddElements?: boolean;
82
+ sortable: boolean;
83
+ storedProps?: object;
84
+ updateItemCustomProps: (internalId: number, props: object) => void;
85
+ };
86
+
87
+ function SortableItem({
88
+ id,
89
+ index,
90
+ size,
91
+ disabled,
92
+ buildEntry,
93
+ remove,
94
+ copy,
95
+ addInIndex,
96
+ canAddElements,
97
+ sortable,
98
+ storedProps,
99
+ updateItemCustomProps
100
+ }: SortableItemProps) {
101
+ const {
102
+ attributes,
103
+ listeners,
104
+ setNodeRef,
105
+ transform,
106
+ transition,
107
+ isDragging
108
+ } = sortable
109
+ ? useSortable({ id })
110
+ : {
111
+ attributes: {},
112
+ listeners: {},
113
+ setNodeRef: (node: HTMLElement | null) => {
114
+ },
115
+ transform: null,
116
+ transition: undefined,
117
+ isDragging: false
118
+ };
119
+
120
+ const style = transform ? {
121
+ transform: CSS.Transform.toString(transform),
122
+ transition
123
+ } : {};
124
+ const dragHandleProps = sortable ? { ...listeners, ...attributes } : {};
125
+
126
+ return (
127
+ <ArrayContainerItem
128
+ nodeRef={setNodeRef}
129
+ style={style}
130
+ dragHandleProps={dragHandleProps}
131
+ internalId={id}
132
+ index={index}
133
+ size={size}
134
+ disabled={disabled}
135
+ buildEntry={buildEntry}
136
+ remove={remove}
137
+ copy={copy}
138
+ addInIndex={addInIndex}
139
+ canAddElements={canAddElements}
140
+ sortable={sortable}
141
+ isDragging={isDragging}
142
+ storedProps={storedProps}
143
+ updateItemCustomProps={updateItemCustomProps}
144
+ />
145
+ );
146
+ }
147
+
148
+ type ArrayContainerItemProps = {
149
+ nodeRef: (node: HTMLElement | null) => void;
150
+ style: React.CSSProperties;
151
+ dragHandleProps: any;
152
+ index: number;
153
+ internalId: number;
154
+ size?: "small" | "medium";
155
+ disabled: boolean;
156
+ buildEntry: ArrayEntryBuilder;
157
+ remove: (index: number) => void;
158
+ copy: (index: number) => void;
159
+ addInIndex?: (index: number) => void;
160
+ canAddElements?: boolean;
161
+ sortable: boolean;
162
+ isDragging: boolean;
163
+ storedProps?: object;
164
+ updateItemCustomProps: (internalId: number, props: object) => void;
165
+ };
166
+
167
+ export function ArrayContainerItem({
168
+ nodeRef,
169
+ style,
170
+ dragHandleProps,
171
+ index,
172
+ internalId,
173
+ size,
174
+ disabled,
175
+ buildEntry,
176
+ remove,
177
+ copy,
178
+ addInIndex,
179
+ canAddElements,
180
+ sortable,
181
+ isDragging,
182
+ storedProps,
183
+ updateItemCustomProps
184
+ }: ArrayContainerItemProps) {
185
+ return (
186
+ <div
187
+ ref={nodeRef}
188
+ style={style}
189
+ className={`relative ${!isDragging ? "hover\\:bg-surface-accent-50 dark\\:hover\\:bg-surface-800 dark\\:hover\\:bg-opacity-20" : ""} rounded-md opacity-100`}
190
+ >
191
+ <div className="flex items-start">
192
+ <div className="flex-grow w-[calc(100%-48px)] text-text-primary dark:text-text-primary-dark">
193
+ {buildEntry({
194
+ index,
195
+ internalId,
196
+ isDragging,
197
+ storedProps,
198
+ storeProps: (props: object) => updateItemCustomProps(internalId, props)
199
+ })}
200
+ </div>
201
+ <ArrayItemOptions
202
+ dragHandleProps={dragHandleProps}
203
+ direction={size === "small" ? "row" : "column"}
204
+ disabled={disabled}
205
+ remove={remove}
206
+ index={index}
207
+ copy={copy}
208
+ canAddElements={canAddElements}
209
+ sortable={sortable}
210
+ addInIndex={addInIndex}
211
+ />
212
+ </div>
213
+ </div>
214
+ );
215
+ }
216
+
217
+ export function ArrayItemOptions({
218
+ dragHandleProps,
219
+ direction,
220
+ disabled,
221
+ remove,
222
+ index,
223
+ copy,
224
+ canAddElements,
225
+ sortable,
226
+ addInIndex,
227
+ }: {
228
+ dragHandleProps: any;
229
+ direction?: "row" | "column";
230
+ disabled: boolean;
231
+ remove: (index: number) => void;
232
+ index: number;
233
+ copy: (index: number) => void;
234
+ sortable: boolean;
235
+ canAddElements?: boolean;
236
+ addInIndex?: (index: number) => void;
237
+ }) {
238
+ const [menuOpen, setMenuOpen] = useState(false);
239
+ const iconRef = useRef<HTMLDivElement>(null);
240
+ useOutsideAlerter(iconRef, () => {
241
+ if (menuOpen) setMenuOpen(false);
242
+ });
243
+
244
+ const showMenu = canAddElements ?? false;
245
+
246
+ const handleIconButtonClick = (e: React.MouseEvent) => {
247
+ if (showMenu) {
248
+ e.stopPropagation();
249
+ e.preventDefault();
250
+ setMenuOpen(o => !o);
251
+ } else if (sortable) {
252
+ // Allow drag to propagate if menu is not shown
253
+ } else {
254
+ e.stopPropagation();
255
+ e.preventDefault();
256
+ }
257
+ };
258
+
259
+ const title = !disabled && sortable && showMenu ? "Drag to move. Click for options" :
260
+ !disabled && showMenu ? "Click for options" :
261
+ !disabled && sortable ? "Drag to move" : undefined;
262
+
263
+ return (
264
+ <div ref={iconRef}
265
+ className={`pl-2 pt-1 pb-1 flex ${direction === "row" ? "flex-row-reverse" : "flex-col"} items-center`}>
266
+ <Tooltip
267
+ delayDuration={400}
268
+ open={menuOpen ? false : undefined}
269
+ side={direction === "column" ? "left" : undefined}
270
+ title={title}
271
+ >
272
+ <IconButton
273
+ size="small"
274
+ disabled={disabled || (!showMenu && !sortable)}
275
+ {...(sortable ? dragHandleProps : {})}
276
+ onClick={handleIconButtonClick}
277
+ onFocus={() => {
278
+ if (sortable && menuOpen) setMenuOpen(false);
279
+ }}
280
+ className={cls(
281
+ (disabled || (!sortable && !showMenu)) ? "cursor-inherit" : "",
282
+ sortable && !disabled ? "cursor-grab" : "",
283
+ !sortable && showMenu && !disabled ? "cursor-pointer" : ""
284
+ )}
285
+ >
286
+ <HandleIcon/>
287
+ </IconButton>
288
+ </Tooltip>
289
+ {showMenu && (
290
+ <Menu portalContainer={iconRef.current} open={menuOpen} trigger={<div tabIndex={-1}/>}>
291
+ <MenuItem
292
+ dense
293
+ onClick={(e: React.MouseEvent) => {
294
+ setMenuOpen(false);
295
+ remove(index);
296
+ }}
297
+ >
298
+ <RemoveIcon size={"small"}/>
299
+ Remove
300
+ </MenuItem>
301
+ <MenuItem
302
+ dense
303
+ onClick={() => {
304
+ setMenuOpen(false);
305
+ copy(index);
306
+ }}
307
+ >
308
+ <ContentCopyIcon size={"small"}/>
309
+ Copy
310
+ </MenuItem>
311
+ {addInIndex && (
312
+ <MenuItem
313
+ dense
314
+ onClick={() => {
315
+ setMenuOpen(false);
316
+ addInIndex(index);
317
+ }}
318
+ >
319
+ <KeyboardArrowUpIcon size={"small"}/>
320
+ Add on top
321
+ </MenuItem>
322
+ )}
323
+ {addInIndex && (
324
+ <MenuItem
325
+ dense
326
+ onClick={() => {
327
+ setMenuOpen(false);
328
+ addInIndex(index + 1);
329
+ }}
330
+ >
331
+ <KeyboardArrowDownIcon size={"small"}/>
332
+ Add below
333
+ </MenuItem>
334
+ )}
335
+ </Menu>
336
+ )}
337
+ </div>
338
+ );
339
+ }
62
340
 
63
- /**
64
- * @group Form custom fields
65
- */
66
341
  export function ArrayContainer<T>({
67
342
  droppableId,
68
343
  addLabel,
@@ -80,27 +355,29 @@ export function ArrayContainer<T>({
80
355
  min = 0,
81
356
  max = Infinity
82
357
  }: ArrayContainerProps<T>) {
83
-
84
- const canAddElements = (canAddElementsProp || canAddElementsProp === undefined)
85
- && (deprecatedIncludeAddButton === undefined || deprecatedIncludeAddButton);
358
+ const canAddElements =
359
+ (canAddElementsProp === undefined ? true : canAddElementsProp) && // Default canAddElementsProp to true if undefined
360
+ (deprecatedIncludeAddButton === undefined || deprecatedIncludeAddButton);
86
361
 
87
362
  const hasValue = value && Array.isArray(value) && value.length > 0;
88
-
89
- // Used to track the ids that have displayed the initial show animation
90
363
  const internalIdsRef = useRef<Record<string, number>>(buildIdsMap(value));
91
-
92
364
  const [internalIds, setInternalIds] = useState<number[]>(
93
- hasValue
94
- ? Object.values(internalIdsRef.current)
95
- : []
365
+ hasValue ? Object.values(internalIdsRef.current) : []
96
366
  );
97
-
98
367
  const itemCustomPropsRef = useRef<Record<number, object>>({});
99
368
 
100
369
  const updateItemCustomProps = useCallback((internalId: number, customProps: object) => {
101
370
  itemCustomPropsRef.current[internalId] = customProps;
102
371
  }, []);
103
372
 
373
+ const pointerSensor = useSensor(PointerSensor, {
374
+ activationConstraint: {
375
+ distance: 5,
376
+ },
377
+ });
378
+ const keyboardSensor = useSensor(KeyboardSensor, {});
379
+ const sensors = useSensors(pointerSensor, keyboardSensor);
380
+
104
381
  useEffect(() => {
105
382
  if (hasValue && value && value.length !== internalIds.length) {
106
383
  const newInternalIds = value.map((v, index) => {
@@ -122,8 +399,7 @@ export function ArrayContainer<T>({
122
399
  if (disabled || (value ?? []).length >= max) return;
123
400
  const id = getRandomId();
124
401
  const newIds: number[] = [...internalIds, id];
125
- if (onInternalIdAdded)
126
- onInternalIdAdded(id);
402
+ if (onInternalIdAdded) onInternalIdAdded(id);
127
403
  setInternalIds(newIds);
128
404
  onValueChange([...(value ?? []), newDefaultEntry]);
129
405
  };
@@ -145,10 +421,8 @@ export function ArrayContainer<T>({
145
421
  id,
146
422
  ...internalIds.slice(index + 1)
147
423
  ];
148
- if (onInternalIdAdded)
149
- onInternalIdAdded(id);
424
+ if (onInternalIdAdded) onInternalIdAdded(id);
150
425
  setInternalIds(newIds);
151
- // insert value in index + 1
152
426
  onValueChange([...value.slice(0, index + 1), copyingItem, ...value.slice(index + 1)]);
153
427
  };
154
428
 
@@ -160,272 +434,112 @@ export function ArrayContainer<T>({
160
434
  id,
161
435
  ...internalIds.slice(index)
162
436
  ];
163
- if (onInternalIdAdded)
164
- onInternalIdAdded(id);
437
+ if (onInternalIdAdded) onInternalIdAdded(id);
165
438
  setInternalIds(newIds);
166
439
  onValueChange([...value.slice(0, index), newDefaultEntry, ...value.slice(index)]);
167
- }
168
-
169
- const onDragEnd = (result: any) => {
170
- // dropped outside the list
171
- if (!result.destination) {
172
- return;
173
- }
174
- const sourceIndex = result.source.index;
175
- const destinationIndex = result.destination.index;
440
+ };
176
441
 
177
- const newIds = [...internalIds];
178
- const temp = newIds[sourceIndex];
179
- newIds[sourceIndex] = newIds[destinationIndex];
180
- newIds[destinationIndex] = temp;
442
+ const onDragEnd = (event: DragEndEvent) => {
443
+ const {
444
+ active,
445
+ over
446
+ } = event;
447
+ if (!over || active.id === over.id) return;
448
+
449
+ const oldIndex = internalIds.indexOf(active.id as number);
450
+ const newIndex = internalIds.indexOf(over.id as number);
451
+ if (oldIndex === -1 || newIndex === -1) return;
452
+ const newIds = arrayMove(internalIds, oldIndex, newIndex);
181
453
  setInternalIds(newIds);
182
-
183
- onValueChange(arrayMove(value, sourceIndex, destinationIndex));
454
+ onValueChange(arrayMove(value, oldIndex, newIndex));
184
455
  };
185
456
 
186
- return (
187
- <DragDropContext onDragEnd={onDragEnd}>
188
- <Droppable droppableId={droppableId}
189
- renderClone={(provided, snapshot, rubric) => {
190
- const index = rubric.source.index;
191
- const internalId = internalIds[index];
192
- return (
193
- <ArrayContainerItem
194
- provided={provided}
195
- internalId={internalId}
196
- index={index}
197
- size={size}
198
- disabled={disabled}
199
- buildEntry={buildEntry}
200
- remove={remove}
201
- copy={copy}
202
- isDragging={snapshot.isDragging}
203
- storedProps={itemCustomPropsRef.current[internalId]}
204
- updateItemCustomProps={updateItemCustomProps}
205
- addInIndex={addInIndex}
206
- canAddElements={canAddElements}
207
- sortable={sortable}
208
- />
209
- );
210
- }}
211
- >
212
- {(droppableProvided, droppableSnapshot) => (
213
- <div
214
- className={cls("space-y-1", className)}
215
- {...droppableProvided.droppableProps}
216
- ref={droppableProvided.innerRef}>
217
- {hasValue && internalIds.map((internalId: number, index: number) => {
218
- return (
219
- <Draggable
220
- key={`array_field_${internalId}`}
221
- draggableId={`array_field_${internalId}`}
222
- isDragDisabled={disabled || !sortable}
223
- index={index}>
224
- {(provided, snapshot) => (
225
- <ArrayContainerItem
226
- provided={provided}
227
- internalId={internalId}
228
- index={index}
229
- size={size}
230
- disabled={disabled}
231
- buildEntry={buildEntry}
232
- remove={remove}
233
- copy={copy}
234
- isDragging={snapshot.isDragging}
235
- storedProps={itemCustomPropsRef.current[internalId]}
236
- updateItemCustomProps={updateItemCustomProps}
237
- addInIndex={addInIndex}
238
- canAddElements={canAddElements}
239
- sortable={sortable}
240
- />
241
- )}
242
- </Draggable>
243
- );
244
- })}
245
-
246
- {droppableProvided.placeholder}
247
-
248
- {canAddElements && (
249
- <div className="my-4 justify-center text-left">
250
- <Button
251
- variant={"text"}
252
- size={size === "small" ? "small" : "medium"}
253
- color="primary"
254
- disabled={disabled || value?.length >= max}
255
- startIcon={<AddIcon/>}
256
- onClick={insertInEnd}>
257
- {addLabel ?? "Add"}
258
- </Button>
259
- </div>
260
- )}
261
- </div>
262
- )}
263
- </Droppable>
264
- </DragDropContext>
265
- );
266
- }
267
-
268
- type ArrayContainerItemProps = {
269
- provided: DraggableProvided,
270
- index: number,
271
- internalId: number,
272
- size?: "small" | "medium",
273
- disabled: boolean,
274
- buildEntry: ArrayEntryBuilder,
275
- remove: (index: number) => void,
276
- copy: (index: number) => void,
277
- addInIndex?: (index: number) => void,
278
- canAddElements?: boolean,
279
- sortable: boolean,
280
- isDragging: boolean,
281
- storedProps?: object,
282
- updateItemCustomProps: (internalId: number, props: object) => void
283
- };
284
-
285
- export function ArrayContainerItem({
286
- provided,
287
- index,
288
- internalId,
289
- size,
290
- disabled,
291
- buildEntry,
292
- remove,
293
- addInIndex,
294
- canAddElements,
295
- sortable,
296
- copy,
297
- isDragging,
298
- storedProps,
299
- updateItemCustomProps
300
- }: ArrayContainerItemProps) {
301
-
302
- return <div
303
- ref={provided.innerRef}
304
- {...provided.draggableProps}
305
- style={provided.draggableProps.style}
306
- className={`relative ${
307
- !isDragging ? "hover:bg-surface-accent-50 dark:hover:bg-surface-800 dark:hover:bg-opacity-20" : ""
308
- } rounded-md opacity-100`}
309
- >
310
- <div
311
- className="flex items-start">
312
- <div
313
- className="flex-grow w-[calc(100%-48px)] text-text-primary dark:text-text-primary-dark">
314
- {buildEntry({
315
- index,
316
- internalId,
317
- isDragging,
318
- storedProps,
319
- storeProps: (props: object) => updateItemCustomProps(internalId, props)
320
- })}
321
- </div>
322
- <ArrayItemOptions direction={size === "small" ? "row" : "column"}
323
- disabled={disabled}
324
- remove={remove}
325
- index={index}
326
- provided={provided}
327
- addInIndex={addInIndex}
328
- canAddElements={canAddElements}
329
- sortable={sortable}
330
- copy={copy}/>
457
+ return sortable ? (
458
+ <DndContext sensors={sensors}
459
+ modifiers={[restrictToVerticalAxis]}
460
+ collisionDetection={closestCenter}
461
+ onDragEnd={onDragEnd}>
462
+ <SortableContext items={internalIds} strategy={verticalListSortingStrategy}>
463
+ <div className={cls("space-y-1", className)} id={droppableId}>
464
+ {hasValue &&
465
+ internalIds.map((internalId: number, index: number) => (
466
+ <SortableItem
467
+ key={`array_field_${internalId}`}
468
+ id={internalId}
469
+ index={index}
470
+ size={size}
471
+ disabled={disabled}
472
+ buildEntry={buildEntry}
473
+ remove={remove}
474
+ copy={copy}
475
+ addInIndex={addInIndex}
476
+ canAddElements={canAddElements}
477
+ sortable={sortable}
478
+ storedProps={itemCustomPropsRef.current[internalId]}
479
+ updateItemCustomProps={updateItemCustomProps}
480
+ />
481
+ ))}
482
+ {canAddElements && (
483
+ <div className="my-4 justify-center text-left">
484
+ <Button
485
+ variant={"text"}
486
+ size={size === "small" ? "small" : "medium"}
487
+ color="primary"
488
+ disabled={disabled || (value?.length ?? 0) >= max}
489
+ startIcon={<AddIcon/>}
490
+ onClick={insertInEnd}
491
+ >
492
+ {addLabel ?? "Add"}
493
+ </Button>
494
+ </div>
495
+ )}
496
+ </div>
497
+ </SortableContext>
498
+ </DndContext>
499
+ ) : (
500
+ <div className={cls("space-y-1", className)} id={droppableId}>
501
+ {hasValue &&
502
+ internalIds.map((internalId: number, index: number) => (
503
+ <ArrayContainerItem
504
+ key={`array_field_${internalId}`}
505
+ nodeRef={(node: HTMLElement | null) => {
506
+ }}
507
+ style={{}}
508
+ dragHandleProps={{}}
509
+ internalId={internalId}
510
+ index={index}
511
+ size={size}
512
+ disabled={disabled}
513
+ buildEntry={buildEntry}
514
+ remove={remove}
515
+ copy={copy}
516
+ addInIndex={addInIndex}
517
+ canAddElements={canAddElements}
518
+ sortable={false}
519
+ isDragging={false}
520
+ storedProps={itemCustomPropsRef.current[internalId]}
521
+ updateItemCustomProps={updateItemCustomProps}
522
+ />
523
+ ))}
524
+ {canAddElements && (
525
+ <div className="my-4 justify-center text-left">
526
+ <Button
527
+ variant={"text"}
528
+ size={size === "small" ? "small" : "medium"}
529
+ color="primary"
530
+ disabled={disabled || (value?.length ?? 0) >= max}
531
+ startIcon={<AddIcon/>}
532
+ onClick={insertInEnd}
533
+ >
534
+ {addLabel ?? "Add"}
535
+ </Button>
536
+ </div>
537
+ )}
331
538
  </div>
332
- </div>;
333
- }
334
-
335
- export function ArrayItemOptions({
336
- direction,
337
- disabled,
338
- remove,
339
- index,
340
- provided,
341
- copy,
342
- canAddElements,
343
- sortable,
344
- addInIndex
345
- }: {
346
- direction?: "row" | "column",
347
- disabled: boolean,
348
- remove: (index: number) => void,
349
- index: number,
350
- provided: any,
351
- copy: (index: number) => void,
352
- sortable: boolean,
353
- canAddElements?: boolean,
354
- addInIndex?: (index: number) => void
355
- }) {
356
-
357
- const [menuOpen, setMenuOpen] = useState(false);
358
-
359
- const iconRef = React.useRef<HTMLDivElement>(null);
360
- useOutsideAlerter(iconRef, () => setMenuOpen(false));
361
-
362
- return <div className={`pl-2 pt-1 pb-1 flex ${direction === "row" ? "flex-row-reverse" : "flex-col"} items-center`}
363
- ref={iconRef}
364
- {...provided.dragHandleProps}>
365
- <Tooltip
366
- delayDuration={400}
367
- open={menuOpen ? false : undefined}
368
- side={direction === "column" ? "left" : undefined}
369
- title={!disabled && sortable ? "Drag to move. Click for more options" : undefined}>
370
- <IconButton
371
- size="small"
372
- disabled={disabled || !canAddElements}
373
- onClick={(e) => {
374
- e.preventDefault();
375
- setMenuOpen(true);
376
- }}
377
- onDragStart={(e: any) => {
378
- setMenuOpen(false);
379
- }}
380
- className={disabled || !sortable ? "cursor-inherit" : "cursor-grab"}>
381
- <HandleIcon/>
382
- </IconButton>
383
-
384
- <Menu
385
- portalContainer={iconRef.current}
386
- open={menuOpen}
387
- trigger={<div tabIndex={-1}/>}>
388
-
389
- <MenuItem dense onClick={(e) => {
390
- setMenuOpen(false);
391
- remove(index);
392
- }}>
393
- <RemoveIcon size={"small"}/>
394
- Remove
395
- </MenuItem>
396
- <MenuItem dense onClick={() => {
397
- setMenuOpen(false);
398
- copy(index);
399
- }}>
400
- <ContentCopyIcon size={"small"}/>
401
- Copy
402
- </MenuItem>
403
-
404
- {addInIndex && <MenuItem dense
405
- onClick={() => {
406
- setMenuOpen(false);
407
- addInIndex(index);
408
- }}>
409
- <KeyboardArrowUpIcon size={"small"}/>
410
- Add on top
411
- </MenuItem>}
412
-
413
- {addInIndex && <MenuItem dense
414
- onClick={() => {
415
- setMenuOpen(false);
416
- addInIndex(index + 1);
417
- }}>
418
- <KeyboardArrowDownIcon size={"small"}/>
419
- Add below
420
- </MenuItem>}
421
-
422
- </Menu>
423
- </Tooltip>
424
-
425
- </div>;
539
+ );
426
540
  }
427
541
 
428
- function arrayMove(value: any[], sourceIndex: number, destinationIndex: number) {
542
+ function arrayMove<T>(value: T[], sourceIndex: number, destinationIndex: number): T[] {
429
543
  const result = Array.from(value);
430
544
  const [removed] = result.splice(sourceIndex, 1);
431
545
  result.splice(destinationIndex, 0, removed);