Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f76c41f

Browse files
committed
column(n, 'list') handles restore back to 12 size
* more fix gridstack#2358 * going 12 -> 1 -> 12 in 'list' mode now correctly restore original item width before doing compact() * updated demo * updated API docs
1 parent 048767c commit f76c41f

File tree

5 files changed

+139
-101
lines changed

5 files changed

+139
-101
lines changed

demo/column.html

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,28 @@ <h1>column() grid demo (fix cellHeight)</h1>
2727
</select>
2828
</div>
2929
<div>
30-
<a onClick="grid.removeAll().load(layout1)" class="btn btn-primary" href="#">random</a>
31-
<a onClick="grid.removeAll().load(layout2)" class="btn btn-primary" href="#">list</a>
30+
load:
31+
<a onClick="grid.removeAll().load(list)" class="btn btn-primary" href="#">list</a>
32+
<a onClick="grid.removeAll().load(test1)" class="btn btn-primary" href="#">case 1</a>
33+
<a onClick="random()" class="btn btn-primary" href="#">random</a>
3234
<a onClick="addWidget()" class="btn btn-primary" href="#">Add Widget</a>
33-
<a onClick="setOneColumn(false)" class="btn btn-primary" href="#">1 Column</a>
34-
<a onClick="setOneColumn(true)" class="btn btn-primary" href="#">1 Column DOM</a>
35-
<a onClick="column(2)" class="btn btn-primary" href="#">2 Column</a>
36-
<a onClick="column(3)" class="btn btn-primary" href="#">3 Column</a>
37-
<a onClick="column(4)" class="btn btn-primary" href="#">4 Column</a>
38-
<a onClick="column(6)" class="btn btn-primary" href="#">6 Column</a>
39-
<a onClick="column(8)" class="btn btn-primary" href="#">8 Column</a>
40-
<a onClick="column(10)" class="btn btn-primary" href="#">10 Column</a>
41-
<a onClick="column(12)" class="btn btn-primary" href="#">12 Column</a>
35+
column:
36+
<a onClick="setOneColumn(false)" class="btn btn-primary" href="#">1</a>
37+
<a onClick="setOneColumn(true)" class="btn btn-primary" href="#">1 DOM</a>
38+
<a onClick="column(2)" class="btn btn-primary" href="#">2</a>
39+
<a onClick="column(3)" class="btn btn-primary" href="#">3</a>
40+
<a onClick="column(4)" class="btn btn-primary" href="#">4</a>
41+
<a onClick="column(6)" class="btn btn-primary" href="#">6</a>
42+
<a onClick="column(8)" class="btn btn-primary" href="#">8</a>
43+
<a onClick="column(10)" class="btn btn-primary" href="#">10</a>
44+
<a onClick="column(12)" class="btn btn-primary" href="#">12</a>
4245
</div>
4346
<br>
4447
<div class="grid-stack"></div>
4548
</div>
4649

4750
<script type="text/javascript">
48-
let layout1 = [ // DOM order will be 0,1,2,3,4,5,6 vs column1 = 0,1,4,3,2,5,6
51+
let test1 = [ // DOM order will be 0,1,2,3,4,5,6 vs column1 = 0,1,4,3,2,5,6
4952
/* match karma testing
5053
{x: 0, y: 0, w: 4, h: 2},
5154
{x: 4, y: 0, w: 4, h: 4},
@@ -61,20 +64,20 @@ <h1>column() grid demo (fix cellHeight)</h1>
6164
{x: 5, y: 3, w: 2},
6265
{x: 0, y: 4, w: 12}
6366
];
64-
let layout2 = [{h:2},{},{},{},{},{},{},{},{},{w:2},{},{},{},{},{},{}];
65-
layout2.forEach((n,i) => {
67+
let list = [{h:2},{},{},{},{},{},{},{},{},{w:2},{},{},{},{},{},{}];
68+
list.forEach((n,i) => {
6669
n.content = '<button onClick="grid.removeWidget(this.parentNode.parentNode)">X</button><br>' + ++i;
6770
});
6871
let count = 0;
69-
layout1.forEach(n => {
72+
test1.forEach(n => {
7073
n.content = '<button onClick="grid.removeWidget(this.parentNode.parentNode)">X</button><br>' + count++ + (n.text ? n.text : '');
7174
});
7275

7376
let grid = GridStack.init({
7477
float: true,
7578
disableOneColumnMode: true, // prevent auto column for this manual demo
7679
cellHeight: 100 // fixed as default 'auto' (square) makes it hard to test 1-3 column in actual large windows tests
77-
}).load(layout2);
80+
}).load(list);
7881
let text = document.querySelector('#column-text');
7982
let layout = 'list';
8083

@@ -85,14 +88,18 @@ <h1>column() grid demo (fix cellHeight)</h1>
8588
});
8689

8790

91+
function random() {
92+
grid.removeAll();
93+
count = 0;
94+
for (i=0; i<8; i++) addWidget(true);
95+
}
96+
8897
function addWidget() {
89-
let n = items[count] || {
90-
x: Math.round(12 * Math.random()),
91-
y: Math.round(5 * Math.random()),
98+
let n = {
9299
w: Math.round(1 + 3 * Math.random()),
93-
h: Math.round(1 + 3 * Math.random())
100+
h: Math.round(1 + 3 * Math.random()),
101+
content: '<button onClick="grid.removeWidget(this.parentNode.parentNode)">X</button><br>' + count++,
94102
};
95-
n.content = '<button onClick="grid.removeWidget(this.parentNode.parentNode)">X</button><br>' + count++ + (n.text ? n.text : '');
96103
grid.addWidget(n);
97104
};
98105

doc/README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ gridstack.js API
3333
- [API](#api)
3434
- [`addWidget(el?: GridStackWidget | GridStackElement, options?: GridStackWidget)`](#addwidgetel-gridstackwidget--gridstackelement-options-gridstackwidget)
3535
- [`batchUpdate(flag = true)`](#batchupdateflag--true)
36-
- [`compact()`](#compact)
36+
- [`compact(layout: CompactOptions = 'compact', doSort = true)`](#compactlayout-compactoptions--compact-dosort--true)
3737
- [`cellHeight(val: number, update = true)`](#cellheightval-number-update--true)
3838
- [`cellWidth()`](#cellwidth)
3939
- [`column(column: number, layout: ColumnOptions = 'moveScale')`](#columncolumn-number-layout-columnoptions--movescale)
@@ -361,9 +361,14 @@ grid.addWidget('<div class="grid-stack-item"><div class="grid-stack-item-content
361361

362362
use before calling a bunch of `addWidget()` to prevent un-necessary relayouts in between (more efficient) and get a single event callback. You will see no changes until `batchUpdate(false)` is called.
363363

364-
### `compact()`
364+
### `compact(layout: CompactOptions = 'compact', doSort = true)`
365365

366-
re-layout grid items to reclaim any empty space.
366+
re-layout grid items to reclaim any empty space. Options are:
367+
- `'list'` keep the widget left->right order the same, even if that means leaving an empty slot if things don't fit
368+
- `'compact'` might re-order items to fill any empty space
369+
370+
- `doSort` - `false` to let you do your own sorting ahead in case you need to control a different order. (default to sort)
371+
367372

368373
### `cellHeight(val: number, update = true)`
369374

@@ -385,10 +390,14 @@ Requires `gridstack-extra.css` (or minimized version) for [2-11],
385390
else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns)
386391

387392
- `column` - Integer > 0 (default 12)
388-
- `layout` - specify the type of re-layout that will happen (position, size, etc...).
389-
Note: items will never be outside of the current column boundaries. default ('moveScale'). Ignored for 1 column.
390-
Possible values: 'moveScale' | 'move' | 'scale' | 'none' | (column: number, oldColumn: number, nodes: GridStackNode[], oldNodes: GridStackNode[]) => void.
391-
A custom function option takes new/old column count, and array of new/old positions.
393+
- `layout` - specify the type of re-layout that will happen (position, size, etc...). Values are: `'list' | 'compact' | 'moveScale' | 'move' | 'scale' | 'none' | ((column: number, oldColumn: number, nodes: GridStackNode[], oldNodes: GridStackNode[]) => void);`
394+
395+
* `'list'` - treat items as sorted list, keeping items (un-sized unless too big for column count) sequentially reflowing them
396+
* `'compact'` - similar to list, but using compact() method which will possibly re-order items if an empty slots are available due to a larger item needing to be pushed to next row
397+
* `'moveScale'` - will scale and move items by the ratio new newColumnCount / oldColumnCount
398+
* `'move'` | `'scale'` - will only size or move items
399+
* `'none'` will leave items unchanged, unless they don't fit in column count
400+
* custom function that takes new/old column count, and array of new/old positions
392401
Note: new list may be partially already filled if we have a partial cache of the layout at that size (items were added later). If complete cache is present this won't get called at all.
393402

394403
### `destroy([removeDOM])`

src/gridstack-engine.ts

Lines changed: 82 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class GridStackEngine {
5454
this.onChange = opts.onChange;
5555
}
5656

57-
public batchUpdate(flag = true): GridStackEngine {
57+
public batchUpdate(flag = true, doPack = true): GridStackEngine {
5858
if (!!this.batchMode === flag) return this;
5959
this.batchMode = flag;
6060
if (flag) {
@@ -64,7 +64,8 @@ export class GridStackEngine {
6464
} else {
6565
this._float = this._prevFloat;
6666
delete this._prevFloat;
67-
this._packNodes()._notify();
67+
if (doPack) this._packNodes();
68+
this._notify();
6869
}
6970
return this;
7071
}
@@ -258,12 +259,14 @@ export class GridStackEngine {
258259
return !this.collide(nn);
259260
}
260261

261-
/** re-layout grid items to reclaim any empty space - optionally keeping the sort order exactly the same (list mode) vs truly finding an empty spaces */
262-
public compact(layout: CompactOptions = 'compact', sortBefore = true): GridStackEngine {
262+
/** re-layout grid items to reclaim any empty space - optionally keeping the sort order exactly the same ('list' mode) vs truly finding an empty spaces */
263+
public compact(layout: CompactOptions = 'compact', doSort = true): GridStackEngine {
263264
if (this.nodes.length === 0) return this;
264-
this.batchUpdate()
265-
if (sortBefore) this.sortNodes();
266-
this._inColumnResize = true; // faster addNode()
265+
if (doSort) this.sortNodes();
266+
const wasBatch = this.batchMode;
267+
if (!wasBatch) this.batchUpdate();
268+
const wasColumnResize = this._inColumnResize;
269+
if (!wasColumnResize) this._inColumnResize = true; // faster addNode()
267270
let copyNodes = this.nodes;
268271
this.nodes = []; // pretend we have no nodes to conflict layout to start with...
269272
copyNodes.forEach((n, index, list) => {
@@ -274,8 +277,9 @@ export class GridStackEngine {
274277
}
275278
this.addNode(n, false, after); // 'false' for add event trigger
276279
});
277-
delete this._inColumnResize;
278-
return this.batchUpdate(false);
280+
if (!wasColumnResize) delete this._inColumnResize;
281+
if (!wasBatch) this.batchUpdate(false);
282+
return this;
279283
}
280284

281285
/** enable/disable floating widgets (default: `false`) See [example](http://gridstackjs.com/demo/float.html) */
@@ -518,14 +522,16 @@ export class GridStackEngine {
518522
delete node._temporaryRemoved;
519523
delete node._removeDOM;
520524

525+
let skipCollision: boolean;
521526
if (node.autoPosition && this.findEmptyPosition(node, this.nodes, this.column, after)) {
522527
delete node.autoPosition; // found our slot
528+
skipCollision = true;
523529
}
524530

525531
this.nodes.push(node);
526532
if (triggerAddEvent) { this.addedNodes.push(node); }
527533

528-
this._fixCollisions(node);
534+
if (!skipCollision) this._fixCollisions(node);
529535
if (!this.batchMode) { this._packNodes()._notify(); }
530536
return node;
531537
}
@@ -792,23 +798,21 @@ export class GridStackEngine {
792798
* @param layout specify the type of re-layout that will happen (position, size, etc...).
793799
* Note: items will never be outside of the current column boundaries. default (moveScale). Ignored for 1 column
794800
*/
795-
public updateNodeWidths(prevColumn: number, column: number, nodes: GridStackNode[], layout: ColumnOptions = 'moveScale'): GridStackEngine {
801+
public columnChanged(prevColumn: number, column: number, nodes: GridStackNode[], layout: ColumnOptions = 'moveScale'): GridStackEngine {
796802
if (!this.nodes.length || !column || prevColumn === column) return this;
797803

798804
// simpler shortcuts layouts
799805
const doCompact = layout === 'compact' || layout === 'list';
800806
if (doCompact) {
801807
this.sortNodes(1, prevColumn); // sort with original layout once and only once (new column will affect order otherwise)
802-
return this.compact(layout, false);
803808
}
804-
805-
// cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data
806-
this.cacheLayout(this.nodes, prevColumn);
809+
810+
// cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data IFF we're sizing down (see below)
811+
if (column < prevColumn) this.cacheLayout(this.nodes, prevColumn);
807812
this.batchUpdate(); // do this EARLY as it will call saveInitial() so we can detect where we started for _dirty and collision
808813
let newNodes: GridStackNode[] = [];
809-
810-
811-
// if we're going to 1 column and using DOM order rather than default sorting, then generate that layout
814+
815+
// if we're going to 1 column and using DOM order (item passed in) rather than default sorting, then generate that layout
812816
let domOrder = false;
813817
if (column === 1 && nodes?.length) {
814818
domOrder = true;
@@ -822,14 +826,13 @@ export class GridStackEngine {
822826
newNodes = nodes;
823827
nodes = [];
824828
} else {
825-
nodes = Utils.sort(this.nodes, -1, prevColumn); // current column reverse sorting so we can insert last to front (limit collision)
829+
nodes = doCompact ? this.nodes : Utils.sort(this.nodes, -1, prevColumn); // current column reverse sorting so we can insert last to front (limit collision)
826830
}
827831

828832
// see if we have cached previous layout IFF we are going up in size (restore) otherwise always
829833
// generate next size down from where we are (looks more natural as you gradually size down).
830-
let cacheNodes: GridStackNode[] = [];
831-
if (column > prevColumn) {
832-
cacheNodes = this._layouts[column] || [];
834+
if (column > prevColumn && this._layouts) {
835+
const cacheNodes = this._layouts[column] || [];
833836
// ...if not, start with the largest layout (if not already there) as down-scaling is more accurate
834837
// by pretending we came from that larger column by assigning those values as starting point
835838
let lastIndex = this._layouts.length - 1;
@@ -839,59 +842,72 @@ export class GridStackEngine {
839842
let n = nodes.find(n => n._id === cacheNode._id);
840843
if (n) {
841844
// still current, use cache info positions
842-
n.x = cacheNode.x;
843-
n.y = cacheNode.y;
845+
if (!doCompact) {
846+
n.x = cacheNode.x;
847+
n.y = cacheNode.y;
848+
}
844849
n.w = cacheNode.w;
845850
}
846851
});
847852
}
848-
}
849853

850-
// if we found cache re-use those nodes that are still current
851-
cacheNodes.forEach(cacheNode => {
852-
let j = nodes.findIndex(n => n._id === cacheNode._id);
853-
if (j !== -1) {
854-
// still current, use cache info positions
855-
if (cacheNode.autoPosition || isNaN(cacheNode.x) || isNaN(cacheNode.y)) {
856-
this.findEmptyPosition(cacheNode, newNodes);
854+
// if we found cache re-use those nodes that are still current
855+
cacheNodes.forEach(cacheNode => {
856+
let j = nodes.findIndex(n => n._id === cacheNode._id);
857+
if (j !== -1) {
858+
// still current, use cache info positions
859+
if (doCompact) {
860+
nodes[j].w = cacheNode.w; // only w is used, and don't trim the list
861+
return;
862+
}
863+
if (cacheNode.autoPosition || isNaN(cacheNode.x) || isNaN(cacheNode.y)) {
864+
this.findEmptyPosition(cacheNode, newNodes);
865+
}
866+
if (!cacheNode.autoPosition) {
867+
nodes[j].x = cacheNode.x;
868+
nodes[j].y = cacheNode.y;
869+
nodes[j].w = cacheNode.w;
870+
newNodes.push(nodes[j]);
871+
}
872+
nodes.splice(j, 1);
857873
}
858-
if (!cacheNode.autoPosition) {
859-
nodes[j].x = cacheNode.x;
860-
nodes[j].y = cacheNode.y;
861-
nodes[j].w = cacheNode.w;
862-
newNodes.push(nodes[j]);
874+
});
875+
}
876+
877+
// much simpler layout that just compacts
878+
if (doCompact) {
879+
this.compact(layout, false);
880+
} else {
881+
// ...and add any extra non-cached ones
882+
if (nodes.length) {
883+
if (typeof layout === 'function') {
884+
layout(column, prevColumn, newNodes, nodes);
885+
} else if (!domOrder) {
886+
let ratio = (doCompact || layout === 'none') ? 1 : column / prevColumn;
887+
let move = (layout === 'move' || layout === 'moveScale');
888+
let scale = (layout === 'scale' || layout === 'moveScale');
889+
nodes.forEach(node => {
890+
// NOTE: x + w could be outside of the grid, but addNode() below will handle that
891+
node.x = (column === 1 ? 0 : (move ? Math.round(node.x * ratio) : Math.min(node.x, column - 1)));
892+
node.w = ((column === 1 || prevColumn === 1) ? 1 : scale ? (Math.round(node.w * ratio) || 1) : (Math.min(node.w, column)));
893+
newNodes.push(node);
894+
});
895+
nodes = [];
863896
}
864-
nodes.splice(j, 1);
865897
}
866-
});
867-
// ...and add any extra non-cached ones
868-
if (nodes.length) {
869-
if (typeof layout === 'function') {
870-
layout(column, prevColumn, newNodes, nodes);
871-
} else if (!domOrder) {
872-
let ratio = column / prevColumn;
873-
let move = (layout === 'move' || layout === 'moveScale');
874-
let scale = (layout === 'scale' || layout === 'moveScale');
875-
nodes.forEach(node => {
876-
// NOTE: x + w could be outside of the grid, but addNode() below will handle that
877-
node.x = (column === 1 ? 0 : (move ? Math.round(node.x * ratio) : Math.min(node.x, column - 1)));
878-
node.w = ((column === 1 || prevColumn === 1) ? 1 :
879-
scale ? (Math.round(node.w * ratio) || 1) : (Math.min(node.w, column)));
880-
newNodes.push(node);
881-
});
882-
nodes = [];
883-
}
884-
}
885898

886-
// finally re-layout them in reverse order (to get correct placement)
887-
if (!domOrder) newNodes = Utils.sort(newNodes, -1, column);
888-
this._inColumnResize = true; // prevent cache update
889-
this.nodes = []; // pretend we have no nodes to start with (add() will use same structures) to simplify layout
890-
newNodes.forEach(node => {
891-
this.addNode(node, false); // 'false' for add event trigger
892-
delete node._orig; // make sure the commit doesn't try to restore things back to original
893-
});
894-
this.batchUpdate(false);
899+
// finally re-layout them in reverse order (to get correct placement)
900+
if (!domOrder) newNodes = Utils.sort(newNodes, -1, column);
901+
this._inColumnResize = true; // prevent cache update
902+
this.nodes = []; // pretend we have no nodes to start with (add() will use same structures) to simplify layout
903+
newNodes.forEach(node => {
904+
this.addNode(node, false); // 'false' for add event trigger
905+
delete node._orig; // make sure the commit doesn't try to restore things back to original
906+
});
907+
}
908+
909+
this.nodes.forEach(n => delete n._orig); // clear _orig before batch=false so it doesn't handle float=true restore
910+
this.batchUpdate(false, !doCompact);
895911
delete this._inColumnResize;
896912
return this;
897913
}

0 commit comments

Comments
 (0)