Skip to content

Commit dd932b6

Browse files
committed
better drag-out/in from below heuristics
* fix #1570 * we now have code to check if dragging outside of the grid (`engine.isOutside()`) which checks for items past maxRow or added as last row in a real position vs being dragged out.
1 parent f0fa426 commit dd932b6

File tree

5 files changed

+41
-18
lines changed

5 files changed

+41
-18
lines changed

doc/CHANGES.md

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

5050
- fix [1572](https://github.com/gridstack/gridstack.js/issues/1572) `column: N` option now sets CSS class
5151
- fix [1571](https://github.com/gridstack/gridstack.js/issues/1571) don't allow drop when grid is full
52+
- fix [1570](https://github.com/gridstack/gridstack.js/issues/1570) easier to drag out/in from below
5253

5354
## 3.1.4 (2021-1-11)
5455

spec/e2e/html/1570_drag_bottom_max_row.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1">
77
<title>drop onto full</title>
88

9-
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
109
<link rel="stylesheet" href="../../../dist/gridstack.min.css"/>
1110
<script src="../../../dist/gridstack-h5.js"></script>
1211

@@ -89,7 +88,7 @@
8988
let options2 = {
9089
disableOneColumnMode: true,
9190
minRow: 3,
92-
// maxRow: 3, // change this to show issue
91+
maxRow: 3, // change this to show issue
9392
acceptWidgets: true,
9493
removable: true,
9594
removeTimeout: 0,

spec/e2e/html/1572_one_column.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ <h1>1 column demo</h1>
3030
let count = 0;
3131
let items = [{x: 0, y: 0, w: 1, h:1, content: String(count++)}];
3232

33-
let grid = GridStack.init({column: 1});
33+
let grid = GridStack.init({column: 1, minRow: 1});
3434
grid.load(items);
3535
addEvents(grid);
3636

src/gridstack-dd.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid
392392
/** called when item is being dragged/resized */
393393
let dragOrResize = (event: Event, ui: DDUIData): void => {
394394
let x = Math.round(ui.position.left / cellWidth);
395-
let y = Math.floor((ui.position.top + cellHeight / 2) / cellHeight);
395+
let y = Math.round(ui.position.top / cellHeight);
396396
let w: number;
397397
let h: number;
398398

@@ -401,7 +401,7 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid
401401
node._prevYPix = ui.position.top;
402402
Utils.updateScrollPosition(el, ui.position, distance);
403403
// if inTrash, outside of the bounds or added to another grid (#393) temporarily remove it from us
404-
if (el.dataset.inTrashZone || x < 0 || x >= this.engine.column || y < 0 || (!this.engine.float && y > this.engine.getRow()) || node._added) {
404+
if (el.dataset.inTrashZone || node._added || this.engine.isOutside(x, y, node)) {
405405
if (node._temporaryRemoved) return;
406406
if (this.opts.removable === true) {
407407
this._setupRemovingTimeout(el);

src/gridstack-engine.ts

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class GridStackEngine {
8181
nn = {x: 0, y: node.y, w: this.column, h: node.h};
8282
}
8383
while (true) {
84-
let collisionNode = this.nodes.find( n => n !== node && Utils.isIntercepted(n, nn), {node: node, nn: nn});
84+
let collisionNode = this.collide(node, nn);
8585
if (!collisionNode) return this;
8686
let moved;
8787
if (collisionNode.locked) {
@@ -96,12 +96,14 @@ export class GridStackEngine {
9696
}
9797
}
9898

99+
/** return any intercepted node with the given area, skipping the passed in node (usually self) */
100+
public collide(node: GridStackNode, area: GridStackNode = node): GridStackNode {
101+
return this.nodes.find(n => n !== node && Utils.isIntercepted(n, area));
102+
}
103+
99104
public isAreaEmpty(x: number, y: number, w: number, h: number): boolean {
100105
let nn: GridStackNode = {x: x || 0, y: y || 0, w: w || 1, h: h || 1};
101-
let collisionNode = this.nodes.find(n => {
102-
return Utils.isIntercepted(n, nn);
103-
});
104-
return !collisionNode;
106+
return !this.collide(nn);
105107
}
106108

107109
/** re-layout grid items to reclaim any empty space */
@@ -153,9 +155,7 @@ export class GridStackEngine {
153155
let newY = n.y;
154156
while (newY >= n._packY) {
155157
let box: GridStackWidget = {x: n.x, y: newY, w: n.w, h: n.h};
156-
let collisionNode = this.nodes
157-
.slice(0, i)
158-
.find(bn => Utils.isIntercepted(box, bn), {n: n, newY: newY});
158+
let collisionNode = this.nodes.slice(0, i).find(bn => Utils.isIntercepted(box, bn));
159159
if (!collisionNode) {
160160
n._dirty = true;
161161
n.y = newY;
@@ -171,10 +171,8 @@ export class GridStackEngine {
171171
let canBeMoved = i === 0;
172172
let box: GridStackWidget = {x: n.x, y: newY, w: n.w, h: n.h};
173173
if (i > 0) {
174-
let collisionNode = this.nodes
175-
.slice(0, i)
176-
.find(bn => Utils.isIntercepted(box, bn), {n: n, newY: newY});
177-
canBeMoved = collisionNode === undefined;
174+
let collisionNode = this.nodes.slice(0, i).find(bn => Utils.isIntercepted(box, bn));
175+
canBeMoved = !collisionNode;
178176
}
179177

180178
if (!canBeMoved) { break; }
@@ -312,7 +310,7 @@ export class GridStackEngine {
312310
continue;
313311
}
314312
let box = {x, y, w: node.w, h: node.h};
315-
if (!this.nodes.find(n => Utils.isIntercepted(box, n), {x, y, node})) {
313+
if (!this.nodes.find(n => Utils.isIntercepted(box, n))) {
316314
node.x = x;
317315
node.y = y;
318316
delete node.autoPosition; // found our slot
@@ -409,6 +407,31 @@ export class GridStackEngine {
409407
return clone.getRow() <= this.maxRow;
410408
}
411409

410+
/** return true if the passed in node (x,y) is being dragged outside of the grid, and not added to bottom */
411+
public isOutside(x: number, y: number, node: GridStackNode): boolean {
412+
// simple outside boundaries
413+
if (x < 0 || x >= this.column || y < 0) return true;
414+
if (this.maxRow) return (y >= this.maxRow);
415+
else if (this.float) return false; // infinite grow with no maxRow
416+
417+
// see if dragging PAST bottom (row+1)
418+
let row = this.getRow();
419+
if (y < row || y === 0) return false;
420+
if (y > row) return true;
421+
// else check to see if we can add that item to the bottom... (y == row)
422+
if (!node._temporaryRemoved) {
423+
let clone = new GridStackEngine({
424+
column: this.column,
425+
float: this.float,
426+
nodes: this.nodes.filter(n => n !== node).map(n => {return {...n}})
427+
});
428+
let nn = {...node, x, y};
429+
clone.addNode(nn);
430+
return nn.y === node.y && nn.x === node.x; // didn't actually move, so last row was a drag out and not a new place...
431+
}
432+
return node._temporaryRemoved; // if still outside so we don't flicker back & forth
433+
}
434+
412435
public isNodeChangedPosition(node: GridStackNode, x: number, y: number, w?: number, h?: number): boolean {
413436
if (typeof x !== 'number') { x = node.x; }
414437
if (typeof y !== 'number') { y = node.y; }

0 commit comments

Comments
 (0)