Răsfoiți Sursa

UI improvements

* Implement visibility choice on board creation;

* Rework the board header bar. Remove links to un-implemented
features;

* Implement a board star counter (visible if the board have >2 stars);

* Define a new icon (a thin cross) to close elements;

* Remove $(document).on('mouseover') event handlers that were
basically fired hundreds of times for nothing, we now define a proper
Tracker dependency to execute jquery-ui plugin initialization only
when something has changed;

* Bug fixes related to list scrolling.
Maxime Quandalle 10 ani în urmă
părinte
comite
dcc64f44f9
51 a modificat fișierele cu 644 adăugiri și 990 ștergeri
  1. 1 1
      client/components/boards/boardBody.jade
  2. 0 0
      client/components/boards/boardBody.js
  3. 0 0
      client/components/boards/boardBody.styl
  4. 94 0
      client/components/boards/boardHeader.jade
  5. 157 0
      client/components/boards/boardHeader.js
  6. 7 0
      client/components/boards/boardList.jade
  7. 34 0
      client/components/boards/boardList.js
  8. 0 0
      client/components/boards/boardList.styl
  9. 14 1
      client/components/boards/colors.styl
  10. 0 96
      client/components/boards/events.js
  11. 0 87
      client/components/boards/header.jade
  12. 0 7
      client/components/boards/header.js
  13. 0 137
      client/components/boards/header.styl
  14. 0 39
      client/components/boards/helpers.js
  15. 2 2
      client/components/cards/details.jade
  16. 0 61
      client/components/cards/events.js
  17. 27 0
      client/components/cards/minicard.jade
  18. 14 0
      client/components/cards/minicard.js
  19. 3 20
      client/components/forms/forms.styl
  20. 4 3
      client/components/forms/inlinedform.js
  21. 2 27
      client/components/lists/body.jade
  22. 16 18
      client/components/lists/body.js
  23. 49 38
      client/components/lists/main.js
  24. 72 212
      client/components/main/header.styl
  25. 18 8
      client/components/main/popup.js
  26. 4 75
      client/components/main/popup.styl
  27. 3 2
      client/components/main/popup.tpl.jade
  28. 26 10
      client/components/sidebar/sidebar.js
  29. 8 12
      client/components/users/headerButtons.jade
  30. 0 19
      client/lib/utils.js
  31. 28 0
      client/styles/icons.styl
  32. 13 80
      client/styles/main.styl
  33. 3 2
      collections/boards.js
  34. 9 0
      collections/users.js
  35. 2 2
      i18n/ar.i18n.json
  36. 2 2
      i18n/br.i18n.json
  37. 2 2
      i18n/cm.i18n.json
  38. 2 2
      i18n/cn.i18n.json
  39. 2 2
      i18n/cs.i18n.json
  40. 2 2
      i18n/de.i18n.json
  41. 5 2
      i18n/en.i18n.json
  42. 2 2
      i18n/es.i18n.json
  43. 2 2
      i18n/fi.i18n.json
  44. 2 2
      i18n/fr.i18n.json
  45. 2 2
      i18n/hk.i18n.json
  46. 2 2
      i18n/id.i18n.json
  47. 2 2
      i18n/ja.i18n.json
  48. 2 2
      i18n/ko.i18n.json
  49. 2 2
      i18n/ru.i18n.json
  50. 2 2
      i18n/tr.i18n.json
  51. 1 1
      sandstorm.js

+ 1 - 1
client/components/boards/body.jade → client/components/boards/boardBody.jade

@@ -27,7 +27,7 @@ template(name="addListForm")
         autocomplete="off" autofocus value=getCache)
       div.edit-controls.clearfix
         button.primary.confirm.js-save-edit(type="submit") {{_ 'save'}}
-        a.fa.fa-times.dark-hover.cancel.js-close-inlined-form
+        a.fa.fa-times-thin.js-close-inlined-form
     else
       .js-open-inlined-form
         i.fa.fa-plus

+ 0 - 0
client/components/boards/body.js → client/components/boards/boardBody.js


+ 0 - 0
client/components/boards/body.styl → client/components/boards/boardBody.styl


+ 94 - 0
client/components/boards/boardHeader.jade

@@ -0,0 +1,94 @@
+template(name="headerBoard")
+  h1.header-board-menu(
+    class="{{#if currentUser.isBoardMember}}is-clickable js-edit-board-title{{/if}}")
+    = title
+
+  .board-header-btns.left
+    unless isSandstorm
+      if currentUser
+        a.board-header-btn.js-star-board(class="{{#if isStarred}}is-hovered{{/if}}"
+          title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
+          i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
+          if showStarCounter
+            span {{_ 'board-nb-stars' stars}}
+
+      a.board-header-btn.js-change-visibility(class="{{#unless currentUser.isBoardAdmin}}no-edit{{/unless}}")
+        i.fa(class="{{#if isPublic}}fa-globe{{else}}fa-lock{{/if}}")
+        span {{_ permission}}
+
+  .board-header-btns.right
+    a.board-header-btn.js-open-filter-view(
+        title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{/if}}"
+        class="{{#if Filter.isActive}}emphasis{{/if}}")
+      i.fa.fa-filter
+      if Filter.isActive
+        span {{_ 'filter-on'}}
+        a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
+          i.fa.fa-times-thin
+      else
+        span {{_ 'filter'}}
+    .separator
+    a.board-header-btn.js-open-board-menu
+      i.board-header-btn-icon.fa.fa-cog
+
+template(name="boardMenuPopup")
+  ul.pop-over-list
+    li: a.js-change-board-color Change color
+    li: a Copy this board
+    li: a Permissions
+
+template(name="boardVisibilityList")
+  ul.pop-over-list
+    li
+      with "private"
+        a.js-select-visibility
+          i.fa.fa-lock.colorful
+          | {{_ 'private'}}
+          if visibilityCheck
+            i.fa.fa-check
+          span.sub-name {{_ 'private-desc'}}
+    li
+      with "public"
+        a.js-select-visibility
+          i.fa.fa-globe.colorful
+          | {{_ 'public'}}
+          if visibilityCheck
+            i.fa.fa-check
+          span.sub-name {{_ 'public-desc'}}
+
+template(name="boardChangeVisibilityPopup")
+  +boardVisibilityList
+
+template(name="boardChangeColorPopup")
+  .board-backgrounds-list.clearfix
+    each backgroundColors
+      .board-background-select.js-select-background
+        span.background-box(class="board-color-{{this}}")
+          if isSelected
+            i.fa.fa-check
+
+template(name="createBoardPopup")
+  form
+    label
+      | {{_ 'title'}}
+      input.js-new-board-title(type="text" placeholder="{{_ 'bucket-example'}}" autofocus required)
+    if visibilityMenuIsOpen.get
+      +boardVisibilityList
+    else
+      p.quiet
+        if $eq visibility.get 'public'
+          span.fa.fa-globe.colorful
+          | {{{_ 'board-public-info'}}}
+        else
+          span.fa.fa-lock.colorful
+          | {{{_ 'board-private-info'}}}
+        a.js-change-visibility Change.
+    input.primary.wide(type="submit" value="{{_ 'create'}}")
+
+
+template(name="boardChangeTitlePopup")
+  form
+    label
+      | {{_ 'title'}}
+      input.js-board-name(type="text" value="{{title}}" autofocus)
+    input.primary.wide(type="submit" value="{{_ 'rename'}}")

+ 157 - 0
client/components/boards/boardHeader.js

@@ -0,0 +1,157 @@
+Template.boardMenuPopup.events({
+  'click .js-rename-board': Popup.open('boardChangeTitle'),
+  'click .js-change-board-color': Popup.open('boardChangeColor')
+});
+
+Template.boardChangeTitlePopup.events({
+  submit: function(evt, t) {
+    var title = t.$('.js-board-name').val().trim();
+    if (title) {
+      Boards.update(this._id, {
+        $set: {
+          title: title
+        }
+      });
+      Popup.close();
+    }
+    evt.preventDefault();
+  }
+});
+
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'headerBoard';
+  },
+
+  isStarred: function() {
+    var boardId = this.currentData()._id;
+    var user = Meteor.user();
+    return boardId && user && user.hasStarred(boardId);
+  },
+
+  // Only show the star counter if the number of star is greater than 2
+  showStarCounter: function() {
+    return this.currentData().stars > 2;
+  },
+
+  events: function() {
+    return [{
+      'click .js-edit-board-title': Popup.open('boardChangeTitle'),
+      'click .js-star-board': function() {
+        Meteor.user().toggleBoardStar(Session.get('currentBoard'));
+      },
+      'click .js-open-board-menu': Popup.open('boardMenu'),
+      'click .js-change-visibility': Popup.open('boardChangeVisibility'),
+      'click .js-open-filter-view': function() {
+        Sidebar.setView('filter');
+      },
+      'click .js-filter-reset': function(evt) {
+        evt.stopPropagation();
+        Sidebar.setView();
+        Filter.reset();
+      }
+    }];
+  }
+}).register('headerBoard');
+
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'boardChangeColorPopup';
+  },
+
+  backgroundColors: function() {
+    return Boards.simpleSchema()._schema.color.allowedValues;
+  },
+
+  isSelected: function() {
+    var currentBoard = Boards.findOne(Session.get('currentBoard'));
+    return currentBoard.color === this.currentData().toString();
+  },
+
+  events: function() {
+    return [{
+      'click .js-select-background': function(evt) {
+        var currentBoardId = Session.get('currentBoard');
+        Boards.update(currentBoardId, {
+          $set: {
+            color: this.currentData().toString()
+          }
+        });
+        evt.preventDefault();
+      }
+    }];
+  }
+}).register('boardChangeColorPopup');
+
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'createBoardPopup';
+  },
+
+  onCreated: function() {
+    this.visibilityMenuIsOpen = new ReactiveVar(false);
+    this.visibility = new ReactiveVar('private');
+  },
+
+  visibilityCheck: function() {
+    return this.currentData() === this.visibility.get();
+  },
+
+  setVisibility: function(visibility) {
+    this.visibility.set(visibility);
+    this.visibilityMenuIsOpen.set(false);
+  },
+
+  toogleVisibilityMenu: function() {
+    this.visibilityMenuIsOpen.set(! this.visibilityMenuIsOpen.get());
+  },
+
+  onSubmit: function(evt) {
+    evt.preventDefault();
+    var title = this.find('.js-new-board-title').value;
+    var visibility = this.visibility.get();
+
+    var boardId = Boards.insert({
+      title: title,
+      permission: visibility
+    });
+
+    Utils.goBoardId(boardId);
+  },
+
+  events: function() {
+    return [{
+      'click .js-select-visibility': function() {
+        this.setVisibility(this.currentData());
+      },
+      'click .js-change-visibility': this.toogleVisibilityMenu,
+      submit: this.onSubmit
+    }];
+  }
+}).register('createBoardPopup');
+
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'boardChangeVisibilityPopup';
+  },
+
+  visibilityCheck: function() {
+    var currentBoard = Boards.findOne(Session.get('currentBoard'));
+    return this.currentData() === currentBoard.permission;
+  },
+
+  selectBoardVisibility: function() {
+    Boards.update(Session.get('currentBoard'), {
+      $set: {
+        permission: this.currentData()
+      }
+    });
+    Popup.close();
+  },
+
+  events: function() {
+    return [{
+      'click .js-select-visibility': this.selectBoardVisibility
+    }];
+  }
+}).register('boardChangeVisibilityPopup');

+ 7 - 0
client/components/boards/list.jade → client/components/boards/boardList.jade

@@ -1,4 +1,11 @@
+//-
+  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="boards")
+  +boardList
+
+template(name="boardList")
   if boards
     ul.board-list.clearfix
       each boards

+ 34 - 0
client/components/boards/boardList.js

@@ -0,0 +1,34 @@
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'boardList';
+  },
+
+  boards: function() {
+    return Boards.find({}, {
+      sort: ['title']
+    });
+  },
+
+  starredBoards: function() {
+    var cursor = Boards.find({
+      _id: { $in: Meteor.user().profile.starredBoards || [] }
+    }, {
+      sort: ['title']
+    });
+    return cursor.count() === 0 ? null : cursor;
+  },
+
+  isStarred: function() {
+    var user = Meteor.user();
+    return user && user.hasStarred(this._id);
+  },
+
+  events: function() {
+    return [{
+      'click .js-star-board': function(evt) {
+        Meteor.user().toggleBoardStar(this._id);
+        evt.preventDefault();
+      }
+    }];
+  }
+}).register('boardList');

+ 0 - 0
client/components/boards/list.styl → client/components/boards/boardList.styl


+ 14 - 1
client/components/boards/colors.styl

@@ -5,16 +5,29 @@ setBoardColor(color)
   &#header,
   &.sk-spinner div,
   .board-backgrounds-list &.background-box,
-  &.pop-over .pop-over-list li a:hover,
   .board-list & a
     background-color: color
 
   & .minicard.is-selected .minicard-details
     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%)
 
+  &#header #header-quick-access ul li.current
+    border-bottom: 2px solid lighten(color, 10%)
+
+  &#header #header-main-bar .board-header-btn.emphasis
+    background: complement(color)
+
+    &:hover,
+    .board-header-btn-close
+      background: darken(complement(color), 10%)
+
+    &:hover .board-header-btn-close
+      background: darken(complement(color), 20%)
+
 .board-color-nephritis
   setBoardColor(#27AE60)
 

+ 0 - 96
client/components/boards/events.js

@@ -1,96 +0,0 @@
-var toggleBoardStar = function(boardId) {
-  var queryType = Meteor.user().hasStarred(boardId) ? '$pull' : '$addToSet';
-  var query = {};
-  query[queryType] = {
-    'profile.starredBoards': boardId
-  };
-  Meteor.users.update(Meteor.userId(), query);
-};
-
-Template.boards.events({
-  'click .js-star-board': function(evt) {
-    toggleBoardStar(this._id);
-    evt.preventDefault();
-  }
-});
-
-Template.headerBoard.events({
-  'click .js-star-board': function() {
-    toggleBoardStar(this._id);
-  },
-  'click .js-open-board-menu': Popup.open('boardMenu'),
-  'click #permission-level:not(.no-edit)': Popup.open('boardChangePermission'),
-  'click .js-filter-cards-indicator': function(evt) {
-    Session.set('currentWidget', 'filter');
-    evt.preventDefault();
-  },
-  'click .js-filter-card-clear': function(evt) {
-    Filter.reset();
-    evt.stopPropagation();
-  }
-});
-
-Template.boardMenuPopup.events({
-  'click .js-rename-board': Popup.open('boardChangeTitle'),
-  'click .js-change-board-color': Popup.open('boardChangeColor')
-});
-
-Template.createBoardPopup.events({
-  'submit #CreateBoardForm': function(evt, t) {
-    var title = t.$('#boardNewTitle');
-
-    // trim value title
-    if ($.trim(title.val())) {
-      // İnsert Board title
-      var boardId = Boards.insert({
-        title: title.val(),
-        permission: 'public'
-      });
-
-      // Go to Board _id
-      Utils.goBoardId(boardId);
-    }
-    evt.preventDefault();
-  }
-});
-
-Template.boardChangeTitlePopup.events({
-  'submit #ChangeBoardTitleForm': function(evt, t) {
-    var title = t.$('.js-board-name').val().trim();
-    if (title) {
-      Boards.update(this._id, {
-        $set: {
-          title: title
-        }
-      });
-      Popup.close();
-    }
-    evt.preventDefault();
-  }
-});
-
-Template.boardChangePermissionPopup.events({
-  'click .js-select': function(evt) {
-    var $this = $(evt.currentTarget);
-    var permission = $this.attr('name');
-
-    Boards.update(this._id, {
-      $set: {
-        permission: permission
-      }
-    });
-    Popup.close();
-  }
-});
-
-Template.boardChangeColorPopup.events({
-  'click .js-select-background': function(evt) {
-    var currentBoardId = Session.get('currentBoard');
-    Boards.update(currentBoardId, {
-      $set: {
-        color: this.toString()
-      }
-    });
-    evt.preventDefault();
-  }
-});

+ 0 - 87
client/components/boards/header.jade

@@ -1,87 +0,0 @@
-template(name="headerBoard")
-  h1.header-board-menu.js-open-board-menu
-    = title
-    span.fa.fa-angle-down
-
-  .board-header-btns.left
-    unless isSandstorm
-      a.board-header-btn.js-star-board(class="{{#if isStarred}}board-header-starred{{/if}}"
-        title="{{# if isStarred }}{{_ 'click-to-unstar'}}{{ else }}{{_ 'click-to-star'}}{{/ if }} {{_ 'starred-boards-description'}}")
-        span.board-header-btn-icon.icon-sm.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
-        //- XXX To implement
-        span.board-header-btn-text Starred
-    //-
-      XXX Normally we would disable this field for sandstorm, but we keep it
-      until sandstorm implements sharing capabilities
-
-      a.board-header-btn.perms-btn.js-change-vis(class="{{#unless currentUser.isBoardAdmin}}no-edit{{/ unless}}" id="permission-level")
-        span.board-header-btn-icon.icon-sm.fa(class="{{#if isPublic}}fa-globe{{else}}fa-lock{{/if}}")
-        span.board-header-btn-text {{_ permission}}
-
-    a.board-header-btn.js-search
-      span.board-header-btn-icon.icon-sm.fa.fa-tag
-      span.board-header-btn-text Labels
-
-    //- XXX Clicking here should open a search field
-    a.board-header-btn.js-search
-      span.board-header-btn-icon.icon-sm.fa.fa-search
-      span.board-header-btn-text {{_ 'search'}}
-
-  //- +boardMembersHeader
-
-template(name="boardMembersHeader")
-  .board-header-members
-    each currentBoard.members
-      +userAvatar(userId=userId draggable=true showBadges=true)
-    unless isSandstorm
-      if currentUser.isBoardAdmin
-        a.member.add-board-member.js-open-manage-board-members
-          i.fa.fa-plus
-
-template(name="boardMenuPopup")
-  ul.pop-over-list
-    li: a.js-rename-board {{_ 'rename-board'}}
-    li: a.js-change-board-color Change color
-    li: a Copy this board
-    li: a Rules
-
-template(name="boardChangeTitlePopup")
-  form#ChangeBoardTitleForm
-    label {{_ 'name'}}
-      input.js-board-name(type="text" value="{{ title }}" autofocus)
-      input.primary.wide.js-rename-board(type="submit" value="{{_ 'rename'}}")
-
-template(name="boardChangePermissionPopup")
-  ul.pop-over-list
-    li
-      a.js-select.light-hover(name="private")
-        span.icon-sm.fa.fa-lock.vis-icon
-        | {{_ 'private'}}
-        if check 'private'
-          span.icon-sm.fa.fa-check
-        span.sub-name {{_ 'private-desc'}}
-    li
-      a.js-select.light-hover(name="public")
-        span.icon-sm.fa.fa-globe.vis-icon
-        | {{_ 'public'}}
-        if check 'public'
-          span.icon-sm.fa.fa-check
-        span.sub-name {{_ 'public-desc'}}
-
-template(name="boardChangeColorPopup")
-  .board-backgrounds-list.clearfix
-    each backgroundColors
-      .board-background-select.js-select-background
-        span.background-box(class="board-color-{{this}}")
-          if isSelected
-            i.fa.fa-check
-
-template(name="createBoardPopup")
-  .content.clearfix
-    form#CreateBoardForm
-      label(for="boardNewTitle") {{_ 'title'}}
-      input#boardNewTitle.non-empty(type="text" name="name" placeholder="{{_ 'bucket-example'}}" autofocus)
-      p.quiet
-        span.icon-sm.fa.fa-globe
-        | {{{_ 'board-public-info'}}}
-      input.primary.wide(type="submit" value="{{_ 'create'}}")

+ 0 - 7
client/components/boards/header.js

@@ -1,7 +0,0 @@
-Template.headerBoard.helpers({
-  isStarred: function() {
-    var boardId = Session.get('currentBoard');
-    var user = Meteor.user();
-    return boardId && user && user.hasStarred(boardId);
-  }
-});

+ 0 - 137
client/components/boards/header.styl

@@ -1,137 +0,0 @@
-@import 'nib'
-
-.board-header {
-    height: auto;
-    overflow: hidden;
-    padding: 10px 30px 10px 8px;
-    position: relative;
-    transition: padding .15s ease-in;
-}
-
-.board-header-btns {
-    position: relative;
-    display: block;
-}
-
-.board-header-btn {
-    border-radius: 3px;
-    color: #f6f6f6;
-    cursor: default;
-    float: left;
-    font-size: 12px;
-    height: 30px;
-    line-height: 32px;
-    margin: 2px 4px 0 0;
-    overflow: hidden;
-    padding-left: 30px;
-    position: relative;
-    text-decoration: none;
-}
-
-.board-header-btn:empty {
-    display: none;
-}
-
-.board-header-btn-without-icon {
-    padding-left: 8px;
-}
-
-.board-header-btn-icon {
-    background-clip: content-box;
-    background-origin: content-box;
-    color: #f6f6f6 !important;
-    padding: 6px;
-    position: absolute;
-    top: 0;
-    left: 0;
-}
-
-.board-header-btn-text {
-    padding-right: 8px;
-}
-
-.board-header-btn:not(.no-edit) .text {
-    text-decoration: underline;
-}
-
-.board-header-btn:not(.no-edit):hover {
-    background: rgba(0, 0, 0, .12);
-    cursor: pointer;
-}
-
-.board-header-btn:hover {
-    color: #f6f6f6;
-}
-
-.board-header-btn.board-header-btn-enabled {
-    background-color: rgba(0, 0, 0, .1);
-
-    &:hover {
-        background-color: rgba(0, 0, 0, .3);
-    }
-
-    .board-header-btn-icon.icon-star {
-        color: #e6bf00 !important;
-    }
-}
-
-.board-header-btn-name {
-    cursor: default;
-    font-size: 18px;
-    font-weight: 700;
-    line-height: 30px;
-    padding-left: 4px;
-    text-decoration: none;
-
-    .board-header-btn-text {
-        padding-left: 6px;
-    }
-}
-
-.board-header-btn-name-org-logo {
-    border-radius: 3px;
-    height: 30px;
-    left: 0;
-    position: absolute;
-    top: 0;
-    width: 30px;
-
-    .board-header-btn-text {
-        padding-left: 32px;
-    }
-}
-
-.board-header-btn-org-name {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    max-width: 400px;
-}
-
-.board-header-btn-filter-indicator {
-    background: #3d990f;
-    padding-right: 30px;
-    color: #fff;
-    text-shadow: 0;
-
-    &:hover {
-        background: #43a711 !important;
-    }
-
-    .board-header-btn-icon-close {
-        background: #43a711;
-        border-top-left-radius: 0;
-        border-top-right-radius: 3px;
-        border-bottom-right-radius: 3px;
-        border-bottom-left-radius: 0;
-        color: #fff;
-        padding: 6px;
-        position: absolute;
-        right: 0;
-        top: 0;
-
-        &:hover {
-            background: #48b512;
-        }
-    }
-}

+ 0 - 39
client/components/boards/helpers.js

@@ -1,42 +1,3 @@
-Template.boards.helpers({
-  boards: function() {
-    return Boards.find({}, {
-      sort: ['title']
-    });
-  },
-
-  starredBoards: function() {
-    var cursor = Boards.find({
-      _id: { $in: Meteor.user().profile.starredBoards || [] }
-    }, {
-      sort: ['title']
-    });
-    return cursor.count() === 0 ? null : cursor;
-  },
-
-  isStarred: function() {
-    var user = Meteor.user();
-    return user && user.hasStarred(this._id);
-  }
-});
-
-Template.boardChangePermissionPopup.helpers({
-  check: function(perm) {
-    return this.permission === perm;
-  }
-});
-
-Template.boardChangeColorPopup.helpers({
-  backgroundColors: function() {
-    return Boards.simpleSchema()._schema.color.allowedValues;
-  },
-
-  isSelected: function() {
-    var currentBoard = Boards.findOne(Session.get('currentBoard'));
-    return currentBoard.color === this.toString();
-  }
-});
-
 Blaze.registerHelper('currentBoard', function() {
   var boardId = Session.get('currentBoard');
   if (boardId) {

+ 2 - 2
client/components/cards/details.jade

@@ -4,7 +4,7 @@ template(name="cardDetails")
       .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
+        i.fa.fa-times-thin
       h2.card-detail-title.js-card-title= title
     p.card-detail-list.js-move-card
       | {{_ 'in-list'}}
@@ -25,7 +25,7 @@ template(name="cardDetails")
     if currentUser.isBoardMember
       h3 Description
       +inlinedForm(classNames="js-card-description")
-        i.fa.fa-times.js-close-inlined-form
+        a.fa.fa-times-thin.js-close-inlined-form
         +editor(autofocus=true)
           = description
         button(type="submit") {{_ 'edit'}}

+ 0 - 61
client/components/cards/events.js

@@ -1,64 +1,3 @@
-// Template.cards.events({
-//   // 'click .js-cancel': function(event, t) {
-//   //   var composer = t.$('.card-composer');
-
-//   //   // Keep the old value in memory to display it again next time
-//   //   var inputCacheKey = "addCard-" + this.listId;
-//   //   var oldValue = composer.find('.js-card-title').val();
-//   //   InputsCache.set(inputCacheKey, oldValue);
-
-//   //   // add composer hide class
-//   //   composer.addClass('hide');
-//   //   composer.find('.js-card-title').val('');
-
-//   //   // remove hide open link class
-//   //   $('.js-open-card-composer').removeClass('hide');
-//   // },
-//   'submit': function(evt, tpl) {
-//     evt.preventDefault();
-//     var textarea = $(evt.currentTarget).find('textarea');
-//     var title = textarea.val();
-//     var lastCard = tpl.find('.js-minicard:last-child');
-//     var sort;
-//     if (lastCard === null) {
-//       sort = 0;
-//     } else {
-//       sort = Blaze.getData(lastCard).sort + 1;
-//     }
-//     // debugger
-
-//     // Clear the form in-memory cache
-//     // var inputCacheKey = "addCard-" + this.listId;
-//     // InputsCache.set(inputCacheKey, '');
-
-//     // title trim if not empty then
-//     if ($.trim(title)) {
-//       Cards.insert({
-//         title: title,
-//         listId: Template.currentData().listId,
-//         boardId: Template.currentData().board._id,
-//         sort: sort
-//       }, function(err, _id) {
-//         // In case the filter is active we need to add the newly
-//         // inserted card in the list of exceptions -- cards that are
-//         // not filtered. Otherwise the card will disappear instantly.
-//         // See https://github.com/libreboard/libreboard/issues/80
-//         Filter.addException(_id);
-//       });
-
-//       // empty and focus.
-//       textarea.val('').focus();
-
-//       // focus complete then scroll top
-//       Utils.Scroll(tpl.find('.js-minicards')).top(1000, true);
-//     }
-//   }
-// });
-
-// Template.cards.events({
-//   'click .member': Popup.open('cardMember')
-// });
-
 Template.cardMemberPopup.events({
   'click .js-remove-member': function() {
     Cards.update(this.cardId, {$pull: {members: this.userId}});

+ 27 - 0
client/components/cards/minicard.jade

@@ -0,0 +1,27 @@
+template(name="minicard")
+  .minicard.card.js-minicard(
+    class="{{#if isSelected}}is-selected{{/if}}")
+    a.minicard-details.clearfix.show(href=absoluteUrl)
+      if cover
+        .minicard-cover.js-card-cover(style="background-image: url({{cover.url}});")
+      if labels
+        .minicard-labels
+          each labels
+            .minicard-label(class="card-label-{{color}}" title="{{name}}")
+      .minicard-title= title
+      if members
+        .minicard-members.js-minicard-members
+          each members
+            +userAvatar(userId=this size="small" cardId="{{../_id}}")
+      .badges
+        if comments.count
+          .badge(title="{{_ 'card-comments-title' comments.count }}")
+            span.badge-icon.icon-sm.fa.fa-comment-o
+            .badge-text= comments.count
+        if description
+          .badge.badge-state-image-only(title=description)
+            span.badge-icon.icon-sm.fa.fa-align-left
+        if attachments.count
+          .badge
+            span.badge-icon.icon-sm.fa.fa-paperclip
+            span.badge-text= attachments.count

+ 14 - 0
client/components/cards/minicard.js

@@ -0,0 +1,14 @@
+// Template.cards.events({
+//   'click .member': Popup.open('cardMember')
+// });
+
+
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'minicard';
+  },
+
+  isSelected: function() {
+    return Session.equals('currentCard', this.currentData()._id);
+  }
+}).register('minicard');

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

@@ -229,26 +229,9 @@ textarea
     padding-top: 5px
     padding-bottom: 5px
 
-  i.fa.fa-times
-    font-size: 20px
-
-  .option
-    border-color: transparent
-    border-radius: 3px
-    color: #8c8c8c
-    display: block
-    float: right
-    height: 30px
-    line-height: 30px
-    padding: 0 8px
-    margin: 0 2px
-
-    &:hover
-      background-color: #dbdbdb
-      color: #4d4d4d
-
-    &:active
-      background-color: #ccc
+  .fa-times-thin
+    font-size: 26px
+    margin: 3px 4px
 
 .button-link
   background: #fff

+ 4 - 3
client/components/forms/inlinedform.js

@@ -30,11 +30,12 @@ BlazeComponent.extendComponent({
     this.isOpen = new ReactiveVar(false);
   },
 
+  onDestroyed: function() {
+    currentlyOpenedForm.set(null);
+  },
+
   open: function() {
     // Close currently opened form, if any
-    // if (currentlyOpenedForm.get() !== null) {
-    //   currentlyOpenedForm.get().close();
-    // }
     EscapeActions.executeLowerThan('inlinedForm');
     this.isOpen.set(true);
     currentlyOpenedForm.set(this);

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

@@ -5,32 +5,7 @@ template(name="listBody")
         +inlinedForm(autoclose=false position="top")
           +addCardForm(listId=_id position="top")
       each cards
-        .minicard.card.js-minicard(
-          class="{{#if isSelected}}is-selected{{/if}}")
-          a.minicard-details.clearfix.show(href=absoluteUrl)
-            if cover
-              .minicard-cover.js-card-cover(style="background-image: url({{cover.url}});")
-            if labels
-              .minicard-labels
-                each labels
-                  .minicard-label(class="card-label-{{color}}" title="{{name}}")
-            .minicard-title= title
-            if members
-              .minicard-members.js-minicard-members
-                each members
-                  +userAvatar(userId=this size="small" cardId="{{../_id}}")
-            .badges
-              if comments.count
-                .badge(title="{{_ 'card-comments-title' comments.count }}")
-                  span.badge-icon.icon-sm.fa.fa-comment-o
-                  .badge-text= comments.count
-              if description
-                .badge.badge-state-image-only(title=description)
-                  span.badge-icon.icon-sm.fa.fa-align-left
-              if attachments.count
-                .badge
-                  span.badge-icon.icon-sm.fa.fa-paperclip
-                  span.badge-text= attachments.count
+        +minicard(this)
       if currentUser.isBoardMember
         +inlinedForm(autoclose=false position="bottom")
           +addCardForm(listId=_id position="bottom")
@@ -49,4 +24,4 @@ template(name="addCardForm")
       .minicard-members.js-minicard-composer-members
   .add-controls.clearfix
     button.primary.confirm(type="submit") {{_ 'add'}}
-    a.fa.fa-times.dark-hover.cancel.js-close-inlined-form
+    a.fa.fa-times-thin.js-close-inlined-form

+ 16 - 18
client/components/lists/body.js

@@ -7,10 +7,6 @@ BlazeComponent.extendComponent({
     return [Mixins.PerfectScrollbar];
   },
 
-  isSelected: function() {
-    return Session.equals('currentCard', this.currentData()._id);
-  },
-
   openForm: function(options) {
     options = options || {};
     options.position = options.position || 'top';
@@ -37,11 +33,6 @@ BlazeComponent.extendComponent({
       sortIndex = Utils.getSortIndex(this.find('.js-minicard:last'), null);
     }
 
-    // Clear the form in-memory cache
-    // var inputCacheKey = "addCard-" + this.listId;
-    // InputsCache.set(inputCacheKey, '');
-
-    // title trim if not empty then
     if ($.trim(title)) {
       Cards.insert({
         title: title,
@@ -49,16 +40,18 @@ BlazeComponent.extendComponent({
         boardId: this.data().board()._id,
         sort: sortIndex
       }, function(err, _id) {
-        // In case the filter is active we need to add the newly
-        // inserted card in the list of exceptions -- cards that are
-        // not filtered. Otherwise the card will disappear instantly.
+        // In case the filter is active we need to add the newly inserted card
+        // in the list of exceptions -- cards that are not filtered. Otherwise
+        // the card will disappear instantly.
         // See https://github.com/libreboard/libreboard/issues/80
         Filter.addException(_id);
       });
 
       // We keep the form opened, empty it, and scroll to it.
       textarea.val('').focus();
-      Utils.Scroll(this.find('.js-minicards')).top(1000, true);
+      if (position === 'bottom') {
+        this.scrollToBottom();
+      }
     }
   },
 
@@ -67,9 +60,9 @@ BlazeComponent.extendComponent({
   },
 
   scrollToBottom: function() {
-    var $container = $(this.firstNode());
-    $container.animate({
-      scrollTop: $container.height()
+    var container = this.firstNode();
+    $(container).animate({
+      scrollTop: container.scrollHeight
     });
   },
 
@@ -94,7 +87,12 @@ BlazeComponent.extendComponent({
     // Pressing Enter should submit the card
     if (evt.keyCode === 13) {
       evt.preventDefault();
-      $(evt.currentTarget).parents('form:first').submit();
+      var $form = $(evt.currentTarget).parents('form:first');
+      // XXX For some reason $form.submit() does not work (it's probably a bug
+      // of blaze-component related to the fact that the submit event is non-
+      // bubbling). This is why we click on the submit button instead -- which
+      // work.
+      $form.find('button[type=submit]').click();
 
     // Pressing Tab should open the form of the next column, and Maj+Tab go
     // in the reverse order
@@ -102,7 +100,7 @@ BlazeComponent.extendComponent({
       evt.preventDefault();
       var isReverse = evt.shiftKey;
       var list = $('#js-list-' + this.data().listId);
-      var listSelector = '.js-list:not(.js-add-list)';
+      var listSelector = '.js-list:not(.js-list-composer)';
       var nextList = list[isReverse ? 'prev' : 'next'](listSelector).get(0);
       // If there isn't no next list, loop back to the beginning.
       if (! nextList) {

+ 49 - 38
client/components/lists/main.js

@@ -24,58 +24,69 @@ BlazeComponent.extendComponent({
   // powerful enough for our use casesShould we “simply” write the drag&drop
   // code ourselves?
   onRendered: function() {
-    if (Meteor.user().isBoardMember()) {
-      var boardComponent = this.componentParent();
-      var itemsSelector = '.js-minicard:not(.placeholder, .hide, .js-composer)';
-      var $cards = this.$('.js-minicards');
-      $cards.sortable({
-        connectWith: '.js-minicards',
-        tolerance: 'pointer',
-        appendTo: '.js-lists',
-        helper: 'clone',
-        items: itemsSelector,
-        placeholder: 'minicard placeholder',
-        start: function(event, ui) {
-          $('.minicard.placeholder').height(ui.item.height());
-          Popup.close();
-          boardComponent.showNewCardForms(false);
-        },
-        stop: function(event, ui) {
-          // To attribute the new index number, we need to get the dom element
-          // of the previous and the following card -- if any.
-          var cardDomElement = ui.item.get(0);
-          var prevCardDomElement = ui.item.prev('.js-minicard').get(0);
-          var nextCardDomElement = ui.item.next('.js-minicard').get(0);
-          var sort = Utils.getSortIndex(prevCardDomElement, nextCardDomElement);
-          var cardId = Blaze.getData(cardDomElement)._id;
-          var listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
-          Cards.update(cardId, {
-            $set: {
-              listId: listId,
-              sort: sort
-            }
-          });
-          boardComponent.showNewCardForms(true);
-        }
-      }).disableSelection();
+    var self = this;
+    if (! Meteor.userId() || ! Meteor.user().isBoardMember())
+      return;
 
-      $(document).on('mouseover', function() {
+    var boardComponent = self.componentParent();
+    var itemsSelector = '.js-minicard:not(.placeholder, .hide, .js-composer)';
+    var $cards = self.$('.js-minicards');
+    $cards.sortable({
+      connectWith: '.js-minicards',
+      tolerance: 'pointer',
+      appendTo: '.js-lists',
+      helper: 'clone',
+      items: itemsSelector,
+      placeholder: 'minicard placeholder',
+      start: function(event, ui) {
+        $('.minicard.placeholder').height(ui.item.height());
+        Popup.close();
+        boardComponent.showNewCardForms(false);
+      },
+      stop: function(event, ui) {
+        // To attribute the new index number, we need to get the dom element
+        // of the previous and the following card -- if any.
+        var cardDomElement = ui.item.get(0);
+        var prevCardDomElement = ui.item.prev('.js-minicard').get(0);
+        var nextCardDomElement = ui.item.next('.js-minicard').get(0);
+        var sort = Utils.getSortIndex(prevCardDomElement, nextCardDomElement);
+        var cardId = Blaze.getData(cardDomElement)._id;
+        var listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
+        Cards.update(cardId, {
+          $set: {
+            listId: listId,
+            sort: sort
+          }
+        });
+        boardComponent.showNewCardForms(true);
+      }
+    });
+
+    // We want to re-run this function any time a card is added.
+    self.autorun(function() {
+      var currentBoardId = Tracker.nonreactive(function() {
+        return Session.get('currentBoard');
+      });
+      Cards.find({ boardId: currentBoardId }).fetch();
+      Tracker.afterFlush(function() {
         $cards.find(itemsSelector).droppable({
           hoverClass: 'draggable-hover-card',
           accept: '.js-member,.js-label',
           drop: function(event, ui) {
             var cardId = Blaze.getData(this)._id;
+            var addToSet;
 
             if (ui.draggable.hasClass('js-member')) {
               var memberId = Blaze.getData(ui.draggable.get(0)).userId;
-              Cards.update(cardId, {$addToSet: {members: memberId}});
+              addToSet = { members: memberId };
             } else {
               var labelId = Blaze.getData(ui.draggable.get(0))._id;
-              Cards.update(cardId, {$addToSet: {labelIds: labelId}});
+              addToSet = { labelIds: labelId };
             }
+            Cards.update(cardId, { $addToSet: addToSet });
           }
         });
       });
-    }
+    });
   }
 }).register('list');

+ 72 - 212
client/components/main/header.styl

@@ -1,7 +1,5 @@
 @import 'nib'
 
-global-reset()
-
 #header
   color: white
   transition: background-color 0.4s
@@ -9,15 +7,16 @@ global-reset()
 
   #header-quick-access
     background-color: rgba(0, 0, 0, 0.2)
-    padding: 4px 10px
-    height: 16px
+    padding: 0px 10px
+    height: 28px
     font-size: 12px
     display: flex
 
-    ul li, #header-user-bar
+    #header-user-bar
+    ul li
       color: darken(white, 17%)
 
-      a
+      a, .fa
         color: inherit
         text-decoration: none
 
@@ -27,240 +26,101 @@ global-reset()
     ul
       flex: 1
       transition: opacity 0.2s
-      margin-left: 5px
+      margin: 4px 0 0 5px
 
       li
         display: block
         float: left
         width: auto
         color: darken(white, 15%)
-        padding: 0 4px 1px 4px
-
-        &.separator
-          padding: 0 2px 1px 2px
+        padding: 2px 5px 0
 
         &.current
-          font-style: italic
+          color: darken(white, 5%)
 
         &:first-child .fa-home
           margin-right: 5px
 
-  #header-main-bar
-    height: 30px
-    padding: 8px
-
-    h1
-      font-size: 19px
-      line-height: 1.7em
-      margin: 0 20px 0 10px
-      float: left
-
-      &.header-board-menu
-        cursor: pointer
-
-        .fa-angle-down
-          font-size: 0.8em
-          // line-height: 1.1em
+        a.js-create-board
           margin-left: 5px
 
-    .board-header-starred .fa
-      color: yellow
-
-    .board-header-members
-      float: right
+    #header-user-bar
+      margin: 2px 0
 
       .member
-        display: block
-        width: 32px
+        width: 24px
         height: @width
+        float: left
+        margin: 0
+        margin-top: 1px
 
-      .add-board-member
-        color: white
-        display: flex
-        align-items: center
-        justify-content: center
-        border: 1px solid white
-        height: 32px - 2px
-        width: @height
-
-        i.fa-plus
-          margin-top: 2px
-
-      .header-btn:last-child
-        margin-right: 0
-
-
-
-// #header {
-//     background: #138871;
-//     height: 30px;
-//     overflow: hidden;
-//     padding: 5px;
-//     position: relative;
-//     z-index: 10;
-// }
-
-// .header-logo {
-//     bottom: 0;
-//     display: block;
-//     height: 25px;
-//     left: 50%;
-//     position: absolute;
-//     top: 8px;
-//     width: 80px;
-//     margin-left: - @width/2;
-//     text-align: center;
-//     z-index: 2;
-//     opacity: .5;
-//     transition: opacity ease-in 85ms;
-//     color: white;
-//     font-size: 22px;
-//     text-decoration: none;
-//     background-image: url('/logos/white_logo.png');
-
-//     &:hover {
-//         opacity: .8;
-//         color: white;
-//     }
-// }
-
-// .header-btn.header-btn-feedback {
-//     background: rgba(255, 255, 255, .1);
-//     background: linear-gradient(to bottom, rgba(255, 255, 255, .1) 0, rgba(255, 255, 255, .05) 100%);
-//     padding-left: 22px;
-//     margin-right: 16px;
-
-//     .header-btn-icon {
-//         top: 1px;
-//     }
-// }
-
-.header-btn {
-    border-radius: 3px;
-    user-select: none;
-    background: rgba(255, 255, 255, .3);
-    background: linear-gradient(to bottom, rgba(255, 255, 255, .3) 0, rgba(255, 255, 255, .2) 100%);
-    color: #f3f3f3;
-    display: block;
-    float: left;
-    font-weight: 700;
-    height: 30px;
-    line-height: 30px;
-    padding: 0 10px;
-    position: relative;
-    margin-right: 8px;
-    min-width: 30px;
-    text-decoration: none;
-    cursor: pointer;
-
-    .header-btn-icon {
-        font-size: 16px;
-        line-height: 28px;
-        position: absolute;
-        top: 0;
-        left: 0;
-    }
-
-    &.new-notifications {
-        background: #ba1212;
-
-        &:hover {
-            background: #d11515;
-        }
-    }
-
-    &.header-member .member {
-        margin: 0;
-        border-top-left-radius: 3px;
-        border-top-right-radius: 0;
-        border-bottom-right-radius: 0;
-        border-bottom-left-radius: 3px;
-
-        &:hover .member-avatar {
-            opacity: 1;
-        }
-    }
-
-    &:hover {
-        background: rgba(255, 255, 255, .4);
-        background: linear-gradient(to bottom, rgba(255, 255, 255, .4) 0, rgba(255, 255, 255, .3) 100%);
-        color: #fff;
-
-        .header-btn-count {
-            background: #d11515;
-        }
-    }
-
-    &:active {
-        background: rgba(255, 255, 255, .4);
-        background: linear-gradient(to bottom, rgba(255, 255, 255, .4) 0, rgba(255, 255, 255, .3) 100%);
-    }
-
-    &.upgrade {
-        margin-right: 16px;
+      .header-user-bar-name
+        margin: 4px 8px 0 0
+        float: left
 
-        .icon-sm {
-            padding: 6px 2px 6px 4px;
-        }
-    }
+  #header-main-bar
+    height: 28px * 1.618034 - 6px
+    padding: 7px 10px 0
 
-    &.upgrade,
-    &.header-boards {
-        padding-left: 4px;
-    }
+    h1
+      font-size: 20px
+      line-height: 1.7em
+      padding: 0 10px
+      margin: 0
+      margin-right: 10px
+      float: left
+      border-radius: 3px
 
-    &.header-boards {
-        padding-right: 4px;
-    }
+      &.is-clickable
+        cursor: pointer
 
-    &.header-login,
-    &.header-signup {
-        padding: 0 12px;
-    }
+    .board-header-btns
+      display: block
+      margin-top: 3px
+      width: auto
 
-    &.header-signup {
-        background: #48b512;
-        background: linear-gradient(to bottom, #48b512 0, #3d990f 100%);
+      // XXX Use a flexbox instead of floats?
+      &.left
+        float: left
 
-        &:hover {
-            background: #3d990f;
-            background: linear-gradient(to bottom, #3d990f 0, #327d0c 100%);
-        }
+      &.right
+        float: right
 
-        &:active {
-            background: #327d0c;
-        }
-    }
+    .board-header-btn
+      border-radius: 3px
+      color: darken(white, 5%)
+      padding: 0
+      height: 28px
+      font-size: 13px
+      float: left
+      overflow: hidden
+      line-height: @height
+      margin: 0 2px
 
-    &.header-go-to-boards {
-        padding: 0 8px 0 38px;
-    }
+      i.fa
+        float: left
+        display: block
+        line-height: 28px
+        color: darken(white, 5%)
+        margin: 0 10px
 
-    &.header-go-to-boards .member {
-        border-top-left-radius: 3px;
-        border-top-right-radius: 0;
-        border-bottom-right-radius: 0;
-        border-bottom-left-radius: 3px;
-        position: absolute;
-        left: 0;
-    }
-}
+        + span
+          margin-right: 10px
 
-// .header-btn-text {
-//     padding: 0 8px;
-// }
+      .board-header-btn-close
+        float: right
 
-// .header-notification-list ul {
-//     margin-top: 8px;
-// }
+        i.fa
+          margin: 0 6px
 
-// .header-notification-list .action-comment {
-//     max-height: 250px;
-//     overflow-y: auto;
-// }
+    .board-header-btn,
+    h1.is-clickable
+      &.is-hovered,
+      &:hover
+        background: rgba(0, 0, 0, .15)
 
-// .header-user {
-//     position: absolute;
-//     top: 5px;
-//     right: 0;
-// }
+    .separator
+      margin: 2px 4px
+      border-left: 1px solid rgba(255, 255, 255, .3)
+      height: 24px
+      float: left

+ 18 - 8
client/components/main/popup.js

@@ -1,11 +1,21 @@
 // XXX This event list must be abstracted somewhere else.
-var endTransitionEvents = [
-  'webkitTransitionEnd',
-  'otransitionend',
-  'oTransitionEnd',
-  'msTransitionEnd',
-  'transitionend'
-].join(' ');
+function whichTransitionEvent() {
+  var t;
+  var el = document.createElement('fakeelement');
+  var transitions = {
+    transition:'transitionend',
+    OTransition:'oTransitionEnd',
+    MozTransition:'transitionend',
+    WebkitTransition:'webkitTransitionEnd'
+  };
+
+  for (t in transitions) {
+    if (el.style[t] !== undefined) {
+      return transitions[t];
+    }
+  }
+}
+var transitionEvent = whichTransitionEvent();
 
 Popup.template.events({
   click: function(evt) {
@@ -32,7 +42,7 @@ Popup.template.onRendered(function() {
   container._uihooks = {
     removeElement: function(node) {
       $(node).addClass('no-height');
-      $(container).one(endTransitionEvents, function() {
+      $(container).one(transitionEvent, function() {
         node.parentNode.removeChild(node);
       });
     }

+ 4 - 75
client/components/main/popup.styl

@@ -91,6 +91,10 @@
       top: 0
       right: 0
 
+
+  &.no-title .header
+    background: none
+
   .content-wrapper
     width: 100%
 
@@ -527,78 +531,3 @@
 
   &.limited li.exceeds-limit
     display: none
-
-.pop-over-emoji-list li > a
-  padding: 2px 4px
-
-  .emoji
-    margin: 0 6px
-
-.pop-over-card-list li > a
-  padding: 2px 4px
-
-.login-signup-popover
-  padding: 15px
-
-  .form-tabs
-    display: none
-
-  h1
-    margin-bottom: 15px
-
-  p
-    margin: 8px 0
-
-  .form-parts-container
-    position: relative
-
-  .active-box
-    position: absolute
-    top: 0
-    background: #e2e2e2
-    border: 1px solid #c9c9c9
-    border-radius: 3px
-    z-index: 1
-    height: 100%
-    width: 49%
-    transition-property: all
-    transition-duration: .4s
-    opacity: 1
-
-    &.start
-      opacity: 0
-      left: 25%
-
-  .signup-form,
-  .login-form
-    position: relative
-    box-sizing: border-box
-    padding: 20px
-    width: 50%
-    z-index: 2
-    opacity: .3
-    transition-property: opacity
-    transition-duration: .2s
-
-    .active
-      opacity: 1
-
-
-  .js-signup-form-pos
-    left: 0
-
-  .login-form
-    position: absolute
-    top: 0
-
-  .login-form .icon-google
-    position: absolute
-    left: 5px
-    top: 3px
-
-  .login-form .button.google
-    padding-left: 40px
-    margin: 0 0 15px 0
-
-  .js-login-form-pos
-    left: 50%

+ 3 - 2
client/components/main/popup.tpl.jade

@@ -1,13 +1,14 @@
 .pop-over.clearfix(
   class="{{#unless title}}miniprofile{{/unless}}"
   class=currentBoard.colorClass
+  class="{{#unless title}}no-title{{/unless}}"
   style="display:block; left:{{offset.left}}px; top:{{offset.top}}px;")
-  .header.clearfix
+  .header
     a.back-btn.js-back-view(class="{{#unless hasPopupParent}}is-hidden{{/unless}}")
       i.fa.fa-chevron-left
     span.header-title= title
     a.close-btn.js-close-pop-over
-      i.fa.fa-times
+      i.fa.fa-times-thin
   .content-wrapper
     //-
       We display the all stack of popup content next to each other and move

+ 26 - 10
client/components/sidebar/sidebar.js

@@ -68,23 +68,39 @@ BlazeComponent.extendComponent({
     return this.getView() + 'Sidebar';
   },
 
+  // Board members can assign people or labels by drag-dropping elements from
+  // the sidebar to the cards on the board. In order to re-initialize the
+  // jquery-ui plugin any time a draggable member or label is modified or
+  // removed we use a autorun function and register a dependency on the both
+  // members and labels fields of the current board document.
   onRendered: function() {
     var self = this;
     if (! Meteor.userId() || ! Meteor.user().isBoardMember())
       return;
 
-    $(document).on('mouseover', function() {
-      self.$('.js-member,.js-label').draggable({
-        appendTo: 'body',
-        helper: 'clone',
-        revert: 'invalid',
-        revertDuration: 150,
-        snap: false,
-        snapMode: 'both',
-        start: function() {
-          Popup.close();
+    self.autorun(function() {
+      var currentBoardId = Tracker.nonreactive(function() {
+        return Session.get('currentBoard');
+      });
+      Boards.findOne(currentBoardId, {
+        fields: {
+          members: 1,
+          labels: 1
         }
       });
+      Tracker.afterFlush(function() {
+        self.$('.js-member,.js-label').draggable({
+          appendTo: 'body',
+          helper: 'clone',
+          revert: 'invalid',
+          revertDuration: 150,
+          snap: false,
+          snapMode: 'both',
+          start: function() {
+            EscapeActions.executeLowerThan('popup');
+          }
+        });
+      });
     });
   },
 

+ 8 - 12
client/components/users/headerButtons.jade

@@ -1,16 +1,12 @@
 template(name="headerUserBar")
-  #header-user-bar
-    if currentUser
-      a.js-open-header-member-menu
-        if currentUser.profile.name
-          = currentUser.profile.name
-        else
-          = currentUser.username
-        i.fa.fa-chevron-down
-    else
-      a(href="{{pathFor route='signUp'}}") Sign in
-      span.separator -
-      a(href="{{pathFor route='signIn'}}") Log in
+  a#header-user-bar.js-open-header-member-menu
+    .header-user-bar-name
+      i.fa.fa-chevron-down
+      if currentUser.profile.name
+        = currentUser.profile.name
+      else
+        = currentUser.username
+    +userAvatar(user=currentUser)
 
 template(name="memberHeader")
   a.header-member.js-open-header-member-menu

+ 0 - 19
client/lib/utils.js

@@ -1,23 +1,4 @@
 Utils = {
-  error: function(err) {
-    Session.set('error', (err && err.message || false));
-  },
-
-  // scroll
-  Scroll: function(selector) {
-    var $el = $(selector);
-    return {
-      top: function(px, add) {
-        var t = $el.scrollTop();
-        $el.animate({ scrollTop: (add ? (t + px) : px) });
-      },
-      left: function(px, add) {
-        var l = $el.scrollLeft();
-        $el.animate({ scrollLeft: (add ? (l + px) : px) });
-      }
-    };
-  },
-
   // XXX We should remove these two methods
   goBoardId: function(_id) {
     var board = Boards.findOne(_id);

+ 28 - 0
client/styles/icons.styl

@@ -0,0 +1,28 @@
+.emoji
+  height: 18px
+  width: 18px
+  vertical-align: text-bottom
+
+// Implement a thiner close icon as suggested in
+// https://github.com/FortAwesome/Font-Awesome/issues/1540#issuecomment-68689950
+.fa.fa-times-thin:before
+  content: '\00d7';
+
+.fa.fa-globe.colorful
+  color: #4caf50
+
+.fa.fa-lock.colorful
+  color: #f44336
+
+.pop-over .pop-over-list li a:hover
+  .fa, .fa.colorful
+    color: white
+
+    &:hover
+      color: white
+
+a.fa, a i.fa
+  color: darken(white, 35%)
+
+  &:hover
+    color: darken(white, 60%)

+ 13 - 80
client/styles/main.styl

@@ -1,5 +1,7 @@
 @import 'nib'
 
+global-reset()
+
 html, body, input, select, textarea, button
   font: 14px "Helvetica Neue", Arial, Helvetica, sans-serif
   line-height: 18px
@@ -7,77 +9,8 @@ html, body, input, select, textarea, button
 
 html
   font-size: 100%
-  -webkit-text-size-adjust: 100%
-
-p
-  margin: 0
-
-ol,
-ul
-  list-style: none
-  margin: 0
-  padding: 0
-
-blockquote, q
-  quotes: none
-
-  &:before,
-  &:after
-    content: none
-
-ins
-  text-decoration: none
-
-del
-  text-decoration: line-through
-
-table
-  border-collapse: collapse
-  border-spacing: 0
-  width: 100%
-
-hr
-  height: 1px
-  border: 0
-  border: none
-  width: 100%
-  background: #dbdbdb
-  color: #dbdbdb
-  margin: 15px 0
-  padding: 0
-
-article,
-aside,
-figure,
-footer,
-header,
-hgroup,
-nav,
-section
-  display: block
-
-caption, th, td
-  text-align: left
-  font-weight: 400
-
-a img
-  border: none
-
-article,
-aside,
-details,
-figcaption,
-figure,
-footer,
-header,
-hgroup,
-nav,
-section,
-summary
-  display: block
-
-html
   max-height: 100%
+  -webkit-text-size-adjust: 100%
 
 body
   background: darken(white, 10%)
@@ -162,6 +95,16 @@ blockquote
   color: #666
   padding: 0 0 0 8px
 
+hr
+  height: 1px
+  border: 0
+  border: none
+  width: 100%
+  background: #dbdbdb
+  color: #dbdbdb
+  margin: 15px 0
+  padding: 0
+
 table, td, th
   vertical-align: top
   border-top: 1px solid #ccc
@@ -188,11 +131,6 @@ dl, dt
 dd
   margin: 0 0 16px 24px
 
-.emoji
-  height: 18px
-  width: 18px
-  vertical-align: text-bottom
-
 .edit
   display: none
   position: relative
@@ -285,7 +223,6 @@ dd
     right: 9px
 
   &.focus
-
     .member
       opacity: 1
 
@@ -408,10 +345,6 @@ dd
     top: 0
     width: 18px
 
-.chrome .minicard.ui-sortable-helper,
-.safari .minicard.ui-sortable-helper
-  box-shadow: -2px 2px 6px rgba(0, 0, 0, .2)
-
 input[type="text"].attachment-add-link-input
   float: left
   margin: 0 0 8px

+ 3 - 2
collections/boards.js

@@ -62,8 +62,9 @@ Boards.attachSchema(new SimpleSchema({
   },
   color: {
     type: String,
-    allowedValues: ['nephritis', 'pomegranate', 'belize',
-            'wisteria', 'midnight', 'pumpkin']
+    allowedValues: [
+    'nephritis', 'pomegranate', 'belize',
+    'wisteria', 'midnight', 'pumpkin']
   }
 }));
 

+ 9 - 0
collections/users.js

@@ -29,6 +29,15 @@ Users.helpers({
     var board = Boards.findOne(Session.get('currentBoard'));
     if (this.isBoardMember(board))
       return _.where(board.members, {userId: this._id})[0].isAdmin;
+  },
+
+  toggleBoardStar: function(boardId) {
+    var queryType = Meteor.user().hasStarred(boardId) ? '$pull' : '$addToSet';
+    var query = {};
+    query[queryType] = {
+      'profile.starredBoards': boardId
+    };
+    Meteor.users.update(Meteor.userId(), query);
   }
 });
 

+ 2 - 2
i18n/ar.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "More",
     "cardDeletePopup-title": "Delete Card?",
     "boardChangeTitlePopup-title": "Rename Board",
-    "boardChangePermissionPopup-title": "Change Visibility",
+    "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
     "closeBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Change Language",
     "cardAttachmentsPopup-title": "Attach From…",
     "attachmentDeletePopup-title": "Delete Attachment?"
-}
+}

+ 2 - 2
i18n/br.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "Mais",
     "cardDeletePopup-title": "Excluir Cartão?",
     "boardChangeTitlePopup-title": "Renomear Quadro",
-    "boardChangePermissionPopup-title": "Alterar Visibilidade",
+    "boardChangeVisibilityPopup-title": "Alterar Visibilidade",
     "addMemberPopup-title": "Membros",
     "closeBoardPopup-title": "Fechar Quadro?",
     "removeMemberPopup-title": "Remover Membro?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Alterar Idioma",
     "cardAttachmentsPopup-title": "Anexar de…",
     "attachmentDeletePopup-title": "Excluir Anexo?"
-}
+}

+ 2 - 2
i18n/cm.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "More",
     "cardDeletePopup-title": "Delete Card?",
     "boardChangeTitlePopup-title": "Rename Board",
-    "boardChangePermissionPopup-title": "Change Visibility",
+    "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
     "closeBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Change Language",
     "cardAttachmentsPopup-title": "Attach From…",
     "attachmentDeletePopup-title": "Delete Attachment?"
-}
+}

+ 2 - 2
i18n/cn.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "更多",
     "cardDeletePopup-title": "删除卡片?",
     "boardChangeTitlePopup-title": "重命名看板",
-    "boardChangePermissionPopup-title": "更改可视级别",
+    "boardChangeVisibilityPopup-title": "更改可视级别",
     "addMemberPopup-title": "成员",
     "closeBoardPopup-title": "关闭看板?",
     "removeMemberPopup-title": "删除成员?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "更改语言",
     "cardAttachmentsPopup-title": "附加自...",
     "attachmentDeletePopup-title": "删除附件?"
-}
+}

+ 2 - 2
i18n/cs.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "More",
     "cardDeletePopup-title": "Delete Card?",
     "boardChangeTitlePopup-title": "Rename Board",
-    "boardChangePermissionPopup-title": "Change Visibility",
+    "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
     "closeBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Change Language",
     "cardAttachmentsPopup-title": "Attach From…",
     "attachmentDeletePopup-title": "Delete Attachment?"
-}
+}

+ 2 - 2
i18n/de.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "Mehr",
     "cardDeletePopup-title": "Karte entfernen?",
     "boardChangeTitlePopup-title": "Bord umbenennen",
-    "boardChangePermissionPopup-title": "Ändere Sichbarkeit",
+    "boardChangeVisibilityPopup-title": "Ändere Sichbarkeit",
     "addMemberPopup-title": "Nutzer",
     "closeBoardPopup-title": "Schliese Bord?",
     "removeMemberPopup-title": "Entferne Nutzer?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Ändere Sprache",
     "cardAttachmentsPopup-title": "Attach From…",
     "attachmentDeletePopup-title": "Delete Attachment?"
-}
+}

+ 5 - 2
i18n/en.i18n.json

@@ -37,6 +37,8 @@
     "board-list-btn-title": "View list of boards",
     "board-not-found": "Board not found",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-private-info": "This board will be <strong>private</strong>.",
+    "board-nb-stars": "%s stars",
     "boards": "Boards",
     "bucket-example": "Like “Bucket List” for example…",
     "cancel": "Cancel",
@@ -70,9 +72,10 @@
     "email": "Email",
     "email-or-username": "Email or username",
     "email-placeholder": "e.g., doc@frankenstein.com",
+    "filter": "Filter",
     "filter-cards": "Filter Cards",
     "filter-clear": "Clear filter.",
-    "filter-on": "Filtering is on.",
+    "filter-on": "Filter is on",
     "filter-on-desc": "You are filtering cards on this board. Click here to edit filter.",
     "fullname": "Full Name",
     "gloabal-search": "Global Search",
@@ -163,7 +166,7 @@
     "cardDeletePopup-title": "Delete Card?",
     "boardMenuPopup-title": "Board Menu",
     "boardChangeTitlePopup-title": "Rename Board",
-    "boardChangePermissionPopup-title": "Change Visibility",
+    "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
     "closeBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",

+ 2 - 2
i18n/es.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "Más",
     "cardDeletePopup-title": "¿Borrar ficha?",
     "boardChangeTitlePopup-title": "Renombrar tablero",
-    "boardChangePermissionPopup-title": "Cambiar visibilidad",
+    "boardChangeVisibilityPopup-title": "Cambiar visibilidad",
     "addMemberPopup-title": "Miembros",
     "closeBoardPopup-title": "Cerrar el tablero",
     "removeMemberPopup-title": "¿Eliminar miembro?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Cambiar idioma",
     "cardAttachmentsPopup-title": "Adjuntar de...",
     "attachmentDeletePopup-title": "¿Borrar adjunto?"
-}
+}

+ 2 - 2
i18n/fi.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "Lisää",
     "cardDeletePopup-title": "Poista kortti?",
     "boardChangeTitlePopup-title": "Nimeä taulu uudelleen",
-    "boardChangePermissionPopup-title": "Vaihda näkyvyyttä",
+    "boardChangeVisibilityPopup-title": "Vaihda näkyvyyttä",
     "addMemberPopup-title": "Jäsenet",
     "closeBoardPopup-title": "Sulje taulu?",
     "removeMemberPopup-title": "Poista jäsen?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Vaihda kieltä",
     "cardAttachmentsPopup-title": "Liitä mistä...",
     "attachmentDeletePopup-title": "Poista liitetiedosto?"
-}
+}

+ 2 - 2
i18n/fr.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "Plus",
     "cardDeletePopup-title": "Supprimer la carte ?",
     "boardChangeTitlePopup-title": "Renommer le tableau",
-    "boardChangePermissionPopup-title": "Changer la visibilité",
+    "boardChangeVisibilityPopup-title": "Changer la visibilité",
     "addMemberPopup-title": "Membres",
     "closeBoardPopup-title": "Fermer le tableau ?",
     "removeMemberPopup-title": "Supprimer le membre ?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Changer la langue",
     "cardAttachmentsPopup-title": "Joindre depuis…",
     "attachmentDeletePopup-title": "Supprimer la pièce jointe ?"
-}
+}

+ 2 - 2
i18n/hk.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "More",
     "cardDeletePopup-title": "Delete Card?",
     "boardChangeTitlePopup-title": "Rename Board",
-    "boardChangePermissionPopup-title": "Change Visibility",
+    "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
     "closeBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Change Language",
     "cardAttachmentsPopup-title": "Attach From…",
     "attachmentDeletePopup-title": "Delete Attachment?"
-}
+}

+ 2 - 2
i18n/id.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "More",
     "cardDeletePopup-title": "Delete Card?",
     "boardChangeTitlePopup-title": "Rename Board",
-    "boardChangePermissionPopup-title": "Change Visibility",
+    "boardChangeVisibilityPopup-title": "Change Visibility",
     "addMemberPopup-title": "Members",
     "closeBoardPopup-title": "Close Board?",
     "removeMemberPopup-title": "Remove Member?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Change Language",
     "cardAttachmentsPopup-title": "Attach From…",
     "attachmentDeletePopup-title": "Delete Attachment?"
-}
+}

+ 2 - 2
i18n/ja.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "More",
     "cardDeletePopup-title": "カードを削除しますか?",
     "boardChangeTitlePopup-title": "ボード名の変更",
-    "boardChangePermissionPopup-title": "公開範囲の変更",
+    "boardChangeVisibilityPopup-title": "公開範囲の変更",
     "addMemberPopup-title": "メンバー",
     "closeBoardPopup-title": "ボードを閉じますか?",
     "removeMemberPopup-title": "メンバーを外しますか?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "言語の変更",
     "cardAttachmentsPopup-title": "Attach From…",
     "attachmentDeletePopup-title": "Delete Attachment?"
-}
+}

+ 2 - 2
i18n/ko.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "더보기",
     "cardDeletePopup-title": "카드를 삭제합니까?",
     "boardChangeTitlePopup-title": "보드 이름 바꾸기",
-    "boardChangePermissionPopup-title": "표시 여부 변경",
+    "boardChangeVisibilityPopup-title": "표시 여부 변경",
     "addMemberPopup-title": "멤버",
     "closeBoardPopup-title": "보드를 닫습니까?",
     "removeMemberPopup-title": "멤버를 제거합니까?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "언어 변경",
     "cardAttachmentsPopup-title": "첨부할 위치...",
     "attachmentDeletePopup-title": "첨부 파일을 삭제합니까?"
-}
+}

+ 2 - 2
i18n/ru.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "Поделиться",
     "cardDeletePopup-title": "Удалить карточку?",
     "boardChangeTitlePopup-title": "Переименовать доску",
-    "boardChangePermissionPopup-title": "Изменить настройки видимости",
+    "boardChangeVisibilityPopup-title": "Изменить настройки видимости",
     "addMemberPopup-title": "Участники",
     "closeBoardPopup-title": "Закрыть доску?",
     "removeMemberPopup-title": "Удалить участника?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Сменить язык",
     "cardAttachmentsPopup-title": "Источник",
     "attachmentDeletePopup-title": "Удалить вложение?"
-}
+}

+ 2 - 2
i18n/tr.i18n.json

@@ -157,7 +157,7 @@
     "cardMorePopup-title": "More",
     "cardDeletePopup-title": "Kart Silinsin mi?",
     "boardChangeTitlePopup-title": "Pano Adı Değiştirme",
-    "boardChangePermissionPopup-title": "Görünebilirliği Değiştir",
+    "boardChangeVisibilityPopup-title": "Görünebilirliği Değiştir",
     "addMemberPopup-title": "Üyeler",
     "closeBoardPopup-title": "Pano Kapatılsın mı?",
     "removeMemberPopup-title": "Üyeyi Çıkarmak mı?",
@@ -172,4 +172,4 @@
     "setLanguagePopup-title": "Dil Değiştir",
     "cardAttachmentsPopup-title": "Şuradan Ekle...",
     "attachmentDeletePopup-title": "Ek Dosya Silinsin Mi?"
-}
+}

+ 1 - 1
sandstorm.js

@@ -109,7 +109,7 @@ if (isSandstorm && Meteor.isClient) {
   Meteor.absoluteUrl.defaultOptions = _defaultOptions;
 }
 
-// We use this blaze helper in the UI to hide some template that does not make
+// We use this blaze helper in the UI to hide some templates that does not make
 // sense in the context of sandstorm, like board staring, board archiving, user
 // name edition, etc.
 Blaze.registerHelper('isSandstorm', function() {