@havue/drag-and-drop 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/drag-and-drop.mjs +476 -0
- package/dist/drag-and-drop.umd.js +477 -0
- package/dist/style.css +20 -0
- package/dist/types/src/Draggable.vue.d.ts +47 -0
- package/dist/types/src/Droppable.vue.d.ts +41 -0
- package/dist/types/src/index.d.ts +6 -0
- package/dist/types/src/manager/index.d.ts +81 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import './style.css';
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
import { defineComponent, ref, reactive, computed, createElementBlock, openBlock, normalizeClass, renderSlot, createCommentVNode, normalizeStyle } from "vue";
|
|
6
|
+
import { EventBus, isMobile } from "@havue/shared";
|
|
7
|
+
const withInstall = (main, extra) => {
|
|
8
|
+
main.install = (app) => {
|
|
9
|
+
for (const comp of [main, ...Object.values({})]) {
|
|
10
|
+
app.component(comp.name, comp);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
return main;
|
|
14
|
+
};
|
|
15
|
+
class DnDManager extends EventBus {
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
/** 是否开始拖动 */
|
|
19
|
+
__publicField(this, "isDragStart", false);
|
|
20
|
+
/** 拖动元素类型 */
|
|
21
|
+
__publicField(this, "dragType");
|
|
22
|
+
/** Draggable传递的数据,
|
|
23
|
+
* 供Droppable使用
|
|
24
|
+
*/
|
|
25
|
+
__publicField(this, "dragData");
|
|
26
|
+
__publicField(this, "isSendFirstMovePos", false);
|
|
27
|
+
/** 移动端长按一定时间才触发 onStart */
|
|
28
|
+
__publicField(this, "touchStartPressTime", 300);
|
|
29
|
+
/** touchstart事件触发的时的时间 */
|
|
30
|
+
__publicField(this, "touchStartTime", 0);
|
|
31
|
+
/** touchstart时间触发的位置 */
|
|
32
|
+
__publicField(this, "touchStartPosition", { x: 0, y: 0 });
|
|
33
|
+
/** onMove最后位置 */
|
|
34
|
+
__publicField(this, "lastMovePoint", { x: 0, y: 0 });
|
|
35
|
+
__publicField(this, "emitTouchStartTimer");
|
|
36
|
+
__publicField(this, "destroy", () => {
|
|
37
|
+
});
|
|
38
|
+
this.bindEventListener();
|
|
39
|
+
}
|
|
40
|
+
updateDargInfo(type, data) {
|
|
41
|
+
if (type === void 0 || type === null) {
|
|
42
|
+
throw new Error("请传入拖动元素 type");
|
|
43
|
+
}
|
|
44
|
+
this.isDragStart = true;
|
|
45
|
+
this.dragType = type;
|
|
46
|
+
this.dragData = data;
|
|
47
|
+
this.emitTouchStartTimer && clearTimeout(this.emitTouchStartTimer);
|
|
48
|
+
this.emitTouchStartTimer = void 0;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 通知 Draggable 开始点击
|
|
52
|
+
* @param point
|
|
53
|
+
*/
|
|
54
|
+
onDown(point) {
|
|
55
|
+
this.emit("down", point);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 通知 Draggable 拖拽开始
|
|
59
|
+
* @param point
|
|
60
|
+
*/
|
|
61
|
+
onFirstMove(point, e) {
|
|
62
|
+
if (this.isDragStart) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.isSendFirstMovePos = true;
|
|
66
|
+
this.emit("first-move", point, e);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 通知 Draggable 拖拽开始
|
|
70
|
+
* @param point
|
|
71
|
+
*/
|
|
72
|
+
onStart(point) {
|
|
73
|
+
if (this.isDragStart) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.emit("start", point);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 通知 Draggable 移动
|
|
80
|
+
* @param point
|
|
81
|
+
* @returns
|
|
82
|
+
*/
|
|
83
|
+
onMove(point) {
|
|
84
|
+
if (!this.isDragStart || !this.dragType) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.lastMovePoint = point;
|
|
88
|
+
this.emit("move", {
|
|
89
|
+
type: this.dragType,
|
|
90
|
+
data: this.dragData,
|
|
91
|
+
point
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 结束拖拽
|
|
96
|
+
* @returns
|
|
97
|
+
*/
|
|
98
|
+
onEnd() {
|
|
99
|
+
this.emitTouchStartTimer && clearTimeout(this.emitTouchStartTimer);
|
|
100
|
+
this.emitTouchStartTimer = void 0;
|
|
101
|
+
this.isSendFirstMovePos = false;
|
|
102
|
+
if (!this.isDragStart || !this.dragType) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.isDragStart = false;
|
|
106
|
+
this.emit("end", {
|
|
107
|
+
point: this.lastMovePoint,
|
|
108
|
+
type: this.dragType,
|
|
109
|
+
data: this.dragData
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
onMouseDown(e) {
|
|
113
|
+
if (e.buttons !== 1) {
|
|
114
|
+
this.onEnd();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { clientX, clientY } = e;
|
|
118
|
+
const position = {
|
|
119
|
+
x: clientX,
|
|
120
|
+
y: clientY
|
|
121
|
+
};
|
|
122
|
+
this.touchStartTime = Date.now();
|
|
123
|
+
this.touchStartPosition = position;
|
|
124
|
+
this.emitTouchStartTimer && clearTimeout(this.emitTouchStartTimer);
|
|
125
|
+
this.onDown(position);
|
|
126
|
+
this.emitTouchStartTimer = window.setTimeout(() => {
|
|
127
|
+
this.onStart(position);
|
|
128
|
+
}, this.touchStartPressTime);
|
|
129
|
+
}
|
|
130
|
+
onMouseMove(e) {
|
|
131
|
+
if (e.buttons !== 1) {
|
|
132
|
+
this.onEnd();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const { clientX, clientY } = e;
|
|
136
|
+
if (this.isDragStart) {
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
e.stopPropagation();
|
|
139
|
+
this.onMove({
|
|
140
|
+
x: clientX,
|
|
141
|
+
y: clientY
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
if (!this.isSendFirstMovePos) {
|
|
145
|
+
this.onFirstMove(
|
|
146
|
+
{
|
|
147
|
+
x: clientX,
|
|
148
|
+
y: clientY
|
|
149
|
+
},
|
|
150
|
+
e
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const { x, y } = this.touchStartPosition;
|
|
154
|
+
const timeInLimit = Date.now() - this.touchStartTime < this.touchStartPressTime;
|
|
155
|
+
if (timeInLimit && (Math.abs(x - clientX) > 30 || Math.abs(y - clientY) > 30)) {
|
|
156
|
+
clearTimeout(this.emitTouchStartTimer);
|
|
157
|
+
this.emitTouchStartTimer = void 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
onMouseUp() {
|
|
162
|
+
this.onEnd();
|
|
163
|
+
}
|
|
164
|
+
onTouchStart(e) {
|
|
165
|
+
if (e.touches.length > 1) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const { clientX, clientY } = e.touches[0];
|
|
169
|
+
const position = {
|
|
170
|
+
x: clientX,
|
|
171
|
+
y: clientY
|
|
172
|
+
};
|
|
173
|
+
this.touchStartTime = Date.now();
|
|
174
|
+
this.touchStartPosition = position;
|
|
175
|
+
this.onDown(position);
|
|
176
|
+
this.emitTouchStartTimer = window.setTimeout(() => {
|
|
177
|
+
this.onStart(position);
|
|
178
|
+
}, this.touchStartPressTime);
|
|
179
|
+
}
|
|
180
|
+
onTouchMove(e) {
|
|
181
|
+
if (e.touches.length > 1) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const { clientX, clientY } = e.touches[0];
|
|
185
|
+
if (this.isDragStart) {
|
|
186
|
+
e.preventDefault();
|
|
187
|
+
e.stopPropagation();
|
|
188
|
+
this.onMove({
|
|
189
|
+
x: clientX,
|
|
190
|
+
y: clientY
|
|
191
|
+
});
|
|
192
|
+
} else {
|
|
193
|
+
if (!this.isSendFirstMovePos) {
|
|
194
|
+
this.onFirstMove(
|
|
195
|
+
{
|
|
196
|
+
x: clientX,
|
|
197
|
+
y: clientY
|
|
198
|
+
},
|
|
199
|
+
e
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const { x, y } = this.touchStartPosition;
|
|
203
|
+
const timeInLimit = Date.now() - this.touchStartTime < this.touchStartPressTime;
|
|
204
|
+
if (timeInLimit && (Math.abs(x - clientX) > 30 || Math.abs(y - clientY) > 30)) {
|
|
205
|
+
clearTimeout(this.emitTouchStartTimer);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
onTouchEnd() {
|
|
210
|
+
this.onEnd();
|
|
211
|
+
}
|
|
212
|
+
bindEventListener() {
|
|
213
|
+
if (document.body) {
|
|
214
|
+
const onTouchStart = this.onTouchStart.bind(this);
|
|
215
|
+
const onTouchMove = this.onTouchMove.bind(this);
|
|
216
|
+
const onTouchEnd = this.onTouchEnd.bind(this);
|
|
217
|
+
const onMouseDown = this.onMouseDown.bind(this);
|
|
218
|
+
const onMouseMove = this.onMouseMove.bind(this);
|
|
219
|
+
const onMouseUp = this.onMouseUp.bind(this);
|
|
220
|
+
if (isMobile) {
|
|
221
|
+
document.body.addEventListener("touchstart", onTouchStart, { passive: false });
|
|
222
|
+
document.body.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
223
|
+
document.body.addEventListener("touchend", onTouchEnd, { passive: false });
|
|
224
|
+
} else {
|
|
225
|
+
document.body.addEventListener("mousedown", onMouseDown);
|
|
226
|
+
document.body.addEventListener("mousemove", onMouseMove);
|
|
227
|
+
document.body.addEventListener("mouseup", onMouseUp);
|
|
228
|
+
}
|
|
229
|
+
this.destroy = () => {
|
|
230
|
+
document.body.removeEventListener("touchstart", onTouchStart);
|
|
231
|
+
document.body.removeEventListener("touchmove", onTouchMove);
|
|
232
|
+
document.body.removeEventListener("touchend", onTouchEnd);
|
|
233
|
+
document.body.removeEventListener("mousedown", onMouseDown);
|
|
234
|
+
document.body.removeEventListener("mousemove", onMouseMove);
|
|
235
|
+
document.body.removeEventListener("mouseup", onMouseUp);
|
|
236
|
+
};
|
|
237
|
+
} else {
|
|
238
|
+
document.addEventListener("DOMContentLoaded", this.bindEventListener);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
removeEventListener() {
|
|
242
|
+
this.destroy();
|
|
243
|
+
}
|
|
244
|
+
destroyed() {
|
|
245
|
+
this.isDragStart = false;
|
|
246
|
+
this.dragType = void 0;
|
|
247
|
+
this.dragData = void 0;
|
|
248
|
+
this.removeEventListener();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const DnDManagerInstance = new DnDManager();
|
|
252
|
+
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
253
|
+
...{
|
|
254
|
+
name: "HvDraggable"
|
|
255
|
+
},
|
|
256
|
+
__name: "Draggable",
|
|
257
|
+
props: {
|
|
258
|
+
type: {},
|
|
259
|
+
immediate: {},
|
|
260
|
+
disabled: { type: Boolean, default: false },
|
|
261
|
+
data: {}
|
|
262
|
+
},
|
|
263
|
+
setup(__props) {
|
|
264
|
+
const ImmediateEnumType = {
|
|
265
|
+
LEFT: "left",
|
|
266
|
+
RIGHT: "right",
|
|
267
|
+
TOP: "top",
|
|
268
|
+
BOTTOM: "bottom",
|
|
269
|
+
ALL: "all"
|
|
270
|
+
};
|
|
271
|
+
const props = __props;
|
|
272
|
+
const dragItemRef = ref();
|
|
273
|
+
const isDownThis = ref(false);
|
|
274
|
+
const downPosition = reactive({
|
|
275
|
+
x: 0,
|
|
276
|
+
y: 0
|
|
277
|
+
});
|
|
278
|
+
const isDragThis = ref(false);
|
|
279
|
+
const cloneNodePosition = reactive({
|
|
280
|
+
x: 0,
|
|
281
|
+
y: 0
|
|
282
|
+
});
|
|
283
|
+
const immediateDirections = computed(() => {
|
|
284
|
+
if (Array.isArray(props.immediate)) {
|
|
285
|
+
return props.immediate;
|
|
286
|
+
}
|
|
287
|
+
return props.immediate ? [props.immediate] : [];
|
|
288
|
+
});
|
|
289
|
+
const cloneNodeStyle = computed(() => {
|
|
290
|
+
return {
|
|
291
|
+
transform: `translate(${cloneNodePosition.x}px, ${cloneNodePosition.y}px) translate(-50%, -50%)`
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
DnDManagerInstance.on("down", (params) => {
|
|
295
|
+
const { x, y } = params;
|
|
296
|
+
const startEl = document.elementFromPoint(x, y);
|
|
297
|
+
if (!props.disabled && dragItemRef.value && dragItemRef.value.contains(startEl)) {
|
|
298
|
+
if (immediateDirections.value.includes(ImmediateEnumType.ALL)) {
|
|
299
|
+
handleStart(params);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
isDownThis.value = true;
|
|
303
|
+
downPosition.x = x;
|
|
304
|
+
downPosition.y = y;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
DnDManagerInstance.on("first-move", (params, event) => {
|
|
308
|
+
const { x, y } = params;
|
|
309
|
+
const directions = immediateDirections.value.filter((item) => item !== ImmediateEnumType.ALL);
|
|
310
|
+
if (isDownThis.value && !props.disabled && directions.length) {
|
|
311
|
+
const distanceH = x - downPosition.x;
|
|
312
|
+
const distanceHAbs = Math.abs(distanceH);
|
|
313
|
+
const distanceV = y - downPosition.y;
|
|
314
|
+
const distanceVAbs = Math.abs(distanceV);
|
|
315
|
+
const max = Math.max(distanceHAbs, distanceVAbs);
|
|
316
|
+
const isMaxH = distanceHAbs === max;
|
|
317
|
+
const isMaxV = distanceVAbs === max;
|
|
318
|
+
let isImmediate = false;
|
|
319
|
+
if (isMaxH) {
|
|
320
|
+
isImmediate = directions.includes(ImmediateEnumType.LEFT) && distanceH < 0 || directions.includes(ImmediateEnumType.RIGHT) && distanceH > 0;
|
|
321
|
+
}
|
|
322
|
+
if (isMaxV && !isImmediate) {
|
|
323
|
+
isImmediate = directions.includes(ImmediateEnumType.TOP) && distanceV < 0 || directions.includes(ImmediateEnumType.BOTTOM) && distanceV > 0;
|
|
324
|
+
}
|
|
325
|
+
if (isImmediate) {
|
|
326
|
+
event.preventDefault();
|
|
327
|
+
event.stopPropagation();
|
|
328
|
+
handleStart(downPosition);
|
|
329
|
+
handleMove(params);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
DnDManagerInstance.on("start", (params) => {
|
|
335
|
+
const { x, y } = params;
|
|
336
|
+
const startEl = document.elementFromPoint(x, y);
|
|
337
|
+
if (!props.disabled && dragItemRef.value && dragItemRef.value.contains(startEl)) {
|
|
338
|
+
handleStart(params);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
DnDManagerInstance.on("move", (params) => {
|
|
342
|
+
const { point } = params;
|
|
343
|
+
if (isDragThis.value) {
|
|
344
|
+
handleMove(point);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
DnDManagerInstance.on("end", () => {
|
|
348
|
+
isDownThis.value = false;
|
|
349
|
+
isDragThis.value = false;
|
|
350
|
+
Object.assign(downPosition, {
|
|
351
|
+
x: 0,
|
|
352
|
+
y: 0
|
|
353
|
+
});
|
|
354
|
+
Object.assign(cloneNodePosition, {
|
|
355
|
+
x: 0,
|
|
356
|
+
y: 0
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
function handleStart(point) {
|
|
360
|
+
isDragThis.value = true;
|
|
361
|
+
cloneNodePosition.x = point.x;
|
|
362
|
+
cloneNodePosition.y = point.y;
|
|
363
|
+
DnDManagerInstance.updateDargInfo(props.type, props.data);
|
|
364
|
+
}
|
|
365
|
+
function handleMove(point) {
|
|
366
|
+
cloneNodePosition.x = point.x;
|
|
367
|
+
cloneNodePosition.y = point.y;
|
|
368
|
+
}
|
|
369
|
+
return (_ctx, _cache) => {
|
|
370
|
+
return openBlock(), createElementBlock("div", {
|
|
371
|
+
ref_key: "dragItemRef",
|
|
372
|
+
ref: dragItemRef,
|
|
373
|
+
class: normalizeClass(["draggable-area", _ctx.disabled ? "disabled" : ""])
|
|
374
|
+
}, [
|
|
375
|
+
renderSlot(_ctx.$slots, "default", {}, void 0, true),
|
|
376
|
+
isDragThis.value ? (openBlock(), createElementBlock("div", {
|
|
377
|
+
key: 0,
|
|
378
|
+
class: "draggable-clone-item",
|
|
379
|
+
style: normalizeStyle(cloneNodeStyle.value)
|
|
380
|
+
}, [
|
|
381
|
+
renderSlot(_ctx.$slots, "drag-item", {}, () => [
|
|
382
|
+
renderSlot(_ctx.$slots, "default", {}, void 0, true)
|
|
383
|
+
], true)
|
|
384
|
+
], 4)) : createCommentVNode("", true)
|
|
385
|
+
], 2);
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
const _export_sfc = (sfc, props) => {
|
|
390
|
+
const target = sfc.__vccOpts || sfc;
|
|
391
|
+
for (const [key, val] of props) {
|
|
392
|
+
target[key] = val;
|
|
393
|
+
}
|
|
394
|
+
return target;
|
|
395
|
+
};
|
|
396
|
+
const Draggable = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-34f8fce1"]]);
|
|
397
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
398
|
+
...{
|
|
399
|
+
name: "HvDroppable"
|
|
400
|
+
},
|
|
401
|
+
__name: "Droppable",
|
|
402
|
+
props: {
|
|
403
|
+
acceptDragType: {}
|
|
404
|
+
},
|
|
405
|
+
emits: ["enter", "move", "drop", "leave"],
|
|
406
|
+
setup(__props, { emit: __emit }) {
|
|
407
|
+
const emits = __emit;
|
|
408
|
+
const props = __props;
|
|
409
|
+
const dropAreaRef = ref();
|
|
410
|
+
const isEntered = ref(false);
|
|
411
|
+
const acceptDragTypeList = computed(() => {
|
|
412
|
+
return Array.isArray(props.acceptDragType) ? props.acceptDragType : [props.acceptDragType];
|
|
413
|
+
});
|
|
414
|
+
function getPositionInArea(point) {
|
|
415
|
+
if (!dropAreaRef.value) {
|
|
416
|
+
return {
|
|
417
|
+
isInArea: false,
|
|
418
|
+
position: {
|
|
419
|
+
x: 0,
|
|
420
|
+
y: 0
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
const { x: pointX, y: pointY } = point;
|
|
425
|
+
const { x, y, width, height } = dropAreaRef.value.getBoundingClientRect();
|
|
426
|
+
const posX = pointX - x;
|
|
427
|
+
const posY = pointY - y;
|
|
428
|
+
return {
|
|
429
|
+
isInArea: posX >= 0 && posX <= width && posY >= 0 && posY <= height,
|
|
430
|
+
position: {
|
|
431
|
+
x: posX,
|
|
432
|
+
y: posY
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
DnDManagerInstance.on("move", (params) => {
|
|
437
|
+
const { type, data, point } = params;
|
|
438
|
+
if (dropAreaRef.value && acceptDragTypeList.value.includes(type)) {
|
|
439
|
+
const { isInArea, position } = getPositionInArea(point);
|
|
440
|
+
if (isInArea) {
|
|
441
|
+
if (isEntered.value) {
|
|
442
|
+
emits("move", type, position, data);
|
|
443
|
+
} else {
|
|
444
|
+
isEntered.value = true;
|
|
445
|
+
emits("enter", type, position, data);
|
|
446
|
+
}
|
|
447
|
+
} else if (isEntered.value) {
|
|
448
|
+
isEntered.value = false;
|
|
449
|
+
emits("leave", type, data);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
DnDManagerInstance.on("end", ({ type, point, data }) => {
|
|
454
|
+
if (dropAreaRef.value && acceptDragTypeList.value.includes(type) && isEntered.value) {
|
|
455
|
+
const { position } = getPositionInArea(point);
|
|
456
|
+
emits("drop", type, position, data);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
return (_ctx, _cache) => {
|
|
460
|
+
return openBlock(), createElementBlock("div", {
|
|
461
|
+
ref_key: "dropAreaRef",
|
|
462
|
+
ref: dropAreaRef,
|
|
463
|
+
class: "droppable-area"
|
|
464
|
+
}, [
|
|
465
|
+
renderSlot(_ctx.$slots, "default", {}, void 0, true)
|
|
466
|
+
], 512);
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
const Droppable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-28d651f3"]]);
|
|
471
|
+
const HvDraggable = withInstall(Draggable);
|
|
472
|
+
const HvDroppable = withInstall(Droppable);
|
|
473
|
+
export {
|
|
474
|
+
HvDraggable,
|
|
475
|
+
HvDroppable
|
|
476
|
+
};
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
(function(global, factory) {
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue"), require("@havue/shared")) : typeof define === "function" && define.amd ? define(["exports", "vue", "@havue/shared"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["drag-and-drop"] = {}, global.Vue, global.shared));
|
|
3
|
+
})(this, function(exports2, vue, shared) {
|
|
4
|
+
"use strict";var __defProp = Object.defineProperty;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
+
|
|
8
|
+
const withInstall = (main, extra) => {
|
|
9
|
+
main.install = (app) => {
|
|
10
|
+
for (const comp of [main, ...Object.values({})]) {
|
|
11
|
+
app.component(comp.name, comp);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
return main;
|
|
15
|
+
};
|
|
16
|
+
class DnDManager extends shared.EventBus {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
/** 是否开始拖动 */
|
|
20
|
+
__publicField(this, "isDragStart", false);
|
|
21
|
+
/** 拖动元素类型 */
|
|
22
|
+
__publicField(this, "dragType");
|
|
23
|
+
/** Draggable传递的数据,
|
|
24
|
+
* 供Droppable使用
|
|
25
|
+
*/
|
|
26
|
+
__publicField(this, "dragData");
|
|
27
|
+
__publicField(this, "isSendFirstMovePos", false);
|
|
28
|
+
/** 移动端长按一定时间才触发 onStart */
|
|
29
|
+
__publicField(this, "touchStartPressTime", 300);
|
|
30
|
+
/** touchstart事件触发的时的时间 */
|
|
31
|
+
__publicField(this, "touchStartTime", 0);
|
|
32
|
+
/** touchstart时间触发的位置 */
|
|
33
|
+
__publicField(this, "touchStartPosition", { x: 0, y: 0 });
|
|
34
|
+
/** onMove最后位置 */
|
|
35
|
+
__publicField(this, "lastMovePoint", { x: 0, y: 0 });
|
|
36
|
+
__publicField(this, "emitTouchStartTimer");
|
|
37
|
+
__publicField(this, "destroy", () => {
|
|
38
|
+
});
|
|
39
|
+
this.bindEventListener();
|
|
40
|
+
}
|
|
41
|
+
updateDargInfo(type, data) {
|
|
42
|
+
if (type === void 0 || type === null) {
|
|
43
|
+
throw new Error("请传入拖动元素 type");
|
|
44
|
+
}
|
|
45
|
+
this.isDragStart = true;
|
|
46
|
+
this.dragType = type;
|
|
47
|
+
this.dragData = data;
|
|
48
|
+
this.emitTouchStartTimer && clearTimeout(this.emitTouchStartTimer);
|
|
49
|
+
this.emitTouchStartTimer = void 0;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 通知 Draggable 开始点击
|
|
53
|
+
* @param point
|
|
54
|
+
*/
|
|
55
|
+
onDown(point) {
|
|
56
|
+
this.emit("down", point);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 通知 Draggable 拖拽开始
|
|
60
|
+
* @param point
|
|
61
|
+
*/
|
|
62
|
+
onFirstMove(point, e) {
|
|
63
|
+
if (this.isDragStart) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.isSendFirstMovePos = true;
|
|
67
|
+
this.emit("first-move", point, e);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 通知 Draggable 拖拽开始
|
|
71
|
+
* @param point
|
|
72
|
+
*/
|
|
73
|
+
onStart(point) {
|
|
74
|
+
if (this.isDragStart) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.emit("start", point);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 通知 Draggable 移动
|
|
81
|
+
* @param point
|
|
82
|
+
* @returns
|
|
83
|
+
*/
|
|
84
|
+
onMove(point) {
|
|
85
|
+
if (!this.isDragStart || !this.dragType) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.lastMovePoint = point;
|
|
89
|
+
this.emit("move", {
|
|
90
|
+
type: this.dragType,
|
|
91
|
+
data: this.dragData,
|
|
92
|
+
point
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 结束拖拽
|
|
97
|
+
* @returns
|
|
98
|
+
*/
|
|
99
|
+
onEnd() {
|
|
100
|
+
this.emitTouchStartTimer && clearTimeout(this.emitTouchStartTimer);
|
|
101
|
+
this.emitTouchStartTimer = void 0;
|
|
102
|
+
this.isSendFirstMovePos = false;
|
|
103
|
+
if (!this.isDragStart || !this.dragType) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.isDragStart = false;
|
|
107
|
+
this.emit("end", {
|
|
108
|
+
point: this.lastMovePoint,
|
|
109
|
+
type: this.dragType,
|
|
110
|
+
data: this.dragData
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
onMouseDown(e) {
|
|
114
|
+
if (e.buttons !== 1) {
|
|
115
|
+
this.onEnd();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const { clientX, clientY } = e;
|
|
119
|
+
const position = {
|
|
120
|
+
x: clientX,
|
|
121
|
+
y: clientY
|
|
122
|
+
};
|
|
123
|
+
this.touchStartTime = Date.now();
|
|
124
|
+
this.touchStartPosition = position;
|
|
125
|
+
this.emitTouchStartTimer && clearTimeout(this.emitTouchStartTimer);
|
|
126
|
+
this.onDown(position);
|
|
127
|
+
this.emitTouchStartTimer = window.setTimeout(() => {
|
|
128
|
+
this.onStart(position);
|
|
129
|
+
}, this.touchStartPressTime);
|
|
130
|
+
}
|
|
131
|
+
onMouseMove(e) {
|
|
132
|
+
if (e.buttons !== 1) {
|
|
133
|
+
this.onEnd();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const { clientX, clientY } = e;
|
|
137
|
+
if (this.isDragStart) {
|
|
138
|
+
e.preventDefault();
|
|
139
|
+
e.stopPropagation();
|
|
140
|
+
this.onMove({
|
|
141
|
+
x: clientX,
|
|
142
|
+
y: clientY
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
if (!this.isSendFirstMovePos) {
|
|
146
|
+
this.onFirstMove(
|
|
147
|
+
{
|
|
148
|
+
x: clientX,
|
|
149
|
+
y: clientY
|
|
150
|
+
},
|
|
151
|
+
e
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const { x, y } = this.touchStartPosition;
|
|
155
|
+
const timeInLimit = Date.now() - this.touchStartTime < this.touchStartPressTime;
|
|
156
|
+
if (timeInLimit && (Math.abs(x - clientX) > 30 || Math.abs(y - clientY) > 30)) {
|
|
157
|
+
clearTimeout(this.emitTouchStartTimer);
|
|
158
|
+
this.emitTouchStartTimer = void 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
onMouseUp() {
|
|
163
|
+
this.onEnd();
|
|
164
|
+
}
|
|
165
|
+
onTouchStart(e) {
|
|
166
|
+
if (e.touches.length > 1) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const { clientX, clientY } = e.touches[0];
|
|
170
|
+
const position = {
|
|
171
|
+
x: clientX,
|
|
172
|
+
y: clientY
|
|
173
|
+
};
|
|
174
|
+
this.touchStartTime = Date.now();
|
|
175
|
+
this.touchStartPosition = position;
|
|
176
|
+
this.onDown(position);
|
|
177
|
+
this.emitTouchStartTimer = window.setTimeout(() => {
|
|
178
|
+
this.onStart(position);
|
|
179
|
+
}, this.touchStartPressTime);
|
|
180
|
+
}
|
|
181
|
+
onTouchMove(e) {
|
|
182
|
+
if (e.touches.length > 1) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const { clientX, clientY } = e.touches[0];
|
|
186
|
+
if (this.isDragStart) {
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
e.stopPropagation();
|
|
189
|
+
this.onMove({
|
|
190
|
+
x: clientX,
|
|
191
|
+
y: clientY
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
if (!this.isSendFirstMovePos) {
|
|
195
|
+
this.onFirstMove(
|
|
196
|
+
{
|
|
197
|
+
x: clientX,
|
|
198
|
+
y: clientY
|
|
199
|
+
},
|
|
200
|
+
e
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const { x, y } = this.touchStartPosition;
|
|
204
|
+
const timeInLimit = Date.now() - this.touchStartTime < this.touchStartPressTime;
|
|
205
|
+
if (timeInLimit && (Math.abs(x - clientX) > 30 || Math.abs(y - clientY) > 30)) {
|
|
206
|
+
clearTimeout(this.emitTouchStartTimer);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
onTouchEnd() {
|
|
211
|
+
this.onEnd();
|
|
212
|
+
}
|
|
213
|
+
bindEventListener() {
|
|
214
|
+
if (document.body) {
|
|
215
|
+
const onTouchStart = this.onTouchStart.bind(this);
|
|
216
|
+
const onTouchMove = this.onTouchMove.bind(this);
|
|
217
|
+
const onTouchEnd = this.onTouchEnd.bind(this);
|
|
218
|
+
const onMouseDown = this.onMouseDown.bind(this);
|
|
219
|
+
const onMouseMove = this.onMouseMove.bind(this);
|
|
220
|
+
const onMouseUp = this.onMouseUp.bind(this);
|
|
221
|
+
if (shared.isMobile) {
|
|
222
|
+
document.body.addEventListener("touchstart", onTouchStart, { passive: false });
|
|
223
|
+
document.body.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
224
|
+
document.body.addEventListener("touchend", onTouchEnd, { passive: false });
|
|
225
|
+
} else {
|
|
226
|
+
document.body.addEventListener("mousedown", onMouseDown);
|
|
227
|
+
document.body.addEventListener("mousemove", onMouseMove);
|
|
228
|
+
document.body.addEventListener("mouseup", onMouseUp);
|
|
229
|
+
}
|
|
230
|
+
this.destroy = () => {
|
|
231
|
+
document.body.removeEventListener("touchstart", onTouchStart);
|
|
232
|
+
document.body.removeEventListener("touchmove", onTouchMove);
|
|
233
|
+
document.body.removeEventListener("touchend", onTouchEnd);
|
|
234
|
+
document.body.removeEventListener("mousedown", onMouseDown);
|
|
235
|
+
document.body.removeEventListener("mousemove", onMouseMove);
|
|
236
|
+
document.body.removeEventListener("mouseup", onMouseUp);
|
|
237
|
+
};
|
|
238
|
+
} else {
|
|
239
|
+
document.addEventListener("DOMContentLoaded", this.bindEventListener);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
removeEventListener() {
|
|
243
|
+
this.destroy();
|
|
244
|
+
}
|
|
245
|
+
destroyed() {
|
|
246
|
+
this.isDragStart = false;
|
|
247
|
+
this.dragType = void 0;
|
|
248
|
+
this.dragData = void 0;
|
|
249
|
+
this.removeEventListener();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const DnDManagerInstance = new DnDManager();
|
|
253
|
+
const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({
|
|
254
|
+
...{
|
|
255
|
+
name: "HvDraggable"
|
|
256
|
+
},
|
|
257
|
+
__name: "Draggable",
|
|
258
|
+
props: {
|
|
259
|
+
type: {},
|
|
260
|
+
immediate: {},
|
|
261
|
+
disabled: { type: Boolean, default: false },
|
|
262
|
+
data: {}
|
|
263
|
+
},
|
|
264
|
+
setup(__props) {
|
|
265
|
+
const ImmediateEnumType = {
|
|
266
|
+
LEFT: "left",
|
|
267
|
+
RIGHT: "right",
|
|
268
|
+
TOP: "top",
|
|
269
|
+
BOTTOM: "bottom",
|
|
270
|
+
ALL: "all"
|
|
271
|
+
};
|
|
272
|
+
const props = __props;
|
|
273
|
+
const dragItemRef = vue.ref();
|
|
274
|
+
const isDownThis = vue.ref(false);
|
|
275
|
+
const downPosition = vue.reactive({
|
|
276
|
+
x: 0,
|
|
277
|
+
y: 0
|
|
278
|
+
});
|
|
279
|
+
const isDragThis = vue.ref(false);
|
|
280
|
+
const cloneNodePosition = vue.reactive({
|
|
281
|
+
x: 0,
|
|
282
|
+
y: 0
|
|
283
|
+
});
|
|
284
|
+
const immediateDirections = vue.computed(() => {
|
|
285
|
+
if (Array.isArray(props.immediate)) {
|
|
286
|
+
return props.immediate;
|
|
287
|
+
}
|
|
288
|
+
return props.immediate ? [props.immediate] : [];
|
|
289
|
+
});
|
|
290
|
+
const cloneNodeStyle = vue.computed(() => {
|
|
291
|
+
return {
|
|
292
|
+
transform: `translate(${cloneNodePosition.x}px, ${cloneNodePosition.y}px) translate(-50%, -50%)`
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
DnDManagerInstance.on("down", (params) => {
|
|
296
|
+
const { x, y } = params;
|
|
297
|
+
const startEl = document.elementFromPoint(x, y);
|
|
298
|
+
if (!props.disabled && dragItemRef.value && dragItemRef.value.contains(startEl)) {
|
|
299
|
+
if (immediateDirections.value.includes(ImmediateEnumType.ALL)) {
|
|
300
|
+
handleStart(params);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
isDownThis.value = true;
|
|
304
|
+
downPosition.x = x;
|
|
305
|
+
downPosition.y = y;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
DnDManagerInstance.on("first-move", (params, event) => {
|
|
309
|
+
const { x, y } = params;
|
|
310
|
+
const directions = immediateDirections.value.filter((item) => item !== ImmediateEnumType.ALL);
|
|
311
|
+
if (isDownThis.value && !props.disabled && directions.length) {
|
|
312
|
+
const distanceH = x - downPosition.x;
|
|
313
|
+
const distanceHAbs = Math.abs(distanceH);
|
|
314
|
+
const distanceV = y - downPosition.y;
|
|
315
|
+
const distanceVAbs = Math.abs(distanceV);
|
|
316
|
+
const max = Math.max(distanceHAbs, distanceVAbs);
|
|
317
|
+
const isMaxH = distanceHAbs === max;
|
|
318
|
+
const isMaxV = distanceVAbs === max;
|
|
319
|
+
let isImmediate = false;
|
|
320
|
+
if (isMaxH) {
|
|
321
|
+
isImmediate = directions.includes(ImmediateEnumType.LEFT) && distanceH < 0 || directions.includes(ImmediateEnumType.RIGHT) && distanceH > 0;
|
|
322
|
+
}
|
|
323
|
+
if (isMaxV && !isImmediate) {
|
|
324
|
+
isImmediate = directions.includes(ImmediateEnumType.TOP) && distanceV < 0 || directions.includes(ImmediateEnumType.BOTTOM) && distanceV > 0;
|
|
325
|
+
}
|
|
326
|
+
if (isImmediate) {
|
|
327
|
+
event.preventDefault();
|
|
328
|
+
event.stopPropagation();
|
|
329
|
+
handleStart(downPosition);
|
|
330
|
+
handleMove(params);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
DnDManagerInstance.on("start", (params) => {
|
|
336
|
+
const { x, y } = params;
|
|
337
|
+
const startEl = document.elementFromPoint(x, y);
|
|
338
|
+
if (!props.disabled && dragItemRef.value && dragItemRef.value.contains(startEl)) {
|
|
339
|
+
handleStart(params);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
DnDManagerInstance.on("move", (params) => {
|
|
343
|
+
const { point } = params;
|
|
344
|
+
if (isDragThis.value) {
|
|
345
|
+
handleMove(point);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
DnDManagerInstance.on("end", () => {
|
|
349
|
+
isDownThis.value = false;
|
|
350
|
+
isDragThis.value = false;
|
|
351
|
+
Object.assign(downPosition, {
|
|
352
|
+
x: 0,
|
|
353
|
+
y: 0
|
|
354
|
+
});
|
|
355
|
+
Object.assign(cloneNodePosition, {
|
|
356
|
+
x: 0,
|
|
357
|
+
y: 0
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
function handleStart(point) {
|
|
361
|
+
isDragThis.value = true;
|
|
362
|
+
cloneNodePosition.x = point.x;
|
|
363
|
+
cloneNodePosition.y = point.y;
|
|
364
|
+
DnDManagerInstance.updateDargInfo(props.type, props.data);
|
|
365
|
+
}
|
|
366
|
+
function handleMove(point) {
|
|
367
|
+
cloneNodePosition.x = point.x;
|
|
368
|
+
cloneNodePosition.y = point.y;
|
|
369
|
+
}
|
|
370
|
+
return (_ctx, _cache) => {
|
|
371
|
+
return vue.openBlock(), vue.createElementBlock("div", {
|
|
372
|
+
ref_key: "dragItemRef",
|
|
373
|
+
ref: dragItemRef,
|
|
374
|
+
class: vue.normalizeClass(["draggable-area", _ctx.disabled ? "disabled" : ""])
|
|
375
|
+
}, [
|
|
376
|
+
vue.renderSlot(_ctx.$slots, "default", {}, void 0, true),
|
|
377
|
+
isDragThis.value ? (vue.openBlock(), vue.createElementBlock("div", {
|
|
378
|
+
key: 0,
|
|
379
|
+
class: "draggable-clone-item",
|
|
380
|
+
style: vue.normalizeStyle(cloneNodeStyle.value)
|
|
381
|
+
}, [
|
|
382
|
+
vue.renderSlot(_ctx.$slots, "drag-item", {}, () => [
|
|
383
|
+
vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
|
|
384
|
+
], true)
|
|
385
|
+
], 4)) : vue.createCommentVNode("", true)
|
|
386
|
+
], 2);
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
const _export_sfc = (sfc, props) => {
|
|
391
|
+
const target = sfc.__vccOpts || sfc;
|
|
392
|
+
for (const [key, val] of props) {
|
|
393
|
+
target[key] = val;
|
|
394
|
+
}
|
|
395
|
+
return target;
|
|
396
|
+
};
|
|
397
|
+
const Draggable = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-34f8fce1"]]);
|
|
398
|
+
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
399
|
+
...{
|
|
400
|
+
name: "HvDroppable"
|
|
401
|
+
},
|
|
402
|
+
__name: "Droppable",
|
|
403
|
+
props: {
|
|
404
|
+
acceptDragType: {}
|
|
405
|
+
},
|
|
406
|
+
emits: ["enter", "move", "drop", "leave"],
|
|
407
|
+
setup(__props, { emit: __emit }) {
|
|
408
|
+
const emits = __emit;
|
|
409
|
+
const props = __props;
|
|
410
|
+
const dropAreaRef = vue.ref();
|
|
411
|
+
const isEntered = vue.ref(false);
|
|
412
|
+
const acceptDragTypeList = vue.computed(() => {
|
|
413
|
+
return Array.isArray(props.acceptDragType) ? props.acceptDragType : [props.acceptDragType];
|
|
414
|
+
});
|
|
415
|
+
function getPositionInArea(point) {
|
|
416
|
+
if (!dropAreaRef.value) {
|
|
417
|
+
return {
|
|
418
|
+
isInArea: false,
|
|
419
|
+
position: {
|
|
420
|
+
x: 0,
|
|
421
|
+
y: 0
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const { x: pointX, y: pointY } = point;
|
|
426
|
+
const { x, y, width, height } = dropAreaRef.value.getBoundingClientRect();
|
|
427
|
+
const posX = pointX - x;
|
|
428
|
+
const posY = pointY - y;
|
|
429
|
+
return {
|
|
430
|
+
isInArea: posX >= 0 && posX <= width && posY >= 0 && posY <= height,
|
|
431
|
+
position: {
|
|
432
|
+
x: posX,
|
|
433
|
+
y: posY
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
DnDManagerInstance.on("move", (params) => {
|
|
438
|
+
const { type, data, point } = params;
|
|
439
|
+
if (dropAreaRef.value && acceptDragTypeList.value.includes(type)) {
|
|
440
|
+
const { isInArea, position } = getPositionInArea(point);
|
|
441
|
+
if (isInArea) {
|
|
442
|
+
if (isEntered.value) {
|
|
443
|
+
emits("move", type, position, data);
|
|
444
|
+
} else {
|
|
445
|
+
isEntered.value = true;
|
|
446
|
+
emits("enter", type, position, data);
|
|
447
|
+
}
|
|
448
|
+
} else if (isEntered.value) {
|
|
449
|
+
isEntered.value = false;
|
|
450
|
+
emits("leave", type, data);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
DnDManagerInstance.on("end", ({ type, point, data }) => {
|
|
455
|
+
if (dropAreaRef.value && acceptDragTypeList.value.includes(type) && isEntered.value) {
|
|
456
|
+
const { position } = getPositionInArea(point);
|
|
457
|
+
emits("drop", type, position, data);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
return (_ctx, _cache) => {
|
|
461
|
+
return vue.openBlock(), vue.createElementBlock("div", {
|
|
462
|
+
ref_key: "dropAreaRef",
|
|
463
|
+
ref: dropAreaRef,
|
|
464
|
+
class: "droppable-area"
|
|
465
|
+
}, [
|
|
466
|
+
vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
|
|
467
|
+
], 512);
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
const Droppable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-28d651f3"]]);
|
|
472
|
+
const HvDraggable = withInstall(Draggable);
|
|
473
|
+
const HvDroppable = withInstall(Droppable);
|
|
474
|
+
exports2.HvDraggable = HvDraggable;
|
|
475
|
+
exports2.HvDroppable = HvDroppable;
|
|
476
|
+
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
477
|
+
});
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.draggable-area[data-v-34f8fce1] {
|
|
2
|
+
width: fit-content;
|
|
3
|
+
cursor: grab;
|
|
4
|
+
}
|
|
5
|
+
.draggable-area.disabled[data-v-34f8fce1] {
|
|
6
|
+
cursor: not-allowed;
|
|
7
|
+
}
|
|
8
|
+
.draggable-clone-item[data-v-34f8fce1] {
|
|
9
|
+
position: fixed;
|
|
10
|
+
top: 0;
|
|
11
|
+
left: 0;
|
|
12
|
+
z-index: 99999;
|
|
13
|
+
width: fit-content;
|
|
14
|
+
cursor: grabbing;
|
|
15
|
+
opacity: 0.8;
|
|
16
|
+
will-change: transform;
|
|
17
|
+
}.droppable-area[data-v-28d651f3] {
|
|
18
|
+
position: relative;
|
|
19
|
+
width: fit-content;
|
|
20
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DragAndDropDragType } from './manager';
|
|
2
|
+
declare const ImmediateEnumType: {
|
|
3
|
+
readonly LEFT: "left";
|
|
4
|
+
readonly RIGHT: "right";
|
|
5
|
+
readonly TOP: "top";
|
|
6
|
+
readonly BOTTOM: "bottom";
|
|
7
|
+
readonly ALL: "all";
|
|
8
|
+
};
|
|
9
|
+
type ImmediateType = (typeof ImmediateEnumType)[keyof typeof ImmediateEnumType];
|
|
10
|
+
type __VLS_Props = {
|
|
11
|
+
type: DragAndDropDragType;
|
|
12
|
+
immediate?: ImmediateType | Array<ImmediateType> | undefined;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
data?: any;
|
|
15
|
+
};
|
|
16
|
+
declare const dragItemRef: import("vue").Ref<HTMLElement | undefined, HTMLElement | undefined>;
|
|
17
|
+
/** 是否开始拖动 */
|
|
18
|
+
declare const isDragThis: import("vue").Ref<boolean, boolean>;
|
|
19
|
+
declare const cloneNodeStyle: import("vue").ComputedRef<{
|
|
20
|
+
transform: string;
|
|
21
|
+
}>;
|
|
22
|
+
declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
|
|
23
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {};
|
|
24
|
+
type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
|
|
25
|
+
default?: (props: typeof __VLS_1) => any;
|
|
26
|
+
} & {
|
|
27
|
+
'drag-item'?: (props: typeof __VLS_3) => any;
|
|
28
|
+
} & {
|
|
29
|
+
default?: (props: typeof __VLS_5) => any;
|
|
30
|
+
}>;
|
|
31
|
+
declare const __VLS_self: import("vue").DefineComponent<__VLS_Props, {
|
|
32
|
+
dragItemRef: typeof dragItemRef;
|
|
33
|
+
isDragThis: typeof isDragThis;
|
|
34
|
+
cloneNodeStyle: typeof cloneNodeStyle;
|
|
35
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
36
|
+
disabled: boolean;
|
|
37
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
38
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
39
|
+
disabled: boolean;
|
|
40
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
41
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
42
|
+
export default _default;
|
|
43
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
44
|
+
new (): {
|
|
45
|
+
$slots: S;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DragAndDropDragType, DragAndDropPoint } from './manager';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
acceptDragType: DragAndDropDragType | Array<DragAndDropDragType>;
|
|
4
|
+
};
|
|
5
|
+
declare const dropAreaRef: import("vue").Ref<HTMLElement | undefined, HTMLElement | undefined>;
|
|
6
|
+
declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
|
|
7
|
+
declare var __VLS_1: {};
|
|
8
|
+
type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
|
|
9
|
+
default?: (props: typeof __VLS_1) => any;
|
|
10
|
+
}>;
|
|
11
|
+
declare const __VLS_self: import("vue").DefineComponent<__VLS_Props, {
|
|
12
|
+
dropAreaRef: typeof dropAreaRef;
|
|
13
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
14
|
+
drop: (type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any;
|
|
15
|
+
move: (type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any;
|
|
16
|
+
enter: (type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any;
|
|
17
|
+
leave: (type: DragAndDropDragType, data: any) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
19
|
+
onDrop?: ((type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any) | undefined;
|
|
20
|
+
onMove?: ((type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any) | undefined;
|
|
21
|
+
onEnter?: ((type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any) | undefined;
|
|
22
|
+
onLeave?: ((type: DragAndDropDragType, data: any) => any) | undefined;
|
|
23
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
25
|
+
drop: (type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any;
|
|
26
|
+
move: (type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any;
|
|
27
|
+
enter: (type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any;
|
|
28
|
+
leave: (type: DragAndDropDragType, data: any) => any;
|
|
29
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
30
|
+
onDrop?: ((type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any) | undefined;
|
|
31
|
+
onMove?: ((type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any) | undefined;
|
|
32
|
+
onEnter?: ((type: DragAndDropDragType, point: DragAndDropPoint, data: any) => any) | undefined;
|
|
33
|
+
onLeave?: ((type: DragAndDropDragType, data: any) => any) | undefined;
|
|
34
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
35
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
36
|
+
export default _default;
|
|
37
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
38
|
+
new (): {
|
|
39
|
+
$slots: S;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SFCWithInstall } from '@havue/utils';
|
|
2
|
+
import Draggable from './Draggable.vue';
|
|
3
|
+
import Droppable from './Droppable.vue';
|
|
4
|
+
export declare const HvDraggable: SFCWithInstall<typeof Draggable>;
|
|
5
|
+
export declare const HvDroppable: SFCWithInstall<typeof Droppable>;
|
|
6
|
+
export type { DragAndDropPoint, DragAndDropDragType, DnDManager } from './manager';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { EventBus } from '@havue/shared';
|
|
2
|
+
export type DragAndDropPoint = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
};
|
|
6
|
+
export type DragAndDropDragType = string | number | symbol;
|
|
7
|
+
type Events = {
|
|
8
|
+
down: (p: DragAndDropPoint) => void;
|
|
9
|
+
'first-move': (p: DragAndDropPoint, e: MouseEvent | TouchEvent) => void;
|
|
10
|
+
start: (p: DragAndDropPoint) => void;
|
|
11
|
+
move: (params: {
|
|
12
|
+
type: DragAndDropDragType;
|
|
13
|
+
data: any;
|
|
14
|
+
point: DragAndDropPoint;
|
|
15
|
+
}) => void;
|
|
16
|
+
end: (params: {
|
|
17
|
+
type: DragAndDropDragType;
|
|
18
|
+
data: any;
|
|
19
|
+
point: DragAndDropPoint;
|
|
20
|
+
}) => void;
|
|
21
|
+
};
|
|
22
|
+
export declare class DnDManager extends EventBus<Events> {
|
|
23
|
+
/** 是否开始拖动 */
|
|
24
|
+
isDragStart: boolean;
|
|
25
|
+
/** 拖动元素类型 */
|
|
26
|
+
dragType: DragAndDropDragType | undefined;
|
|
27
|
+
/** Draggable传递的数据,
|
|
28
|
+
* 供Droppable使用
|
|
29
|
+
*/
|
|
30
|
+
dragData: any;
|
|
31
|
+
private isSendFirstMovePos;
|
|
32
|
+
/** 移动端长按一定时间才触发 onStart */
|
|
33
|
+
private touchStartPressTime;
|
|
34
|
+
/** touchstart事件触发的时的时间 */
|
|
35
|
+
private touchStartTime;
|
|
36
|
+
/** touchstart时间触发的位置 */
|
|
37
|
+
private touchStartPosition;
|
|
38
|
+
/** onMove最后位置 */
|
|
39
|
+
private lastMovePoint;
|
|
40
|
+
private emitTouchStartTimer;
|
|
41
|
+
private destroy;
|
|
42
|
+
constructor();
|
|
43
|
+
updateDargInfo(type: Exclude<DragAndDropDragType, undefined>, data: any): void;
|
|
44
|
+
/**
|
|
45
|
+
* 通知 Draggable 开始点击
|
|
46
|
+
* @param point
|
|
47
|
+
*/
|
|
48
|
+
private onDown;
|
|
49
|
+
/**
|
|
50
|
+
* 通知 Draggable 拖拽开始
|
|
51
|
+
* @param point
|
|
52
|
+
*/
|
|
53
|
+
private onFirstMove;
|
|
54
|
+
/**
|
|
55
|
+
* 通知 Draggable 拖拽开始
|
|
56
|
+
* @param point
|
|
57
|
+
*/
|
|
58
|
+
private onStart;
|
|
59
|
+
/**
|
|
60
|
+
* 通知 Draggable 移动
|
|
61
|
+
* @param point
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
64
|
+
private onMove;
|
|
65
|
+
/**
|
|
66
|
+
* 结束拖拽
|
|
67
|
+
* @returns
|
|
68
|
+
*/
|
|
69
|
+
private onEnd;
|
|
70
|
+
private onMouseDown;
|
|
71
|
+
private onMouseMove;
|
|
72
|
+
private onMouseUp;
|
|
73
|
+
private onTouchStart;
|
|
74
|
+
private onTouchMove;
|
|
75
|
+
private onTouchEnd;
|
|
76
|
+
private bindEventListener;
|
|
77
|
+
private removeEventListener;
|
|
78
|
+
destroyed(): void;
|
|
79
|
+
}
|
|
80
|
+
export declare const DnDManagerInstance: DnDManager;
|
|
81
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@havue/drag-and-drop",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Drag and drop components for Vue3",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"havue",
|
|
7
|
+
"components",
|
|
8
|
+
"drag",
|
|
9
|
+
"drop",
|
|
10
|
+
"vue3"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://happypedestrian.github.io/havue/guide/",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/HappyPedestrian/havue.git"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@havue/shared": "^1.0.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"vue": "^3.3.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"vue": "^3.3.0"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"registry": "https://registry.npmjs.org/"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"main": "./dist/drag-and-drop.umd.js",
|
|
35
|
+
"module": "./dist/drag-and-drop.mjs",
|
|
36
|
+
"types": "./dist/types/src/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"require": "./dist/drag-and-drop.umd.js",
|
|
40
|
+
"import": "./dist/drag-and-drop.mjs",
|
|
41
|
+
"types": "./dist/types/src/index.d.ts"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "vite build --mode package"
|
|
46
|
+
}
|
|
47
|
+
}
|