From dea52907bdbed92c95dec7e7e832ac95d9f9d388 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Fri, 5 Jun 2015 21:37:13 +0200 Subject: Start designing the card details pane Implement a dynamic overflow to focus sight on the pane. --- client/components/boards/boardBody.jade | 8 +- client/components/boards/boardBody.js | 8 +- client/components/boards/boardBody.styl | 54 ++-- client/components/boards/boardList.styl | 11 - client/components/boards/colors.styl | 6 +- client/components/cards/details.jade | 112 ++++++-- client/components/cards/details.js | 107 ++++---- client/components/cards/details.styl | 96 ++++--- client/components/cards/labels.styl | 45 +--- client/components/cards/minicard.js | 8 + client/components/cards/minicard.styl | 36 +-- client/components/cards/popups.jade | 11 - client/components/cards/templates.html | 54 ++-- client/components/forms/forms.styl | 3 + client/components/forms/inlinedform.js | 2 +- client/components/lists/body.jade | 3 +- client/components/lists/header.jade | 13 +- client/components/lists/main.styl | 33 +-- client/components/sidebar/sidebar.jade | 8 +- client/components/sidebar/sidebar.styl | 85 +----- client/components/users/userAvatar.jade | 3 +- client/components/users/userAvatar.styl | 7 + client/styles/main.styl | 441 +------------------------------- 23 files changed, 304 insertions(+), 850 deletions(-) delete mode 100644 client/components/cards/popups.jade (limited to 'client') diff --git a/client/components/boards/boardBody.jade b/client/components/boards/boardBody.jade index c3043327..12fc0e36 100644 --- a/client/components/boards/boardBody.jade +++ b/client/components/boards/boardBody.jade @@ -12,6 +12,8 @@ template(name="boardComponent") class=sidebarSize class="{{#if MultiSelection.isActive}}is-multiselection-active{{/if}}" class="{{#if draggingActive.get}}is-dragging-active{{/if}}") + if showOverlay.get + .board-overlay .lists.js-lists each lists +list(this) @@ -28,10 +30,10 @@ template(name="addListForm") +inlinedForm(autoclose=false) input.list-name-input(type="text" placeholder="{{_ 'add-list'}}" autocomplete="off" autofocus) - div.edit-controls.clearfix - button.primary.confirm.js-save-edit(type="submit") {{_ 'save'}} + .edit-controls.clearfix + button.primary.confirm(type="submit") {{_ 'save'}} a.fa.fa-times-thin.js-close-inlined-form else - .js-open-inlined-form + a.js-open-inlined-form i.fa.fa-plus | {{_ 'add-list'}} diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index b5e4154a..6de7fba6 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -14,12 +14,16 @@ BlazeComponent.extendComponent({ onCreated: function() { this.draggingActive = new ReactiveVar(false); + this.showOverlay = new ReactiveVar(false); }, openNewListForm: function() { this.componentChildren('addListForm')[0].open(); }, + // XXX Flow components allow us to avoid creating these two setter methods by + // exposing a public API to modify the component state. We need to investigate + // best practices here. setIsDragging: function(bool) { this.draggingActive.set(bool); }, @@ -60,9 +64,9 @@ BlazeComponent.extendComponent({ var removeNode = _.once(function() { node.parentNode.removeChild(node); }); - if ($(node).hasClass('js-card-detail')) { + if ($(node).hasClass('js-card-details')) { $(node).css({ - flex: '0 0 0', + flexBasis: 0, padding: 0 }); $(lists).one(endTransitionEvents, removeNode); diff --git a/client/components/boards/boardBody.styl b/client/components/boards/boardBody.styl index 281199e2..68034a16 100644 --- a/client/components/boards/boardBody.styl +++ b/client/components/boards/boardBody.styl @@ -1,21 +1,29 @@ @import 'nib' -.board-wrapper - left: 0 - top: 0 - bottom: 0 - right: 0 - position: absolute - overflow: hidden - - .board-canvas +position() + if arguments[0] == cover position: absolute left: 0 right: 0 top: 0 bottom: 0 + else + position: arguments + +.board-wrapper + position: cover + + .board-canvas + position: cover transition: margin .1s + .board-overlay + position: cover + background: black + opacity: 0.33 + animation: fadeIn 0.2s + z-index: 10 + &.next-sidebar margin-right: 248px @@ -25,16 +33,18 @@ .open-minicard-composer display: none -.lists - align-items: flex-start - display: flex - flex-direction: row - margin-bottom: 10px - overflow-x: auto - overflow-y: hidden - padding-bottom: 10px - position: absolute - top: 0 - right: 0 - bottom: 0 - left: 0 + .lists + align-items: flex-start + display: flex + flex-direction: row + margin-bottom: 10px + overflow: auto + padding-bottom: 5px + position: cover + + // In order for the card details pane to overlap the header we need to + // virtually increase this container size with the below hack. (Note that it + // is not possible to set overflow-x: auto, overflow-y: hidden as I + // originally tried). + padding-top: 10px + top: -10px diff --git a/client/components/boards/boardList.styl b/client/components/boards/boardList.styl index c068dbb0..61095d5b 100644 --- a/client/components/boards/boardList.styl +++ b/client/components/boards/boardList.styl @@ -30,17 +30,6 @@ background-size: auto background-repeat: repeat - .details - height: 84px - padding-right: 36px - bottom: 0 - left: 0 - overflow: hidden - padding: 9px 12px - position: absolute - right: 0 - top: 0 - .board-list-item-sub-name color: rgba(255, 255, 255, .5) display: block diff --git a/client/components/boards/colors.styl b/client/components/boards/colors.styl index 1097b20a..ff351880 100644 --- a/client/components/boards/colors.styl +++ b/client/components/boards/colors.styl @@ -2,9 +2,9 @@ // 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. +// value is derived from the same color, doesn't make any sense. We should +// create a mixin/macro that would generate 6 versions of a given property and +// dispatch this list in the other stylus files. setBoardColor(color) &#header, &.sk-spinner div, diff --git a/client/components/cards/details.jade b/client/components/cards/details.jade index 4e8fd8c0..46929f56 100644 --- a/client/components/cards/details.jade +++ b/client/components/cards/details.jade @@ -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 + | ({{ user.username }}) + 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'}} diff --git a/client/components/cards/details.js b/client/components/cards/details.js index 385310bb..d44adb67 100644 --- a/client/components/cards/details.js +++ b/client/components/cards/details.js @@ -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(); - }, - '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'); + var description = this.currentComponent().getValue(); + this.updateCard({ description: description }); }, - '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(); } }); diff --git a/client/components/cards/details.styl b/client/components/cards/details.styl index 6b1a4cd4..4da8a371 100644 --- a/client/components/cards/details.styl +++ b/client/components/cards/details.styl @@ -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 + .card-details-items + display: flex + margin: 15px 0 -@keyframes growIn - from - flex: 0 0 0 - to - flex: 0 0 470px + .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 diff --git a/client/components/cards/labels.styl b/client/components/cards/labels.styl index 9514ce45..c93ecd12 100644 --- a/client/components/cards/labels.styl +++ b/client/components/cards/labels.styl @@ -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 diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 81d8c0d4..8f229be8 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -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')); } }, diff --git a/client/components/cards/minicard.styl b/client/components/cards/minicard.styl index a5110584..ebad8dec 100644 --- a/client/components/cards/minicard.styl +++ b/client/components/cards/minicard.styl @@ -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 diff --git a/client/components/cards/popups.jade b/client/components/cards/popups.jade deleted file mode 100644 index 0d10c147..00000000 --- a/client/components/cards/popups.jade +++ /dev/null @@ -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 - | ({{ user.username }}) - if isCardMember - i.fa.fa-check diff --git a/client/components/cards/templates.html b/client/components/cards/templates.html index 4c65e429..23f9ce4f 100644 --- a/client/components/cards/templates.html +++ b/client/components/cards/templates.html @@ -33,26 +33,6 @@

- -