quasar-ui-danx 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +10 -3
- package/src/components/DragAndDrop/HandleDraggable.vue +53 -0
- package/src/components/DragAndDrop/Icons/DragHandleDotsIcon.svg +5 -0
- package/src/components/DragAndDrop/Icons/DragHandleIcon.svg +5 -0
- package/src/components/DragAndDrop/Icons/index.ts +2 -0
- package/src/components/DragAndDrop/ListItemDraggable.vue +52 -0
- package/src/components/DragAndDrop/dragAndDrop.ts +225 -0
- package/src/components/DragAndDrop/index.ts +5 -0
- package/src/components/DragAndDrop/listDragAndDrop.ts +237 -0
- package/src/index.common.js +0 -1
- package/src/index.js +1 -0
- package/src/index.sass +0 -4
- package/src/vue-plugin.js +8 -12
- package/tsconfig.json +40 -0
- package/types/svg.d.ts +4 -0
- package/types/vue-shims.d.ts +4 -0
- package/src/components/Component.js +0 -13
- package/src/components/Component.sass +0 -2
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "quasar-ui-danx",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.5",
|
4
4
|
"author": "Dan <dan@flytedesk.com>",
|
5
5
|
"description": "DanX Vue / Quasar component library",
|
6
6
|
"license": "MIT",
|
@@ -30,7 +30,7 @@
|
|
30
30
|
"@rollup/plugin-json": "^4.0.0",
|
31
31
|
"@rollup/plugin-node-resolve": "^11.2.1",
|
32
32
|
"@rollup/plugin-replace": "^2.4.2",
|
33
|
-
"autoprefixer": "^10.
|
33
|
+
"autoprefixer": "^10.4.17",
|
34
34
|
"chalk": "^4.1.0",
|
35
35
|
"core-js": "^3.0.0",
|
36
36
|
"cssnano": "^4.1.10",
|
@@ -38,10 +38,14 @@
|
|
38
38
|
"open": "^7.3.0",
|
39
39
|
"postcss": "^8.1.9",
|
40
40
|
"quasar": "^2.0.0",
|
41
|
+
"raw-loader": "^4.0.2",
|
41
42
|
"rimraf": "^3.0.0",
|
42
43
|
"rollup": "^2.45.0",
|
43
44
|
"rtlcss": "^2.6.1",
|
44
45
|
"sass": "^1.33.0",
|
46
|
+
"svg-inline-loader": "^0.8.2",
|
47
|
+
"ts-loader": "^9.5.1",
|
48
|
+
"typescript": "^5.3.3",
|
45
49
|
"uglify-js": "^3.13.3",
|
46
50
|
"vue": "^3.0.0",
|
47
51
|
"vue-router": "^4.0.0",
|
@@ -56,5 +60,8 @@
|
|
56
60
|
"last 4 ChromeAndroid versions",
|
57
61
|
"last 4 FirefoxAndroid versions",
|
58
62
|
"last 4 iOS versions"
|
59
|
-
]
|
63
|
+
],
|
64
|
+
"dependencies": {
|
65
|
+
"@vueuse/core": "^10.7.2"
|
66
|
+
}
|
60
67
|
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
:class="{
|
4
|
+
'cursor-ew-resize': direction === 'horizontal',
|
5
|
+
'cursor-ns-resize': direction === 'vertical',
|
6
|
+
}"
|
7
|
+
class="flex justify-center items-center w-full h-full"
|
8
|
+
draggable="true"
|
9
|
+
@dragstart="dragAndDrop.dragStart"
|
10
|
+
@dragend="dragAndDrop.dragEnd"
|
11
|
+
>
|
12
|
+
<slot />
|
13
|
+
</div>
|
14
|
+
</template>
|
15
|
+
<script setup>
|
16
|
+
import { useDebounceFn } from "@vueuse/core";
|
17
|
+
import { DragAndDrop } from "./dragAndDrop";
|
18
|
+
|
19
|
+
const emit = defineEmits(["start", "end", "resize"]);
|
20
|
+
const props = defineProps({
|
21
|
+
initialValue: {
|
22
|
+
type: Number,
|
23
|
+
default: null,
|
24
|
+
},
|
25
|
+
dropZone: {
|
26
|
+
type: [Function, String],
|
27
|
+
required: true,
|
28
|
+
},
|
29
|
+
direction: {
|
30
|
+
type: String,
|
31
|
+
default: "horizontal",
|
32
|
+
validator: (value) => ["vertical", "horizontal"].includes(value),
|
33
|
+
},
|
34
|
+
});
|
35
|
+
|
36
|
+
const dragAndDrop = new DragAndDrop()
|
37
|
+
.setDropZone(props.dropZone)
|
38
|
+
.setOptions({
|
39
|
+
showPlaceholder: true,
|
40
|
+
direction: props.direction,
|
41
|
+
hideDragImage: true,
|
42
|
+
})
|
43
|
+
.onDragging(useDebounceFn(() => {
|
44
|
+
emit("resize", {
|
45
|
+
distance: dragAndDrop.getDistance(),
|
46
|
+
percent: dragAndDrop.getPercentChange(),
|
47
|
+
startDropZoneSize: dragAndDrop.startSize,
|
48
|
+
dropZoneSize: dragAndDrop.getDropZoneSize(),
|
49
|
+
});
|
50
|
+
}, 20, { maxWait: 30 }))
|
51
|
+
.onStart(() => emit("start"))
|
52
|
+
.onEnd(() => emit("end"));
|
53
|
+
</script>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path
|
3
|
+
d="M7.5 16C7.08333 16 6.72933 15.854 6.438 15.562C6.146 15.2707 6 14.9167 6 14.5C6 14.0833 6.146 13.7293 6.438 13.438C6.72933 13.146 7.08333 13 7.5 13C7.91667 13 8.27067 13.146 8.562 13.438C8.854 13.7293 9 14.0833 9 14.5C9 14.9167 8.854 15.2707 8.562 15.562C8.27067 15.854 7.91667 16 7.5 16ZM12.5 16C12.0833 16 11.7293 15.854 11.438 15.562C11.146 15.2707 11 14.9167 11 14.5C11 14.0833 11.146 13.7293 11.438 13.438C11.7293 13.146 12.0833 13 12.5 13C12.9167 13 13.2707 13.146 13.562 13.438C13.854 13.7293 14 14.0833 14 14.5C14 14.9167 13.854 15.2707 13.562 15.562C13.2707 15.854 12.9167 16 12.5 16ZM7.5 11.5C7.08333 11.5 6.72933 11.354 6.438 11.062C6.146 10.7707 6 10.4167 6 10C6 9.58333 6.146 9.22933 6.438 8.938C6.72933 8.646 7.08333 8.5 7.5 8.5C7.91667 8.5 8.27067 8.646 8.562 8.938C8.854 9.22933 9 9.58333 9 10C9 10.4167 8.854 10.7707 8.562 11.062C8.27067 11.354 7.91667 11.5 7.5 11.5ZM12.5 11.5C12.0833 11.5 11.7293 11.354 11.438 11.062C11.146 10.7707 11 10.4167 11 10C11 9.58333 11.146 9.22933 11.438 8.938C11.7293 8.646 12.0833 8.5 12.5 8.5C12.9167 8.5 13.2707 8.646 13.562 8.938C13.854 9.22933 14 9.58333 14 10C14 10.4167 13.854 10.7707 13.562 11.062C13.2707 11.354 12.9167 11.5 12.5 11.5ZM7.5 7C7.08333 7 6.72933 6.854 6.438 6.562C6.146 6.27067 6 5.91667 6 5.5C6 5.08333 6.146 4.72933 6.438 4.438C6.72933 4.146 7.08333 4 7.5 4C7.91667 4 8.27067 4.146 8.562 4.438C8.854 4.72933 9 5.08333 9 5.5C9 5.91667 8.854 6.27067 8.562 6.562C8.27067 6.854 7.91667 7 7.5 7ZM12.5 7C12.0833 7 11.7293 6.854 11.438 6.562C11.146 6.27067 11 5.91667 11 5.5C11 5.08333 11.146 4.72933 11.438 4.438C11.7293 4.146 12.0833 4 12.5 4C12.9167 4 13.2707 4.146 13.562 4.438C13.854 4.72933 14 5.08333 14 5.5C14 5.91667 13.854 6.27067 13.562 6.562C13.2707 6.854 12.9167 7 12.5 7Z"
|
4
|
+
fill="currentColor"/>
|
5
|
+
</svg>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<svg viewBox="0 0 7 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path
|
3
|
+
d="M0.5 1L0.5 15C0.5 15.55 0.95 16 1.5 16C2.05 16 2.5 15.55 2.5 15L2.5 1C2.5 0.45 2.05 0 1.5 0C0.95 0 0.5 0.45 0.5 1ZM6.5 15L6.5 1C6.5 0.45 6.05 0 5.5 0C4.95 0 4.5 0.45 4.5 1L4.5 15C4.5 15.55 4.95 16 5.5 16C6.05 16 6.5 15.55 6.5 15Z"
|
4
|
+
fill="currentColor"/>
|
5
|
+
</svg>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
class="cursor-move"
|
4
|
+
draggable="true"
|
5
|
+
@dragstart="dragAndDrop.dragStart"
|
6
|
+
@dragend="dragAndDrop.dragEnd"
|
7
|
+
>
|
8
|
+
<div class="flex items-center">
|
9
|
+
<div v-if="showHandle">
|
10
|
+
<img :src="DragHandleIcon" class="w-4 h-4" alt="drag-handle" />
|
11
|
+
</div>
|
12
|
+
<div class="flex-grow">
|
13
|
+
<slot />
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</template>
|
18
|
+
<script setup>
|
19
|
+
import { HandleDraggableDotsIcon as DragHandleIcon } from "./Icons";
|
20
|
+
import { ListDragAndDrop } from "./listDragAndDrop";
|
21
|
+
|
22
|
+
const emit = defineEmits(["position", "update:list-items"]);
|
23
|
+
const props = defineProps({
|
24
|
+
dropZone: {
|
25
|
+
type: [Function, String],
|
26
|
+
required: true,
|
27
|
+
},
|
28
|
+
direction: {
|
29
|
+
type: String,
|
30
|
+
default: "vertical",
|
31
|
+
validator: (value) => ["vertical", "horizontal"].includes(value),
|
32
|
+
},
|
33
|
+
showHandle: Boolean,
|
34
|
+
listItems: {
|
35
|
+
type: Array,
|
36
|
+
default: null,
|
37
|
+
}
|
38
|
+
});
|
39
|
+
|
40
|
+
const dragAndDrop = new ListDragAndDrop()
|
41
|
+
.setDropZone(props.dropZone)
|
42
|
+
.setOptions({ showPlaceholder: true, direction: props.direction })
|
43
|
+
.onPositionChange((newPosition, oldPosition) => {
|
44
|
+
emit("position", newPosition);
|
45
|
+
|
46
|
+
if (props.listItems) {
|
47
|
+
const items = [...props.listItems];
|
48
|
+
items.splice(newPosition, 0, items.splice(oldPosition, 1)[0]);
|
49
|
+
emit("update:list-items", items);
|
50
|
+
}
|
51
|
+
});
|
52
|
+
</script>
|
@@ -0,0 +1,225 @@
|
|
1
|
+
/**
|
2
|
+
* Drag and Drop basic functionality for dragging elements and firing events on drag start, drag over and drag end
|
3
|
+
*/
|
4
|
+
export class DragAndDrop {
|
5
|
+
options: {
|
6
|
+
direction?: string,
|
7
|
+
hideDragImage?: boolean,
|
8
|
+
showPlaceholder?: boolean,
|
9
|
+
} = { direction: "vertical", hideDragImage: false };
|
10
|
+
|
11
|
+
// State
|
12
|
+
startY = 0;
|
13
|
+
startX = 0;
|
14
|
+
startSize = 0;
|
15
|
+
cursorY = 0;
|
16
|
+
cursorX = 0;
|
17
|
+
onStartCb = null;
|
18
|
+
onEndCb = null;
|
19
|
+
onDropCb = null;
|
20
|
+
onDraggingCb = null;
|
21
|
+
dropZoneResolver = null;
|
22
|
+
currentDropZone = null;
|
23
|
+
draggableData = null;
|
24
|
+
// Used to abort dragging event listeners on the element
|
25
|
+
abortController = null;
|
26
|
+
|
27
|
+
constructor(options = {}) {
|
28
|
+
// Options
|
29
|
+
options = {
|
30
|
+
direction: "vertical",
|
31
|
+
hideDragImage: false,
|
32
|
+
...options,
|
33
|
+
};
|
34
|
+
|
35
|
+
this.setOptions(options);
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Set the options for the drag and drop instance
|
40
|
+
* @param options
|
41
|
+
* @returns {DragAndDrop}
|
42
|
+
*/
|
43
|
+
setOptions(options = {}) {
|
44
|
+
this.options = { ...this.options, ...options };
|
45
|
+
return this;
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Returns if the list is stacked vertically or horizontally
|
50
|
+
* @returns {boolean}
|
51
|
+
*/
|
52
|
+
isVertical() {
|
53
|
+
return this.options.direction === "vertical";
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Set the target drop zone for draggable elements
|
58
|
+
* @param dropZone
|
59
|
+
* @returns {DragAndDrop}
|
60
|
+
*/
|
61
|
+
setDropZone(dropZone) {
|
62
|
+
this.dropZoneResolver = dropZone;
|
63
|
+
return this;
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Callback that fires when an element has started dragging
|
68
|
+
* @param cb
|
69
|
+
* @returns {DragAndDrop}
|
70
|
+
*/
|
71
|
+
onStart(cb) {
|
72
|
+
this.onStartCb = cb;
|
73
|
+
return this;
|
74
|
+
}
|
75
|
+
|
76
|
+
/**
|
77
|
+
* Callback that fires when an element has stopped dragging
|
78
|
+
* @param cb
|
79
|
+
* @returns {DragAndDrop}
|
80
|
+
*/
|
81
|
+
onEnd(cb) {
|
82
|
+
this.onEndCb = cb;
|
83
|
+
return this;
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Callback that fires when the dragging element is moved
|
88
|
+
* @param cb
|
89
|
+
* @returns {DragAndDrop}
|
90
|
+
*/
|
91
|
+
onDragging(cb) {
|
92
|
+
this.onDraggingCb = cb;
|
93
|
+
return this;
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Callback that fires when the dragging element has been dropped
|
98
|
+
* @param cb
|
99
|
+
* @returns {DragAndDrop}
|
100
|
+
*/
|
101
|
+
onDrop(cb) {
|
102
|
+
this.onDropCb = cb;
|
103
|
+
return this;
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Start listening for drag events and prepare an element for drag/drop
|
108
|
+
* @param e
|
109
|
+
* @param data
|
110
|
+
*/
|
111
|
+
dragStart(e, data) {
|
112
|
+
this.currentDropZone = this.getDropZone(e);
|
113
|
+
|
114
|
+
if (this.currentDropZone) {
|
115
|
+
this.startY = e.clientY;
|
116
|
+
this.startX = e.clientX;
|
117
|
+
this.startSize = this.getDropZoneSize();
|
118
|
+
e.dataTransfer.effectAllowed = "move";
|
119
|
+
e.dataTransfer.dropEffect = "move";
|
120
|
+
this.draggableData = data;
|
121
|
+
this.abortController = new AbortController();
|
122
|
+
const options = { signal: this.abortController.signal };
|
123
|
+
document.addEventListener("dragenter", (e) => this.dragEnter(e), options);
|
124
|
+
document.addEventListener("dragover", (e) => this.dragOver(e), options);
|
125
|
+
document.addEventListener("drop", (e) => this.drop(e), options);
|
126
|
+
this.onStartCb && this.onStartCb(e);
|
127
|
+
|
128
|
+
if (this.options.hideDragImage) {
|
129
|
+
e.dataTransfer.setDragImage(new Image(), 0, 0);
|
130
|
+
}
|
131
|
+
} else {
|
132
|
+
console.error("Drop zone was not found", e, data);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
/**
|
137
|
+
* Clean up event listeners after dragging is done
|
138
|
+
*/
|
139
|
+
dragEnd(e) {
|
140
|
+
this.currentDropZone = null;
|
141
|
+
this.abortController.abort();
|
142
|
+
this.draggableData = null;
|
143
|
+
this.onEndCb && this.onEndCb(e);
|
144
|
+
}
|
145
|
+
|
146
|
+
/**
|
147
|
+
* The dragging element has entered a new target
|
148
|
+
* @param e
|
149
|
+
*/
|
150
|
+
dragEnter(e) {
|
151
|
+
e.preventDefault();
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
* The dragging element is moving
|
156
|
+
* @param e
|
157
|
+
*/
|
158
|
+
dragOver(e) {
|
159
|
+
e.preventDefault();
|
160
|
+
this.cursorY = e.clientY;
|
161
|
+
this.cursorX = e.clientX;
|
162
|
+
this.onDraggingCb && this.onDraggingCb(e);
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* Handle dropping the element into its correct position
|
167
|
+
* @param e
|
168
|
+
*/
|
169
|
+
drop(e) {
|
170
|
+
e.dataTransfer.dropEffect = "move";
|
171
|
+
e.preventDefault();
|
172
|
+
this.onDropCb && this.onDropCb(e, this.draggableData);
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Returns the drop zone if the current target element is or is inside the drop zone
|
177
|
+
* @param e
|
178
|
+
* @returns {HTMLElement|null}
|
179
|
+
*/
|
180
|
+
getDropZone(e) {
|
181
|
+
if (typeof this.dropZoneResolver === "string") {
|
182
|
+
let target = e.target;
|
183
|
+
while (target) {
|
184
|
+
if (target.dataset?.dropZone === this.dropZoneResolver) {
|
185
|
+
return target;
|
186
|
+
}
|
187
|
+
target = target.parentNode;
|
188
|
+
}
|
189
|
+
return null;
|
190
|
+
} else if (typeof this.dropZoneResolver === "function") {
|
191
|
+
return this.dropZoneResolver(e);
|
192
|
+
} else {
|
193
|
+
return document.body;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Returns the distance between the start and current cursor position
|
199
|
+
* @returns {number}
|
200
|
+
*/
|
201
|
+
getDistance() {
|
202
|
+
return this.isVertical()
|
203
|
+
? this.cursorY - this.startY
|
204
|
+
: this.cursorX - this.startX;
|
205
|
+
}
|
206
|
+
|
207
|
+
/**
|
208
|
+
* Returns the size of the drop zone
|
209
|
+
*/
|
210
|
+
getDropZoneSize() {
|
211
|
+
return this.isVertical()
|
212
|
+
? this.currentDropZone?.offsetHeight
|
213
|
+
: this.currentDropZone?.offsetWidth;
|
214
|
+
}
|
215
|
+
|
216
|
+
/**
|
217
|
+
* Returns the percent change between the start and current cursor position relative to the drop zone size
|
218
|
+
*
|
219
|
+
* @returns {number}
|
220
|
+
*/
|
221
|
+
getPercentChange() {
|
222
|
+
const distance = this.getDistance();
|
223
|
+
return (distance / this.startSize) * 100;
|
224
|
+
}
|
225
|
+
}
|
@@ -0,0 +1,237 @@
|
|
1
|
+
import { DragAndDrop } from "./dragAndDrop";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* ListDragAndDrop supports dragging elements in a list to new positions in the same list.
|
5
|
+
* A placeholder is rendered to show the position the list item will be dropped.
|
6
|
+
*
|
7
|
+
* @class
|
8
|
+
*/
|
9
|
+
export class ListDragAndDrop extends DragAndDrop {
|
10
|
+
listPosition = 0;
|
11
|
+
cursorPosition = 0;
|
12
|
+
initialPosition = 0;
|
13
|
+
onPositionChangeCb = null;
|
14
|
+
onDragPositionChangeCb = null;
|
15
|
+
placeholder = null;
|
16
|
+
|
17
|
+
constructor(options = {}) {
|
18
|
+
super({
|
19
|
+
showPlaceholder: true,
|
20
|
+
...options,
|
21
|
+
});
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Callback that fires after dragging has ended and the list position has changed from the original
|
26
|
+
* @param cb
|
27
|
+
* @returns {ListDragAndDrop}
|
28
|
+
*/
|
29
|
+
onPositionChange(cb) {
|
30
|
+
this.onPositionChangeCb = cb;
|
31
|
+
return this;
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Callback that fires while dragging the element when the cursor's position has changed in the list
|
36
|
+
* @param cb
|
37
|
+
* @returns {ListDragAndDrop}
|
38
|
+
*/
|
39
|
+
onDragPositionChange(cb) {
|
40
|
+
this.onDragPositionChangeCb = cb;
|
41
|
+
return this;
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Start listening for drag events and prepare an element for drag/drop
|
46
|
+
* @param e
|
47
|
+
* @param data
|
48
|
+
*/
|
49
|
+
dragStart(e, data) {
|
50
|
+
super.dragStart(e, data);
|
51
|
+
|
52
|
+
if (this.currentDropZone) {
|
53
|
+
this.listPosition = this.getListPosition(e.target);
|
54
|
+
this.initialPosition = this.listPosition;
|
55
|
+
this.updateScrollPosition();
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* When dragging has ended, check for list position changes and fire the onPositionChange callback if it has
|
61
|
+
*/
|
62
|
+
dragEnd(e) {
|
63
|
+
const draggableData = this.draggableData;
|
64
|
+
this.placeholder.remove();
|
65
|
+
super.dragEnd(e);
|
66
|
+
|
67
|
+
// If our list position has changed, trigger the drop callback
|
68
|
+
if (this.listPosition !== this.initialPosition) {
|
69
|
+
this.onPositionChangeCb &&
|
70
|
+
this.onPositionChangeCb(this.listPosition, this.initialPosition, draggableData);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* The dragging element is moving
|
76
|
+
* @param e
|
77
|
+
*/
|
78
|
+
dragOver(e) {
|
79
|
+
super.dragOver(e);
|
80
|
+
this.updateListPosition(e);
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Notify if the targeted position of the cursor is different from the current position
|
85
|
+
* @param e
|
86
|
+
*/
|
87
|
+
updateListPosition(e) {
|
88
|
+
const prevPosition = this.listPosition;
|
89
|
+
const newPosition = this.getListPositionOfPoint({
|
90
|
+
x: e.clientX,
|
91
|
+
y: e.clientY,
|
92
|
+
});
|
93
|
+
|
94
|
+
// If the cursor position has changed, we should update the rendering and see if our actual list position has
|
95
|
+
// changed
|
96
|
+
if (this.cursorPosition !== newPosition) {
|
97
|
+
this.cursorPosition = newPosition;
|
98
|
+
this.listPosition =
|
99
|
+
this.initialPosition < this.cursorPosition
|
100
|
+
? this.cursorPosition - 1
|
101
|
+
: this.cursorPosition;
|
102
|
+
if (this.options.showPlaceholder) {
|
103
|
+
this.renderPlaceholder();
|
104
|
+
}
|
105
|
+
|
106
|
+
// The position has changed, trigger the callback
|
107
|
+
if (this.listPosition !== prevPosition) {
|
108
|
+
this.onDragPositionChangeCb &&
|
109
|
+
this.onDragPositionChangeCb(this.listPosition, this.draggableData);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Find the numeric position of the element in the children of the list
|
116
|
+
* @returns {Number|null}
|
117
|
+
* @param item
|
118
|
+
*/
|
119
|
+
getListPosition(item) {
|
120
|
+
let index = 0;
|
121
|
+
for (const child of this.getChildren()) {
|
122
|
+
if (child === item) {
|
123
|
+
return index;
|
124
|
+
}
|
125
|
+
index++;
|
126
|
+
}
|
127
|
+
|
128
|
+
return null;
|
129
|
+
}
|
130
|
+
|
131
|
+
/**
|
132
|
+
* Get all the children of the current drop zone, excluding the placeholder
|
133
|
+
* @returns {*}
|
134
|
+
*/
|
135
|
+
getChildren() {
|
136
|
+
return [...this.currentDropZone.children].filter(
|
137
|
+
(c) => c.className.match(/drag-placeholder/) === null
|
138
|
+
);
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Find the element at the current cursor position in the given drop zone
|
143
|
+
* @param point
|
144
|
+
* @returns {null}
|
145
|
+
*/
|
146
|
+
getListPositionOfPoint(point) {
|
147
|
+
let index = 0;
|
148
|
+
const children = this.getChildren();
|
149
|
+
|
150
|
+
while (index < children.length) {
|
151
|
+
const rect = children[index].getBoundingClientRect();
|
152
|
+
if (this.isVertical()) {
|
153
|
+
if (point.y < rect.top + rect.height / 2) {
|
154
|
+
break;
|
155
|
+
}
|
156
|
+
} else {
|
157
|
+
if (point.x < rect.left + rect.width / 2) {
|
158
|
+
break;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
index++;
|
162
|
+
}
|
163
|
+
|
164
|
+
return index;
|
165
|
+
}
|
166
|
+
|
167
|
+
/**
|
168
|
+
* Updates the scroll position while dragging an element so a user can navigate a longer list while dragging
|
169
|
+
*/
|
170
|
+
updateScrollPosition() {
|
171
|
+
if (this.currentDropZone) {
|
172
|
+
const rect = this.currentDropZone.getBoundingClientRect();
|
173
|
+
const threshold = 100;
|
174
|
+
let velocity = 0;
|
175
|
+
const velocityFn = (x) => x * 5;
|
176
|
+
const cursorPos = this.isVertical() ? this.cursorY : this.cursorX;
|
177
|
+
const rectStart = this.isVertical() ? rect.top : rect.left;
|
178
|
+
const rectEnd = this.isVertical() ? rect.bottom : rect.right;
|
179
|
+
const beforeDiff = rectStart + threshold - cursorPos;
|
180
|
+
const afterDiff = cursorPos - (rectEnd - threshold);
|
181
|
+
|
182
|
+
if (beforeDiff > 0) {
|
183
|
+
velocity = -velocityFn(beforeDiff);
|
184
|
+
} else if (afterDiff > 0) {
|
185
|
+
velocity = velocityFn(afterDiff);
|
186
|
+
}
|
187
|
+
|
188
|
+
if (velocity) {
|
189
|
+
if (this.isVertical()) {
|
190
|
+
this.currentDropZone.scrollTo({
|
191
|
+
top: this.currentDropZone.scrollTop + velocity,
|
192
|
+
behavior: "smooth",
|
193
|
+
});
|
194
|
+
} else {
|
195
|
+
this.currentDropZone.scrollTo({
|
196
|
+
left: this.currentDropZone.scrollLeft + velocity,
|
197
|
+
behavior: "smooth",
|
198
|
+
});
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
setTimeout(() => this.updateScrollPosition(), 500);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
/**
|
207
|
+
* Render a placeholder element at the given position (in between the elements)
|
208
|
+
*/
|
209
|
+
renderPlaceholder() {
|
210
|
+
if (!this.placeholder) {
|
211
|
+
this.placeholder = document.createElement("div");
|
212
|
+
this.placeholder.classList.add("drag-placeholder");
|
213
|
+
}
|
214
|
+
|
215
|
+
// Make sure the placeholder is oriented correctly
|
216
|
+
if (this.isVertical()) {
|
217
|
+
this.placeholder.classList.add("direction-vertical");
|
218
|
+
this.placeholder.classList.remove("direction-horizontal");
|
219
|
+
this.placeholder.style.height = undefined;
|
220
|
+
} else {
|
221
|
+
this.placeholder.classList.add("direction-horizontal");
|
222
|
+
this.placeholder.classList.remove("direction-vertical");
|
223
|
+
this.placeholder.style.height =
|
224
|
+
this.currentDropZone.getBoundingClientRect().height + "px";
|
225
|
+
}
|
226
|
+
|
227
|
+
const children = this.getChildren();
|
228
|
+
if (this.cursorPosition < children.length) {
|
229
|
+
this.currentDropZone.insertBefore(
|
230
|
+
this.placeholder,
|
231
|
+
children[this.cursorPosition]
|
232
|
+
);
|
233
|
+
} else {
|
234
|
+
this.currentDropZone.appendChild(this.placeholder);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
}
|
package/src/index.common.js
CHANGED
package/src/index.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./vue-plugin";
|
package/src/index.sass
CHANGED
package/src/vue-plugin.js
CHANGED
@@ -1,17 +1,13 @@
|
|
1
|
-
|
2
|
-
export * from './
|
1
|
+
export * from "./helpers";
|
2
|
+
export * from './components/DragAndDrop';
|
3
3
|
|
4
|
+
const version = __UI_VERSION__;
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
function install (app) {
|
8
|
-
app.component(Component.name, Component)
|
9
|
-
|
6
|
+
function install(app) {
|
7
|
+
console.log('Installing Danx UI... Nothing to do really.');
|
10
8
|
}
|
11
9
|
|
12
10
|
export {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
install
|
17
|
-
}
|
11
|
+
version,
|
12
|
+
install,
|
13
|
+
};
|
package/tsconfig.json
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "es5",
|
4
|
+
// Specify ECMAScript target version
|
5
|
+
"module": "esnext",
|
6
|
+
// Specify module code generation
|
7
|
+
"strict": false,
|
8
|
+
// Enable all strict type-checking options
|
9
|
+
"esModuleInterop": true,
|
10
|
+
// Enables compatibility with Babel-imported CommonJS modules
|
11
|
+
"moduleResolution": "node",
|
12
|
+
// Resolve modules using Node.js style
|
13
|
+
"allowSyntheticDefaultImports": true,
|
14
|
+
// Allow default imports from modules with no default export
|
15
|
+
"experimentalDecorators": true,
|
16
|
+
// Enables experimental support for decorators
|
17
|
+
"skipLibCheck": true,
|
18
|
+
// Skip type checking of all declaration files (*.d.ts)
|
19
|
+
"forceConsistentCasingInFileNames": true,
|
20
|
+
// Disallow inconsistently-cased references to the same file.
|
21
|
+
"baseUrl": "./",
|
22
|
+
// Base directory to resolve non-relative module names
|
23
|
+
"paths": {
|
24
|
+
"@/*": [
|
25
|
+
"src/*"
|
26
|
+
]
|
27
|
+
// Path alias to resolve imports (adjust as needed)
|
28
|
+
}
|
29
|
+
},
|
30
|
+
"include": [
|
31
|
+
"src/**/*.ts",
|
32
|
+
"src/**/*.vue",
|
33
|
+
"src/**/*.svg",
|
34
|
+
"types/**/*.d.ts"
|
35
|
+
],
|
36
|
+
"exclude": [
|
37
|
+
"node_modules"
|
38
|
+
// Exclude the node_modules directory
|
39
|
+
]
|
40
|
+
}
|
package/types/svg.d.ts
ADDED