11import { default as Pagination } from "antd/es/pagination" ;
22import { EditorContext } from "comps/editorState" ;
33import { BackgroundColorContext } from "comps/utils/backgroundColorContext" ;
4- import _ from "lodash" ;
4+ import _ , { findIndex } from "lodash" ;
55import { ConstructorToView , deferAction } from "lowcoder-core" ;
6- import { HintPlaceHolder , ScrollBar , pageItemRender } from "lowcoder-design" ;
6+ import { DragIcon , HintPlaceHolder , ScrollBar , pageItemRender } from "lowcoder-design" ;
77import { RefObject , useContext , createContext , useMemo , useRef , useEffect } from "react" ;
88import ReactResizeDetector from "react-resize-detector" ;
99import styled from "styled-components" ;
@@ -22,6 +22,11 @@ import { useMergeCompStyles } from "@lowcoder-ee/util/hooks";
2222import { childrenToProps } from "@lowcoder-ee/comps/generators/multi" ;
2323import { AnimationStyleType } from "@lowcoder-ee/comps/controls/styleControlConstants" ;
2424import { 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" ;
2530
2631const ListViewWrapper = styled . div < { $style : any ; $paddingWidth : string , $animationStyle :AnimationStyleType } > `
2732 height: 100%;
@@ -63,6 +68,22 @@ const ListOrientationWrapper = styled.div<{
6368 flex-direction: ${ ( props ) => ( props . $isHorizontal ? "row" : "column" ) } ;
6469` ;
6570
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+
6687type MinHorizontalWidthContextType = {
6788 horizontalWidth : string ,
6889 minHorizontalWidth ?: string ,
@@ -73,19 +94,30 @@ const MinHorizontalWidthContext = createContext<MinHorizontalWidthContextType>({
7394 minHorizontalWidth : '100px' ,
7495} ) ;
7596
76- const ContainerInListView = ( props : ContainerBaseProps ) => {
97+ const ContainerInListView = ( props : ContainerBaseProps & { itemIdx : number } ) => {
7798 const {
7899 horizontalWidth,
79100 minHorizontalWidth
80101 } = useContext ( MinHorizontalWidthContext ) ;
81102
103+ const { attributes, listeners, setNodeRef, transform, transition } = useSortable ( {
104+ id : String ( props . itemIdx ) ,
105+ } ) ;
106+
82107 return (
83108 < div
109+ ref = { setNodeRef }
84110 style = { {
85111 width : horizontalWidth ,
86112 minWidth : minHorizontalWidth || '0px' ,
113+ transform : CSS . Transform . toString ( transform ) ,
114+ transition,
115+ display : 'flex' ,
116+ flexWrap : 'nowrap' ,
117+ alignItems : 'center' ,
87118 } }
88119 >
120+ { < StyledDragIcon { ...attributes } { ...listeners } /> }
89121 < InnerGrid
90122 { ...props }
91123 emptyRows = { 15 }
@@ -133,35 +165,36 @@ function ListItem({
133165 // }, []);
134166
135167 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",
140188 } }
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 }
165198 />
166199 </ MinHorizontalWidthContext . Provider >
167200 ) ;
@@ -190,6 +223,7 @@ export function ListView(props: Props) {
190223 ( ) => getData ( children . noOfRows . getView ( ) ) ,
191224 [ children . noOfRows ]
192225 ) ;
226+ const listData = useMemo ( ( ) => children . listData . getView ( ) , [ children . listData ] ) ;
193227 const horizontalGridCells = useMemo ( ( ) => children . horizontalGridCells . getView ( ) , [ children . horizontalGridCells ] ) ;
194228 const autoHeight = useMemo ( ( ) => children . autoHeight . getView ( ) , [ children . autoHeight ] ) ;
195229 const showHorizontalScrollbar = useMemo ( ( ) => children . showHorizontalScrollbar . getView ( ) , [ children . showHorizontalScrollbar ] ) ;
@@ -213,6 +247,11 @@ export function ListView(props: Props) {
213247 total,
214248 } ;
215249 } , [ children . pagination , totalCount ] ) ;
250+
251+ useEffect ( ( ) => {
252+ children . listData . dispatchChangeValueAction ( data ) ;
253+ } , [ JSON . stringify ( data ) ] ) ;
254+
216255 const style = children . style . getView ( ) ;
217256 const animationStyle = children . animationStyle . getView ( ) ;
218257
@@ -229,6 +268,7 @@ export function ListView(props: Props) {
229268 // log.log("List. listHeight: ", listHeight, " minHeight: ", minHeight);
230269 const renders = _ . range ( 0 , noOfRows ) . map ( ( rowIdx ) => {
231270 // 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 ) ;
232272 const render = (
233273 < div
234274 key = { rowIdx }
@@ -238,7 +278,7 @@ export function ListView(props: Props) {
238278 } }
239279 >
240280 < FlexWrapper >
241- { _ . range ( 0 , noOfColumns ) . map ( ( colIdx ) => {
281+ { items . map ( ( colIdx ) => {
242282 const itemIdx = rowIdx * noOfColumns + colIdx + pageInfo . offset ;
243283 if (
244284 itemIdx >= pageInfo . total ||
@@ -250,7 +290,7 @@ export function ListView(props: Props) {
250290 const containerProps = containerFn (
251291 {
252292 [ itemIndexName ] : itemIdx ,
253- [ itemDataName ] : getCurrentItemParams ( data , itemIdx )
293+ [ itemDataName ] : getCurrentItemParams ( listData as JSONObject [ ] , itemIdx )
254294 } ,
255295 String ( itemIdx )
256296 ) . getView ( ) ;
@@ -259,6 +299,7 @@ export function ListView(props: Props) {
259299 deferAction ( ContextContainerComp . batchDeleteAction ( [ String ( itemIdx ) ] ) )
260300 ) ;
261301 } ;
302+
262303 return (
263304 < ListItem
264305 key = { itemIdx }
@@ -278,6 +319,7 @@ export function ListView(props: Props) {
278319 </ FlexWrapper >
279320 </ div >
280321 ) ;
322+
281323 return render ;
282324 } ) ;
283325
@@ -289,6 +331,23 @@ export function ListView(props: Props) {
289331
290332 useMergeCompStyles ( childrenProps , comp . dispatch ) ;
291333
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+
292351 // log.debug("renders: ", renders);
293352 return (
294353 < BackgroundColorContext . Provider value = { style . background } >
@@ -306,7 +365,15 @@ export function ListView(props: Props) {
306365 $isGrid = { noOfColumns > 1 }
307366 $autoHeight = { autoHeight }
308367 >
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 >
310377 </ ListOrientationWrapper >
311378 ) }
312379 >
0 commit comments