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', } );