Improve the multi-selection experience

New features:
- select all filtered cards
- assign or unassign a member to selected cards
- archive selected cards

This commit also fix the card sort indexes calculation when a multi-
selection is drag-dropped.
This commit is contained in:
Maxime Quandalle 2015-06-15 17:16:56 +02:00
parent a41e07b37e
commit 5478fc93db
12 changed files with 146 additions and 53 deletions

View file

@ -51,6 +51,19 @@
.fa.fa-check
margin: 0 4px
.sidebar-btn
display: block
margin: 5px 0
padding: 10px
border-radius: 3px
background: darken(white, 10%)
&:hover *
color: white
i.fa
margin-right: 10px
.board-sidebar
width: 248px
right: -@width

View file

@ -1,7 +1,7 @@
//-
XXX There is a *lot* of code duplication in the above templates and in the
XXX There is a *lot* of code duplication in the below templates and in the
corresponding JavaScript components. We will probably need the upcoming #let
and #each x in y constructors.
and #each x in y constructors to fix this.
template(name="filterSidebar")
ul.sidebar-list
@ -16,22 +16,27 @@ template(name="filterSidebar")
span.quiet {{_ "label-default" color}}
if Filter.labelIds.isSelected _id
i.fa.fa-check
hr
ul.sidebar-list
each currentBoard.members
if isActive
with getUser userId
li(class="{{#if Filter.members.isSelected _id}}active{{/if}}")
a.name.js-toogle-member-filter
+userAvatar(userId=this._id)
span.sidebar-list-item-description
= profile.name
| (<span class="username">{{ username }}</span>)
if Filter.members.isSelected _id
i.fa.fa-check
if Filter.isActive
hr
ul.sidebar-list
each currentBoard.members
if isActive
with getUser userId
li(class="{{#if Filter.members.isSelected _id}}active{{/if}}")
a.name.js-toogle-member-filter
+userAvatar(userId=this._id)
span.sidebar-list-item-description
= profile.name
| (<span class="username">{{ username }}</span>)
if Filter.members.isSelected _id
i.fa.fa-check
hr
a.js-clear-all(class="{{#unless Filter.isActive}}disabled{{/unless}}")
| {{_ 'filter-clear'}}
a.sidebar-btn.js-clear-all
i.fa.fa-filter
span {{_ 'filter-clear'}}
a.sidebar-btn.js-filter-to-selection
i.fa.fa-check-square-o
span Filter to selection
template(name="multiselectionSidebar")
ul.sidebar-list
@ -48,10 +53,32 @@ template(name="multiselectionSidebar")
i.fa.fa-check
else if someSelectedElementHave 'label' _id
i.fa.fa-ellipsis-h
//-
XXX We should be able to assign a member to the list of selected cards.
hr
ul.sidebar-list
each currentBoard.members
if isActive
with getUser userId
li(class="{{#if Filter.members.isSelected _id}}active{{/if}}")
a.name.js-toogle-member-multiselection
+userAvatar(userId=this._id)
span.sidebar-list-item-description
= profile.name
| (<span class="username">{{ username }}</span>)
if allSelectedElementHave 'member' _id
i.fa.fa-check
else if someSelectedElementHave 'member' _id
i.fa.fa-ellipsis-h
hr
a.sidebar-btn.js-archive-selection
i.fa.fa-archive
span Archive selection
template(name="disambiguateMultiLabelPopup")
p What do you want to do?
button.wide.js-remove-label Remove the label
button.wide.js-add-label Add the label
template(name="disambiguateMultiMemberPopup")
p What do you want to do?
button.wide.js-unassign-member Unassign member
button.wide.js-assign-member Assign member

View file

@ -5,19 +5,26 @@ BlazeComponent.extendComponent({
events: function() {
return [{
'click .js-toggle-label-filter': function(event) {
'click .js-toggle-label-filter': function(evt) {
evt.preventDefault();
Filter.labelIds.toogle(this.currentData()._id);
Filter.resetExceptions();
event.preventDefault();
},
'click .js-toogle-member-filter': function(event) {
'click .js-toogle-member-filter': function(evt) {
evt.preventDefault();
Filter.members.toogle(this.currentData()._id);
Filter.resetExceptions();
event.preventDefault();
},
'click .js-clear-all': function(event) {
'click .js-clear-all': function(evt) {
evt.preventDefault();
Filter.reset();
event.preventDefault();
},
'click .js-filter-to-selection': function(evt) {
evt.preventDefault();
var selectedCards = Cards.find(Filter.mongoSelector()).map(function(c) {
return c._id;
});
MultiSelection.add(selectedCards);
}
}];
}
@ -57,7 +64,7 @@ BlazeComponent.extendComponent({
events: function() {
return [{
'click .js-toggle-label-multiselection': function(evt, tpl) {
'click .js-toggle-label-multiselection': function(evt) {
var labelId = this.currentData()._id;
var mappedSelection = this.mapSelection('label', labelId);
var operation;
@ -69,7 +76,7 @@ BlazeComponent.extendComponent({
var popup = Popup.open('disambiguateMultiLabel');
// XXX We need to have a better integration between the popup and the
// UI components systems.
return popup.call(this.currentData(), evt, tpl);
return popup.call(this.currentData(), evt);
}
var query = {};
@ -77,6 +84,30 @@ BlazeComponent.extendComponent({
labelIds: labelId
};
updateSelectedCards(query);
},
'click .js-toogle-member-multiselection': function(evt) {
var memberId = this.currentData()._id;
var mappedSelection = this.mapSelection('member', memberId);
var operation;
if (_.every(mappedSelection))
operation = '$pull';
else if (_.every(mappedSelection, function(bool) { return ! bool; }))
operation = '$addToSet';
else {
var popup = Popup.open('disambiguateMultiMember');
// XXX We need to have a better integration between the popup and the
// UI components systems.
return popup.call(this.currentData(), evt);
}
var query = {};
query[operation] = {
members: memberId
};
updateSelectedCards(query);
},
'click .js-archive-selection': function() {
updateSelectedCards({$set: {archived: true}});
}
}];
}
@ -92,3 +123,14 @@ Template.disambiguateMultiLabelPopup.events({
Popup.close();
}
});
Template.disambiguateMultiMemberPopup.events({
'click .js-unassign-member': function() {
updateSelectedCards({$pull: {members: this._id}});
Popup.close();
},
'click .js-assign-member': function() {
updateSelectedCards({$addToSet: {members: this._id}});
Popup.close();
}
});