-
-
- {{ title }}
-
+
+
-
-
diff --git a/src/directives/draggable.js b/src/directives/draggable.js
index 726d156..bb43ae8 100644
--- a/src/directives/draggable.js
+++ b/src/directives/draggable.js
@@ -1,64 +1,97 @@
-const moveModal = (elmnt) => {
- console.log(elmnt);
- var pos1 = 0,
- pos2 = 0,
- pos3 = 0,
- pos4 = 0;
- document.body.addEventListener("mousedown", dragMouseDown, {
- passive: false,
- });
+// src/directives/draggable.js
+// 用法:在 Modal 外層加 v-draggable="{ handle: '[data-drag-handle]', target: '.modal-box' }"
+// - handle:只在這個節點按下才可拖動
+// - target:實際被拖動的 DOM(預設用 el 本身)
- function dragMouseDown(e) {
- console.log("dragMouseDown", e);
- e = e || window.event;
+function createDraggable(el, options = {}) {
+ const cancelSelector =
+ 'input, textarea, select, button, [contenteditable="true"], [data-drag-cancel]';
+
+ const target =
+ (options.target && el.querySelector(options.target)) ||
+ el.querySelector('.modal-box') ||
+ el;
+
+ const handle =
+ (options.handle && el.querySelector(options.handle)) || target;
+
+ let startX = 0,
+ startY = 0,
+ originLeft = 0,
+ originTop = 0,
+ dragging = false;
+
+ // 只在「左鍵」且「不是表單控制項」時啟動拖曳
+ function onMouseDown(e) {
+ if (e.button !== 0) return;
+
+ // 點到可互動元件就不要拖(也不要 preventDefault,讓它能聚焦)
+ if (e.target.matches(cancelSelector) || e.target.closest(cancelSelector)) {
+ return;
+ }
+
+ // 這裡才開始準備拖曳
+ dragging = true;
+ const rect = target.getBoundingClientRect();
+ originLeft = rect.left;
+ originTop = rect.top;
+ startX = e.clientX;
+ startY = e.clientY;
+
+ // 只有在真的要拖時才阻止預設,避免選字/圖片拖移
e.preventDefault();
- // get the mouse cursor position at startup:
- pos3 = e.clientX;
- pos4 = e.clientY;
- document.body.addEventListener("mouseup", closeDragElement, {
- passive: false,
- });
- // call a function whenever the cursor moves:
- document.body.addEventListener("mousemove", elementDrag, {
- passive: false,
- });
+
+ // 以視窗為座標系比較直觀
+ target.style.position = 'fixed';
+ target.style.left = '0px';
+ target.style.top = '0px';
+ target.style.transform = `translate(${originLeft}px, ${originTop}px)`;
+
+ window.addEventListener('mousemove', onMouseMove, { passive: false });
+ window.addEventListener('mouseup', onMouseUp, { passive: true });
}
- function elementDrag(e) {
- e = e || window.event;
- e.preventDefault();
- // calculate the new cursor position:
- pos1 = pos3 - e.clientX;
- pos2 = pos4 - e.clientY;
- pos3 = e.clientX;
- pos4 = e.clientY;
- // set the element's new position:
- elmnt.style.top = elmnt.offsetTop - pos2 + "px";
- elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
+ function onMouseMove(e) {
+ if (!dragging) return;
+ const dx = e.clientX - startX;
+ const dy = e.clientY - startY;
+ target.style.transform = `translate(${originLeft + dx}px, ${originTop + dy}px)`;
}
- function closeDragElement() {
- // stop moving when mouse button is released:
- document.body.removeEventListener("mouseup", closeDragElement);
- document.body.removeEventListener("mousemove", elementDrag);
+ function onMouseUp() {
+ dragging = false;
+ window.removeEventListener('mousemove', onMouseMove);
+ window.removeEventListener('mouseup', onMouseUp);
}
-};
+
+ handle.style.cursor = 'move';
+ handle.addEventListener('mousedown', onMouseDown, { passive: false });
+
+ // 提供清理函式給 unmounted 用
+ return () => {
+ handle.removeEventListener('mousedown', onMouseDown);
+ window.removeEventListener('mousemove', onMouseMove);
+ window.removeEventListener('mouseup', onMouseUp);
+ };
+}
export const draggable = {
install(app) {
- app.directive("draggable", {
- mounted: (el, binding, vnode, prevVnode) => {
- console.log("draggable", $(`#${el.id}`).draggable);
- if (binding.value) {
- if ($(`#${el.id}`).draggable) {
- $(`#${el.id}`).draggable({
- cursor: "move",
- scroll: true,
- container: ".app-container",
- });
- } else {
- moveModal(el);
- }
+ app.directive('draggable', {
+ mounted(el, binding) {
+ // 允許 v-draggable 或 v-draggable="true" 或 v-draggable="{ handle, target }"
+ const enabled =
+ binding.value === '' || binding.value === true || typeof binding.value === 'object';
+ if (!enabled) return;
+
+ // 以前用 jQuery UI 的判斷會在沒有 $ 或 .draggable 時噴錯,直接移除
+ const options = typeof binding.value === 'object' ? binding.value : {};
+ el.__dragCleanup__ = createDraggable(el, options);
+ },
+ unmounted(el) {
+ if (el.__dragCleanup__) {
+ el.__dragCleanup__();
+ delete el.__dragCleanup__;
}
},
});