##// END OF EJS Templates
Responsive layout for mobile devices (#19097)....
Jean-Philippe Lang -
r14435:e680ae1aa31b
parent child
Show More
@@ -0,0 +1,83
1 // generic layout specific responsive stuff goes here
2
3 function openFlyout() {
4 $('html').addClass('flyout-is-active');
5 $('#wrapper2').on('click', function(e){
6 e.preventDefault();
7 e.stopPropagation();
8 closeFlyout();
9 });
10 }
11
12 function closeFlyout() {
13 $('html').removeClass('flyout-is-active');
14 $('#wrapper2').off('click');
15 }
16
17
18 function isMobile() {
19 return $('.js-flyout-menu-toggle-button').is(":visible");
20 }
21
22 function setupFlyout() {
23 var mobileInit = false,
24 desktopInit = false;
25
26 /* click handler for mobile menu toggle */
27 $('.js-flyout-menu-toggle-button').on('click', function(e) {
28 e.preventDefault();
29 e.stopPropagation();
30 if($('html').hasClass('flyout-is-active')) {
31 closeFlyout();
32 } else {
33 openFlyout();
34 }
35 });
36
37 /* bind resize handler */
38 $(window).resize(function() {
39 initMenu();
40 })
41
42 /* menu init function for dom detaching and appending on mobile / desktop view */
43 function initMenu() {
44
45 var _initMobileMenu = function() {
46 /* only init mobile menu, if it hasn't been done yet */
47 if(!mobileInit) {
48
49 $('#main-menu > ul').detach().appendTo('.js-project-menu');
50 $('#top-menu > ul').detach().appendTo('.js-general-menu');
51 $('#sidebar > *').detach().appendTo('.js-sidebar');
52 $('#account ul').detach().appendTo('.js-profile-menu');
53
54 mobileInit = true;
55 desktopInit = false;
56 }
57 }
58
59 var _initDesktopMenu = function() {
60 if(!desktopInit) {
61
62 $('.js-project-menu > ul').detach().appendTo('#main-menu');
63 $('.js-general-menu ul').detach().appendTo('#top-menu');
64 $('.js-sidebar > *').detach().appendTo('#sidebar');
65 $('.js-profile-menu ul').detach().appendTo('#account');
66
67 desktopInit = true;
68 mobileInit = false;
69 }
70 }
71
72 if(isMobile()) {
73 _initMobileMenu();
74 } else {
75 _initDesktopMenu();
76 }
77 }
78
79 // init menu on page load
80 initMenu();
81 }
82
83 $(document).ready(setupFlyout);
This diff has been collapsed as it changes many lines, (595 lines changed) Show them Hide them
@@ -0,0 +1,595
1 /*----------------------------------------*\
2 RESPONSIVE CSS
3 \*----------------------------------------*/
4
5
6 /*
7
8 CONTENTS
9
10 A) BASIC MOBILE RESETS
11 B) HEADER & TOP MENUS
12 C) MAIN CONTENT & SIDEBAR
13 D) TOGGLE BUTTON & FLYOUT MENU
14
15 */
16
17
18 /* Hide new elements (toggle button and flyout menu) above 900px */
19 .mobile-toggle-button,
20 .flyout-menu
21 {
22 display: none;
23 }
24
25 /*
26 redmine's body is set to min-width: 900px
27 add first breakpoint here and start adding responsiveness
28 */
29
30 @media all and (max-width: 899px)
31 {
32 /*----------------------------------------*\
33 A) BASIC MOBILE RESETS
34 \*----------------------------------------*/
35
36 /*
37 apply natural border box, see: http://www.paulirish.com/2012/box-sizing-border-box-ftw/
38 this helps us to better deal with percentages and padding / margin
39 */
40 *,
41 *:before,
42 *:after
43 {
44 -webkit-box-sizing: border-box;
45 -moz-box-sizing: border-box;
46 box-sizing: border-box;
47 }
48
49 body,
50 html
51 {
52 height: 100%;
53 margin: 0;
54 padding: 0;
55 }
56
57 html
58 {
59 overflow-y: auto; /* avoid 2nd scrollbar on desktop */
60 }
61
62 body
63 {
64 overflow-x: hidden; /* hide horizontal overflow */
65
66 min-width: 0; /* reset the min-width of 900px */
67 }
68
69
70 body,
71 input,
72 select,
73 textarea,
74 button
75 {
76 font-size: 14px; /* Set font-size for standard elements to 14px */
77 }
78
79
80 select
81 {
82 max-width: 100%; /* prevent long names within select menues from breaking content */
83 }
84
85
86 #wrapper
87 {
88 position: relative;
89
90 max-width: 100%;
91 }
92
93 #wrapper,
94 #wrapper2
95 {
96 margin: 0;
97 }
98
99 /*----------------------------------------*\
100 B) HEADER & TOP MENUS
101 \*----------------------------------------*/
102
103 #header
104 {
105 width: 100%;
106 height: 64px; /* the height of our header on mobile */
107 min-height: 0;
108 margin: 0;
109 padding: 0;
110
111 border: none;
112 background-color: #628db6;
113 }
114
115 /* Hide project name on mobile (project name is still visible in select menu) */
116 #header h1
117 {
118 display: none !important;
119 }
120
121 /* reset #header a color for mobile toggle button */
122 #header a.mobile-toggle-button
123 {
124 color: #f8f8f8;
125 }
126
127
128 /* Hide top-menu and main-menu on mobile, because it's placed in our flyout menu */
129 #top-menu,
130 #header #main-menu
131 {
132 display: none;
133 }
134
135 /* the quick search within header holding search form and #project_quick_jump_box box*/
136 #header #quick-search
137 {
138 float: none;
139 clear: none; /* there are themes which set clear property, this resets it */
140
141 max-width: 100%; /* reset max-width */
142 margin: 0;
143
144 background: inherit;
145 }
146
147 /* this represents the dropdown arrow to left of the mobile project menu */
148 #header .jump-box-arrow:before
149 {
150 /* set a font-size in order to achive same result in different themes */
151 font-family: Verdana, sans-serif;
152 font-size: 2em;
153 line-height: 64px;
154
155 position: absolute;
156 left: 0;
157
158 width: 2em;
159 padding: 0 .5em;
160 /* achieve dropdwon arrow by scaling a caret character */
161
162 content: '^';
163 -webkit-transform: scale(1,-.8);
164 -ms-transform: scale(1,-.8);
165 transform: scale(1,-.8);
166 text-align: right;
167 pointer-events: none;
168
169 opacity: .6;
170 }
171
172 /* styles for combobox within quick-search (#project_quick_jump_box) */
173 #header #quick-search select
174 {
175 font-size: 1.5em;
176 font-weight: bold;
177 line-height: 1.2;
178
179 position: absolute;
180 top: 15px;
181 left: 0;
182
183 float: left;
184
185 width: 100%;
186 max-width: 100%;
187 height: 2em;
188 height: 35px;
189 padding: 5px;
190 padding-right: 72px;
191 padding-left: 50px;
192
193 text-indent: .01px;
194
195 color: inherit;
196 border: 0;
197 -webkit-border-radius: 0;
198 border-radius: 0;
199 background: none;
200 -webkit-box-shadow: none;
201 box-shadow: none;
202 /* hide default browser arrow */
203
204 -webkit-appearance: none;
205 -moz-appearance: none;
206 }
207
208 #header #quick-search form
209 {
210 display: none;
211 }
212
213 /*----------------------------------------*\
214 C) MAIN CONTENT & SIDEBAR
215 \*----------------------------------------*/
216
217 #main
218 {
219 padding: 0;
220 }
221
222 #main.nosidebar #content,
223 div#content
224 {
225 width: 100%;
226 min-height: 0; /* reset min-height of #content */
227 margin: 0;
228 }
229
230
231 /* hide sidebar and sidebar switch panel, since it's placed in mobile flyout menu */
232 #sidebar,
233 #sidebar-switch-panel
234 {
235 display: none;
236 }
237
238 .splitcontentleft
239 {
240 width: 100%; /* use full width */
241 }
242
243 .splitcontentright
244 {
245 width: 100%; /* use full width */
246 }
247
248 /*----------------------------------------*\
249 D) TOGGLE BUTTON & FLYOUT MENU
250 \*----------------------------------------*/
251
252 /* Mobile toggle button */
253
254 .mobile-toggle-button
255 {
256 font-size: 42px;
257 line-height: 64px;
258
259 position: relative;
260 z-index: 10;
261
262 display: block; /* remove display: none; of non-mobile version */
263 float: right;
264
265 width: 60px;
266 height: 64px;
267 margin-top: 0;
268
269 text-align: center;
270
271 border-left: 1px solid #ddd;
272 }
273
274 .mobile-toggle-button:hover,
275 .mobile-toggle-button:active
276 {
277 text-decoration: none;
278 }
279
280 .mobile-toggle-button:after
281 {
282 font-family: Verdana, sans-serif;
283
284 display: block;
285
286 margin-top: -3px;
287
288 content: '\2261';
289 }
290
291 /* search magnifier icon */
292 .search-magnifier
293 {
294 font-family: Verdana;
295
296 cursor: pointer;
297 -webkit-transform: rotate(-45deg);
298 -moz-transform: rotate(45deg);
299 -o-transform: rotate(45deg);
300
301 color: #bbb;
302 }
303
304 .search-magnifier--flyout
305 {
306 font-size: 25px;
307 line-height: 54px;
308
309 position: absolute;
310 z-index: 1;
311 left: 12px;
312 }
313
314
315 /* Flyout Menu */
316
317 .flyout-menu
318 {
319 position: absolute;
320 right: -250px;
321
322 display: block; /* remove display: none; of non-mobile version */
323 overflow-x: hidden;
324
325 width: 250px;
326 height: 100%;
327 margin: 0; /* reset margin for themes that define it */
328 padding: 0; /* reset padding for themes that define it */
329
330 color: white;
331 background-color: #3e5b76;
332 }
333
334
335 /* avoid zoom on search input focus for ios devices */
336 .flyout-menu input[type='text']
337 {
338 font-size: 16px;
339 }
340
341 .flyout-menu h3
342 {
343 font-size: 11px;
344 line-height: 19px;
345
346 height: 20px;
347 margin: 0;
348 padding: 0;
349
350 letter-spacing: .1em;
351 text-transform: uppercase;
352
353 color: white;
354 border-top: 1px solid #506a83;
355 border-bottom: 1px solid #506a83;
356 background-color: #628db6;
357 }
358
359 .flyout-menu h4
360 {
361 color: white;
362 }
363
364 .flyout-menu h3,
365 .flyout-menu h4,
366 .flyout-menu > p,
367 .flyout-menu > a,
368 .flyout-menu ul li a,
369 .flyout-menu__search,
370 .flyout-menu__sidebar > div,
371 .flyout-menu__sidebar > p,
372 .flyout-menu__sidebar > a,
373 .flyout-menu__sidebar > form,
374 .flyout-menu > div,
375 .flyout-menu > form
376 {
377 padding-left: 8px;
378 }
379
380 .flyout-menu .flyout-menu__avatar
381 {
382 margin-top: -1px; /* move avatar up 1px */
383 padding-left: 0;
384 }
385
386 .flyout-menu__sidebar > form
387 {
388 display: block;
389 }
390
391 .flyout-menu__sidebar > form h3
392 {
393 margin-left: -8px;
394 }
395
396 .flyout-menu__sidebar > form label
397 {
398 display: inline-block;
399
400 margin: 8px 0;
401 }
402
403 .flyout-menu__sidebar > form br br
404 {
405 display: none;
406 }
407
408 .flyout-menu ul
409 {
410 margin: 0;
411 padding: 0;
412
413 list-style: none;
414 }
415
416 .flyout-menu ul li a
417 {
418 line-height: 40px;
419
420 display: block;
421 overflow: hidden;
422
423 height: 40px;
424
425 white-space: nowrap;
426 text-overflow: ellipsis;
427
428 border-top: 1px solid rgba(255,255,255,.1);
429 }
430
431 .flyout-menu ul li:first-child a
432 {
433 line-height: 39px;
434
435 height: 39px;
436
437 border-top: none;
438 }
439
440 .flyout-menu a
441 {
442 color: white;
443 }
444
445 .flyout-menu ul li a:hover
446 {
447 text-decoration: none;
448 }
449
450 .flyout-menu ul li a.new-object,
451 .new-object ~ .menu-children
452 {
453 display: none;
454 }
455
456 /* Left flyout search container */
457 .flyout-menu__search
458 {
459 line-height: 54px;
460
461 height: 64px;
462 padding-top: 3px;
463 padding-right: 8px;
464 }
465
466 .flyout-menu__search input[type='text']
467 {
468 line-height: 2;
469
470 width: 100%;
471 height: 38px;
472 padding-left: 27px;
473
474 vertical-align: middle;
475
476 border: none;
477 -webkit-border-radius: 3px;
478 border-radius: 3px;
479 background-color: #fff;
480 }
481
482 .flyout-menu__avatar
483 {
484 display: -webkit-box;
485 display: -webkit-flex;
486 display: -ms-flexbox;
487 display: flex;
488
489 width: 100%;
490
491 border-top: 1px solid rgba(255,255,255,.1);
492 }
493
494
495 .flyout-menu__avatar img.gravatar
496 {
497 width: 40px;
498 height: 40px;
499 padding: 0;
500
501 vertical-align: top;
502
503 border-width: 0;
504 }
505
506 .flyout-menu__avatar a
507 {
508 line-height: 40px;
509
510 height: auto;
511 height: 40px;
512
513 text-decoration: none;
514
515 color: white;
516 }
517
518 /* avatar */
519 .flyout-menu__avatar a:first-child
520 {
521 line-height: 0;
522
523 width: 40px;
524 padding: 0;
525 }
526
527 .flyout-menu__avatar .user
528 {
529 padding-left: 15px;
530 }
531
532 /* user link when no avatar is present */
533 .flyout-menu__avatar--no-avatar a.user
534 {
535 line-height: 40px;
536
537 padding-left: 8px;
538 }
539
540
541 .flyout-is-active body
542 {
543 overflow: hidden; /* for body not to have scrollbars when left flyout menu is active */
544 }
545
546 html.flyout-is-active
547 {
548 overflow: hidden;
549 }
550
551
552 .flyout-is-active #wrapper
553 {
554 right: 250px; /* when left flyout is active, move body to the right (same amount like flyout-menu's width) */
555
556 height: 100%;
557 }
558
559 .flyout-is-active .mobile-toggle-button:after
560 {
561 content: '\00D7'; /* close glyph */
562 }
563
564 .flyout-is-active #wrapper2
565 {
566
567 /*
568 * only relevant for devices with cursor when flyout it active, in order to show,
569 * that whole wrapper content is clickable and closes flyout menu
570 */
571 cursor: pointer;
572 }
573
574
575 #admin-menu
576 {
577 padding-left: 0;
578 }
579
580 #admin-menu li
581 {
582 padding-bottom: 0;
583 }
584
585 #admin-menu a,
586 #admin-menu a.selected
587 {
588 line-height: 40px;
589
590 padding: 0;
591 padding-left: 32px !important;
592
593 background-position: 8px 50%;
594 }
595 }
@@ -340,6 +340,7 module ApplicationHelper
340 { :value => project_path(:id => p, :jump => current_menu_item) }
340 { :value => project_path(:id => p, :jump => current_menu_item) }
341 end
341 end
342
342
343 content_tag( :span, nil, :class => 'jump-box-arrow') +
343 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
344 select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
344 end
345 end
345 end
346 end
@@ -1267,7 +1268,7 module ApplicationHelper
1267
1268
1268 # Returns the javascript tags that are included in the html layout head
1269 # Returns the javascript tags that are included in the html layout head
1269 def javascript_heads
1270 def javascript_heads
1270 tags = javascript_include_tag('jquery-1.11.1-ui-1.11.0-ujs-3.1.4', 'application')
1271 tags = javascript_include_tag('jquery-1.11.1-ui-1.11.0-ujs-3.1.4', 'application', 'responsive')
1271 unless User.current.pref.warn_on_leaving_unsaved == '0'
1272 unless User.current.pref.warn_on_leaving_unsaved == '0'
1272 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1273 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1273 end
1274 end
@@ -4,11 +4,12
4 <meta charset="utf-8" />
4 <meta charset="utf-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
5 <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
6 <title><%= html_title %></title>
6 <title><%= html_title %></title>
7 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <meta name="description" content="<%= Redmine::Info.app_name %>" />
8 <meta name="description" content="<%= Redmine::Info.app_name %>" />
8 <meta name="keywords" content="issue,bug,tracker" />
9 <meta name="keywords" content="issue,bug,tracker" />
9 <%= csrf_meta_tag %>
10 <%= csrf_meta_tag %>
10 <%= favicon %>
11 <%= favicon %>
11 <%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'application', :media => 'all' %>
12 <%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'application', 'responsive', :media => 'all' %>
12 <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
13 <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
13 <%= javascript_heads %>
14 <%= javascript_heads %>
14 <%= heads_for_theme %>
15 <%= heads_for_theme %>
@@ -18,6 +19,44
18 </head>
19 </head>
19 <body class="<%= body_css_classes %>">
20 <body class="<%= body_css_classes %>">
20 <div id="wrapper">
21 <div id="wrapper">
22
23 <div class="flyout-menu js-flyout-menu">
24
25
26 <% if User.current.logged? || !Setting.login_required? %>
27 <div class="flyout-menu__search">
28 <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
29 <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
30 <%= label_tag 'flyout-search', '&#9906;'.html_safe, :class => 'search-magnifier search-magnifier--flyout' %>
31 <%= text_field_tag 'q', @question, :id => 'flyout-search', :class => 'small js-search-input', :placeholder => l(:label_search) %>
32 <% end %>
33 </div>
34 <% end %>
35
36 <% if User.current.logged? %>
37 <div class="flyout-menu__avatar <% if !Setting.gravatar_enabled? %>flyout-menu__avatar--no-avatar<% end %>">
38 <% if Setting.gravatar_enabled? %>
39 <%= link_to(avatar(User.current, :size => "80"), user_path(User.current)) %>
40 <% end %>
41 <%= link_to_user(User.current, :format => :username) %>
42 </div>
43 <% end %>
44
45 <% if display_main_menu?(@project) %>
46 <h3><%= l(:label_project) %></h3>
47 <span class="js-project-menu"></span>
48 <% end %>
49
50 <h3><%= l(:label_general) %></h3>
51 <span class="js-general-menu"></span>
52
53 <span class="js-sidebar flyout-menu__sidebar"></span>
54
55 <h3><%= l(:label_profile) %></h3>
56 <span class="js-profile-menu"></span>
57
58 </div>
59
21 <div id="wrapper2">
60 <div id="wrapper2">
22 <div id="wrapper3">
61 <div id="wrapper3">
23 <div id="top-menu">
62 <div id="top-menu">
@@ -29,6 +68,9
29 </div>
68 </div>
30
69
31 <div id="header">
70 <div id="header">
71
72 <a href="#" class="mobile-toggle-button js-flyout-menu-toggle-button"></a>
73
32 <% if User.current.logged? || !Setting.login_required? %>
74 <% if User.current.logged? || !Setting.login_required? %>
33 <div id="quick-search">
75 <div id="quick-search">
34 <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
76 <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
General Comments 0
You need to be logged in to leave comments. Login now