Browse Source

Collapse Lists.

Thanks to xet7 !
Lauri Ojansivu 1 year ago
parent
commit
a601ba542a

+ 28 - 1
client/components/lists/list.css

@@ -7,7 +7,7 @@
   border-left: 1px solid #ccc;
   border-left: 1px solid #ccc;
   padding: 0;
   padding: 0;
   float: left;
   float: left;
-  min-width: 100px; /* TODO(mark-i-m): hardcoded? */
+  /* min-width: 100px; TODO(mark-i-m): hardcoded? */
   /*max-width: 270px;*/
   /*max-width: 270px;*/
   /* Reverted incomplete change list width: */
   /* Reverted incomplete change list width: */
   /* https://github.com/wekan/wekan/issues/4558 */
   /* https://github.com/wekan/wekan/issues/4558 */
@@ -82,6 +82,16 @@
   text-overflow: ellipsis;
   text-overflow: ellipsis;
   word-wrap: break-word;
   word-wrap: break-word;
 }
 }
+.list-rotated {
+  width: 10px;
+  margin-top: 10px;
+  margin-left: 0;
+  margin-right: 0;
+  transform: rotate(90deg);
+  position: relative;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
 .list-header .list-header-watch-icon {
 .list-header .list-header-watch-icon {
   padding-left: 10px;
   padding-left: 10px;
   color: #a6a6a6;
   color: #a6a6a6;
@@ -99,6 +109,23 @@
   color: #a6a6a6;
   color: #a6a6a6;
   margin-right: 15px;
   margin-right: 15px;
 }
 }
+.list-header .list-header-collapse-right {
+  color: #a6a6a6;
+}
+.list-header .list-header-collapse-left {
+  color: #a6a6a6;
+  margin-right: 15px;
+}
+.list-header .list-header-uncollapse-left {
+  color: #a6a6a6;
+}
+.list-header .list-header-uncollapse-right {
+  color: #a6a6a6;
+}
+.list-header .list-header-collapse {
+  color: #a6a6a6;
+  margin-right: 15px;
+}
 .list-header .highlight {
 .list-header .highlight {
   color: #ce1414;
   color: #ce1414;
 }
 }

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

@@ -1,6 +1,7 @@
 template(name='list')
 template(name='list')
   .list.js-list(id="js-list-{{_id}}"
   .list.js-list(id="js-list-{{_id}}"
-                style="width:{{listWidth}}px;")
+                style="{{#unless collapsed}}width:{{listWidth}}px;{{/unless}}"
+                class="{{#if collapsed}}list-collapsed{{/if}}")
     +listHeader
     +listHeader
     +listBody
     +listBody
 
 

+ 32 - 31
client/components/lists/listBody.jade

@@ -1,37 +1,38 @@
 template(name="listBody")
 template(name="listBody")
-  .list-body
-    .minicards.clearfix.js-minicards(class="{{#if reachedWipLimit}}js-list-full{{/if}}")
-      if cards.length
-        +inlinedForm(autoclose=false position="top")
-          +addCardForm(listId=_id position="top")
-      ul.sidebar-list
-        each customFieldsSum
-          li
-            +viewer
-              = name
-            if $eq customFieldsSum.type "number"
+  unless collapsed
+    .list-body
+      .minicards.clearfix.js-minicards(class="{{#if reachedWipLimit}}js-list-full{{/if}}")
+        if cards.length
+          +inlinedForm(autoclose=false position="top")
+            +addCardForm(listId=_id position="top")
+        ul.sidebar-list
+          each customFieldsSum
+            li
               +viewer
               +viewer
-                = value
-            if $eq customFieldsSum.type "currency"
-              +viewer
-                = formattedCurrencyCustomFieldValue(value)
-      each (cardsWithLimit (idOrNull ../../_id))
-        a.minicard-wrapper.js-minicard(href=originRelativeUrl
-          class="{{#if cardIsSelected}}is-selected{{/if}}"
-          class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
-          if MultiSelection.isActive
-            .materialCheckBox.multi-selection-checkbox.js-toggle-multi-selection(
-              class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
-          +minicard(this)
-      if (showSpinner (idOrNull ../../_id))
-        +spinnerList
+                = name
+              if $eq customFieldsSum.type "number"
+                +viewer
+                  = value
+              if $eq customFieldsSum.type "currency"
+                +viewer
+                  = formattedCurrencyCustomFieldValue(value)
+        each (cardsWithLimit (idOrNull ../../_id))
+          a.minicard-wrapper.js-minicard(href=originRelativeUrl
+            class="{{#if cardIsSelected}}is-selected{{/if}}"
+            class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
+            if MultiSelection.isActive
+              .materialCheckBox.multi-selection-checkbox.js-toggle-multi-selection(
+                class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
+            +minicard(this)
+        if (showSpinner (idOrNull ../../_id))
+          +spinnerList
 
 
-      if canSeeAddCard
-        +inlinedForm(autoclose=false position="bottom")
-          +addCardForm(listId=_id position="bottom")
-        else
-          a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
-            i.fa.fa-plus
+        if canSeeAddCard
+          +inlinedForm(autoclose=false position="bottom")
+            +addCardForm(listId=_id position="bottom")
+          else
+            a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
+              i.fa.fa-plus
 
 
 template(name="spinnerList")
 template(name="spinnerList")
   .sk-spinner.sk-spinner-list(
   .sk-spinner.sk-spinner-list(

+ 48 - 23
client/components/lists/listHeader.jade

@@ -5,22 +5,43 @@ template(name="listHeader")
     +inlinedForm
     +inlinedForm
       +editListTitleForm
       +editListTitleForm
     else
     else
+      unless isMiniScreen
+        if collapsed
+          a.js-collapse(title="{{_ 'uncollapse'}}")
+            i.fa.fa-arrow-left.list-header-uncollapse-left
+            i.fa.fa-arrow-right.list-header-uncollapse-right
       if isMiniScreen
       if isMiniScreen
         if currentList
         if currentList
           a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
           a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
-      h2.list-header-name(
-        title="{{ moment modifiedAt 'LLL' }}"
-        class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
-        +viewer
-          = title
-        if wipLimit.enabled
-         | (
-         span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
-         |/#{wipLimit.value})
-
-      if showCardsCountForList cards.length
-        span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
-
+      unless isMiniScreen
+        if collapsed
+          if showCardsCountForList cards.length
+            br
+            span.cardCount {{cardsCount}}
+      if isMiniScreen
+        h2.list-header-name(
+          title="{{ moment modifiedAt 'LLL' }}"
+          class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
+          +viewer
+            = title
+          if wipLimit.enabled
+           | (
+           span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
+           |/#{wipLimit.value})
+      unless isMiniScreen
+        div(class="{{#if collapsed}}list-rotated{{/if}}")
+          h2.list-header-name(
+            title="{{ moment modifiedAt 'LLL' }}"
+            class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
+            +viewer
+              = title
+            if wipLimit.enabled
+             | (
+             span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
+             |/#{wipLimit.value})
+        unless collapsed
+          if showCardsCountForList cards.length
+            span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
       if isMiniScreen
       if isMiniScreen
         if currentList
         if currentList
           if isWatching
           if isWatching
@@ -36,16 +57,20 @@ template(name="listHeader")
       else if currentUser.isBoardMember
       else if currentUser.isBoardMember
         if isWatching
         if isWatching
           i.list-header-watch-icon.fa.fa-eye
           i.list-header-watch-icon.fa.fa-eye
-        div.list-header-menu
-          unless currentUser.isCommentOnly
-            //if isBoardAdmin
-            //  a.fa.js-list-star.list-header-plus-top(class="fa-star{{#unless starred}}-o{{/unless}}")
-            if canSeeAddCard
-              a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
-            a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
-          if currentUser.isBoardAdmin
-            if isTouchScreenOrShowDesktopDragHandles
-              a.list-header-handle.handle.fa.fa-arrows.js-list-handle
+        unless collapsed
+          div.list-header-menu
+            unless currentUser.isCommentOnly
+              //if isBoardAdmin
+              //  a.fa.js-list-star.list-header-plus-top(class="fa-star{{#unless starred}}-o{{/unless}}")
+              if canSeeAddCard
+                a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
+                a.js-collapse(title="{{_ 'collapse'}}")
+                  i.fa.fa-arrow-right.list-header-collapse-right
+                  i.fa.fa-arrow-left.list-header-collapse-left
+                a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
+            if currentUser.isBoardAdmin
+              if isTouchScreenOrShowDesktopDragHandles
+                a.list-header-handle.handle.fa.fa-arrows.js-list-handle
 
 
 template(name="editListTitleForm")
 template(name="editListTitleForm")
   .list-composer
   .list-composer

+ 15 - 0
client/components/lists/listHeader.js

@@ -31,6 +31,17 @@ BlazeComponent.extendComponent({
       return !status;
       return !status;
     }
     }
   },
   },
+  collapsed(check = undefined) {
+    const list = Template.currentData();
+    const status = list.isCollapsed();
+    if (check === undefined) {
+      // just check
+      return status;
+    } else {
+      list.collapse(!status);
+      return !status;
+    }
+  },
   editTitle(event) {
   editTitle(event) {
     event.preventDefault();
     event.preventDefault();
     const newTitle = this.childComponents('inlinedForm')[0]
     const newTitle = this.childComponents('inlinedForm')[0]
@@ -104,6 +115,10 @@ BlazeComponent.extendComponent({
           event.preventDefault();
           event.preventDefault();
           this.starred(!this.starred());
           this.starred(!this.starred());
         },
         },
+        'click .js-collapse'(event) {
+          event.preventDefault();
+          this.collapsed(!this.collapsed());
+        },
         'click .js-open-list-menu': Popup.open('listAction'),
         'click .js-open-list-menu': Popup.open('listAction'),
         'click .js-add-card.list-header-plus-top'(event) {
         'click .js-add-card.list-header-plus-top'(event) {
           const listDom = $(event.target).parents(
           const listDom = $(event.target).parents(

+ 3 - 1
imports/i18n/data/en.i18n.json

@@ -1246,5 +1246,7 @@
   "show-checklist-at-minicard": "Show checklist at minicard",
   "show-checklist-at-minicard": "Show checklist at minicard",
   "show-subtasks-field": "Show subtasks field",
   "show-subtasks-field": "Show subtasks field",
   "convert-to-markdown": "Convert to markdown",
   "convert-to-markdown": "Convert to markdown",
-  "import-board-zip": "Add .zip file that has board JSON files, and board name subdirectories with attachments"
+  "import-board-zip": "Add .zip file that has board JSON files, and board name subdirectories with attachments",
+  "collapse": "Collapse",
+  "uncollapse": "Uncollapse"
 }
 }

+ 14 - 0
models/lists.js

@@ -156,6 +156,13 @@ Lists.attachSchema(
       type: String,
       type: String,
       defaultValue: 'list',
       defaultValue: 'list',
     },
     },
+    collapsed: {
+      /**
+       * is the list collapsed
+       */
+      type: Boolean,
+      defaultValue: false,
+    },
   }),
   }),
 );
 );
 
 
@@ -286,6 +293,10 @@ Lists.helpers({
     return this.starred === true;
     return this.starred === true;
   },
   },
 
 
+  isCollapsed() {
+    return this.collapsed === true;
+  },
+
   absoluteUrl() {
   absoluteUrl() {
     const card = ReactiveCache.getCard({ listId: this._id });
     const card = ReactiveCache.getCard({ listId: this._id });
     return card && card.absoluteUrl();
     return card && card.absoluteUrl();
@@ -306,6 +317,9 @@ Lists.mutations({
   star(enable = true) {
   star(enable = true) {
     return { $set: { starred: !!enable } };
     return { $set: { starred: !!enable } };
   },
   },
+  collapse(enable = true) {
+    return { $set: { collapsed: !!enable } };
+  },
 
 
   archive() {
   archive() {
     if (this.isTemplateList()) {
     if (this.isTemplateList()) {

+ 15 - 0
models/swimlanes.js

@@ -107,6 +107,13 @@ Swimlanes.attachSchema(
       type: String,
       type: String,
       defaultValue: 'swimlane',
       defaultValue: 'swimlane',
     },
     },
+    collapsed: {
+      /**
+       * is the swimlane collapsed
+       */
+      type: Boolean,
+      defaultValue: false,
+    },
   }),
   }),
 );
 );
 
 
@@ -232,6 +239,10 @@ Swimlanes.helpers({
     return ret;
     return ret;
   },
   },
 
 
+  isCollapsed() {
+    return this.collapsed === true;
+  },
+
   board() {
   board() {
     return ReactiveCache.getBoard(this.boardId);
     return ReactiveCache.getBoard(this.boardId);
   },
   },
@@ -274,6 +285,10 @@ Swimlanes.mutations({
     return { $set: { title } };
     return { $set: { title } };
   },
   },
 
 
+  collapse(enable = true) {
+    return { $set: { collapsed: !!enable } };
+  },
+
   archive() {
   archive() {
     if (this.isTemplateSwimlane()) {
     if (this.isTemplateSwimlane()) {
       this.myLists().forEach(list => {
       this.myLists().forEach(list => {

+ 2 - 1
server/publications/boards.js

@@ -61,6 +61,7 @@ Meteor.publishRelations('boards', function() {
               title: 1,
               title: 1,
               boardId: 1,
               boardId: 1,
               archived: 1,
               archived: 1,
+              collapsed: 1,
               sort: 1
               sort: 1
             }
             }
           },
           },
@@ -221,7 +222,7 @@ Meteor.publishRelations('board', function(boardId, isArchived) {
     ),
     ),
     function(boardId, board) {
     function(boardId, board) {
       this.cursor(ReactiveCache.getLists({ boardId, archived: isArchived }, {}, true));
       this.cursor(ReactiveCache.getLists({ boardId, archived: isArchived }, {}, true));
-      this.cursor(ReactiveCache.getSwimlanes({ boardId, archived: isArchived }, {}, true));
+      this.cursor(ReactiveCache.getSwimlanes({ boardId, collapsed: 1, archived: isArchived }, {}, true));
       this.cursor(ReactiveCache.getIntegrations({ boardId }, {}, true));
       this.cursor(ReactiveCache.getIntegrations({ boardId }, {}, true));
       this.cursor(ReactiveCache.getCardCommentReactions({ boardId }, {}, true));
       this.cursor(ReactiveCache.getCardCommentReactions({ boardId }, {}, true));
       this.cursor(
       this.cursor(