@satriobagasp/vue-slicksort 1.0.1
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/LICENSE +24 -0
- package/README.md +461 -0
- package/dist/vue-slicksort.cjs.js +1284 -0
- package/dist/vue-slicksort.esm.js +1273 -0
- package/dist/vue-slicksort.min.js +2 -0
- package/dist/vue-slicksort.min.js.map +1 -0
- package/dist/vue-slicksort.umd.js +1288 -0
- package/index.d.ts +16 -0
- package/package.json +112 -0
|
@@ -0,0 +1,1288 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.VueSlicksort = {}, global.vue));
|
|
5
|
+
})(this, (function (exports, vue) { 'use strict';
|
|
6
|
+
|
|
7
|
+
// Export Sortable Element Component Mixin
|
|
8
|
+
const ElementMixin = vue.defineComponent({
|
|
9
|
+
inject: ['manager'],
|
|
10
|
+
props: {
|
|
11
|
+
index: {
|
|
12
|
+
type: Number,
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
disabled: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: false,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
data() {
|
|
21
|
+
return {};
|
|
22
|
+
},
|
|
23
|
+
watch: {
|
|
24
|
+
index(newIndex) {
|
|
25
|
+
if (this.$el && this.$el.sortableInfo) {
|
|
26
|
+
this.$el.sortableInfo.index = newIndex;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
disabled(isDisabled) {
|
|
30
|
+
if (isDisabled) {
|
|
31
|
+
this.removeDraggable();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.setDraggable(this.index);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
mounted() {
|
|
39
|
+
const { disabled, index } = this.$props;
|
|
40
|
+
if (!disabled) {
|
|
41
|
+
this.setDraggable(index);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
beforeUnmount() {
|
|
45
|
+
if (!this.disabled)
|
|
46
|
+
this.removeDraggable();
|
|
47
|
+
},
|
|
48
|
+
methods: {
|
|
49
|
+
setDraggable(index) {
|
|
50
|
+
const node = this.$el;
|
|
51
|
+
node.sortableInfo = {
|
|
52
|
+
index,
|
|
53
|
+
manager: this.manager,
|
|
54
|
+
};
|
|
55
|
+
this.ref = { node };
|
|
56
|
+
this.manager.add(this.ref);
|
|
57
|
+
},
|
|
58
|
+
removeDraggable() {
|
|
59
|
+
this.manager.remove(this.ref);
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
class Manager {
|
|
65
|
+
constructor() {
|
|
66
|
+
this.refs = [];
|
|
67
|
+
this.active = null;
|
|
68
|
+
}
|
|
69
|
+
add(ref) {
|
|
70
|
+
if (!this.refs) {
|
|
71
|
+
this.refs = [];
|
|
72
|
+
}
|
|
73
|
+
this.refs.push(ref);
|
|
74
|
+
}
|
|
75
|
+
remove(ref) {
|
|
76
|
+
const index = this.getIndex(ref);
|
|
77
|
+
if (index !== -1) {
|
|
78
|
+
this.refs.splice(index, 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
isActive() {
|
|
82
|
+
return !!this.active;
|
|
83
|
+
}
|
|
84
|
+
getActive() {
|
|
85
|
+
return this.refs.find(({ node }) => { var _a, _b; return ((_a = node === null || node === void 0 ? void 0 : node.sortableInfo) === null || _a === void 0 ? void 0 : _a.index) == ((_b = this === null || this === void 0 ? void 0 : this.active) === null || _b === void 0 ? void 0 : _b.index); }) || null;
|
|
86
|
+
}
|
|
87
|
+
getIndex(ref) {
|
|
88
|
+
return this.refs.indexOf(ref);
|
|
89
|
+
}
|
|
90
|
+
getRefs() {
|
|
91
|
+
return this.refs;
|
|
92
|
+
}
|
|
93
|
+
getOrderedRefs() {
|
|
94
|
+
return this.refs.sort((a, b) => {
|
|
95
|
+
return a.node.sortableInfo.index - b.node.sortableInfo.index;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const isTouch = (e) => {
|
|
101
|
+
return e.touches != null;
|
|
102
|
+
};
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
104
|
+
function hasOwnProperty(obj, prop) {
|
|
105
|
+
return !!obj && Object.prototype.hasOwnProperty.call(obj, prop);
|
|
106
|
+
}
|
|
107
|
+
function arrayMove(arr, previousIndex, newIndex) {
|
|
108
|
+
const array = arr.slice(0);
|
|
109
|
+
if (newIndex >= array.length) {
|
|
110
|
+
let k = newIndex - array.length;
|
|
111
|
+
while (k-- + 1) {
|
|
112
|
+
array.push(undefined);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
array.splice(newIndex, 0, array.splice(previousIndex, 1)[0]);
|
|
116
|
+
return array;
|
|
117
|
+
}
|
|
118
|
+
function arrayRemove(arr, previousIndex) {
|
|
119
|
+
const array = arr.slice(0);
|
|
120
|
+
if (previousIndex >= array.length)
|
|
121
|
+
return array;
|
|
122
|
+
array.splice(previousIndex, 1);
|
|
123
|
+
return array;
|
|
124
|
+
}
|
|
125
|
+
function arrayInsert(arr, newIndex, value) {
|
|
126
|
+
const array = arr.slice(0);
|
|
127
|
+
if (newIndex === array.length) {
|
|
128
|
+
array.push(value);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
array.splice(newIndex, 0, value);
|
|
132
|
+
}
|
|
133
|
+
return array;
|
|
134
|
+
}
|
|
135
|
+
const events = {
|
|
136
|
+
start: ['touchstart', 'mousedown'],
|
|
137
|
+
move: ['touchmove', 'mousemove'],
|
|
138
|
+
end: ['touchend', 'mouseup'],
|
|
139
|
+
cancel: ['touchcancel', 'keyup'],
|
|
140
|
+
};
|
|
141
|
+
function closest(el, fn) {
|
|
142
|
+
while (el) {
|
|
143
|
+
if (fn(el))
|
|
144
|
+
return el;
|
|
145
|
+
el = el.parentNode;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function limit(min, max, value) {
|
|
149
|
+
if (value < min) {
|
|
150
|
+
return min;
|
|
151
|
+
}
|
|
152
|
+
if (value > max) {
|
|
153
|
+
return max;
|
|
154
|
+
}
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
function getCSSPixelValue(stringValue) {
|
|
158
|
+
if (stringValue.substr(-2) === 'px') {
|
|
159
|
+
return parseFloat(stringValue);
|
|
160
|
+
}
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
function getElementMargin(element) {
|
|
164
|
+
const style = window.getComputedStyle(element);
|
|
165
|
+
return {
|
|
166
|
+
top: getCSSPixelValue(style.marginTop),
|
|
167
|
+
right: getCSSPixelValue(style.marginRight),
|
|
168
|
+
bottom: getCSSPixelValue(style.marginBottom),
|
|
169
|
+
left: getCSSPixelValue(style.marginLeft),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function getPointerOffset(e, reference = 'page') {
|
|
173
|
+
const x = `${reference}X`;
|
|
174
|
+
const y = `${reference}Y`;
|
|
175
|
+
return {
|
|
176
|
+
x: isTouch(e) ? e.touches[0][x] : e[x],
|
|
177
|
+
y: isTouch(e) ? e.touches[0][y] : e[y],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function offsetParents(node) {
|
|
181
|
+
const nodes = [node];
|
|
182
|
+
for (; node; node = node.offsetParent) {
|
|
183
|
+
nodes.unshift(node);
|
|
184
|
+
}
|
|
185
|
+
return nodes;
|
|
186
|
+
}
|
|
187
|
+
function commonOffsetParent(node1, node2) {
|
|
188
|
+
const parents1 = offsetParents(node1);
|
|
189
|
+
const parents2 = offsetParents(node2);
|
|
190
|
+
if (parents1[0] != parents2[0])
|
|
191
|
+
throw 'No common ancestor!';
|
|
192
|
+
for (let i = 0; i < parents1.length; i++) {
|
|
193
|
+
if (parents1[i] != parents2[i])
|
|
194
|
+
return parents1[i - 1];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function getEdgeOffset(node, container, offset = { top: 0, left: 0 }) {
|
|
198
|
+
// Get the actual offsetTop / offsetLeft value, no matter how deep the node is nested
|
|
199
|
+
if (node) {
|
|
200
|
+
const nodeOffset = {
|
|
201
|
+
top: offset.top + node.offsetTop,
|
|
202
|
+
left: offset.left + node.offsetLeft,
|
|
203
|
+
};
|
|
204
|
+
if (node.offsetParent !== container.offsetParent) {
|
|
205
|
+
return getEdgeOffset(node.offsetParent, container, nodeOffset);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
return nodeOffset;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { top: 0, left: 0 };
|
|
212
|
+
}
|
|
213
|
+
function cloneNode(node) {
|
|
214
|
+
const fields = node.querySelectorAll('input, textarea, select');
|
|
215
|
+
const clonedNode = node.cloneNode(true);
|
|
216
|
+
const clonedFields = [...clonedNode.querySelectorAll('input, textarea, select')]; // Convert NodeList to Array
|
|
217
|
+
clonedFields.forEach((field, index) => {
|
|
218
|
+
if (field.type !== 'file' && fields[index]) {
|
|
219
|
+
field.value = fields[index].value;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return clonedNode;
|
|
223
|
+
}
|
|
224
|
+
function getLockPixelOffsets(lockOffset, width, height) {
|
|
225
|
+
if (typeof lockOffset == 'string') {
|
|
226
|
+
lockOffset = +lockOffset;
|
|
227
|
+
}
|
|
228
|
+
if (!Array.isArray(lockOffset)) {
|
|
229
|
+
lockOffset = [lockOffset, lockOffset];
|
|
230
|
+
}
|
|
231
|
+
if (lockOffset.length !== 2) {
|
|
232
|
+
throw new Error(`lockOffset prop of SortableContainer should be a single value or an array of exactly two values. Given ${lockOffset}`);
|
|
233
|
+
}
|
|
234
|
+
const [minLockOffset, maxLockOffset] = lockOffset;
|
|
235
|
+
return [getLockPixelOffset(minLockOffset, width, height), getLockPixelOffset(maxLockOffset, width, height)];
|
|
236
|
+
}
|
|
237
|
+
function getLockPixelOffset(lockOffset, width, height) {
|
|
238
|
+
let offsetX = lockOffset;
|
|
239
|
+
let offsetY = lockOffset;
|
|
240
|
+
let unit = 'px';
|
|
241
|
+
if (typeof lockOffset === 'string') {
|
|
242
|
+
const match = /^[+-]?\d*(?:\.\d*)?(px|%)$/.exec(lockOffset);
|
|
243
|
+
if (match === null) {
|
|
244
|
+
throw new Error(`lockOffset value should be a number or a string of a number followed by "px" or "%". Given ${lockOffset}`);
|
|
245
|
+
}
|
|
246
|
+
offsetX = offsetY = parseFloat(lockOffset);
|
|
247
|
+
unit = match[1];
|
|
248
|
+
}
|
|
249
|
+
if (!isFinite(offsetX) || !isFinite(offsetY)) {
|
|
250
|
+
throw new Error(`lockOffset value should be a finite. Given ${lockOffset}`);
|
|
251
|
+
}
|
|
252
|
+
if (unit === '%') {
|
|
253
|
+
offsetX = (offsetX * width) / 100;
|
|
254
|
+
offsetY = (offsetY * height) / 100;
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
x: offsetX,
|
|
258
|
+
y: offsetY,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function getDistance(x1, y1, x2, y2) {
|
|
262
|
+
const x = x1 - x2;
|
|
263
|
+
const y = y1 - y2;
|
|
264
|
+
return Math.sqrt(x * x + y * y);
|
|
265
|
+
}
|
|
266
|
+
function getRectCenter(clientRect) {
|
|
267
|
+
return {
|
|
268
|
+
x: clientRect.left + clientRect.width / 2,
|
|
269
|
+
y: clientRect.top + clientRect.height / 2,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function resetTransform(nodes = []) {
|
|
273
|
+
for (let i = 0, len = nodes.length; i < len; i++) {
|
|
274
|
+
const node = nodes[i];
|
|
275
|
+
const el = node.node;
|
|
276
|
+
if (!el)
|
|
277
|
+
return;
|
|
278
|
+
// Clear the cached offsetTop / offsetLeft value
|
|
279
|
+
node.edgeOffset = null;
|
|
280
|
+
// Remove the transforms / transitions
|
|
281
|
+
setTransform(el);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function setTransform(el, transform = '', duration = '') {
|
|
285
|
+
if (!el)
|
|
286
|
+
return;
|
|
287
|
+
el.style['transform'] = transform;
|
|
288
|
+
el.style['transitionDuration'] = duration;
|
|
289
|
+
}
|
|
290
|
+
function withinBounds(pos, top, bottom) {
|
|
291
|
+
const upper = Math.max(top, bottom);
|
|
292
|
+
const lower = Math.min(top, bottom);
|
|
293
|
+
return lower <= pos && pos <= upper;
|
|
294
|
+
}
|
|
295
|
+
function isPointWithinRect({ x, y }, { top, left, width, height }) {
|
|
296
|
+
const withinX = withinBounds(x, left, left + width);
|
|
297
|
+
const withinY = withinBounds(y, top, top + height);
|
|
298
|
+
return withinX && withinY;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
302
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
303
|
+
const timeout = setTimeout;
|
|
304
|
+
// Export Sortable Container Component Mixin
|
|
305
|
+
const ContainerMixin = vue.defineComponent({
|
|
306
|
+
inject: {
|
|
307
|
+
SlicksortHub: {
|
|
308
|
+
from: 'SlicksortHub',
|
|
309
|
+
default: null,
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
provide() {
|
|
313
|
+
return {
|
|
314
|
+
manager: this.manager,
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
props: {
|
|
318
|
+
list: { type: Array, required: true },
|
|
319
|
+
axis: { type: String, default: 'y' },
|
|
320
|
+
distance: { type: Number, default: 0 },
|
|
321
|
+
pressDelay: { type: Number, default: 0 },
|
|
322
|
+
pressThreshold: { type: Number, default: 5 },
|
|
323
|
+
useDragHandle: { type: Boolean, default: false },
|
|
324
|
+
useWindowAsScrollContainer: { type: Boolean, default: false },
|
|
325
|
+
hideSortableGhost: { type: Boolean, default: true },
|
|
326
|
+
lockToContainerEdges: { type: Boolean, default: false },
|
|
327
|
+
lockOffset: { type: [String, Number, Array], default: '50%' },
|
|
328
|
+
transitionDuration: { type: Number, default: 300 },
|
|
329
|
+
appendTo: { type: String, default: 'body' },
|
|
330
|
+
draggedSettlingDuration: { type: Number, default: null },
|
|
331
|
+
group: { type: String, default: '' },
|
|
332
|
+
accept: { type: [Boolean, Array, Function], default: null },
|
|
333
|
+
cancelKey: { type: String, default: 'Escape' },
|
|
334
|
+
block: { type: Array, default: () => [] },
|
|
335
|
+
lockAxis: { type: String, default: '' },
|
|
336
|
+
helperClass: { type: String, default: '' },
|
|
337
|
+
contentWindow: { type: Object, default: null },
|
|
338
|
+
shouldCancelStart: {
|
|
339
|
+
type: Function,
|
|
340
|
+
default: (e) => {
|
|
341
|
+
// Cancel sorting if the event target is an `input`, `textarea`, `select` or `option`
|
|
342
|
+
const disabledElements = ['input', 'textarea', 'select', 'option', 'button'];
|
|
343
|
+
return disabledElements.indexOf(e.target.tagName.toLowerCase()) !== -1;
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
getHelperDimensions: {
|
|
347
|
+
type: Function,
|
|
348
|
+
default: ({ node }) => ({
|
|
349
|
+
width: node.offsetWidth,
|
|
350
|
+
height: node.offsetHeight,
|
|
351
|
+
}),
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
emits: ['sort-start', 'sort-move', 'sort-end', 'sort-cancel', 'sort-insert', 'sort-remove', 'update:list'],
|
|
355
|
+
data() {
|
|
356
|
+
let useHub = false;
|
|
357
|
+
if (this.group) {
|
|
358
|
+
// If the group option is set, it is assumed the user intends
|
|
359
|
+
// to drag between containers and the required plugin has been installed
|
|
360
|
+
if (this.SlicksortHub) {
|
|
361
|
+
useHub = true;
|
|
362
|
+
}
|
|
363
|
+
else if (process.env.NODE_ENV !== 'production') {
|
|
364
|
+
throw new Error('Slicksort plugin required to use "group" prop');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
sorting: false,
|
|
369
|
+
hub: useHub ? this.SlicksortHub : null,
|
|
370
|
+
manager: new Manager(),
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
mounted() {
|
|
374
|
+
if (this.hub) {
|
|
375
|
+
this.id = this.hub.getId();
|
|
376
|
+
}
|
|
377
|
+
this.container = this.$el;
|
|
378
|
+
this.document = this.container.ownerDocument || document;
|
|
379
|
+
this._window = this.contentWindow || window;
|
|
380
|
+
// Find the actual scroll container by traversing up the DOM
|
|
381
|
+
if (this.useWindowAsScrollContainer) {
|
|
382
|
+
this.scrollContainer = { scrollLeft: 0, scrollTop: 0 };
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
// Start from parent element to find scrollable container
|
|
386
|
+
let scrollContainer = this.container.parentElement;
|
|
387
|
+
while (scrollContainer && scrollContainer !== document.body) {
|
|
388
|
+
const overflowY = window.getComputedStyle(scrollContainer).overflowY;
|
|
389
|
+
if (overflowY === 'auto' || overflowY === 'scroll') {
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
scrollContainer = scrollContainer.parentElement;
|
|
393
|
+
}
|
|
394
|
+
this.scrollContainer = scrollContainer || this.container;
|
|
395
|
+
}
|
|
396
|
+
this.events = {
|
|
397
|
+
start: this.handleStart,
|
|
398
|
+
move: this.handleMove,
|
|
399
|
+
end: this.handleEnd,
|
|
400
|
+
};
|
|
401
|
+
for (const key in this.events) {
|
|
402
|
+
if (hasOwnProperty(this.events, key)) {
|
|
403
|
+
// @ts-ignore
|
|
404
|
+
events[key].forEach((eventName) => this.container.addEventListener(eventName, this.events[key]));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (this.hub) {
|
|
408
|
+
this.hub.addContainer(this);
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
beforeUnmount() {
|
|
412
|
+
for (const key in this.events) {
|
|
413
|
+
if (hasOwnProperty(this.events, key)) {
|
|
414
|
+
// @ts-ignore
|
|
415
|
+
events[key].forEach((eventName) => this.container.removeEventListener(eventName, this.events[key]));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (this.hub) {
|
|
419
|
+
this.hub.removeContainer(this);
|
|
420
|
+
}
|
|
421
|
+
if (this.dragendTimer)
|
|
422
|
+
clearTimeout(this.dragendTimer);
|
|
423
|
+
if (this.cancelTimer)
|
|
424
|
+
clearTimeout(this.cancelTimer);
|
|
425
|
+
if (this.pressTimer)
|
|
426
|
+
clearTimeout(this.pressTimer);
|
|
427
|
+
if (this.autoscrollInterval)
|
|
428
|
+
clearInterval(this.autoscrollInterval);
|
|
429
|
+
},
|
|
430
|
+
methods: {
|
|
431
|
+
handleStart(e) {
|
|
432
|
+
const { distance, shouldCancelStart } = this.$props;
|
|
433
|
+
if ((!isTouch(e) && e.button === 2) || shouldCancelStart(e)) {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
this._touched = true;
|
|
437
|
+
this._pos = getPointerOffset(e);
|
|
438
|
+
const target = e.target;
|
|
439
|
+
const node = closest(target, (el) => el.sortableInfo != null);
|
|
440
|
+
if (node && node.sortableInfo && this.nodeIsChild(node) && !this.sorting) {
|
|
441
|
+
const { useDragHandle } = this.$props;
|
|
442
|
+
const { index } = node.sortableInfo;
|
|
443
|
+
if (useDragHandle && !closest(target, (el) => el.sortableHandle != null))
|
|
444
|
+
return;
|
|
445
|
+
this.manager.active = { index };
|
|
446
|
+
/*
|
|
447
|
+
* Fixes a bug in Firefox where the :active state of anchor tags
|
|
448
|
+
* prevent subsequent 'mousemove' events from being fired
|
|
449
|
+
* (see https://github.com/clauderic/react-sortable-hoc/issues/118)
|
|
450
|
+
*/
|
|
451
|
+
if (target.tagName.toLowerCase() === 'a') {
|
|
452
|
+
e.preventDefault();
|
|
453
|
+
}
|
|
454
|
+
if (!distance) {
|
|
455
|
+
if (this.pressDelay === 0) {
|
|
456
|
+
this.handlePress(e);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
this.pressTimer = timeout(() => this.handlePress(e), this.pressDelay);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
nodeIsChild(node) {
|
|
465
|
+
return node.sortableInfo.manager === this.manager;
|
|
466
|
+
},
|
|
467
|
+
handleMove(e) {
|
|
468
|
+
const { distance, pressThreshold } = this.$props;
|
|
469
|
+
if (!this.sorting && this._touched) {
|
|
470
|
+
const offset = getPointerOffset(e);
|
|
471
|
+
this._delta = {
|
|
472
|
+
x: this._pos.x - offset.x,
|
|
473
|
+
y: this._pos.y - offset.y,
|
|
474
|
+
};
|
|
475
|
+
const delta = Math.abs(this._delta.x) + Math.abs(this._delta.y);
|
|
476
|
+
if (!distance && (!pressThreshold || (pressThreshold && delta >= pressThreshold))) {
|
|
477
|
+
if (this.cancelTimer)
|
|
478
|
+
clearTimeout(this.cancelTimer);
|
|
479
|
+
this.cancelTimer = timeout(this.cancel, 0);
|
|
480
|
+
}
|
|
481
|
+
else if (distance && delta >= distance && this.manager.isActive()) {
|
|
482
|
+
this.handlePress(e);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
handleEnd() {
|
|
487
|
+
if (!this._touched)
|
|
488
|
+
return;
|
|
489
|
+
const { distance } = this.$props;
|
|
490
|
+
this._touched = false;
|
|
491
|
+
if (!distance) {
|
|
492
|
+
this.cancel();
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
cancel() {
|
|
496
|
+
if (!this.sorting) {
|
|
497
|
+
clearTimeout(this.pressTimer);
|
|
498
|
+
this.manager.active = null;
|
|
499
|
+
if (this.hub)
|
|
500
|
+
this.hub.cancel();
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
handleSortCancel(e) {
|
|
504
|
+
if (isTouch(e) || e.key === this.cancelKey) {
|
|
505
|
+
this.newIndex = this.index;
|
|
506
|
+
this.canceling = true;
|
|
507
|
+
this.translate = { x: 0, y: 0 };
|
|
508
|
+
this.animateNodes();
|
|
509
|
+
this.handleSortEnd(e);
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
handlePress(e) {
|
|
513
|
+
e.stopPropagation();
|
|
514
|
+
const active = this.manager.getActive();
|
|
515
|
+
if (active) {
|
|
516
|
+
const { getHelperDimensions, helperClass, hideSortableGhost, appendTo } = this.$props;
|
|
517
|
+
const { node } = active;
|
|
518
|
+
const { index } = node.sortableInfo;
|
|
519
|
+
const margin = getElementMargin(node);
|
|
520
|
+
const containerBoundingRect = this.container.getBoundingClientRect();
|
|
521
|
+
const dimensions = getHelperDimensions({ index, node });
|
|
522
|
+
this.node = node;
|
|
523
|
+
this.margin = margin;
|
|
524
|
+
this.width = dimensions.width;
|
|
525
|
+
this.height = dimensions.height;
|
|
526
|
+
this.marginOffset = {
|
|
527
|
+
x: this.margin.left + this.margin.right,
|
|
528
|
+
y: Math.max(this.margin.top, this.margin.bottom),
|
|
529
|
+
};
|
|
530
|
+
this.boundingClientRect = node.getBoundingClientRect();
|
|
531
|
+
this.containerBoundingRect = containerBoundingRect;
|
|
532
|
+
this.index = index;
|
|
533
|
+
this.newIndex = index;
|
|
534
|
+
const clonedNode = cloneNode(node);
|
|
535
|
+
this.helper = this.document.querySelector(appendTo).appendChild(clonedNode);
|
|
536
|
+
this.helper.style.position = 'fixed';
|
|
537
|
+
this.helper.style.top = `${this.boundingClientRect.top - margin.top}px`;
|
|
538
|
+
this.helper.style.left = `${this.boundingClientRect.left - margin.left}px`;
|
|
539
|
+
this.helper.style.width = `${this.width}px`;
|
|
540
|
+
this.helper.style.height = `${this.height}px`;
|
|
541
|
+
this.helper.style.boxSizing = 'border-box';
|
|
542
|
+
this.helper.style.pointerEvents = 'none';
|
|
543
|
+
if (hideSortableGhost) {
|
|
544
|
+
this.sortableGhost = node;
|
|
545
|
+
node.style.visibility = 'hidden';
|
|
546
|
+
node.style.opacity = '0';
|
|
547
|
+
}
|
|
548
|
+
if (this.hub) {
|
|
549
|
+
this.hub.sortStart(this);
|
|
550
|
+
this.hub.helper = this.helper;
|
|
551
|
+
this.hub.ghost = this.sortableGhost;
|
|
552
|
+
}
|
|
553
|
+
this.intializeOffsets(e, this.boundingClientRect);
|
|
554
|
+
this.offsetEdge = getEdgeOffset(node, this.container);
|
|
555
|
+
if (helperClass) {
|
|
556
|
+
this.helper.classList.add(...helperClass.split(' '));
|
|
557
|
+
}
|
|
558
|
+
this.listenerNode = isTouch(e) ? node : this._window;
|
|
559
|
+
// @ts-ignore
|
|
560
|
+
events.move.forEach((eventName) => this.listenerNode.addEventListener(eventName, this.handleSortMove));
|
|
561
|
+
// @ts-ignore
|
|
562
|
+
events.end.forEach((eventName) => this.listenerNode.addEventListener(eventName, this.handleSortEnd));
|
|
563
|
+
// @ts-ignore
|
|
564
|
+
events.cancel.forEach((eventName) => this.listenerNode.addEventListener(eventName, this.handleSortCancel));
|
|
565
|
+
this.sorting = true;
|
|
566
|
+
this.$emit('sort-start', { event: e, node, index });
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
handleSortMove(e) {
|
|
570
|
+
e.preventDefault(); // Prevent scrolling on mobile
|
|
571
|
+
this.updatePosition(e);
|
|
572
|
+
if (this.hub) {
|
|
573
|
+
const payload = this.list[this.index];
|
|
574
|
+
this.hub.handleSortMove(e, payload);
|
|
575
|
+
}
|
|
576
|
+
if (!this.hub || this.hub.isDest(this)) {
|
|
577
|
+
this.animateNodes();
|
|
578
|
+
this.autoscroll();
|
|
579
|
+
}
|
|
580
|
+
this.$emit('sort-move', { event: e });
|
|
581
|
+
},
|
|
582
|
+
handleDropOut() {
|
|
583
|
+
const removed = this.list[this.index];
|
|
584
|
+
const newValue = arrayRemove(this.list, this.index);
|
|
585
|
+
this.$emit('sort-remove', {
|
|
586
|
+
oldIndex: this.index,
|
|
587
|
+
});
|
|
588
|
+
this.$emit('update:list', newValue);
|
|
589
|
+
return removed;
|
|
590
|
+
},
|
|
591
|
+
handleDropIn(payload) {
|
|
592
|
+
const newValue = arrayInsert(this.list, this.newIndex, payload);
|
|
593
|
+
this.$emit('sort-insert', {
|
|
594
|
+
newIndex: this.newIndex,
|
|
595
|
+
value: payload,
|
|
596
|
+
});
|
|
597
|
+
this.$emit('update:list', newValue);
|
|
598
|
+
this.handleDragEnd();
|
|
599
|
+
},
|
|
600
|
+
handleDragOut() {
|
|
601
|
+
if (this.autoscrollInterval) {
|
|
602
|
+
clearInterval(this.autoscrollInterval);
|
|
603
|
+
this.autoscrollInterval = null;
|
|
604
|
+
}
|
|
605
|
+
if (this.hub.isSource(this)) {
|
|
606
|
+
// Trick to animate all nodes up
|
|
607
|
+
this.translate = {
|
|
608
|
+
x: 10000,
|
|
609
|
+
y: 10000,
|
|
610
|
+
};
|
|
611
|
+
this.animateNodes();
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
this.manager.getRefs().forEach((ref) => {
|
|
615
|
+
ref.node.style['transform'] = '';
|
|
616
|
+
});
|
|
617
|
+
this.dragendTimer = timeout(this.handleDragEnd, this.transitionDuration || 0);
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
handleDragEnd() {
|
|
621
|
+
if (this.autoscrollInterval) {
|
|
622
|
+
clearInterval(this.autoscrollInterval);
|
|
623
|
+
this.autoscrollInterval = null;
|
|
624
|
+
}
|
|
625
|
+
resetTransform(this.manager.getRefs());
|
|
626
|
+
if (this.sortableGhost) {
|
|
627
|
+
this.sortableGhost.remove();
|
|
628
|
+
this.sortableGhost = null;
|
|
629
|
+
}
|
|
630
|
+
if (this.dragendTimer) {
|
|
631
|
+
clearTimeout(this.dragendTimer);
|
|
632
|
+
this.dragendTimer = null;
|
|
633
|
+
}
|
|
634
|
+
this.manager.active = null;
|
|
635
|
+
this._touched = false;
|
|
636
|
+
this.sorting = false;
|
|
637
|
+
},
|
|
638
|
+
intializeOffsets(e, clientRect) {
|
|
639
|
+
const { useWindowAsScrollContainer, containerBoundingRect, _window } = this;
|
|
640
|
+
this.marginOffset = {
|
|
641
|
+
x: this.margin.left + this.margin.right,
|
|
642
|
+
y: Math.max(this.margin.top, this.margin.bottom),
|
|
643
|
+
};
|
|
644
|
+
this._axis = {
|
|
645
|
+
x: this.axis.indexOf('x') >= 0,
|
|
646
|
+
y: this.axis.indexOf('y') >= 0,
|
|
647
|
+
};
|
|
648
|
+
this.initialOffset = getPointerOffset(e);
|
|
649
|
+
// initialScroll;
|
|
650
|
+
this.initialScroll = {
|
|
651
|
+
top: this.scrollContainer.scrollTop,
|
|
652
|
+
left: this.scrollContainer.scrollLeft,
|
|
653
|
+
};
|
|
654
|
+
// initialWindowScroll;
|
|
655
|
+
this.initialWindowScroll = {
|
|
656
|
+
top: window.pageYOffset,
|
|
657
|
+
left: window.pageXOffset,
|
|
658
|
+
};
|
|
659
|
+
this.translate = { x: 0, y: 0 };
|
|
660
|
+
this.minTranslate = {};
|
|
661
|
+
this.maxTranslate = {};
|
|
662
|
+
if (this._axis.x) {
|
|
663
|
+
this.minTranslate.x =
|
|
664
|
+
(useWindowAsScrollContainer ? 0 : containerBoundingRect.left) - clientRect.left - this.width / 2;
|
|
665
|
+
this.maxTranslate.x =
|
|
666
|
+
(useWindowAsScrollContainer ? _window.innerWidth : containerBoundingRect.left + containerBoundingRect.width) -
|
|
667
|
+
clientRect.left -
|
|
668
|
+
this.width / 2;
|
|
669
|
+
}
|
|
670
|
+
if (this._axis.y) {
|
|
671
|
+
this.minTranslate.y =
|
|
672
|
+
(useWindowAsScrollContainer ? 0 : containerBoundingRect.top) - clientRect.top - this.height / 2;
|
|
673
|
+
this.maxTranslate.y =
|
|
674
|
+
(useWindowAsScrollContainer
|
|
675
|
+
? _window.innerHeight
|
|
676
|
+
: containerBoundingRect.top + containerBoundingRect.height) -
|
|
677
|
+
clientRect.top -
|
|
678
|
+
this.height / 2;
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
handleDragIn(e, sortableGhost, helper) {
|
|
682
|
+
if (this.hub.isSource(this)) {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
if (this.dragendTimer) {
|
|
686
|
+
this.handleDragEnd();
|
|
687
|
+
clearTimeout(this.dragendTimer);
|
|
688
|
+
this.dragendTimer = null;
|
|
689
|
+
}
|
|
690
|
+
const nodes = this.manager.getRefs();
|
|
691
|
+
this.index = nodes.length;
|
|
692
|
+
this.manager.active = { index: this.index };
|
|
693
|
+
const containerBoundingRect = this.container.getBoundingClientRect();
|
|
694
|
+
const helperBoundingRect = helper.getBoundingClientRect();
|
|
695
|
+
this.containerBoundingRect = containerBoundingRect;
|
|
696
|
+
this.sortableGhost = cloneNode(sortableGhost);
|
|
697
|
+
this.container.appendChild(this.sortableGhost);
|
|
698
|
+
const ghostRect = this.sortableGhost.getBoundingClientRect();
|
|
699
|
+
this.boundingClientRect = ghostRect;
|
|
700
|
+
this.margin = getElementMargin(this.sortableGhost);
|
|
701
|
+
this.width = ghostRect.width;
|
|
702
|
+
this.height = ghostRect.height;
|
|
703
|
+
// XY coords of the inserted node, relative to the top-left corner of the container
|
|
704
|
+
this.offsetEdge = getEdgeOffset(this.sortableGhost, this.container);
|
|
705
|
+
this.intializeOffsets(e, ghostRect);
|
|
706
|
+
// Move the initialOffset back to the insertion point of the
|
|
707
|
+
// sortableGhost (end of the list), as if we had started the drag there.
|
|
708
|
+
this.initialOffset.x += ghostRect.x - helperBoundingRect.x;
|
|
709
|
+
this.initialOffset.y += ghostRect.y - helperBoundingRect.y;
|
|
710
|
+
// Turn on dragging
|
|
711
|
+
this.sorting = true;
|
|
712
|
+
},
|
|
713
|
+
handleSortEnd(e) {
|
|
714
|
+
// Remove the event listeners if the node is still in the DOM
|
|
715
|
+
if (this.listenerNode) {
|
|
716
|
+
events.move.forEach((eventName) =>
|
|
717
|
+
// @ts-ignore
|
|
718
|
+
this.listenerNode.removeEventListener(eventName, this.handleSortMove));
|
|
719
|
+
events.end.forEach((eventName) =>
|
|
720
|
+
// @ts-ignore
|
|
721
|
+
this.listenerNode.removeEventListener(eventName, this.handleSortEnd));
|
|
722
|
+
events.cancel.forEach((eventName) =>
|
|
723
|
+
// @ts-ignore
|
|
724
|
+
this.listenerNode.removeEventListener(eventName, this.handleSortCancel));
|
|
725
|
+
}
|
|
726
|
+
const nodes = this.manager.getRefs();
|
|
727
|
+
// Remove the helper class(es) early to give it a chance to transition back
|
|
728
|
+
if (this.helper && this.helperClass) {
|
|
729
|
+
this.helper.classList.remove(...this.helperClass.split(' '));
|
|
730
|
+
}
|
|
731
|
+
// Stop autoscroll
|
|
732
|
+
if (this.autoscrollInterval)
|
|
733
|
+
clearInterval(this.autoscrollInterval);
|
|
734
|
+
this.autoscrollInterval = null;
|
|
735
|
+
const onEnd = () => {
|
|
736
|
+
// Remove the helper from the DOM
|
|
737
|
+
if (this.helper) {
|
|
738
|
+
this.helper.remove();
|
|
739
|
+
this.helper = null;
|
|
740
|
+
}
|
|
741
|
+
if (this.hideSortableGhost && this.sortableGhost) {
|
|
742
|
+
this.sortableGhost.style.visibility = '';
|
|
743
|
+
this.sortableGhost.style.opacity = '';
|
|
744
|
+
}
|
|
745
|
+
resetTransform(nodes);
|
|
746
|
+
// Update state
|
|
747
|
+
if (this.hub && !this.hub.isDest(this)) {
|
|
748
|
+
this.canceling ? this.hub.cancel() : this.hub.handleSortEnd();
|
|
749
|
+
}
|
|
750
|
+
else if (this.canceling) {
|
|
751
|
+
this.$emit('sort-cancel', { event: e });
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
this.$emit('sort-end', {
|
|
755
|
+
event: e,
|
|
756
|
+
oldIndex: this.index,
|
|
757
|
+
newIndex: this.newIndex,
|
|
758
|
+
});
|
|
759
|
+
this.$emit('update:list', arrayMove(this.list, this.index, this.newIndex));
|
|
760
|
+
}
|
|
761
|
+
this.manager.active = null;
|
|
762
|
+
this._touched = false;
|
|
763
|
+
this.canceling = false;
|
|
764
|
+
this.sorting = false;
|
|
765
|
+
};
|
|
766
|
+
if (this.transitionDuration || this.draggedSettlingDuration) {
|
|
767
|
+
this.transitionHelperIntoPlace(nodes, onEnd);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
onEnd();
|
|
771
|
+
}
|
|
772
|
+
},
|
|
773
|
+
transitionHelperIntoPlace(nodes, cb) {
|
|
774
|
+
if (this.draggedSettlingDuration === 0 || nodes.length === 0 || !this.helper) {
|
|
775
|
+
return Promise.resolve();
|
|
776
|
+
}
|
|
777
|
+
const indexNode = nodes[this.index].node;
|
|
778
|
+
let targetX = 0;
|
|
779
|
+
let targetY = 0;
|
|
780
|
+
const scrollDifference = {
|
|
781
|
+
top: window.pageYOffset - this.initialWindowScroll.top,
|
|
782
|
+
left: window.pageXOffset - this.initialWindowScroll.left,
|
|
783
|
+
};
|
|
784
|
+
if (this.hub && !this.hub.isDest(this) && !this.canceling) {
|
|
785
|
+
const dest = this.hub.getDest();
|
|
786
|
+
if (!dest)
|
|
787
|
+
return;
|
|
788
|
+
const destIndex = dest.newIndex;
|
|
789
|
+
const destRefs = dest.manager.getOrderedRefs();
|
|
790
|
+
const destNode = destIndex < destRefs.length ? destRefs[destIndex].node : dest.sortableGhost;
|
|
791
|
+
const ancestor = commonOffsetParent(indexNode, destNode);
|
|
792
|
+
const sourceOffset = getEdgeOffset(indexNode, ancestor);
|
|
793
|
+
const targetOffset = getEdgeOffset(destNode, ancestor);
|
|
794
|
+
targetX = targetOffset.left - sourceOffset.left - scrollDifference.left;
|
|
795
|
+
targetY = targetOffset.top - sourceOffset.top - scrollDifference.top;
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
const newIndexNode = nodes[this.newIndex].node;
|
|
799
|
+
const deltaScroll = {
|
|
800
|
+
left: this.scrollContainer.scrollLeft - this.initialScroll.left + scrollDifference.left,
|
|
801
|
+
top: this.scrollContainer.scrollTop - this.initialScroll.top + scrollDifference.top,
|
|
802
|
+
};
|
|
803
|
+
targetX = -deltaScroll.left;
|
|
804
|
+
if (this.translate && this.translate.x > 0) {
|
|
805
|
+
// Diff against right edge when moving to the right
|
|
806
|
+
targetX +=
|
|
807
|
+
newIndexNode.offsetLeft + newIndexNode.offsetWidth - (indexNode.offsetLeft + indexNode.offsetWidth);
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
targetX += newIndexNode.offsetLeft - indexNode.offsetLeft;
|
|
811
|
+
}
|
|
812
|
+
targetY = -deltaScroll.top;
|
|
813
|
+
if (this.translate && this.translate.y > 0) {
|
|
814
|
+
// Diff against the bottom edge when moving down
|
|
815
|
+
targetY +=
|
|
816
|
+
newIndexNode.offsetTop + newIndexNode.offsetHeight - (indexNode.offsetTop + indexNode.offsetHeight);
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
targetY += newIndexNode.offsetTop - indexNode.offsetTop;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const duration = this.draggedSettlingDuration !== null ? this.draggedSettlingDuration : this.transitionDuration;
|
|
823
|
+
setTransform(this.helper, `translate3d(${targetX}px,${targetY}px, 0)`, `${duration}ms`);
|
|
824
|
+
// Register an event handler to clean up styles when the transition
|
|
825
|
+
// finishes.
|
|
826
|
+
const cleanup = (event) => {
|
|
827
|
+
if (!event || event.propertyName === 'transform') {
|
|
828
|
+
clearTimeout(cleanupTimer);
|
|
829
|
+
setTransform(this.helper);
|
|
830
|
+
cb();
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
// Force cleanup in case 'transitionend' never fires
|
|
834
|
+
const cleanupTimer = setTimeout(cleanup, duration + 10);
|
|
835
|
+
this.helper.addEventListener('transitionend', cleanup);
|
|
836
|
+
},
|
|
837
|
+
updatePosition(e) {
|
|
838
|
+
const { lockAxis, lockToContainerEdges } = this.$props;
|
|
839
|
+
const offset = getPointerOffset(e);
|
|
840
|
+
const translate = {
|
|
841
|
+
x: offset.x - this.initialOffset.x,
|
|
842
|
+
y: offset.y - this.initialOffset.y,
|
|
843
|
+
};
|
|
844
|
+
// Adjust for window scroll
|
|
845
|
+
translate.y -= window.pageYOffset - this.initialWindowScroll.top;
|
|
846
|
+
translate.x -= window.pageXOffset - this.initialWindowScroll.left;
|
|
847
|
+
this.translate = translate;
|
|
848
|
+
if (lockToContainerEdges) {
|
|
849
|
+
const [minLockOffset, maxLockOffset] = getLockPixelOffsets(this.lockOffset, this.height, this.width);
|
|
850
|
+
const minOffset = {
|
|
851
|
+
x: this.width / 2 - minLockOffset.x,
|
|
852
|
+
y: this.height / 2 - minLockOffset.y,
|
|
853
|
+
};
|
|
854
|
+
const maxOffset = {
|
|
855
|
+
x: this.width / 2 - maxLockOffset.x,
|
|
856
|
+
y: this.height / 2 - maxLockOffset.y,
|
|
857
|
+
};
|
|
858
|
+
if (this.minTranslate.x && this.maxTranslate.x)
|
|
859
|
+
translate.x = limit(this.minTranslate.x + minOffset.x, this.maxTranslate.x - maxOffset.x, translate.x);
|
|
860
|
+
if (this.minTranslate.y && this.maxTranslate.y)
|
|
861
|
+
translate.y = limit(this.minTranslate.y + minOffset.y, this.maxTranslate.y - maxOffset.y, translate.y);
|
|
862
|
+
}
|
|
863
|
+
if (lockAxis === 'x') {
|
|
864
|
+
translate.y = 0;
|
|
865
|
+
}
|
|
866
|
+
else if (lockAxis === 'y') {
|
|
867
|
+
translate.x = 0;
|
|
868
|
+
}
|
|
869
|
+
if (this.helper) {
|
|
870
|
+
this.helper.style['transform'] = `translate3d(${translate.x}px,${translate.y}px, 0)`;
|
|
871
|
+
}
|
|
872
|
+
},
|
|
873
|
+
animateNodes() {
|
|
874
|
+
const { transitionDuration, hideSortableGhost } = this.$props;
|
|
875
|
+
const nodes = this.manager.getOrderedRefs();
|
|
876
|
+
const deltaScroll = {
|
|
877
|
+
left: this.scrollContainer.scrollLeft - this.initialScroll.left,
|
|
878
|
+
top: this.scrollContainer.scrollTop - this.initialScroll.top,
|
|
879
|
+
};
|
|
880
|
+
const sortingOffset = {
|
|
881
|
+
left: this.offsetEdge.left + this.translate.x + deltaScroll.left,
|
|
882
|
+
top: this.offsetEdge.top + this.translate.y + deltaScroll.top,
|
|
883
|
+
};
|
|
884
|
+
const scrollDifference = {
|
|
885
|
+
top: window.pageYOffset - this.initialWindowScroll.top,
|
|
886
|
+
left: window.pageXOffset - this.initialWindowScroll.left,
|
|
887
|
+
};
|
|
888
|
+
this.newIndex = null;
|
|
889
|
+
for (let i = 0, len = nodes.length; i < len; i++) {
|
|
890
|
+
const { node } = nodes[i];
|
|
891
|
+
const index = node.sortableInfo.index;
|
|
892
|
+
const width = node.offsetWidth;
|
|
893
|
+
const height = node.offsetHeight;
|
|
894
|
+
const offset = {
|
|
895
|
+
width: this.width > width ? width / 2 : this.width / 2,
|
|
896
|
+
height: this.height > height ? height / 2 : this.height / 2,
|
|
897
|
+
};
|
|
898
|
+
const translate = {
|
|
899
|
+
x: 0,
|
|
900
|
+
y: 0,
|
|
901
|
+
};
|
|
902
|
+
let { edgeOffset } = nodes[i];
|
|
903
|
+
// If we haven't cached the node's offsetTop / offsetLeft value
|
|
904
|
+
if (!edgeOffset) {
|
|
905
|
+
nodes[i].edgeOffset = edgeOffset = getEdgeOffset(node, this.container);
|
|
906
|
+
}
|
|
907
|
+
// Get a reference to the next and previous node
|
|
908
|
+
const nextNode = i < nodes.length - 1 && nodes[i + 1];
|
|
909
|
+
const prevNode = i > 0 && nodes[i - 1];
|
|
910
|
+
// Also cache the next node's edge offset if needed.
|
|
911
|
+
// We need this for calculating the animation in a grid setup
|
|
912
|
+
if (nextNode && !nextNode.edgeOffset) {
|
|
913
|
+
nextNode.edgeOffset = getEdgeOffset(nextNode.node, this.container);
|
|
914
|
+
}
|
|
915
|
+
// If the node is the one we're currently animating, skip it
|
|
916
|
+
if (index === this.index) {
|
|
917
|
+
/*
|
|
918
|
+
* With windowing libraries such as `react-virtualized`, the sortableGhost
|
|
919
|
+
* node may change while scrolling down and then back up (or vice-versa),
|
|
920
|
+
* so we need to update the reference to the new node just to be safe.
|
|
921
|
+
*/
|
|
922
|
+
if (hideSortableGhost) {
|
|
923
|
+
this.sortableGhost = node;
|
|
924
|
+
node.style.visibility = 'hidden';
|
|
925
|
+
node.style.opacity = '0';
|
|
926
|
+
}
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
if (transitionDuration) {
|
|
930
|
+
node.style['transitionDuration'] = `${transitionDuration}ms`;
|
|
931
|
+
}
|
|
932
|
+
if (this._axis.x) {
|
|
933
|
+
if (this._axis.y) {
|
|
934
|
+
// Calculations for a grid setup
|
|
935
|
+
if (index < this.index &&
|
|
936
|
+
((sortingOffset.left + scrollDifference.left - offset.width <= edgeOffset.left &&
|
|
937
|
+
sortingOffset.top + scrollDifference.top <= edgeOffset.top + offset.height) ||
|
|
938
|
+
sortingOffset.top + scrollDifference.top + offset.height <= edgeOffset.top)) {
|
|
939
|
+
// If the current node is to the left on the same row, or above the node that's being dragged
|
|
940
|
+
// then move it to the right
|
|
941
|
+
translate.x = this.width + this.marginOffset.x;
|
|
942
|
+
if (edgeOffset.left + translate.x > this.containerBoundingRect.width - offset.width && nextNode) {
|
|
943
|
+
// If it moves passed the right bounds, then animate it to the first position of the next row.
|
|
944
|
+
// We just use the offset of the next node to calculate where to move, because that node's original position
|
|
945
|
+
// is exactly where we want to go
|
|
946
|
+
translate.x = nextNode.edgeOffset.left - edgeOffset.left;
|
|
947
|
+
translate.y = nextNode.edgeOffset.top - edgeOffset.top;
|
|
948
|
+
}
|
|
949
|
+
if (this.newIndex === null) {
|
|
950
|
+
this.newIndex = index;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
else if (index > this.index &&
|
|
954
|
+
((sortingOffset.left + scrollDifference.left + offset.width >= edgeOffset.left &&
|
|
955
|
+
sortingOffset.top + scrollDifference.top + offset.height >= edgeOffset.top) ||
|
|
956
|
+
sortingOffset.top + scrollDifference.top + offset.height >= edgeOffset.top + height)) {
|
|
957
|
+
// If the current node is to the right on the same row, or below the node that's being dragged
|
|
958
|
+
// then move it to the left
|
|
959
|
+
translate.x = -(this.width + this.marginOffset.x);
|
|
960
|
+
if (edgeOffset.left + translate.x < this.containerBoundingRect.left + offset.width && prevNode) {
|
|
961
|
+
// If it moves passed the left bounds, then animate it to the last position of the previous row.
|
|
962
|
+
// We just use the offset of the previous node to calculate where to move, because that node's original position
|
|
963
|
+
// is exactly where we want to go
|
|
964
|
+
translate.x = prevNode.edgeOffset.left - edgeOffset.left;
|
|
965
|
+
translate.y = prevNode.edgeOffset.top - edgeOffset.top;
|
|
966
|
+
}
|
|
967
|
+
this.newIndex = index;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
if (index > this.index && sortingOffset.left + scrollDifference.left + offset.width >= edgeOffset.left) {
|
|
972
|
+
translate.x = -(this.width + this.marginOffset.x);
|
|
973
|
+
this.newIndex = index;
|
|
974
|
+
}
|
|
975
|
+
else if (index < this.index &&
|
|
976
|
+
sortingOffset.left + scrollDifference.left <= edgeOffset.left + offset.width) {
|
|
977
|
+
translate.x = this.width + this.marginOffset.x;
|
|
978
|
+
if (this.newIndex == null) {
|
|
979
|
+
this.newIndex = index;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
else if (this._axis.y) {
|
|
985
|
+
if (index > this.index && sortingOffset.top + scrollDifference.top + offset.height >= edgeOffset.top) {
|
|
986
|
+
translate.y = -(this.height + this.marginOffset.y);
|
|
987
|
+
this.newIndex = index;
|
|
988
|
+
}
|
|
989
|
+
else if (index < this.index &&
|
|
990
|
+
sortingOffset.top + scrollDifference.top <= edgeOffset.top + offset.height) {
|
|
991
|
+
translate.y = this.height + this.marginOffset.y;
|
|
992
|
+
if (this.newIndex == null) {
|
|
993
|
+
this.newIndex = index;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
node.style['transform'] = `translate3d(${translate.x}px,${translate.y}px,0)`;
|
|
998
|
+
}
|
|
999
|
+
if (this.newIndex == null) {
|
|
1000
|
+
this.newIndex = this.index;
|
|
1001
|
+
}
|
|
1002
|
+
},
|
|
1003
|
+
autoscroll() {
|
|
1004
|
+
const translate = this.translate;
|
|
1005
|
+
const direction = {
|
|
1006
|
+
x: 0,
|
|
1007
|
+
y: 0,
|
|
1008
|
+
};
|
|
1009
|
+
const speed = {
|
|
1010
|
+
x: 1,
|
|
1011
|
+
y: 1,
|
|
1012
|
+
};
|
|
1013
|
+
const acceleration = {
|
|
1014
|
+
x: 10,
|
|
1015
|
+
y: 10,
|
|
1016
|
+
};
|
|
1017
|
+
if (translate.y >= this.maxTranslate.y - this.height / 2) {
|
|
1018
|
+
direction.y = 1; // Scroll Down
|
|
1019
|
+
speed.y = acceleration.y * Math.abs((this.maxTranslate.y - this.height / 2 - translate.y) / this.height);
|
|
1020
|
+
}
|
|
1021
|
+
else if (translate.x >= this.maxTranslate.x - this.width / 2) {
|
|
1022
|
+
direction.x = 1; // Scroll Right
|
|
1023
|
+
speed.x = acceleration.x * Math.abs((this.maxTranslate.x - this.width / 2 - translate.x) / this.width);
|
|
1024
|
+
}
|
|
1025
|
+
else if (translate.y <= this.minTranslate.y + this.height / 2) {
|
|
1026
|
+
direction.y = -1; // Scroll Up
|
|
1027
|
+
speed.y = acceleration.y * Math.abs((translate.y - this.height / 2 - this.minTranslate.y) / this.height);
|
|
1028
|
+
}
|
|
1029
|
+
else if (translate.x <= this.minTranslate.x + this.width / 2) {
|
|
1030
|
+
direction.x = -1; // Scroll Left
|
|
1031
|
+
speed.x = acceleration.x * Math.abs((translate.x - this.width / 2 - this.minTranslate.x) / this.width);
|
|
1032
|
+
}
|
|
1033
|
+
if (this.autoscrollInterval) {
|
|
1034
|
+
clearInterval(this.autoscrollInterval);
|
|
1035
|
+
this.autoscrollInterval = null;
|
|
1036
|
+
}
|
|
1037
|
+
if (direction.x !== 0 || direction.y !== 0) {
|
|
1038
|
+
this.autoscrollInterval = window.setInterval(() => {
|
|
1039
|
+
const offset = {
|
|
1040
|
+
left: 1 * speed.x * direction.x,
|
|
1041
|
+
top: 1 * speed.y * direction.y,
|
|
1042
|
+
};
|
|
1043
|
+
if (this.useWindowAsScrollContainer) {
|
|
1044
|
+
this._window.scrollBy(offset.left, offset.top);
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
this.scrollContainer.scrollTop += offset.top;
|
|
1048
|
+
this.scrollContainer.scrollLeft += offset.left;
|
|
1049
|
+
}
|
|
1050
|
+
this.translate.x += offset.left;
|
|
1051
|
+
this.translate.y += offset.top;
|
|
1052
|
+
this.animateNodes();
|
|
1053
|
+
}, 5);
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
// Export Sortable Element Handle Directive
|
|
1060
|
+
const HandleDirective = {
|
|
1061
|
+
beforeMount(el) {
|
|
1062
|
+
el.sortableHandle = true;
|
|
1063
|
+
},
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
const SlickItem = vue.defineComponent({
|
|
1067
|
+
name: 'SlickItem',
|
|
1068
|
+
mixins: [ElementMixin],
|
|
1069
|
+
props: {
|
|
1070
|
+
tag: {
|
|
1071
|
+
type: String,
|
|
1072
|
+
default: 'div',
|
|
1073
|
+
},
|
|
1074
|
+
},
|
|
1075
|
+
render() {
|
|
1076
|
+
var _a, _b;
|
|
1077
|
+
return vue.h(this.tag, (_b = (_a = this.$slots).default) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
1078
|
+
},
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
const SlickList = vue.defineComponent({
|
|
1082
|
+
name: 'SlickList',
|
|
1083
|
+
mixins: [ContainerMixin],
|
|
1084
|
+
props: {
|
|
1085
|
+
tag: {
|
|
1086
|
+
type: String,
|
|
1087
|
+
default: 'div',
|
|
1088
|
+
},
|
|
1089
|
+
itemKey: {
|
|
1090
|
+
type: [String, Function],
|
|
1091
|
+
default: 'id',
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
render() {
|
|
1095
|
+
var _a, _b;
|
|
1096
|
+
if (this.$slots.item) {
|
|
1097
|
+
return vue.h(this.tag, this.list.map((item, index) => {
|
|
1098
|
+
let key;
|
|
1099
|
+
if (item == null) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
else if (typeof this.itemKey === 'function') {
|
|
1103
|
+
key = this.itemKey(item);
|
|
1104
|
+
}
|
|
1105
|
+
else if (typeof item === 'object' &&
|
|
1106
|
+
hasOwnProperty(item, this.itemKey) &&
|
|
1107
|
+
typeof item[this.itemKey] == 'string') {
|
|
1108
|
+
key = item[this.itemKey];
|
|
1109
|
+
}
|
|
1110
|
+
else if (typeof item === 'string') {
|
|
1111
|
+
key = item;
|
|
1112
|
+
}
|
|
1113
|
+
else {
|
|
1114
|
+
throw new Error('Cannot find key for item, use the item-key prop and pass a function or string');
|
|
1115
|
+
}
|
|
1116
|
+
return vue.h(SlickItem, {
|
|
1117
|
+
key,
|
|
1118
|
+
index,
|
|
1119
|
+
}, {
|
|
1120
|
+
default: () => { var _a, _b; return (_b = (_a = this.$slots).item) === null || _b === void 0 ? void 0 : _b.call(_a, { item, index }); },
|
|
1121
|
+
});
|
|
1122
|
+
}));
|
|
1123
|
+
}
|
|
1124
|
+
return vue.h(this.tag, (_b = (_a = this.$slots).default) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
1125
|
+
},
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
const DragHandle = vue.defineComponent({
|
|
1129
|
+
props: {
|
|
1130
|
+
tag: {
|
|
1131
|
+
type: String,
|
|
1132
|
+
default: 'span',
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
mounted() {
|
|
1136
|
+
this.$el.sortableHandle = true;
|
|
1137
|
+
},
|
|
1138
|
+
render() {
|
|
1139
|
+
var _a, _b;
|
|
1140
|
+
return vue.h(this.tag, (_b = (_a = this.$slots).default) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
1141
|
+
},
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
let containerIDCounter = 1;
|
|
1145
|
+
/**
|
|
1146
|
+
* Always allow when dest === source
|
|
1147
|
+
* Defer to 'dest.accept()' if it is a function
|
|
1148
|
+
* Allow any group in the accept lists
|
|
1149
|
+
* Deny any group in the block list
|
|
1150
|
+
* Allow the same group by default, this can be overridden with the block prop
|
|
1151
|
+
*/
|
|
1152
|
+
function canAcceptElement(dest, source, payload) {
|
|
1153
|
+
if (source.id === dest.id)
|
|
1154
|
+
return true;
|
|
1155
|
+
if (dest.block && dest.block.includes(source.group))
|
|
1156
|
+
return false;
|
|
1157
|
+
if (typeof dest.accept === 'function') {
|
|
1158
|
+
return dest.accept({ dest, source, payload });
|
|
1159
|
+
}
|
|
1160
|
+
if (typeof dest.accept === 'boolean') {
|
|
1161
|
+
return dest.accept;
|
|
1162
|
+
}
|
|
1163
|
+
if (dest.accept && dest.accept.includes(source.group))
|
|
1164
|
+
return true;
|
|
1165
|
+
if (dest.group === source.group)
|
|
1166
|
+
return true;
|
|
1167
|
+
return false;
|
|
1168
|
+
}
|
|
1169
|
+
function findClosestDest({ x, y }, refs, currentDest) {
|
|
1170
|
+
// Quickly check if we are within the bounds of the current destination
|
|
1171
|
+
if (isPointWithinRect({ x, y }, currentDest.container.getBoundingClientRect())) {
|
|
1172
|
+
return currentDest;
|
|
1173
|
+
}
|
|
1174
|
+
let closest = null;
|
|
1175
|
+
let minDistance = Infinity;
|
|
1176
|
+
for (let i = 0; i < refs.length; i++) {
|
|
1177
|
+
const ref = refs[i];
|
|
1178
|
+
const rect = ref.container.getBoundingClientRect();
|
|
1179
|
+
const isWithin = isPointWithinRect({ x, y }, rect);
|
|
1180
|
+
if (isWithin) {
|
|
1181
|
+
// If we are within another destination, stop here
|
|
1182
|
+
return ref;
|
|
1183
|
+
}
|
|
1184
|
+
const center = getRectCenter(rect);
|
|
1185
|
+
const distance = getDistance(x, y, center.x, center.y);
|
|
1186
|
+
if (distance < minDistance) {
|
|
1187
|
+
closest = ref;
|
|
1188
|
+
minDistance = distance;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
// Try to guess the closest destination
|
|
1192
|
+
return closest;
|
|
1193
|
+
}
|
|
1194
|
+
class SlicksortHub {
|
|
1195
|
+
constructor() {
|
|
1196
|
+
this.helper = null;
|
|
1197
|
+
this.ghost = null;
|
|
1198
|
+
this.refs = [];
|
|
1199
|
+
this.source = null;
|
|
1200
|
+
this.dest = null;
|
|
1201
|
+
}
|
|
1202
|
+
getId() {
|
|
1203
|
+
return '' + containerIDCounter++;
|
|
1204
|
+
}
|
|
1205
|
+
isSource({ id }) {
|
|
1206
|
+
var _a;
|
|
1207
|
+
return ((_a = this.source) === null || _a === void 0 ? void 0 : _a.id) === id;
|
|
1208
|
+
}
|
|
1209
|
+
getSource() {
|
|
1210
|
+
return this.source;
|
|
1211
|
+
}
|
|
1212
|
+
isDest({ id }) {
|
|
1213
|
+
var _a;
|
|
1214
|
+
return ((_a = this.dest) === null || _a === void 0 ? void 0 : _a.id) === id;
|
|
1215
|
+
}
|
|
1216
|
+
getDest() {
|
|
1217
|
+
return this.dest;
|
|
1218
|
+
}
|
|
1219
|
+
addContainer(ref) {
|
|
1220
|
+
this.refs.push(ref);
|
|
1221
|
+
}
|
|
1222
|
+
removeContainer(ref) {
|
|
1223
|
+
this.refs = this.refs.filter((c) => c.id !== ref.id);
|
|
1224
|
+
}
|
|
1225
|
+
sortStart(ref) {
|
|
1226
|
+
this.source = ref;
|
|
1227
|
+
this.dest = ref;
|
|
1228
|
+
}
|
|
1229
|
+
handleSortMove(e, payload) {
|
|
1230
|
+
var _a, _b, _c, _d;
|
|
1231
|
+
const dest = this.dest;
|
|
1232
|
+
const source = this.source;
|
|
1233
|
+
if (!dest || !source)
|
|
1234
|
+
return;
|
|
1235
|
+
const refs = this.refs;
|
|
1236
|
+
const pointer = getPointerOffset(e, 'client');
|
|
1237
|
+
const newDest = findClosestDest(pointer, refs, dest) || dest;
|
|
1238
|
+
if (dest.id !== newDest.id && canAcceptElement(newDest, source, payload)) {
|
|
1239
|
+
this.dest = newDest;
|
|
1240
|
+
dest.handleDragOut();
|
|
1241
|
+
newDest.handleDragIn(e, this.ghost, this.helper);
|
|
1242
|
+
}
|
|
1243
|
+
if (dest.id !== ((_a = this.source) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
1244
|
+
(_b = this.dest) === null || _b === void 0 ? void 0 : _b.updatePosition(e);
|
|
1245
|
+
(_c = this.dest) === null || _c === void 0 ? void 0 : _c.animateNodes();
|
|
1246
|
+
(_d = this.dest) === null || _d === void 0 ? void 0 : _d.autoscroll();
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
handleSortEnd() {
|
|
1250
|
+
var _a, _b, _c, _d;
|
|
1251
|
+
if (((_a = this.source) === null || _a === void 0 ? void 0 : _a.id) === ((_b = this.dest) === null || _b === void 0 ? void 0 : _b.id))
|
|
1252
|
+
return;
|
|
1253
|
+
const payload = (_c = this.source) === null || _c === void 0 ? void 0 : _c.handleDropOut();
|
|
1254
|
+
(_d = this.dest) === null || _d === void 0 ? void 0 : _d.handleDropIn(payload);
|
|
1255
|
+
this.reset();
|
|
1256
|
+
}
|
|
1257
|
+
reset() {
|
|
1258
|
+
this.source = null;
|
|
1259
|
+
this.dest = null;
|
|
1260
|
+
this.helper = null;
|
|
1261
|
+
this.ghost = null;
|
|
1262
|
+
}
|
|
1263
|
+
cancel() {
|
|
1264
|
+
var _a;
|
|
1265
|
+
(_a = this.dest) === null || _a === void 0 ? void 0 : _a.handleDragEnd();
|
|
1266
|
+
this.reset();
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
const plugin = {
|
|
1271
|
+
install(app) {
|
|
1272
|
+
app.directive('drag-handle', HandleDirective);
|
|
1273
|
+
app.provide('SlicksortHub', new SlicksortHub());
|
|
1274
|
+
},
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
exports.ContainerMixin = ContainerMixin;
|
|
1278
|
+
exports.DragHandle = DragHandle;
|
|
1279
|
+
exports.ElementMixin = ElementMixin;
|
|
1280
|
+
exports.HandleDirective = HandleDirective;
|
|
1281
|
+
exports.SlickItem = SlickItem;
|
|
1282
|
+
exports.SlickList = SlickList;
|
|
1283
|
+
exports.arrayMove = arrayMove;
|
|
1284
|
+
exports.plugin = plugin;
|
|
1285
|
+
|
|
1286
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1287
|
+
|
|
1288
|
+
}));
|