Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multidrag select across sortables #2181

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
144 changes: 113 additions & 31 deletions plugins/MultiDrag/MultiDrag.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
setRect,
unsetRect,
matrix,
expando
expando,
getParentOrHost,
} from '../../src/utils.js';

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

if (sortable.options.group) {
const group = typeof sortable.options.group === 'string' ? { name: sortable.options.group } : sortable.options.group;
if (multiDragGroupMembers[group.name] === undefined) {
multiDragGroupMembers[group.name] = [];
}
multiDragGroupMembers[group.name].push(sortable);
}

on(document, 'keydown', this._checkKeyDown);
on(document, 'keyup', this._checkKeyUp);

Expand All @@ -69,7 +79,6 @@ function MultiDragPlugin() {
multiDragKeyDown: false,
isMultiDrag: false,


delayStartGlobal({ dragEl: dragged }) {
dragEl = dragged;
},
Expand All @@ -84,6 +93,7 @@ function MultiDragPlugin() {
multiDragClones.push(clone(multiDragElements[i]));

multiDragClones[i].sortableIndex = multiDragElements[i].sortableIndex;
multiDragClones[i].sortableParentEl = multiDragElements[i].sortableParentEl;

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

dragStartGlobal({ sortable }) {
if (!this.isMultiDrag && multiDragSortable) {
multiDragSortable.multiDrag._deselectMultiDrag();
MultiDrag.utils.clear();
}

multiDragElements.forEach(multiDragElement => {
multiDragElement.sortableIndex = index(multiDragElement);
multiDragElement.sortableParentEl = getParentOrHost(multiDragElement);
});

// Sort multi-drag elements
Expand Down Expand Up @@ -197,10 +208,46 @@ function MultiDragPlugin() {
});
},

dragOver({ target, completed, cancel }) {
dragOver({ target, completed, cancel, originalEvent }) {
if (folding && ~multiDragElements.indexOf(target)) {
completed(false);
cancel();
return;
}

const toSortable = target.parentNode[expando];

if (!toSortable || multiDragElements.length === 0) {
return;
}

let checkPut;

if (toSortable.options.group) {
checkPut = toSortable.options.group.checkPut;
}

const forbiddenMove = ~multiDragElements.findIndex((el) => {
if (!el.sortableParentEl) {
return false;
}

const fromSortable = el.sortableParentEl[expando];

if (fromSortable && fromSortable.options.group && !fromSortable.options.group.checkPull(toSortable, fromSortable, el, originalEvent)) {
return true;
}

if (checkPut && !checkPut(toSortable, fromSortable, el, originalEvent)) {
return true;
}

return false;
});

if (forbiddenMove) {
completed(false);
cancel();
}
},

Expand Down Expand Up @@ -311,7 +358,7 @@ function MultiDragPlugin() {
// Multi-drag selection
if (!dragStarted) {
if (options.multiDragKey && !this.multiDragKeyDown) {
this._deselectMultiDrag();
MultiDrag.utils.clear();
}
toggleClass(dragEl, options.selectedClass, !~multiDragElements.indexOf(dragEl));

Expand Down Expand Up @@ -461,39 +508,38 @@ function MultiDragPlugin() {
},

destroyGlobal() {
this._deselectMultiDrag();
MultiDrag.utils.clear();

off(document, 'pointerup', this._deselectMultiDrag);
off(document, 'mouseup', this._deselectMultiDrag);
off(document, 'touchend', this._deselectMultiDrag);

off(document, 'keydown', this._checkKeyDown);
off(document, 'keyup', this._checkKeyUp);

const groupMembers = findAllMembersInSortableGroup(this.sortable);

if (groupMembers) {
let membersIndex;
if (~(membersIndex = groupMembers.indexOf(this.sortable))) {
groupMembers.splice(membersIndex, 1);
}
}
},

_deselectMultiDrag(evt) {
if (typeof dragStarted !== "undefined" && dragStarted) return;

// Only deselect if selection is in this sortable
if (multiDragSortable !== this.sortable) return;

// Only deselect if target is not item in this sortable
if (evt && closest(evt.target, this.options.draggable, this.sortable.el, false)) return;

// Only deselect if left click
if (evt && evt.button !== 0) return;

while (multiDragElements.length) {
let el = multiDragElements[0];
toggleClass(el, this.options.selectedClass, false);
multiDragElements.shift();
dispatchEvent({
sortable: this.sortable,
rootEl: this.sortable.el,
name: 'deselect',
targetEl: el,
originalEvent: evt
});
if (evt) {
// Only deselect if left click
if (evt.button !== 0) return;

// Only deselect if target is not item in any sortable in group (including this)
if (itemElIsInSortableGroup(evt.target, this.sortable)) return;
}

MultiDrag.utils.clear(evt);
},

_checkKeyDown(evt) {
Expand Down Expand Up @@ -521,7 +567,9 @@ function MultiDragPlugin() {
let sortable = el.parentNode[expando];
if (!sortable || !sortable.options.multiDrag || ~multiDragElements.indexOf(el)) return;
if (multiDragSortable && multiDragSortable !== sortable) {
multiDragSortable.multiDrag._deselectMultiDrag();
if (!itemElIsInSortableGroup(el, multiDragSortable)) {
MultiDrag.utils.clear();
}
multiDragSortable = sortable;
}
toggleClass(el, sortable.options.selectedClass, true);
Expand All @@ -537,6 +585,24 @@ function MultiDragPlugin() {
if (!sortable || !sortable.options.multiDrag || !~index) return;
toggleClass(el, sortable.options.selectedClass, false);
multiDragElements.splice(index, 1);
},
clear(evt) {
if (typeof dragStarted !== "undefined" && dragStarted) return;

while (multiDragElements.length) {
const el = multiDragElements[0];
const sortableEl = getParentOrHost(el);
const sortable = sortableEl[expando];
toggleClass(el, sortable.options.selectedClass, false);
multiDragElements.shift();
dispatchEvent({
sortable: sortable,
rootEl: sortableEl,
name: 'deselect',
targetEl: el,
originalEvent: evt
});
}
}
},
eventProperties() {
Expand All @@ -546,6 +612,7 @@ function MultiDragPlugin() {
multiDragElements.forEach(multiDragElement => {
oldIndicies.push({
multiDragElement,
parentElement: multiDragElement.sortableParentEl,
index: multiDragElement.sortableIndex
});

Expand All @@ -560,9 +627,11 @@ function MultiDragPlugin() {
}
newIndicies.push({
multiDragElement,
parentElement: multiDragElement.sortableParentEl,
index: newIndex
});
});

return {
items: [...multiDragElements],
clones: [...multiDragClones],
Expand All @@ -572,11 +641,13 @@ function MultiDragPlugin() {
},
optionListeners: {
multiDragKey(key) {
key = key.toLowerCase();
if (key === 'ctrl') {
key = 'Control';
} else if (key.length > 1) {
key = key.charAt(0).toUpperCase() + key.substr(1);
if (typeof key === 'string') {
key = key.toLowerCase();
if (key === 'ctrl') {
key = 'Control';
} else if (key.length > 1) {
key = key.charAt(0).toUpperCase() + key.substr(1);
}
}
return key;
}
Expand Down Expand Up @@ -618,4 +689,15 @@ function removeMultiDragElements() {
});
}

function findAllMembersInSortableGroup(sortable) {
if (!sortable.options.group) {
return null;
}
return multiDragGroupMembers[sortable.options.group.name] || [];
}

function itemElIsInSortableGroup(itemEl, sortable) {
return ~(findAllMembersInSortableGroup(sortable) || [sortable]).findIndex((sortable) => closest(itemEl, sortable.options.draggable, sortable.el, false));
}

export default MultiDragPlugin;