Start designing the card details pane

Implement a dynamic overflow to focus sight on the pane.
This commit is contained in:
Maxime Quandalle 2015-06-05 21:37:13 +02:00
parent 97807abd70
commit dea52907bd
24 changed files with 305 additions and 850 deletions

View file

@ -1,47 +1,105 @@
template(name="cardDetails")
.card-detail.js-card-detail: .card-detail-canvas
section.card-details.js-card-details: .card-details-canvas
if cover
.card-detail-cover(style="background-image: url({{ card.cover.url }})")
.card-detail-header(class="{{#if currentUser.isBoardMember}}editable{{/if}}")
a.js-close-card-detail
i.fa.fa-times-thin
h2.card-detail-title.js-card-title= title
p.card-detail-list.js-move-card
| {{_ 'in-list'}}
a.card-detail-list-title(
class="{{#if currentUser.isBoardMember}}js-open-move-from-header is-editable{{/if}}")
= list.title
hr
//- if card.members
.card-detail-item.card-detail-item-members.clearfix.js-card-detail-members
h3.card-detail-item-header {{_ 'members'}}
.js-card-detail-members-list.clearfix
.card-details-cover(style="background-image: url({{ cover.url }})")
.card-details-header
+inlinedForm(classNames="js-card-details-title")
input.field.single-line(type="text" value=title autofocus)
.edit-controls.clearfix
button.primary.confirm(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
else
a.fa.fa-angle-left.close-card-details.js-close-card-details
a.fa.fa-bars.card-details-menu.js-open-card-details-menu
h2.card-details-title.js-card-title(
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
= title
p.card-details-list
| {{_ 'in-list'}}
a.card-details-list-title(
class="{{#if currentUser.isBoardMember}}js-move-card is-editable{{/if}}")
= list.title
if currentUser.isBoardMember
i.fa.fa-chevron-down
.card-details-items
.card-details-item.card-details-item-members
h3 {{_ 'members'}}
each members
+userAvatar(userId=this size="small" cardId=../_id)
a.card-detail-item-add-button.dark-hover.js-details-edit-members
a.member.add-member.card-details-item-add-button.js-add-members
i.fa.fa-plus
//- We should use "editable" to avoide repetiting ourselves
.clearfix
.card-details-item.card-details-item-labels
h3 {{_ 'labels'}}
.js-add-labels
each labels
span.card-label(class="card-label-{{color}}" title=name)= name
a.card-label.add-label.js-add-labels
i.fa.fa-plus
//- XXX We should use "editable" to avoide repetiting ourselves
if currentUser.isBoardMember
h3 Description
+inlinedForm(classNames="js-card-description")
a.fa.fa-times-thin.js-close-inlined-form
+editor(autofocus=true)
= description
button(type="submit") {{_ 'edit'}}
.edit-controls.clearfix
button.primary(type="submit") {{_ 'edit'}}
a.fa.fa-times-thin.js-close-inlined-form
else
.js-open-inlined-form
a {{_ 'edit'}}
+viewer
= description
a.js-open-inlined-form
if description
+viewer
= description
else
| {{_ 'edit'}}
else if description
h3 Description
+viewer
= description
hr
if attachments.count
hr
+WindowAttachmentsModule(card=this)
+WindowActivityModule(card=this)
if isLoaded
hr
+WindowActivityModule(card=this)
template(name="cardDetailsActionsPopup")
if currentUser.isBoardMember
ul.pop-over-list
li: a.js-members Edit Members…
li: a.js-labels Edit Labels…
li: a.js-attachments Edit Attachments…
hr
ul.pop-over-list
li: a.js-copy Copy card
li: a.js-archive Archive Card
template(name="moveCardPopup")
+boardLists
template(name="cardMembersPopup")
ul.pop-over-member-list
each board.members
li.item(class="{{#if isCardMember}}active{{/if}}")
a.name.js-select-member(href="#")
+userAvatar(user=user size="small")
span.full-name
= user.profile.name
| (<span class="username">{{ user.username }}</span>)
if isCardMember
i.fa.fa-check
template(name="cardLabelsPopup")
ul.edit-labels-pop-over
each board.labels
li
a.card-label-edit-button.fa.fa-pencil.js-edit-label
span.card-label.card-label-selectable.js-select-label(class="card-label-{{color}}"
class="{{# if isLabelSelected ../_id }}active{{/ if }}")
= name
if currentUser.isBoardAdmin
span.card-label-selectable-icon.fa.fa-check
a.quiet-button.full.js-add-label {{_ 'label-create'}}

View file

@ -8,7 +8,7 @@ BlazeComponent.extendComponent({
},
calculateNextPeak: function() {
var altitude = this.find('.js-card-detail').scrollHeight;
var altitude = this.find('.js-card-details').scrollHeight;
this.callFirstWith(this, 'setNextPeak', altitude);
},
@ -25,77 +25,67 @@ BlazeComponent.extendComponent({
bodyBoardComponent.scrollLeft(scollLeft);
},
onDestroyed: function() {
this.componentParent().showOverlay.set(false);
},
updateCard: function(modifier) {
Cards.update(this.data()._id, {
$set: modifier
});
},
events: function() {
return [{
'click .js-close-card-details': function() {
Utils.goBoardId(this.data().boardId);
},
'click .js-move-card': Popup.open('moveCard'),
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
'submit .js-card-description': function(evt) {
evt.preventDefault();
var cardId = Session.get('currentCard');
var form = this.componentChildren('inlinedForm')[0];
var newDescription = form.getValue();
Cards.update(cardId, {
$set: {
description: newDescription
}
});
form.close();
var description = this.currentComponent().getValue();
this.updateCard({ description: description });
},
'click .js-close-card-detail': function() {
Utils.goBoardId(Session.get('currentBoard'));
},
'click .editable .js-card-title': function(event, t) {
var editable = t.$('.card-detail-title');
// add class editing and focus
$('.editing').removeClass('editing');
editable.addClass('editing');
editable.find('#title').focus();
},
'click .js-edit-desc': function(event, t) {
var editable = t.$('.card-detail-item');
// editing remove based and add current editing.
$('.editing').removeClass('editing');
editable.addClass('editing');
editable.find('#desc').focus();
event.preventDefault();
},
'click .js-cancel-edit': function() {
// remove editing hide.
$('.editing').removeClass('editing');
},
'submit #WindowTitleEdit': function(event, t) {
var title = t.find('#title').value;
'submit .js-card-details-title': function(evt) {
evt.preventDefault();
var title = this.currentComponent().getValue();
if ($.trim(title)) {
Cards.update(this.card._id, {
$set: {
title: title
}
}, function(err) {
if (! err) $('.editing').removeClass('editing');
});
this.updateCard({ title: title });
}
event.preventDefault();
},
'submit #WindowDescEdit': function(event, t) {
Cards.update(this.card._id, {
$set: {
description: t.find('#desc').value
}
}, function(err) {
if (! err) $('.editing').removeClass('editing');
});
event.preventDefault();
'click .js-member': Popup.open('cardMember'),
'click .js-add-members': Popup.open('cardMembers'),
'click .js-add-labels': Popup.open('cardLabels'),
'mouseenter .js-card-details': function() {
this.componentParent().showOverlay.set(true);
},
'click .member': Popup.open('cardMember'),
'click .js-details-edit-members': Popup.open('cardMembers'),
'click .js-details-edit-labels': Popup.open('cardLabels')
'mouseleave .js-card-details': function(evt) {
// We don't want to hide the overlay if the mouse is entering a pop-over
var $pointedElement = $(evt.toElement || evt.relatedTarget);
if ($pointedElement.closest('.pop-over').length === 0)
this.componentParent().showOverlay.set(false);
}
}];
}
}).register('cardDetails');
Template.cardDetailsActionsPopup.events({
'click .js-members': Popup.open('cardMembers'),
'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'),
// 'click .js-copy': Popup.open(),
'click .js-archive': function(evt) {
evt.preventDefault();
Cards.update(this._id, {
$set: {
archived: true
}
});
Popup.close();
}
});
Template.moveCardPopup.events({
'click .js-select-list': function() {
// XXX We should *not* get the currentCard from the global state, but
@ -107,5 +97,6 @@ Template.moveCardPopup.events({
listId: newListId
}
});
Popup.close();
}
});

View file

@ -1,57 +1,73 @@
@import 'nib'
.card-detail
.card-details
padding: 0 20px
height: 100%
flex: 0 0 470px
flex-shrink: 0
flex-basis: 470px
will-change: flex-basis
overflow: hidden
background: white
border-radius: 3px
z-index: 20 !important
animation: growIn 0.2s
animation: flexGrowIn 0.2s
box-shadow: 0 0 7px 0 darken(white, 30%)
transition: flex 0.2s, padding 0.2s
transition: flex-basis 0.2s, padding 0.2s
margin-top: -9px
.card-detail-canvas
.card-details-canvas
width: 470px
.card-detail-header
.card-details-header
margin: 0 -20px 5px
padding 7px 20px 0
background: #F7F7F7
border-bottom: 1px solid darken(white, 10%)
min-height: 38px
position: relative
i.fa
.close-card-details
float: left
font-size: 24px
padding: 8px
padding-right: 11px
margin-left: -18px
.card-details-menu
float: right
font-size: 1.3em
color: darken(white, 35%)
margin-top: 7px
position: absolute
bottom: 6px
right: 15px
.card-detail-title
.card-details-title
font-weight: bold
font-size: 1.7em
margin: 3px 0 0
font-size: 1.33em
margin: 3px 0
padding: 0
.card-detail-list
font-size: 0.85em
margin-bottom: 3px
.card-details-list
font-size: 0.85em
margin-bottom: 3px
a.card-detail-list-title
font-weight: bold
a.card-details-list-title
font-weight: bold
&.is-editable
display: inline-block
background: darken(white, 10%)
border-radius: 3px
padding: 0px 5px
&.is-editable
display: inline-block
background: darken(white, 10%)
border-radius: 3px
padding: 0px 5px
@keyframes growIn
from
flex: 0 0 0
to
flex: 0 0 470px
.card-details-items
display: flex
margin: 15px 0
.card-details-item
flex-grow: 1
h3
font-size: 14px
color: darken(white, 45%)
.new-comment
position: relative
@ -107,30 +123,6 @@
&:focus
cursor: auto
.list-voters.compact .voter
position: relative
min-height: 36px
.member
left: 0
position: absolute
top: 0
.title
display: block
line-height: 30px
left: 0
overflow: hidden
padding-left: 38px
position: absolute
text-overflow: ellipsis
top: 0
white-space: nowrap
width: 230px
.list-voters .title
display: none
.card-composer
padding-bottom: 8px

View file

@ -24,6 +24,10 @@
width: @height
padding: 0
&.add-label
box-shadow: 0 0 0 2px darken(white, 25%) inset
background: darken(white, 5%)
.card-label-green
background-color: #3cb500
@ -84,31 +88,6 @@
left: 0
width: 260px
.minicard-labels
position: relative
z-index: 30
top: -6px
.card-label
border-radius: 0
float: left
height: 4px
margin-bottom: 1px
padding: 0
width: 40px
line-height: 100px
.card-detail-item-labels .card-label
border-radius: 3px
display: block
float: left
height: 20px
line-height: 20px
margin: 0 4px 4px 0
min-width: 30px
padding: 5px 10px
width: auto
.editable-labels .card-label:hover
cursor: pointer
opacity: .75
@ -170,19 +149,3 @@
&:hover
background: #dbdbdb
.card-label-color-select-icon
left: 14px
position: absolute
top: 9px
.phenom .card-label
display: inline-block
font-size: 12px
height: 14px
line-height: 13px
padding: 0 4px
min-width: 16px
overflow: ellipsis
.board-widget .phenom .card-label
max-width: 130px

View file

@ -23,6 +23,14 @@ BlazeComponent.extendComponent({
evt.preventDefault();
var methodName = evt.shiftKey ? 'toogleRange' : 'toogle';
MultiSelection[methodName](this.currentData()._id);
// If the card is already selected, we want to de-select it.
// XXX We should probably modify the minicard href attribute instead of
// overwriting the event in case the card is already selected.
} else if (Session.equals('currentCard', this.currentData()._id)) {
evt.stopImmediatePropagation();
evt.preventDefault();
Utils.goBoardId(Session.get('currentBoard'));
}
},

View file

@ -43,14 +43,13 @@
color: #4d4d4d
overflow: hidden
transition: transform 0.2s,
border-radius 0.2s,
border-left 0.2s
border-radius 0.2s
.is-selected &
transform: translateX(11px)
border-bottom-right-radius: 0
border-top-right-radius: 0
z-index: 100
z-index: 25
box-shadow: -2px 1px 2px rgba(0,0,0,.2)
.minicard-cover
@ -66,56 +65,33 @@
background-size: auto
background-position: center
.minicard-details-overlay
background: transparent
bottom: 0
left: 0
position: absolute
right: 0
top: 0
.minicard-dropzone
display: none
.minicard.drophover .minicard-dropzone
background: rgba(255, 255, 255, .8)
// border-radius: 3px
// bottom: 0
// display: block
// font-weight: 700
// line-height: 100%
// left: 0
// margin: 0
// opacity: 1
// padding: 0
// position: absolute
// right: 0
// text-align: center
// top: 0
// z-index: 40
.minicard-title
display: block
font-weight: 400
margin: 0 0 4px
overflow: hidden
margin-bottom: 2px
text-decoration: none
word-wrap: break-word
clear: both
&::selection
background: transparent
.minicard-labels
padding-top: 3px
margin-top: 4px
float: right
.minicard-label
float: right
width: 8px
width: 11px
height: @width
border-radius: 2px
margin-left: 4px
margin-right: 3px
.minicard-members
float: right

View file

@ -1,11 +0,0 @@
template(name="cardMembersPopup")
ul.pop-over-member-list.js-mem-list
each board.members
li.item(class="{{#if isCardMember}}active{{/if}}")
a.name.js-select-member(href="#")
+userAvatar(user=user size="small")
span.full-name
= user.profile.name
| (<span class="username">{{ user.username }}</span>)
if isCardMember
i.fa.fa-check

View file

@ -33,26 +33,6 @@
</p>
</template>
<template name="cardLabelsPopup">
<div>
{{! <input id="labelSearch" name="search" class="js-autofocus js-label-search" placeholder="Search labels…" value="" type="text"> }}
<ul class="edit-labels-pop-over js-labels-list">
{{# each card.board.labels }}
<li>
<a href="#" class="card-label-edit-button icon-sm fa fa-pencil js-edit-label"></a>
<span class="card-label card-label-selectable card-label-{{color}} js-select-label {{# if isLabelSelected ../card._id }}active{{/ if }}">
{{name}}
{{# if currentUser.isBoardAdmin }}
<span class="card-label-selectable-icon icon-sm fa fa-check light"></span>
{{/ if }}
</span>
</li>
{{/ each}}
</ul>
<a class="quiet-button full js-add-label">{{_ 'label-create'}}</a>
</div>
</template>
<template name="cardAttachmentsPopup">
<div>
<ul class="pop-over-list">
@ -114,7 +94,7 @@
</template>
<template name="cardDetailSidebarOld">
<div class="card-detail-window clearfix">
<div class="card-details-window clearfix">
{{# if card.cover }}
<div class="window-cover js-card-cover-box js-open-card-cover-in-viewer has-cover" style="background-image: url({{ card.cover.url }}); background-color: rgb(119, 119, 119); background-size: contain;">
</div>
@ -127,7 +107,7 @@
{{ /if }}
<div class="window-header clearfix">
<span class="window-header-icon icon-lg fa fa-calendar-o"></span>
<div class="window-title card-detail-title non-empty inline {{# if currentUser.isBoardMember }}editable{{/ if }}">
<div class="window-title card-details-title non-empty inline {{# if currentUser.isBoardMember }}editable{{/ if }}">
<h2 class="window-title-text current hide-on-edit js-card-title">{{ card.title }}</h2>
<div class="edit edit-heavy">
<form id="WindowTitleEdit">
@ -147,39 +127,39 @@
</div>
</div>
<div class="window-main-col clearfix">
<div class="card-detail-data gutter clearfix">
<div class="card-detail-item card-detail-item-block clear clearfix editable">
<div class="card-details-data gutter clearfix">
<div class="card-details-item card-details-item-block clear clearfix editable">
{{# if card.members }}
<div class="card-detail-item card-detail-item-members clearfix js-card-detail-members">
<h3 class="card-detail-item-header">{{_ 'members'}}</h3>
<div class="js-card-detail-members-list clearfix">
<div class="card-details-item card-details-item-members clearfix js-card-details-members">
<h3 class="card-details-item-header">{{_ 'members'}}</h3>
<div class="js-card-details-members-list clearfix">
{{# each card.members }}
{{> userAvatar userId=this size="small" cardId=../card._id }}
{{/ each }}
<a class="card-detail-item-add-button dark-hover js-details-edit-members">
<a class="card-details-item-add-button dark-hover js-details-edit-members">
<span class="icon-sm fa fa-plus"></span>
</a>
</div>
</div>
{{/ if }}
{{# if card.labels }}
<div class="card-detail-item card-detail-item-labels clearfix js-card-detail-labels">
<h3 class="card-detail-item-header">{{_ 'labels'}}</h3>
<div class="js-card-detail-labels-list clearfix editable-labels js-edit-label">
<div class="card-details-item card-details-item-labels clearfix js-card-details-labels">
<h3 class="card-details-item-header">{{_ 'labels'}}</h3>
<div class="js-card-details-labels-list clearfix editable-labels js-edit-label">
{{# each card.labels }}
<span class="card-label card-label-{{color}}" title="{{name}}">{{ name }}</span>
{{/ each }}
<a class="card-detail-item-add-button dark-hover js-details-edit-labels">
<a class="card-details-item-add-button dark-hover js-details-edit-labels">
<span class="icon-sm fa fa-plus"></span>
</a>
</div>
</div>
{{/ if }}
<div class="card-detail-item card-detail-item-block clear clearfix editable" attr="desc">
<div class="card-details-item card-details-item-block clear clearfix editable" attr="desc">
{{# if card.description }}
<h3 class="card-detail-item-header js-show-with-desc">{{_ 'description'}}</h3>
<h3 class="card-details-item-header js-show-with-desc">{{_ 'description'}}</h3>
{{# if currentUser.isBoardMember }}
<a href="#" class="card-detail-item-header-edit hide-on-edit js-show-with-desc js-edit-desc">{{_ 'edit'}}</a>
<a href="#" class="card-details-item-header-edit hide-on-edit js-show-with-desc js-edit-desc">{{_ 'edit'}}</a>
{{/ if }}
<div class="current markeddown hide-on-edit js-card-desc js-show-with-desc">
{{#viewer}}{{ card.description }}{{/viewer}}
@ -194,7 +174,7 @@
</p>
{{/ if }}
{{/ if }}
<div class="card-detail-edit edit">
<div class="card-details-edit edit">
<form id="WindowDescEdit">
{{#editor class="field single-line2" id="desc"}}{{ card.description }}{{/editor}}
<div class="edit-controls clearfix">
@ -218,7 +198,7 @@
</template>
<template name="WindowActivityModule">
<div class="card-detailwindow-module">
<div class="card-detailswindow-module">
<div class="window-module-title window-module-title-no-divider">
<span class="window-module-title-icon icon-lg fa fa-comments-o"></span>
<h3>{{ _ 'activity'}}</h3>