summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorMarius Balteanu <marius.balteanu@zitec.com>2024-11-06 23:08:15 +0000
committerMarius Balteanu <marius.balteanu@zitec.com>2024-11-06 23:08:15 +0000
commit5bbab5f8e6fbcf7b16b62975a34b3798541e177d (patch)
tree8c8c42374e490cd0ae0f445224614197daf368f3 /app
parent13db8424228bdda74539ec0ccdce5f8c9777f4d3 (diff)
downloadredmine-5bbab5f8e6fbcf7b16b62975a34b3798541e177d.tar.gz
redmine-5bbab5f8e6fbcf7b16b62975a34b3798541e177d.zip
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
Diffstat (limited to 'app')
-rw-r--r--app/assets/images/icons.svg8
-rw-r--r--app/assets/javascripts/application.js71
-rw-r--r--app/assets/stylesheets/application.css27
-rw-r--r--app/views/layouts/base.html.erb16
4 files changed, 117 insertions, 5 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 @@
<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"/>
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 %>
</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">