Implement multi-selection

The UI and the internal APIs are still rough around the edges but the
feature is basically working. You can now select multiple cards and
move them together or (un|)assign them a label.
This commit is contained in:
Maxime Quandalle 2015-05-29 23:35:30 +02:00
parent 6457615e6a
commit 2c0030da62
45 changed files with 883 additions and 933 deletions

View file

@ -8,7 +8,10 @@ template(name="board")
template(name="boardComponent")
if this
.board-wrapper(class=colorClass)
.board-canvas(class=sidebarSize)
.board-canvas(
class=sidebarSize
class="{{#if MultiSelection.isActive}}is-multiselection-active{{/if}}"
class="{{#if draggingActive.get}}is-dragging-active{{/if}}")
.lists.js-lists
each lists
+list(this)

View file

@ -12,14 +12,16 @@ BlazeComponent.extendComponent({
return 'boardComponent';
},
onCreated: function() {
this.draggingActive = new ReactiveVar(false);
},
openNewListForm: function() {
this.componentChildren('addListForm')[0].open();
},
showNewCardForms: function(value) {
_.each(this.componentChildren('list'), function(listComponent) {
listComponent.showNewCardForm(value);
});
setIsDragging: function(bool) {
this.draggingActive.set(bool);
},
scrollLeft: function(position) {
@ -79,8 +81,8 @@ BlazeComponent.extendComponent({
helper: 'clone',
items: '.js-list:not(.js-list-composer)',
placeholder: 'list placeholder',
start: function(event, ui) {
$('.list.placeholder').height(ui.item.height());
start: function(evt, ui) {
ui.placeholder.height(ui.helper.height());
Popup.close();
},
stop: function() {
@ -97,6 +99,11 @@ BlazeComponent.extendComponent({
}
});
// Disable drag-dropping while in multi-selection mode
self.autorun(function() {
self.$(lists).sortable('option', 'disabled', MultiSelection.isActive());
});
// If there is no data in the board (ie, no lists) we autofocus the list
// creation form by clicking on the corresponding element.
if (self.data().lists().count() === 0) {

View file

@ -19,6 +19,11 @@
&.next-sidebar
margin-right: 248px
&.is-dragging-active
.open-minicard-composer
display: none
.lists
align-items: flex-start
display: flex

View file

@ -27,15 +27,43 @@ template(name="headerBoard")
i.fa.fa-times-thin
else
span {{_ 'filter'}}
if currentUser.isBoardMember
a.board-header-btn.js-multiselection-activate(
title="{{#if MultiSelection.isActive}}{{_ 'filter-on-desc'}}{{/if}}"
class="{{#if MultiSelection.isActive}}emphasis{{/if}}")
i.fa.fa-check-square-o
if MultiSelection.isActive
span Multi-Selection is on
a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}")
i.fa.fa-times-thin
else
span Multi-Selection
.separator
a.board-header-btn.js-open-board-menu
i.board-header-btn-icon.fa.fa-cog
template(name="boardMenuPopup")
if currentUser.isBoardMember
ul.pop-over-list
li: a Archived elements
li: a.js-change-board-color Change color
li: a Permissions
hr
ul.pop-over-list
li: a.js-change-board-color Change color
li: a Copy this board
li: a Permissions
//-
XXX Language should be handled by sandstorm, but for now display a
language selection link in the board menu. This link is normally present
in the header bar that is not displayed on sandstorm.
if isSandstorm
li: a.js-change-language {{_ 'language'}}
unless isSandstorm
if currentUser.isBoardAdmin
hr
ul.pop-over-list
li: a Close Board…
template(name="boardVisibilityList")
ul.pop-over-list

View file

@ -1,6 +1,7 @@
Template.boardMenuPopup.events({
'click .js-rename-board': Popup.open('boardChangeTitle'),
'click .js-change-board-color': Popup.open('boardChangeColor')
'click .js-change-board-color': Popup.open('boardChangeColor'),
'click .js-change-language': Popup.open('setLanguage')
});
Template.boardChangeTitlePopup.events({
@ -24,14 +25,15 @@ BlazeComponent.extendComponent({
},
isStarred: function() {
var boardId = this.currentData()._id;
var currentBoard = this.currentData();
var user = Meteor.user();
return boardId && user && user.hasStarred(boardId);
return currentBoard && user && user.hasStarred(currentBoard._id);
},
// Only show the star counter if the number of star is greater than 2
showStarCounter: function() {
return this.currentData().stars > 2;
var currentBoard = this.currentData();
return currentBoard && currentBoard.stars > 2;
},
events: function() {
@ -49,6 +51,17 @@ BlazeComponent.extendComponent({
evt.stopPropagation();
Sidebar.setView();
Filter.reset();
},
'click .js-multiselection-activate': function() {
var currentCard = Session.get('currentCard');
MultiSelection.activate();
if (currentCard) {
MultiSelection.add(currentCard);
}
},
'click .js-multiselection-reset': function(evt) {
evt.stopPropagation();
MultiSelection.disable();
}
}];
}

View file

@ -1,6 +1,10 @@
// We define a set of six board colors that we took from the FlatUI palette.
// http://flatuicolors.com
//
// XXX Centralizing all these properties in a single file just because their
// value is derivedform the same color, doesn't make any sense. We should create
// a macro that would generate 6 version of a given propertie and dispatch this
// list in the other stylus files.
setBoardColor(color)
&#header,
&.sk-spinner div,
@ -8,13 +12,16 @@ setBoardColor(color)
.board-list & a
background-color: color
& .minicard.is-selected .minicard-details
.is-selected .minicard
border-left: 3px solid color
&.pop-over .pop-over-list li a:hover,
button[type=submit].primary, input[type=submit].primary
background-color: darken(color, 20%)
&.pop-over .pop-over-list li a:hover,
.sidebar-list li a:hover
background-color: lighten(color, 10%)
&#header #header-quick-access ul li.current
border-bottom: 2px solid lighten(color, 10%)
@ -28,6 +35,17 @@ setBoardColor(color)
&:hover .board-header-btn-close
background: darken(complement(color), 20%)
.materialCheckBox.is-checked
border-bottom: 2px solid color
border-right: 2px solid color
.is-multiselection-active .multi-selection-checkbox
&.is-checked + .minicard
background: lighten(color, 90%)
&:not(.is-checked) + .minicard:hover:not(.minicard-composer)
background: lighten(color, 97%)
.board-color-nephritis
setBoardColor(#27AE60)

View file

@ -19,7 +19,6 @@ Router.route('/boards/:_id/:slug', {
onAfterAction: function() {
// XXX We probably shouldn't rely on Session
Session.set('sidebarIsOpen', true);
Session.set('currentWidget', 'home');
Session.set('menuWidgetIsOpen', false);
},
waitOn: function() {
@ -37,6 +36,7 @@ Router.route('/boards/:_id/:slug', {
Router.route('/boards/:boardId/:slug/:cardId', {
name: 'Card',
template: 'board',
noEscapeActions: true,
onAfterAction: function() {
Tracker.nonreactive(function() {
if (! Session.get('currentCard') && Sidebar) {
@ -57,7 +57,7 @@ Router.route('/boards/:boardId/:slug/:cardId', {
});
// Close the card details pane by pressing escape
EscapeActions.register('detailedPane',
function() { return ! Session.equals('currentCard', null); },
function() { Utils.goBoardId(Session.get('currentBoard')); }
EscapeActions.register('detailsPane',
function() { Utils.goBoardId(Session.get('currentBoard')); },
function() { return ! Session.equals('currentCard', null); }
);