Skip to content

Commit 915f06a

Browse files
authored
Merge pull request jbaysolutions#346 from SheanDe/feat/prevent-collision
Add prevent-collision options to GridLayout
2 parents 98f3b3b + 2fc5fdd commit 915f06a

File tree

7 files changed

+186
-17
lines changed

7 files changed

+186
-17
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ Include the browser-ready bundle (download from [releases](https://github.com/jb
250250

251251
Says if the layout should be compact vertically.
252252

253+
* **preventCollision**
254+
255+
* type: `Boolean`
256+
* required: `false`
257+
* default: `false`
258+
259+
Says if grid items will move when being dragged over.
260+
253261
* **useCssTransforms**
254262

255263
* type: `Boolean`

examples/06-responsive.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ <h1>Vue Grid Layout Example 6 - Responsive</h1>
1313
<a href="https://github.com/jbaysolutions/vue-grid-layout">View project on Github</a>
1414
<br/>
1515
<a href="05-mirrored.html">Previous example: Mirrored grid layout</a>
16+
<br/>
17+
<a href="07-prevent-collision.html">Next example: Responsive</a>
1618

1719
<div id="app" style="width: 100%;">
1820
<!--<pre>{{ $data | json }}</pre>-->

examples/07-prevent-collision.html

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Vue Grid Layout Example 8 - Prevent Collision</title>
6+
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
7+
<link rel="stylesheet" href="app.css">
8+
<!--<link rel="stylesheet" href="../dist/vue-grid-layout.css">-->
9+
</head>
10+
<body>
11+
<h1>Vue Grid Layout Example 8 - Prevent Collision</h1>
12+
13+
<a href="https://github.com/jbaysolutions/vue-grid-layout">View project on Github</a>
14+
<br/>
15+
<a href="06-responsive.html">Previous example: Mirrored grid layout</a>
16+
17+
<div id="app" style="width: 100%;">
18+
<!--<pre>{{ $data | json }}</pre>-->
19+
<div>
20+
<div class="layoutJSON">
21+
Displayed as <code>[x, y, w, h]</code>:
22+
<div class="columns">
23+
<div class="layoutItem" v-for="item in layout">
24+
<b>{{item.i}}</b>: [{{item.x}}, {{item.y}}, {{item.w}}, {{item.h}}]
25+
</div>
26+
</div>
27+
</div>
28+
</div>
29+
<div id="content">
30+
<!--<button @click="decreaseWidth">Decrease Width</button>
31+
<button @click="increaseWidth">Increase Width</button>
32+
<button @click="addItem">Add an item</button>-->
33+
<br/>
34+
<grid-layout :layout.sync="layout"
35+
:col-num="12"
36+
:row-height="30"
37+
:is-draggable="draggable"
38+
:is-resizable="resizable"
39+
:vertical-compact="false"
40+
:prevent-collision="true"
41+
:use-css-transforms="true"
42+
:responsive="true"
43+
>
44+
<grid-item v-for="item in layout"
45+
:x="item.x"
46+
:y="item.y"
47+
:w="item.w"
48+
:h="item.h"
49+
:i="item.i"
50+
>
51+
<span class="text">{{item.i}}</span>
52+
</grid-item>
53+
</grid-layout>
54+
</div>
55+
56+
</div>
57+
<script src="vue.min.js"></script>
58+
<script src="../dist/vue-grid-layout.umd.min.js"></script>
59+
<script src="07-prevent-collision.js"></script>
60+
</body>
61+
</html>

examples/07-prevent-collision.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
var testLayout = [
2+
{"x":0,"y":0,"w":2,"h":5,"i":"0", static: false},
3+
{"x":2,"y":0,"w":2,"h":2,"i":"1", static: false},
4+
{"x":4,"y":0,"w":2,"h":5,"i":"2", static: false},
5+
{"x":6,"y":0,"w":2,"h":3,"i":"3", static: false},
6+
{"x":8,"y":0,"w":2,"h":3,"i":"4", static: false},
7+
{"x":10,"y":0,"w":2,"h":3,"i":"5", static: false},
8+
{"x":0,"y":5,"w":2,"h":5,"i":"6", static: false},
9+
{"x":2,"y":5,"w":2,"h":5,"i":"7", static: false},
10+
{"x":4,"y":5,"w":2,"h":5,"i":"8", static: false},
11+
{"x":6,"y":3,"w":2,"h":4,"i":"9", static: false},
12+
{"x":8,"y":4,"w":2,"h":4,"i":"10", static: false},
13+
{"x":10,"y":4,"w":2,"h":4,"i":"11", static: false},
14+
{"x":0,"y":10,"w":2,"h":5,"i":"12", static: false},
15+
{"x":2,"y":10,"w":2,"h":5,"i":"13", static: false},
16+
{"x":4,"y":8,"w":2,"h":4,"i":"14", static: false},
17+
{"x":6,"y":8,"w":2,"h":4,"i":"15", static: false},
18+
{"x":8,"y":10,"w":2,"h":5,"i":"16", static: false},
19+
{"x":10,"y":4,"w":2,"h":2,"i":"17", static: false},
20+
{"x":0,"y":9,"w":2,"h":3,"i":"18", static: false},
21+
{"x":2,"y":6,"w":2,"h":2,"i":"19", static: false}
22+
];
23+
24+
// var GridLayout = VueGridLayout.GridLayout;
25+
// var GridItem = VueGridLayout.GridItem;
26+
27+
new Vue({
28+
el: '#app',
29+
// components: {
30+
// "GridLayout": GridLayout,
31+
// "GridItem": GridItem
32+
// },
33+
data: {
34+
layout: testLayout,
35+
draggable: true,
36+
resizable: true,
37+
index: 0
38+
},
39+
40+
/*
41+
mounted: function () {
42+
this.index = this.layout.length;
43+
},
44+
*/
45+
methods: {
46+
itemTitle(item) {
47+
var result = item.i;
48+
if (item.static) {
49+
result += " - Static";
50+
}
51+
return result;
52+
}
53+
}
54+
});
55+
56+

src/App.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<input type="checkbox" v-model="resizable"/> Resizable
3131
<input type="checkbox" v-model="mirrored"/> Mirrored
3232
<input type="checkbox" v-model="responsive"/> Responsive
33+
<input type="checkbox" v-model="preventCollision"/> Prevent Collision
3334
<div style="margin-top: 10px;margin-bottom: 10px;">
3435
Row Height: <input type="number" v-model="rowHeight"/> Col nums: <input type="number" v-model="colNum"/>
3536
</div>
@@ -40,6 +41,7 @@
4041
:is-draggable="draggable"
4142
:is-resizable="resizable"
4243
:is-mirrored="mirrored"
44+
:prevent-collision="preventCollision"
4345
:vertical-compact="true"
4446
:use-css-transforms="true"
4547
:responsive="responsive"
@@ -143,6 +145,7 @@
143145
resizable: true,
144146
mirrored: false,
145147
responsive: true,
148+
preventCollision: false,
146149
rowHeight: 30,
147150
colNum: 12,
148151
index: 0

src/components/GridLayout.vue

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import Vue from 'vue';
2121
var elementResizeDetectorMaker = require("element-resize-detector");
2222
23-
import {bottom, compact, getLayoutItem, moveElement, validateLayout, cloneLayout} from '../helpers/utils';
23+
import {bottom, compact, getLayoutItem, moveElement, validateLayout, cloneLayout, getAllCollisions} from '../helpers/utils';
2424
import {getBreakpointFromWidth, getColsFromBreakpoint, findOrGenerateResponsiveLayout} from "../helpers/responsiveUtils";
2525
//var eventBus = require('./eventBus');
2626
@@ -97,6 +97,10 @@
9797
type: Object,
9898
default: function(){return{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }},
9999
},
100+
preventCollision: {
101+
type: Boolean,
102+
default: false
103+
}
100104
},
101105
data: function () {
102106
return {
@@ -301,24 +305,55 @@
301305
});
302306
}
303307
304-
// set layout element coordinates to dragged position
305-
l.x = x;
306-
l.y = y;
307308
// Move the element to the dragged ___location.
308-
this.layout = moveElement(this.layout, l, x, y, true);
309+
this.layout = moveElement(this.layout, l, x, y, true, this.preventCollision);
309310
compact(this.layout, this.verticalCompact);
310311
// needed because vue can't detect changes on array element properties
311312
this.eventBus.$emit("compact");
312313
this.updateHeight();
313314
if (eventName === 'dragend') this.$emit('layout-updated', this.layout);
314315
},
315316
resizeEvent: function (eventName, id, x, y, h, w) {
317+
let l = getLayoutItem(this.layout, id);
318+
//GetLayoutItem sometimes return null object
319+
if (l === undefined || l === null){
320+
l = {h:0, w:0}
321+
}
322+
323+
let hasCollisions;
324+
if (this.preventCollision) {
325+
const collisions = getAllCollisions(this.layout, { ...l, w, h }).filter(
326+
layoutItem => layoutItem.i !== l.i
327+
);
328+
hasCollisions = collisions.length > 0;
329+
330+
// If we're colliding, we need adjust the placeholder.
331+
if (hasCollisions) {
332+
// adjust w && h to maximum allowed space
333+
let leastX = Infinity,
334+
leastY = Infinity;
335+
collisions.forEach(layoutItem => {
336+
if (layoutItem.x > l.x) leastX = Math.min(leastX, layoutItem.x);
337+
if (layoutItem.y > l.y) leastY = Math.min(leastY, layoutItem.y);
338+
});
339+
340+
if (Number.isFinite(leastX)) l.w = leastX - l.x;
341+
if (Number.isFinite(leastY)) l.h = leastY - l.y;
342+
}
343+
}
344+
345+
if (!hasCollisions) {
346+
// Set new width and height.
347+
l.w = w;
348+
l.h = h;
349+
}
350+
316351
if (eventName === "resizestart" || eventName === "resizemove") {
317352
this.placeholder.i = id;
318353
this.placeholder.x = x;
319354
this.placeholder.y = y;
320-
this.placeholder.w = w;
321-
this.placeholder.h = h;
355+
this.placeholder.w = l.w;
356+
this.placeholder.h = l.h;
322357
this.$nextTick(function() {
323358
this.isDragging = true;
324359
});
@@ -330,13 +365,6 @@
330365
this.isDragging = false;
331366
});
332367
}
333-
let l = getLayoutItem(this.layout, id);
334-
//GetLayoutItem sometimes return null object
335-
if (l === undefined || l === null){
336-
l = {h:0, w:0}
337-
}
338-
l.h = h;
339-
l.w = w;
340368
341369
if (this.responsive) this.responsiveGridLayout();
342370

src/helpers/utils.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,15 @@ export function getStatics(layout: Layout): Array<LayoutItem> {
206206
* @param {Boolean} [isUserAction] If true, designates that the item we're moving is
207207
* being dragged/resized by th euser.
208208
*/
209-
export function moveElement(layout: Layout, l: LayoutItem, x: Number, y: Number, isUserAction: Boolean): Layout {
209+
export function moveElement(layout: Layout, l: LayoutItem, x: Number, y: Number, isUserAction: Boolean, preventCollision: Boolean): Layout {
210210
if (l.static) return layout;
211211

212212
// Short-circuit if nothing to do.
213213
//if (l.y === y && l.x === x) return layout;
214214

215+
const oldX = l.x;
216+
const oldY = l.y;
217+
215218
const movingUp = y && l.y > y;
216219
// This is quite a bit faster than extending the object
217220
if (typeof x === 'number') l.x = x;
@@ -226,6 +229,13 @@ export function moveElement(layout: Layout, l: LayoutItem, x: Number, y: Number,
226229
if (movingUp) sorted = sorted.reverse();
227230
const collisions = getAllCollisions(sorted, l);
228231

232+
if (preventCollision && collisions.length) {
233+
l.x = oldX;
234+
l.y = oldY;
235+
l.moved = false;
236+
return layout;
237+
}
238+
229239
// Move each item that collides away from this element.
230240
for (let i = 0, len = collisions.length; i < len; i++) {
231241
const collision = collisions[i];
@@ -261,6 +271,7 @@ export function moveElement(layout: Layout, l: LayoutItem, x: Number, y: Number,
261271
export function moveElementAwayFromCollision(layout: Layout, collidesWith: LayoutItem,
262272
itemToMove: LayoutItem, isUserAction: ?boolean): Layout {
263273

274+
const preventCollision = false // we're already colliding
264275
// If there is enough space above the collision to put this element, move it there.
265276
// We only do this on the main collision as this can get funky in cascades and cause
266277
// unwanted swapping behavior.
@@ -275,13 +286,13 @@ export function moveElementAwayFromCollision(layout: Layout, collidesWith: Layou
275286
};
276287
fakeItem.y = Math.max(collidesWith.y - itemToMove.h, 0);
277288
if (!getFirstCollision(layout, fakeItem)) {
278-
return moveElement(layout, itemToMove, undefined, fakeItem.y);
289+
return moveElement(layout, itemToMove, undefined, fakeItem.y, preventCollision);
279290
}
280291
}
281292

282293
// Previously this was optimized to move below the collision directly, but this can cause problems
283294
// with cascading moves, as an item may actually leapflog a collision and cause a reversal in order.
284-
return moveElement(layout, itemToMove, undefined, itemToMove.y + 1);
295+
return moveElement(layout, itemToMove, undefined, itemToMove.y + 1, preventCollision);
285296
}
286297

287298
/**

0 commit comments

Comments
 (0)