diff options
Diffstat (limited to 'client/lib')
-rw-r--r-- | client/lib/filter.js | 11 | ||||
-rw-r--r-- | client/lib/keyboard.js | 13 | ||||
-rw-r--r-- | client/lib/multiSelection.js | 159 | ||||
-rw-r--r-- | client/lib/popup.js | 4 |
4 files changed, 182 insertions, 5 deletions
diff --git a/client/lib/filter.js b/client/lib/filter.js index d96fa89c..359b65d3 100644 --- a/client/lib/filter.js +++ b/client/lib/filter.js @@ -91,7 +91,7 @@ Filter = { }); }, - getMongoSelector: function() { + _getMongoSelector: function() { var self = this; if (! self.isActive()) @@ -110,6 +110,14 @@ Filter = { return {$or: [filterSelector, exceptionsSelector]}; }, + mongoSelector: function(additionalSelector) { + var filterSelector = this._getMongoSelector(); + if (_.isUndefined(additionalSelector)) + return filterSelector; + else + return {$and: [filterSelector, additionalSelector]}; + }, + reset: function() { var self = this; _.forEach(self._fields, function(fieldName) { @@ -123,6 +131,7 @@ Filter = { if (this.isActive()) { this._exceptions.push(_id); this._exceptionsDep.changed(); + Tracker.flush(); } }, diff --git a/client/lib/keyboard.js b/client/lib/keyboard.js index 0fbdbfd5..8b105c28 100644 --- a/client/lib/keyboard.js +++ b/client/lib/keyboard.js @@ -47,11 +47,16 @@ EscapeActions = { 'textcomplete', 'popup', 'inlinedForm', + 'multiselection-disable', 'sidebarView', - 'detailedPane' + 'detailsPane', + 'multiselection-reset' ], - register: function(label, condition, action) { + register: function(label, action, condition) { + if (_.isUndefined(condition)) + condition = function() { return true; }; + // XXX Rewrite this with ES6: .push({ priority, condition, action }) var priority = this.hierarchy.indexOf(label); if (priority === -1) { @@ -87,6 +92,10 @@ EscapeActions = { if (!! currentAction.condition()) currentAction.action(); } + }, + + executeAll: function() { + return this.executeLowerThan(); } }; diff --git a/client/lib/multiSelection.js b/client/lib/multiSelection.js new file mode 100644 index 00000000..53c16da0 --- /dev/null +++ b/client/lib/multiSelection.js @@ -0,0 +1,159 @@ + +var getCardsBetween = function(idA, idB) { + + var pluckId = function(doc) { + return doc._id; + }; + + var getListsStrictlyBetween = function(id1, id2) { + return Lists.find({ + $and: [ + { sort: { $gt: Lists.findOne(id1).sort } }, + { sort: { $lt: Lists.findOne(id2).sort } } + ], + archived: false + }).map(pluckId); + }; + + var cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], function(c) { + return c.sort; + }); + + var 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: function() { + this._selectedCards.set([]); + }, + + getMongoSelector: function() { + return Filter.mongoSelector({ + _id: { $in: this._selectedCards.get() } + }); + }, + + isActive: function() { + return this._isActive.get(); + }, + + isEmpty: function() { + return this._selectedCards.get().length === 0; + }, + + activate: function() { + if (! this.isActive()) { + EscapeActions.executeLowerThan('detailsPane'); + this._isActive.set(true); + Sidebar.setView(this.sidebarView); + Tracker.flush(); + } + }, + + disable: function() { + if (this.isActive()) { + this._isActive.set(false); + if (Sidebar && Sidebar.getView() === this.sidebarView) { + Sidebar.setView(); + } + } + }, + + add: function(cardIds) { + return this.toogle(cardIds, { add: true, remove: false }); + }, + + remove: function(cardIds) { + return this.toogle(cardIds, { add: false, remove: true }); + }, + + toogleRange: function(cardId) { + var selectedCards = this._selectedCards.get(); + var startRange; + this.reset(); + if (! this.isActive() || selectedCards.length === 0) { + this.toogle(cardId); + } else { + startRange = selectedCards[selectedCards.length - 1]; + this.toogle(getCardsBetween(startRange, cardId)); + } + }, + + toogle: function(cardIds, options) { + var self = this; + cardIds = _.isString(cardIds) ? [cardIds] : cardIds; + options = _.extend({ + add: true, + remove: true + }, options || {}); + + if (! self.isActive()) { + self.reset(); + self.activate(); + } + + var selectedCards = self._selectedCards.get(); + + _.each(cardIds, function(cardId) { + var indexOfCard = selectedCards.indexOf(cardId); + + if (options.remove && indexOfCard > -1) + selectedCards.splice(indexOfCard, 1); + + else if (options.add) + selectedCards.push(cardId); + }); + + self._selectedCards.set(selectedCards); + }, + + isSelected: function(cardId) { + return this._selectedCards.get().indexOf(cardId) > -1; + } +}; + +Blaze.registerHelper('MultiSelection', MultiSelection); + +EscapeActions.register('multiselection-disable', + function() { MultiSelection.disable(); }, + function() { return MultiSelection.isActive(); } +); + +EscapeActions.register('multiselection-reset', + function() { MultiSelection.reset(); } +); diff --git a/client/lib/popup.js b/client/lib/popup.js index 6298ba81..46c137e8 100644 --- a/client/lib/popup.js +++ b/client/lib/popup.js @@ -205,6 +205,6 @@ $(document).on('click', function(evt) { // Press escape to close the popup. var bindPopup = function(f) { return _.bind(f, Popup); }; EscapeActions.register('popup', - bindPopup(Popup.isOpen), - bindPopup(Popup.close) + bindPopup(Popup.close), + bindPopup(Popup.isOpen) ); |