From 5bbab5f8e6fbcf7b16b62975a34b3798541e177d Mon Sep 17 00:00:00 2001 From: Marius Balteanu Date: Wed, 6 Nov 2024 23:08:15 +0000 Subject: [PATCH] Makes the sidebar collapsible and stateful (#21808). 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 | 8 +++ app/assets/javascripts/application.js | 71 ++++++++++++++++++++++++++ app/assets/stylesheets/application.css | 27 +++++++++- app/views/layouts/base.html.erb | 16 ++++-- config/icon_source.yml | 5 +- 5 files changed, 121 insertions(+), 6 deletions(-) diff --git a/app/assets/images/icons.svg b/app/assets/images/icons.svg index 92c345560..c94249ba8 100644 --- a/app/assets/images/icons.svg +++ b/app/assets/images/icons.svg @@ -96,6 +96,14 @@ + + + + + + + + diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 5a01f16c7..068ac98d8 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -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); diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 07d0f0c93..80e4507c9 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -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;} diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb index 89ea2ef80..865ca9928 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -100,10 +100,20 @@ <% end %> -