Skip to content

Commit

Permalink
better drag-out/in from below heuristics
Browse files Browse the repository at this point in the history
* fix gridstack#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.
  • Loading branch information
adumesny committed Jan 20, 2021
1 parent f0fa426 commit dd932b6
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 18 deletions.
1 change: 1 addition & 0 deletions doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Change log

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

## 3.1.4 (2021-1-11)

Expand Down
3 changes: 1 addition & 2 deletions spec/e2e/html/1570_drag_bottom_max_row.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>drop onto full</title>

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

Expand Down Expand Up @@ -89,7 +88,7 @@
let options2 = {
disableOneColumnMode: true,
minRow: 3,
// maxRow: 3, // change this to show issue
maxRow: 3, // change this to show issue
acceptWidgets: true,
removable: true,
removeTimeout: 0,
Expand Down
2 changes: 1 addition & 1 deletion spec/e2e/html/1572_one_column.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ <h1>1 column demo</h1>
let count = 0;
let items = [{x: 0, y: 0, w: 1, h:1, content: String(count++)}];

let grid = GridStack.init({column: 1});
let grid = GridStack.init({column: 1, minRow: 1});
grid.load(items);
addEvents(grid);

Expand Down
4 changes: 2 additions & 2 deletions src/gridstack-dd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid
/** called when item is being dragged/resized */
let dragOrResize = (event: Event, ui: DDUIData): void => {
let x = Math.round(ui.position.left / cellWidth);
let y = Math.floor((ui.position.top + cellHeight / 2) / cellHeight);
let y = Math.round(ui.position.top / cellHeight);
let w: number;
let h: number;

Expand All @@ -401,7 +401,7 @@ GridStack.prototype._prepareDragDropByNode = function(node: GridStackNode): Grid
node._prevYPix = ui.position.top;
Utils.updateScrollPosition(el, ui.position, distance);
// if inTrash, outside of the bounds or added to another grid (#393) temporarily remove it from us
if (el.dataset.inTrashZone || x < 0 || x >= this.engine.column || y < 0 || (!this.engine.float && y > this.engine.getRow()) || node._added) {
if (el.dataset.inTrashZone || node._added || this.engine.isOutside(x, y, node)) {
if (node._temporaryRemoved) return;
if (this.opts.removable === true) {
this._setupRemovingTimeout(el);
Expand Down
49 changes: 36 additions & 13 deletions src/gridstack-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class GridStackEngine {
nn = {x: 0, y: node.y, w: this.column, h: node.h};
}
while (true) {
let collisionNode = this.nodes.find( n => n !== node && Utils.isIntercepted(n, nn), {node: node, nn: nn});
let collisionNode = this.collide(node, nn);
if (!collisionNode) return this;
let moved;
if (collisionNode.locked) {
Expand All @@ -96,12 +96,14 @@ export class GridStackEngine {
}
}

/** return any intercepted node with the given area, skipping the passed in node (usually self) */
public collide(node: GridStackNode, area: GridStackNode = node): GridStackNode {
return this.nodes.find(n => n !== node && Utils.isIntercepted(n, area));
}

public isAreaEmpty(x: number, y: number, w: number, h: number): boolean {
let nn: GridStackNode = {x: x || 0, y: y || 0, w: w || 1, h: h || 1};
let collisionNode = this.nodes.find(n => {
return Utils.isIntercepted(n, nn);
});
return !collisionNode;
return !this.collide(nn);
}

/** re-layout grid items to reclaim any empty space */
Expand Down Expand Up @@ -153,9 +155,7 @@ export class GridStackEngine {
let newY = n.y;
while (newY >= n._packY) {
let box: GridStackWidget = {x: n.x, y: newY, w: n.w, h: n.h};
let collisionNode = this.nodes
.slice(0, i)
.find(bn => Utils.isIntercepted(box, bn), {n: n, newY: newY});
let collisionNode = this.nodes.slice(0, i).find(bn => Utils.isIntercepted(box, bn));
if (!collisionNode) {
n._dirty = true;
n.y = newY;
Expand All @@ -171,10 +171,8 @@ export class GridStackEngine {
let canBeMoved = i === 0;
let box: GridStackWidget = {x: n.x, y: newY, w: n.w, h: n.h};
if (i > 0) {
let collisionNode = this.nodes
.slice(0, i)
.find(bn => Utils.isIntercepted(box, bn), {n: n, newY: newY});
canBeMoved = collisionNode === undefined;
let collisionNode = this.nodes.slice(0, i).find(bn => Utils.isIntercepted(box, bn));
canBeMoved = !collisionNode;
}

if (!canBeMoved) { break; }
Expand Down Expand Up @@ -312,7 +310,7 @@ export class GridStackEngine {
continue;
}
let box = {x, y, w: node.w, h: node.h};
if (!this.nodes.find(n => Utils.isIntercepted(box, n), {x, y, node})) {
if (!this.nodes.find(n => Utils.isIntercepted(box, n))) {
node.x = x;
node.y = y;
delete node.autoPosition; // found our slot
Expand Down Expand Up @@ -409,6 +407,31 @@ export class GridStackEngine {
return clone.getRow() <= this.maxRow;
}

/** return true if the passed in node (x,y) is being dragged outside of the grid, and not added to bottom */
public isOutside(x: number, y: number, node: GridStackNode): boolean {
// simple outside boundaries
if (x < 0 || x >= this.column || y < 0) return true;
if (this.maxRow) return (y >= this.maxRow);
else if (this.float) return false; // infinite grow with no maxRow

// see if dragging PAST bottom (row+1)
let row = this.getRow();
if (y < row || y === 0) return false;
if (y > row) return true;
// else check to see if we can add that item to the bottom... (y == row)
if (!node._temporaryRemoved) {
let clone = new GridStackEngine({
column: this.column,
float: this.float,
nodes: this.nodes.filter(n => n !== node).map(n => {return {...n}})
});
let nn = {...node, x, y};
clone.addNode(nn);
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...
}
return node._temporaryRemoved; // if still outside so we don't flicker back & forth
}

public isNodeChangedPosition(node: GridStackNode, x: number, y: number, w?: number, h?: number): boolean {
if (typeof x !== 'number') { x = node.x; }
if (typeof y !== 'number') { y = node.y; }
Expand Down

0 comments on commit dd932b6

Please sign in to comment.