Commit 8cdf8f1f authored by Martin Hanzel's avatar Martin Hanzel
Browse files

Allow collapsing all issue boards

All issue boards can now be collapsed via a button, re-ordered by
dragging the header, and the vertical collapsed header style was
reworked.
parent f4605ad8
Loading
Loading
Loading
Loading
+21 −11
Original line number Diff line number Diff line
import $ from 'jquery';
import Sortable from 'sortablejs';
import Vue from 'vue';
import { n__ } from '~/locale';
@@ -59,6 +60,10 @@ export default Vue.extend({
        (!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank')
      );
    },
    uniqueKey() {
      // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
      return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
    },
  },
  watch: {
    filter: {
@@ -72,31 +77,35 @@ export default Vue.extend({
    },
  },
  mounted() {
    const instance = this;

    this.sortableOptions = getBoardSortableDefaultOptions({
      disabled: this.disabled,
      group: 'boards',
      draggable: '.is-draggable',
      handle: '.js-board-handle',
      onEnd: e => {
      onEnd: function onEnd(e) {
        sortableEnd();

        const sortable = this;

        if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
          const order = this.sortable.toArray();
          const order = sortable.toArray();
          const list = boardsStore.findList('id', parseInt(e.item.dataset.id, 10));

          this.$nextTick(() => {
          instance.$nextTick(() => {
            boardsStore.moveList(list, order);
          });
        }
      },
    });

    this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
    Sortable.create(this.$el.parentNode, this.sortableOptions);
  },
  created() {
    if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
      const isCollapsed =
        localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
        localStorage.getItem(`${this.uniqueKey}.expanded`) === 'false';

      this.list.isExpanded = !isCollapsed;
    }
@@ -105,16 +114,17 @@ export default Vue.extend({
    showNewIssueForm() {
      this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
    },
    toggleExpanded(e) {
      if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
    toggleExpanded() {
      if (this.list.isExpandable) {
        this.list.isExpanded = !this.list.isExpanded;

        if (AccessorUtilities.isLocalStorageAccessSafe()) {
          localStorage.setItem(
            `boards.${this.boardId}.${this.list.type}.expanded`,
            this.list.isExpanded,
          );
          localStorage.setItem(`${this.uniqueKey}.expanded`, this.list.isExpanded);
        }

        // When expanding/collapsing, the tooltip on the caret button sometimes stays open.
        // Close all tooltips manually to prevent dangling tooltips.
        $('.tooltip').tooltip('hide');
      }
    },
  },
+1 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ export function getBoardSortableDefaultOptions(obj) {
    'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);

  const defaultSortOptions = Object.assign({}, sortableConfig, {
    filter: '.board-delete, .btn',
    filter: '.no-drag',
    delay: touchEnabled ? 100 : 0,
    scrollSensitivity: touchEnabled ? 60 : 100,
    scrollSpeed: 20,
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ class List {

    const typeInfo = this.getTypeInfo(this.type);
    this.preset = Boolean(typeInfo.isPreset);
    this.isExpandable = Boolean(typeInfo.isExpandable);
    this.isExpandable = true;
    this.isExpanded = true;
    this.page = 1;
    this.loading = true;
+42 −13
Original line number Diff line number Diff line
@@ -92,9 +92,19 @@
    width: 400px;
  }

  &.is-expandable {
    .board-header {
  .board-title-collapse {
    margin-left: -10px;
  }

  .board-title-expand,
  .board-title-collapse {
    cursor: pointer;
    border-radius: 4px;
    padding: 4px;

    &:hover {
      background-color: rgb(235, 235, 235);
      transition: background-color 0.1s linear;
    }
  }

@@ -102,20 +112,36 @@
    width: 50px;

    .board-title {
      flex-direction: column;
      height: 100%;
      padding: 10px 0;
    }

    .board-title-text {
      flex-grow: 0;

      > span {
        width: 100%;
        margin-top: -12px;
        display: block;
        transform: rotate(90deg) translate(35px, 0);
        overflow: initial;
        height: 16px;

        // Force the height to be equal to the parent's width while centering the contents.
        // The contents *should* be about 16 px.
        margin: calc(50% - 8px) 0;

        transform: rotate(90deg);
        transform-origin: center;
      }
    }

    .board-title-expandable-toggle {
      position: absolute;
      top: 50%;
      left: 50%;
      margin-left: -10px;
    .issue-count-badge {
      border: 0;

      > span {
        height: 16px;
        margin: calc(50% - 8px) 0;
        transform: rotate(90deg);
        transform-origin: center;
      }
    }
  }
}
@@ -152,12 +178,15 @@
}

.board-title {
  align-items: center;
  font-size: 1em;
  border-bottom: 1px solid $border-color;
  padding: 8px 16px;
}

.board-title-text {
  margin: $gl-vert-padding auto $gl-vert-padding 0;
  flex-grow: 1;
  margin: $gl-vert-padding 0 $gl-vert-padding 0;
}

.board-delete {
+37 −21
Original line number Diff line number Diff line
.board.d-inline-block.h-100.px-2.align-top.ws-normal{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded, "board-type-assignee": list.type === "assignee" }',
  ":data-id" => "list.id" }
  .board-inner.d-flex.flex-column.position-relative.h-100.rounded
    %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color, "position-relative": list.isExpanded, "position-absolute position-top-0 position-left-0 w-100 h-100": !list.isExpanded }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" }
      %h3.board-title.m-0.d-flex.align-items-center.py-2.px-3.js-board-handle{ ":class" => '{ "user-can-drag": (!disabled && !list.preset), "p-0 border-bottom-0 justify-content-center": !list.isExpanded }' }
        %i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable",
          ":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }",
          "aria-hidden": "true" }
    %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color, "position-relative": list.isExpanded, "position-absolute position-top-0 position-left-0 w-100 h-100": !list.isExpanded }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }" }
      %h3.board-title.m-0.d-flex.js-board-handle{":class" => '{ "user-can-drag": (!disabled && !list.preset), "border-bottom-0": !list.isExpanded }' }

        -# We need two caret elements because they have different tooltip texts
        %div.board-title-collapse.no-drag.mr-2.has-tooltip{ "v-if": "list.isExpandable && list.isExpanded",
          "aria-hidden": "true",
          "aria-label": "Collapse",
          title: "Collapse",
          key: "collapse",
          data: { placement: "bottom" },
          "@click": "toggleExpanded" }
          = icon("caret-right", { class: "fa-fw" })
        %div.board-title-expand.no-drag.has-tooltip{ "v-if": "list.isExpandable && !list.isExpanded",
          "aria-hidden": "true",
          "aria-label": "Expand",
          title: "Expand",
          key: "expand",
          data: { placement: "bottom" },
          "@click": "toggleExpanded" }
          = icon("caret-down", { class: "fa-fw" })
        = render_if_exists "shared/boards/components/list_milestone"

        %a.user-avatar-link.js-no-trigger{ "v-if": "list.type === \"assignee\"", ":href": "list.assignee.path" }
          -# haml-lint:disable AltText
          %img.avatar.s20.has-tooltip{ height: "20", width: "20", ":src": "list.assignee.avatar", ":alt": "list.assignee.name" }

        %span.board-title-text.has-tooltip.block-truncated{ "v-if": "list.type !== \"label\"",
        %div.board-title-text
          %span.has-tooltip.block-truncated{ "v-if": "list.type !== \"label\"",
            ":title" => '((list.label && list.label.description) || list.title || "")', data: { container: "body" } }
            {{ list.title }}

@@ -20,10 +36,9 @@
            ":title" => '(list.assignee && list.assignee.username || "")' }
            @{{ list.assignee.username }}

        %span.has-tooltip{ "v-if": "list.type === \"label\"",
          %span.has-tooltip.badge.color-label.title{ "v-if": "list.type === \"label\"",
            ":title" => '(list.label ? list.label.description : "")',
            data: { container: "body", placement: "bottom" },
          class: "badge color-label title board-title-text",
            ":style" => "{ backgroundColor: (list.label && list.label.color ? list.label.color : null), color: (list.label && list.label.textColor ? list.label.textColor : \"#2e2e2e\") }" }
            {{ list.title }}

@@ -31,22 +46,23 @@
          %board-delete{ "inline-template" => true,
            ":list" => "list",
            "v-if" => "!list.preset && list.id" }
            %button.board-delete.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
            %button.board-delete.no-drag.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
              = icon("trash")
        .issue-count-badge.text-secondary{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', ":title": "counterTooltip", ":class": "{ 'd-none': !list.isExpanded }", "v-tooltip": true, data: { placement: "top" } }

        .issue-count-badge.no-drag.text-secondary{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } }
          %span.issue-count-badge-count
            %icon.mr-1{ name: "issues" }
            {{ list.issuesSize }}
          = render_if_exists "shared/boards/components/list_weight"

        %button.issue-count-badge-add-button.btn.btn-sm.btn-default.ml-1.has-tooltip.js-no-trigger-collapse{ type: "button",
        %button.issue-count-badge-add-button.no-drag.btn.btn-sm.btn-default.ml-1.has-tooltip{ type: "button",
          "@click" => "showNewIssueForm",
          "v-if" => "isNewIssueShown",
          ":class": "{ 'd-none': !list.isExpanded }",
          "aria-label" => _("New issue"),
          "title" => _("New issue"),
          data: { placement: "top", container: "body" } }
          = icon("plus", class: "js-no-trigger-collapse")
          = icon("plus")

    %board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"',
      ":list" => "list",