From b2f8ebbb4295e5f070187050e094db87ff9d6ad0 Mon Sep 17 00:00:00 2001
From: Mike Greiling <mike@pixelcog.com>
Date: Fri, 2 Sep 2016 01:33:43 -0500
Subject: [PATCH] refactor sidebar logic into singleton class

---
 CHANGELOG                                     |  1 +
 app/assets/javascripts/application.js         | 43 +-------
 app/assets/javascripts/sidebar.js             | 41 --------
 app/assets/javascripts/sidebar.js.es6         | 98 +++++++++++++++++++
 app/assets/stylesheets/framework/sidebar.scss |  4 +-
 5 files changed, 104 insertions(+), 83 deletions(-)
 delete mode 100644 app/assets/javascripts/sidebar.js
 create mode 100644 app/assets/javascripts/sidebar.js.es6

diff --git a/CHANGELOG b/CHANGELOG
index 16b813492a8..8f3c6fb8e76 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,6 +13,7 @@ v 8.12.0 (unreleased)
   - Instructions for enabling Git packfile bitmaps !6104
   - Fix pagination on user snippets page
   - Escape search term before passing it to Regexp.new !6241 (winniehell)
+  - Fix pinned sidebar behavior in smaller viewports !6169
   - Change merge_error column from string to text type
   - Reduce contributions calendar data payload (ClemMakesApps)
   - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 43a679501a7..fea6f41d5e9 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -174,9 +174,7 @@
     $body.tooltip({
       selector: '.has-tooltip, [data-toggle="tooltip"]',
       placement: function(_, el) {
-        var $el;
-        $el = $(el);
-        return $el.data('placement') || 'bottom';
+        return $(el).data('placement') || 'bottom';
       }
     });
     $('.trigger-submit').on('change', function() {
@@ -286,42 +284,9 @@
     gl.awardsHandler = new AwardsHandler();
     checkInitialSidebarSize();
     new Aside();
-    if ($window.width() < 1024 && $.cookie('pin_nav') === 'true') {
-      $.cookie('pin_nav', 'false', {
-        path: gon.relative_url_root || '/',
-        expires: 365 * 10
-      });
-      $('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded').removeClass('page-sidebar-pinned');
-      $('.navbar-fixed-top').removeClass('header-pinned-nav');
-    }
-    $document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
-      var $page, $pinBtn, $tooltip, $topNav, doPinNav, tooltipText;
-      e.preventDefault();
-      $pinBtn = $(e.currentTarget);
-      $page = $('.page-with-sidebar');
-      $topNav = $('.navbar-fixed-top');
-      $tooltip = $("#" + ($pinBtn.attr('aria-describedby')));
-      doPinNav = !$page.is('.page-sidebar-pinned');
-      tooltipText = 'Pin navigation';
-      $(this).toggleClass('is-active');
-      if (doPinNav) {
-        $page.addClass('page-sidebar-pinned');
-        $topNav.addClass('header-pinned-nav');
-      } else {
-        $tooltip.remove();
-        $page.removeClass('page-sidebar-pinned').toggleClass('page-sidebar-collapsed page-sidebar-expanded');
-        $topNav.removeClass('header-pinned-nav').toggleClass('header-collapsed header-expanded');
-      }
-      $.cookie('pin_nav', doPinNav, {
-        path: gon.relative_url_root || '/',
-        expires: 365 * 10
-      });
-      if ($.cookie('pin_nav') === 'true' || doPinNav) {
-        tooltipText = 'Unpin navigation';
-      }
-      $tooltip.find('.tooltip-inner').text(tooltipText);
-      return $pinBtn.attr('title', tooltipText).tooltip('fixTitle');
-    });
+
+    // bind sidebar events
+    new gl.Sidebar();
 
     // Custom time ago
     gl.utils.shortTimeAgo($('.js-short-timeago'));
diff --git a/app/assets/javascripts/sidebar.js b/app/assets/javascripts/sidebar.js
deleted file mode 100644
index bd0c1194b36..00000000000
--- a/app/assets/javascripts/sidebar.js
+++ /dev/null
@@ -1,41 +0,0 @@
-(function() {
-  var collapsed, expanded, toggleSidebar;
-
-  collapsed = 'page-sidebar-collapsed';
-
-  expanded = 'page-sidebar-expanded';
-
-  toggleSidebar = function() {
-    $('.page-with-sidebar').toggleClass(collapsed + " " + expanded);
-    $('.navbar-fixed-top').toggleClass("header-collapsed header-expanded");
-    if ($.cookie('pin_nav') === 'true') {
-      $('.navbar-fixed-top').toggleClass('header-pinned-nav');
-      $('.page-with-sidebar').toggleClass('page-sidebar-pinned');
-    }
-    return setTimeout((function() {
-      var niceScrollBars;
-      niceScrollBars = $('.nav-sidebar').niceScroll();
-      return niceScrollBars.updateScrollBar();
-    }), 300);
-  };
-
-  $(document).off('click', 'body').on('click', 'body', function(e) {
-    var $nav, $target, $toggle, pageExpanded;
-    if ($.cookie('pin_nav') !== 'true') {
-      $target = $(e.target);
-      $nav = $target.closest('.sidebar-wrapper');
-      pageExpanded = $('.page-with-sidebar').hasClass('page-sidebar-expanded');
-      $toggle = $target.closest('.toggle-nav-collapse, .side-nav-toggle');
-      if ($nav.length === 0 && pageExpanded && $toggle.length === 0) {
-        $('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded');
-        return $('.navbar-fixed-top').toggleClass('header-collapsed header-expanded');
-      }
-    }
-  });
-
-  $(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', function(e) {
-    e.preventDefault();
-    return toggleSidebar();
-  });
-
-}).call(this);
diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6
new file mode 100644
index 00000000000..737f343ed0d
--- /dev/null
+++ b/app/assets/javascripts/sidebar.js.es6
@@ -0,0 +1,98 @@
+((global) => {
+  let singleton;
+
+  const pinnedStateCookie = 'pin_nav';
+  const sidebarBreakpoint = 1024;
+
+  const pageSelector = '.page-with-sidebar';
+  const navbarSelector = '.navbar-fixed-top';
+  const sidebarWrapperSelector = '.sidebar-wrapper';
+  const sidebarContentSelector = '.nav-sidebar';
+
+  const pinnedToggleSelector = '.js-nav-pin';
+  const sidebarToggleSelector = '.toggle-nav-collapse, .side-nav-toggle';
+
+  const pinnedPageClass = 'page-sidebar-pinned';
+  const expandedPageClass = 'page-sidebar-expanded';
+  const collapsedPageClass = 'page-sidebar-collapsed';
+
+  const pinnedNavbarClass = 'header-pinned-nav';
+  const expandedNavbarClass = 'header-expanded';
+  const collapsedNavbarClass = 'header-collapsed';
+
+  class Sidebar {
+    constructor() {
+      if (!singleton) {
+        singleton = this;
+        singleton.init();
+      } else {
+        singleton.renderState();
+      }
+      return singleton;
+    }
+
+    init() {
+      this.isPinned = $.cookie(pinnedStateCookie) === 'true';
+      this.isExpanded = (
+        window.innerWidth >= sidebarBreakpoint &&
+        $(pageSelector).hasClass(expandedPageClass)
+      );
+      $(document)
+        .on('click', sidebarToggleSelector, () => this.toggleSidebar())
+        .on('click', pinnedToggleSelector, () => this.togglePinnedState())
+        .on('click', 'html, body', (e) => this.handleClickEvent(e));
+      this.renderState();
+    }
+
+    handleClickEvent(e) {
+      if (this.isExpanded && (!this.isPinned || window.innerWidth < sidebarBreakpoint)) {
+        const $target = $(e.target);
+        const targetIsToggle = $target.closest(sidebarToggleSelector).length > 0;
+        const targetIsSidebar = $target.closest(sidebarWrapperSelector).length > 0;
+        if (!targetIsToggle && (!targetIsSidebar || $target.closest('a'))) {
+          this.toggleSidebar();
+        }
+      }
+    }
+
+    toggleSidebar() {
+      this.isExpanded = !this.isExpanded;
+      this.renderState();
+    }
+
+    togglePinnedState() {
+      this.isPinned = !this.isPinned;
+      if (!this.isPinned) {
+        this.isExpanded = false;
+      }
+      $.cookie(pinnedStateCookie, this.isPinned ? 'true' : 'false', {
+        path: gon.relative_url_root || '/',
+        expires: 3650
+      });
+      this.renderState();
+    }
+
+    renderState() {
+      $(pageSelector)
+        .toggleClass(pinnedPageClass, this.isPinned && this.isExpanded)
+        .toggleClass(expandedPageClass, this.isExpanded)
+        .toggleClass(collapsedPageClass, !this.isExpanded);
+      $(navbarSelector)
+        .toggleClass(pinnedNavbarClass, this.isPinned && this.isExpanded)
+        .toggleClass(expandedNavbarClass, this.isExpanded)
+        .toggleClass(collapsedNavbarClass, !this.isExpanded);
+
+      const $pinnedToggle = $(pinnedToggleSelector);
+      const tooltipText = this.isPinned ? 'Unpin navigation' : 'Pin navigation';
+      const tooltipState = $pinnedToggle.attr('aria-describedby') && this.isExpanded ? 'show' : 'hide';
+      $pinnedToggle.attr('title', tooltipText).tooltip('fixTitle').tooltip(tooltipState);
+
+      if (this.isExpanded) {
+        setTimeout(() => $(sidebarContentSelector).niceScroll().updateScrollBar(), 200);
+      }
+    }
+  }
+
+  global.Sidebar = Sidebar;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 015fe3debf9..558a5fb5d25 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -128,10 +128,8 @@
 
     .fa {
       transition: transform .15s;
-    }
 
-    &.is-active {
-      .fa {
+      .page-sidebar-pinned & {
         transform: rotate(90deg);
       }
     }
-- 
GitLab