Skip to content
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
10 changes: 6 additions & 4 deletions demo/column.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ <h1>setColumn() grid demo</h1>
<div>
<a class="btn btn-primary" id="add-widget" href="#">Add Widget</a>
<a class="btn btn-primary" id="1column" href="#">1 Column</a>
<a class="btn btn-primary" id="1columnDOM" href="#">1 Column DOM</a>
<a class="btn btn-primary" id="2column" href="#">2 Column</a>
<a class="btn btn-primary" id="3column" href="#">3 Column</a>
<a class="btn btn-primary" id="4column" href="#">4 Column</a>
Expand Down Expand Up @@ -54,15 +55,15 @@ <h1>setColumn() grid demo</h1>
var items = [
{x: 0, y: 0, width: 2, height: 2},
{x: 2, y: 0, width: 2, height: 1},
{x: 5, y: 0, width: 1, height: 1},
{x: 5, y: 1, width: 1, height: 1},
{x: 5, y: 0, width: 2, height: 1},
{text: ' auto'}, // autoPosition testing
{x: 1, y: 3, width: 4, height: 1},
{x: 5, y: 3, width: 2, height: 1},
{x: 0, y: 4, width: 12, height: 1}
];
var count = 0;
grid.batchUpdate();
for (count=0; count<3;) {
for (count=0; count<4;) {
var n = items[count];
grid.addWidget($('<div><div class="grid-stack-item-content">' + count++ + (n.text ? n.text : '') + '</div></div>'), n);
};
Expand All @@ -78,7 +79,8 @@ <h1>setColumn() grid demo</h1>
grid.addWidget($('<div><div class="grid-stack-item-content">' + count++ + (n.text ? n.text : '') + '</div></div>'), n);
});

$('#1column').click(function() { grid.setColumn(1); $text.text(1);});
$('#1column').click(function() { delete grid.opts.oneColumnModeDomSort; grid.setColumn(1); $text.text(1);});
$('#1columnDOM').click(function() { grid.opts.oneColumnModeDomSort = true; grid.setColumn(1); $text.text('1 DOM');});
$('#2column').click(function() { grid.setColumn(2); $text.text(2);});
$('#3column').click(function() { grid.setColumn(3); $text.text(3);});
$('#4column').click(function() { grid.setColumn(4); $text.text(4);});
Expand Down
1 change: 1 addition & 0 deletions doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Change log

## v0.6.1-dev (upcoming changes)

- 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)
- fix oneColumnMode to only restore if we auto went to it as window sizes up [#1125](https://github.com/gridstack/gridstack.js/pull/1125)

## v0.6.1 (2020-02-02)
Expand Down
104 changes: 104 additions & 0 deletions spec/gridstack-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ describe('gridstack', function() {
' </div>' +
' </div>' +
'</div>';
// empty grid
var gridstackEmptyHTML =
'<div style="width: 992px; height: 800px" id="gs-cont">' +
' <div class="grid-stack">' +
' </div>' +
'</div>';
// generic widget with no param
var widgetHTML = '<div class="grid-stack-item" id="item3"><div class="grid-stack-item-content"> hello </div></div>';

Expand Down Expand Up @@ -441,6 +447,104 @@ describe('gridstack', function() {
});
});

describe('oneColumnModeDomSort', function() {
beforeEach(function() {
document.body.insertAdjacentHTML('afterbegin', gridstackEmptyHTML);
});
afterEach(function() {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should support default going to 1 column', function() {
var options = {
column: 12,
float: true
};
$('.grid-stack').gridstack(options);
var grid = $('.grid-stack').data('gridstack');
var el1 = grid.addWidget(widgetHTML, {width:1, height:1});
var el2 = grid.addWidget(widgetHTML, {x:2, y:0, width:2, height:1});
var el3 = grid.addWidget(widgetHTML, {x:1, y:0, width:1, height:2});

// items are item1[1x1], item3[1x1], item2[2x1]
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);

expect(parseInt(el3.attr('data-gs-x'))).toBe(1);
expect(parseInt(el3.attr('data-gs-y'))).toBe(0);
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);

expect(parseInt(el2.attr('data-gs-x'))).toBe(2);
expect(parseInt(el2.attr('data-gs-y'))).toBe(0);
expect(parseInt(el2.attr('data-gs-width'))).toBe(2);
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);

// items are item1[1x1], item3[1x2], item2[1x1] in 1 column
grid.setColumn(1);
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);

expect(parseInt(el3.attr('data-gs-x'))).toBe(0);
expect(parseInt(el3.attr('data-gs-y'))).toBe(1);
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);

expect(parseInt(el2.attr('data-gs-x'))).toBe(0);
expect(parseInt(el2.attr('data-gs-y'))).toBe(3);
expect(parseInt(el2.attr('data-gs-width'))).toBe(1);
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);
});
it('should support oneColumnModeDomSort ON going to 1 column', function() {
var options = {
column: 12,
oneColumnModeDomSort: true,
float: true
};
$('.grid-stack').gridstack(options);
var grid = $('.grid-stack').data('gridstack');
var el1 = grid.addWidget(widgetHTML, {width:1, height:1});
var el2 = grid.addWidget(widgetHTML, {x:2, y:0, width:2, height:1});
var el3 = grid.addWidget(widgetHTML, {x:1, y:0, width:1, height:2});

// items are item1[1x1], item3[1x1], item2[2x1]
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);

expect(parseInt(el3.attr('data-gs-x'))).toBe(1);
expect(parseInt(el3.attr('data-gs-y'))).toBe(0);
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);

expect(parseInt(el2.attr('data-gs-x'))).toBe(2);
expect(parseInt(el2.attr('data-gs-y'))).toBe(0);
expect(parseInt(el2.attr('data-gs-width'))).toBe(2);
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);

// items are item1[1x1], item2[1x1], item3[1x2] in 1 column dom ordered
grid.setColumn(1);
expect(parseInt(el1.attr('data-gs-x'))).toBe(0);
expect(parseInt(el1.attr('data-gs-y'))).toBe(0);
expect(parseInt(el1.attr('data-gs-width'))).toBe(1);
expect(parseInt(el1.attr('data-gs-height'))).toBe(1);

expect(parseInt(el2.attr('data-gs-x'))).toBe(0);
expect(parseInt(el2.attr('data-gs-y'))).toBe(1);
expect(parseInt(el2.attr('data-gs-width'))).toBe(1);
expect(parseInt(el2.attr('data-gs-height'))).toBe(1);

expect(parseInt(el3.attr('data-gs-x'))).toBe(0);
expect(parseInt(el3.attr('data-gs-y'))).toBe(2);
expect(parseInt(el3.attr('data-gs-width'))).toBe(1);
expect(parseInt(el3.attr('data-gs-height'))).toBe(2);
});
});

describe('grid.minWidth', function() {
beforeEach(function() {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
Expand Down
6 changes: 6 additions & 0 deletions src/gridstack.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,12 @@ interface GridstackOptions {
*/
oneColumnModeClass ? : string;

/**
* set to true if you want oneColumnMode to use the DOM order and ignore x,y from normal multi column
* layouts during sorting. This enables you to have custom 1 column layout that differ from the rest. (default?: false)
*/
oneColumnModeDomSort?: boolean;

/**
* class for placeholder (default?: 'grid-stack-placeholder')
*/
Expand Down
62 changes: 45 additions & 17 deletions src/gridstack.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@
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);
},

sort: function(nodes, dir, width) {
if (!width) {
sort: function(nodes, dir, column) {
if (!column) {
var widths = nodes.map(function(node) { return node.x + node.width; });
width = Math.max.apply(Math, widths);
column = Math.max.apply(Math, widths);
}

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

createStylesheet: function(id) {
Expand Down Expand Up @@ -721,6 +721,7 @@
cellHeightUnit: 'px',
disableOneColumnMode: opts.disableOneColumnMode || false,
oneColumnModeClass: opts.oneColumnModeClass || 'grid-stack-one-column-mode',
oneColumnModeDomSort: opts.oneColumnModeDomSort,
ddPlugin: null
});

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

// cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data
var copy = [nodes.length];
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
var copy = [this.nodes.length];
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
this._layouts = this._layouts || []; // use array to find larger quick
this._layouts[oldColumn] = copy;

// 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
var lastIndex = this._layouts.length - 1;
// if we're going to 1 column and using DOM order rather than default sorting, then generate that layout
if (column === 1 && nodes && nodes.length) {
var top = 0;
nodes.forEach(function(n) {
n.x = 0;
n.width = 1;
n.y = Math.max(n.y, top);
top = n.y + n.height;
});
} else {
nodes = Utils.sort(this.nodes, -1, oldColumn); // current column reverse sorting so we can insert last to front (limit collision)
}

// see if we have cached previous layout.
var cacheNodes = this._layouts[column] || [];
// if not AND we are going up in size start with the largest layout as down-scaling is more accurate
var lastIndex = this._layouts.length - 1;
if (cacheNodes.length === 0 && column > oldColumn && column < lastIndex) {
cacheNodes = this._layouts[lastIndex] || [];
if (cacheNodes.length) {
Expand Down Expand Up @@ -1872,13 +1890,13 @@
var ratio = column / oldColumn;
nodes.forEach(function(node) {
if (!node) return;
node.x = Math.round(node.x * ratio);
node.width = (oldColumn === 1 ? 1 : (Math.round(node.width * ratio) || 1));
node.x = (column === 1 ? 0 : Math.round(node.x * ratio));
node.width = ((column === 1 || oldColumn === 1) ? 1 : (Math.round(node.width * ratio) || 1));
newNodes.push(node);
});
newNodes = Utils.sort(newNodes, -1, column);

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

// update the items now
if (doNotPropagate === true) { return; }
this.grid._updateNodeWidths(oldColumn, column);

// update the items now - see if the dom order nodes should be passed instead (else default to current list)
var domNodes;
if (this.opts.oneColumnModeDomSort && column === 1) {
domNodes = [];
this.container.children('.' + this.opts.itemClass).each(function(index, el) {
var node = $(el).data('_gridstack_node');
if (node) { domNodes.push(node); }
});
if (!domNodes.length) { domNodes = undefined; }
}
this.grid._updateNodeWidths(oldColumn, column, domNodes);

// and trigger our event last...
this.grid._ignoreLayoutsNodeChange = true;
Expand Down