mirror of https://github.com/wekan/wekan
The Open Source kanban (built with Meteor). Keep variable/table/field names camelCase. For translations, only add Pull Request changes to wekan/i18n/en.i18n.json , other translations are done at https://transifex.com/wekan/wekan only.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
3.4 KiB
167 lines
3.4 KiB
function getCardsBetween(idA, idB) {
|
|
function pluckId(doc) {
|
|
return doc._id;
|
|
}
|
|
|
|
function getListsStrictlyBetween(id1, id2) {
|
|
return Lists.find({
|
|
$and: [
|
|
{ sort: { $gt: Lists.findOne(id1).sort } },
|
|
{ sort: { $lt: Lists.findOne(id2).sort } },
|
|
],
|
|
archived: false,
|
|
}).map(pluckId);
|
|
}
|
|
|
|
const cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], c => {
|
|
return c.sort;
|
|
});
|
|
|
|
let selector;
|
|
if (cards[0].listId === cards[1].listId) {
|
|
selector = {
|
|
listId: cards[0].listId,
|
|
sort: {
|
|
$gte: cards[0].sort,
|
|
$lte: cards[1].sort,
|
|
},
|
|
archived: false,
|
|
};
|
|
} else {
|
|
selector = {
|
|
$or: [
|
|
{
|
|
listId: cards[0].listId,
|
|
sort: { $lte: cards[0].sort },
|
|
},
|
|
{
|
|
listId: {
|
|
$in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
|
|
},
|
|
},
|
|
{
|
|
listId: cards[1].listId,
|
|
sort: { $gte: cards[1].sort },
|
|
},
|
|
],
|
|
archived: false,
|
|
};
|
|
}
|
|
|
|
return Cards.find(Filter.mongoSelector(selector)).map(pluckId);
|
|
}
|
|
|
|
MultiSelection = {
|
|
sidebarView: 'multiselection',
|
|
|
|
_selectedCards: new ReactiveVar([]),
|
|
|
|
_isActive: new ReactiveVar(false),
|
|
|
|
startRangeCardId: null,
|
|
|
|
reset() {
|
|
this._selectedCards.set([]);
|
|
},
|
|
|
|
getMongoSelector() {
|
|
return Filter.mongoSelector({
|
|
_id: { $in: this._selectedCards.get() },
|
|
});
|
|
},
|
|
|
|
isActive() {
|
|
return this._isActive.get();
|
|
},
|
|
|
|
count() {
|
|
return Cards.find(this.getMongoSelector()).count();
|
|
},
|
|
|
|
isEmpty() {
|
|
return this.count() === 0;
|
|
},
|
|
|
|
activate() {
|
|
if (!this.isActive()) {
|
|
EscapeActions.executeUpTo('detailsPane');
|
|
this._isActive.set(true);
|
|
Tracker.flush();
|
|
}
|
|
Sidebar.setView(this.sidebarView);
|
|
},
|
|
|
|
disable() {
|
|
if (this.isActive()) {
|
|
this._isActive.set(false);
|
|
if (Sidebar && Sidebar.getView() === this.sidebarView) {
|
|
Sidebar.setView();
|
|
}
|
|
this.reset();
|
|
}
|
|
},
|
|
|
|
add(cardIds) {
|
|
return this.toggle(cardIds, { add: true, remove: false });
|
|
},
|
|
|
|
remove(cardIds) {
|
|
return this.toggle(cardIds, { add: false, remove: true });
|
|
},
|
|
|
|
toggleRange(cardId) {
|
|
const selectedCards = this._selectedCards.get();
|
|
this.reset();
|
|
if (!this.isActive() || selectedCards.length === 0) {
|
|
this.toggle(cardId);
|
|
} else {
|
|
const startRange = selectedCards[selectedCards.length - 1];
|
|
this.toggle(getCardsBetween(startRange, cardId));
|
|
}
|
|
},
|
|
|
|
toggle(cardIds, options = {}) {
|
|
cardIds = _.isString(cardIds) ? [cardIds] : cardIds;
|
|
options = {
|
|
add: true,
|
|
remove: true,
|
|
...options,
|
|
};
|
|
|
|
if (!this.isActive()) {
|
|
this.reset();
|
|
this.activate();
|
|
}
|
|
|
|
const selectedCards = this._selectedCards.get();
|
|
|
|
cardIds.forEach(cardId => {
|
|
const indexOfCard = selectedCards.indexOf(cardId);
|
|
|
|
if (options.remove && indexOfCard > -1)
|
|
selectedCards.splice(indexOfCard, 1);
|
|
else if (options.add) selectedCards.push(cardId);
|
|
});
|
|
|
|
this._selectedCards.set(selectedCards);
|
|
},
|
|
|
|
isSelected(cardId) {
|
|
return this._selectedCards.get().indexOf(cardId) > -1;
|
|
},
|
|
};
|
|
|
|
Blaze.registerHelper('MultiSelection', MultiSelection);
|
|
|
|
EscapeActions.register(
|
|
'multiselection',
|
|
() => {
|
|
MultiSelection.disable();
|
|
},
|
|
() => {
|
|
return MultiSelection.isActive();
|
|
},
|
|
{
|
|
noClickEscapeOn: '.js-minicard,.js-board-sidebar-content',
|
|
},
|
|
);
|
|
|