Compact user menu
authorJohn Firebaugh <john.firebaugh@gmail.com>
Tue, 16 Jul 2013 23:37:58 +0000 (16:37 -0700)
committerJohn Firebaugh <john.firebaugh@gmail.com>
Thu, 22 Aug 2013 00:20:04 +0000 (17:20 -0700)
Vendorfile
app/assets/javascripts/application.js
app/assets/stylesheets/common.css.scss
app/assets/stylesheets/small.css.scss
app/views/layouts/_inbox.html.erb
app/views/layouts/_user_menu.html.erb [new file with mode: 0644]
app/views/layouts/site.html.erb
app/views/site/_home_link.html.erb
config/locales/en.yml
vendor/assets/bootstrap/bootstrap.dropdown.js [new file with mode: 0644]

index 88f999a405607f91d7ff1f869d9695c86d464f52..c1242beb7fd92e0ccd9eb9d6aca11d26795f6acd 100644 (file)
@@ -5,6 +5,7 @@ folder 'vendor/assets' do
 
   folder 'bootstrap' do
     file 'bootstrap.tooltip.js', 'https://raw.github.com/twbs/bootstrap/v2.3.2/js/bootstrap-tooltip.js'
+    file 'bootstrap.dropdown.js', 'https://raw.github.com/twbs/bootstrap/v2.3.2/js/bootstrap-dropdown.js'
   end
 
   folder 'leaflet' do
index a33e281ce8e0762bfcc64faa693adebfc30b7911..564089f3a128eb423ad009d918e53602f5069187 100644 (file)
@@ -4,6 +4,7 @@
 //= require jquery.cookie
 //= require jquery.throttle-debounce
 //= require bootstrap.tooltip
+//= require bootstrap.dropdown
 //= require augment
 //= require osm
 //= require leaflet
index 0e03da6de7d3c9870f6412be2db9cb8b1e4870f3..0c2d8ed01330cde6d6db5278d2ac54a305c14bf9 100644 (file)
@@ -202,6 +202,7 @@ h6:first-child {
 .icon.close       { background-position: -200px 0; }
 .icon.check       { background-position: -220px 0; }
 .icon.note        { background-position: -240px 0; }
+.icon.gear        { background-position: -260px 0; }
 
 /* Rules for links */
 
@@ -472,7 +473,11 @@ a.donate {
   height: 30px;
   border-bottom: 1px solid #ccc;
   background: white;
-  z-index: 100;
+  z-index: 1001;
+
+  .caret {
+    margin-top: 10px;
+  }
 }
 
 .site-edit #top-bar,
@@ -544,12 +549,52 @@ a.donate {
 
 #greeting {
   float: right;
-  padding-top: 3px;
-  margin-right: $lineheight/4;
-}
+  height: 100%;
 
-.greeting-bar-unread {
-  font-weight: bold;
+  &.secondary-actions {
+    padding: 3px $lineheight/2;
+  }
+
+  &.dropdown {
+    background-color: #EEE;
+    &:hover {
+      background-color: #CCC;
+    }
+  }
+
+  img {
+    vertical-align: top;
+    border-radius: 2px 0 0 2px;
+    padding-right: 5px;
+  }
+
+  #inboxanchor {
+    display: inline-block;
+    position: relative;
+    height: 20px;
+    top: -2px;
+    margin: 0 2px 0 0;
+    padding: 0 5px 0 0;
+    border-radius: 2px;
+  }
+
+  .dropdown-toggle {
+    display: block;
+    padding: 3px 7px;
+    color: #000;
+    text-decoration: none;
+  }
+
+  .dropdown-menu {
+    left: auto;
+    right: 0;
+
+    .count-number {
+      float: right;
+      padding: 0 5px;
+      margin: 0;
+    }
+  }
 }
 
 /* Rules for the message shown in place of the map when javascript is disabled */
@@ -2292,6 +2337,134 @@ a.button {
   background: #fff;
 }
 
+/* Rules for dropdown menus */
+
+.dropdown {
+  position: relative;
+}
+
+.dropdown-toggle {
+  *margin-bottom: -3px;
+}
+
+.dropdown-toggle:active,
+.open .dropdown-toggle {
+  outline: 0;
+}
+
+.caret {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  vertical-align: top;
+  border-top: 4px solid #000000;
+  border-right: 4px solid transparent;
+  border-left: 4px solid transparent;
+  content: "";
+}
+
+.dropdown .caret {
+  margin-top: 8px;
+  margin-left: 2px;
+}
+
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none;
+  float: left;
+  min-width: 160px;
+  padding: 5px 0;
+  margin: 0;
+  list-style: none;
+  background-color: #ffffff;
+  border: 1px solid #ccc;
+  *border-right-width: 2px;
+  *border-bottom-width: 2px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+     -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+  -webkit-background-clip: padding-box;
+     -moz-background-clip: padding;
+          background-clip: padding-box;
+}
+
+.dropdown-menu.pull-right {
+  right: 0;
+  left: auto;
+}
+
+.dropdown-menu .divider {
+  *width: 100%;
+  height: 1px;
+  margin: 9px 1px;
+  *margin: -5px 0 5px;
+  overflow: hidden;
+  background-color: #e5e5e5;
+  border-bottom: 1px solid #ffffff;
+}
+
+.dropdown-menu > li > a {
+  display: block;
+  padding: 3px 10px;
+  clear: both;
+  font-weight: normal;
+  line-height: 20px;
+  color: #333333;
+  white-space: nowrap;
+}
+
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus,
+.dropdown-submenu:hover > a,
+.dropdown-submenu:focus > a {
+  color: #ffffff;
+  text-decoration: none;
+  background-color: #0081c2;
+}
+
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  color: #ffffff;
+  text-decoration: none;
+  background-color: #0081c2;
+  outline: 0;
+}
+
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  color: #999999;
+}
+
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  text-decoration: none;
+  cursor: default;
+  background-color: transparent;
+  background-image: none;
+}
+
+.open {
+  *z-index: 1000;
+}
+
+.open > .dropdown-menu {
+  display: block;
+}
+
+.dropdown-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 990;
+}
+
 /* Rules for the "Welcome" page */
 .site-welcome {
   .center {
index 3f90883117b69373cc75a631cb3e1c3eaeef9f82..419abeac4eab7803fee4da49a9f1bfa919ab9afa 100644 (file)
@@ -84,13 +84,6 @@ h2, h3, h4 {
 
 /* Rules for greeting bar in the top right corner */
 
-#greeting {
-  position: absolute;
-  right: 0;
-  top: 0;
-  background: none;
-}
-
 #browse_map ul.secondary-actions {
   float: right;
   font-size: 10px;
@@ -267,4 +260,4 @@ p.search_results_entry {
   .aside {
     display: none;
   }
-}
\ No newline at end of file
+}
index fcaa5ae648031e971543f029fb852a06aa671d54..6dd6218eecb9434bd359f1de63044ccef8e95072 100644 (file)
@@ -1,13 +1,3 @@
-<%=
-  link_to(
-    t("layouts.inbox_html",
-      :count => @user.new_messages.size > 0 ?
-                content_tag(
-                  :span, @user.new_messages.size, :class => "count-number"
-                ) :
-                ""
-    ),
-    inbox_path(:display_name => @user.display_name),
-    :id => "inboxanchor"
-  )
-%>
+<span id="inboxanchor" class="count-number">
+  <img src='<%= user_image_url(@user, :size => 20) %>'><span><%= @user.new_messages.size %></span>
+</span>
\ No newline at end of file
diff --git a/app/views/layouts/_user_menu.html.erb b/app/views/layouts/_user_menu.html.erb
new file mode 100644 (file)
index 0000000..19ac93d
--- /dev/null
@@ -0,0 +1,55 @@
+<div class='dropdown' id='greeting'>
+  <a class='dropdown-toggle' data-toggle='dropdown' href="#">
+    <%= render :partial => 'layouts/inbox' %>
+    <%= @user.display_name %>
+    <b class="caret"></b>
+  </a>
+  <ul class='dropdown-menu' role='menu' aria-labelledby='dLabel'>
+    <li>
+      <%= link_to inbox_path(:display_name => @user.display_name) do %>
+        <span class='count-number'><%= number_with_delimiter(@user.new_messages.size) %></span>
+        <%= t('message.inbox.my_inbox') %>
+      <% end %>
+    </li>
+    <li>
+      <%= link_to :controller => 'changeset', :action => 'list', :display_name => @user.display_name do %>
+        <span class='count-number'><%= number_with_delimiter(@user.changesets.size) %></span>
+        <%= t('user.view.my edits') %>
+      <% end %>
+    </li>
+    <li>
+      <%= link_to :controller => 'notes', :action => 'mine', :display_name => @user.display_name do %>
+        <span class='count-number'><%= number_with_delimiter(@user.notes.size) %></span>
+        <%= t('user.view.my notes') %>
+      <% end %>
+    </li>
+    <li>
+      <%= link_to :controller => 'trace', :action => 'mine' do %>
+        <span class='count-number'><%= number_with_delimiter(@user.traces.size) %></span>
+        <%= t('user.view.my traces') %>
+      <% end %>
+    </li>
+    <li>
+      <%= link_to :controller => 'diary_entry', :action => 'list', :display_name => @user.display_name do %>
+        <span class='count-number'><%= number_with_delimiter(@user.diary_entries.size) %></span>
+        <%= t('user.view.my diary') %>
+      <% end %>
+    </li>
+    <li>
+      <%= link_to t('user.view.my comments' ), :controller => 'diary_entry', :action => 'comments', :display_name => @user.display_name %>
+    </li>
+    <li>
+      <%= link_to t('user.view.my profile'), user_path(:display_name => @user.display_name) %>
+    </li>
+    <li>
+      <%= link_to t('user.view.my settings'), :controller => 'user', :action => 'account', :display_name => @user.display_name %>
+    </li>
+    <li class="divider"></li>
+    <li>
+      <%= yield :greeting %>
+    </li>
+    <li>
+      <%= link_to t('layouts.logout'), logout_path(:session => request.session_options[:id], :referer => request.fullpath) %>
+    </li>
+  </ul>
+</div>
index a0e894e462c315e834dd193ae01fea1a4b403f54..512b95905bac4b8e174fd001ec9adb720199a54c 100644 (file)
       </div>
     </div>
     <div id='top-bar'>
-      <ul class='secondary-actions' id="greeting">
-         <% if @user and @user.id %>
-           <li id="full-greeting"><%=link_to h(@user.display_name), user_path(:display_name => @user.display_name), :title => t('layouts.welcome_user_link_tooltip') %></li>
-           <li><%= yield :greeting %></li>
-           <li><%= render :partial => "layouts/inbox" %></li>
-           <li><%= link_to t('layouts.logout'), logout_path(:session => request.session_options[:id], :referer => request.fullpath), {:id => 'logoutanchor', :title => t('layouts.logout_tooltip')}%></li>
-         <% else %>
-           <li><%= link_to t('layouts.log_in'), login_path(:referer => request.fullpath), {:id => 'loginanchor', :title => t('layouts.log_in_tooltip')} %></li>
-           <li><%= link_to t('layouts.sign_up'), user_new_path, {:id => 'registeranchor', :title => t('layouts.sign_up_tooltip')} %></li>
-         <% end %>
-      </ul>
+      <% if @user and @user.id %>
+        <%= render :partial => "layouts/user_menu" %>
+      <% else %>
+        <ul class='secondary-actions' id='greeting'>
+          <li><%= link_to t('layouts.log_in'), login_path(:referer => request.fullpath), {:id => 'loginanchor', :title => t('layouts.log_in_tooltip')} %></li>
+          <li><%= link_to t('layouts.sign_up'), user_new_path, {:id => 'registeranchor', :title => t('layouts.sign_up_tooltip')} %></li>
+        </ul>
+      <% end %>
       <ul id="tabnav">
         <li><%= link_to t('layouts.view'), root_path, {
           :id => 'viewanchor',
index 979ae7681fecfdfc8a3c3ba679a01a28088e005d..e907b3fb1e62008a7c9b78bbd549f4f8454b7505 100644 (file)
@@ -6,7 +6,6 @@
                 :class => "set_position",
                 :data => { :lat => @user.home_lat,
                            :lon => @user.home_lon,
-                           :zoom => 15 },
-                :title => t("layouts.home_tooltip") %>
+                           :zoom => 15 } %>
   <% end %>
 <% end %>
index 901e863595b76f57b72d4016eee9ea963d9811c1..d5c59a8fe3cee171413111570c468cbc52a41300 100644 (file)
@@ -996,16 +996,8 @@ en:
       h1: OpenStreetMap
     logo:
       alt_text: OpenStreetMap logo
-    welcome_user_link_tooltip: Your user page
-    home: home
-    home_tooltip: Go to home location
-    inbox_html: "inbox %{count}"
-    inbox_tooltip:
-      zero: Your inbox contains no unread messages
-      one: Your inbox contains 1 unread message
-      other: Your inbox contains %{count} unread messages
-    logout: logout
-    logout_tooltip: "Log out"
+    home: Go to Home Location
+    logout: Logout
     log_in: log in
     log_in_tooltip: Log in with an existing account
     sign_up: sign up
@@ -1294,7 +1286,7 @@ en:
   message:
     inbox:
       title: "Inbox"
-      my_inbox: "My inbox"
+      my_inbox: "My Inbox"
       outbox: "outbox"
       messages: "You have %{new_messages} and %{old_messages}"
       new_messages:
@@ -1777,13 +1769,14 @@ en:
       heading: "The user %{user} does not exist"
       body: "Sorry, there is no user with the name %{user}. Please check your spelling, or maybe the link you clicked is wrong."
     view:
-      my diary: my diary
+      my diary: My Diary
       new diary entry: new diary entry
-      my edits: my edits
-      my traces: my traces
-      my notes: my map notes
-      my settings: my settings
-      my comments: my comments
+      my edits: My Edits
+      my traces: My Traces
+      my notes: My Notes
+      my profile: My Profile
+      my settings: My Settings
+      my comments: My Comments
       oauth settings: oauth settings
       blocks on me: blocks on me
       blocks by me: blocks by me
diff --git a/vendor/assets/bootstrap/bootstrap.dropdown.js b/vendor/assets/bootstrap/bootstrap.dropdown.js
new file mode 100644 (file)
index 0000000..d04da5d
--- /dev/null
@@ -0,0 +1,169 @@
+/* ============================================================
+ * bootstrap-dropdown.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#dropdowns
+ * ============================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function ($) {
+
+  "use strict"; // jshint ;_;
+
+
+ /* DROPDOWN CLASS DEFINITION
+  * ========================= */
+
+  var toggle = '[data-toggle=dropdown]'
+    , Dropdown = function (element) {
+        var $el = $(element).on('click.dropdown.data-api', this.toggle)
+        $('html').on('click.dropdown.data-api', function () {
+          $el.parent().removeClass('open')
+        })
+      }
+
+  Dropdown.prototype = {
+
+    constructor: Dropdown
+
+  , toggle: function (e) {
+      var $this = $(this)
+        , $parent
+        , isActive
+
+      if ($this.is('.disabled, :disabled')) return
+
+      $parent = getParent($this)
+
+      isActive = $parent.hasClass('open')
+
+      clearMenus()
+
+      if (!isActive) {
+        if ('ontouchstart' in document.documentElement) {
+          // if mobile we we use a backdrop because click events don't delegate
+          $('<div class="dropdown-backdrop"/>').insertBefore($(this)).on('click', clearMenus)
+        }
+        $parent.toggleClass('open')
+      }
+
+      $this.focus()
+
+      return false
+    }
+
+  , keydown: function (e) {
+      var $this
+        , $items
+        , $active
+        , $parent
+        , isActive
+        , index
+
+      if (!/(38|40|27)/.test(e.keyCode)) return
+
+      $this = $(this)
+
+      e.preventDefault()
+      e.stopPropagation()
+
+      if ($this.is('.disabled, :disabled')) return
+
+      $parent = getParent($this)
+
+      isActive = $parent.hasClass('open')
+
+      if (!isActive || (isActive && e.keyCode == 27)) {
+        if (e.which == 27) $parent.find(toggle).focus()
+        return $this.click()
+      }
+
+      $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+      if (!$items.length) return
+
+      index = $items.index($items.filter(':focus'))
+
+      if (e.keyCode == 38 && index > 0) index--                                        // up
+      if (e.keyCode == 40 && index < $items.length - 1) index++                        // down
+      if (!~index) index = 0
+
+      $items
+        .eq(index)
+        .focus()
+    }
+
+  }
+
+  function clearMenus() {
+    $('.dropdown-backdrop').remove()
+    $(toggle).each(function () {
+      getParent($(this)).removeClass('open')
+    })
+  }
+
+  function getParent($this) {
+    var selector = $this.attr('data-target')
+      , $parent
+
+    if (!selector) {
+      selector = $this.attr('href')
+      selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+    }
+
+    $parent = selector && $(selector)
+
+    if (!$parent || !$parent.length) $parent = $this.parent()
+
+    return $parent
+  }
+
+
+  /* DROPDOWN PLUGIN DEFINITION
+   * ========================== */
+
+  var old = $.fn.dropdown
+
+  $.fn.dropdown = function (option) {
+    return this.each(function () {
+      var $this = $(this)
+        , data = $this.data('dropdown')
+      if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  $.fn.dropdown.Constructor = Dropdown
+
+
+ /* DROPDOWN NO CONFLICT
+  * ==================== */
+
+  $.fn.dropdown.noConflict = function () {
+    $.fn.dropdown = old
+    return this
+  }
+
+
+  /* APPLY TO STANDARD DROPDOWN ELEMENTS
+   * =================================== */
+
+  $(document)
+    .on('click.dropdown.data-api', clearMenus)
+    .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+    .on('click.dropdown.data-api'  , toggle, Dropdown.prototype.toggle)
+    .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);