1
1
import { default as Pagination } from "antd/es/pagination" ;
2
2
import { EditorContext } from "comps/editorState" ;
3
3
import { BackgroundColorContext } from "comps/utils/backgroundColorContext" ;
4
- import _ from "lodash" ;
4
+ import _ , { findIndex } from "lodash" ;
5
5
import { ConstructorToView , deferAction } from "lowcoder-core" ;
6
- import { HintPlaceHolder , ScrollBar , pageItemRender } from "lowcoder-design" ;
6
+ import { DragIcon , HintPlaceHolder , ScrollBar , pageItemRender } from "lowcoder-design" ;
7
7
import { RefObject , useContext , createContext , useMemo , useRef , useEffect } from "react" ;
8
8
import ReactResizeDetector from "react-resize-detector" ;
9
9
import styled from "styled-components" ;
@@ -22,6 +22,11 @@ import { useMergeCompStyles } from "@lowcoder-ee/util/hooks";
22
22
import { childrenToProps } from "@lowcoder-ee/comps/generators/multi" ;
23
23
import { AnimationStyleType } from "@lowcoder-ee/comps/controls/styleControlConstants" ;
24
24
import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils" ;
25
+ import { DndContext } from "@dnd-kit/core" ;
26
+ import { SortableContext , useSortable , verticalListSortingStrategy } from "@dnd-kit/sortable" ;
27
+ import { restrictToVerticalAxis } from "@dnd-kit/modifiers" ;
28
+ import { CSS } from "@dnd-kit/utilities" ;
29
+ import { JSONObject } from "@lowcoder-ee/index.sdk" ;
25
30
26
31
const ListViewWrapper = styled . div < { $style : any ; $paddingWidth : string , $animationStyle :AnimationStyleType } > `
27
32
height: 100%;
@@ -63,6 +68,22 @@ const ListOrientationWrapper = styled.div<{
63
68
flex-direction: ${ ( props ) => ( props . $isHorizontal ? "row" : "column" ) } ;
64
69
` ;
65
70
71
+ const StyledDragIcon = styled ( DragIcon ) `
72
+ height: 16px;
73
+ width: 16px;
74
+ color: #8b8fa3;
75
+
76
+ &:hover {
77
+ cursor: grab;
78
+ outline: none;
79
+ }
80
+
81
+ &:focus {
82
+ cursor: grab;
83
+ outline: none;
84
+ }
85
+ ` ;
86
+
66
87
type MinHorizontalWidthContextType = {
67
88
horizontalWidth : string ,
68
89
minHorizontalWidth ?: string ,
@@ -73,19 +94,30 @@ const MinHorizontalWidthContext = createContext<MinHorizontalWidthContextType>({
73
94
minHorizontalWidth : '100px' ,
74
95
} ) ;
75
96
76
- const ContainerInListView = ( props : ContainerBaseProps ) => {
97
+ const ContainerInListView = ( props : ContainerBaseProps & { itemIdx : number } ) => {
77
98
const {
78
99
horizontalWidth,
79
100
minHorizontalWidth
80
101
} = useContext ( MinHorizontalWidthContext ) ;
81
102
103
+ const { attributes, listeners, setNodeRef, transform, transition } = useSortable ( {
104
+ id : String ( props . itemIdx ) ,
105
+ } ) ;
106
+
82
107
return (
83
108
< div
109
+ ref = { setNodeRef }
84
110
style = { {
85
111
width : horizontalWidth ,
86
112
minWidth : minHorizontalWidth || '0px' ,
113
+ transform : CSS . Transform . toString ( transform ) ,
114
+ transition,
115
+ display : 'flex' ,
116
+ flexWrap : 'nowrap' ,
117
+ alignItems : 'center' ,
87
118
} }
88
119
>
120
+ { < StyledDragIcon { ...attributes } { ...listeners } /> }
89
121
< InnerGrid
90
122
{ ...props }
91
123
emptyRows = { 15 }
@@ -133,35 +165,36 @@ function ListItem({
133
165
// }, []);
134
166
135
167
return (
136
- < MinHorizontalWidthContext . Provider
137
- value = { {
138
- horizontalWidth,
139
- minHorizontalWidth
168
+ < MinHorizontalWidthContext . Provider
169
+ value = { {
170
+ horizontalWidth,
171
+ minHorizontalWidth
172
+ } }
173
+ >
174
+ < ContainerInListView
175
+ itemIdx = { itemIdx }
176
+ layout = { containerProps . layout }
177
+ items = { gridItemCompToGridItems ( containerProps . items ) }
178
+ horizontalGridCells = { horizontalGridCells }
179
+ positionParams = { containerProps . positionParams }
180
+ // all layout changes should only reflect on the commonContainer
181
+ dispatch = { itemIdx === offset ? containerProps . dispatch : _ . noop }
182
+ style = { {
183
+ height : "100%" ,
184
+ // in case of horizontal mode, minHorizontalWidth is 0px
185
+ width : minHorizontalWidth || '100%' ,
186
+ backgroundColor : "transparent" ,
187
+ // flex: "auto",
140
188
} }
141
- >
142
- < ContainerInListView
143
- layout = { containerProps . layout }
144
- items = { gridItemCompToGridItems ( containerProps . items ) }
145
- horizontalGridCells = { horizontalGridCells }
146
- positionParams = { containerProps . positionParams }
147
- // all layout changes should only reflect on the commonContainer
148
- dispatch = { itemIdx === offset ? containerProps . dispatch : _ . noop }
149
- style = { {
150
- height : "100%" ,
151
- // in case of horizontal mode, minHorizontalWidth is 0px
152
- width : minHorizontalWidth || '100%' ,
153
- backgroundColor : "transparent" ,
154
- // flex: "auto",
155
- } }
156
- autoHeight = { autoHeight }
157
- isDroppable = { itemIdx === offset }
158
- isDraggable = { itemIdx === offset }
159
- isResizable = { itemIdx === offset }
160
- isSelectable = { itemIdx === offset }
161
- scrollContainerRef = { scrollContainerRef }
162
- overflow = { "hidden" }
163
- minHeight = { minHeight }
164
- enableGridLines = { true }
189
+ autoHeight = { autoHeight }
190
+ isDroppable = { itemIdx === offset }
191
+ isDraggable = { itemIdx === offset }
192
+ isResizable = { itemIdx === offset }
193
+ isSelectable = { itemIdx === offset }
194
+ scrollContainerRef = { scrollContainerRef }
195
+ overflow = { "hidden" }
196
+ minHeight = { minHeight }
197
+ enableGridLines = { true }
165
198
/>
166
199
</ MinHorizontalWidthContext . Provider >
167
200
) ;
@@ -190,6 +223,7 @@ export function ListView(props: Props) {
190
223
( ) => getData ( children . noOfRows . getView ( ) ) ,
191
224
[ children . noOfRows ]
192
225
) ;
226
+ const listData = useMemo ( ( ) => children . listData . getView ( ) , [ children . listData ] ) ;
193
227
const horizontalGridCells = useMemo ( ( ) => children . horizontalGridCells . getView ( ) , [ children . horizontalGridCells ] ) ;
194
228
const autoHeight = useMemo ( ( ) => children . autoHeight . getView ( ) , [ children . autoHeight ] ) ;
195
229
const showHorizontalScrollbar = useMemo ( ( ) => children . showHorizontalScrollbar . getView ( ) , [ children . showHorizontalScrollbar ] ) ;
@@ -213,6 +247,11 @@ export function ListView(props: Props) {
213
247
total,
214
248
} ;
215
249
} , [ children . pagination , totalCount ] ) ;
250
+
251
+ useEffect ( ( ) => {
252
+ children . listData . dispatchChangeValueAction ( data ) ;
253
+ } , [ JSON . stringify ( data ) ] ) ;
254
+
216
255
const style = children . style . getView ( ) ;
217
256
const animationStyle = children . animationStyle . getView ( ) ;
218
257
@@ -229,6 +268,7 @@ export function ListView(props: Props) {
229
268
// log.log("List. listHeight: ", listHeight, " minHeight: ", minHeight);
230
269
const renders = _ . range ( 0 , noOfRows ) . map ( ( rowIdx ) => {
231
270
// log.log("renders. i: ", i, "containerProps: ", containerProps, " text: ", Object.values(containerProps.items as Record<string, any>)[0].children.comp.children.text);
271
+ const items = _ . range ( 0 , noOfColumns ) ;
232
272
const render = (
233
273
< div
234
274
key = { rowIdx }
@@ -238,7 +278,7 @@ export function ListView(props: Props) {
238
278
} }
239
279
>
240
280
< FlexWrapper >
241
- { _ . range ( 0 , noOfColumns ) . map ( ( colIdx ) => {
281
+ { items . map ( ( colIdx ) => {
242
282
const itemIdx = rowIdx * noOfColumns + colIdx + pageInfo . offset ;
243
283
if (
244
284
itemIdx >= pageInfo . total ||
@@ -250,7 +290,7 @@ export function ListView(props: Props) {
250
290
const containerProps = containerFn (
251
291
{
252
292
[ itemIndexName ] : itemIdx ,
253
- [ itemDataName ] : getCurrentItemParams ( data , itemIdx )
293
+ [ itemDataName ] : getCurrentItemParams ( listData as JSONObject [ ] , itemIdx )
254
294
} ,
255
295
String ( itemIdx )
256
296
) . getView ( ) ;
@@ -259,6 +299,7 @@ export function ListView(props: Props) {
259
299
deferAction ( ContextContainerComp . batchDeleteAction ( [ String ( itemIdx ) ] ) )
260
300
) ;
261
301
} ;
302
+
262
303
return (
263
304
< ListItem
264
305
key = { itemIdx }
@@ -278,6 +319,7 @@ export function ListView(props: Props) {
278
319
</ FlexWrapper >
279
320
</ div >
280
321
) ;
322
+
281
323
return render ;
282
324
} ) ;
283
325
@@ -289,6 +331,23 @@ export function ListView(props: Props) {
289
331
290
332
useMergeCompStyles ( childrenProps , comp . dispatch ) ;
291
333
334
+ const handleDragEnd = ( e : { active : { id : string } ; over : { id : string } | null } ) => {
335
+ if ( ! e . over ) {
336
+ return ;
337
+ }
338
+ const fromIndex = Number ( e . active . id ) ;
339
+ const toIndex = Number ( e . over . id ) ;
340
+ if ( fromIndex < 0 || toIndex < 0 || fromIndex === toIndex ) {
341
+ return ;
342
+ }
343
+
344
+ const newData = [ ...listData ] ;
345
+ const [ movedItem ] = newData . splice ( fromIndex , 1 ) ;
346
+ newData . splice ( toIndex , 0 , movedItem ) ;
347
+
348
+ children . listData . dispatchChangeValueAction ( newData ) ;
349
+ } ;
350
+
292
351
// log.debug("renders: ", renders);
293
352
return (
294
353
< BackgroundColorContext . Provider value = { style . background } >
@@ -306,7 +365,15 @@ export function ListView(props: Props) {
306
365
$isGrid = { noOfColumns > 1 }
307
366
$autoHeight = { autoHeight }
308
367
>
309
- { renders }
368
+ < DndContext onDragEnd = { handleDragEnd } >
369
+ < SortableContext
370
+ items = {
371
+ _ . range ( 0 , totalCount ) . map ( ( colIdx ) => String ( colIdx ) )
372
+ }
373
+ >
374
+ { renders }
375
+ </ SortableContext >
376
+ </ DndContext >
310
377
</ ListOrientationWrapper >
311
378
) }
312
379
>
0 commit comments