Feature #21808 » collapsible-sidebar-with-expand-on-hover-for_r23166.patch
| app/assets/javascripts/application.js | ||
|---|---|---|
| 1261 | 1261 |
tribute.attach(element); |
| 1262 | 1262 |
} |
| 1263 | 1263 | |
| 1264 |
// collapsible sidebar jQuery plugin |
|
| 1265 |
(function($) {
|
|
| 1266 |
// main container this is applied to |
|
| 1267 |
var main; |
|
| 1268 |
// triggers show/hide |
|
| 1269 |
var button; |
|
| 1270 |
// the key to use in local storage |
|
| 1271 |
// this will later be expanded using the current controller and action to |
|
| 1272 |
// allow for different sidebar states for different pages |
|
| 1273 |
var localStorageKey = 'redmine-sidebar-state'; |
|
| 1274 |
// true if local storage is available |
|
| 1275 |
var canUseLocalStorage = function(){
|
|
| 1276 |
try {
|
|
| 1277 |
if('localStorage' in window){
|
|
| 1278 |
localStorage.setItem('redmine.test.storage', 'ok');
|
|
| 1279 |
var item = localStorage.getItem('redmine.test.storage');
|
|
| 1280 |
localStorage.removeItem('redmine.test.storage');
|
|
| 1281 |
if(item === 'ok') return true; |
|
| 1282 |
} |
|
| 1283 |
} catch (err) {}
|
|
| 1284 |
return false; |
|
| 1285 |
}(); |
|
| 1286 |
// function to set current sidebar state |
|
| 1287 |
var setState = function(state){
|
|
| 1288 |
if(canUseLocalStorage){
|
|
| 1289 |
localStorage.setItem(localStorageKey, state); |
|
| 1290 |
} |
|
| 1291 |
}; |
|
| 1292 |
var applyState = function(){
|
|
| 1293 |
if(main.hasClass('collapsedsidebar')){
|
|
| 1294 |
button.html("«");
|
|
| 1295 |
setState('hidden');
|
|
| 1296 |
} else {
|
|
| 1297 |
button.html("»");
|
|
| 1298 |
setState('visible');
|
|
| 1299 |
} |
|
| 1300 |
}; |
|
| 1301 |
var setupToggleButton = function(){
|
|
| 1302 |
button = $('#sidebar-switch-button');
|
|
| 1303 |
button.click(function(e){
|
|
| 1304 |
main.addClass("animate");
|
|
| 1305 |
main.toggleClass('collapsedsidebar');
|
|
| 1306 |
applyState(); |
|
| 1307 |
e.preventDefault(); |
|
| 1308 |
return false; |
|
| 1309 |
}); |
|
| 1310 |
applyState(); |
|
| 1311 |
}; |
|
| 1312 |
$.fn.collapsibleSidebar = function() {
|
|
| 1313 |
main = this; |
|
| 1314 |
// determine previously stored sidebar state for this page |
|
| 1315 |
if(canUseLocalStorage) {
|
|
| 1316 |
// determine current controller/action pair and use them as storage key |
|
| 1317 |
var bodyClass = $('body').attr('class');
|
|
| 1318 |
if(bodyClass){
|
|
| 1319 |
try {
|
|
| 1320 |
localStorageKey += '-' + bodyClass.split(/\s+/).filter(function(s){
|
|
| 1321 |
return s.match(/(action|controller)-.*/); |
|
| 1322 |
}).sort().join('-');
|
|
| 1323 |
} catch(e) {
|
|
| 1324 |
// in case of error (probably IE8), continue with the unmodified key |
|
| 1325 |
} |
|
| 1326 |
} |
|
| 1327 |
var storedState = localStorage.getItem(localStorageKey); |
|
| 1328 |
main.toggleClass('collapsedsidebar', storedState === 'hidden');
|
|
| 1329 |
} |
|
| 1330 |
// draw the toggle button once the DOM is complete |
|
| 1331 |
$(document).ready(setupToggleButton); |
|
| 1332 |
}; |
|
| 1333 |
}(jQuery)); |
|
| 1334 | ||
| 1264 | 1335 |
$(document).ready(setupAjaxIndicator); |
| 1265 | 1336 |
$(document).ready(hideOnLoad); |
| 1266 | 1337 |
$(document).ready(addFormObserversForDoubleSubmit); |
| app/assets/stylesheets/application.css | ||
|---|---|---|
| 135 | 135 | |
| 136 | 136 |
#main {flex-grow: 2; display: flex; flex-direction: row-reverse;}
|
| 137 | 137 | |
| 138 |
#sidebar{ font-size: 0.8125rem; flex-shrink: 0; padding-left: 20px; padding-right: 8px; background: #f9fafb; border-left: 1px solid #d0d7de}
|
|
| 138 |
#main.nosidebar #sidebar{ display: none; }
|
|
| 139 |
#sidebar{ font-size: 0.8125rem; flex-shrink: 0; padding-left: 20px; padding-right: 8px; background: #f9fafb; border-left: 1px solid #d0d7de; z-index: 1;}
|
|
| 140 | ||
| 139 | 141 |
@media screen and (min-width: 0px) and (max-width: 1089px) {#sidebar{width: 22%;}}
|
| 140 | 142 |
@media screen and (min-width: 1090px) and (max-width: 1279px) {#sidebar{width: 240px;}}
|
| 141 | 143 |
@media screen and (min-width: 1280px) and (max-width: 1599px) {#sidebar{width: 280px;}}
|
| ... | ... | |
| 143 | 145 |
@media screen and (min-width: 1920px) and (max-width: 2559px) {#sidebar{width: 360px;}}
|
| 144 | 146 |
@media screen and (min-width: 2560px) {#sidebar{width: 380px;}}
|
| 145 | 147 |
#sidebar h3{ font-size: 0.875rem; margin-top:14px; color: #555; }
|
| 148 |
#sidebar h3:first-of-type { margin-top: 4px ; }
|
|
| 146 | 149 |
#sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
|
| 147 | 150 |
#sidebar .contextual { margin-right: 1em; }
|
| 148 | 151 |
#sidebar ul, ul.flat {margin: 0; padding: 0;}
|
| ... | ... | |
| 154 | 157 |
#sidebar span.icon-warning {margin-left: 5px;}
|
| 155 | 158 |
#sidebar li input[type=checkbox] {height: 20px;}
|
| 156 | 159 | |
| 157 |
#content { flex-grow: 1; background-color: #fff; margin: 0px; padding: 10px 16px 10px 16px; overflow-x: auto;}
|
|
| 160 |
#main.collapsedsidebar #sidebar { display: block; width: 0px !important; position: absolute; box-sizing: border-box; height: 100%; overflow: hidden; transition: 0.35s; }
|
|
| 161 |
#main.collapsedsidebar #sidebar:hover { width: 22% !important; }
|
|
| 162 |
#main:not(.collapsedsidebar) #content { margin-right: 0; }
|
|
| 163 |
#main.collapsedsidebar #sidebar > :not(#sidebar-switch-panel) {
|
|
| 164 |
visibility: hidden; |
|
| 165 |
} |
|
| 166 |
#main.collapsedsidebar #sidebar:hover > :not(#sidebar-switch-panel) {
|
|
| 167 |
visibility: visible; |
|
| 168 |
} |
|
| 169 |
body div#content {
|
|
| 170 |
margin-right: 30px; |
|
| 171 |
} |
|
| 172 |
body #main {
|
|
| 173 |
position: relative; |
|
| 174 |
} |
|
| 175 |
#sidebar-switch-panel {
|
|
| 176 |
position: relative; |
|
| 177 |
left: 6px; |
|
| 178 |
font-size: 20px; |
|
| 179 |
width: 100% !important; |
|
| 180 |
color: #666; |
|
| 181 |
display: block; |
|
| 182 |
margin-left: -21px; |
|
| 183 |
padding-right: 21px; |
|
| 184 |
border-bottom: 1px solid #ddd; |
|
| 185 |
padding-left: 4px; |
|
| 186 |
} |
|
| 187 |
#sidebar-switch-panel:hover {
|
|
| 188 |
background-color: #e6e6e6; |
|
| 189 |
} |
|
| 190 |
#sidebar-switch-button {
|
|
| 191 |
color: #666; |
|
| 192 |
width: 100% !important; |
|
| 193 |
display: block; |
|
| 194 |
} |
|
| 195 |
#sidebar-switch-button:hover {
|
|
| 196 |
text-decoration: none; |
|
| 197 |
} |
|
| 158 | 198 | |
| 159 |
#main.nosidebar #sidebar{ display: none; }
|
|
| 199 |
#content { flex-grow: 1; background-color: #fff; margin: 0px; padding: 10px 16px 10px 16px; overflow-x: auto;}
|
|
| 160 | 200 | |
| 161 | 201 |
#footer {clear: both; border-top: 1px solid #d0d7de; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
|
| 162 | 202 | |
| app/views/layouts/base.html.erb | ||
|---|---|---|
| 100 | 100 |
<% end %> |
| 101 | 101 |
</div> |
| 102 | 102 | |
| 103 |
<div id="main" class="<%= sidebar_content? ? '' : 'nosidebar' %>"> |
|
| 103 |
<div id="main" class="<%= sidebar_content? ? 'collapsiblesidebar' : 'nosidebar' %>"> |
|
| 104 |
<%= javascript_tag "$('#main.collapsiblesidebar').collapsibleSidebar();" if sidebar_content? %>
|
|
| 104 | 105 |
<div id="sidebar"> |
| 106 |
<% if sidebar_content? %> |
|
| 107 |
<div id="sidebar-switch-panel" style="visibility: hidden;"> |
|
| 108 |
<a id="sidebar-switch-button" href="#">»</a> |
|
| 109 |
</div> |
|
| 110 |
<%= javascript_tag "$('#sidebar-switch-panel').css('visibility', 'visible');" %>
|
|
| 111 |
<% end %> |
|
| 105 | 112 |
<%= yield :sidebar %> |
| 106 | 113 |
<%= view_layouts_base_sidebar_hook_response %> |
| 107 | 114 |
</div> |