1
- import React , { useEffect , useState , useRef , useMemo } from "react" ;
2
- import { DndProvider } from "react-dnd" ;
3
- import { HTML5Backend } from "react-dnd-html5-backend" ;
1
+ import React , { useMemo , forwardRef } from "react" ;
4
2
import { MarginProps } from "styled-system" ;
5
-
6
3
import invariant from "invariant" ;
7
4
import { filterStyledSystemMarginProps } from "../../style/utils" ;
8
5
import DraggableItem from "./draggable-item/draggable-item.component" ;
9
- import DropTarget from "./__internal__/drop-target.component " ;
6
+ import { StyledDraggableContainer } from "./draggable-item/draggable-item.style " ;
10
7
import { TagProps } from "../../__internal__/utils/helpers/tags" ;
8
+ import useDraggable , { UseDraggableHandle } from "../../hooks/useDraggable" ;
11
9
12
10
export interface DraggableContainerProps extends MarginProps , TagProps {
13
11
/** Callback fired when order is changed */
@@ -28,108 +26,56 @@ export interface DraggableContainerProps extends MarginProps, TagProps {
28
26
flexDirection ?: "row" | "row-reverse" ;
29
27
}
30
28
31
- const DraggableContainer = ( {
32
- "data-element" : dataElement ,
33
- "data-role" : dataRole = "draggable-container" ,
34
- children,
35
- getOrder,
36
- flexDirection = "row" ,
37
- ...rest
38
- } : DraggableContainerProps ) : JSX . Element => {
39
- const [ draggableItems , setDraggableItems ] = useState (
40
- React . Children . toArray ( children ) ,
41
- ) ;
42
-
43
- const hasProperChildren = useMemo ( ( ) => {
44
- const invalidChild = React . Children . toArray ( children ) . find (
45
- ( child ) =>
46
- ! React . isValidElement ( child ) ||
47
- ( child . type as React . FunctionComponent ) . displayName !== "DraggableItem" ,
48
- ) ;
49
- return ! invalidChild ;
50
- } , [ children ] ) ;
51
-
52
- // `<DraggableItem />` is required to make `Draggable` work
53
- invariant (
54
- hasProperChildren ,
55
- `\`${ DraggableContainer . displayName } \` only accepts children of type \`${ DraggableItem . displayName } \`.` ,
56
- ) ;
57
-
58
- const isFirstRender = useRef ( true ) ;
59
-
60
- useEffect ( ( ) => {
61
- if ( ! isFirstRender . current ) {
62
- setDraggableItems ( React . Children . toArray ( children ) ) ;
63
- } else {
64
- isFirstRender . current = false ;
65
- }
66
- } , [ children ] ) ;
67
-
68
- const findItem = ( id : string | number ) => {
69
- const draggableItem = draggableItems . filter ( ( item ) => {
70
- return React . isValidElement ( item ) && `${ item . props . id } ` === id ;
71
- } ) [ 0 ] ;
72
-
73
- return {
74
- draggableItem,
75
- index : draggableItems . indexOf ( draggableItem ) ,
76
- } ;
77
- } ;
78
-
79
- const moveItem = ( id : string , atIndex : number ) => {
80
- const { draggableItem, index } = findItem ( id ) ;
81
-
82
- // istanbul ignore if
83
- if ( ! draggableItem ) return ;
84
-
85
- const copyOfDraggableItems = [ ...draggableItems ] ;
86
- copyOfDraggableItems . splice ( index , 1 ) ;
87
- copyOfDraggableItems . splice ( atIndex , 0 , draggableItem ) ;
88
- setDraggableItems ( copyOfDraggableItems ) ;
89
- } ;
90
-
91
- const getItemsId = ( item ?: string | number ) => {
92
- if ( ! getOrder ) {
93
- return ;
94
- }
95
-
96
- const draggableItemIds = draggableItems . map (
97
- ( draggableItem ) => ( draggableItem as { props : { id : string } } ) . props . id ,
29
+ const DraggableContainer = forwardRef <
30
+ UseDraggableHandle ,
31
+ DraggableContainerProps
32
+ > (
33
+ (
34
+ {
35
+ "data-element" : dataElement ,
36
+ "data-role" : dataRole = "draggable-container" ,
37
+ children,
38
+ getOrder,
39
+ flexDirection = "row" ,
40
+ ...rest
41
+ } : DraggableContainerProps ,
42
+ ref ,
43
+ ) : JSX . Element => {
44
+ const hasProperChildren = useMemo ( ( ) => {
45
+ const invalidChild = React . Children . toArray ( children ) . find (
46
+ ( child ) =>
47
+ ! React . isValidElement ( child ) ||
48
+ ( child . type as React . FunctionComponent ) . displayName !==
49
+ "DraggableItem" ,
50
+ ) ;
51
+ return ! invalidChild ;
52
+ } , [ children ] ) ;
53
+
54
+ // `<DraggableItem />` is required to make `Draggable` work
55
+ invariant (
56
+ hasProperChildren ,
57
+ `\`${ DraggableContainer . displayName } \` only accepts children of type \`${ DraggableItem . displayName } \`.` ,
98
58
) ;
99
59
100
- getOrder ( draggableItemIds , item ) ;
101
- } ;
102
-
103
- const marginProps = filterStyledSystemMarginProps ( rest ) ;
104
-
105
- return (
106
- < DndProvider backend = { HTML5Backend } >
107
- < DropTarget
108
- data-element = { dataElement }
109
- data-role = { dataRole }
110
- getOrder = { getItemsId }
111
- { ...marginProps }
112
- >
113
- { draggableItems . map ( ( item ) => {
114
- return (
115
- React . isValidElement ( item ) &&
116
- React . cloneElement (
117
- item as React . ReactElement ,
118
- {
119
- id : `${ item . props . id } ` ,
120
- findItem,
121
- moveItem,
122
- flexDirection,
123
- } ,
124
- [ item . props . children ] ,
125
- )
126
- ) ;
127
- } ) }
128
- </ DropTarget >
129
- </ DndProvider >
130
- ) ;
131
- } ;
60
+ const marginProps = filterStyledSystemMarginProps ( rest ) ;
61
+ const items = Array . isArray ( children ) ? children : [ children ] ;
62
+
63
+ const { draggableElement } = useDraggable ( {
64
+ draggableItems : items ,
65
+ containerNode : StyledDraggableContainer ,
66
+ getOrder,
67
+ ref,
68
+ containerProps : {
69
+ "data-element" : dataElement ,
70
+ "data-role" : dataRole ,
71
+ flexDirection,
72
+ ...marginProps ,
73
+ } ,
74
+ } ) ;
75
+
76
+ return draggableElement ;
77
+ } ,
78
+ ) ;
132
79
133
80
DraggableContainer . displayName = "DraggableContainer" ;
134
-
135
81
export default DraggableContainer ;
0 commit comments