Skip to content

Commit

Permalink
Prevent draging outside of the parent element with isBounded prop (#523
Browse files Browse the repository at this point in the history
…) (#532)

* add bounded prop

* bounded example page

* fix

* Update config.js
  • Loading branch information
rin4ik authored Jul 29, 2022
1 parent 1e26de4 commit 6932b95
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<input type="checkbox" v-model="draggable"/> Draggable
<input type="checkbox" v-model="resizable"/> Resizable
<input type="checkbox" v-model="mirrored"/> Mirrored
<input type="checkbox" v-model="bounded"/> Bounded
<input type="checkbox" v-model="responsive"/> Responsive
<input type="checkbox" v-model="preventCollision"/> Prevent Collision
<input type="checkbox" v-model="compact"/> Vertical Compact
Expand All @@ -45,6 +46,7 @@
:is-draggable="draggable"
:is-resizable="resizable"
:is-mirrored="mirrored"
:is-bounded="bounded"
:prevent-collision="preventCollision"
:vertical-compact="compact"
:restore-on-drag="restoreOnDrag"
Expand Down Expand Up @@ -166,6 +168,7 @@
resizable: true,
mirrored: false,
responsive: true,
bounded: false,
preventCollision: false,
compact: true,
restoreOnDrag: true,
Expand Down
44 changes: 43 additions & 1 deletion src/components/GridItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@
required: false,
default: null
},
isBounded: {
type: Boolean,
required: false,
default: null
},
/*useCssTransforms: {
type: Boolean,
required: true
Expand Down Expand Up @@ -274,7 +279,11 @@
self.resizable = isResizable;
}
};
self.setBoundedHandler = function (isBounded) {
if (self.isBounded === null) {
self.bounded = isBounded;
}
};
self.setRowHeightHandler = function (rowHeight) {
self.rowHeight = rowHeight;
};
Expand All @@ -296,6 +305,7 @@
this.eventBus.$on('compact', self.compactHandler);
this.eventBus.$on('setDraggable', self.setDraggableHandler);
this.eventBus.$on('setResizable', self.setResizableHandler);
this.eventBus.$on('setBounded', self.setBoundedHandler);
this.eventBus.$on('setRowHeight', self.setRowHeightHandler);
this.eventBus.$on('setMaxRows', self.setMaxRowsHandler);
this.eventBus.$on('directionchange', self.directionchangeHandler);
Expand All @@ -310,6 +320,7 @@
this.eventBus.$off('compact', self.compactHandler);
this.eventBus.$off('setDraggable', self.setDraggableHandler);
this.eventBus.$off('setResizable', self.setResizableHandler);
this.eventBus.$off('setBounded', self.setBoundedHandler);
this.eventBus.$off('setRowHeight', self.setRowHeightHandler);
this.eventBus.$off('setMaxRows', self.setMaxRowsHandler);
this.eventBus.$off('directionchange', self.directionchangeHandler);
Expand Down Expand Up @@ -339,6 +350,11 @@
} else {
this.resizable = this.isResizable;
}
if (this.isBounded === null) {
this.bounded = this.layout.isBounded;
} else {
this.bounded = this.isBounded;
}
this.useCssTransforms = this.layout.useCssTransforms;
this.useStyleCursor = this.layout.useStyleCursor;
this.createStyle();
Expand All @@ -357,6 +373,9 @@
isResizable: function () {
this.resizable = this.isResizable;
},
isBounded: function () {
this.bounded = this.isBounded;
},
resizable: function () {
this.tryMakeResizable();
},
Expand Down Expand Up @@ -645,6 +664,13 @@
newPosition.left = this.dragging.left + coreEvent.deltaX;
}
newPosition.top = this.dragging.top + coreEvent.deltaY;
if(this.bounded){
const bottomBoundary = event.target.offsetParent.clientHeight - this.calcGridItemWHPx(this.h, this.rowHeight, this.margin[1]);
newPosition.top = this.clamp(newPosition.top, 0, bottomBoundary);
const colWidth = this.calcColWidth();
const rightBoundary = this.containerWidth - this.calcGridItemWHPx(this.w, colWidth, this.margin[0]);
newPosition.left = this.clamp(newPosition.left, 0, rightBoundary);
}
// console.log("### drag => " + event.type + ", x=" + x + ", y=" + y);
// console.log("### drag => " + event.type + ", deltaX=" + coreEvent.deltaX + ", deltaY=" + coreEvent.deltaY);
// console.log("### drag end => " + JSON.stringify(newPosition));
Expand Down Expand Up @@ -733,6 +759,22 @@
// console.log("### COLS=" + this.cols + " COL WIDTH=" + colWidth + " MARGIN " + this.margin[0]);
return colWidth;
},
// This can either be called:
// calcGridItemWHPx(w, colWidth, margin[0])
// or
// calcGridItemWHPx(h, rowHeight, margin[1])
calcGridItemWHPx(gridUnits,colOrRowSize,marginPx) {
// 0 * Infinity === NaN, which causes problems with resize contraints
if (!Number.isFinite(gridUnits)) return gridUnits;
return Math.round(
colOrRowSize * gridUnits + Math.max(0, gridUnits - 1) * marginPx
);
},
// Similar to _.clamp
clamp(num, lowerBound, upperBound) {
return Math.max(Math.min(num, upperBound), lowerBound);
},
/**
* Given a height and width in pixel values, calculate grid units.
Expand Down
7 changes: 7 additions & 0 deletions src/components/GridLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
type: Boolean,
default: false
},
isBounded: {
type: Boolean,
default: false
},
useCssTransforms: {
type: Boolean,
default: true
Expand Down Expand Up @@ -249,6 +253,9 @@
isResizable: function() {
this.eventBus.$emit("setResizable", this.isResizable);
},
isBounded: function() {
this.eventBus.$emit("setBounded", this.isBounded);
},
responsive() {
if (!this.responsive) {
this.$emit('update:layout', this.originalLayout);
Expand Down
153 changes: 153 additions & 0 deletions website/docs/.vuepress/components/Example11Bounded.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<template>
<div style="width:100%;height:2000px;">
<div class="layoutJSON">
Displayed as <code>[x, y, w, h]</code>:
<div class="columns">
<div v-for="item in layout">
<b>{{item.i}}</b>: [{{item.x}}, {{item.y}}, {{item.w}}, {{item.h}}]
</div>
</div>
</div>
<hr/>
<input type="checkbox" v-model="draggable"/> Draggable
<input type="checkbox" v-model="resizable"/> Resizable
<input type="checkbox" v-model="bounded"/> Bounded
<br/>
<div style="width:100%;margin-top: 10px;height:100%;">
<grid-layout :layout.sync="layout"
:col-num="12"
:row-height="30"
:is-draggable="draggable"
:is-resizable="resizable"
:is-bounded="bounded"
:vertical-compact="true"
:use-css-transforms="true"
>
<grid-item v-for="item in layout"
:static="item.static"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
>
<span class="text">{{item.i}}</span>
</grid-item>
</grid-layout>
</div>
</div>
</template>

<script>
import { GridLayout, GridItem } from "vue-grid-layout"
export default {
components: {
GridLayout,
GridItem
},
data() {
return {
layout: [
{"x":0,"y":0,"w":2,"h":2,"i":"0"},
{"x":2,"y":0,"w":2,"h":4,"i":"1"},
{"x":4,"y":0,"w":2,"h":5,"i":"2"},
{"x":6,"y":0,"w":2,"h":3,"i":"3"},
{"x":8,"y":0,"w":2,"h":3,"i":"4"},
{"x":10,"y":0,"w":2,"h":3,"i":"5"},
{"x":0,"y":5,"w":2,"h":5,"i":"6"},
{"x":2,"y":5,"w":2,"h":5,"i":"7"},
{"x":4,"y":5,"w":2,"h":5,"i":"8"},
{"x":6,"y":4,"w":2,"h":4,"i":"9"},
{"x":8,"y":4,"w":2,"h":4,"i":"10"},
{"x":10,"y":4,"w":2,"h":4,"i":"11"},
{"x":0,"y":10,"w":2,"h":5,"i":"12"},
{"x":2,"y":10,"w":2,"h":5,"i":"13"},
{"x":4,"y":8,"w":2,"h":4,"i":"14"},
{"x":6,"y":8,"w":2,"h":4,"i":"15"},
{"x":8,"y":10,"w":2,"h":5,"i":"16"},
{"x":10,"y":4,"w":2,"h":2,"i":"17"},
{"x":0,"y":9,"w":2,"h":3,"i":"18"},
{"x":2,"y":6,"w":2,"h":2,"i":"19"}
],
draggable: true,
resizable: true,
bounded: true
}
}
}
</script>

<style scoped>
.vue-grid-layout {
background: #eee;
}
.vue-grid-item:not(.vue-grid-placeholder) {
background: #ccc;
border: 1px solid black;
}
.vue-grid-item .resizing {
opacity: 0.9;
}
.vue-grid-item .static {
background: #cce;
}
.vue-grid-item .text {
font-size: 24px;
text-align: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
height: 100%;
width: 100%;
}
.vue-grid-item .no-drag {
height: 100%;
width: 100%;
}
.vue-grid-item .minMax {
font-size: 12px;
}
.vue-grid-item .add {
cursor: pointer;
}
.vue-draggable-handle {
position: absolute;
width: 20px;
height: 20px;
top: 0;
left: 0;
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat;
background-position: bottom right;
padding: 0 8px 8px 0;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
cursor: pointer;
}
.layoutJSON {
background: #ddd;
border: 1px solid black;
margin-top: 10px;
padding: 10px;
}
.columns {
-moz-columns: 120px;
-webkit-columns: 120px;
columns: 120px;
}
</style>
2 changes: 2 additions & 0 deletions website/docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ module.exports = {
'08-responsive-predefined-layouts',
'09-dynamic-add-remove',
'10-drag-from-outside',
'11-bounded',
]
}
]
Expand Down Expand Up @@ -108,6 +109,7 @@ module.exports = {
'08-responsive-predefined-layouts',
'09-dynamic-add-remove',
'10-drag-from-outside',
'11-bounded',
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h1>Vue Grid Layout Example 09 - Drag From Outside</h1>
<br/>
<a href="09-dynamic-add-remove.html">Previous example: Dynamic Add/Remove</a>
<br/>
<!--<a href="">Next example: </a>-->
<a href="11-bounded.html">Next example: Bounded</a>

<br/>
This demo shows what happens when an item is added from outside of the grid.
Expand Down
64 changes: 64 additions & 0 deletions website/docs/.vuepress/public/examples/11-bounded.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>Vue Grid Layout Example 11 - Bounded</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="app.css">
<!--<link rel="stylesheet" href="../dist/vue-grid-layout.css">-->
</head>

<body>
<h1>Vue Grid Layout Example 11 - Bounded</h1>

<a href="https://github.com/jbaysolutions/vue-grid-layout">View project on Github</a>
<br />
<a href="10-drag-from-outside.html">Previous example: Drag From Outside</a>
<br />
<!-- <a href="09-dynamic-add-remove.html">Next example: Dynamic Add/Remove</a> -->

<div id="app" style="width: 100%;">
<!--<pre>{{ $data | json }}</pre>-->
<div>
<div class="layoutJSON">
Displayed as <code>[x, y, w, h]</code>:
<div class="columns">
<div class="layoutItem" v-for="item in layout">
<b>{{item.i}}</b>: [{{item.x}}, {{item.y}}, {{item.w}}, {{item.h}}]
</div>
</div>
</div>
</div>
<div id="content">
<input type="checkbox" v-model="draggable" /> Draggable
<input type="checkbox" v-model="resizable" /> Resizable
<input type="checkbox" v-model="bounded" /> Bounded
<br />
<grid-layout :layout.sync="layout"
:col-num="12"
:row-height="30"
:is-draggable="draggable"
:is-resizable="resizable"
:is-bounded="bounded"
:vertical-compact="true"
:use-css-transforms="true"
>
<grid-item v-for="item in layout"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
>
<span class="text">{{item.i}}</span>
</grid-item>
</grid-layout>
</div>
</div>
<script src="vue.min.js"></script>
<script src="../dist/vue-grid-layout.umd.min.js"></script>
<script src="11-bounded.js"></script>
</body>

</html>
Loading

0 comments on commit 6932b95

Please sign in to comment.