vue-draggable-resizable
Version:
Vue3 Component for resizable and draggable elements
528 lines (527 loc) • 20.4 kB
JavaScript
import { openBlock as z, createElementBlock as v, normalizeStyle as H, normalizeClass as T, Fragment as E, renderList as L, withModifiers as B, renderSlot as k } from "vue";
function D(t) {
return typeof t == "function" || Object.prototype.toString.call(t) === "[object Function]";
}
function w(t, e, i, s = 1) {
const [h, o] = typeof s == "number" ? [s, s] : s, a = Math.round(e / h / t[0]) * t[0], l = Math.round(i / o / t[1]) * t[1];
return [a, l];
}
function R(t, e, i) {
return t - e - i;
}
function S(t, e, i) {
return t - e - i;
}
function c(t, e, i) {
return e !== null && t < e ? e : i !== null && i < t ? i : t;
}
function W(t, e, i) {
let s = t;
const h = [
"matches",
"webkitMatchesSelector",
"mozMatchesSelector",
"msMatchesSelector",
"oMatchesSelector"
].find((o) => D(s[o]));
if (!D(s[h]))
return !1;
do {
if (s[h](e))
return !0;
if (s === i)
return !1;
s = s.parentNode;
} while (s);
return !1;
}
function N(t) {
const e = window.getComputedStyle(t);
return [
parseFloat(e.getPropertyValue("width"), 10),
parseFloat(e.getPropertyValue("height"), 10)
];
}
function b(t, e, i) {
t && (t.attachEvent ? t.attachEvent("on" + e, i) : t.addEventListener ? t.addEventListener(e, i, !0) : t["on" + e] = i);
}
function f(t, e, i) {
t && (t.detachEvent ? t.detachEvent("on" + e, i) : t.removeEventListener ? t.removeEventListener(e, i, !0) : t["on" + e] = null);
}
const P = (t, e) => {
const i = t.__vccOpts || t;
for (const [s, h] of e)
i[s] = h;
return i;
}, y = {
mouse: {
start: "mousedown",
move: "mousemove",
stop: "mouseup"
},
touch: {
start: "touchstart",
move: "touchmove",
stop: "touchend"
}
}, C = {
userSelect: "none",
MozUserSelect: "none",
WebkitUserSelect: "none",
MsUserSelect: "none"
}, X = {
userSelect: "auto",
MozUserSelect: "auto",
WebkitUserSelect: "auto",
MsUserSelect: "auto"
};
let p = y.mouse;
const Y = {
replace: !0,
name: "vue-draggable-resizable",
props: {
className: {
type: String,
default: "vdr"
},
classNameDraggable: {
type: String,
default: "draggable"
},
classNameResizable: {
type: String,
default: "resizable"
},
classNameDragging: {
type: String,
default: "dragging"
},
classNameResizing: {
type: String,
default: "resizing"
},
classNameActive: {
type: String,
default: "active"
},
classNameHandle: {
type: String,
default: "handle"
},
disableUserSelect: {
type: Boolean,
default: !0
},
enableNativeDrag: {
type: Boolean,
default: !1
},
preventDeactivation: {
type: Boolean,
default: !1
},
active: {
type: Boolean,
default: !1
},
draggable: {
type: Boolean,
default: !0
},
resizable: {
type: Boolean,
default: !0
},
lockAspectRatio: {
type: Boolean,
default: !1
},
w: {
type: [Number, String],
default: 200,
validator: (t) => typeof t == "number" ? t > 0 : t === "auto"
},
h: {
type: [Number, String],
default: 200,
validator: (t) => typeof t == "number" ? t > 0 : t === "auto"
},
minWidth: {
type: Number,
default: 0,
validator: (t) => t >= 0
},
minHeight: {
type: Number,
default: 0,
validator: (t) => t >= 0
},
maxWidth: {
type: Number,
default: null,
validator: (t) => t >= 0
},
maxHeight: {
type: Number,
default: null,
validator: (t) => t >= 0
},
x: {
type: Number,
default: 0
},
y: {
type: Number,
default: 0
},
z: {
type: [String, Number],
default: "auto",
validator: (t) => typeof t == "string" ? t === "auto" : t >= 0
},
handles: {
type: Array,
default: () => ["tl", "tm", "tr", "mr", "br", "bm", "bl", "ml"],
validator: (t) => {
const e = /* @__PURE__ */ new Set(["tl", "tm", "tr", "mr", "br", "bm", "bl", "ml"]);
return new Set(t.filter((i) => e.has(i))).size === t.length;
}
},
dragHandle: {
type: String,
default: null
},
dragCancel: {
type: String,
default: null
},
axis: {
type: String,
default: "both",
validator: (t) => ["x", "y", "both"].includes(t)
},
grid: {
type: Array,
default: () => [1, 1]
},
parent: {
type: Boolean,
default: !1
},
scale: {
type: [Number, Array],
default: 1,
validator: (t) => typeof t == "number" ? t > 0 : t.length === 2 && t[0] > 0 && t[1] > 0
},
onDragStart: {
type: Function,
default: () => !0
},
onDrag: {
type: Function,
default: () => !0
},
onResizeStart: {
type: Function,
default: () => !0
},
onResize: {
type: Function,
default: () => !0
}
},
data: function() {
return {
left: this.x,
top: this.y,
right: null,
bottom: null,
width: null,
height: null,
widthTouched: !1,
heightTouched: !1,
aspectFactor: null,
parentWidth: null,
parentHeight: null,
handle: null,
enabled: this.active,
resizing: !1,
dragging: !1,
dragEnable: !1,
resizeEnable: !1,
zIndex: this.z
};
},
created: function() {
this.maxWidth && this.minWidth > this.maxWidth && console.warn("[Vdr warn]: Invalid prop: minWidth cannot be greater than maxWidth"), this.maxHeight && this.minHeight > this.maxHeight && console.warn("[Vdr warn]: Invalid prop: minHeight cannot be greater than maxHeight"), this.resetBoundsAndMouseState();
},
mounted: function() {
this.enableNativeDrag || (this.$el.ondragstart = () => !1);
const [t, e] = this.getParentSize();
this.parentWidth = t, this.parentHeight = e;
const [i, s] = N(this.$el);
this.aspectFactor = (this.w !== "auto" ? this.w : i) / (this.h !== "auto" ? this.h : s), this.width = this.w !== "auto" ? this.w : i, this.height = this.h !== "auto" ? this.h : s, this.right = this.parentWidth - this.width - this.left, this.bottom = this.parentHeight - this.height - this.top, this.active && this.$emit("activated"), b(document.documentElement, "mousedown", this.deselect), b(document.documentElement, "touchend touchcancel", this.deselect), b(window, "resize", this.checkParentSize);
},
beforeUnmount: function() {
f(document.documentElement, "mousedown", this.deselect), f(document.documentElement, "touchstart", this.handleUp), f(document.documentElement, "mousemove", this.move), f(document.documentElement, "touchmove", this.move), f(document.documentElement, "mouseup", this.handleUp), f(document.documentElement, "touchend touchcancel", this.deselect), f(window, "resize", this.checkParentSize);
},
methods: {
resetBoundsAndMouseState() {
this.mouseClickPosition = { mouseX: 0, mouseY: 0, x: 0, y: 0, w: 0, h: 0 }, this.bounds = {
minLeft: null,
maxLeft: null,
minRight: null,
maxRight: null,
minTop: null,
maxTop: null,
minBottom: null,
maxBottom: null
};
},
checkParentSize() {
if (this.parent) {
const [t, e] = this.getParentSize();
this.parentWidth = t, this.parentHeight = e, this.right = this.parentWidth - this.width - this.left, this.bottom = this.parentHeight - this.height - this.top;
}
},
getParentSize() {
if (this.parent) {
const t = window.getComputedStyle(this.$el.parentNode, null);
return [
parseInt(t.getPropertyValue("width"), 10),
parseInt(t.getPropertyValue("height"), 10)
];
}
return [null, null];
},
elementTouchDown(t) {
p = y.touch, this.elementDown(t);
},
elementMouseDown(t) {
p = y.mouse, this.elementDown(t);
},
elementDown(t) {
if (t instanceof MouseEvent && t.button !== 0)
return;
const e = t.target || t.srcElement;
if (this.$el.contains(e)) {
if (this.onDragStart(t) === !1)
return;
if (this.dragHandle && !W(e, this.dragHandle, this.$el) || this.dragCancel && W(e, this.dragCancel, this.$el)) {
this.dragging = !1;
return;
}
this.enabled || (this.enabled = !0, this.$emit("activated"), this.$emit("update:active", !0)), this.draggable && (this.dragEnable = !0), this.mouseClickPosition.mouseX = t.touches ? t.touches[0].pageX : t.pageX, this.mouseClickPosition.mouseY = t.touches ? t.touches[0].pageY : t.pageY, this.mouseClickPosition.left = this.left, this.mouseClickPosition.right = this.right, this.mouseClickPosition.top = this.top, this.mouseClickPosition.bottom = this.bottom, this.parent && (this.bounds = this.calcDragLimits()), b(document.documentElement, p.move, this.move), b(document.documentElement, p.stop, this.handleUp);
}
},
calcDragLimits() {
return {
minLeft: this.left % this.grid[0],
maxLeft: Math.floor((this.parentWidth - this.width - this.left) / this.grid[0]) * this.grid[0] + this.left,
minRight: this.right % this.grid[0],
maxRight: Math.floor((this.parentWidth - this.width - this.right) / this.grid[0]) * this.grid[0] + this.right,
minTop: this.top % this.grid[1],
maxTop: Math.floor((this.parentHeight - this.height - this.top) / this.grid[1]) * this.grid[1] + this.top,
minBottom: this.bottom % this.grid[1],
maxBottom: Math.floor((this.parentHeight - this.height - this.bottom) / this.grid[1]) * this.grid[1] + this.bottom
};
},
deselect(t) {
const e = t.target || t.srcElement, i = new RegExp(this.className + "-([trmbl]{2})", "");
!this.$el.contains(e) && !i.test(e.className) && (this.enabled && !this.preventDeactivation && (this.enabled = !1, this.$emit("deactivated"), this.$emit("update:active", !1)), f(document.documentElement, p.move, this.handleResize)), this.resetBoundsAndMouseState();
},
handleTouchDown(t, e) {
p = y.touch, this.handleDown(t, e);
},
handleDown(t, e) {
e instanceof MouseEvent && e.which !== 1 || this.onResizeStart(t, e) !== !1 && (e.stopPropagation && e.stopPropagation(), this.lockAspectRatio && !t.includes("m") ? this.handle = "m" + t.substring(1) : this.handle = t, this.resizeEnable = !0, this.mouseClickPosition.mouseX = e.touches ? e.touches[0].pageX : e.pageX, this.mouseClickPosition.mouseY = e.touches ? e.touches[0].pageY : e.pageY, this.mouseClickPosition.left = this.left, this.mouseClickPosition.right = this.right, this.mouseClickPosition.top = this.top, this.mouseClickPosition.bottom = this.bottom, this.bounds = this.calcResizeLimits(), b(document.documentElement, p.move, this.handleResize), b(document.documentElement, p.stop, this.handleUp));
},
calcResizeLimits() {
let t = this.minW, e = this.minH, i = this.maxW, s = this.maxH;
const h = this.aspectFactor, [o, a] = this.grid, l = this.width, g = this.height, r = this.left, u = this.top, m = this.right, d = this.bottom;
this.lockAspectRatio && (t / e > h ? e = t / h : t = h * e, i && s ? (i = Math.min(i, h * s), s = Math.min(s, i / h)) : i ? s = i / h : s && (i = h * s)), i = i - i % o, s = s - s % a;
const n = {
minLeft: null,
maxLeft: null,
minTop: null,
maxTop: null,
minRight: null,
maxRight: null,
minBottom: null,
maxBottom: null
};
return this.parent ? (n.minLeft = r % o, n.maxLeft = r + Math.floor((l - t) / o) * o, n.minTop = u % a, n.maxTop = u + Math.floor((g - e) / a) * a, n.minRight = m % o, n.maxRight = m + Math.floor((l - t) / o) * o, n.minBottom = d % a, n.maxBottom = d + Math.floor((g - e) / a) * a, i && (n.minLeft = Math.max(n.minLeft, this.parentWidth - m - i), n.minRight = Math.max(n.minRight, this.parentWidth - r - i)), s && (n.minTop = Math.max(n.minTop, this.parentHeight - d - s), n.minBottom = Math.max(n.minBottom, this.parentHeight - u - s)), this.lockAspectRatio && (n.minLeft = Math.max(n.minLeft, r - u * h), n.minTop = Math.max(n.minTop, u - r / h), n.minRight = Math.max(n.minRight, m - d * h), n.minBottom = Math.max(n.minBottom, d - m / h))) : (n.minLeft = null, n.maxLeft = r + Math.floor((l - t) / o) * o, n.minTop = null, n.maxTop = u + Math.floor((g - e) / a) * a, n.minRight = null, n.maxRight = m + Math.floor((l - t) / o) * o, n.minBottom = null, n.maxBottom = d + Math.floor((g - e) / a) * a, i && (n.minLeft = -(m + i), n.minRight = -(r + i)), s && (n.minTop = -(d + s), n.minBottom = -(u + s)), this.lockAspectRatio && i && s && (n.minLeft = Math.min(n.minLeft, -(m + i)), n.minTop = Math.min(n.minTop, -(s + d)), n.minRight = Math.min(n.minRight, -r - i), n.minBottom = Math.min(n.minBottom, -u - s))), n;
},
move(t) {
this.resizing ? this.handleResize(t) : this.dragEnable && this.handleDrag(t);
},
handleDrag(t) {
const e = this.axis, i = this.grid, s = this.bounds, h = this.mouseClickPosition, o = e && e !== "y" ? h.mouseX - (t.touches ? t.touches[0].pageX : t.pageX) : 0, a = e && e !== "x" ? h.mouseY - (t.touches ? t.touches[0].pageY : t.pageY) : 0, [l, g] = w(i, o, a, this.scale), r = c(h.left - l, s.minLeft, s.maxLeft), u = c(h.top - g, s.minTop, s.maxTop);
if (this.onDrag(r, u) === !1)
return;
const m = c(h.right + l, s.minRight, s.maxRight), d = c(h.bottom + g, s.minBottom, s.maxBottom);
this.left = r, this.top = u, this.right = m, this.bottom = d, this.$emit("dragging", this.left, this.top), this.dragging = !0;
},
moveHorizontally(t) {
const [e, i] = w(this.grid, t, this.top, 1), s = c(e, this.bounds.minLeft, this.bounds.maxLeft);
this.left = s, this.right = this.parentWidth - this.width - s;
},
moveVertically(t) {
const [e, i] = w(this.grid, this.left, t, 1), s = c(i, this.bounds.minTop, this.bounds.maxTop);
this.top = s, this.bottom = this.parentHeight - this.height - s;
},
handleResize(t) {
let e = this.left, i = this.top, s = this.right, h = this.bottom;
const o = this.mouseClickPosition, a = this.aspectFactor, l = o.mouseX - (t.touches ? t.touches[0].pageX : t.pageX), g = o.mouseY - (t.touches ? t.touches[0].pageY : t.pageY);
!this.widthTouched && l && (this.widthTouched = !0), !this.heightTouched && g && (this.heightTouched = !0);
const [r, u] = w(this.grid, l, g, this.scale);
this.handle.includes("b") ? (h = c(
o.bottom + u,
this.bounds.minBottom,
this.bounds.maxBottom
), this.lockAspectRatio && this.resizingOnY && (s = this.right - (this.bottom - h) * a)) : this.handle.includes("t") && (i = c(
o.top - u,
this.bounds.minTop,
this.bounds.maxTop
), this.lockAspectRatio && this.resizingOnY && (e = this.left - (this.top - i) * a)), this.handle.includes("r") ? (s = c(
o.right + r,
this.bounds.minRight,
this.bounds.maxRight
), this.lockAspectRatio && this.resizingOnX && (h = this.bottom - (this.right - s) / a)) : this.handle.includes("l") && (e = c(
o.left - r,
this.bounds.minLeft,
this.bounds.maxLeft
), this.lockAspectRatio && this.resizingOnX && (i = this.top - (this.left - e) / a));
const m = R(this.parentWidth, e, s), d = S(this.parentHeight, i, h);
this.onResize(this.handle, e, i, m, d) !== !1 && (this.left = e, this.top = i, this.right = s, this.bottom = h, this.width = m, this.height = d, this.$emit("resizing", this.left, this.top, this.width, this.height), this.resizing = !0);
},
changeWidth(t) {
const [e, i] = w(this.grid, t, 0, 1), s = c(
this.parentWidth - e - this.left,
this.bounds.minRight,
this.bounds.maxRight
);
let h = this.bottom;
this.lockAspectRatio && (h = this.bottom - (this.right - s) / this.aspectFactor);
const o = R(this.parentWidth, this.left, s), a = S(this.parentHeight, this.top, h);
this.right = s, this.bottom = h, this.width = o, this.height = a;
},
changeHeight(t) {
const [e, i] = w(this.grid, 0, t, 1), s = c(
this.parentHeight - i - this.top,
this.bounds.minBottom,
this.bounds.maxBottom
);
let h = this.right;
this.lockAspectRatio && (h = this.right - (this.bottom - s) * this.aspectFactor);
const o = R(this.parentWidth, this.left, h), a = S(this.parentHeight, this.top, s);
this.right = h, this.bottom = s, this.width = o, this.height = a;
},
handleUp(t) {
this.handle = null, this.resetBoundsAndMouseState(), this.dragEnable = !1, this.resizeEnable = !1, this.resizing && (this.resizing = !1, this.$emit("resizeStop", this.left, this.top, this.width, this.height)), this.dragging && (this.dragging = !1, this.$emit("dragStop", this.left, this.top)), f(document.documentElement, p.move, this.handleResize);
}
},
computed: {
style() {
return {
transform: `translate(${this.left}px, ${this.top}px)`,
width: this.computedWidth,
height: this.computedHeight,
zIndex: this.zIndex,
...this.dragging && this.disableUserSelect ? C : X
};
},
actualHandles() {
return this.resizable ? this.handles : [];
},
computedWidth() {
return this.w === "auto" && !this.widthTouched ? "auto" : this.width + "px";
},
computedHeight() {
return this.h === "auto" && !this.heightTouched ? "auto" : this.height + "px";
},
minW() {
return this.minWidth;
},
minH() {
return this.minHeight;
},
maxW() {
return this.maxWidth;
},
maxH() {
return this.maxHeight;
},
resizingOnX() {
return !!this.handle && (this.handle.includes("l") || this.handle.includes("r"));
},
resizingOnY() {
return !!this.handle && (this.handle.includes("t") || this.handle.includes("b"));
},
isCornerHandle() {
return !!this.handle && ["tl", "tr", "br", "bl"].includes(this.handle);
}
},
watch: {
active(t) {
this.enabled = t, t ? this.$emit("activated") : this.$emit("deactivated");
},
z(t) {
(t >= 0 || t === "auto") && (this.zIndex = t);
},
x(t) {
this.resizing || this.dragging || (this.parent && (this.bounds = this.calcDragLimits()), this.moveHorizontally(t));
},
y(t) {
this.resizing || this.dragging || (this.parent && (this.bounds = this.calcDragLimits()), this.moveVertically(t));
},
lockAspectRatio(t) {
t ? this.aspectFactor = this.width / this.height : this.aspectFactor = void 0;
},
w(t) {
this.resizing || this.dragging || (this.parent && (this.bounds = this.calcResizeLimits()), this.changeWidth(t));
},
h(t) {
this.resizing || this.dragging || (this.parent && (this.bounds = this.calcResizeLimits()), this.changeHeight(t));
}
}
}, A = ["onMousedown", "onTouchstart"];
function F(t, e, i, s, h, o) {
return z(), v("div", {
style: H(o.style),
class: T([{
[i.classNameActive]: t.enabled,
[i.classNameDragging]: t.dragging,
[i.classNameResizing]: t.resizing,
[i.classNameDraggable]: i.draggable,
[i.classNameResizable]: i.resizable
}, i.className]),
onMousedown: e[0] || (e[0] = (...a) => o.elementMouseDown && o.elementMouseDown(...a)),
onTouchstart: e[1] || (e[1] = (...a) => o.elementTouchDown && o.elementTouchDown(...a))
}, [
(z(!0), v(E, null, L(o.actualHandles, (a) => (z(), v("div", {
key: a,
class: T([i.classNameHandle, i.classNameHandle + "-" + a]),
style: H({ display: t.enabled ? "block" : "none" }),
onMousedown: B((l) => o.handleDown(a, l), ["stop", "prevent"]),
onTouchstart: B((l) => o.handleTouchDown(a, l), ["stop", "prevent"])
}, [
k(t.$slots, a)
], 46, A))), 128)),
k(t.$slots, "default")
], 38);
}
const U = /* @__PURE__ */ P(Y, [["render", F]]);
function M(t) {
M.installed || (M.installed = !0, t.component("VueDraggableResizable", U));
}
const V = {
install: M
};
let x = null;
typeof window < "u" ? x = window.Vue : typeof global < "u" && (x = global.Vue);
x && x.use(V);
export {
U as default,
M as install
};