Skip to content

Commit 77df15c

Browse files
author
Alain Dumesny
committed
Custom Responsive Sort Order
* add `oneColumnModeDomSort` true|false to let you specify a custom layout (use dom order instead of x,y) for oneColumnMode `setColumn(1)` * fix for #713 * better than PR #966 which attempted to make a generic support for oneColumn only mode, yet didn't modify the other layouts sorting. Instead user will have complete control (dom order) iresptive of layouts sort. Also works with current setColumn() implementation * added karma tests * Note: you cannot change this flag and expect 1 column (which is cached) to change (can see in column.html, unless you add/remove items in larger layouts the blows caches). Future todo.
1 parent aed5a23 commit 77df15c

File tree

5 files changed

+162
-21
lines changed

5 files changed

+162
-21
lines changed

demo/column.html

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ <h1>setColumn() grid demo</h1>
2222
<div>
2323
<a class="btn btn-primary" id="add-widget" href="#">Add Widget</a>
2424
<a class="btn btn-primary" id="1column" href="#">1 Column</a>
25+
<a class="btn btn-primary" id="1columnDOM" href="#">1 Column DOM</a>
2526
<a class="btn btn-primary" id="2column" href="#">2 Column</a>
2627
<a class="btn btn-primary" id="3column" href="#">3 Column</a>
2728
<a class="btn btn-primary" id="4column" href="#">4 Column</a>
@@ -54,15 +55,15 @@ <h1>setColumn() grid demo</h1>
5455
var items = [
5556
{x: 0, y: 0, width: 2, height: 2},
5657
{x: 2, y: 0, width: 2, height: 1},
57-
{x: 5, y: 0, width: 1, height: 1},
58+
{x: 5, y: 1, width: 1, height: 1},
59+
{x: 5, y: 0, width: 2, height: 1},
5860
{text: ' auto'}, // autoPosition testing
59-
{x: 1, y: 3, width: 4, height: 1},
6061
{x: 5, y: 3, width: 2, height: 1},
6162
{x: 0, y: 4, width: 12, height: 1}
6263
];
6364
var count = 0;
6465
grid.batchUpdate();
65-
for (count=0; count<3;) {
66+
for (count=0; count<4;) {
6667
var n = items[count];
6768
grid.addWidget($('<div><div class="grid-stack-item-content">' + count++ + (n.text ? n.text : '') + '</div></div>'), n);
6869
};
@@ -78,7 +79,8 @@ <h1>setColumn() grid demo</h1>
7879
grid.addWidget($('<div><div class="grid-stack-item-content">' + count++ + (n.text ? n.text : '') + '</div></div>'), n);
7980
});
8081

81-
$('#1column').click(function() { grid.setColumn(1); $text.text(1);});
82+
$('#1column').click(function() { delete grid.opts.oneColumnModeDomSort; grid.setColumn(1); $text.text(1);});
83+
$('#1columnDOM').click(function() { grid.opts.oneColumnModeDomSort = true; grid.setColumn(1); $text.text('1 DOM');});
8284
$('#2column').click(function() { grid.setColumn(2); $text.text(2);});
8385
$('#3column').click(function() { grid.setColumn(3); $text.text(3);});
8486
$('#4column').click(function() { grid.setColumn(4); $text.text(4);});

doc/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Change log
2929

3030
## v0.6.1-dev (upcoming changes)
3131

32+
- add `oneColumnModeDomSort` true|false to let you specify a custom layout (use dom order instead of x,y) for oneColumnMode `setColumn(1)` [#713](https://github.com/gridstack/gridstack.js/issues/713)
3233
- fix oneColumnMode to only restore if we auto went to it as window sizes up [#1125](https://github.com/gridstack/gridstack.js/pull/1125)
3334

3435
## v0.6.1 (2020-02-02)

spec/gridstack-spec.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ describe('gridstack', function() {
1515
' </div>' +
1616
' </div>' +
1717
'</div>';
18+
// empty grid
19+
var gridstackEmptyHTML =
20+
'<div style="width: 992px; height: 800px" id="gs-cont">' +
21+
' <div class="grid-stack">' +
22+
' </div>' +
23+
'</div>';
1824
// generic widget with no param
1925
var widgetHTML = '<div class="grid-stack-item" id="item3"><div class="grid-stack-item-content"> hello </div></div>';
2026

@@ -441,6 +447,104 @@ describe('gridstack', function() {
441447
});
442448
});
443449

450+
describe('oneColumnModeDomSort', function() {
451+
beforeEach(function() {
452+
document.body.insertAdjacentHTML('afterbegin', gridstackEmptyHTML);
453+
});
454+
afterEach(function() {
455+
document.body.removeChild(document.getElementById('gs-cont'));
456+
});
457+
it('should support default going to 1 column', function() {
458+
var options = {
459+
column: 12,
460+
float: true
461+
};
462+
$('.grid-stack').gridstack(options);
463+
var grid = $('.grid-stack').data('gridstack');
464+
var el1 = grid.addWidget(widgetHTML, {width:1, height:1});
465+
var el2 = grid.addWidget(widgetHTML, {x:2, y:0, width:2, height:1});
466+
var el3 = grid.addWidget(widgetHTML, {x:1, y:0, width:1, height:2});
467+
468+
// items are item1[1x1], item3[1x1], item2[2x1]
469+
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
470+
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
471+
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
472+
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);
473+
474+
expect(parseInt(el3.attr('data-gs-x'))).toBe(1);
475+
expect(parseInt(el3.attr('data-gs-y'))).toBe(0);
476+
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
477+
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);
478+
479+
expect(parseInt(el2.attr('data-gs-x'))).toBe(2);
480+
expect(parseInt(el2.attr('data-gs-y'))).toBe(0);
481+
expect(parseInt(el2.attr('data-gs-width'))).toBe(2);
482+
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);
483+
484+
// items are item1[1x1], item3[1x2], item2[1x1] in 1 column
485+
grid.setColumn(1);
486+
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
487+
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
488+
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
489+
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);
490+
491+
expect(parseInt(el3.attr('data-gs-x'))).toBe(0);
492+
expect(parseInt(el3.attr('data-gs-y'))).toBe(1);
493+
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
494+
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);
495+
496+
expect(parseInt(el2.attr('data-gs-x'))).toBe(0);
497+
expect(parseInt(el2.attr('data-gs-y'))).toBe(3);
498+
expect(parseInt(el2.attr('data-gs-width'))).toBe(1);
499+
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);
500+
});
501+
it('should support oneColumnModeDomSort ON going to 1 column', function() {
502+
var options = {
503+
column: 12,
504+
oneColumnModeDomSort: true,
505+
float: true
506+
};
507+
$('.grid-stack').gridstack(options);
508+
var grid = $('.grid-stack').data('gridstack');
509+
var el1 = grid.addWidget(widgetHTML, {width:1, height:1});
510+
var el2 = grid.addWidget(widgetHTML, {x:2, y:0, width:2, height:1});
511+
var el3 = grid.addWidget(widgetHTML, {x:1, y:0, width:1, height:2});
512+
513+
// items are item1[1x1], item3[1x1], item2[2x1]
514+
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
515+
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
516+
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
517+
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);
518+
519+
expect(parseInt(el3.attr('data-gs-x'))).toBe(1);
520+
expect(parseInt(el3.attr('data-gs-y'))).toBe(0);
521+
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
522+
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);
523+
524+
expect(parseInt(el2.attr('data-gs-x'))).toBe(2);
525+
expect(parseInt(el2.attr('data-gs-y'))).toBe(0);
526+
expect(parseInt(el2.attr('data-gs-width'))).toBe(2);
527+
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);
528+
529+
// items are item1[1x1], item2[1x1], item3[1x2] in 1 column dom ordered
530+
grid.setColumn(1);
531+
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
532+
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
533+
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
534+
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);
535+
536+
expect(parseInt(el2.attr('data-gs-x'))).toBe(0);
537+
expect(parseInt(el2.attr('data-gs-y'))).toBe(1);
538+
expect(parseInt(el2.attr('data-gs-width'))).toBe(1);
539+
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);
540+
541+
expect(parseInt(el3.attr('data-gs-x'))).toBe(0);
542+
expect(parseInt(el3.attr('data-gs-y'))).toBe(2);
543+
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
544+
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);
545+
});
546+
});
547+
444548
describe('grid.minWidth', function() {
445549
beforeEach(function() {
446550
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);

src/gridstack.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,12 @@ interface GridstackOptions {
496496
*/
497497
oneColumnModeClass ? : string;
498498

499+
/**
500+
* set to true if you want oneColumnMode to use the DOM order and ignore x,y from normal multi column
501+
* layouts during sorting. This enables you to have custom 1 column layout that differ from the rest. (default?: false)
502+
*/
503+
oneColumnModeDomSort?: boolean;
504+
499505
/**
500506
* class for placeholder (default?: 'grid-stack-placeholder')
501507
*/

src/gridstack.js

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@
5656
return !(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y);
5757
},
5858

59-
sort: function(nodes, dir, width) {
60-
if (!width) {
59+
sort: function(nodes, dir, column) {
60+
if (!column) {
6161
var widths = nodes.map(function(node) { return node.x + node.width; });
62-
width = Math.max.apply(Math, widths);
62+
column = Math.max.apply(Math, widths);
6363
}
6464

6565
if (dir === -1)
66-
return Utils.sortBy(nodes, function(n) { return -(n.x + n.y * width); });
66+
return Utils.sortBy(nodes, function(n) { return -(n.x + n.y * column); });
6767
else
68-
return Utils.sortBy(nodes, function(n) { return (n.x + n.y * width); });
68+
return Utils.sortBy(nodes, function(n) { return (n.x + n.y * column); });
6969
},
7070

7171
createStylesheet: function(id) {
@@ -721,6 +721,7 @@
721721
cellHeightUnit: 'px',
722722
disableOneColumnMode: opts.disableOneColumnMode || false,
723723
oneColumnModeClass: opts.oneColumnModeClass || 'grid-stack-one-column-mode',
724+
oneColumnModeDomSort: opts.oneColumnModeDomSort,
724725
ddPlugin: null
725726
});
726727

@@ -1823,20 +1824,37 @@
18231824
* Called to scale the widget width & position up/down based on the column change.
18241825
* Note we store previous layouts (especially original ones) to make it possible to go
18251826
* from say 12 -> 1 -> 12 and get back to where we were.
1827+
*
1828+
* oldColumn: previous number of columns
1829+
* column: new column number
1830+
* nodes?: different sorted list (ex: DOM order) instead of current list
18261831
*/
1827-
GridStackEngine.prototype._updateNodeWidths = function(oldColumn, column) {
1828-
if (this.nodes.length === 0 || oldColumn === column) { return; }
1829-
var nodes = Utils.sort(this.nodes, -1, oldColumn); // current column reverse sorting so we can insert last to front (limit collision)
1832+
GridStackEngine.prototype._updateNodeWidths = function(oldColumn, column, nodes) {
1833+
if (!this.nodes.length || oldColumn === column) { return; }
18301834

18311835
// cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data
1832-
var copy = [nodes.length];
1833-
nodes.forEach(function(n, i) {copy[i] = {x: n.x, y: n.y, width: n.width, _id: n._id}}); // only thing we change is x,y,w and id to find it back
1836+
var copy = [this.nodes.length];
1837+
this.nodes.forEach(function(n, i) {copy[i] = {x: n.x, y: n.y, width: n.width, _id: n._id}}); // only thing we change is x,y,w and id to find it back
18341838
this._layouts = this._layouts || []; // use array to find larger quick
18351839
this._layouts[oldColumn] = copy;
18361840

1837-
// see if we have cached previous layout. if NOT and we are going up in size start with the largest layout as down-scaling is more accurate
1838-
var lastIndex = this._layouts.length - 1;
1841+
// if we're going to 1 column and using DOM order rather than default sorting, then generate that layout
1842+
if (column === 1 && nodes && nodes.length) {
1843+
var top = 0;
1844+
nodes.forEach(function(n) {
1845+
n.x = 0;
1846+
n.width = 1;
1847+
n.y = Math.max(n.y, top);
1848+
top = n.y + n.height;
1849+
});
1850+
} else {
1851+
nodes = Utils.sort(this.nodes, -1, oldColumn); // current column reverse sorting so we can insert last to front (limit collision)
1852+
}
1853+
1854+
// see if we have cached previous layout.
18391855
var cacheNodes = this._layouts[column] || [];
1856+
// if not AND we are going up in size start with the largest layout as down-scaling is more accurate
1857+
var lastIndex = this._layouts.length - 1;
18401858
if (cacheNodes.length === 0 && column > oldColumn && column < lastIndex) {
18411859
cacheNodes = this._layouts[lastIndex] || [];
18421860
if (cacheNodes.length) {
@@ -1872,13 +1890,13 @@
18721890
var ratio = column / oldColumn;
18731891
nodes.forEach(function(node) {
18741892
if (!node) return;
1875-
node.x = Math.round(node.x * ratio);
1876-
node.width = (oldColumn === 1 ? 1 : (Math.round(node.width * ratio) || 1));
1893+
node.x = (column === 1 ? 0 : Math.round(node.x * ratio));
1894+
node.width = ((column === 1 || oldColumn === 1) ? 1 : (Math.round(node.width * ratio) || 1));
18771895
newNodes.push(node);
18781896
});
1879-
newNodes = Utils.sort(newNodes, -1, column);
18801897

18811898
// finally relayout them in reverse order (to get correct placement)
1899+
newNodes = Utils.sort(newNodes, -1, column);
18821900
this._ignoreLayoutsNodeChange = true;
18831901
this.batchUpdate();
18841902
this.nodes = []; // pretend we have no nodes to start with (we use same structures) to simplify layout
@@ -1913,9 +1931,19 @@
19131931
this.container.addClass('grid-stack-' + column);
19141932
this.opts.column = this.grid.column = column;
19151933

1916-
// update the items now
19171934
if (doNotPropagate === true) { return; }
1918-
this.grid._updateNodeWidths(oldColumn, column);
1935+
1936+
// update the items now - see if the dom order nodes should be passed instead (else default to current list)
1937+
var domNodes;
1938+
if (this.opts.oneColumnModeDomSort && column === 1) {
1939+
domNodes = [];
1940+
this.container.children('.' + this.opts.itemClass).each(function(index, el) {
1941+
var node = $(el).data('_gridstack_node');
1942+
if (node) { domNodes.push(node); }
1943+
});
1944+
if (!domNodes.length) { domNodes = undefined; }
1945+
}
1946+
this.grid._updateNodeWidths(oldColumn, column, domNodes);
19191947

19201948
// and trigger our event last...
19211949
this.grid._ignoreLayoutsNodeChange = true;

0 commit comments

Comments
 (0)