From e66f11134854649d1a7afe0653cefc24f8537a3c Mon Sep 17 00:00:00 2001
From: Robin Ward <robin.ward@gmail.com>
Date: Wed, 26 Aug 2015 14:42:42 -0400
Subject: [PATCH] UX: Use dropdown for hamburger on wide screens, full height
 on smaller

---
 .../components/hamburger-menu.js.es6          | 59 ++++++++++++++++---
 .../templates/components/hamburger-menu.hbs   | 11 ++--
 .../discourse/templates/header.hbs            |  2 +-
 .../javascripts/discourse/views/header.js.es6 |  6 +-
 .../stylesheets/common/base/hamburger.scss    | 41 +++++++------
 .../acceptance/hamburger-menu-test.js.es6     | 16 +----
 6 files changed, 90 insertions(+), 45 deletions(-)

diff --git a/app/assets/javascripts/discourse/components/hamburger-menu.js.es6 b/app/assets/javascripts/discourse/components/hamburger-menu.js.es6
index 3c9a13fef..e7066dbc8 100644
--- a/app/assets/javascripts/discourse/components/hamburger-menu.js.es6
+++ b/app/assets/javascripts/discourse/components/hamburger-menu.js.es6
@@ -2,19 +2,56 @@ import { default as computed, on, observes } from 'ember-addons/ember-computed-d
 
 
 export default Ember.Component.extend({
-  classNameBindings: ['visible::slideright'],
+  classNameBindings: ['visible::hidden', 'viewMode'],
+  attributeBindings: ['style'],
   elementId: 'hamburger-menu',
+  viewMode: 'dropDown',
+
+  showClose: Ember.computed.equal('viewMode', 'slide-in'),
+
+  @computed('viewMode')
+  style(viewMode) {
+    if (viewMode === 'drop-down') {
+      const $buttonPanel = $('header ul.icons');
+
+      const buttonPanelPos = $buttonPanel.offset();
+      const myWidth = this.$().width();
+
+      const posTop = parseInt(buttonPanelPos.top + $buttonPanel.height());
+      const posLeft = parseInt(buttonPanelPos.left + $buttonPanel.width() - myWidth);
+
+      return `left: ${posLeft}px; top: ${posTop}px`.htmlSafe();
+    }
+  },
+
+  @computed('viewMode')
+  bodyStyle(viewMode) {
+    if (viewMode === 'drop-down') {
+      const height = parseInt($(window).height() * 0.8)
+      return `height: ${height}px`.htmlSafe();
+    }
+  },
 
   @observes('visible')
-  _catchClickOutside() {
+  _visibleChanged() {
     if (this.get('visible')) {
+      $('.hamburger-dropdown').addClass('active');
+
+      if ($(window).width() < 1024) {
+        this.set('viewMode', 'slide-in');
+      } else {
+        this.set('viewMode', 'drop-down');
+      }
+
       $('html').on('click.close-hamburger', (e) => {
         const $target = $(e.target);
-        if ($target.closest('.dropdown.hamburger').length > 0) { return; }
+        if ($target.closest('.hamburger-dropdown').length > 0) { return; }
         if ($target.closest('#hamburger-menu').length > 0) { return; }
-        this.set('visible', false);
+        this.hide();
       });
+
     } else {
+      $('.hamburger-dropdown').removeClass('active');
       $('html').off('click.close-hamburger');
     }
   },
@@ -42,19 +79,21 @@ export default Ember.Component.extend({
   @on('didInsertElement')
   _bindEvents() {
     this.$().on('click.discourse-hamburger', 'a', () => {
-      this.set('visible', false);
+      this.hide();
     });
 
+    this.appEvents.on('dropdowns:closeAll', this, this.hide);
+
     $('body').on('keydown.discourse-hambuger', (e) => {
       if (e.which === 27) {
-        this.set('visible', false);
+        this.hide();
       }
     });
-
   },
 
   @on('willDestroyElement')
   _removeEvents() {
+    this.appEvents.off('dropdowns:closeAll', this, this.hide);
     this.$().off('click.discourse-hamburger');
     $('body').off('keydown.discourse-hambuger');
     $('html').off('click.close-hamburger');
@@ -73,9 +112,13 @@ export default Ember.Component.extend({
     });
   },
 
+  hide() {
+    this.set('visible', false);
+  },
+
   actions: {
     close() {
-      this.set('visible', false);
+      this.hide();
     },
     keyboardShortcuts() {
       this.sendAction('showKeyboardAction');
diff --git a/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs b/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs
index 7cef85af9..474e3a3cd 100644
--- a/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs
+++ b/app/assets/javascripts/discourse/templates/components/hamburger-menu.hbs
@@ -1,8 +1,11 @@
 {{#if visible}}
-  <div class='hamburger-header clearfix'>
-    <a href {{action "close"}} class='close-hamburger'>{{fa-icon 'times'}}</a>
-  </div>
-  <div class='hamburger-body'>
+  {{#if showClose}}
+    <div class='hamburger-header clearfix'>
+      <a href {{action "close"}} class='close-hamburger'>{{fa-icon 'times'}}</a>
+    </div>
+  {{/if}}
+
+  <div class='hamburger-body' style={{bodyStyle}}>
     <ul class="location-links">
       {{#if currentUser.staff}}
         <li>
diff --git a/app/assets/javascripts/discourse/templates/header.hbs b/app/assets/javascripts/discourse/templates/header.hbs
index 6c7a40313..6d833d3f3 100644
--- a/app/assets/javascripts/discourse/templates/header.hbs
+++ b/app/assets/javascripts/discourse/templates/header.hbs
@@ -42,7 +42,7 @@
             </a>
           {{/if}}
         </li>
-        <li class='hamburger categories dropdown'>
+        <li class='hamburger-dropdown'>
           {{#if loginRequired}}
             <a class='icon'
                href
diff --git a/app/assets/javascripts/discourse/views/header.js.es6 b/app/assets/javascripts/discourse/views/header.js.es6
index 310a06604..2251fe080 100644
--- a/app/assets/javascripts/discourse/views/header.js.es6
+++ b/app/assets/javascripts/discourse/views/header.js.es6
@@ -7,9 +7,11 @@ export default Ember.View.extend({
   templateName: 'header',
   renderDropdowns: false,
 
-  showDropdown: function($target) {
+  showDropdown($target) {
     var self = this;
 
+    this.appEvents.trigger('dropdowns:closeAll');
+
     if (!this.get("renderDropdowns")) {
       this.set("renderDropdowns", true);
       Em.run.next(function(){
@@ -138,7 +140,7 @@ export default Ember.View.extend({
     const self = this;
 
     this.$('a[data-dropdown]').on('click.dropdown', function(e) {
-      self.showDropdown.apply(self, [$(e.currentTarget)]);
+      self.showDropdown.call(self, $(e.currentTarget));
       return false;
     });
     this.$().on('click.notifications','a.unread-private-messages, a.unread-notifications, a[data-notifications]', function(e) {
diff --git a/app/assets/stylesheets/common/base/hamburger.scss b/app/assets/stylesheets/common/base/hamburger.scss
index 8fb4a2f13..c42b094c7 100644
--- a/app/assets/stylesheets/common/base/hamburger.scss
+++ b/app/assets/stylesheets/common/base/hamburger.scss
@@ -1,15 +1,27 @@
-#hamburger-menu {
+#hamburger-menu.slide-in {
   position: fixed;
   right: 0;
   top: 0;
-  background-color: $secondary;
-  z-index: 1002;
   height: 100%;
-  overflow: none;
-  transition: 0.3s ease-in-out;
-  @include transform(translateX(0));
-
   box-shadow: 0 4px 4px 4px rgba(0,0,0, .25);
+
+  .hamburger-body {
+    position: absolute;
+    top: 40px;
+    bottom: 37px;
+  }
+}
+
+#hamburger-menu.drop-down {
+  border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
+  box-shadow: 0 2px 2px rgba(0,0,0, .25);
+  position: absolute;
+}
+
+#hamburger-menu {
+  background-color: $secondary;
+  z-index: 1100;
+  overflow: none;
   padding: 0.5em 0.5em 0.5em 0.5em;
   width: 300px;
 
@@ -28,13 +40,6 @@
     right: 20px;
   }
 
-  .hamburger-body {
-    overflow-y: auto;
-    overflow-x: hidden;
-    position: absolute;
-    top: 40px;
-    bottom: 37px;
-  }
 
 
   ul {
@@ -73,8 +78,10 @@
     font-weight: normal;
     font-size: 11px;
   }
+
+  .hamburger-body {
+    overflow-y: auto;
+    overflow-x: hidden;
+  }
 }
 
-#hamburger-menu.slideright {
-  @include transform(translateX(330px));
-}
diff --git a/test/javascripts/acceptance/hamburger-menu-test.js.es6 b/test/javascripts/acceptance/hamburger-menu-test.js.es6
index 312cab6e3..0b135767f 100644
--- a/test/javascripts/acceptance/hamburger-menu-test.js.es6
+++ b/test/javascripts/acceptance/hamburger-menu-test.js.es6
@@ -5,27 +5,17 @@ acceptance("Hamburger Menu");
 test("Toggle Menu", (assert) => {
   visit("/");
   andThen(() => {
-    assert.ok(exists("#hamburger-menu.slideright"), "hidden by default");
+    assert.ok(exists("#hamburger-menu.hidden"), "hidden by default");
   });
 
   click("#toggle-hamburger-menu");
   andThen(() => {
-    assert.ok(!exists("#hamburger-menu.slideright"), "a click makes it appear");
-  });
-
-  click(".close-hamburger");
-  andThen(() => {
-    assert.ok(exists("#hamburger-menu.slideright"), "clicking the X hides it");
-  });
-
-  click("#toggle-hamburger-menu");
-  andThen(() => {
-    assert.ok(!exists("#hamburger-menu.slideright"), "it opens again");
+    assert.ok(!exists("#hamburger-menu.hidden"), "a click makes it appear");
   });
 
   click('#main-outlet')
   andThen(() => {
-    assert.ok(exists("#hamburger-menu.slideright"), "clicking the body hides the menu");
+    assert.ok(exists("#hamburger-menu.hidden"), "clicking the body hides the menu");
   });
 
 });