6
6
< meta name ="viewport " content ="width=device-width, initial-scale=1.0 " />
7
7
< title > Gridstack.js React integration example</ title >
8
8
< link rel ="stylesheet " href ="demo.css " />
9
+ < link rel ="stylesheet " href ="../dist/gridstack-extra.css "/>
9
10
< script src ="../dist/gridstack-all.js "> </ script >
10
11
11
- <!-- Scripts to use react inside html -->
12
- < script src ="https://unpkg.com/react@16/umd/react.production.min .js "> </ script >
13
- < script src ="https://unpkg.com/react-dom@16/umd/react-dom.production.min .js "> </ script >
14
- < script src ="https://unpkg.com/babel-standalone@6.15.0/babel.min. js "> </ script >
12
+ <!-- Scripts to use react inside html - DEVELOPMENT FILES - ->
13
+ < script src ="https://unpkg.com/react@16/umd/react.development .js "> </ script >
14
+ < script src ="https://unpkg.com/react-dom@16/umd/react-dom.development .js "> </ script >
15
+ < script src ="https://unpkg.com/babel-standalone@6.15.0/babel.js "> </ script >
15
16
</ head >
16
17
17
18
< body >
@@ -22,8 +23,12 @@ <h2>Controlled stack</h2>
22
23
</ body >
23
24
24
25
< script type ="text/babel ">
26
+ /***********************************************************************************************************/
27
+ /********************************* NOT IDEAL - see comments below line 111) ********************************/
28
+ /***********************************************************************************************************/
29
+
25
30
const { useState, useEffect, useLayoutEffect, createRef, useRef } = React
26
- const Item = ( { id } ) => < div > I am item: { id } </ div >
31
+ const Item = ( { id } ) => < div > { id } </ div >
27
32
28
33
//
29
34
// Controlled example
@@ -40,45 +45,57 @@ <h2>Controlled stack</h2>
40
45
}
41
46
42
47
useLayoutEffect ( ( ) => {
43
- gridRef . current =
44
- // gridRef.current ||
45
- GridStack . init (
48
+ if ( ! gridRef . current ) {
49
+ // no need to init twice (would will return same grid) or register dup events
50
+ const grid = gridRef . current = GridStack . init (
46
51
{
47
52
float : true ,
48
53
acceptWidgets : true ,
54
+ disableOneColumnMode : true , // side-by-side and fever columns to fit smaller screens
55
+ column : 6 ,
56
+ minRow : 1 ,
49
57
} ,
50
58
`.controlled-${ id } `
51
59
)
52
- const grid = gridRef . current
53
-
54
- grid . on ( 'removed' , ( e , el ) => {
55
- const elId = Array . isArray ( el ) ? el [ el . length - 1 ] . id : el . id ;
56
- removeItem ( elId . split ( ':' ) [ 1 ] )
57
- } )
58
-
59
- grid . on ( 'dropped' , ( e , p , n ) => {
60
- console . log ( 'dropped' , p , n )
61
- // Remove "placeholder" item
62
- // Gridstack creates a DOM element for dropped items, those need to be removed
63
- // as they are rendered by React
64
- grid . getGridItems ( ) . forEach ( ( item ) => {
65
- const ids = item . getAttribute ( 'gs-id' ) . split ( ':' )
66
- if ( ids [ 0 ] !== id ) {
67
- grid . removeWidget ( item , true , false ) ;
68
- }
69
- } ) ;
70
- addItem ( { id : n . id . split ( ':' ) [ 1 ] , x : n . x , y : n . y , w : n . w , h : n . h } )
71
- } )
60
+ . on ( 'added' , ( ev , gsItems ) => {
61
+ if ( grid . _ignoreCB ) return ;
62
+ // remove the new element as React will re-create it again (dup) once we add to the list or we get 2 of them with same ids but different DOM el!
63
+ // TODO: this is really not ideal - we shouldn't mix React templating with GS making it own edits as those get out of sync! see comment below @111.
64
+ gsItems . forEach ( n => {
65
+ grid . removeWidget ( n . el , true , false ) ; // true=remove DOM, false=don't call use back!
66
+ // can't pass n directly even though similar structs as it has n.el.gridstackNode which gives JSON error for circular write.
67
+ addItem ( { id :n . id , x :n . x , y :n . y , w :n . w , h :n . h } ) ;
68
+ } ) ;
69
+ } )
70
+ . on ( 'removed' , ( ev , gsItems ) => {
71
+ if ( grid . _ignoreCB ) return ;
72
+ gsItems . forEach ( n => removeItem ( n . id ) ) ;
73
+ } )
74
+
75
+ } else {
76
+ //
77
+ // update existing grid layout, which is optimized to updates only diffs (will add new/delete items for examples)
78
+ //
79
+ const grid = gridRef . current ;
80
+ const layout = [ ] ;
81
+ items . forEach ( ( a ) => layout . push (
82
+ refs . current [ a . id ] . current . gridstackNode || { ...a , el : refs . current [ a . id ] . current }
83
+ ) ) ;
84
+ grid . _ignoreCB = true ; // hack: ignore added/removed since we're the one doing the update
85
+ grid . load ( layout ) ;
86
+ delete grid . _ignoreCB ;
87
+ }
72
88
73
- grid . batchUpdate ( )
74
- items . forEach ( ( a ) => {
75
- // remove existing widgets
76
- if ( refs . current [ a . id ] && refs . current [ a . id ] . current ) {
77
- grid . removeWidget ( refs . current [ a . id ] . current , false , false )
78
- }
79
- grid . makeWidget ( refs . current [ a . id ] . current )
80
- } )
81
- grid . batchUpdate ( false )
89
+ // NOTE: old code is incorrect as it re-does the GS binding, but dragged item is left behind so you get dup DOM elements with same ids
90
+ // grid.batchUpdate()
91
+ // items.forEach((a) => {
92
+ // // remove existing widgets
93
+ // if (refs.current[a.id] && refs.current[a.id].current) {
94
+ // grid.removeWidget(refs.current[a.id].current, false, false)
95
+ // }
96
+ // grid.makeWidget(refs.current[a.id].current)
97
+ // })
98
+ // grid.batchUpdate(false)
82
99
83
100
} , [ items ] )
84
101
@@ -91,12 +108,21 @@ <h2>Controlled stack</h2>
91
108
} )
92
109
93
110
return (
94
- < div style = { { width : '100%' } } >
111
+ // ********************
112
+ // NOTE: constructing DOM grid items in template when gridstack is also allowed editing (dragging between grids, or adding/removing from say a toolbar)
113
+ // is NOT A GOOD IDEA as you end up fighting between gridstack users' edits and your template items structure which are not in sync.
114
+ // At best, you end up re-creating widgets DOM (from React template) and all their content & state after a widget was inserted/re-parented by the user.
115
+ // a MUCH better way is to let GS create React components using it's API/user interactions, with only initial load() of a stored layout.
116
+ // see the upcoming Angular component wrapper that does that instead (lib author uses Angular). TBD creating React equivalent...
117
+ //
118
+ // Also templating forces you to spell out the 12+ attributes GS supports (only x,w,w,h done below) instead of passing a option structure that supports everything
119
+ // is not robust as things get added, and pollutes the DOM attr for default/missing entries, vs optimized code in GS.
120
+ // ********************
121
+ < div style = { { width : '100%' , marginRight : '10px' } } >
95
122
< div className = { `grid-stack controlled-${ id } ` } >
96
123
{ items . map ( ( item , i ) => {
97
- // console.log('render', id, item.id)
98
124
return (
99
- < div ref = { refs . current [ item . id ] } key = { `${ id } -${ item . id } ` } className = { 'grid-stack-item' } gs-id = { ` ${ id } : ${ item . id } ` } gs-w = { item . w } gs-h = { item . h } gs-x = { item . x } gs-y = { item . y } >
125
+ < div ref = { refs . current [ item . id ] } key = { `${ id } -${ item . id } ` } className = { 'grid-stack-item' } gs-id = { item . id } gs-w = { item . w } gs-h = { item . h } gs-x = { item . x } gs-y = { item . y } >
100
126
< div className = "grid-stack-item-content" >
101
127
< Item { ...item } />
102
128
</ div >
@@ -122,10 +148,10 @@ <h2>Controlled stack</h2>
122
148
id = 'gs1'
123
149
items = { items1 }
124
150
addItem = { ( item ) => {
125
- setItems1 ( [ ...items1 , item ] )
151
+ setItems1 ( items => [ ...items , item ] )
126
152
} }
127
153
removeItem = { ( id ) => {
128
- setItems1 ( items1 . filter ( i => i . id !== id ) )
154
+ setItems1 ( items => items . filter ( i => i . id !== id ) )
129
155
} }
130
156
/>
131
157
</ div >
@@ -134,10 +160,10 @@ <h2>Controlled stack</h2>
134
160
id = 'gs2'
135
161
items = { items2 }
136
162
addItem = { ( item ) => {
137
- setItems2 ( [ ...items2 , item ] )
163
+ setItems2 ( items => [ ...items , item ] )
138
164
} }
139
165
removeItem = { ( id ) => {
140
- setItems2 ( items2 . filter ( i => i . id !== id ) )
166
+ setItems2 ( items => items . filter ( i => i . id !== id ) )
141
167
} }
142
168
/>
143
169
</ div >
0 commit comments