Closes #396.
Unified · Split
| @@ -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> |
| @@ -3,7 +3,7 @@ app.templates.splash = """<div class="_splash-title">DevDocs</div>""" · 2 unmodified lines | |||
| 3 | <% if App.development? %> | 3 | <% if App.development? %> |
| 4 | app.templates.intro = """ | 4 | app.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 %> |
| 28 | app.templates.intro = """ | 28 | app.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: |
| @@ -3,7 +3,7 @@ templates = app.templates · 2 unmodified lines | |||
| 3 | templates.sidebarDoc = (doc, options = {}) -> | 3 | templates.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.disabled | 7 | 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 | else | 9 | else |
| @@ -14,10 +14,10 @@ templates.sidebarDoc = (doc, options = {}) -> · 4 unmodified lines | |||
| 14 | link + "</span></a>" | 14 | link + "</span></a>" |
| 15 | 15 | ||
| 16 | templates.sidebarType = (type) -> | 16 | templates.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 | ||
| 19 | templates.sidebarEntry = (entry) -> | 19 | templates.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 | ||
| 22 | templates.sidebarResult = (entry) -> | 22 | templates.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 | else | 25 | 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 | ||
| 30 | templates.sidebarNoResults = -> | 30 | templates.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 | html | 35 | html |
| 36 | 36 | ||
| 37 | templates.sidebarPageLink = (count) -> | 37 | templates.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 | ||
| 40 | templates.sidebarLabel = (doc, options = {}) -> | 40 | templates.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 | |||
| 47 | templates.sidebarVersionedDoc = (doc, versions, options = {}) -> | 47 | templates.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.open | 49 | 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 | ||
| 52 | templates.sidebarDisabled = (options) -> | 52 | templates.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 | ||
| 58 | templates.sidebarDisabledVersionedDoc = (doc, versions) -> | 58 | templates.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 | ||
| 61 | templates.sidebarPickerNote = """ | 61 | templates.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 | ||
| 68 | templates.sidebarSettings = -> | 68 | templates.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 | ||
| 75 | templates.sidebarSave = -> | 75 | templates.sidebarSave = -> |
| @@ -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.activeClass | 11 | @current.classList.add @constructor.activeClass |
| 12 | @current.setAttribute 'tabindex', '-1' | ||
| 12 | return | 13 | return |
| 13 | 14 | ||
| 14 | deselect: -> | 15 | deselect: -> |
| 15 | if @current | 16 | if @current |
| 16 | @current.classList.remove @constructor.activeClass | 17 | @current.classList.remove @constructor.activeClass |
| 18 | @current.removeAttribute 'tabindex' | ||
| 17 | @current = null | 19 | @current = null |
| 18 | return | 20 | return |
| 19 | 21 | ||
| @@ -84,10 +84,11 @@ class app.views.SearchScope extends app.View · 83 unmodified lines | |||
| 84 | if @doc and not @input.value | 84 | if @doc and not @input.value |
| 85 | $.stopEvent(event) | 85 | $.stopEvent(event) |
| 86 | @reset() | 86 | @reset() |
| 87 | else if event.which is 9 or # tab | 87 | else if not @doc and @input.value |
| 88 | event.which is 32 and (app.isMobile() or $.isTouchScreen()) # space | 88 | 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 | return | 92 | return |
| 92 | 93 | ||
| 93 | extractHashValue: -> | 94 | extractHashValue: -> |
| @@ -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', @onAppCacheProgress | 23 | app.appCache?.on 'progress', @onAppCacheProgress |
| 24 | $.on @el, 'focus', @onFocus, true | 24 | $.on @el, 'focus', @onDOMFocus, true |
| 25 | return | 25 | return |
| 26 | 26 | ||
| 27 | deactivate: -> | 27 | deactivate: -> |
| 28 | if super | 28 | if super |
| 29 | @empty() | 29 | @empty() |
| 30 | app.appCache?.off 'progress', @onAppCacheProgress | 30 | app.appCache?.off 'progress', @onAppCacheProgress |
| 31 | $.off @el, 'focus', @onFocus, true | 31 | $.off @el, 'focus', @onDOMFocus, true |
| 32 | return | 32 | return |
| 33 | 33 | ||
| 34 | render: -> | 34 | render: -> |
| @@ -84,14 +84,27 @@ class app.views.DocPicker extends app.View · 49 unmodified lines | |||
| 84 | input.name | 84 | 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 1 | 90 | return if event.which isnt 1 |
| 88 | if event.target is @saveLink | 91 | if event.target is @saveLink |
| 89 | $.stopEvent(event) | 92 | $.stopEvent(event) |
| 90 | @save() | 93 | @save() |
| 91 | return | 94 | return |
| 92 | 95 | ||
| 93 | onFocus: (event) -> | 96 | onDOMFocus: (event) => |
| 94 | $.scrollTo event.target.parentNode, null, 'continuous', bottomGap: 2 | 97 | 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() |
| @@ -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 | return | 110 | return |
| 109 | 111 | ||
| @@ -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 | ||
| @@ -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 { |
| @@ -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 | ||
| 162 | section, main { outline: 0; } | ||
| 163 | |||
| 162 | input, button { | 164 | input, 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 | ||
| 184 | button:focus { | ||
| 185 | outline: 1px dotted; | ||
| 186 | outline: -webkit-focus-ring-color auto 5px; | ||
| 187 | } | ||
| 188 | |||
| 182 | input[type="search"]::-webkit-search-cancel-button, | 189 | input[type="search"]::-webkit-search-cancel-button, |
| 183 | input[type="search"]::-webkit-search-decoration { | 190 | input[type="search"]::-webkit-search-decoration { |
| 184 | -webkit-appearance: none; | 191 | -webkit-appearance: none; |
| @@ -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 | ||
| @@ -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> |