Skip to content

Commit 05e5343

Browse files
committed
multidrag select across sortables
fix: programmatic selection yields unexpected results, multiDragKey not working as expected fix: multiDragKey explicitly set to null causes TypeError in option listener fix: selecting even one multi drag element should be forbidden unless multidragkey is down if set fix: use group.name to detach multi drag sortable on destroyGlobal fix: more robust multiDragGroupMember detachment revert to old meta key behavior fix: handle targets without parentNode in dragOver
1 parent babf6ab commit 05e5343

File tree

1 file changed

+113
-32
lines changed

1 file changed

+113
-32
lines changed

plugins/MultiDrag/MultiDrag.js

+113-32
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
setRect,
1111
unsetRect,
1212
matrix,
13-
expando
13+
expando,
14+
getParentOrHost,
1415
} from '../../src/utils.js';
1516

1617
import dispatchEvent from '../../src/EventDispatcher.js';
@@ -19,6 +20,7 @@ let multiDragElements = [],
1920
multiDragClones = [],
2021
lastMultiDragSelect, // for selection with modifier key down (SHIFT)
2122
multiDragSortable,
23+
multiDragGroupMembers = {},
2224
initialFolding = false, // Initial multi-drag fold when drag started
2325
folding = false, // Folding any other time
2426
dragStarted = false,
@@ -44,6 +46,14 @@ function MultiDragPlugin() {
4446
}
4547
}
4648

49+
if (sortable.options.group) {
50+
const group = typeof sortable.options.group === 'string' ? { name: sortable.options.group } : sortable.options.group;
51+
if (multiDragGroupMembers[group.name] === undefined) {
52+
multiDragGroupMembers[group.name] = [];
53+
}
54+
multiDragGroupMembers[group.name].push(sortable);
55+
}
56+
4757
on(document, 'keydown', this._checkKeyDown);
4858
on(document, 'keyup', this._checkKeyUp);
4959

@@ -69,7 +79,6 @@ function MultiDragPlugin() {
6979
multiDragKeyDown: false,
7080
isMultiDrag: false,
7181

72-
7382
delayStartGlobal({ dragEl: dragged }) {
7483
dragEl = dragged;
7584
},
@@ -84,6 +93,7 @@ function MultiDragPlugin() {
8493
multiDragClones.push(clone(multiDragElements[i]));
8594

8695
multiDragClones[i].sortableIndex = multiDragElements[i].sortableIndex;
96+
multiDragClones[i].sortableParentEl = multiDragElements[i].sortableParentEl;
8797

8898
multiDragClones[i].draggable = false;
8999
multiDragClones[i].style['will-change'] = '';
@@ -136,11 +146,12 @@ function MultiDragPlugin() {
136146

137147
dragStartGlobal({ sortable }) {
138148
if (!this.isMultiDrag && multiDragSortable) {
139-
multiDragSortable.multiDrag._deselectMultiDrag();
149+
MultiDrag.utils.clear();
140150
}
141151

142152
multiDragElements.forEach(multiDragElement => {
143153
multiDragElement.sortableIndex = index(multiDragElement);
154+
multiDragElement.sortableParentEl = getParentOrHost(multiDragElement);
144155
});
145156

146157
// Sort multi-drag elements
@@ -197,10 +208,46 @@ function MultiDragPlugin() {
197208
});
198209
},
199210

200-
dragOver({ target, completed, cancel }) {
211+
dragOver({ target, completed, cancel, originalEvent }) {
201212
if (folding && ~multiDragElements.indexOf(target)) {
202213
completed(false);
203214
cancel();
215+
return;
216+
}
217+
218+
const toSortable = target.parentNode && target.parentNode[expando];
219+
220+
if (!toSortable || multiDragElements.length === 0) {
221+
return;
222+
}
223+
224+
let checkPut;
225+
226+
if (toSortable.options.group) {
227+
checkPut = toSortable.options.group.checkPut;
228+
}
229+
230+
const forbiddenMove = ~multiDragElements.findIndex((el) => {
231+
if (!el.sortableParentEl) {
232+
return false;
233+
}
234+
235+
const fromSortable = el.sortableParentEl[expando];
236+
237+
if (fromSortable && fromSortable.options.group && !fromSortable.options.group.checkPull(toSortable, fromSortable, el, originalEvent)) {
238+
return true;
239+
}
240+
241+
if (checkPut && !checkPut(toSortable, fromSortable, el, originalEvent)) {
242+
return true;
243+
}
244+
245+
return false;
246+
});
247+
248+
if (forbiddenMove) {
249+
completed(false);
250+
cancel();
204251
}
205252
},
206253

@@ -311,9 +358,8 @@ function MultiDragPlugin() {
311358
// Multi-drag selection
312359
if (!dragStarted) {
313360
if (options.multiDragKey && !this.multiDragKeyDown) {
314-
this._deselectMultiDrag();
361+
MultiDrag.utils.clear();
315362
}
316-
toggleClass(dragEl, options.selectedClass, !~multiDragElements.indexOf(dragEl));
317363

318364
if (!~multiDragElements.indexOf(dragEl)) {
319365
multiDragElements.push(dragEl);
@@ -461,39 +507,38 @@ function MultiDragPlugin() {
461507
},
462508

463509
destroyGlobal() {
464-
this._deselectMultiDrag();
510+
MultiDrag.utils.clear();
511+
465512
off(document, 'pointerup', this._deselectMultiDrag);
466513
off(document, 'mouseup', this._deselectMultiDrag);
467514
off(document, 'touchend', this._deselectMultiDrag);
468515

469516
off(document, 'keydown', this._checkKeyDown);
470517
off(document, 'keyup', this._checkKeyUp);
518+
519+
const groupMembers = findAllMembersInSortableGroup(this.sortable);
520+
521+
if (groupMembers) {
522+
let membersIndex;
523+
if (~(membersIndex = groupMembers.indexOf(this.sortable))) {
524+
groupMembers.splice(membersIndex, 1);
525+
}
526+
}
471527
},
472528

473529
_deselectMultiDrag(evt) {
474-
if (typeof dragStarted !== "undefined" && dragStarted) return;
475-
476530
// Only deselect if selection is in this sortable
477531
if (multiDragSortable !== this.sortable) return;
478532

479-
// Only deselect if target is not item in this sortable
480-
if (evt && closest(evt.target, this.options.draggable, this.sortable.el, false)) return;
481-
482-
// Only deselect if left click
483-
if (evt && evt.button !== 0) return;
484-
485-
while (multiDragElements.length) {
486-
let el = multiDragElements[0];
487-
toggleClass(el, this.options.selectedClass, false);
488-
multiDragElements.shift();
489-
dispatchEvent({
490-
sortable: this.sortable,
491-
rootEl: this.sortable.el,
492-
name: 'deselect',
493-
targetEl: el,
494-
originalEvent: evt
495-
});
533+
if (evt) {
534+
// Only deselect if left click
535+
if (evt.button !== 0) return;
536+
537+
// Only deselect if target is not item in any sortable in group (including this)
538+
if (itemElIsInSortableGroup(evt.target, this.sortable)) return;
496539
}
540+
541+
MultiDrag.utils.clear(evt);
497542
},
498543

499544
_checkKeyDown(evt) {
@@ -521,7 +566,9 @@ function MultiDragPlugin() {
521566
let sortable = el.parentNode[expando];
522567
if (!sortable || !sortable.options.multiDrag || ~multiDragElements.indexOf(el)) return;
523568
if (multiDragSortable && multiDragSortable !== sortable) {
524-
multiDragSortable.multiDrag._deselectMultiDrag();
569+
if (!itemElIsInSortableGroup(el, multiDragSortable)) {
570+
MultiDrag.utils.clear();
571+
}
525572
multiDragSortable = sortable;
526573
}
527574
toggleClass(el, sortable.options.selectedClass, true);
@@ -537,6 +584,24 @@ function MultiDragPlugin() {
537584
if (!sortable || !sortable.options.multiDrag || !~index) return;
538585
toggleClass(el, sortable.options.selectedClass, false);
539586
multiDragElements.splice(index, 1);
587+
},
588+
clear(evt) {
589+
if (typeof dragStarted !== "undefined" && dragStarted) return;
590+
591+
while (multiDragElements.length) {
592+
const el = multiDragElements[0];
593+
const sortableEl = getParentOrHost(el);
594+
const sortable = sortableEl[expando];
595+
toggleClass(el, sortable.options.selectedClass, false);
596+
multiDragElements.shift();
597+
dispatchEvent({
598+
sortable: sortable,
599+
rootEl: sortableEl,
600+
name: 'deselect',
601+
targetEl: el,
602+
originalEvent: evt
603+
});
604+
}
540605
}
541606
},
542607
eventProperties() {
@@ -546,6 +611,7 @@ function MultiDragPlugin() {
546611
multiDragElements.forEach(multiDragElement => {
547612
oldIndicies.push({
548613
multiDragElement,
614+
parentElement: multiDragElement.sortableParentEl,
549615
index: multiDragElement.sortableIndex
550616
});
551617

@@ -560,9 +626,11 @@ function MultiDragPlugin() {
560626
}
561627
newIndicies.push({
562628
multiDragElement,
629+
parentElement: multiDragElement.sortableParentEl,
563630
index: newIndex
564631
});
565632
});
633+
566634
return {
567635
items: [...multiDragElements],
568636
clones: [...multiDragClones],
@@ -572,11 +640,13 @@ function MultiDragPlugin() {
572640
},
573641
optionListeners: {
574642
multiDragKey(key) {
575-
key = key.toLowerCase();
576-
if (key === 'ctrl') {
577-
key = 'Control';
578-
} else if (key.length > 1) {
579-
key = key.charAt(0).toUpperCase() + key.substr(1);
643+
if (typeof key === 'string') {
644+
key = key.toLowerCase();
645+
if (key === 'ctrl') {
646+
key = 'Control';
647+
} else if (key.length > 1) {
648+
key = key.charAt(0).toUpperCase() + key.substr(1);
649+
}
580650
}
581651
return key;
582652
}
@@ -618,4 +688,15 @@ function removeMultiDragElements() {
618688
});
619689
}
620690

691+
function findAllMembersInSortableGroup(sortable) {
692+
if (!sortable.options.group) {
693+
return null;
694+
}
695+
return multiDragGroupMembers[sortable.options.group.name] || [];
696+
}
697+
698+
function itemElIsInSortableGroup(itemEl, sortable) {
699+
return ~(findAllMembersInSortableGroup(sortable) || [sortable]).findIndex((sortable) => closest(itemEl, sortable.options.draggable, sortable.el, false));
700+
}
701+
621702
export default MultiDragPlugin;

0 commit comments

Comments
 (0)