Experiment new ergonomics to interact with card details

The idea is that by displaying card details in a sidebar stuck on the
right of the screen, the mouse had to travel too much before
interacting with it. I also don’t want to use the Trello solution
(modal) on big screens, because I like the ability to interact with
the selected card and with the board at the same time (like in a
e-mail client).

The solution introduced in this commit consist of opening the card
detail in a column next to the minicard list.

This commit also fix right sidebar members and labels drag and drop.
This commit is contained in:
Maxime Quandalle 2015-05-24 12:30:58 +02:00
parent 40b605f7d8
commit 781577db04
22 changed files with 225 additions and 203 deletions

View file

@ -1,6 +1,7 @@
//-
XXX This template can't be transformed into a component because it is
included by iron-router. That's a bug.
See https://github.com/peerlibrary/meteor-blaze-components/issues/44
template(name="board")
+boardComponent
@ -11,11 +12,11 @@ template(name="boardComponent")
.lists.js-lists
each lists
+list(this)
if currentCardIsInThisList
+cardDetails(currentCard)
if currentUser.isBoardMember
+addListForm
+boardSidebar
if currentCard
+cardSidebar(currentCard)
else
+message(label="board-no-found")

View file

@ -1,3 +1,12 @@
// XXX This event list must be abstracted somewhere else.
var endTransitionEvents = [
'webkitTransitionEnd',
'otransitionend',
'oTransitionEnd',
'msTransitionEnd',
'transitionend'
].join(' ');
BlazeComponent.extendComponent({
template: function() {
return 'boardComponent';
@ -17,50 +26,78 @@ BlazeComponent.extendComponent({
// TODO
},
currentCardIsInThisList: function() {
var currentCard = Cards.findOne(Session.get('currentCard'));
var listId = this.currentData()._id;
return currentCard && currentCard.listId === listId;
},
onRendered: function() {
var self = this;
self.scrollLeft();
if (Meteor.user().isBoardMember()) {
self.$('.js-lists').sortable({
tolerance: 'pointer',
appendTo: '.js-lists',
helper: 'clone',
items: '.js-list:not(.add-list)',
placeholder: 'list placeholder',
start: function(event, ui) {
$('.list.placeholder').height(ui.item.height());
Popup.close();
},
stop: function() {
self.$('.js-lists').find('.js-list:not(.add-list)').each(
function(i, list) {
var data = Blaze.getData(list);
Lists.update(data._id, {
$set: {
sort: i
}
});
}
);
}
});
var lists = this.find('.js-lists');
// 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) {
this.openNewListForm();
// We want to animate the card details window closing. We rely on CSS
// transition for the actual animation.
lists._uihooks = {
removeElement: function(node) {
var removeNode = function() {
node.parentNode.removeChild(node);
};
if ($(node).hasClass('js-card-detail')) {
$(node).css({
flex: '0',
padding: 0
});
$(lists).one(endTransitionEvents, function() {
removeNode();
});
} else {
removeNode();
}
}
};
if (! Meteor.user().isBoardMember())
return;
self.$(lists).sortable({
tolerance: 'pointer',
appendTo: '.js-lists',
helper: 'clone',
items: '.js-list:not(.add-list)',
placeholder: 'list placeholder',
start: function(event, ui) {
$('.list.placeholder').height(ui.item.height());
Popup.close();
},
stop: function() {
self.$('.js-lists').find('.js-list:not(.add-list)').each(
function(i, list) {
var data = Blaze.getData(list);
Lists.update(data._id, {
$set: {
sort: i
}
});
}
);
}
});
// 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) {
this.openNewListForm();
}
},
sidebarSize: function() {
var sidebar = this.componentChildren('boardSidebar')[0];
if (Session.get('currentCard') !== null)
return 'next-large-sidebar';
else if (sidebar && sidebar.isOpen())
return 'next-small-sidebar';
if (sidebar && sidebar.isOpen())
return 'next-sidebar';
}
}).register('boardComponent');

View file

@ -16,13 +16,9 @@
bottom: 0
transition: margin .1s
&.next-small-sidebar
&.next-sidebar
margin-right: 248px
&.next-large-sidebar
opacity: 0.8
margin-right: 496px
.lists
align-items: flex-start
display: flex

View file

@ -10,7 +10,7 @@ setBoardColor(color)
background-color: color
& .minicard.is-selected .minicard-details
border-bottom: 2px solid color
border-left: 3px solid color
button[type=submit].primary, input[type=submit].primary
background-color: darken(color, 20%)

View file

@ -1,6 +1,6 @@
Meteor.subscribe('boards');
BoardSubsManager = new SubsManager();
var boardSubsManager = new SubsManager();
Router.route('/boards', {
name: 'Boards',
@ -17,6 +17,7 @@ Router.route('/boards/:_id/:slug', {
name: 'Board',
template: 'board',
onAfterAction: function() {
// XXX We probably shouldn't rely on Session
Session.set('sidebarIsOpen', true);
Session.set('currentWidget', 'home');
Session.set('menuWidgetIsOpen', false);
@ -26,9 +27,31 @@ Router.route('/boards/:_id/:slug', {
Session.set('currentBoard', params._id);
Session.set('currentCard', null);
return BoardSubsManager.subscribe('board', params._id, params.slug);
return boardSubsManager.subscribe('board', params._id, params.slug);
},
data: function() {
return Boards.findOne(this.params._id);
}
});
Router.route('/boards/:boardId/:slug/:cardId', {
name: 'Card',
template: 'board',
onAfterAction: function() {
Tracker.nonreactive(function() {
if (! Session.get('currentCard') && typeof Sidebar !== 'undefined') {
Sidebar.hide();
}
});
var params = this.params;
Session.set('currentBoard', params.boardId);
Session.set('currentCard', params.cardId);
},
waitOn: function() {
var params = this.params;
return boardSubsManager.subscribe('board', params.boardId, params.slug);
},
data: function() {
return Boards.findOne(this.params.boardId);
}
});