From 07f4f15dcbe14e09f85da2ba357795bd95a51101 Mon Sep 17 00:00:00 2001 From: Sergey Fomin Date: Fri, 13 Jan 2023 15:42:26 +0200 Subject: [PATCH 1/7] improves new react demo example --- demo/react-hooks-controlled-multiple.html | 127 +++++++++++----------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/demo/react-hooks-controlled-multiple.html b/demo/react-hooks-controlled-multiple.html index 5d92cedfd..8b8ea2047 100644 --- a/demo/react-hooks-controlled-multiple.html +++ b/demo/react-hooks-controlled-multiple.html @@ -6,7 +6,7 @@ Gridstack.js React integration example - + @@ -33,7 +33,7 @@

Controlled stack

// // Controlled example // - const ControlledStack = ({ items, addItem, removeItem, id }) => { + const ControlledStack = ({ items, addItem, removeItem, id, changeItems }) => { const refs = useRef({}) const gridRef = useRef() refs.current = {} @@ -49,7 +49,7 @@

Controlled stack

// no need to init twice (would will return same grid) or register dup events const grid = gridRef.current = GridStack.init( { - float: true, + float: false, acceptWidgets: true, disableOneColumnMode: true, // side-by-side and fever columns to fit smaller screens column: 6, @@ -57,21 +57,32 @@

Controlled stack

}, `.controlled-${id}` ) - .on('added', (ev, gsItems) => { - if (grid._ignoreCB) return; - // 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! - // 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. - gsItems.forEach(n => { - grid.removeWidget(n.el, true, false); // true=remove DOM, false=don't call use back! - // can't pass n directly even though similar structs as it has n.el.gridstackNode which gives JSON error for circular write. - addItem({id:n.id, x:n.x, y:n.y, w:n.w, h:n.h}); - }); - }) - .on('removed', (ev, gsItems) => { - if (grid._ignoreCB) return; - gsItems.forEach(n => removeItem(n.id)); - }) - + .on('added', (ev, gsItems) => { + if (grid._ignoreCB) return; + // 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! + // 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. + gsItems.forEach(n => { + grid.removeWidget(n.el, true, false); // true=remove DOM, false=don't call use back! + // can't pass n directly even though similar structs as it has n.el.gridstackNode which gives JSON error for circular write. + addItem && addItem({ id: n.id, x: n.x, y: n.y, w: n.w, h: n.h }); + }); + }) + .on('removed', (ev, gsItems) => { + /* Looks like a bug in GridStack */ + const dirtyNodes = grid.engine.getDirtyNodes(); + if (dirtyNodes !== undefined && dirtyNodes.length !== 0) { + const newItems = grid.save(false); + changeItems && changeItems(newItems); + } + + if (grid._ignoreCB) return; + gsItems.forEach(n => removeItem && removeItem(n.id)); + + }) + .on('change', (ev, gsItems) => { + const newItems = grid.save(false); + changeItems && changeItems(newItems); + }) } else { // // update existing grid layout, which is optimized to updates only diffs (will add new/delete items for examples) @@ -79,34 +90,15 @@

Controlled stack

const grid = gridRef.current; const layout = []; items.forEach((a) => layout.push( - refs.current[a.id].current.gridstackNode || {...a, el: refs.current[a.id].current} + refs.current[a.id].current.gridstackNode || { ...a, el: refs.current[a.id].current } )); grid._ignoreCB = true; // hack: ignore added/removed since we're the one doing the update grid.load(layout); delete grid._ignoreCB; } - // 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 - // grid.batchUpdate() - // items.forEach((a) => { - // // remove existing widgets - // if (refs.current[a.id] && refs.current[a.id].current) { - // grid.removeWidget(refs.current[a.id].current, false, false) - // } - // grid.makeWidget(refs.current[a.id].current) - // }) - // grid.batchUpdate(false) - }, [items]) - useEffect(() => { - return () => { - // console.log('cleanup', id) - // gridRef.current.destroy(false, false) - // gridRef.current = null - } - }) - return ( // ******************** // NOTE: constructing DOM grid items in template when gridstack is also allowed editing (dragging between grids, or adding/removing from say a toolbar) @@ -142,35 +134,44 @@

Controlled stack

const [items2, setItems2] = useState([{ id: 'item-2-1', x: 0, y: 0, w: 1, h: 1 }, { id: 'item-2-2', x: 0, y: 1, w: 1, h: 1 }, { id: 'item-2-3', x: 1, y: 0, w: 1, h: 1 }]) return ( -
-
- { - setItems1(items => [...items, item]) - }} - removeItem={(id) => { - setItems1(items => items.filter(i => i.id !== id)) - }} - /> -
-
- { - setItems2(items => [...items, item]) - }} - removeItem={(id) => { - setItems2(items => items.filter(i => i.id !== id)) - }} - /> +
+
+ +
-
+
+
+ { + setItems1(items => [...items, item]) + }} + removeItem={(id) => { + setItems1(items => items.filter(i => i.id !== id)) + }} + changeItems={(items) => setItems1(items)} + /> +
+
+ { + setItems2(items => [...items, item]) + }} + removeItem={(id) => { + setItems2(items => items.filter(i => i.id !== id)) + }} + changeItems={(items) => setItems2(items)} + /> +
+
+
) } ReactDOM.render(, document.getElementById('controlled-stack')) + \ No newline at end of file From 97c56383ad39de7ed854661e857ab743d2cc5501 Mon Sep 17 00:00:00 2001 From: Sergey Fomin Date: Fri, 13 Jan 2023 16:10:01 +0200 Subject: [PATCH 2/7] adds usage of ref instead of an id property --- demo/react-hooks-controlled-multiple.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/demo/react-hooks-controlled-multiple.html b/demo/react-hooks-controlled-multiple.html index 8b8ea2047..3d3b995db 100644 --- a/demo/react-hooks-controlled-multiple.html +++ b/demo/react-hooks-controlled-multiple.html @@ -33,9 +33,10 @@

Controlled stack

// // Controlled example // - const ControlledStack = ({ items, addItem, removeItem, id, changeItems }) => { + const ControlledStack = ({ items, addItem, removeItem, changeItems }) => { const refs = useRef({}) const gridRef = useRef() + const gridContainerRef = useRef(null) refs.current = {} if (Object.keys(refs.current).length !== items.length) { @@ -55,7 +56,7 @@

Controlled stack

column: 6, minRow: 1, }, - `.controlled-${id}` + gridContainerRef.current ) .on('added', (ev, gsItems) => { if (grid._ignoreCB) return; @@ -111,10 +112,10 @@

Controlled stack

// is not robust as things get added, and pollutes the DOM attr for default/missing entries, vs optimized code in GS. // ********************
-
+
{items.map((item, i) => { return ( -
+
From 1031f2872d3a9fce60b8a1fda0968758f954a389 Mon Sep 17 00:00:00 2001 From: Sergey Fomin Date: Fri, 13 Jan 2023 16:13:13 +0200 Subject: [PATCH 3/7] removes id property in the example --- demo/react-hooks-controlled-multiple.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/demo/react-hooks-controlled-multiple.html b/demo/react-hooks-controlled-multiple.html index 3d3b995db..5ec04f867 100644 --- a/demo/react-hooks-controlled-multiple.html +++ b/demo/react-hooks-controlled-multiple.html @@ -143,7 +143,6 @@

Controlled stack

{ setItems1(items => [...items, item]) @@ -156,7 +155,6 @@

Controlled stack

{ setItems2(items => [...items, item]) From 744e8014aebf09c43e70284a1d21e6c73befbb70 Mon Sep 17 00:00:00 2001 From: Sergey Fomin Date: Fri, 13 Jan 2023 23:21:25 +0200 Subject: [PATCH 4/7] reverts indentation --- demo/react-hooks-controlled-multiple.html | 58 ++++++++++++----------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/demo/react-hooks-controlled-multiple.html b/demo/react-hooks-controlled-multiple.html index 5ec04f867..a478991e2 100644 --- a/demo/react-hooks-controlled-multiple.html +++ b/demo/react-hooks-controlled-multiple.html @@ -6,7 +6,7 @@ Gridstack.js React integration example - + @@ -58,32 +58,31 @@

Controlled stack

}, gridContainerRef.current ) - .on('added', (ev, gsItems) => { - if (grid._ignoreCB) return; - // 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! - // 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. - gsItems.forEach(n => { - grid.removeWidget(n.el, true, false); // true=remove DOM, false=don't call use back! - // can't pass n directly even though similar structs as it has n.el.gridstackNode which gives JSON error for circular write. - addItem && addItem({ id: n.id, x: n.x, y: n.y, w: n.w, h: n.h }); - }); - }) - .on('removed', (ev, gsItems) => { - /* Looks like a bug in GridStack */ - const dirtyNodes = grid.engine.getDirtyNodes(); - if (dirtyNodes !== undefined && dirtyNodes.length !== 0) { - const newItems = grid.save(false); - changeItems && changeItems(newItems); - } - - if (grid._ignoreCB) return; - gsItems.forEach(n => removeItem && removeItem(n.id)); - - }) - .on('change', (ev, gsItems) => { + .on('added', (ev, gsItems) => { + if (grid._ignoreCB) return; + // 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! + // 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. + gsItems.forEach(n => { + grid.removeWidget(n.el, true, false); // true=remove DOM, false=don't call use back! + // can't pass n directly even though similar structs as it has n.el.gridstackNode which gives JSON error for circular write. + addItem({ id: n.id, x: n.x, y: n.y, w: n.w, h: n.h }); + }); + }) + .on('removed', (ev, gsItems) => { + /* Looks like a bug in GridStack */ + const dirtyNodes = grid.engine.getDirtyNodes(); + if (dirtyNodes !== undefined && dirtyNodes.length !== 0) { const newItems = grid.save(false); - changeItems && changeItems(newItems); - }) + changeItems(newItems); + } + + if (grid._ignoreCB) return; + gsItems.forEach(n => removeItem(n.id)); + }) + .on('change', (ev, gsItems) => { + const newItems = grid.save(false); + changeItems(newItems); + }) } else { // // update existing grid layout, which is optimized to updates only diffs (will add new/delete items for examples) @@ -91,7 +90,7 @@

Controlled stack

const grid = gridRef.current; const layout = []; items.forEach((a) => layout.push( - refs.current[a.id].current.gridstackNode || { ...a, el: refs.current[a.id].current } + refs.current[a.id].current.gridstackNode || {...a, el: refs.current[a.id].current} )); grid._ignoreCB = true; // hack: ignore added/removed since we're the one doing the update grid.load(layout); @@ -136,6 +135,10 @@

Controlled stack

return (
+
+
+
+
@@ -172,5 +175,4 @@

Controlled stack

ReactDOM.render(, document.getElementById('controlled-stack')) - \ No newline at end of file From d4692b0df8daff540113d0bfa62152e0170f9837 Mon Sep 17 00:00:00 2001 From: Sergey Fomin Date: Fri, 13 Jan 2023 23:29:01 +0200 Subject: [PATCH 5/7] reverts indentation --- demo/react-hooks-controlled-multiple.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/react-hooks-controlled-multiple.html b/demo/react-hooks-controlled-multiple.html index a478991e2..34cf3d868 100644 --- a/demo/react-hooks-controlled-multiple.html +++ b/demo/react-hooks-controlled-multiple.html @@ -65,7 +65,7 @@

Controlled stack

gsItems.forEach(n => { grid.removeWidget(n.el, true, false); // true=remove DOM, false=don't call use back! // can't pass n directly even though similar structs as it has n.el.gridstackNode which gives JSON error for circular write. - addItem({ id: n.id, x: n.x, y: n.y, w: n.w, h: n.h }); + addItem({id:n.id, x:n.x, y:n.y, w:n.w, h:n.h}); }); }) .on('removed', (ev, gsItems) => { @@ -110,11 +110,11 @@

Controlled stack

// 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 // is not robust as things get added, and pollutes the DOM attr for default/missing entries, vs optimized code in GS. // ******************** -
+
{items.map((item, i) => { return ( -
+
@@ -135,7 +135,7 @@

Controlled stack

return (
-
+
@@ -143,7 +143,7 @@

Controlled stack

-
+
Date: Fri, 13 Jan 2023 23:31:00 +0200 Subject: [PATCH 6/7] reverts indentation --- demo/react-hooks-controlled-multiple.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/react-hooks-controlled-multiple.html b/demo/react-hooks-controlled-multiple.html index 34cf3d868..dd89c6ac6 100644 --- a/demo/react-hooks-controlled-multiple.html +++ b/demo/react-hooks-controlled-multiple.html @@ -110,7 +110,7 @@

Controlled stack

// 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 // is not robust as things get added, and pollutes the DOM attr for default/missing entries, vs optimized code in GS. // ******************** -
+
{items.map((item, i) => { return ( From 72aaab808d6ca8a3c305629a254f2138a5d7f89d Mon Sep 17 00:00:00 2001 From: Sergey Fomin Date: Sat, 14 Jan 2023 00:09:06 +0200 Subject: [PATCH 7/7] minor improvements --- demo/react-hooks-controlled-multiple.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/demo/react-hooks-controlled-multiple.html b/demo/react-hooks-controlled-multiple.html index dd89c6ac6..bb96e828a 100644 --- a/demo/react-hooks-controlled-multiple.html +++ b/demo/react-hooks-controlled-multiple.html @@ -88,10 +88,9 @@

Controlled stack

// update existing grid layout, which is optimized to updates only diffs (will add new/delete items for examples) // const grid = gridRef.current; - const layout = []; - items.forEach((a) => layout.push( + const layout = items.map((a) => refs.current[a.id].current.gridstackNode || {...a, el: refs.current[a.id].current} - )); + ); grid._ignoreCB = true; // hack: ignore added/removed since we're the one doing the update grid.load(layout); delete grid._ignoreCB; @@ -140,8 +139,8 @@

Controlled stack

- - + +