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,10 @@ 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 } from "@dnd-kit/sortable" ;
27+ import { CSS } from "@dnd-kit/utilities" ;
28+ import { JSONObject } from "@lowcoder-ee/index.sdk" ;
2529
2630const ListViewWrapper = styled . div < { $style : any ; $paddingWidth : string , $animationStyle :AnimationStyleType } > `
2731 height: 100%;
@@ -63,6 +67,22 @@ const ListOrientationWrapper = styled.div<{
6367 flex-direction: ${ ( props ) => ( props . $isHorizontal ? "row" : "column" ) } ;
6468` ;
6569
70+ const StyledDragIcon = styled ( DragIcon ) `
71+ height: 16px;
72+ width: 16px;
73+ color: #8b8fa3;
74+
75+ &:hover {
76+ cursor: grab;
77+ outline: none;
78+ }
79+
80+ &:focus {
81+ cursor: grab;
82+ outline: none;
83+ }
84+ ` ;
85+
6686type MinHorizontalWidthContextType = {
6787 horizontalWidth : string ,
6888 minHorizontalWidth ?: string ,
@@ -73,19 +93,48 @@ const MinHorizontalWidthContext = createContext<MinHorizontalWidthContextType>({
7393 minHorizontalWidth : '100px' ,
7494} ) ;
7595
76- const ContainerInListView = ( props : ContainerBaseProps ) => {
96+ const ContainerInListView = ( props : ContainerBaseProps & { itemIdx : number , enableSorting ?: boolean } ) => {
7797 const {
7898 horizontalWidth,
7999 minHorizontalWidth
80100 } = useContext ( MinHorizontalWidthContext ) ;
81101
102+ const { attributes, listeners, setNodeRef, transform, transition } = useSortable ( {
103+ id : String ( props . itemIdx ) ,
104+ } ) ;
105+
106+ if ( ! props . enableSorting ) {
107+ return (
108+ < div
109+ style = { {
110+ width : horizontalWidth ,
111+ minWidth : minHorizontalWidth || '0px' ,
112+ } }
113+ >
114+ < InnerGrid
115+ { ...props }
116+ emptyRows = { 15 }
117+ containerPadding = { [ 4 , 4 ] }
118+ hintPlaceholder = { HintPlaceHolder }
119+ />
120+ </ div >
121+ )
122+ }
123+
82124 return (
83125 < div
126+ ref = { setNodeRef }
84127 style = { {
85128 width : horizontalWidth ,
86129 minWidth : minHorizontalWidth || '0px' ,
130+ transform : CSS . Transform . toString ( transform ) ,
131+ transition,
132+ display : 'flex' ,
133+ flexWrap : 'nowrap' ,
134+ alignItems : 'center' ,
87135 } }
88136 >
137+ { < StyledDragIcon { ...attributes } { ...listeners } /> }
89138 < InnerGrid
90139 { ...props }
91140 emptyRows = { 15 }
@@ -107,6 +156,7 @@ type ListItemProps = {
107156 unMountFn ?: ( ) => void ;
108157 minHorizontalWidth ?: string ;
109158 horizontalWidth : string ;
159+ enableSorting ?: boolean ;
110160} ;
111161
112162function ListItem ( {
@@ -122,6 +172,7 @@ function ListItem({
122172 scrollContainerRef,
123173 minHeight,
124174 horizontalGridCells,
175+ enableSorting,
125176 } = props ;
126177
127178 // disable the unmount function to save user's state with pagination
@@ -133,35 +184,37 @@ function ListItem({
133184 // }, []);
134185
135186 return (
136- < MinHorizontalWidthContext . Provider
137- value = { {
138- horizontalWidth,
139- minHorizontalWidth
187+ < MinHorizontalWidthContext . Provider
188+ value = { {
189+ horizontalWidth,
190+ minHorizontalWidth
191+ } }
192+ >
193+ < ContainerInListView
194+ itemIdx = { itemIdx }
195+ layout = { containerProps . layout }
196+ items = { gridItemCompToGridItems ( containerProps . items ) }
197+ horizontalGridCells = { horizontalGridCells }
198+ positionParams = { containerProps . positionParams }
199+ // all layout changes should only reflect on the commonContainer
200+ dispatch = { itemIdx === offset ? containerProps . dispatch : _ . noop }
201+ style = { {
202+ height : "100%" ,
203+ // in case of horizontal mode, minHorizontalWidth is 0px
204+ width : minHorizontalWidth || '100%' ,
205+ backgroundColor : "transparent" ,
206+ // flex: "auto",
140207 } }
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 }
208+ autoHeight = { autoHeight }
209+ isDroppable = { itemIdx === offset }
210+ isDraggable = { itemIdx === offset }
211+ isResizable = { itemIdx === offset }
212+ isSelectable = { itemIdx === offset }
213+ scrollContainerRef = { scrollContainerRef }
214+ overflow = { "hidden" }
215+ minHeight = { minHeight }
216+ enableGridLines = { true }
217+ enableSorting = { enableSorting }
165218 />
166219 </ MinHorizontalWidthContext . Provider >
167220 ) ;
@@ -190,6 +243,7 @@ export function ListView(props: Props) {
190243 ( ) => getData ( children . noOfRows . getView ( ) ) ,
191244 [ children . noOfRows ]
192245 ) ;
246+ const listData = useMemo ( ( ) => children . listData . getView ( ) , [ children . listData ] ) ;
193247 const horizontalGridCells = useMemo ( ( ) => children . horizontalGridCells . getView ( ) , [ children . horizontalGridCells ] ) ;
194248 const autoHeight = useMemo ( ( ) => children . autoHeight . getView ( ) , [ children . autoHeight ] ) ;
195249 const showHorizontalScrollbar = useMemo ( ( ) => children . showHorizontalScrollbar . getView ( ) , [ children . showHorizontalScrollbar ] ) ;
@@ -213,6 +267,13 @@ export function ListView(props: Props) {
213267 total,
214268 } ;
215269 } , [ children . pagination , totalCount ] ) ;
270+
271+ const enableSorting = useMemo ( ( ) => children . enableSorting . getView ( ) , [ children . enableSorting ] ) ;
272+
273+ useEffect ( ( ) => {
274+ children . listData . dispatchChangeValueAction ( data ) ;
275+ } , [ JSON . stringify ( data ) ] ) ;
276+
216277 const style = children . style . getView ( ) ;
217278 const animationStyle = children . animationStyle . getView ( ) ;
218279
@@ -229,6 +290,7 @@ export function ListView(props: Props) {
229290 // log.log("List. listHeight: ", listHeight, " minHeight: ", minHeight);
230291 const renders = _ . range ( 0 , noOfRows ) . map ( ( rowIdx ) => {
231292 // log.log("renders. i: ", i, "containerProps: ", containerProps, " text: ", Object.values(containerProps.items as Record<string, any>)[0].children.comp.children.text);
293+ const items = _ . range ( 0 , noOfColumns ) ;
232294 const render = (
233295 < div
234296 key = { rowIdx }
@@ -238,7 +300,7 @@ export function ListView(props: Props) {
238300 } }
239301 >
240302 < FlexWrapper >
241- { _ . range ( 0 , noOfColumns ) . map ( ( colIdx ) => {
303+ { items . map ( ( colIdx ) => {
242304 const itemIdx = rowIdx * noOfColumns + colIdx + pageInfo . offset ;
243305 if (
244306 itemIdx >= pageInfo . total ||
@@ -250,7 +312,7 @@ export function ListView(props: Props) {
250312 const containerProps = containerFn (
251313 {
252314 [ itemIndexName ] : itemIdx ,
253- [ itemDataName ] : getCurrentItemParams ( data , itemIdx )
315+ [ itemDataName ] : getCurrentItemParams ( listData as JSONObject [ ] , itemIdx )
254316 } ,
255317 String ( itemIdx )
256318 ) . getView ( ) ;
@@ -259,6 +321,7 @@ export function ListView(props: Props) {
259321 deferAction ( ContextContainerComp . batchDeleteAction ( [ String ( itemIdx ) ] ) )
260322 ) ;
261323 } ;
324+
262325 return (
263326 < ListItem
264327 key = { itemIdx }
@@ -272,12 +335,14 @@ export function ListView(props: Props) {
272335 unMountFn = { unMountFn }
273336 horizontalWidth = { `${ 100 / noOfColumns } %` }
274337 minHorizontalWidth = { horizontal ? minHorizontalWidth : undefined }
338+ enableSorting = { enableSorting }
275339 />
276340 ) ;
277341 } ) }
278342 </ FlexWrapper >
279343 </ div >
280344 ) ;
345+
281346 return render ;
282347 } ) ;
283348
@@ -289,6 +354,23 @@ export function ListView(props: Props) {
289354
290355 useMergeCompStyles ( childrenProps , comp . dispatch ) ;
291356
357+ const handleDragEnd = ( e : { active : { id : string } ; over : { id : string } | null } ) => {
358+ if ( ! e . over ) {
359+ return ;
360+ }
361+ const fromIndex = Number ( e . active . id ) ;
362+ const toIndex = Number ( e . over . id ) ;
363+ if ( fromIndex < 0 || toIndex < 0 || fromIndex === toIndex ) {
364+ return ;
365+ }
366+
367+ const newData = [ ...listData ] ;
368+ const [ movedItem ] = newData . splice ( fromIndex , 1 ) ;
369+ newData . splice ( toIndex , 0 , movedItem ) ;
370+
371+ children . listData . dispatchChangeValueAction ( newData ) ;
372+ } ;
373+
292374 // log.debug("renders: ", renders);
293375 return (
294376 < BackgroundColorContext . Provider value = { style . background } >
@@ -306,7 +388,20 @@ export function ListView(props: Props) {
306388 $isGrid = { noOfColumns > 1 }
307389 $autoHeight = { autoHeight }
308390 >
309- { renders }
391+ { ! enableSorting
392+ ? renders
393+ : (
394+ < DndContext onDragEnd = { handleDragEnd } >
395+ < SortableContext
396+ items = {
397+ _ . range ( 0 , totalCount ) . map ( ( colIdx ) => String ( colIdx ) )
398+ }
399+ >
400+ { renders }
401+ </ SortableContext >
402+ </ DndContext >
403+ )
404+ }
310405 </ ListOrientationWrapper >
311406 ) }
312407 >
0 commit comments