]> source.dussan.org Git - redmine.git/commitdiff
Makes the sidebar collapsible and stateful (#21808).
authorMarius Balteanu <marius.balteanu@zitec.com>
Wed, 6 Nov 2024 23:08:15 +0000 (23:08 +0000)
committerMarius Balteanu <marius.balteanu@zitec.com>
Wed, 6 Nov 2024 23:08:15 +0000 (23:08 +0000)
Patch inspired from user:jkraemer.

git-svn-id: https://svn.redmine.org/redmine/trunk@23218 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/assets/images/icons.svg
app/assets/javascripts/application.js
app/assets/stylesheets/application.css
app/views/layouts/base.html.erb
config/icon_source.yml

index 92c345560ef4af3005cff10925d2b9ab72c743f8..c94249ba8c8def7fe68094fa538ad636cdd78fda 100644 (file)
     <symbol viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" id="icon--checked">
       <path d="M5 12l5 5l10 -10"/>
     </symbol>
+    <symbol viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" id="icon--chevrons-left">
+      <path d="M11 7l-5 5l5 5"/>
+      <path d="M17 7l-5 5l5 5"/>
+    </symbol>
+    <symbol viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" id="icon--chevrons-right">
+      <path d="M7 7l5 5l-5 5"/>
+      <path d="M13 7l5 5l-5 5"/>
+    </symbol>
     <symbol viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" id="icon--clear-query">
       <path d="M3 5a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14z"/>
       <path d="M9 9l6 6m0 -6l-6 6"/>
index 5a01f16c7aeafe21a7fa23abd7d1c7a3ebdde312..068ac98d8ce6244afe9e89537ff4ec3081d9fc48 100644 (file)
@@ -1261,6 +1261,77 @@ function inlineAutoComplete(element) {
     tribute.attach(element);
 }
 
+// collapsible sidebar jQuery plugin
+(function($) {
+  // main container this is applied to
+  var main;
+  // triggers show/hide
+  var button;
+  // the key to use in local storage
+  // this will later be expanded using the current controller and action to
+  // allow for different sidebar states for different pages
+  var localStorageKey = 'redmine-sidebar-state';
+  // true if local storage is available
+  var canUseLocalStorage = function(){
+    try {
+      if('localStorage' in window){
+        localStorage.setItem('redmine.test.storage', 'ok');
+        var item = localStorage.getItem('redmine.test.storage');
+        localStorage.removeItem('redmine.test.storage');
+        if(item === 'ok') return true;
+      }
+    } catch (err) {}
+    return false;
+  }();
+  // function to set current sidebar state
+  var setState = function(state){
+    if(canUseLocalStorage){
+      localStorage.setItem(localStorageKey, state);
+    }
+  };
+  var applyState = function(){
+    if(main.hasClass('collapsedsidebar')){
+      updateSVGIcon(main[0], 'chevrons-left')
+      setState('hidden');
+    } else {
+      updateSVGIcon(main[0], 'chevrons-right')
+      setState('visible');
+    }
+  };
+  var setupToggleButton = function(){
+    button = $('#sidebar-switch-button');
+    button.click(function(e){
+      main.addClass("animate");
+      main.toggleClass('collapsedsidebar');
+      applyState();
+      e.preventDefault();
+      return false;
+    });
+    applyState();
+  };
+  $.fn.collapsibleSidebar = function() {
+    main = this;
+    // determine previously stored sidebar state for this page
+    if(canUseLocalStorage) {
+      // determine current controller/action pair and use them as storage key
+      var bodyClass = $('body').attr('class');
+      if(bodyClass){
+        try {
+          localStorageKey += '-' + bodyClass.split(/\s+/).filter(function(s){
+            return s.match(/(action|controller)-.*/);
+          }).sort().join('-');
+        } catch(e) {
+          // in case of error (probably IE8), continue with the unmodified key
+        }
+      }
+      var storedState = localStorage.getItem(localStorageKey);
+      main.toggleClass('collapsedsidebar', storedState === 'hidden');
+    }
+    // draw the toggle button once the DOM is complete
+    $(document).ready(setupToggleButton);
+  };
+}(jQuery));
+
 $(document).ready(setupAjaxIndicator);
 $(document).ready(hideOnLoad);
 $(document).ready(addFormObserversForDoubleSubmit);
index 07d0f0c935fea54efa6e9cb1849862d911de968e..80e4507c985ba555325654f4c96128cfd4da62aa 100644 (file)
@@ -142,7 +142,7 @@ pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
 @media screen and (min-width: 1600px) and (max-width: 1919px) {#sidebar{width: 320px;}}
 @media screen and (min-width: 1920px) and (max-width: 2559px) {#sidebar{width: 360px;}}
 @media screen and (min-width: 2560px) {#sidebar{width: 380px;}}
-#sidebar h3{ font-size: 0.875rem; margin-top:14px; color: #555; }
+#sidebar h3{ font-size: 0.875rem; color: #555; }
 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
 #sidebar .contextual { margin-right: 1em; }
 #sidebar ul, ul.flat {margin: 0;  padding: 0;}
@@ -154,9 +154,32 @@ pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;}
 #sidebar span.icon-warning {margin-left: 5px;}
 #sidebar li input[type=checkbox] {height: 20px;}
 
+#sidebar-switch-panel {
+  margin-left: -20px;
+  padding-right: 28px;
+  width: 100%;
+}
+
+#sidebar-switch-button {
+  display: block;
+  padding: 3px 0;
+  padding-right: 28px;
+  width: 100%;
+
+  svg {
+    stroke: #555;
+  }
+
+  &:hover {
+    background-color: #eeeeee;
+  }
+}
+
 #content { flex-grow: 1; background-color: #fff; margin: 0px; padding: 10px 16px 10px 16px; overflow-x: auto;}
 
-#main.nosidebar #sidebar{ display: none; }
+#main.nosidebar #sidebar { width: 0; padding-right: 0 }
+#main.collapsedsidebar #sidebar { width: 0; padding-right: 0 }
+#main.collapsedsidebar #sidebar-wrapper { display: none; }
 
 #footer {clear: both; border-top: 1px solid #d0d7de; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
 
index 89ea2ef809c15627143498809c50664a47f16a39..865ca9928ab4002667aee8becce4cbd98e12dfab 100644 (file)
     <% end %>
 </div>
 
-<div id="main" class="<%= sidebar_content? ? '' : 'nosidebar' %>">
+<div id="main" class="<%= sidebar_content? ? 'collapsiblesidebar' : 'nosidebar' %>">
+  <%= javascript_tag "$('#main.collapsiblesidebar').collapsibleSidebar();" if sidebar_content? %>
     <div id="sidebar">
-        <%= yield :sidebar %>
-        <%= view_layouts_base_sidebar_hook_response %>
+        <% if sidebar_content? %>
+          <div id="sidebar-switch-panel" style="visibility: hidden;">
+            <a id="sidebar-switch-button" class="" href="#">
+              <%= sprite_icon("chevrons-right", size: 20) %></a>
+          </div>
+          <%= javascript_tag "$('#sidebar-switch-panel').css('visibility', 'visible');" %>
+        <% end %>
+        <div id=sidebar-wrapper %>
+          <%= yield :sidebar %>
+          <%= view_layouts_base_sidebar_hook_response %>
+        </div>
     </div>
 
     <div id="content">
index 32d28b6b00b532c119434ca7f79928c5e87be485..05d024718f4be7e48c906030d9def53634e1dbc1 100644 (file)
   svg: file-type-zip
 - name: application-gzip
   svg: file-zip
-
+- name: chevrons-right
+  svg: chevrons-right
+- name: chevrons-left
+  svg: chevrons-left