Feature #21808 » collapsible-sidebar-with-expand-on-hover-for_Redmine_4_2_1.patch
app/views/layouts/base.html.erb (working copy) | ||
---|---|---|
99 | 99 |
<% end %> |
100 | 100 |
</div> |
101 | 101 | |
102 |
<div id="main" class="<%= sidebar_content? ? '' : 'nosidebar' %>"> |
|
102 |
<div id="main" class="<%= sidebar_content? ? 'collapsiblesidebar' : 'nosidebar' %>"> |
|
103 |
<%= javascript_tag "$('#main.collapsiblesidebar').collapsibleSidebar();" if sidebar_content? %> |
|
103 | 104 |
<div id="sidebar"> |
105 |
<% if sidebar_content? %> |
|
106 |
<div id="sidebar-switch-panel" style="visibility: hidden;"> |
|
107 |
<a id="sidebar-switch-button" href="#">»</a> |
|
108 |
</div> |
|
109 |
<%= javascript_tag "$('#sidebar-switch-panel').css('visibility', 'visible');" %> |
|
110 |
<% end %> |
|
104 | 111 |
<%= yield :sidebar %> |
105 | 112 |
<%= view_layouts_base_sidebar_hook_response %> |
106 | 113 |
</div> |
public/javascripts/application.js (working copy) | ||
---|---|---|
1051 | 1051 |
tribute.attach(element); |
1052 | 1052 |
} |
1053 | 1053 | |
1054 |
// collapsible sidebar jQuery plugin |
|
1055 |
(function($) { |
|
1056 |
// main container this is applied to |
|
1057 |
var main; |
|
1058 |
// triggers show/hide |
|
1059 |
var button; |
|
1060 |
// the key to use in local storage |
|
1061 |
// this will later be expanded using the current controller and action to |
|
1062 |
// allow for different sidebar states for different pages |
|
1063 |
var localStorageKey = 'redmine-sidebar-state'; |
|
1064 |
// true if local storage is available |
|
1065 |
var canUseLocalStorage = function(){ |
|
1066 |
try { |
|
1067 |
if('localStorage' in window){ |
|
1068 |
localStorage.setItem('redmine.test.storage', 'ok'); |
|
1069 |
var item = localStorage.getItem('redmine.test.storage'); |
|
1070 |
localStorage.removeItem('redmine.test.storage'); |
|
1071 |
if(item === 'ok') return true; |
|
1072 |
} |
|
1073 |
} catch (err) {} |
|
1074 |
return false; |
|
1075 |
}(); |
|
1076 |
// function to set current sidebar state |
|
1077 |
var setState = function(state){ |
|
1078 |
if(canUseLocalStorage){ |
|
1079 |
localStorage.setItem(localStorageKey, state); |
|
1080 |
} |
|
1081 |
}; |
|
1082 |
var applyState = function(){ |
|
1083 |
if(main.hasClass('collapsedsidebar')){ |
|
1084 |
button.html("«"); |
|
1085 |
setState('hidden'); |
|
1086 |
} else { |
|
1087 |
button.html("»"); |
|
1088 |
setState('visible'); |
|
1089 |
} |
|
1090 |
}; |
|
1091 |
var setupToggleButton = function(){ |
|
1092 |
button = $('#sidebar-switch-button'); |
|
1093 |
button.click(function(e){ |
|
1094 |
main.addClass("animate"); |
|
1095 |
main.toggleClass('collapsedsidebar'); |
|
1096 |
applyState(); |
|
1097 |
e.preventDefault(); |
|
1098 |
return false; |
|
1099 |
}); |
|
1100 |
applyState(); |
|
1101 |
}; |
|
1102 |
$.fn.collapsibleSidebar = function() { |
|
1103 |
main = this; |
|
1104 |
// determine previously stored sidebar state for this page |
|
1105 |
if(canUseLocalStorage) { |
|
1106 |
// determine current controller/action pair and use them as storage key |
|
1107 |
var bodyClass = $('body').attr('class'); |
|
1108 |
if(bodyClass){ |
|
1109 |
try { |
|
1110 |
localStorageKey += '-' + bodyClass.split(/\s+/).filter(function(s){ |
|
1111 |
return s.match(/(action|controller)-.*/); |
|
1112 |
}).sort().join('-'); |
|
1113 |
} catch(e) { |
|
1114 |
// in case of error (probably IE8), continue with the unmodified key |
|
1115 |
} |
|
1116 |
} |
|
1117 |
var storedState = localStorage.getItem(localStorageKey); |
|
1118 |
main.toggleClass('collapsedsidebar', storedState === 'hidden'); |
|
1119 |
} |
|
1120 |
// draw the toggle button once the DOM is complete |
|
1121 |
$(document).ready(setupToggleButton); |
|
1122 |
}; |
|
1123 |
}(jQuery)); |
|
1054 | 1124 | |
1055 | 1125 |
$(document).ready(setupAjaxIndicator); |
1056 | 1126 |
$(document).ready(hideOnLoad); |
public/stylesheets/application.css Mon Mar 21 22:01:41 2022 | ||
---|---|---|
94 |
#main.nosidebar #sidebar {display: none;} |
|
95 |
#sidebar{ flex-shrink: 0; padding-left: 20px; padding-right: 8px; background: #EEEEEE; border-left: 1px solid #ddd} |
|
... | ... | |
96 |
#sidebar{ flex-shrink: 0; padding-left: 20px; padding-right: 8px; background: #EEEEEE; border-left: 1px solid #ddd; z-index: 1;} |
|
... | ... | |
102 |
#sidebar h3{ font-size: 14px; margin-top:14px; color: #666; } |
|
... | ... | |
103 |
#sidebar h3 { font-size: 14px; margin-top:14px; color: #666; } |
|
104 |
#sidebar h3:first-of-type { margin-top: 4px ; } |
|
... | ... | |
119 |
#main.nosidebar #sidebar{ display: none; } |
|
... | ... | |
121 |
#main.collapsedsidebar #sidebar { display: block; width: 0px !important; position: absolute; box-sizing: border-box; height: 100%; overflow: hidden; transition: 0.35s; } |
|
122 |
#main.collapsedsidebar #sidebar:hover { width: 22% !important; } |
|
123 |
#main:not(.collapsedsidebar) #content { margin-right: 0; } |
|
... | ... | |
125 |
#main.collapsedsidebar #sidebar > :not(#sidebar-switch-panel) { |
|
126 |
visibility: hidden; |
|
127 |
} |
|
128 |
|
|
129 |
#main.collapsedsidebar #sidebar:hover > :not(#sidebar-switch-panel) { |
|
130 |
visibility: visible; |
|
131 |
} |
|
132 |
|
|
133 |
body div#content { |
|
134 |
margin-right: 30px; |
|
135 |
} |
|
136 |
|
|
137 |
body #main { |
|
138 |
position: relative; |
|
139 |
} |
|
140 |
|
|
141 |
#sidebar-switch-panel { |
|
142 |
position: relative; |
|
143 |
left: 6px; |
|
144 |
font-size: 20px; |
|
145 |
width: 100% !important; |
|
146 |
color: #666; |
|
147 |
display: block; |
|
148 |
margin-left: -21px; |
|
149 |
padding-right: 21px; |
|
150 |
border-bottom: 1px solid #ddd; |
|
151 |
padding-left: 4px; |
|
152 |
} |
|
153 |
|
|
154 |
#sidebar-switch-panel:hover { |
|
155 |
background-color: #e6e6e6; |
|
156 |
} |
|
157 |
|
|
158 |
#sidebar-switch-button { |
|
159 |
color: #666; |
|
160 |
width: 100% !important; |
|
161 |
display: block; |
|
162 |
} |
|
163 |
|
|
164 |
#sidebar-switch-button:hover { |
|
165 |
text-decoration: none; |
|
166 |
} |
|
167 |
|