@fr0st/ui-sortable 2.0.4 → 3.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/LICENSE +0 -0
- package/README.md +375 -37
- package/dist/frost-ui-sortable.esm.js +361 -0
- package/dist/frost-ui-sortable.esm.js.map +1 -0
- package/dist/frost-ui-sortable.esm.min.js +2 -0
- package/dist/frost-ui-sortable.esm.min.js.map +1 -0
- package/dist/frost-ui-sortable.js +394 -323
- package/dist/frost-ui-sortable.js.map +1 -1
- package/dist/frost-ui-sortable.min.js +2 -1
- package/dist/frost-ui-sortable.min.js.map +1 -1
- package/package.json +56 -24
- package/src/{wrapper.js → browser.js} +0 -0
- package/src/helpers.js +53 -0
- package/src/index.js +0 -28
- package/src/sortable.js +475 -27
- package/src/prototype/events.js +0 -150
- package/src/prototype/helpers.js +0 -80
package/src/sortable.js
CHANGED
|
@@ -1,62 +1,510 @@
|
|
|
1
1
|
import $ from '@fr0st/query';
|
|
2
|
-
import { BaseComponent } from '@fr0st/ui';
|
|
2
|
+
import { BaseComponent, getPosition } from '@fr0st/ui';
|
|
3
|
+
import { resetCursor, setCursor, triggerSortableEvent } from './helpers.js';
|
|
4
|
+
|
|
5
|
+
const document = $.getContext();
|
|
6
|
+
const window = $.getWindow();
|
|
7
|
+
|
|
8
|
+
/** @typedef {'x'|'y'} SortableAxis */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} SortableOptions
|
|
12
|
+
* @property {SortableAxis|null} [axis=null] The axis used to determine item placement.
|
|
13
|
+
* @property {string} [cancel='input, textarea, button, select, option'] The selector for elements that cannot start a sort.
|
|
14
|
+
* @property {string|null} [connectWith=null] The selector for connected Sortable containers.
|
|
15
|
+
* @property {string} [cursor='auto'] The cursor applied to sortable items or handles.
|
|
16
|
+
* @property {boolean} [disabled=false] Whether sorting is initially disabled.
|
|
17
|
+
* @property {boolean} [dropOnEmpty=true] Whether connected empty containers accept items.
|
|
18
|
+
* @property {string|null} [handle=null] The handle selector within each sortable item.
|
|
19
|
+
* @property {string|null} [helperClass='bg-body-tertiary'] The class applied to an item while it is being sorted.
|
|
20
|
+
* @property {string} [items=':scope > li'] The selector for sortable items.
|
|
21
|
+
* @property {boolean} [scroll=true] Whether to scroll the window while sorting near an edge.
|
|
22
|
+
* @property {number} [scrollSensitivity=20] The distance from a window edge that starts scrolling, in pixels.
|
|
23
|
+
* @property {number} [scrollSpeed=5] The scroll distance applied per animation frame, in pixels.
|
|
24
|
+
* @property {number} [swapThreshold=0.25] The fraction of an item crossed before swapping, from `0` to `1`.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {object} SortableEventData
|
|
29
|
+
* @property {HTMLElement} item The item being sorted.
|
|
30
|
+
* @property {HTMLElement} from The previous or original Sortable container.
|
|
31
|
+
* @property {HTMLElement} to The next or final Sortable container.
|
|
32
|
+
* @property {number} oldIndex The previous or original item index.
|
|
33
|
+
* @property {number} newIndex The next or final item index.
|
|
34
|
+
*/
|
|
3
35
|
|
|
4
36
|
/**
|
|
5
|
-
*
|
|
6
|
-
* @
|
|
37
|
+
* Controls a sortable collection of elements.
|
|
38
|
+
* @augments {BaseComponent<SortableOptions>}
|
|
7
39
|
*/
|
|
8
40
|
export default class Sortable extends BaseComponent {
|
|
41
|
+
/** @type {SortableOptions} */
|
|
42
|
+
static defaults = {
|
|
43
|
+
axis: null,
|
|
44
|
+
cancel: 'input, textarea, button, select, option',
|
|
45
|
+
connectWith: null,
|
|
46
|
+
cursor: 'auto',
|
|
47
|
+
disabled: false,
|
|
48
|
+
dropOnEmpty: true,
|
|
49
|
+
handle: null,
|
|
50
|
+
helperClass: 'bg-body-tertiary',
|
|
51
|
+
items: ':scope > li',
|
|
52
|
+
scroll: true,
|
|
53
|
+
scrollSensitivity: 20,
|
|
54
|
+
scrollSpeed: 5,
|
|
55
|
+
swapThreshold: 0.25,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
#currentIndex;
|
|
59
|
+
#currentSortable;
|
|
60
|
+
#dragging;
|
|
61
|
+
#enabled;
|
|
62
|
+
#pos;
|
|
63
|
+
#scrollFrame;
|
|
64
|
+
#selector;
|
|
65
|
+
#startIndex;
|
|
66
|
+
#target;
|
|
67
|
+
|
|
9
68
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @param {HTMLElement} node The
|
|
12
|
-
* @param {
|
|
69
|
+
* Creates a Sortable.
|
|
70
|
+
* @param {HTMLElement} node The sortable container.
|
|
71
|
+
* @param {SortableOptions} [options] The Sortable options.
|
|
13
72
|
*/
|
|
14
73
|
constructor(node, options) {
|
|
15
74
|
super(node, options);
|
|
16
75
|
|
|
17
|
-
this
|
|
18
|
-
|
|
19
|
-
this.
|
|
20
|
-
}
|
|
76
|
+
this.#selector = this.options.handle ?
|
|
77
|
+
`:is(${this.options.items}) :is(${this.options.handle})` :
|
|
78
|
+
this.options.items;
|
|
21
79
|
|
|
22
|
-
this
|
|
80
|
+
this.#dragging = false;
|
|
81
|
+
this.#enabled = !this.options.disabled;
|
|
23
82
|
|
|
24
|
-
this.
|
|
25
|
-
this
|
|
83
|
+
setCursor($.find(this.#selector, this.node), this.options.cursor);
|
|
84
|
+
this.#events();
|
|
26
85
|
}
|
|
27
86
|
|
|
28
87
|
/**
|
|
29
|
-
*
|
|
88
|
+
* Disables the Sortable.
|
|
30
89
|
*/
|
|
31
90
|
disable() {
|
|
32
|
-
this
|
|
91
|
+
this.#enabled = false;
|
|
33
92
|
}
|
|
34
93
|
|
|
35
|
-
/**
|
|
36
|
-
* Dispose the Sortable.
|
|
37
|
-
*/
|
|
94
|
+
/** @inheritdoc */
|
|
38
95
|
dispose() {
|
|
39
|
-
|
|
40
|
-
|
|
96
|
+
if (!this.node) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
41
99
|
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
100
|
+
$.removeEventDelegate(this.node, 'mousedown.ui.sortable touchstart.ui.sortable');
|
|
101
|
+
$.removeEvent(this.node, 'receive.ui.sortable');
|
|
102
|
+
resetCursor($.find(this.#selector, this.node));
|
|
103
|
+
|
|
104
|
+
if (this.#target && this.options.helperClass) {
|
|
105
|
+
$.removeClass(this.#target, this.options.helperClass);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.#resetDrag();
|
|
44
109
|
|
|
45
110
|
super.dispose();
|
|
46
111
|
}
|
|
47
112
|
|
|
48
113
|
/**
|
|
49
|
-
*
|
|
114
|
+
* Enables the Sortable.
|
|
50
115
|
*/
|
|
51
116
|
enable() {
|
|
52
|
-
this
|
|
117
|
+
this.#enabled = true;
|
|
53
118
|
}
|
|
54
119
|
|
|
55
120
|
/**
|
|
56
|
-
*
|
|
57
|
-
* @
|
|
121
|
+
* Gets the Sortable item elements.
|
|
122
|
+
* @returns {HTMLElement[]} The Sortable item elements.
|
|
58
123
|
*/
|
|
59
124
|
items() {
|
|
60
|
-
return $.find(this.
|
|
125
|
+
return $.find(this.options.items, this.node);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Checks whether the window should scroll while sorting.
|
|
130
|
+
*/
|
|
131
|
+
#checkScroll() {
|
|
132
|
+
this.#scrollFrame = null;
|
|
133
|
+
|
|
134
|
+
if (!this.node || !this.#dragging || !this.#target || !this.#pos) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const originalX = $.getScrollX(window);
|
|
139
|
+
const originalY = $.getScrollY(window);
|
|
140
|
+
const windowHeight = $.height(window);
|
|
141
|
+
const windowWidth = $.width(window);
|
|
142
|
+
|
|
143
|
+
let offsetX = originalX;
|
|
144
|
+
let offsetY = originalY;
|
|
145
|
+
|
|
146
|
+
if (this.#pos.y < originalY + this.options.scrollSensitivity) {
|
|
147
|
+
offsetY = Math.max(0, originalY - this.options.scrollSpeed);
|
|
148
|
+
} else if (this.#pos.y > originalY + windowHeight - this.options.scrollSensitivity) {
|
|
149
|
+
const docHeight = $.height(document, { boxSize: $.SCROLL_BOX });
|
|
150
|
+
const maxY = Math.max(0, docHeight - windowHeight);
|
|
151
|
+
offsetY = Math.min(maxY, originalY + this.options.scrollSpeed);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this.#pos.x < originalX + this.options.scrollSensitivity) {
|
|
155
|
+
offsetX = Math.max(0, originalX - this.options.scrollSpeed);
|
|
156
|
+
} else if (this.#pos.x > originalX + windowWidth - this.options.scrollSensitivity) {
|
|
157
|
+
const docWidth = $.width(document, { boxSize: $.SCROLL_BOX });
|
|
158
|
+
const maxX = Math.max(0, docWidth - windowWidth);
|
|
159
|
+
offsetX = Math.min(maxX, originalX + this.options.scrollSpeed);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (offsetX === originalX && offsetY === originalY) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
window.scrollTo({
|
|
167
|
+
left: offsetX,
|
|
168
|
+
top: offsetY,
|
|
169
|
+
behavior: 'instant',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const deltaX = offsetX - originalX;
|
|
173
|
+
const deltaY = offsetY - originalY;
|
|
174
|
+
|
|
175
|
+
this.#pos.x += deltaX;
|
|
176
|
+
this.#pos.y += deltaY;
|
|
177
|
+
|
|
178
|
+
this.#updatePlacement(deltaX, deltaY);
|
|
179
|
+
this.#scheduleScroll();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Attaches events for the Sortable.
|
|
184
|
+
*/
|
|
185
|
+
#events() {
|
|
186
|
+
const downEvent = (e) => {
|
|
187
|
+
const cancelled = this.options.cancel &&
|
|
188
|
+
(
|
|
189
|
+
$.is(e.target, this.options.cancel) ||
|
|
190
|
+
$.is(e.currentTarget, this.options.cancel) ||
|
|
191
|
+
$.closest(e.target, this.options.cancel, this.node).length
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
if (
|
|
195
|
+
e.button ||
|
|
196
|
+
!this.#enabled ||
|
|
197
|
+
cancelled ||
|
|
198
|
+
$.getDataset(this.node, 'uiDragging')
|
|
199
|
+
) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this.#target = this.options.handle ?
|
|
204
|
+
this.items().find((node) => $.hasDescendent(node, e.currentTarget)) :
|
|
205
|
+
e.currentTarget;
|
|
206
|
+
|
|
207
|
+
if (!this.#target) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const moveEvent = (e) => {
|
|
213
|
+
if (!this.node || !this.#target) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const previousPos = this.#pos;
|
|
218
|
+
this.#pos = getPosition(e);
|
|
219
|
+
const deltaX = previousPos ?
|
|
220
|
+
this.#pos.x - previousPos.x :
|
|
221
|
+
0;
|
|
222
|
+
const deltaY = previousPos ?
|
|
223
|
+
this.#pos.y - previousPos.y :
|
|
224
|
+
0;
|
|
225
|
+
|
|
226
|
+
if (!this.#dragging) {
|
|
227
|
+
this.#dragging = true;
|
|
228
|
+
this.#currentSortable = this.node;
|
|
229
|
+
this.#startIndex = this.items().indexOf(this.#target);
|
|
230
|
+
this.#currentIndex = this.#startIndex;
|
|
231
|
+
|
|
232
|
+
$.setDataset(this.node, { uiDragging: true });
|
|
233
|
+
|
|
234
|
+
/** @type {SortableEventData} */
|
|
235
|
+
const eventData = {
|
|
236
|
+
item: this.#target,
|
|
237
|
+
from: this.node,
|
|
238
|
+
to: this.node,
|
|
239
|
+
oldIndex: this.#startIndex,
|
|
240
|
+
newIndex: this.#startIndex,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
triggerSortableEvent(this.node, 'sort', eventData);
|
|
244
|
+
|
|
245
|
+
if (!this.node) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (this.options.helperClass) {
|
|
250
|
+
$.addClass(this.#target, this.options.helperClass);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.#updatePlacement(deltaX, deltaY);
|
|
255
|
+
this.#scheduleScroll();
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const upEvent = (e) => {
|
|
259
|
+
if (!this.node) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!this.#dragging) {
|
|
264
|
+
this.#target = null;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (e.type === 'mouseup') {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const source = this.node;
|
|
273
|
+
const destination = this.#currentSortable;
|
|
274
|
+
/** @type {SortableEventData} */
|
|
275
|
+
const eventData = {
|
|
276
|
+
item: this.#target,
|
|
277
|
+
from: source,
|
|
278
|
+
to: destination,
|
|
279
|
+
oldIndex: this.#startIndex,
|
|
280
|
+
newIndex: this.#currentIndex,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
triggerSortableEvent(destination, 'sorted', eventData);
|
|
284
|
+
|
|
285
|
+
if (!this.node) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (this.options.helperClass) {
|
|
290
|
+
$.removeClass(this.#target, this.options.helperClass);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (source !== destination) {
|
|
294
|
+
triggerSortableEvent(source, 'update', eventData);
|
|
295
|
+
|
|
296
|
+
if (this.node) {
|
|
297
|
+
triggerSortableEvent(destination, 'update', eventData);
|
|
298
|
+
}
|
|
299
|
+
} else if (this.#currentIndex !== this.#startIndex) {
|
|
300
|
+
triggerSortableEvent(destination, 'update', eventData);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (this.node) {
|
|
304
|
+
this.#resetDrag();
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const dragEvent = $.mouseDragFactory(downEvent, moveEvent, upEvent);
|
|
309
|
+
|
|
310
|
+
$.addEventDelegate(
|
|
311
|
+
this.node,
|
|
312
|
+
'mousedown.ui.sortable touchstart.ui.sortable',
|
|
313
|
+
this.#selector,
|
|
314
|
+
dragEvent,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
$.addEvent(this.node, 'receive.ui.sortable', (_) => {
|
|
318
|
+
setCursor($.find(this.#selector, this.node), this.options.cursor);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Resets the active drag state.
|
|
324
|
+
*/
|
|
325
|
+
#resetDrag() {
|
|
326
|
+
if (this.#scrollFrame !== null && this.#scrollFrame !== undefined) {
|
|
327
|
+
window.cancelAnimationFrame(this.#scrollFrame);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (this.#dragging && this.node) {
|
|
331
|
+
$.removeDataset(this.node, 'uiDragging');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.#currentIndex = null;
|
|
335
|
+
this.#currentSortable = null;
|
|
336
|
+
this.#dragging = false;
|
|
337
|
+
this.#pos = null;
|
|
338
|
+
this.#scrollFrame = null;
|
|
339
|
+
this.#startIndex = null;
|
|
340
|
+
this.#target = null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Schedules the next auto-scroll update.
|
|
345
|
+
*/
|
|
346
|
+
#scheduleScroll() {
|
|
347
|
+
if (
|
|
348
|
+
!this.node ||
|
|
349
|
+
!this.options.scroll ||
|
|
350
|
+
!this.#dragging ||
|
|
351
|
+
this.#scrollFrame !== null && this.#scrollFrame !== undefined
|
|
352
|
+
) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
this.#scrollFrame = window.requestAnimationFrame((_) => {
|
|
357
|
+
this.#checkScroll();
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Updates item placement and sorting events for the current pointer position.
|
|
363
|
+
* @param {number} deltaX The horizontal pointer movement.
|
|
364
|
+
* @param {number} deltaY The vertical pointer movement.
|
|
365
|
+
*/
|
|
366
|
+
#updatePlacement(deltaX, deltaY) {
|
|
367
|
+
let nearestSortable = this.node;
|
|
368
|
+
|
|
369
|
+
if (this.options.connectWith) {
|
|
370
|
+
const connected = $.find(this.options.connectWith)
|
|
371
|
+
.filter((node) => node !== this.node)
|
|
372
|
+
.filter((node) => {
|
|
373
|
+
const sortable = this.constructor.init(node);
|
|
374
|
+
|
|
375
|
+
return sortable.#enabled &&
|
|
376
|
+
(sortable.options.dropOnEmpty || sortable.items().length);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (connected.length) {
|
|
380
|
+
nearestSortable = $.nearestTo(
|
|
381
|
+
[this.node, ...connected],
|
|
382
|
+
this.#pos.x,
|
|
383
|
+
this.#pos.y,
|
|
384
|
+
{ offset: true },
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const nearestInstance = this.constructor.init(nearestSortable);
|
|
390
|
+
const nearestItems = nearestInstance.items()
|
|
391
|
+
.filter((node) => node !== this.#target);
|
|
392
|
+
const nearestItem = $.nearestTo(
|
|
393
|
+
nearestItems,
|
|
394
|
+
this.#pos.x,
|
|
395
|
+
this.#pos.y,
|
|
396
|
+
{ offset: true },
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
if (nearestItem) {
|
|
400
|
+
this.#updateTarget(
|
|
401
|
+
nearestItem,
|
|
402
|
+
this.#pos.x,
|
|
403
|
+
this.#pos.y,
|
|
404
|
+
deltaX,
|
|
405
|
+
deltaY,
|
|
406
|
+
);
|
|
407
|
+
} else if (nearestSortable !== this.#currentSortable) {
|
|
408
|
+
$.append(nearestSortable, this.#target);
|
|
409
|
+
} else {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const newIndex = nearestInstance.items().indexOf(this.#target);
|
|
414
|
+
|
|
415
|
+
if (nearestSortable !== this.#currentSortable) {
|
|
416
|
+
const previousSortable = this.#currentSortable;
|
|
417
|
+
const previousInstance = this.constructor.init(previousSortable);
|
|
418
|
+
const { handle } = previousInstance.options;
|
|
419
|
+
|
|
420
|
+
resetCursor(handle ?
|
|
421
|
+
$.find(handle, this.#target) :
|
|
422
|
+
[this.#target]);
|
|
423
|
+
|
|
424
|
+
/** @type {SortableEventData} */
|
|
425
|
+
const eventData = {
|
|
426
|
+
item: this.#target,
|
|
427
|
+
from: previousSortable,
|
|
428
|
+
to: nearestSortable,
|
|
429
|
+
oldIndex: this.#currentIndex,
|
|
430
|
+
newIndex,
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
triggerSortableEvent(previousSortable, 'send', eventData);
|
|
434
|
+
|
|
435
|
+
if (!this.node) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
triggerSortableEvent(nearestSortable, 'receive', eventData);
|
|
440
|
+
} else if (this.#currentIndex !== newIndex) {
|
|
441
|
+
/** @type {SortableEventData} */
|
|
442
|
+
const eventData = {
|
|
443
|
+
item: this.#target,
|
|
444
|
+
from: nearestSortable,
|
|
445
|
+
to: nearestSortable,
|
|
446
|
+
oldIndex: this.#currentIndex,
|
|
447
|
+
newIndex,
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
triggerSortableEvent(nearestSortable, 'sorting', eventData);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!this.node) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
this.#currentIndex = newIndex;
|
|
458
|
+
this.#currentSortable = nearestSortable;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Moves the target relative to the nearest item.
|
|
463
|
+
* @param {HTMLElement} nearestItem The nearest item.
|
|
464
|
+
* @param {number} x The mouse X position.
|
|
465
|
+
* @param {number} y The mouse Y position.
|
|
466
|
+
* @param {number} deltaX The horizontal pointer movement.
|
|
467
|
+
* @param {number} deltaY The vertical pointer movement.
|
|
468
|
+
*/
|
|
469
|
+
#updateTarget(nearestItem, x, y, deltaX, deltaY) {
|
|
470
|
+
const nearestCenter = $.center(nearestItem, { offset: true });
|
|
471
|
+
const nearestRect = $.rect(nearestItem, { offset: true });
|
|
472
|
+
const targetCenter = $.center(this.#target, { offset: true });
|
|
473
|
+
const threshold = Math.min(1, Math.max(0, this.options.swapThreshold));
|
|
474
|
+
let thresholdX = 0.5;
|
|
475
|
+
let thresholdY = 0.5;
|
|
476
|
+
|
|
477
|
+
if (deltaX) {
|
|
478
|
+
thresholdX = deltaX > 0 ?
|
|
479
|
+
threshold :
|
|
480
|
+
1 - threshold;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (deltaY) {
|
|
484
|
+
thresholdY = deltaY > 0 ?
|
|
485
|
+
threshold :
|
|
486
|
+
1 - threshold;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let method;
|
|
490
|
+
if (this.options.axis === 'y' || nearestCenter.x === targetCenter.x) {
|
|
491
|
+
const swapY = nearestRect.top + nearestRect.height * thresholdY;
|
|
492
|
+
method = y < swapY ?
|
|
493
|
+
'before' :
|
|
494
|
+
'after';
|
|
495
|
+
} else if (this.options.axis === 'x' || nearestCenter.y === targetCenter.y) {
|
|
496
|
+
const swapX = nearestRect.left + nearestRect.width * thresholdX;
|
|
497
|
+
method = x < swapX ?
|
|
498
|
+
'before' :
|
|
499
|
+
'after';
|
|
500
|
+
} else {
|
|
501
|
+
const swapX = nearestRect.left + nearestRect.width * thresholdX;
|
|
502
|
+
const swapY = nearestRect.top + nearestRect.height * thresholdY;
|
|
503
|
+
method = x < swapX && y < swapY ?
|
|
504
|
+
'before' :
|
|
505
|
+
'after';
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
$[method](nearestItem, this.#target);
|
|
61
509
|
}
|
|
62
510
|
}
|
package/src/prototype/events.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import $ from '@fr0st/query';
|
|
2
|
-
import { getPosition } from '@fr0st/ui';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Attach events for the Sortable.
|
|
6
|
-
*/
|
|
7
|
-
export function _events() {
|
|
8
|
-
const downEvent = (e) => {
|
|
9
|
-
if (
|
|
10
|
-
e.button ||
|
|
11
|
-
!this._enabled ||
|
|
12
|
-
$.is(e.currentTarget, this._options.cancel) ||
|
|
13
|
-
$.getDataset(this._node, 'uiDragging')
|
|
14
|
-
) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
this._target = this._options.handle ?
|
|
19
|
-
this.items().find((node) => $.hasDescendent(node, e.currentTarget)) :
|
|
20
|
-
e.currentTarget;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const moveEvent = (e) => {
|
|
24
|
-
this._pos = getPosition(e);
|
|
25
|
-
|
|
26
|
-
if (!$.getDataset(this._node, 'uiDragging')) {
|
|
27
|
-
$.setDataset(this._node, { uiDragging: true });
|
|
28
|
-
|
|
29
|
-
this._startIndex = $.index(this._target);
|
|
30
|
-
this._currentIndex = this._startIndex;
|
|
31
|
-
|
|
32
|
-
$.triggerEvent(this._node, 'sort.ui.sortable', {
|
|
33
|
-
detail: {
|
|
34
|
-
target: this._target,
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
if (this._options.helperClass) {
|
|
39
|
-
$.addClass(this._target, this._options.helperClass);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
this._currentSortable = this._node;
|
|
43
|
-
|
|
44
|
-
if (this._options.scroll) {
|
|
45
|
-
this._checkScroll();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let nearestSortable = this._node;
|
|
50
|
-
|
|
51
|
-
if (this._options.connectWith) {
|
|
52
|
-
const connected = $.find(this._options.connectWith)
|
|
53
|
-
.filter((node) => {
|
|
54
|
-
const sortable = this.constructor.init(node);
|
|
55
|
-
|
|
56
|
-
return sortable._options.dropOnEmpty || sortable.items().length;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
if (connected.length) {
|
|
60
|
-
nearestSortable = $.nearestTo([this._node, ...connected], this._pos.x, this._pos.y, { offset: true });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const nearestItems = $.isSame(this._node, nearestSortable) ?
|
|
65
|
-
this.items() :
|
|
66
|
-
this.constructor.init(nearestSortable).items();
|
|
67
|
-
|
|
68
|
-
const nearestItem = $.nearestTo(nearestItems, this._pos.x, this._pos.y, { offset: true });
|
|
69
|
-
|
|
70
|
-
if (!nearestItem) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
this._updateTarget(nearestItem, this._pos.x, this._pos.y);
|
|
75
|
-
|
|
76
|
-
const newIndex = $.index(this._target);
|
|
77
|
-
|
|
78
|
-
if (!$.isSame(nearestSortable, this._currentSortable)) {
|
|
79
|
-
$.triggerEvent(this._currentSortable, 'send.ui.sortable', {
|
|
80
|
-
detail: {
|
|
81
|
-
target: this._target,
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
$.triggerEvent(nearestSortable, 'receive.ui.sortable', {
|
|
86
|
-
detail: {
|
|
87
|
-
target: this._target,
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
} else if (this._currentIndex !== newIndex) {
|
|
91
|
-
$.triggerEvent(nearestSortable, 'sorting.ui.sortable', {
|
|
92
|
-
detail: {
|
|
93
|
-
target: this._target,
|
|
94
|
-
},
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
this._currentIndex = newIndex;
|
|
99
|
-
this._currentSortable = nearestSortable;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const upEvent = (e) => {
|
|
103
|
-
if (!$.getDataset(this._node, 'uiDragging')) {
|
|
104
|
-
this._target = null;
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (e.type === 'mouseup') {
|
|
109
|
-
e.preventDefault();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
$.triggerEvent(this._currentSortable, 'sorted.ui.sortable', {
|
|
113
|
-
detail: {
|
|
114
|
-
target: this._target,
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
if (this._options.helperClass) {
|
|
119
|
-
$.removeClass(this._target, this._options.helperClass);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (this._currentIndex !== this._startIndex || !$.isSame(this._currentSortable, this._node)) {
|
|
123
|
-
$.triggerEvent(this._currentSortable, 'update.ui.sortable', {
|
|
124
|
-
detail: {
|
|
125
|
-
target: this._target,
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
$.removeDataset(this._node, 'uiDragging');
|
|
131
|
-
|
|
132
|
-
if (this._checkScrollTimer) {
|
|
133
|
-
clearTimeout(this._checkScrollTimer);
|
|
134
|
-
this._checkScrollTimer = null;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
this._target = null;
|
|
138
|
-
this._currentSortable = null;
|
|
139
|
-
this._currentIndex = null;
|
|
140
|
-
this._startIndex = null;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const dragEvent = $.mouseDragFactory(downEvent, moveEvent, upEvent);
|
|
144
|
-
|
|
145
|
-
$.addEventDelegate(this._node, 'mousedown.ui.sortable touchstart.ui.sortable', this._selector, dragEvent);
|
|
146
|
-
|
|
147
|
-
$.addEvent(this._node, 'receive.ui.sortable', (_) => {
|
|
148
|
-
this._refreshCursors();
|
|
149
|
-
});
|
|
150
|
-
};
|