Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

load() now creates in order #2720

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions demo/two.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,13 @@ <h1>Two grids demo</h1>

let items = [
{x: 0, y: 0, w: 2, h: 2},
{x: 3, y: 1, h: 2},
{x: 4, y: 1},
{x: 2, y: 3, w: 3, maxW: 3, id: 'special', content: 'has maxW=3'},
{x: 1, y: 1, h: 2}, // intentional overlap to test collision on load
{x: 1, y: 1}, // intentional overlap to test collision on load
{x: 3, y: 1},
{x: 2, y: 3, w: 3, maxW: 3, content: 'has maxW=3'},
{x: 2, y: 5}
];
items.forEach((item, i) => item.content = item.content || String(i));

grids.forEach(function (grid, i) {
addEvents(grid, i);
Expand Down
4 changes: 4 additions & 0 deletions doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Change log
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*

- [10.2.1-dev (TBD)](#1021-dev-tbd)
- [10.2.1 (2024-06-23)](#1021-2024-06-23)
- [10.2.0 (2024-06-02)](#1020-2024-06-02)
- [10.1.2 (2024-03-30)](#1012-2024-03-30)
Expand Down Expand Up @@ -111,6 +112,9 @@ Change log

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## 10.2.1-dev (TBD)
* fix: [#2717](https://github.com/gridstack/gridstack.js/pull/2717) load() now creates in order

## 10.2.1 (2024-06-23)
* fix: [#2683](https://github.com/gridstack/gridstack.js/issues/2683) check for fixed grid maxRow during resize
* fix: [#2694](https://github.com/gridstack/gridstack.js/issues/2694) prevent 'r' rotation to items that can't resize (locked, noResize, fixed sizes)
Expand Down
10 changes: 6 additions & 4 deletions src/gridstack-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export class GridStackEngine {
protected _prevFloat: boolean;
/** @internal cached layouts of difference column count so we can restore back (eg 12 -> 1 -> 12) */
protected _layouts?: GridStackNode[][]; // maps column # to array of values nodes
/** @internal set during loading (which is sorted) so item gets added AFTER collision nodes */
public _loading?: boolean
/** @internal true while we are resizing widgets during column resize to skip certain parts */
protected _inColumnResize?: boolean;
/** @internal true if we have some items locked */
Expand Down Expand Up @@ -91,7 +93,7 @@ export class GridStackEngine {

// during while() collisions MAKE SURE to check entire row so larger items don't leap frog small ones (push them all down starting last in grid)
let area = nn;
if (this._useEntireRowArea(node, nn)) {
if (!this._loading && this._useEntireRowArea(node, nn)) {
area = {x: 0, w: this.column, y: nn.y, h: nn.h};
collide = this.collide(node, area, opt.skip); // force new hit
}
Expand All @@ -100,14 +102,14 @@ export class GridStackEngine {
let newOpt: GridStackMoveOpts = {nested: true, pack: false};
while (collide = collide || this.collide(node, area, opt.skip)) { // could collide with more than 1 item... so repeat for each
let moved: boolean;
// if colliding with a locked item OR moving down with top gravity (and collide could move up) -> skip past the collide,
// if colliding with a locked item OR loading (move after) OR moving down with top gravity (and collide could move up) -> skip past the collide,
// but remember that skip down so we only do this once (and push others otherwise).
if (collide.locked || node._moving && !node._skipDown && nn.y > node.y && !this.float &&
if (collide.locked || this._loading || node._moving && !node._skipDown && nn.y > node.y && !this.float &&
// can take space we had, or before where we're going
(!this.collide(collide, {...collide, y: node.y}, node) || !this.collide(collide, {...collide, y: nn.y - collide.h}, node))) {
node._skipDown = (node._skipDown || nn.y > node.y);
moved = this.moveNode(node, {...nn, y: collide.y + collide.h, ...newOpt});
if (collide.locked && moved) {
if ((collide.locked || this._loading) && moved) {
Utils.copyPos(nn, node); // moving after lock become our new desired location
} else if (!collide.locked && moved && opt.pack) {
// we moved after and will pack: do it now and keep the original drop location, but past the old collide to see what else we might push way
Expand Down
50 changes: 16 additions & 34 deletions src/gridstack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,6 @@ export class GridStack {
protected _sizeThrottle: () => void;
/** @internal limit auto cell resizing method */
protected prevWidth: number;
/** @internal true when loading items to insert first rather than append */
protected _insertNotAppend: boolean;
/** @internal extra row added when dragging at the bottom of the grid */
protected _extraDragRow = 0;
/** @internal true if nested grid should get column count from our width */
Expand Down Expand Up @@ -421,7 +419,7 @@ export class GridStack {

// load any passed in children as well, which overrides any DOM layout done above
if (opts.children) {
let children = opts.children;
const children = opts.children;
delete opts.children;
if (children.length) this.load(children); // don't load empty
}
Expand Down Expand Up @@ -495,11 +493,7 @@ export class GridStack {
node = this.engine.prepareNode(options);
this._writeAttr(el, options);

if (this._insertNotAppend) {
this.el.prepend(el);
} else {
this.el.appendChild(el);
}
this.el.appendChild(el);

this.makeWidget(el, options);

Expand Down Expand Up @@ -703,22 +697,16 @@ export class GridStack {
// make sure size 1x1 (default) is present as it may need to override current sizes
items.forEach(n => { n.w = n.w || 1; n.h = n.h || 1 });

// if we have a mix of new items without coordinates and existing items, separate them out so they can be added after #2639
let addAfter = items.filter(n => (n.x === undefined || n.y === undefined) && !Utils.find(this.engine.nodes, n.id));
if (addAfter.length && addAfter.length !== items.length) {
items = items.filter(n => !Utils.find(addAfter, n.id));
} else addAfter = [];

// if passed list has coordinates, use them (insert from end to beginning for conflict resolution) else keep widget order
const haveCoord = items.some(w => w.x !== undefined || w.y !== undefined);
if (haveCoord) items = Utils.sort(items, -1);
this._insertNotAppend = haveCoord; // if we create in reverse order...
// sort items. those without coord will be appended last
items = Utils.sort(items);

// if we're loading a layout into for example 1 column and items don't fit, make sure to save
// the original wanted layout so we can scale back up correctly #1471
if (items.some(n => ((n.x || 0) + (n.w || 1)) > column)) {
let maxColumn = 0;
items.forEach(n => { maxColumn = Math.max(maxColumn, (n.x || 0) + n.w) });
if (maxColumn > column) {
this._ignoreLayoutsNodeChange = true; // skip layout update
this.engine.cacheLayout(items, 12, true); // TODO: 12 is arbitrary. use max value in layout ?
this.engine.cacheLayout(items, maxColumn, true);
}

// if given a different callback, temporally set it as global option so creating will use it
Expand All @@ -728,19 +716,18 @@ export class GridStack {
let removed: GridStackNode[] = [];
this.batchUpdate();

// if we are blank (loading into empty like startup) temp remove animation
const noAnim = !this.engine.nodes.length;
if (noAnim) this.setAnimation(false);
// if we are loading from empty temporarily remove animation
const blank = !this.engine.nodes.length;
if (blank) this.setAnimation(false);

// see if any items are missing from new layout and need to be removed first
if (addRemove) {
if (!blank && addRemove) {
let copyNodes = [...this.engine.nodes]; // don't loop through array you modify
copyNodes.forEach(n => {
if (!n.id) return;
let item = Utils.find(items, n.id);
if (!item) {
if (GridStack.addRemoveCB)
GridStack.addRemoveCB(this.el, n, false, false);
if (GridStack.addRemoveCB) GridStack.addRemoveCB(this.el, n, false, false);
removed.push(n); // batch keep track
this.removeWidget(n.el, true, false);
}
Expand All @@ -749,6 +736,7 @@ export class GridStack {

// now add/update the widgets - starting with removing items in the new layout we will reposition
// to reduce collision and add no-coord ones at next available spot
this.engine._loading = true; // help with collision
let updateNodes: GridStackWidget[] = [];
this.engine.nodes = this.engine.nodes.filter(n => {
if (Utils.find(items, n.id)) { updateNodes.push(n); return false; } // remove if found from list
Expand Down Expand Up @@ -778,28 +766,22 @@ export class GridStack {
let sub = item.el.querySelector('.grid-stack') as GridHTMLElement;
if (sub && sub.gridstack) {
sub.gridstack.load(w.subGridOpts.children); // TODO: support updating grid options ?
this._insertNotAppend = true; // got reset by above call
}
}
} else if (addRemove) {
this.addWidget(w);
}
});

// finally append any separate ones that didn't have explicit coordinates last so they can find next empty spot
if (addRemove) {
addAfter.forEach(w => this.addWidget(w))
}

delete this.engine._loading; // done loading
this.engine.removedNodes = removed;
this.batchUpdate(false);

// after commit, clear that flag
delete this._ignoreLayoutsNodeChange;
delete this._insertNotAppend;
prevCB ? GridStack.addRemoveCB = prevCB : delete GridStack.addRemoveCB;
// delay adding animation back
if (noAnim && this.opts?.animate) this.setAnimation(this.opts.animate, true);
if (blank && this.opts?.animate) this.setAnimation(this.opts.animate, true);
return this;
}

Expand Down