@@ -5,7 +5,6 @@ import React, {
5
5
useRef ,
6
6
useState
7
7
} from "react" ;
8
- import { FieldArray } from "formik" ;
9
8
10
9
import {
11
10
Box ,
@@ -35,30 +34,32 @@ import useMeasure from "react-use-measure";
35
34
import { MoreVert } from "@mui/icons-material" ;
36
35
37
36
interface ArrayContainerProps < T > {
37
+ droppableId : string ;
38
38
value : T [ ] ;
39
- name : string ;
40
39
addLabel : string ;
41
40
buildEntry : ( index : number , internalId : number ) => React . ReactNode ;
42
- disabled : boolean ;
41
+ disabled ? : boolean ;
43
42
small ?: boolean ;
44
43
onInternalIdAdded ?: ( id : number ) => void ;
45
44
includeAddButton ?: boolean ;
46
- newDefaultEntry ?: T | null ;
45
+ newDefaultEntry : T ;
46
+ onValueChange : ( value : T [ ] ) => void
47
47
}
48
48
49
49
/**
50
50
* @category Form custom fields
51
51
*/
52
52
export function ArrayContainer < T > ( {
53
- name ,
53
+ droppableId ,
54
54
addLabel,
55
55
value,
56
- disabled,
56
+ disabled = false ,
57
57
buildEntry,
58
58
small,
59
59
onInternalIdAdded,
60
60
includeAddButton,
61
- newDefaultEntry = null
61
+ newDefaultEntry,
62
+ onValueChange
62
63
} : ArrayContainerProps < T > ) {
63
64
64
65
const hasValue = value && Array . isArray ( value ) && value . length > 0 ;
@@ -74,7 +75,6 @@ export function ArrayContainer<T>({
74
75
[ value , hasValue ] ) ;
75
76
76
77
// Used to track the ids that have displayed the initial show animation
77
- const animatedIds = useRef < Set < number > > ( new Set ( Object . values ( internalIdsMap ) ) ) ;
78
78
const internalIdsRef = useRef < Record < string , number > > ( internalIdsMap ) ;
79
79
80
80
const [ internalIds , setInternalIds ] = useState < number [ ] > (
@@ -98,83 +98,25 @@ export function ArrayContainer<T>({
98
98
}
99
99
} , [ hasValue , internalIds . length , value ] ) ;
100
100
101
- return < FieldArray
102
- name = { name }
103
- validateOnChange = { true }
104
- render = { arrayHelpers => {
105
- return < ArrayContainerInternal
106
- disabled = { disabled }
107
- internalIds = { internalIds }
108
- onInternalIdAdded = { onInternalIdAdded }
109
- setInternalIds = { setInternalIds }
110
- arrayHelpers = { arrayHelpers }
111
- newDefaultEntry = { newDefaultEntry }
112
- value = { value }
113
- name = { name }
114
- small = { small }
115
- buildEntry = { buildEntry }
116
- hasValue = { hasValue }
117
- includeAddButton = { includeAddButton }
118
- addLabel = { addLabel }
119
- animatedIds = { animatedIds }
120
- /> ;
121
- } }
122
- /> ;
123
- }
124
101
125
- function ArrayContainerInternal < T > ( {
126
- disabled,
127
- internalIds,
128
- onInternalIdAdded,
129
- setInternalIds,
130
- arrayHelpers,
131
- newDefaultEntry,
132
- value,
133
- name,
134
- small,
135
- buildEntry,
136
- hasValue,
137
- includeAddButton,
138
- addLabel,
139
- animatedIds
140
- } : {
141
- disabled : boolean ,
142
- internalIds : any ,
143
- onInternalIdAdded : ( ( id : number ) => void ) | undefined ,
144
- setInternalIds : any ,
145
- arrayHelpers : any ,
146
- newDefaultEntry : T | null | undefined ,
147
- value : T [ ] ,
148
- name : string ,
149
- small : boolean | undefined ,
150
- buildEntry : ( index : number , internalId : number ) => React . ReactNode ,
151
- hasValue : boolean ,
152
- includeAddButton : boolean | undefined ,
153
- addLabel : string ,
154
- animatedIds : React . MutableRefObject < Set < number > > ,
155
- } ) {
156
- // eslint-disable-next-line react-hooks/rules-of-hooks
157
- const insertInEnd = useCallback ( ( ) => {
102
+ const insertInEnd = ( ) => {
158
103
if ( disabled ) return ;
159
104
const id = getRandomId ( ) ;
160
105
const newIds : number [ ] = [ ...internalIds , id ] ;
161
106
if ( onInternalIdAdded )
162
107
onInternalIdAdded ( id ) ;
163
108
setInternalIds ( newIds ) ;
164
- arrayHelpers . push ( newDefaultEntry ) ;
165
- } , [ arrayHelpers , disabled , internalIds , newDefaultEntry , onInternalIdAdded , setInternalIds ] ) ;
109
+ onValueChange ( [ ... ( value ?? [ ] ) , newDefaultEntry ] ) ;
110
+ } ;
166
111
167
- // eslint-disable-next-line react-hooks/rules-of-hooks
168
- const remove = useCallback ( ( index : number ) => {
112
+ const remove = ( index : number ) => {
169
113
const newIds = [ ...internalIds ] ;
170
114
newIds . splice ( index , 1 ) ;
171
115
setInternalIds ( newIds ) ;
172
- animatedIds . current . delete ( internalIds [ index ] ) ;
173
- arrayHelpers . remove ( index ) ;
174
- } , [ arrayHelpers , internalIds , setInternalIds ] ) ;
116
+ onValueChange ( value . filter ( ( _ , i ) => i !== index ) ) ;
117
+ } ;
175
118
176
- // eslint-disable-next-line react-hooks/rules-of-hooks
177
- const copy = useCallback ( ( index : number ) => {
119
+ const copy = ( index : number ) => {
178
120
const id = getRandomId ( ) ;
179
121
const copyingItem = value [ index ] ;
180
122
const newIds : number [ ] = [
@@ -184,11 +126,11 @@ function ArrayContainerInternal<T>({
184
126
if ( onInternalIdAdded )
185
127
onInternalIdAdded ( id ) ;
186
128
setInternalIds ( newIds ) ;
187
- arrayHelpers . insert ( index + 1 , copyingItem ) ;
188
- } , [ arrayHelpers , internalIds , onInternalIdAdded , setInternalIds , value ] ) ;
129
+ // insert value in index + 1
130
+ onValueChange ( [ ...value . slice ( 0 , index + 1 ) , copyingItem , ...value . slice ( index + 1 ) ] ) ;
131
+ } ;
189
132
190
- // eslint-disable-next-line react-hooks/rules-of-hooks
191
- const onDragEnd = useCallback ( ( result : any ) => {
133
+ const onDragEnd = ( result : any ) => {
192
134
// dropped outside the list
193
135
if ( ! result . destination ) {
194
136
return ;
@@ -202,12 +144,12 @@ function ArrayContainerInternal<T>({
202
144
newIds [ destinationIndex ] = temp ;
203
145
setInternalIds ( newIds ) ;
204
146
205
- arrayHelpers . move ( sourceIndex , destinationIndex ) ;
206
- } , [ arrayHelpers , internalIds , setInternalIds ] ) ;
147
+ onValueChange ( arrayMove ( value , sourceIndex , destinationIndex ) ) ;
148
+ } ;
207
149
208
150
return (
209
151
< DragDropContext onDragEnd = { onDragEnd } >
210
- < Droppable droppableId = { `droppable_ ${ name } ` }
152
+ < Droppable droppableId = { droppableId }
211
153
renderClone = { ( provided , snapshot , rubric ) => {
212
154
const index = rubric . source . index ;
213
155
const internalId = internalIds [ index ] ;
@@ -216,13 +158,11 @@ function ArrayContainerInternal<T>({
216
158
provided = { provided }
217
159
internalId = { internalId }
218
160
index = { index }
219
- name = { name }
220
161
small = { small }
221
162
disabled = { disabled }
222
163
buildEntry = { buildEntry }
223
164
remove = { remove }
224
165
copy = { copy }
225
- animatedIds = { animatedIds }
226
166
/>
227
167
) ;
228
168
} }
@@ -234,22 +174,20 @@ function ArrayContainerInternal<T>({
234
174
{ hasValue && internalIds . map ( ( internalId : number , index : number ) => {
235
175
return (
236
176
< Draggable
237
- key = { `array_field_${ name } _ ${ internalId } ` }
238
- draggableId = { `array_field_${ name } _ ${ internalId } ` }
177
+ key = { `array_field_${ internalId } ` }
178
+ draggableId = { `array_field_${ internalId } ` }
239
179
isDragDisabled = { disabled }
240
180
index = { index } >
241
181
{ ( provided , snapshot ) => (
242
182
< ArrayContainerItem
243
183
provided = { provided }
244
184
internalId = { internalId }
245
185
index = { index }
246
- name = { name }
247
186
small = { small }
248
187
disabled = { disabled }
249
188
buildEntry = { buildEntry }
250
189
remove = { remove }
251
190
copy = { copy }
252
- animatedIds = { animatedIds }
253
191
/>
254
192
) }
255
193
</ Draggable >
@@ -261,12 +199,13 @@ function ArrayContainerInternal<T>({
261
199
{ includeAddButton && < Box p = { 1 }
262
200
justifyContent = "center"
263
201
textAlign = { "left" } >
264
- < Button variant = "outlined"
265
- size = { "large" }
266
- color = "primary"
267
- disabled = { disabled }
268
- startIcon = { < AddIcon /> }
269
- onClick = { insertInEnd } >
202
+ < Button
203
+ variant = { small ? "text" : "outlined" }
204
+ size = { small ? "small" : "large" }
205
+ color = "primary"
206
+ disabled = { disabled }
207
+ startIcon = { < AddIcon /> }
208
+ onClick = { insertInEnd } >
270
209
{ addLabel ?? "Add" }
271
210
</ Button >
272
211
</ Box > }
@@ -280,65 +219,43 @@ function ArrayContainerInternal<T>({
280
219
type ArrayContainerItemProps = {
281
220
provided : DraggableProvided ,
282
221
index : number ,
283
- name : string ,
284
222
internalId : number ,
285
223
small ?: boolean ,
286
224
disabled : boolean ,
287
225
buildEntry : ( index : number , internalId : number ) => React . ReactNode ,
288
226
remove : ( index : number ) => void ,
289
227
copy : ( index : number ) => void ,
290
- animatedIds : React . MutableRefObject < Set < number > > ,
291
228
} ;
292
229
293
- function ArrayContainerItem ( {
294
- provided,
295
- index,
296
- name,
297
- internalId,
298
- small,
299
- disabled,
300
- buildEntry,
301
- remove,
302
- copy,
303
- animatedIds
304
- } : ArrayContainerItemProps ) {
230
+ export function ArrayContainerItem ( {
231
+ provided,
232
+ index,
233
+ internalId,
234
+ small,
235
+ disabled,
236
+ buildEntry,
237
+ remove,
238
+ copy,
239
+ } : ArrayContainerItemProps ) {
305
240
306
241
const [ measureRef , bounds ] = useMeasure ( ) ;
307
- const smallContent = bounds . height < 100 ;
308
-
309
- // WIP of animation
310
- // const initiallyDisplayed = animatedIds.current.has(internalId);
311
- // const [displayed, setDisplayed] = useState(initiallyDisplayed);
312
- // useEffect(() => {
313
- // setDisplayed(true);
314
- // animatedIds.current.add(internalId);
315
- // }, []);
316
- //
317
- // console.log(animatedIds.current);
242
+ const contentOverflow = ! small && bounds . height < 100 ;
318
243
319
244
return < Box
320
245
ref = { provided . innerRef }
321
246
{ ...provided . draggableProps }
322
- style = {
323
- provided . draggableProps . style
324
- }
247
+ style = { provided . draggableProps . style }
325
248
sx = { theme => ( {
326
249
marginBottom : 1 ,
327
250
borderRadius : theme . shape . borderRadius ,
328
251
opacity : 1
329
252
} ) }
330
253
>
331
- { /*<Collapse*/ }
332
- { /* in={displayed}*/ }
333
- { /* appear={false}*/ }
334
- { /* enter={initiallyDisplayed}*/ }
335
- { /* timeout={500}>*/ }
336
254
< Box
337
255
display = "flex" >
338
256
< Box ref = { measureRef }
339
257
flexGrow = { 1 }
340
258
width = { "calc(100% - 48px)" }
341
- // key={`field_${name}_entryValue`}
342
259
>
343
260
{ buildEntry ( index , internalId ) }
344
261
</ Box >
@@ -347,24 +264,23 @@ function ArrayContainerItem({
347
264
remove = { remove }
348
265
index = { index }
349
266
provided = { provided }
350
- smallContent = { smallContent }
267
+ contentOverflow = { contentOverflow }
351
268
copy = { copy } />
352
269
</ Box >
353
- { /*</Collapse>*/ }
354
270
</ Box > ;
355
271
}
356
272
357
- function ArrayItemOptions ( {
358
- direction,
359
- disabled,
360
- remove,
361
- index,
362
- provided,
363
- copy,
364
- smallContent
365
- } : {
273
+ export function ArrayItemOptions ( {
274
+ direction,
275
+ disabled,
276
+ remove,
277
+ index,
278
+ provided,
279
+ copy,
280
+ contentOverflow
281
+ } : {
366
282
direction ?: "row" | "column" ,
367
- smallContent : boolean ,
283
+ contentOverflow : boolean ,
368
284
disabled : boolean ,
369
285
remove : ( index : number ) => void ,
370
286
index : number ,
@@ -384,7 +300,7 @@ function ArrayItemOptions({
384
300
} , [ setAnchorEl ] ) ;
385
301
386
302
return < Box display = "flex"
387
- flexDirection = { direction ?? "column" }
303
+ flexDirection = { direction === "row" ? "row-reverse" : "column" }
388
304
sx = { {
389
305
pl : 1
390
306
} }
@@ -405,7 +321,7 @@ function ArrayItemOptions({
405
321
</ Tooltip >
406
322
</ div >
407
323
408
- { ! smallContent && < >
324
+ { ! contentOverflow && < >
409
325
< Tooltip
410
326
title = "Remove"
411
327
placement = { direction === "column" ? "left" : undefined } >
@@ -433,7 +349,7 @@ function ArrayItemOptions({
433
349
</ Tooltip >
434
350
</ > }
435
351
436
- { smallContent && < >
352
+ { contentOverflow && < >
437
353
< IconButton onClick = { openMenu }
438
354
size = { "small" } >
439
355
< MoreVert />
@@ -462,6 +378,13 @@ function ArrayItemOptions({
462
378
</ Box > ;
463
379
}
464
380
465
- function getRandomId ( ) {
381
+ function arrayMove ( value : any [ ] , sourceIndex : number , destinationIndex : number ) {
382
+ const result = Array . from ( value ) ;
383
+ const [ removed ] = result . splice ( sourceIndex , 1 ) ;
384
+ result . splice ( destinationIndex , 0 , removed ) ;
385
+ return result ;
386
+ }
387
+
388
+ export function getRandomId ( ) {
466
389
return Math . floor ( Math . random ( ) * Math . floor ( Number . MAX_SAFE_INTEGER ) ) ;
467
390
}
0 commit comments