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
3 changes: 3 additions & 0 deletions doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ Change log
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## 3.3.0-dev

- fix [#149](https://github.com/gridstack/gridstack.js/issues/149) [#1094](https://github.com/gridstack/gridstack.js/issues/1094) [#1605](https://github.com/gridstack/gridstack.js/issues/1605) re-write of the **collision code**! you can now swap items of the same size (vertical/horizontal) when grid is full, and is the default in `float:false` (top gravity) as it feels more natural. Could add Alt key for swap vs push behavior later.<br>
Dragging up and down now behave the same (used to require push WAY down past to swap/append). Also much more efficient collision code.<br>
Still TODO: handle mid point of dragged over items rather 50% of row/column and check for the most covered when multiple items collide.
- fix [1617](https://github.com/gridstack/gridstack.js/issues/1617) FireFox DOM order issue. Thanks [@marcel-necker](https://github.com/marcel-necker)

## 3.3.0 (2021-2-2)
Expand Down
92 changes: 92 additions & 0 deletions spec/e2e/html/141_1534_swap.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>swap demo</title>

<link rel="stylesheet" href="../../../demo/demo.css"/>
<script src="../../../dist/gridstack-h5.js"></script>

</head>
<body>
<div class="container-fluid">
<h1>Swap collision demo</h1>
<div>
<a class="btn btn-primary" onClick="addNewWidget()" href="#">Add Widget</a>
<a class="btn btn-primary" onclick="toggleFloat()" id="float" href="#"></a>
<a class="btn btn-primary" onclick="toggleMax()" id="max" href="#"></a>
<a class="btn btn-primary" onclick="toggleBigger()" id="big" href="#"></a>
</div>
<br><br>
<div class="grid-stack"></div>
</div>
<script src="../../../demo/events.js"></script>
<script type="text/javascript">
let floatButton = document.getElementById('float');
let maxButton = document.getElementById('max');
let bigButton = document.getElementById('big');
let size = 1;

let grid = GridStack.init({float: false, cellHeight: 70, maxRow: 0});
addEvents(grid);

let count = 0;
let items = [
{x:0, y:0}, {x:0, y:1}, {x:0, y:2},{x:1, y:0}, {x:1, y:1}, {x:1, y:2},
{x:5, y:0}, {x:4, y:1, w:3, locked: false, _content: 'locked'}, {x:5, y:2},
{x:7, y:0}, {x:8, y:0}, {x:9, y:0}, {x:7, y:1, w:3}, {x:8, y:2},
{x:11, y:0}, {x:11, y:1, h:2},
];
items.forEach(n => {n.id = count; n.content = n.content || String(count); count++})
grid.load(items);

addNewWidget = function() {
let n = {
x: Math.round(12 * Math.random()),
y: Math.round(5 * Math.random()),
w: Math.round(1 + 3 * Math.random()),
h: Math.round(1 + 3 * Math.random()),
content: String(count++)
};
grid.addWidget(n);
};

toggleFloat = function() {
grid.float(! grid.getFloat());
floatButton.innerHTML = 'float: ' + grid.getFloat();
};
floatButton.innerHTML = 'float: ' + grid.getFloat();

toggleMax = function() {
grid.opts.maxRow = grid.engine.maxRow = grid.opts.maxRow ? 0 : 3;
maxButton.innerHTML = 'Max: ' + grid.opts.maxRow;
};
maxButton.innerHTML = 'Max: ' + grid.opts.maxRow;

toggleBigger = function() {
size = size === 1 ? 2 : 1;
setSize(size);
};
setSize = function(size) {
items.sort((a,b) => a.id - b.id);
items.forEach((n,i) => {
if (i<6) {
n.w = n.h = size;
n.y = i * size;
if (n.x) n.x = size;
} else {
n.h = size;
}
});
grid.opts.maxRow = grid.engine.maxRow = grid.opts.maxRow ? (size === 1 ? 3 : 6) : 0;
grid.load(items);
bigButton.innerHTML = 'Size: ' + size;
maxButton.innerHTML = 'Max: ' + grid.opts.maxRow;
}
bigButton.innerHTML = 'Size: ' + size;
if (size !== 1) setSize(size);
</script>
</body>
</html>
63 changes: 63 additions & 0 deletions spec/e2e/html/141_swap_old.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>old swap</title>

<link rel="stylesheet" href="../../../demo/demo.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gridstack@1.0.0/dist/gridstack.min.css"/>
<script src="https://cdn.jsdelivr.net/npm/gridstack@1.0.0/dist/gridstack.all.js"></script>

</head>
<body>
<div class="container-fluid">
<h1>Swap collision demo from older 1.x builds</h1>
<div>
<a class="btn btn-primary" onClick="addNewWidget()" href="#">Add Widget</a>
<a class="btn btn-primary" onclick="toggleFloat()" id="float" href="#"></a>
<a class="btn btn-primary" onclick="toggleMax()" id="max" href="#"></a>
</div>
<br><br>
<div class="grid-stack">
<div class="grid-stack-item" data-gs-x="0" data-gs-y="0"><div class="grid-stack-item-content">0</div></div>
<div class="grid-stack-item" data-gs-x="0" data-gs-y="1"><div class="grid-stack-item-content">1</div></div>
<div class="grid-stack-item" data-gs-x="0" data-gs-y="2"><div class="grid-stack-item-content">2</div></div>
<div class="grid-stack-item" data-gs-x="1" data-gs-y="0"><div class="grid-stack-item-content">3</div></div>
<div class="grid-stack-item" data-gs-x="1" data-gs-y="1"><div class="grid-stack-item-content">4</div></div>
<div class="grid-stack-item" data-gs-x="1" data-gs-y="2"><div class="grid-stack-item-content">5</div></div>
</div>
</div>
<script type="text/javascript">
let floatButton = document.getElementById('float');
let maxButton = document.getElementById('max');
let count = 6;
let grid = GridStack.init({float: false, cellHeight: 70, maxRow: 3});

addNewWidget = function() {
let n = {
x: Math.round(12 * Math.random()),
y: Math.round(5 * Math.random()),
w: Math.round(1 + 3 * Math.random()),
h: Math.round(1 + 3 * Math.random()),
content: String(count++)
};
grid.addWidget(n);
};

toggleFloat = function() {
grid.float(! grid.getFloat());
floatButton.innerHTML = 'float: ' + grid.float();
};
floatButton.innerHTML = 'float: ' + grid.float();

toggleMax = function() {
grid.opts.maxRow = grid.engine.maxRow = grid.opts.maxRow ? 0 : 3;
maxButton.innerHTML = 'Max: ' + grid.opts.maxRow;
};
maxButton.innerHTML = 'Max: ' + grid.opts.maxRow;

</script>
</body>
</html>
28 changes: 15 additions & 13 deletions spec/gridstack-engine-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ describe('gridstack engine', function() {
describe('batch update', function() {

it('should set float and batchMode when calling batchUpdate.', function() {
// Note: legacy weird call on global window to hold data
e.prototype.batchUpdate.call(w);
expect(w.float).toBe(undefined);
expect(w.batchMode).toBeTrue();
engine = new GridStackEngine({float: true});
engine.batchUpdate();
expect(engine.float).toBe(true);
expect(engine.batchMode).toBeTrue();
});
});

Expand Down Expand Up @@ -327,29 +327,29 @@ describe('gridstack engine', function() {
});
});

describe('test isNodeChangedPosition', function() {
describe('test changedPos', function() {
beforeAll(function() {
engine = new GridStackEngine();
});
it('should return true for changed x', function() {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(engine.isNodeChangedPosition(widget, 2, 2)).toEqual(true);
expect(engine.changedPos(widget, 2, 2)).toEqual(true);
});
it('should return true for changed y', function() {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(engine.isNodeChangedPosition(widget, 1, 1)).toEqual(true);
expect(engine.changedPos(widget, 1, 1)).toEqual(true);
});
it('should return true for changed width', function() {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(engine.isNodeChangedPosition(widget, 2, 2, 4, 4)).toEqual(true);
expect(engine.changedPos(widget, 2, 2, 4, 4)).toEqual(true);
});
it('should return true for changed height', function() {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(engine.isNodeChangedPosition(widget, 1, 2, 3, 3)).toEqual(true);
expect(engine.changedPos(widget, 1, 2, 3, 3)).toEqual(true);
});
it('should return false for unchanged position', function() {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(engine.isNodeChangedPosition(widget, 1, 2, 3, 4)).toEqual(false);
expect(engine.changedPos(widget, 1, 2, 3, 4)).toEqual(false);
});
});

Expand All @@ -371,13 +371,15 @@ describe('gridstack engine', function() {
expect(findNode(engine, 2)).toEqual(jasmine.objectContaining({x: 1, y: 2}));
// prevents moving locked item
let node1 = findNode(engine, 1);
expect(engine.moveNode(node1, 6, 6)).toEqual(null);
expect(engine.moveNode(node1, 6, 6)).toEqual(false);
// but moves regular one (gravity ON)
let node2 = findNode(engine, 2);
expect(engine.moveNode(node2, 6, 6)).toEqual(jasmine.objectContaining({x: 6, y: 2, w: 2, h: 3,}));
expect(engine.moveNode(node2, 6, 6)).toEqual(true);
expect(node2).toEqual(jasmine.objectContaining({x: 6, y: 2, w: 2, h: 3}));
// but moves regular one (gravity OFF)
engine.float = true;
expect(engine.moveNode(node2, 7, 6)).toEqual(jasmine.objectContaining({x: 7, y: 6, w: 2, h: 3,}));
expect(engine.moveNode(node2, 7, 6)).toEqual(true);
expect(node2).toEqual(jasmine.objectContaining({x: 7, y: 6, w: 2, h: 3}));
});
});

Expand Down
Loading