diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 5a01f16c7..806bd5a70 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')){ + button.html("«"); + setState('hidden'); + } else { + button.html("»"); + 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 630c78476..22ba2b0a5 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -135,7 +135,9 @@ pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;} #main {flex-grow: 2; display: flex; flex-direction: row-reverse;} -#sidebar{ font-size: 0.8125rem; flex-shrink: 0; padding-left: 20px; padding-right: 8px; background: #f9fafb; border-left: 1px solid #d0d7de} +#main.nosidebar #sidebar{ display: none; } +#sidebar{ font-size: 0.8125rem; flex-shrink: 0; padding-left: 20px; padding-right: 8px; background: #f9fafb; border-left: 1px solid #d0d7de; z-index: 1;} + @media screen and (min-width: 0px) and (max-width: 1089px) {#sidebar{width: 22%;}} @media screen and (min-width: 1090px) and (max-width: 1279px) {#sidebar{width: 240px;}} @media screen and (min-width: 1280px) and (max-width: 1599px) {#sidebar{width: 280px;}} @@ -143,6 +145,7 @@ pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;} @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:first-of-type { margin-top: 4px ; } #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 +157,46 @@ pre, code {font-family: Consolas, Menlo, "Liberation Mono", Courier, monospace;} #sidebar span.icon-warning {margin-left: 5px;} #sidebar li input[type=checkbox] {height: 20px;} -#content { flex-grow: 1; background-color: #fff; margin: 0px; padding: 10px 16px 10px 16px; overflow-x: auto;} +#main.collapsedsidebar #sidebar { display: block; width: 0px !important; position: absolute; box-sizing: border-box; height: 100%; overflow: hidden; transition: 0.35s; } +#main.collapsedsidebar #sidebar:hover { width: 22% !important; } +#main:not(.collapsedsidebar) #content { margin-right: 0; } +#main.collapsedsidebar #sidebar > :not(#sidebar-switch-panel) { + visibility: hidden; +} +#main.collapsedsidebar #sidebar:hover > :not(#sidebar-switch-panel) { + visibility: visible; +} +body div#content { + margin-right: 30px; +} +body #main { + position: relative; +} +#sidebar-switch-panel { + position: relative; + left: 6px; + font-size: 20px; + width: 100% !important; + color: #666; + display: block; + margin-left: -21px; + padding-right: 21px; + border-bottom: 1px solid #ddd; + padding-left: 4px; +} +#sidebar-switch-panel:hover { + background-color: #e6e6e6; +} +#sidebar-switch-button { + color: #666; + width: 100% !important; + display: block; +} +#sidebar-switch-button:hover { + text-decoration: none; +} -#main.nosidebar #sidebar{ display: none; } +#content { flex-grow: 1; background-color: #fff; margin: 0px; padding: 10px 16px 10px 16px; overflow-x: auto;} #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..6f451f05d 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -100,8 +100,15 @@ <% end %> -