@@ -17,8 +17,18 @@ import {
17
17
IconButton ,
18
18
} from 'decap-cms-ui-default' ;
19
19
import { basename } from 'decap-cms-lib-util' ;
20
- import { SortableContainer , SortableElement } from 'react-sortable-hoc' ;
21
20
import { arrayMoveImmutable as arrayMove } from 'array-move' ;
21
+ import {
22
+ DndContext ,
23
+ MouseSensor ,
24
+ TouchSensor ,
25
+ closestCenter ,
26
+ useSensor ,
27
+ useSensors ,
28
+ } from '@dnd-kit/core' ;
29
+ import { SortableContext , useSortable } from '@dnd-kit/sortable' ;
30
+ import { CSS } from '@dnd-kit/utilities' ;
31
+ import { restrictToParentElement } from '@dnd-kit/modifiers' ;
22
32
23
33
const MAX_DISPLAY_LENGTH = 50 ;
24
34
@@ -64,9 +74,20 @@ function SortableImageButtons({ onRemove, onReplace }) {
64
74
) ;
65
75
}
66
76
67
- const SortableImage = SortableElement ( ( { itemValue, getAsset, field, onRemove, onReplace } ) => {
77
+ function SortableImage ( props ) {
78
+ const { attributes, listeners, setNodeRef, transform, transition } = useSortable ( {
79
+ id : props . id ,
80
+ } ) ;
81
+
82
+ const style = {
83
+ transform : CSS . Transform . toString ( transform ) ,
84
+ transition,
85
+ } ;
86
+
87
+ const { itemValue, getAsset, field, onRemove, onReplace } = props ;
88
+
68
89
return (
69
- < div >
90
+ < div ref = { setNodeRef } style = { style } { ... attributes } { ... listeners } >
70
91
< ImageWrapper sortable >
71
92
< Image src = { getAsset ( itemValue , field ) || '' } />
72
93
</ ImageWrapper >
@@ -77,32 +98,61 @@ const SortableImage = SortableElement(({ itemValue, getAsset, field, onRemove, o
77
98
> </ SortableImageButtons >
78
99
</ div >
79
100
) ;
80
- } ) ;
81
-
82
- const SortableMultiImageWrapper = SortableContainer (
83
- ( { items, getAsset, field, onRemoveOne, onReplaceOne } ) => {
84
- return (
85
- < div
86
- css = { css `
87
- display: flex;
88
- flex-wrap: wrap;
89
- ` }
101
+ }
102
+
103
+ function SortableMultiImageWrapper ( {
104
+ items,
105
+ getAsset,
106
+ field,
107
+ onSortEnd,
108
+ onRemoveOne,
109
+ onReplaceOne,
110
+ } ) {
111
+ const activationConstraint = { distance : 4 } ;
112
+ const sensors = useSensors (
113
+ useSensor ( MouseSensor , { activationConstraint } ) ,
114
+ useSensor ( TouchSensor , { activationConstraint } ) ,
115
+ ) ;
116
+
117
+ function handleSortEnd ( { active, over } ) {
118
+ onSortEnd ( {
119
+ oldIndex : items . findIndex ( item => item . id === active . id ) ,
120
+ newIndex : items . findIndex ( item => item . id === over . id ) ,
121
+ } ) ;
122
+ }
123
+
124
+ return (
125
+ < div
126
+ // eslint-disable-next-line react/no-unknown-property
127
+ css = { css `
128
+ display: flex;
129
+ flex-wrap: wrap;
130
+ ` }
131
+ >
132
+ < DndContext
133
+ modifiers = { [ restrictToParentElement ] }
134
+ collisionDetection = { closestCenter }
135
+ sensors = { sensors }
136
+ onDragEnd = { handleSortEnd }
90
137
>
91
- { items . map ( ( itemValue , index ) => (
92
- < SortableImage
93
- key = { `item-${ itemValue } ` }
94
- index = { index }
95
- itemValue = { itemValue }
96
- getAsset = { getAsset }
97
- field = { field }
98
- onRemove = { onRemoveOne ( index ) }
99
- onReplace = { onReplaceOne ( index ) }
100
- />
101
- ) ) }
102
- </ div >
103
- ) ;
104
- } ,
105
- ) ;
138
+ < SortableContext items = { items } >
139
+ { items . map ( ( item , index ) => (
140
+ < SortableImage
141
+ key = { item . id }
142
+ id = { item . id }
143
+ index = { index }
144
+ itemValue = { item . value }
145
+ getAsset = { getAsset }
146
+ field = { field }
147
+ onRemove = { onRemoveOne ( index ) }
148
+ onReplace = { onReplaceOne ( index ) }
149
+ > </ SortableImage >
150
+ ) ) }
151
+ </ SortableContext >
152
+ </ DndContext >
153
+ </ div >
154
+ ) ;
155
+ }
106
156
107
157
const FileLink = styled . a `
108
158
margin-bottom: 20px;
@@ -152,7 +202,20 @@ function sizeOfValue(value) {
152
202
}
153
203
154
204
function valueListToArray ( value ) {
155
- return List . isList ( value ) ? value . toArray ( ) : value ;
205
+ return List . isList ( value ) ? value . toArray ( ) : value ?? [ ] ;
206
+ }
207
+
208
+ function valueListToSortableArray ( value ) {
209
+ if ( ! isMultiple ( value ) ) {
210
+ return value ;
211
+ }
212
+
213
+ const valueArray = valueListToArray ( value ) . map ( value => ( {
214
+ id : uuid ( ) ,
215
+ value,
216
+ } ) ) ;
217
+
218
+ return valueArray ;
156
219
}
157
220
158
221
const warnDeprecatedOptions = once ( field =>
@@ -259,7 +322,7 @@ export default function withFileControl({ forImage } = {}) {
259
322
} ;
260
323
261
324
onRemoveOne = index => ( ) => {
262
- const { value } = this . props ;
325
+ const value = valueListToArray ( this . props . value ) ;
263
326
value . splice ( index , 1 ) ;
264
327
return this . props . onChange ( sizeOfValue ( value ) > 0 ? [ ...value ] : null ) ;
265
328
} ;
@@ -346,11 +409,11 @@ export default function withFileControl({ forImage } = {}) {
346
409
347
410
renderImages = ( ) => {
348
411
const { getAsset, value, field } = this . props ;
349
-
412
+ const items = valueListToSortableArray ( value ) ;
350
413
if ( isMultiple ( value ) ) {
351
414
return (
352
415
< SortableMultiImageWrapper
353
- items = { value }
416
+ items = { items }
354
417
onSortEnd = { this . onSortEnd }
355
418
onRemoveOne = { this . onRemoveOne }
356
419
onReplaceOne = { this . onReplaceOne }
0 commit comments