History

Improve tab navigation

Closes #396.

Thibaut Courouble committed 10 years ago

Commit 39b98461 parent 87ede1f12 files · +54 −27Browse files

Unified · Split

12 files
  1. assets/javascripts/templates/pages/about_tmpl.coffee +1 −1
  2. assets/javascripts/templates/pages/root_tmpl.coffee.erb +2 −2
  3. assets/javascripts/templates/sidebar_tmpl.coffee +9 −9
  4. assets/javascripts/views/layout/nav.coffee +2 −0
  5. assets/javascripts/views/search/search_scope.coffee +5 −4
  6. assets/javascripts/views/sidebar/doc_picker.coffee +17 −4
  7. assets/javascripts/views/sidebar/sidebar.coffee +2 −0
  8. assets/stylesheets/components/_header.scss +2 −2
  9. assets/stylesheets/components/_sidebar.scss +4 −2
  10. assets/stylesheets/global/_base.scss +7 −0
  11. assets/stylesheets/global/_classes.scss +1 −1
  12. views/app.erb +2 −2
assets/javascripts/templates/pages/about_tmpl.coffee+1−1
@@ -15,7 +15,7 @@ app.templates.aboutPage = -> """ · 14 unmodified lines
15 <ul>15 <ul>
16 <li>Created and maintained by <a href="http://thibaut.me">Thibaut Courouble</a>16 <li>Created and maintained by <a href="http://thibaut.me">Thibaut Courouble</a>
17 <li>Free and <a href="https://github.com/Thibaut/devdocs">open source</a>17 <li>Free and <a href="https://github.com/Thibaut/devdocs">open source</a>
18 <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>18 <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20" tabindex="-1"></iframe>
19 </ul>19 </ul>
20 <p>To keep up-to-date with the latest news:20 <p>To keep up-to-date with the latest news:
21 <ul>21 <ul>
assets/javascripts/templates/pages/root_tmpl.coffee.erb+2−2
@@ -3,7 +3,7 @@ app.templates.splash = """<div class="_splash-title">DevDocs</div>""" · 2 unmodified lines
3<% if App.development? %>3<% if App.development? %>
4app.templates.intro = """4app.templates.intro = """
5 <div class="_intro"><div class="_intro-message">5 <div class="_intro"><div class="_intro-message">
6 <a class="_intro-hide" data-hide-intro>Stop showing this message</a>6 <a href="#" class="_intro-hide" data-hide-intro>Stop showing this message</a>
7 <h2 class="_intro-title">Hi there!</h2>7 <h2 class="_intro-title">Hi there!</h2>
8 <p>Thanks for downloading DevDocs. Here are a few things you should know:8 <p>Thanks for downloading DevDocs. Here are a few things you should know:
9 <ol class="_intro-list">9 <ol class="_intro-list">
@@ -27,7 +27,7 @@ app.templates.intro = """ · 17 unmodified lines
27<% else %>27<% else %>
28app.templates.intro = """28app.templates.intro = """
29 <div class="_intro"><div class="_intro-message">29 <div class="_intro"><div class="_intro-message">
30 <a class="_intro-hide" data-hide-intro>Stop showing this message</a>30 <a href="#" class="_intro-hide" data-hide-intro>Stop showing this message</a>
31 <h2 class="_intro-title">Welcome!</h2>31 <h2 class="_intro-title">Welcome!</h2>
32 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.32 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.
33 Here's what you should know before you start:33 Here's what you should know before you start:
assets/javascripts/templates/sidebar_tmpl.coffee+9−9
@@ -3,7 +3,7 @@ templates = app.templates · 2 unmodified lines
3templates.sidebarDoc = (doc, options = {}) ->3templates.sidebarDoc = (doc, options = {}) ->
4 link = """<a href="#{doc.fullPath()}" class="_list-item _icon-#{doc.icon} """4 link = """<a href="#{doc.fullPath()}" class="_list-item _icon-#{doc.icon} """
5 link += if options.disabled then '_list-disabled' else '_list-dir'5 link += if options.disabled then '_list-disabled' else '_list-dir'
6 link += """" data-slug="#{doc.slug}" title="#{doc.fullName}">"""6 link += """" data-slug="#{doc.slug}" title="#{doc.fullName}" tabindex="-1">"""
7 if options.disabled7 if options.disabled
8 link += """<span class="_list-enable" data-enable="#{doc.slug}">Enable</span>"""8 link += """<span class="_list-enable" data-enable="#{doc.slug}">Enable</span>"""
9 else9 else
@@ -14,10 +14,10 @@ templates.sidebarDoc = (doc, options = {}) -> · 4 unmodified lines
14 link + "</span></a>"14 link + "</span></a>"
15 15
16templates.sidebarType = (type) ->16templates.sidebarType = (type) ->
17 """<a href="#{type.fullPath()}" class="_list-item _list-dir" data-slug="#{type.slug}"><span class="_list-arrow"></span><span class="_list-count">#{type.count}</span><span class="_list-text">#{type.name}</span></a>"""17 """<a href="#{type.fullPath()}" class="_list-item _list-dir" data-slug="#{type.slug}" tabindex="-1"><span class="_list-arrow"></span><span class="_list-count">#{type.count}</span><span class="_list-text">#{type.name}</span></a>"""
18 18
19templates.sidebarEntry = (entry) ->19templates.sidebarEntry = (entry) ->
20 """<a href="#{entry.fullPath()}" class="_list-item _list-hover">#{$.escape entry.name}</a>"""20 """<a href="#{entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">#{$.escape entry.name}</a>"""
21 21
22templates.sidebarResult = (entry) ->22templates.sidebarResult = (entry) ->
23 addons = if entry.isIndex() and app.disabledDocs.contains(entry.doc)23 addons = if entry.isIndex() and app.disabledDocs.contains(entry.doc)
@@ -25,17 +25,17 @@ templates.sidebarResult = (entry) -> · 1 unmodified line
25 else25 else
26 """<span class="_list-reveal" data-reset-list title="Reveal in list"></span>"""26 """<span class="_list-reveal" data-reset-list title="Reveal in list"></span>"""
27 addons += """<span class="_list-count">#{entry.doc.short_version}</span>""" if entry.doc.version and not entry.isIndex()27 addons += """<span class="_list-count">#{entry.doc.short_version}</span>""" if entry.doc.version and not entry.isIndex()
28 """<a href="#{entry.fullPath()}" class="_list-item _list-hover _list-result _icon-#{entry.doc.icon}">#{addons}<span class="_list-text">#{$.escape entry.name}</span></a>"""28 """<a href="#{entry.fullPath()}" class="_list-item _list-hover _list-result _icon-#{entry.doc.icon}" tabindex="-1">#{addons}<span class="_list-text">#{$.escape entry.name}</span></a>"""
29 29
30templates.sidebarNoResults = ->30templates.sidebarNoResults = ->
31 html = """ <div class="_list-note">No results.</div> """31 html = """ <div class="_list-note">No results.</div> """
32 html += """32 html += """
33 <div class="_list-note">Note: documentations must be <a class="_list-note-link" data-pick-docs>enabled</a> to appear in the search.</div>33 <div class="_list-note">Note: documentations must be <a href="#" class="_list-note-link" data-pick-docs>enabled</a> to appear in the search.</div>
34 """ unless app.isSingleDoc() or app.disabledDocs.isEmpty()34 """ unless app.isSingleDoc() or app.disabledDocs.isEmpty()
35 html35 html
36 36
37templates.sidebarPageLink = (count) ->37templates.sidebarPageLink = (count) ->
38 """<span class="_list-item _list-pagelink">Show more\u2026 (#{count})</span>"""38 """<span role="link" class="_list-item _list-pagelink">Show more\u2026 (#{count})</span>"""
39 39
40templates.sidebarLabel = (doc, options = {}) ->40templates.sidebarLabel = (doc, options = {}) ->
41 label = """<label class="_list-item"""41 label = """<label class="_list-item"""
@@ -47,7 +47,7 @@ templates.sidebarLabel = (doc, options = {}) -> · 5 unmodified lines
47templates.sidebarVersionedDoc = (doc, versions, options = {}) ->47templates.sidebarVersionedDoc = (doc, versions, options = {}) ->
48 html = """<div class="_list-item _list-dir _list-rdir _icon-#{doc.icon}"""48 html = """<div class="_list-item _list-dir _list-rdir _icon-#{doc.icon}"""
49 html += " open" if options.open49 html += " open" if options.open
50 html + """"><span class="_list-arrow"></span>#{doc.name}</div><div class="_list _list-sub">#{versions}</div>"""50 html + """" tabindex="0"><span class="_list-arrow"></span>#{doc.name}</div><div class="_list _list-sub">#{versions}</div>"""
51 51
52templates.sidebarDisabled = (options) ->52templates.sidebarDisabled = (options) ->
53 """<h6 class="_list-title"><span class="_list-arrow"></span>Disabled (#{options.count})</h6>"""53 """<h6 class="_list-title"><span class="_list-arrow"></span>Disabled (#{options.count})</h6>"""
@@ -56,7 +56,7 @@ templates.sidebarDisabledList = (html) -> · 2 unmodified lines
56 """<div class="_disabled-list">#{html}</div>"""56 """<div class="_disabled-list">#{html}</div>"""
57 57
58templates.sidebarDisabledVersionedDoc = (doc, versions) ->58templates.sidebarDisabledVersionedDoc = (doc, versions) ->
59 """<a class="_list-item _list-dir _icon-#{doc.icon} _list-disabled" data-slug="#{doc.slug_without_version}"><span class="_list-arrow"></span>#{doc.name}</a><div class="_list _list-sub">#{versions}</div>"""59 """<a class="_list-item _list-dir _icon-#{doc.icon} _list-disabled" data-slug="#{doc.slug_without_version}" tabindex="-1"><span class="_list-arrow"></span>#{doc.name}</a><div class="_list _list-sub">#{versions}</div>"""
60 60
61templates.sidebarPickerNote = """61templates.sidebarPickerNote = """
62 <div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div>62 <div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div>
@@ -67,9 +67,9 @@ sidebarFooter = (html) -> """<div class="_sidebar-footer">#{html}</div>""" · 4 unmodified lines
67 67
68templates.sidebarSettings = ->68templates.sidebarSettings = ->
69 sidebarFooter """69 sidebarFooter """
70 <button type="button" class="_sidebar-footer-link _sidebar-footer-edit" data-pick-docs>Select documentation</button>
70 <button type="button" class="_sidebar-footer-link _sidebar-footer-light" title="Toggle light" data-light>Toggle light</button>71 <button type="button" class="_sidebar-footer-link _sidebar-footer-light" title="Toggle light" data-light>Toggle light</button>
71 <button type="button" class="_sidebar-footer-link _sidebar-footer-layout" title="Toggle layout" data-layout>Toggle layout</button>72 <button type="button" class="_sidebar-footer-link _sidebar-footer-layout" title="Toggle layout" data-layout>Toggle layout</button>
72 <a href="#" class="_sidebar-footer-link _sidebar-footer-edit" data-pick-docs>Select documentation</a>
73 """73 """
74 74
75templates.sidebarSave = ->75templates.sidebarSave = ->
assets/javascripts/views/layout/nav.coffee+2−0
@@ -9,11 +9,13 @@ class app.views.Nav extends app.View · 8 unmodified lines
9 @deselect()9 @deselect()
10 if @current = @find "a[href='#{href}']"10 if @current = @find "a[href='#{href}']"
11 @current.classList.add @constructor.activeClass11 @current.classList.add @constructor.activeClass
12 @current.setAttribute 'tabindex', '-1'
12 return13 return
13 14
14 deselect: ->15 deselect: ->
15 if @current16 if @current
16 @current.classList.remove @constructor.activeClass17 @current.classList.remove @constructor.activeClass
18 @current.removeAttribute 'tabindex'
17 @current = null19 @current = null
18 return20 return
19 21
assets/javascripts/views/search/search_scope.coffee+5−4
@@ -84,10 +84,11 @@ class app.views.SearchScope extends app.View · 83 unmodified lines
84 if @doc and not @input.value84 if @doc and not @input.value
85 $.stopEvent(event)85 $.stopEvent(event)
86 @reset()86 @reset()
87 else if event.which is 9 or # tab87 else if not @doc and @input.value
88 event.which is 32 and (app.isMobile() or $.isTouchScreen()) # space88 if event.which is 9 or # tab
89 $.stopEvent(event)89 event.which is 32 and (app.isMobile() or $.isTouchScreen()) # space
90 @search @input.value[0...@input.selectionStart]90 @search @input.value[0...@input.selectionStart]
91 $.stopEvent(event) if @doc
91 return92 return
92 93
93 extractHashValue: ->94 extractHashValue: ->
assets/javascripts/views/sidebar/doc_picker.coffee+17−4
@@ -21,14 +21,14 @@ class app.views.DocPicker extends app.View · 20 unmodified lines
21 @render()21 @render()
22 @findByTag('input')?.focus()22 @findByTag('input')?.focus()
23 app.appCache?.on 'progress', @onAppCacheProgress23 app.appCache?.on 'progress', @onAppCacheProgress
24 $.on @el, 'focus', @onFocus, true24 $.on @el, 'focus', @onDOMFocus, true
25 return25 return
26 26
27 deactivate: ->27 deactivate: ->
28 if super28 if super
29 @empty()29 @empty()
30 app.appCache?.off 'progress', @onAppCacheProgress30 app.appCache?.off 'progress', @onAppCacheProgress
31 $.off @el, 'focus', @onFocus, true31 $.off @el, 'focus', @onDOMFocus, true
32 return32 return
33 33
34 render: ->34 render: ->
@@ -84,14 +84,27 @@ class app.views.DocPicker extends app.View · 49 unmodified lines
84 input.name84 input.name
85 85
86 onClick: (event) =>86 onClick: (event) =>
87 if @focusTimeout
88 clearTimeout @focusTimeout
89 @focusTimeout = null
87 return if event.which isnt 190 return if event.which isnt 1
88 if event.target is @saveLink91 if event.target is @saveLink
89 $.stopEvent(event)92 $.stopEvent(event)
90 @save()93 @save()
91 return94 return
92 95
93 onFocus: (event) ->96 onDOMFocus: (event) =>
94 $.scrollTo event.target.parentNode, null, 'continuous', bottomGap: 297 target = event.target
98 if target.tagName is 'INPUT'
99 $.scrollTo target.parentNode, null, 'continuous', bottomGap: 2
100 else if target.classList.contains(app.views.ListFold.targetClass)
101 target.blur()
102 @focusTimeout = setTimeout =>
103 @listFold.open(target) unless target.classList.contains(app.views.ListFold.activeClass)
104 $('input', target.nextElementSibling).focus()
105 @focusTimeout = null
106 , 10
107 return
95 108
96 onEnter: =>109 onEnter: =>
97 @save()110 @save()
assets/javascripts/views/sidebar/sidebar.coffee+2−0
@@ -101,9 +101,11 @@ class app.views.Sidebar extends app.View · 100 unmodified lines
101 @reset()101 @reset()
102 else if event.target.hasAttribute? 'data-light'102 else if event.target.hasAttribute? 'data-light'
103 $.stopEvent(event)103 $.stopEvent(event)
104 document.activeElement?.blur()
104 app.document.toggleLight()105 app.document.toggleLight()
105 else if event.target.hasAttribute? 'data-layout'106 else if event.target.hasAttribute? 'data-layout'
106 $.stopEvent(event)107 $.stopEvent(event)
108 document.activeElement?.blur()
107 app.document.toggleLayout()109 app.document.toggleLayout()
108 return110 return
109 111
assets/stylesheets/components/_header.scss+2−2
@@ -13,8 +13,6 @@ · 12 unmodified lines
13 background: $headerBackground;13 background: $headerBackground;
14 border-bottom: 1px solid $headerBorder;14 border-bottom: 1px solid $headerBorder;
15 @extend %user-select-none;15 @extend %user-select-none;
16
17 a:focus { outline: 0; }
18}16}
19 17
20//18//
@@ -58,6 +56,8 @@ · 37 unmodified lines
58}56}
59 57
60._nav-current {58._nav-current {
59 outline: 0;
60
61 &:before, &:after { content: ''; }61 &:before, &:after { content: ''; }
62}62}
63 63
assets/stylesheets/components/_sidebar.scss+4−2
@@ -32,8 +32,6 @@ · 31 unmodified lines
32 }32 }
33 }33 }
34 34
35 a:focus { outline: 0; }
36
37 ._sidebar-hidden & {35 ._sidebar-hidden & {
38 display: none;36 display: none;
39 }37 }
@@ -88,6 +86,8 @@ · 48 unmodified lines
88 min-height: 100%;86 min-height: 100%;
89 padding-bottom: 3.5rem;87 padding-bottom: 3.5rem;
90 }88 }
89
90 a:focus { outline: 0; }
91}91}
92 92
93._list-title {93._list-title {
@@ -422,6 +422,8 @@ · 328 unmodified lines
422}422}
423 423
424._sidebar-footer-edit {424._sidebar-footer-edit {
425 display: inline-block;
426
425 @if $style == 'dark' {427 @if $style == 'dark' {
426 &:before { @extend %icon-settings-white; }428 &:before { @extend %icon-settings-white; }
427 } @else {429 } @else {
assets/stylesheets/global/_base.scss+7−0
@@ -159,6 +159,8 @@ td { · 158 unmodified lines
159 > pre:last-child, > p:last-child, > ul:last-child, > ol:last-child { margin-bottom: 0; }159 > pre:last-child, > p:last-child, > ul:last-child, > ol:last-child { margin-bottom: 0; }
160}160}
161 161
162section, main { outline: 0; }
163
162input, button {164input, button {
163 margin: 0;165 margin: 0;
164 font-family: inherit;166 font-family: inherit;
@@ -179,6 +181,11 @@ button, input[type="search"] { · 14 unmodified lines
179 -moz-appearance: none;181 -moz-appearance: none;
180}182}
181 183
184button:focus {
185 outline: 1px dotted;
186 outline: -webkit-focus-ring-color auto 5px;
187}
188
182input[type="search"]::-webkit-search-cancel-button,189input[type="search"]::-webkit-search-cancel-button,
183input[type="search"]::-webkit-search-decoration {190input[type="search"]::-webkit-search-decoration {
184 -webkit-appearance: none;191 -webkit-appearance: none;
assets/stylesheets/global/_classes.scss+1−1
@@ -22,7 +22,7 @@ · 21 unmodified lines
22%hide-text {22%hide-text {
23 white-space: nowrap;23 white-space: nowrap;
24 overflow: hidden;24 overflow: hidden;
25 color: transparent !important;25 text-indent: -1000px;
26 @extend %user-select-none;26 @extend %user-select-none;
27}27}
28 28
views/app.erb+2−2
@@ -19,7 +19,7 @@ · 18 unmodified lines
19 <a href="/help" class="_nav-link">Tips</a>19 <a href="/help" class="_nav-link">Tips</a>
20 </nav>20 </nav>
21 </header>21 </header>
22 <section class="_sidebar">22 <section class="_sidebar" tabindex="-1">
23 <div class="_list" role="navigation">23 <div class="_list" role="navigation">
24 <% unless @doc %>24 <% unless @doc %>
25 <% App.docs.each do |slug, doc| %>25 <% App.docs.each do |slug, doc| %>
@@ -29,7 +29,7 @@ · 3 unmodified lines
29 </div>29 </div>
30 </section>30 </section>
31 <div class="_container" role="document">31 <div class="_container" role="document">
32 <main class="_content _content-loading" role="main"></main>32 <main class="_content _content-loading" role="main" tabindex="1"></main>
33 </div>33 </div>
34</div>34</div>
35<style data-size="<%= app_size %>" data-resizer>35<style data-size="<%= app_size %>" data-resizer>