Browse Source

Start designing the card details pane

Implement a dynamic overflow to focus sight on the pane.
Maxime Quandalle 10 years ago
parent
commit
dea52907bd

+ 5 - 3
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'}}

+ 6 - 2
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);

+ 32 - 22
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

+ 0 - 11
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

+ 3 - 3
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,

+ 85 - 27
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
+            | (<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'}}

+ 49 - 58
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();
   }
 });

+ 44 - 52
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
 

+ 4 - 41
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

+ 8 - 0
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'));
     }
   },
 

+ 6 - 30
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

+ 0 - 11
client/components/cards/popups.jade

@@ -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

+ 17 - 37
client/components/cards/templates.html

@@ -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>

+ 3 - 0
client/components/forms/forms.styl

@@ -479,6 +479,9 @@ button
     color: #53492d
     background: #e1cc93
 
+.is-editable
+  cursor: pointer
+
 .big-link
   border-radius: 3px
   display: block

+ 1 - 1
client/components/forms/inlinedform.js

@@ -76,7 +76,7 @@ BlazeComponent.extendComponent({
       // Pressing Ctrl+Enter should submit the form
       'keydown form textarea': function(evt) {
         if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
-          $(evt.currentTarget).parents('form:first').submit();
+          this.find('button[type=submit]').click();
         }
       },
 

+ 1 - 2
client/components/lists/body.jade

@@ -16,8 +16,7 @@ template(name="listBody")
 
 template(name="addCardForm")
   .minicard.minicard-composer.js-composer
-    .minicard-labels.js-minicard-composer-labels
-    .minicard-details.clearfix
+    .minicard-detailss.clearfix
       textarea.minicard-composer-textarea.js-card-title(autofocus)
       .minicard-members.js-minicard-composer-members
   .add-controls.clearfix

+ 8 - 5
client/components/lists/header.jade

@@ -3,11 +3,14 @@ template(name="listHeader")
     +inlinedForm
       +editListTitleForm
     else
-      h2.list-header-name.js-open-inlined-form= title
+      h2.list-header-name(
+        class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
+        = title
       a.list-header-menu-icon.fa.fa-bars.js-open-list-menu
 
 template(name="editListTitleForm")
-  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
+  .list-composer
+    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

+ 8 - 25
client/components/lists/main.styl

@@ -8,7 +8,7 @@
   position: relative
   // Even if this background color is the same as the body we can't leave it
   // transparent, because that won't work during a list drag.
-  background: darken(white, 10%)
+  background: darken(white, 13%)
   height: 100%
   border-left: 1px solid darken(white, 20%)
   padding: 0
@@ -17,7 +17,7 @@
     margin-left: 5px
     border-left: none
 
-  .card-detail + &
+  .card-details + &
     border-left: none
 
   &.ui-sortable-helper
@@ -32,13 +32,15 @@
     box-shadow: none
     height: 100px
 
-  &.list-composer
+  &.list-composer, & list-composer
     padding: 17px
 
+    form
+      margin-top: -5px
+
     .list-name-input
-      background: rgba(0, 0, 0, .05)
+      background: rgba(255, 255, 255, .4)
       border-color: #aaa
-      box-shadow: inset 0 1px 8px rgba(0, 0, 0, .15)
       display: block
       margin: 0
       transition: margin 85ms ease-in,
@@ -52,14 +54,9 @@
       overflow: hidden
       margin: 4px 0 0
 
-      input[type=submit]
-        margin-top: 0
-        min-height: 30px
-        height: 30px
-
 .list-header
   flex: 0 0 auto
-  margin: 20px 15px 4px
+  margin: 20px 12px 4px
   position: relative
   min-height: 20px
 
@@ -76,17 +73,9 @@
     word-wrap: break-word
 
   .list-header-menu-icon
-    background-clip: content-box
-    background-origin: content-box
-    // padding: 6px 8px
     position: absolute
     top: 0
     right: 0
-    color: #a6a6a6
-
-  .list-header-num-cards
-    color: #8c8c8c
-    margin: 0
 
 .list-body
   flex: 1
@@ -119,9 +108,3 @@
       background: #fafafa
       color: #222
       box-shadow: 0 1px 2px rgba(0,0,0,.2)
-
-@keyframes fadeIn
-  from
-    opacity: 0
-  to
-    opacity: 1

+ 4 - 4
client/components/sidebar/sidebar.jade

@@ -2,7 +2,7 @@ template(name="sidebar")
   .board-sidebar.sidebar(class="{{#if isOpen}}is-open{{/if}}")
     a.sidebar-tongue.js-toogle-sidebar(
       class="{{#if isTongueHidden}}is-hidden{{/if}}")
-      i.fa.fa-chevron-left
+      i.fa.fa-angle-left
     .sidebar-content.js-board-sidebar-content.js-perfect-scrollbar
       unless isDefaultView
         h2
@@ -44,9 +44,9 @@ template(name="labelsWidget")
       | {{_ 'labels'}}
     .board-widget-content
       each currentBoard.labels
-        a.card-label(class="card-label-{{color}}").js-label
-          span.card-label-name= name
-      a.card-label.js-add-label
+          a.card-label(class="card-label-{{color}}").js-label
+            span.card-label-name= name
+      a.card-label.add-label.js-add-label
         i.fa.fa-plus
 
 template(name="memberPopup")

+ 2 - 83
client/components/sidebar/sidebar.styl

@@ -59,88 +59,6 @@
   &.is-open
     right: 0
 
-.board-widget-nav
-  border-radius: 3px
-  background: #dcdcdc
-  overflow: hidden
-  padding: 0
-  position: relative
-
-  .toggle-widget-nav
-    border-radius: 3px
-    color: #8c8c8c
-    margin: 0
-    padding: 7px 10px
-    position: relative
-    cursor: pointer
-
-    .toggle-menu-icon
-      position: absolute
-      top: 8px
-      right: 8px
-
-    &:hover
-      background: #ccc
-      color: #4d4d4d
-
-  .nav-list
-    display: block
-    opacity: 1
-    max-height: 400px
-
-    hr
-      margin: 2px 0
-      color: #ccc
-      background: #ccc
-
-  .nav-list-item
-    display: block
-    font-weight: 700
-    line-height: 30px
-    overflow: hidden
-    padding: 0 8px 0 36px
-    position: relative
-    text-decoration: none
-
-    .icon-type
-      left: 10px
-      position: absolute
-      top: 6px
-
-    &:hover
-      background: #ccc
-
-      .icon-type
-        color: #686868
-
-  .nav-list-sub-item
-    font-weight: 400
-    color: #666
-
-    &:hover
-      color: #4d4d4d
-
-  &.collapsed
-
-    .nav-list
-      max-height: 0
-      opacity: 0
-
-      hr
-        margin: 0
-
-    .toggle-widget-nav
-      color: #4d4d4d
-
-.board-widget-title
-  display: block
-  min-height: 20px
-  margin-bottom: 6px
-
-.board-widget-content
-  position: relative
-  z-index: 1
-
 .board-widget h4
   margin: 5px 0
 
@@ -162,7 +80,8 @@
   transition: left .1s
 
   i.fa
-    margin: 9px
+    padding: 3px 9px
+    font-size: 24px
     transition: transform 0.5s
 
   .board-sidebar.is-open &

+ 1 - 2
client/components/users/userAvatar.jade

@@ -1,6 +1,5 @@
 template(name="userAvatar")
-  .member(class="{{class}} {{# if draggable }}js-member{{else}}js-member-on-card-menu{{/if}}"
-    title="{{userData.profile.name}} ({{userData.username}})")
+  .member.js-member(class="{{class}}" title="{{userData.profile.name}} ({{userData.username}})")
     +avatar(user=userData size=size)
     if showStatus
       span.member-presence-status(class=presenceStatusClassName)

+ 7 - 0
client/components/users/userAvatar.styl

@@ -96,6 +96,13 @@ avatar-radius = 50%
         line-height: 85px
         width: 85px
 
+  &.add-member
+    display: flex
+    align-items: center
+    justify-content: center
+    box-shadow: 0 0 0 2px darken(white, 25%) inset
+    background: darken(white, 5%)
+
 .atMention
   background: #dbdbdb
   border-radius: 3px

+ 10 - 431
client/styles/main.styl

@@ -13,7 +13,7 @@ html
   -webkit-text-size-adjust: 100%
 
 body
-  background: darken(white, 10%)
+  background: darken(white, 13%)
   margin: 0
   position: relative
   z-index: 0
@@ -23,6 +23,7 @@ body
   display: flex
   flex-direction: column
   min-height: 100vh
+  overflow: hidden
 
 #content
   position: relative
@@ -216,12 +217,6 @@ dd
     top: 1px
     left: -38px
 
-  .helper
-    bottom: 0
-    display: none
-    position: absolute
-    right: 9px
-
   &.focus
     .member
       opacity: 1
@@ -259,368 +254,6 @@ dd
   &:focus
     cursor: auto
 
-.editing-members
-  float: right
-
-  .edit-in-progress
-    display: inline-block
-    border: 1px solid #ccc
-    background: #ddd
-    margin: 0 4px
-    border-radius: 2px
-
-    .inline-member
-      cursor: default
-
-    .inline-member-av
-      width: 18px
-      height: 18px
-      margin: 0 0 -4px 0
-
-    .initials
-      margin-left: 3px
-
-    .icon
-      animation: pulsate 1s ease-in alternate
-      animation-iteration-count: infinite
-
-@keyframes pulsate
-  0%
-    opacity: 1
-
-  to
-    opacity: .4
-
-.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
-
-.card-detail-badge
-  background-color: #dbdbdb
-  border-radius: 3px
-  color: #737373
-  cursor: default
-  display: block
-  height: 20px
-  line-height: 20px
-  margin: 0 4px 4px 0
-  padding: 5px 10px
-  text-align: center
-  text-decoration: none
-
-  &:hover
-    color: #737373
-
-  &.badge-state-clickable
-    text-decoration: underline
-
-.badge-state-clickable:hover
-  color: #262626
-  cursor: pointer
-  text-decoration: underline
-
-.card-detail-badge-aging:first-letter
-  text-transform: uppercase
-
-.badge
-  color: #8c8c8c
-  float: left
-  height: 18px
-  margin: 0 3px 3px 0
-  padding: 0 4px 0 0
-  position: relative
-  text-decoration: none
-
-.badge-icon
-  float: left
-
-.badge-text
-  float: left
-  font-size: 12px
-
-.badge-state-image-only
-  padding: 0
-
-  .badge-icon
-    margin-right: 0
-
-.badge-state-clickable
-  cursor: pointer
-
-  .badge-text
-    text-decoration: underline
-
-.badge-state-complete
-  background-color: #4aba12
-  border-radius: 3px
-  color: #fff
-
-  .badge-icon
-    color: #fff
-
-.badge-state-unread-notification
-  background-color: #990f0f
-  border-radius: 3px
-  color: #fff
-
-  .badge-icon
-    color: #fff
-
-.badge-state-voted
-  background-color: #dbdbdb
-  border-radius: 3px
-  color: #8c8c8c
-
-  .badge-icon
-    color: #999
-
-.badge-state-due-soon, .badge-state-due-soon:hover
-  background-color: #e6bf00
-  border-radius: 3px
-  color: #fff
-
-  .badge-icon
-    color: #fff
-
-.badge-state-due-now, .badge-state-due-now:hover
-  background-color: #990f0f
-  border-radius: 3px
-  color: #fff
-
-  .badge-icon
-    color: #fff
-
-.badge-state-due-past, .badge-state-due-past:hover
-  background-color: #ad8585
-  border-radius: 3px
-  color: #fff
-
-  .badge-icon
-    color: #fff
-
-.checklist-list:empty
-  display: none
-
-.checklist
-  margin-bottom: 16px
-
-.checklist.placeholder
-  background: #dcdcdc
-  border-radius: 3px
-
-.checklist.ui-sortable-helper
-  background: rgba(240, 240, 240, .85)
-  border-radius: 3px
-
-  .checklist-title,
-  .current,
-  .window-module-title
-    cursor: grabbing
-
-  .icon-menu
-    visibility: hidden
-
-.checklist-items-list
-  min-height: 2px
-
-.checklist-item
-  clear: both
-  margin: 0 0 6px
-  padding: 0 0 4px 38px
-  position: relative
-  transform-origin: left bottom
-  transition-property: transform, opacity, height, padding, margin
-  transition-duration: .14s
-  transition-timing-function: ease-in
-
-  &.placeholder
-    background: #dcdcdc
-    border-radius: 3px
-    margin: -5px -5px 5px 5px
-    padding: 5px 0
-
-  &.ui-sortable-helper
-    background: rgba(240, 240, 240, .85)
-    border-radius: 3px
-    margin: -3px -3px -3px 7px
-    padding: 3px 3px 3px 33px
-
-    .checklist-item-checkbox
-      top: 2px
-      left: 2px
-
-.hide-completed-items .checklist-item-fade-out
-  height: 0
-  margin: 0
-  opacity: 0
-  padding: 0
-  transform: rotate(-5deg) translateX(-10px) translateY(-10px)
-
-.checklist-item-checkbox
-  background: #fff
-  border-radius: 3px
-  box-shadow: 0 2px 3px rgba(0, 0, 0, .1)
-  border: 1px solid #ccc
-  border-bottom-color: #b3b3b3
-  font-weight: 700
-  position: absolute
-  left: 6px
-  line-height: 18px
-  overflow: hidden
-  text-align: center
-  text-indent: 100%
-  top: -2px
-  height: 18px
-  width: 18px
-  white-space: nowrap
-
-  &.enabled:hover
-    background-color: #f0f0f0
-    border-color: #ccc
-    box-shadow: 0 1px 2px rgba(0, 0, 0, .1)
-    color: #8c8c8c
-    cursor: pointer
-    text-indent: 0
-
-  &.enabled:active
-    background-color: #e3e3e3
-    border-color: #ccc
-    box-shadow: inset 0 3px 6px rgba(0, 0, 0, .1)
-    color: #4d4d4d
-    text-indent: 0
-
-.checklist-item-details-text
-  min-height: 18px
-  margin-bottom: 0
-
-  &.enabled:hover
-    color: #4d4d4d
-    cursor: pointer
-
-  &:empty
-    content: "No name"
-    color: #8c8c8c
-
-.checklist-item-state-complete
-
-  .checklist-item-details-text
-    color: #8c8c8c
-    font-style: italic
-    text-decoration: line-through
-
-  img
-    opacity: .3
-
-  .checklist-item-checkbox
-    background-color: #f0f0f0
-    border-color: #dbdbdb
-    border-bottom-color: #ccc
-    box-shadow: none
-    text-indent: 0
-
-    &.enabled:hover
-      background-color: #e6e6e6
-      border-color: #ccc
-      box-shadow: none
-
-    &.enabled:active
-      background-color: #dbdbdb
-      box-shadow: inset 0 3px 6px rgba(0, 0, 0, .1)
-
-.hide-completed-items .checklist-item-state-complete
-  display: none
-
-.checklist-new-item-text,
-.checklist-new-item-text:hover
-  background: transparent
-  border-color: transparent
-  box-shadow: none
-  color: #8c8c8c
-  cursor: pointer
-  margin-bottom: 4px
-  max-height: 32px
-  overflow: hidden
-  resize: none
-  text-decoration: none
-
-  .checklist-new-item.focus &
-    background: #fff
-    border-color: #2b7cab
-    box-shadow: 0 0 3px #2b7cab
-    color: #4d4d4d
-    cursor: text
-    max-height: none
-    resize: vertical
-
-.checklist-progress
-  margin-bottom: 12px
-  position: relative
-
-.checklist-progress-percentage
-  color: #8c8c8c
-  font-size: 11px
-  line-height: 10px
-  position: absolute
-  left: 0
-  top: -1px
-  text-align: center
-  width: 38px
-
-.checklist-progress-bar
-  background: #dbdbdb
-  border-radius: 3px
-  clear: both
-  height: 8px
-  margin: 0 0 0 38px
-  overflow: hidden
-  position: relative
-
-.checklist-progress-bar-current
-  background: #479fd1
-  background: linear-gradient(to bottom, #479fd1 0, #2288c3 100%)
-  bottom: 0
-  left: 0
-  position: absolute
-  top: 0
-  transition: width .14s ease-in, background .14s ease-in
-
-.checklist-progress-bar-current-complete
-  background: #24a828
-
-.checklist-completed-text
-  display: block
-  margin: 8px 0 0 38px
-
-.checklist .edit
-  clear: both
-  margin-top: -5px
-
-.explorer .av-btn
-  background: url(about:blank)
-
 .atMention
   background: #dbdbdb
   border-radius: 3px
@@ -631,45 +264,6 @@ dd
   &.me
     background: #cfdfe8
 
-.helper
-  background-color: #e6e6e6
-  border-radius: 3px
-  color: #8c8c8c
-  font-size: 13px
-  line-height: 15px
-  margin: 4px 0 0
-  padding: 6px 8px
-  width: auto
-
-  a
-    color: #8c8c8c
-
-    &:hover
-      color: #666
-
-.empty-list, .empty
-  background: #e6e6e6
-  border: 1px dashed #ccc
-  border-radius: 3px
-  color: #8c8c8c
-  display: block
-  padding: 6px
-  text-align: center
-
-.empty-list
-  border-radius: 6px
-  padding: 25px 6px
-
-.search-results-page-contents .empty-list
-  margin: 12px 0 0 52px
-
-.window-module .empty-list
-  margin: 8px 0 0 38px
-
-.loading
-  margin: 19px auto
-  text-align: center
-
 .big-message
   display: block
   margin: 75px auto
@@ -684,28 +278,13 @@ dd
     font-size: 18px
     line-height: 22px
 
-  &.with-picture
-    margin-top: 35px
-
-    h1
-      margin-top: 20px
-
-    .callout
-      margin: 20px 0
-
-.callout
-  background: #e3e3e3
-  border-radius: 5px
-  padding: 20px
-
-  ol
-    text-align: left
-    list-style-type: decimal
-    margin-left: 25px
-    font-size: 16px
-
-  li
-    margin: 10px 0
-
 .gutter
   margin-left: 38px
+
+@keyframes fadeIn
+  from
+    opacity: 0
+
+@keyframes flexGrowIn
+  from
+    flex-basis: 0

+ 1 - 0
i18n/en.i18n.json

@@ -188,5 +188,6 @@
     "editProfilePopup-title": "Edit Profile",
     "changeAvatarPopup-title": "Change Avatar",
     "changePasswordPopup-title": "Change Password",
+    "cardDetailsActionsPopup-title": "Card Actions",
     "disambiguateMultiLabelPopup-title": "Disambiguate Label Action"
 }