Project

General

Profile

Feature #19097 » 0001-Responsive-layout.patch

Jan from Planio www.plan.io, 2015-08-20 13:59

View differences:

app/helpers/application_helper.rb
337 337
        { :value => project_path(:id => p, :jump => current_menu_item) }
338 338
      end
339
      content_tag( :span, nil, :class => 'jump-box-arrow') +
339 340
      select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
340 341
    end
341 342
  end
......
1256 1257
  # Returns the javascript tags that are included in the html layout head
1257 1258
  def javascript_heads
1258
    tags = javascript_include_tag('jquery-1.11.1-ui-1.11.0-ujs-3.1.3', 'application')
1259
    tags = javascript_include_tag('jquery-1.11.1-ui-1.11.0-ujs-3.1.3', 'application', 'responsive')
1259 1260
    unless User.current.pref.warn_on_leaving_unsaved == '0'
1260 1261
      tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1261 1262
    end
app/views/layouts/base.html.erb
3 3
<head>
4 4
<meta charset="utf-8" />
5 5
<title><%= html_title %></title>
6
<meta name="viewport" content="width=device-width, initial-scale=1">
6 7
<meta name="description" content="<%= Redmine::Info.app_name %>" />
7 8
<meta name="keywords" content="issue,bug,tracker" />
8 9
<%= csrf_meta_tag %>
9 10
<%= favicon %>
10
<%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'application', :media => 'all' %>
11
<%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'application', 'responsive', :media => 'all' %>
11 12
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
12 13
<%= javascript_heads %>
13 14
<%= heads_for_theme %>
......
17 18
</head>
18 19
<body class="<%= body_css_classes %>">
19 20
<div id="wrapper">
21

  
22
<div class="flyout-menu js-flyout-menu">
23

  
24

  
25
    <% if User.current.logged? || !Setting.login_required? %>
26
        <div class="flyout-menu__search">
27
            <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
28
            <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
29
            <%= label_tag 'flyout-search', '&#9906;'.html_safe, :class => 'search-magnifier search-magnifier--flyout' %>
30
            <%= text_field_tag 'q', @question, :id => 'flyout-search', :class => 'small js-search-input', :placeholder => l(:label_search) %>
31
            <% end %>
32
        </div>
33
    <% end %>
34

  
35
    <% if User.current.logged? %>
36
        <div class="flyout-menu__avatar <% if !Setting.gravatar_enabled? %>flyout-menu__avatar--no-avatar<% end %>">
37
            <% if Setting.gravatar_enabled? %>
38
                <%= link_to(avatar(User.current, :size => "80"), user_path(User.current)) %>
39
            <% end %>
40
            <%= link_to_user(User.current, :format => :username) %>
41
        </div>
42
    <% end %>
43

  
44
    <% if display_main_menu?(@project) %>
45
        <h3><%= l(:label_project) %></h3>
46
        <span class="js-project-menu"></span>
47
    <% end %>
48

  
49
    <h3><%= l(:label_general) %></h3>
50
    <span class="js-general-menu"></span>
51

  
52
    <span class="js-sidebar flyout-menu__sidebar"></span>
53

  
54
    <h3><%= l(:label_profile) %></h3>
55
    <span class="js-profile-menu"></span>
56

  
57
</div>
58

  
20 59
<div id="wrapper2">
21 60
<div id="wrapper3">
22 61
<div id="top-menu">
......
28 67
</div>
29 68
<div id="header">
69

  
70
    <a href="#" class="mobile-toggle-button js-flyout-menu-toggle-button"></a>
71

  
30 72
    <% if User.current.logged? || !Setting.login_required? %>
31 73
    <div id="quick-search">
32 74
        <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
public/javascripts/responsive.js
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);
public/stylesheets/responsive.css
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
}
(16-16/16)