diff --git a/doc/CHANGES.md b/doc/CHANGES.md
index 7f22b0b14..54e2123fa 100644
--- a/doc/CHANGES.md
+++ b/doc/CHANGES.md
@@ -103,6 +103,7 @@ Change log
## 9.2.1-dev (TBD)
* fix - sub-grid styles now look for immediate correct parent, not any depth above.
* fix [#2469](https://github.com/gridstack/gridstack.js/issues/2469) "Invalid height" error CSS minHeight
+* fix [#2394](https://github.com/gridstack/gridstack.js/issues/2394) nested grid size issue when sub-items moved up/down
## 9.2.1 (2023-09-20)
* fix _updateContainerHeight() to use height rather than min-height again (apart for nested grids which need it) and partial getComputedStyle CSS minHeight support
diff --git a/spec/e2e/html/2394_save_sub_item_moved.html b/spec/e2e/html/2394_save_sub_item_moved.html
new file mode 100644
index 000000000..901b36dff
--- /dev/null
+++ b/spec/e2e/html/2394_save_sub_item_moved.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+ #2394 Save sub item moved
+
+
+
+
+
+
+
+ #2394 Save sub item moved
+
+
+
+
+
+
+
+
+
diff --git a/src/gridstack-engine.ts b/src/gridstack-engine.ts
index db35bc5c7..70cd55287 100644
--- a/src/gridstack-engine.ts
+++ b/src/gridstack-engine.ts
@@ -392,9 +392,9 @@ export class GridStackEngine {
const saveOrig = (node.x || 0) + (node.w || 1) > this.column;
if (saveOrig && this.column < 12 && !this._inColumnResize && node._id && this.findCacheLayout(node, 12) === -1) {
let copy = {...node}; // need _id + positions
- if (copy.autoPosition) { delete copy.x; delete copy.y; }
+ if (copy.autoPosition || copy.x === undefined) { delete copy.x; delete copy.y; }
else copy.x = Math.min(11, copy.x);
- copy.w = Math.min(12, copy.w);
+ copy.w = Math.min(12, copy.w || 1);
this.cacheOneLayout(copy, 12);
}
@@ -744,9 +744,8 @@ export class GridStackEngine {
this.sortNodes();
this.nodes.forEach(n => {
let wl = layout?.find(l => l._id === n._id);
- let w: GridStackNode = {...n};
- // use layout info instead if set
- if (wl) { w.x = wl.x; w.y = wl.y; w.w = wl.w; }
+ // use layout info fields instead if set
+ let w: GridStackNode = {...n, ...(wl || {})};
Utils.removeInternalForSave(w, !saveElement);
if (saveCB) saveCB(n, w);
list.push(w);
@@ -943,7 +942,7 @@ export class GridStackEngine {
public cacheOneLayout(n: GridStackNode, column: number): GridStackEngine {
n._id = n._id ?? GridStackEngine._idSeq++;
let l: GridStackNode = {x: n.x, y: n.y, w: n.w, _id: n._id}
- if (n.autoPosition) { delete l.x; delete l.y; l.autoPosition = true; }
+ if (n.autoPosition || n.x === undefined) { delete l.x; delete l.y; if (n.autoPosition) l.autoPosition = true; }
this._layouts = this._layouts || [];
this._layouts[column] = this._layouts[column] || [];
let index = this.findCacheLayout(n, column);
diff --git a/src/gridstack.ts b/src/gridstack.ts
index e9d265df8..01d2ade59 100644
--- a/src/gridstack.ts
+++ b/src/gridstack.ts
@@ -2042,12 +2042,14 @@ export class GridStack {
// console.log('dropover cloning node'); // TEST
if (!el._gridstackNodeOrig) el._gridstackNodeOrig = node; // shouldn't have multiple nested!
el.gridstackNode = node = {...node, w, h, grid: this};
+ delete node.x;
+ delete node.y;
this.engine.cleanupNode(node)
.nodeBoundFix(node);
// restore some internal fields we need after clearing them all
node._initDD =
- node._isExternal = // DOM needs to be re-parented on a drop
- node._temporaryRemoved = true; // so it can be inserted onDrag below
+ node._isExternal = // DOM needs to be re-parented on a drop
+ node._temporaryRemoved = true; // so it can be inserted onDrag below
} else {
node.w = w; node.h = h;
node._temporaryRemoved = true; // so we can insert it
@@ -2096,6 +2098,7 @@ export class GridStack {
delete el._gridstackNodeOrig;
if (wasAdded && origNode?.grid && origNode.grid !== this) {
let oGrid = origNode.grid;
+ oGrid.engine.removeNodeFromLayoutCache(origNode);
oGrid.engine.removedNodes.push(origNode);
oGrid._triggerRemoveEvent()._triggerChangeEvent();
// if it's an empty sub-grid that got auto-created, nuke it
@@ -2141,7 +2144,6 @@ export class GridStack {
this._updateContainerHeight();
this.engine.addedNodes.push(node);// @ts-ignore
this._triggerAddEvent();// @ts-ignore
- this.engine.removeNodeFromLayoutCache(node);
this._triggerChangeEvent();
this.engine.endUpdate();