Kaynağa Gözat

Merge pull request #4062 from mfilser/popup_card_details

Popup card details
Lauri Ojansivu 3 yıl önce
ebeveyn
işleme
35469364b4
35 değiştirilmiş dosya ile 176 ekleme ve 196 silme
  1. 1 1
      client/components/activities/activities.jade
  2. 8 1
      client/components/activities/activities.js
  3. 2 2
      client/components/activities/comments.js
  4. 3 13
      client/components/boards/boardBody.js
  5. 1 1
      client/components/boards/boardHeader.js
  6. 4 4
      client/components/cards/cardCustomFields.js
  7. 4 1
      client/components/cards/cardDescription.js
  8. 10 3
      client/components/cards/cardDetails.jade
  9. 36 16
      client/components/cards/cardDetails.js
  10. 15 0
      client/components/cards/cardDetails.styl
  11. 2 2
      client/components/cards/checklists.jade
  12. 13 4
      client/components/cards/checklists.js
  13. 1 1
      client/components/cards/labels.js
  14. 2 6
      client/components/cards/minicard.jade
  15. 0 10
      client/components/cards/minicard.js
  16. 2 26
      client/components/lists/list.js
  17. 10 0
      client/components/lists/listBody.js
  18. 1 1
      client/components/lists/listHeader.jade
  19. 1 12
      client/components/lists/listHeader.js
  20. 1 1
      client/components/main/editor.js
  21. 1 12
      client/components/main/popup.styl
  22. 1 1
      client/components/swimlanes/swimlaneHeader.jade
  23. 0 13
      client/components/swimlanes/swimlaneHeader.js
  24. 6 38
      client/components/swimlanes/swimlanes.js
  25. 1 1
      client/components/users/userAvatar.js
  26. 1 1
      client/components/users/userHeader.jade
  27. 0 10
      client/components/users/userHeader.js
  28. 2 6
      client/config/blazeHelpers.js
  29. 2 2
      client/lib/datepicker.js
  30. 9 4
      client/lib/inlinedform.js
  31. 1 1
      client/lib/keyboard.js
  32. 1 1
      client/lib/popup.js
  33. 23 0
      client/lib/utils.js
  34. 9 0
      config/router.js
  35. 2 1
      i18n/en.i18n.json

+ 1 - 1
client/components/activities/activities.jade

@@ -12,7 +12,7 @@ template(name="boardActivities")
     +activity(activity=activityData card=card mode=mode)
 
 template(name="cardActivities")
-  each activityData in currentCard.activities
+  each activityData in activities
     +activity(activity=activityData card=card mode=mode)
 
 template(name="editOrDeleteComment")

+ 8 - 1
client/components/activities/activities.js

@@ -15,7 +15,7 @@ BlazeComponent.extendComponent({
       const capitalizedMode = Utils.capitalize(mode);
       let thisId, searchId;
       if (mode === 'linkedcard' || mode === 'linkedboard') {
-        thisId = Session.get('currentCard');
+        thisId = Utils.getCurrentCardId();
         searchId = Cards.findOne({ _id: thisId }).linkedId;
         mode = mode.replace('linked', '');
       } else {
@@ -54,6 +54,13 @@ BlazeComponent.extendComponent({
   },
 }).register('activities');
 
+Template.activities.helpers({
+  activities() {
+    const ret = this.card.activities();
+    return ret;
+  },
+});
+
 BlazeComponent.extendComponent({
   checkItem() {
     const checkItemId = this.currentData().activity.checklistItemId;

+ 2 - 2
client/components/activities/comments.js

@@ -64,7 +64,7 @@ function resetCommentInput(input) {
 // Tracker.autorun to register the component dependencies, and re-run when these
 // dependencies are invalidated. A better component API would remove this hack.
 Tracker.autorun(() => {
-  Session.get('currentCard');
+  Utils.getCurrentCardId();
   Tracker.afterFlush(() => {
     autosize.update($('.js-new-comment-input'));
   });
@@ -75,7 +75,7 @@ EscapeActions.register(
   () => {
     const draftKey = {
       fieldName: 'cardComment',
-      docId: Session.get('currentCard'),
+      docId: Utils.getCurrentCardId(),
     };
     const commentInput = $('.js-new-comment-input');
     const draft = commentInput.val().trim();

+ 3 - 13
client/components/boards/boardBody.js

@@ -23,7 +23,7 @@ BlazeComponent.extendComponent({
   },
 
   onlyShowCurrentCard() {
-    return Utils.isMiniScreen() && Session.get('currentCard');
+    return Utils.isMiniScreen() && Utils.getCurrentCardId(true);
   },
 
   goHome() {
@@ -191,21 +191,11 @@ BlazeComponent.extendComponent({
     });
 
     this.autorun(() => {
-      let showDesktopDragHandles = false;
-      currentUser = Meteor.user();
-      if (currentUser) {
-        showDesktopDragHandles = (currentUser.profile || {})
-          .showDesktopDragHandles;
-      } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-        showDesktopDragHandles = true;
-      } else {
-        showDesktopDragHandles = false;
-      }
-      if (Utils.isMiniScreen() || showDesktopDragHandles) {
+      if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
         $swimlanesDom.sortable({
           handle: '.js-swimlane-header-handle',
         });
-      } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
+      } else {
         $swimlanesDom.sortable({
           handle: '.swimlane-header',
         });

+ 1 - 1
client/components/boards/boardHeader.js

@@ -136,7 +136,7 @@ BlazeComponent.extendComponent({
           Sidebar.setView('search');
         },
         'click .js-multiselection-activate'() {
-          const currentCard = Session.get('currentCard');
+          const currentCard = Utils.getCurrentCardId();
           MultiSelection.activate();
           if (currentCard) {
             MultiSelection.add(currentCard);

+ 4 - 4
client/components/cards/cardCustomFields.js

@@ -3,7 +3,7 @@ import Cards from '/models/cards';
 
 Template.cardCustomFieldsPopup.helpers({
   hasCustomField() {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const customFieldId = this._id;
     return card.customFieldIndex(customFieldId) > -1;
   },
@@ -11,7 +11,7 @@ Template.cardCustomFieldsPopup.helpers({
 
 Template.cardCustomFieldsPopup.events({
   'click .js-select-field'(event) {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const customFieldId = this._id;
     card.toggleCustomField(customFieldId);
     event.preventDefault();
@@ -31,7 +31,7 @@ const CardCustomField = BlazeComponent.extendComponent({
 
   onCreated() {
     const self = this;
-    self.card = Cards.findOne(Session.get('currentCard'));
+    self.card = Utils.getCurrentCard();
     self.customFieldId = this.data()._id;
   },
 
@@ -194,7 +194,7 @@ CardCustomField.register('cardCustomField');
   onCreated() {
     super.onCreated();
     const self = this;
-    self.card = Cards.findOne(Session.get('currentCard'));
+    self.card = Utils.getCurrentCard();
     self.customFieldId = this.data()._id;
     this.data().value && this.date.set(moment(this.data().value));
   }

+ 4 - 1
client/components/cards/cardDescription.js

@@ -25,7 +25,10 @@ BlazeComponent.extendComponent({
         // Pressing Ctrl+Enter should submit the form
         'keydown form textarea'(evt) {
           if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
-            this.find('button[type=submit]').click();
+            const submitButton = this.find('button[type=submit]');
+            if (submitButton) {
+              submitButton.click();
+            }
           }
         },
       },

+ 10 - 3
client/components/cards/cardDetails.jade

@@ -1,5 +1,8 @@
+template(name="cardDetailsPopup")
+  +cardDetails(popupCard)
+
 template(name="cardDetails")
-  section.card-details.js-card-details(class='{{#if cardMaximized}}card-details-maximized{{/if}}'): .card-details-canvas
+  section.card-details.js-card-details(class='{{#if cardMaximized}}card-details-maximized{{/if}}' class='{{#if isPopup}}card-details-popup{{/if}}'): .card-details-canvas
     .card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
       +inlinedForm(classNames="js-card-details-title")
         +editCardTitleForm
@@ -18,10 +21,14 @@ template(name="cardDetails")
               title="{{_ 'copy-card-link-to-clipboard'}}"
             )
         if isMiniScreen
-          a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details(title="{{_ 'close-card'}}")
+          unless isPopup
+            a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
           if currentUser.isBoardMember
             a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
-            a.fa.fa-link.card-copy-mobile-button
+            a.fa.fa-link.card-copy-mobile-button(
+              class="fa-link"
+              title="{{_ 'copy-card-link-to-clipboard'}}"
+            )
         h2.card-details-title.js-card-title(
           class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
             +viewer

+ 36 - 16
client/components/cards/cardDetails.js

@@ -34,11 +34,14 @@ BlazeComponent.extendComponent({
   onCreated() {
     this.currentBoard = Boards.findOne(Session.get('currentBoard'));
     this.isLoaded = new ReactiveVar(false);
-    const boardBody = this.parentComponent().parentComponent();
-    //in Miniview parent is Board, not BoardBody.
-    if (boardBody !== null) {
-      boardBody.showOverlay.set(true);
-      boardBody.mouseHasEnterCardDetails = false;
+
+    if (this.parentComponent() && this.parentComponent().parentComponent()) {
+      const boardBody = this.parentComponent().parentComponent();
+      //in Miniview parent is Board, not BoardBody.
+      if (boardBody !== null) {
+        boardBody.showOverlay.set(true);
+        boardBody.mouseHasEnterCardDetails = false;
+      }
     }
     this.calculateNextPeak();
 
@@ -215,7 +218,7 @@ BlazeComponent.extendComponent({
       distance: 7,
       start(evt, ui) {
         ui.placeholder.height(ui.helper.height());
-        EscapeActions.executeUpTo('popup-close');
+        EscapeActions.clickExecute(evt.target, 'inlinedForm');
       },
       stop(evt, ui) {
         let prevChecklist = ui.item.prev('.js-checklist').get(0);
@@ -297,6 +300,7 @@ BlazeComponent.extendComponent({
   },
 
   onDestroyed() {
+    if (this.parentComponent() === null) return;
     const parentComponent = this.parentComponent().parentComponent();
     //on mobile view parent is Board, not board body.
     if (parentComponent === null) return;
@@ -408,6 +412,7 @@ BlazeComponent.extendComponent({
         'click .js-show-positive-votes': Popup.open('positiveVoteMembers'),
         'click .js-show-negative-votes': Popup.open('negativeVoteMembers'),
         'mouseenter .js-card-details'() {
+          if (this.parentComponent() === null) return;
           const parentComponent = this.parentComponent().parentComponent();
           //on mobile view parent is Board, not BoardBody.
           if (parentComponent === null) return;
@@ -532,6 +537,22 @@ BlazeComponent.extendComponent({
   },
 }).register('cardDetails');
 
+Template.cardDetails.helpers({
+  isPopup() {
+    let ret = !!Utils.getPopupCardId();
+    return ret;
+  }
+});
+Template.cardDetailsPopup.onDestroyed(() => {
+  Session.delete('popupCard');
+});
+Template.cardDetailsPopup.helpers({
+  popupCard() {
+    const ret = Utils.getPopupCard();
+    return ret;
+  },
+});
+
 BlazeComponent.extendComponent({
   template() {
     return 'exportCard';
@@ -582,16 +603,15 @@ Template.editCardSortOrderForm.onRendered(function () {
       // XXX Recovering the currentCard identifier form a session variable is
       // fragile because this variable may change for instance if the route
       // change. We should use some component props instead.
-      docId: Session.get('currentCard'),
+      docId: Utils.getCurrentCardId(),
     };
   }
 
   close(isReset = false) {
     if (this.isOpen.get() && !isReset) {
       const draft = this.getValue().trim();
-      if (
-        draft !== Cards.findOne(Session.get('currentCard')).getDescription()
-      ) {
+      let card = Utils.getCurrentCard();
+      if (card && draft !== card.getDescription()) {
         UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
       }
     }
@@ -786,7 +806,7 @@ Template.moveCardPopup.events({
   'click .js-done'() {
     // XXX We should *not* get the currentCard from the global state, but
     // instead from a “component” state.
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const bSelect = $('.js-select-boards')[0];
     let boardId;
     // if we are a worker, we won't have a board select so we just use the
@@ -844,7 +864,7 @@ BlazeComponent.extendComponent({
 
 Template.copyCardPopup.events({
   'click .js-done'() {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const lSelect = $('.js-select-lists')[0];
     const listId = lSelect.options[lSelect.selectedIndex].value;
     const slSelect = $('.js-select-swimlanes')[0];
@@ -873,7 +893,7 @@ Template.copyCardPopup.events({
 
 Template.convertChecklistItemToCardPopup.events({
   'click .js-done'() {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const lSelect = $('.js-select-lists')[0];
     const listId = lSelect.options[lSelect.selectedIndex].value;
     const slSelect = $('.js-select-swimlanes')[0];
@@ -901,7 +921,7 @@ Template.convertChecklistItemToCardPopup.events({
 
 Template.copyChecklistToManyCardsPopup.events({
   'click .js-done'() {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const oldId = card._id;
     card._id = null;
     const lSelect = $('.js-select-lists')[0];
@@ -1020,7 +1040,7 @@ BlazeComponent.extendComponent({
   },
 
   cards() {
-    const currentId = Session.get('currentCard');
+    const currentId = Utils.getCurrentCardId();
     if (this.parentBoard.get()) {
       return Cards.find({
         boardId: this.parentBoard.get(),
@@ -1686,7 +1706,7 @@ Template.cardAssigneesPopup.onCreated(function () {
 
 Template.cardAssigneesPopup.events({
   'click .js-select-assignee'(event) {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const assigneeId = this.userId;
     card.toggleAssignee(assigneeId);
     event.preventDefault();

+ 15 - 0
client/components/cards/cardDetails.styl

@@ -315,6 +315,21 @@ input[type="submit"].attachment-add-link-submit
       .minimize-card-details
         margin-right: 40px
 
+  .card-details-popup
+    padding: 0px 10px
+
+  .pop-over > .content-wrapper > .popup-container-depth-0
+    width: 100%
+
+    & > .content
+      width: calc(100% - 10px)
+
+    & > .content > .card-details-popup
+      overflow-y: auto
+
+      & hr
+        margin: 15px 0px
+
 card-details-color(background, color...)
   background: background !important
   if color

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

@@ -18,14 +18,14 @@ template(name="checklists")
 
 
   .card-checklist-items
-    each checklist in currentCard.checklists
+    each checklist in checklists
       +checklistDetail(checklist = checklist)
 
   if canModifyCard
     +inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
       +addChecklistItemForm
     else
-      a.js-open-inlined-form(title="{{_ 'add-checklist'}}")
+      a.add-checklist.js-open-inlined-form(title="{{_ 'add-checklist'}}")
         i.fa.fa-plus
 
 template(name="checklistDetail")

+ 13 - 4
client/components/cards/checklists.js

@@ -16,7 +16,7 @@ function initSorting(items) {
     scroll: false,
     start(evt, ui) {
       ui.placeholder.height(ui.helper.height());
-      EscapeActions.executeUpTo('popup-close');
+      EscapeActions.clickExecute(evt.target, 'inlinedForm');
     },
     stop(evt, ui) {
       const parent = ui.item.parents('.js-checklist-items');
@@ -94,16 +94,14 @@ BlazeComponent.extendComponent({
         title,
         sort: card.checklists().count(),
       });
+      this.closeAllInlinedForms();
       setTimeout(() => {
         this.$('.add-checklist-item')
           .last()
           .click();
       }, 100);
     }
-    textarea.value = '';
-    textarea.focus();
   },
-
   addChecklistItem(event) {
     event.preventDefault();
     const textarea = this.find('textarea.js-add-checklist-item');
@@ -190,6 +188,10 @@ BlazeComponent.extendComponent({
     }
   },
 
+  closeAllInlinedForms() {
+    this.$('.js-close-inlined-form').click();
+  },
+
   events() {
     const events = {
       'click .toggle-delete-checklist-dialog'(event) {
@@ -214,6 +216,8 @@ BlazeComponent.extendComponent({
         'click .js-delete-checklist-item': this.deleteItem,
         'click .confirm-checklist-delete': this.deleteChecklist,
         'focus .js-add-checklist-item': this.focusChecklistItem,
+        'click .add-checklist-item.js-open-inlined-form': this.closeAllInlinedForms,
+        'click .add-checklist.js-open-inlined-form': this.closeAllInlinedForms,
         keydown: this.pressKey,
       },
     ];
@@ -262,6 +266,11 @@ BlazeComponent.extendComponent({
 }).register('boardsSwimlanesAndLists');
 
 Template.checklists.helpers({
+  checklists() {
+    const card = Cards.findOne(this.cardId);
+    const ret = card.checklists();
+    return ret;
+  },
   hideCheckedItems() {
     const currentUser = Meteor.user();
     if (currentUser) return currentUser.hasHideCheckedItems();

+ 1 - 1
client/components/cards/labels.js

@@ -41,7 +41,7 @@ Template.createLabelPopup.helpers({
 
 Template.cardLabelsPopup.events({
   'click .js-select-label'(event) {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const labelId = this._id;
     card.toggleLabel(labelId);
     event.preventDefault();

+ 2 - 6
client/components/cards/minicard.jade

@@ -2,14 +2,10 @@ template(name="minicard")
   .minicard(
     class="{{#if isLinkedCard}}linked-card{{/if}}"
     class="{{#if isLinkedBoard}}linked-board{{/if}}"
-    class="minicard-{{colorClass}}")
-    if isMiniScreen
+    class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
+    if isMiniScreenOrShowDesktopDragHandles
       .handle
         .fa.fa-arrows
-    unless isMiniScreen
-      if showDesktopDragHandles
-        .handle
-          .fa.fa-arrows
     if cover
       .minicard-cover(style="background-image: url('{{cover.url}}');")
     if labels

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

@@ -75,16 +75,6 @@ BlazeComponent.extendComponent({
 }).register('minicard');
 
 Template.minicard.helpers({
-  showDesktopDragHandles() {
-    currentUser = Meteor.user();
-    if (currentUser) {
-      return (currentUser.profile || {}).showDesktopDragHandles;
-    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-      return true;
-    } else {
-      return false;
-    }
-  },
   hiddenMinicardLabelText() {
     currentUser = Meteor.user();
     if (currentUser) {

+ 2 - 26
client/components/lists/list.js

@@ -117,22 +117,11 @@ BlazeComponent.extendComponent({
     });
 
     this.autorun(() => {
-      let showDesktopDragHandles = false;
-      currentUser = Meteor.user();
-      if (currentUser) {
-        showDesktopDragHandles = (currentUser.profile || {})
-          .showDesktopDragHandles;
-      } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-        showDesktopDragHandles = true;
-      } else {
-        showDesktopDragHandles = false;
-      }
-
-      if (Utils.isMiniScreen() || showDesktopDragHandles) {
+      if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
         $cards.sortable({
           handle: '.handle',
         });
-      } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
+      } else {
         $cards.sortable({
           handle: '.minicard',
         });
@@ -178,19 +167,6 @@ BlazeComponent.extendComponent({
   },
 }).register('list');
 
-Template.list.helpers({
-  showDesktopDragHandles() {
-    currentUser = Meteor.user();
-    if (currentUser) {
-      return (currentUser.profile || {}).showDesktopDragHandles;
-    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-      return true;
-    } else {
-      return false;
-    }
-  },
-});
-
 Template.miniList.events({
   'click .js-select-list'() {
     const listId = this._id;

+ 10 - 0
client/components/lists/listBody.js

@@ -148,6 +148,10 @@ BlazeComponent.extendComponent({
       // 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 (Utils.isMiniScreen()) {
+      evt.preventDefault();
+      Session.set('popupCard', this.currentData()._id);
+      this.cardDetailsPopup(evt);
     } else if (Session.equals('currentCard', this.currentData()._id)) {
       evt.stopImmediatePropagation();
       evt.preventDefault();
@@ -216,6 +220,12 @@ BlazeComponent.extendComponent({
     );
   },
 
+  cardDetailsPopup(event) {
+    if (!Popup.isOpen()) {
+      Popup.open("cardDetails")(event);
+    }
+  },
+
   events() {
     return [
       {

+ 1 - 1
client/components/lists/listHeader.jade

@@ -44,7 +44,7 @@ template(name="listHeader")
               a.js-add-card.fa.fa-plus.list-header-plus-icon(title="{{_ 'add-card-to-top-of-list'}}")
             a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
           if currentUser.isBoardAdmin
-            if showDesktopDragHandles
+            if isShowDesktopDragHandles
               a.list-header-handle.handle.fa.fa-arrows.js-list-handle
 
 template(name="editListTitleForm")

+ 1 - 12
client/components/lists/listHeader.js

@@ -122,18 +122,7 @@ BlazeComponent.extendComponent({
 Template.listHeader.helpers({
   isBoardAdmin() {
     return Meteor.user().isBoardAdmin();
-  },
-
-  showDesktopDragHandles() {
-    currentUser = Meteor.user();
-    if (currentUser) {
-      return (currentUser.profile || {}).showDesktopDragHandles;
-    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-      return true;
-    } else {
-      return false;
-    }
-  },
+  }
 });
 
 Template.listActionPopup.helpers({

+ 1 - 1
client/components/main/editor.js

@@ -150,7 +150,7 @@ Template.editor.onRendered(() => {
               const $summernote = getSummernote(this);
               if (files && files.length > 0) {
                 const image = files[0];
-                const currentCard = Cards.findOne(Session.get('currentCard'));
+                const currentCard = Utils.getCurrentCard();
                 const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
                 const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
                 const insertImage = src => {

+ 1 - 12
client/components/main/popup.styl

@@ -14,8 +14,7 @@ $popupWidth = 300px
   margin-top: 5px
 
   hr
-    margin: 4px -10px
-    width: $popupWidth
+    margin: 4px 0px
 
   p,
   textarea,
@@ -23,7 +22,6 @@ $popupWidth = 300px
   input[type="email"],
   input[type="password"],
   input[type="file"]
-    margin: 4px 0 12px
     width: 100%
 
   select
@@ -313,22 +311,13 @@ $popupWidth = 300px
         input[type="email"],
         input[type="password"],
         input[type="file"]
-          margin: 4px 0 12px
           width: 100%
           box-sizing: border-box
 
     .pop-over-list
       li > a
         width: calc(100% - 20px)
-        padding: 10px 10px
         margin: 0px 0px
-        border-bottom: 1px solid #eee
-
-    hr
-      width: 100%
-      height: 20px
-      margin: 0px 0px
-      color: #eee
 
     for depth in (1..6)
       .popup-container-depth-{depth}

+ 1 - 1
client/components/swimlanes/swimlaneHeader.jade

@@ -26,7 +26,7 @@ template(name="swimlaneFixedHeader")
         a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}")
         a.fa.fa-navicon.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}")
       unless isMiniScreen
-        if showDesktopDragHandles
+        if isShowDesktopDragHandles
           a.swimlane-header-handle.handle.fa.fa-arrows.js-swimlane-header-handle
       if isMiniScreen
         a.swimlane-header-miniscreen-handle.handle.fa.fa-arrows.js-swimlane-header-handle

+ 0 - 13
client/components/swimlanes/swimlaneHeader.js

@@ -28,19 +28,6 @@ BlazeComponent.extendComponent({
   },
 }).register('swimlaneHeader');
 
-Template.swimlaneHeader.helpers({
-  showDesktopDragHandles() {
-    currentUser = Meteor.user();
-    if (currentUser) {
-      return (currentUser.profile || {}).showDesktopDragHandles;
-    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-      return true;
-    } else {
-      return false;
-    }
-  },
-});
-
 Template.swimlaneFixedHeader.helpers({
   isBoardAdmin() {
     return Meteor.user().isBoardAdmin();

+ 6 - 38
client/components/swimlanes/swimlanes.js

@@ -9,7 +9,7 @@ function currentListIsInThisSwimlane(swimlaneId) {
 }
 
 function currentCardIsInThisList(listId, swimlaneId) {
-  const currentCard = Cards.findOne(Session.get('currentCard'));
+  const currentCard = Utils.getCurrentCard();
   const currentUser = Meteor.user();
   if (
     currentUser &&
@@ -95,22 +95,11 @@ function initSortable(boardComponent, $listsDom) {
   //}
 
   boardComponent.autorun(() => {
-    let showDesktopDragHandles = false;
-    currentUser = Meteor.user();
-    if (currentUser) {
-      showDesktopDragHandles = (currentUser.profile || {})
-        .showDesktopDragHandles;
-    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-      showDesktopDragHandles = true;
-    } else {
-      showDesktopDragHandles = false;
-    }
-
-    if (Utils.isMiniScreen() || showDesktopDragHandles) {
+    if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
       $listsDom.sortable({
         handle: '.js-list-handle',
       });
-    } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
+    } else {
       $listsDom.sortable({
         handle: '.js-list-header',
       });
@@ -136,7 +125,7 @@ BlazeComponent.extendComponent({
     const boardComponent = this.parentComponent();
     const $listsDom = this.$('.js-lists');
 
-    if (!Session.get('currentCard')) {
+    if (!Utils.getCurrentCardId()) {
       boardComponent.scrollLeft();
     }
 
@@ -172,19 +161,8 @@ BlazeComponent.extendComponent({
           // the user will legitimately expect to be able to select some text with
           // his mouse.
 
-          let showDesktopDragHandles = false;
-          currentUser = Meteor.user();
-          if (currentUser) {
-            showDesktopDragHandles = (currentUser.profile || {})
-              .showDesktopDragHandles;
-          } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-            showDesktopDragHandles = true;
-          } else {
-            showDesktopDragHandles = false;
-          }
-
           const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
-            Utils.isMiniScreen() || showDesktopDragHandles
+            Utils.isMiniScreenOrShowDesktopDragHandles()
               ? ['.js-list-handle', '.js-swimlane-header-handle']
               : ['.js-list-header'],
           );
@@ -264,16 +242,6 @@ BlazeComponent.extendComponent({
 }).register('addListForm');
 
 Template.swimlane.helpers({
-  showDesktopDragHandles() {
-    currentUser = Meteor.user();
-    if (currentUser) {
-      return (currentUser.profile || {}).showDesktopDragHandles;
-    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-      return true;
-    } else {
-      return false;
-    }
-  },
   canSeeAddList() {
     return Meteor.user().isBoardAdmin();
     /*
@@ -316,7 +284,7 @@ BlazeComponent.extendComponent({
     const boardComponent = this.parentComponent();
     const $listsDom = this.$('.js-lists');
 
-    if (!Session.get('currentCard')) {
+    if (!Utils.getCurrentCardId()) {
       boardComponent.scrollLeft();
     }
 

+ 1 - 1
client/components/users/userAvatar.js

@@ -274,7 +274,7 @@ Template.cardMembersPopup.helpers({
 
 Template.cardMembersPopup.events({
   'click .js-select-member'(event) {
-    const card = Cards.findOne(Session.get('currentCard'));
+    const card = Utils.getCurrentCard();
     const memberId = this.userId;
     card.toggleMember(memberId);
     event.preventDefault();

+ 1 - 1
client/components/users/userHeader.jade

@@ -134,7 +134,7 @@ template(name="changeSettingsPopup")
       a.js-toggle-desktop-drag-handles
         i.fa.fa-arrows
         | {{_ 'show-desktop-drag-handles'}}
-        if showDesktopDragHandles
+        if isShowDesktopDragHandles
           i.fa.fa-check
     unless currentUser.isWorker
       li

+ 0 - 10
client/components/users/userHeader.js

@@ -259,16 +259,6 @@ Template.changeLanguagePopup.events({
 });
 
 Template.changeSettingsPopup.helpers({
-  showDesktopDragHandles() {
-    currentUser = Meteor.user();
-    if (currentUser) {
-      return (currentUser.profile || {}).showDesktopDragHandles;
-    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
-      return true;
-    } else {
-      return false;
-    }
-  },
   hiddenSystemMessages() {
     currentUser = Meteor.user();
     if (currentUser) {

+ 2 - 6
client/config/blazeHelpers.js

@@ -8,12 +8,8 @@ Blaze.registerHelper('currentBoard', () => {
 });
 
 Blaze.registerHelper('currentCard', () => {
-  const cardId = Session.get('currentCard');
-  if (cardId) {
-    return Cards.findOne(cardId);
-  } else {
-    return null;
-  }
+  const ret = Utils.getCurrentCard();
+  return ret;
 });
 
 Blaze.registerHelper('currentList', () => {

+ 2 - 2
client/lib/datepicker.js

@@ -120,7 +120,7 @@ export class DatePicker extends BlazeComponent {
           }
           if (newCompleteDate.isValid()) {
             this._storeDate(newCompleteDate.toDate());
-            Popup.close();
+            Popup.back();
           } else if (!this.error) {
             this.error.set('invalid');
           }
@@ -128,7 +128,7 @@ export class DatePicker extends BlazeComponent {
         'click .js-delete-date'(evt) {
           evt.preventDefault();
           this._deleteDate();
-          Popup.close();
+          Popup.back();
         },
       },
     ];

+ 9 - 4
client/lib/inlinedform.js

@@ -29,10 +29,15 @@ InlinedForm = BlazeComponent.extendComponent({
   },
 
   open(evt) {
-    evt && evt.preventDefault();
+    if (evt) {
+      evt.preventDefault();
+      // Close currently opened form, if any
+      EscapeActions.clickExecute(evt.target, 'inlinedForm');
+    } else {
+      // Close currently opened form, if any
+      EscapeActions.executeUpTo('inlinedForm');
+    }
 
-    // Close currently opened form, if any
-    EscapeActions.executeUpTo('inlinedForm');
     this.isOpen.set(true);
     currentlyOpenedForm.set(this);
   },
@@ -44,7 +49,7 @@ InlinedForm = BlazeComponent.extendComponent({
 
   getValue() {
     const input = this.find('textarea,input[type=text]');
-    return this.isOpen.get() && input && input.value;
+    return this.isOpen.get() && input && input.value.replaceAll(/\s +$/gm, '');
   },
 
   events() {

+ 1 - 1
client/lib/keyboard.js

@@ -54,7 +54,7 @@ Mousetrap.bind('/', () => {
 });
 
 Mousetrap.bind(['down', 'up'], (evt, key) => {
-  if (!Session.get('currentCard')) {
+  if (!Utils.getCurrentCardId()) {
     return;
   }
 

+ 1 - 1
client/lib/popup.js

@@ -201,7 +201,7 @@ escapeActions.forEach(actionName => {
     () => Popup[actionName](),
     () => Popup.isOpen(),
     {
-      noClickEscapeOn: '.js-pop-over,.js-open-card-title-popup',
+      noClickEscapeOn: '.js-pop-over,.js-open-card-title-popup,.js-open-inlined-form',
       enabledOnClick: actionName === 'close',
     },
   );

+ 23 - 0
client/lib/utils.js

@@ -1,4 +1,25 @@
 Utils = {
+  getCurrentCardId(ignorePopupCard) {
+    let ret = Session.get('currentCard');
+    if (!ret && !ignorePopupCard) {
+      ret = Utils.getPopupCardId();
+    }
+    return ret;
+  },
+  getPopupCardId() {
+    const ret = Session.get('popupCard');
+    return ret;
+  },
+  getCurrentCard(ignorePopupCard) {
+    const cardId = Utils.getCurrentCardId(ignorePopupCard);
+    const ret = Cards.findOne(cardId);
+    return ret;
+  },
+  getPopupCard() {
+    const cardId = Utils.getPopupCardId();
+    const ret = Cards.findOne(cardId);
+    return ret;
+  },
   reload () {
     // we move all window.location.reload calls into this function
     // so we can disable it when running tests.
@@ -248,6 +269,8 @@ Utils = {
     const currentUser = Meteor.user();
     if (currentUser) {
       return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (window.localStorage.getItem('showDesktopDragHandles')) {
+      return true;
     } else {
       return false;
     }

+ 9 - 0
config/router.js

@@ -12,6 +12,7 @@ FlowRouter.route('/', {
     Session.set('currentBoard', null);
     Session.set('currentList', null);
     Session.set('currentCard', null);
+    Session.set('popupCard', null);
 
     Filter.reset();
     Session.set('sortBy', '');
@@ -34,6 +35,7 @@ FlowRouter.route('/public', {
     Session.set('currentBoard', null);
     Session.set('currentList', null);
     Session.set('currentCard', null);
+    Session.set('popupCard', null);
 
     Filter.reset();
     Session.set('sortBy', '');
@@ -56,6 +58,7 @@ FlowRouter.route('/b/:id/:slug', {
     const previousBoard = Session.get('currentBoard');
     Session.set('currentBoard', currentBoard);
     Session.set('currentCard', null);
+    Session.set('popupCard', null);
 
     // If we close a card, we'll execute again this route action but we don't
     // want to excape every current actions (filters, etc.)
@@ -84,6 +87,7 @@ FlowRouter.route('/b/:boardId/:slug/:cardId', {
 
     Session.set('currentBoard', params.boardId);
     Session.set('currentCard', params.cardId);
+    Session.set('popupCard', null);
 
     Utils.manageCustomUI();
     Utils.manageMatomo();
@@ -212,6 +216,7 @@ FlowRouter.route('/import/:source', {
     Session.set('currentBoard', null);
     Session.set('currentList', null);
     Session.set('currentCard', null);
+    Session.set('popupCard', null);
     Session.set('importSource', params.source);
 
     Filter.reset();
@@ -232,6 +237,7 @@ FlowRouter.route('/setting', {
       Session.set('currentBoard', null);
       Session.set('currentList', null);
       Session.set('currentCard', null);
+      Session.set('popupCard', null);
 
       Filter.reset();
       Session.set('sortBy', '');
@@ -255,6 +261,7 @@ FlowRouter.route('/information', {
       Session.set('currentBoard', null);
       Session.set('currentList', null);
       Session.set('currentCard', null);
+      Session.set('popupCard', null);
 
       Filter.reset();
       Session.set('sortBy', '');
@@ -277,6 +284,7 @@ FlowRouter.route('/people', {
       Session.set('currentBoard', null);
       Session.set('currentList', null);
       Session.set('currentCard', null);
+      Session.set('popupCard', null);
 
       Filter.reset();
       Session.set('sortBy', '');
@@ -299,6 +307,7 @@ FlowRouter.route('/admin-reports', {
       Session.set('currentBoard', null);
       Session.set('currentList', null);
       Session.set('currentCard', null);
+      Session.set('popupCard', null);
 
       Filter.reset();
       Session.set('sortBy', '');

+ 2 - 1
i18n/en.i18n.json

@@ -1086,5 +1086,6 @@
   "request": "Request",
   "requests": "Requests",
   "help-request": "Help Request",
-  "editCardSortOrderPopup-title": "Change Sorting"
+  "editCardSortOrderPopup-title": "Change Sorting",
+  "cardDetailsPopup-title": "Card Details"
 }