History

Add settings page

Thibaut Courouble committed 9 years ago

Commit e94af971 parent ce8079f59 files · +477 −375Browse files

Unified · Split

59 files
  1. assets/images/icons.png +0 −0
  2. assets/images/icons@2x.png +0 −0
  3. assets/javascripts/app/appcache.coffee +6 −1
  4. assets/javascripts/app/router.coffee +15 −10
  5. assets/javascripts/app/settings.coffee +3 −0
  6. assets/javascripts/news.json +3 −3
  7. assets/javascripts/templates/notice_tmpl.coffee +1 −1
  8. assets/javascripts/templates/notif_tmpl.coffee +1 −1
  9. assets/javascripts/templates/pages/offline_tmpl.coffee +1 −1
  10. assets/javascripts/templates/pages/root_tmpl.coffee.erb +12 −12
  11. assets/javascripts/templates/pages/settings_tmpl.coffee +27 −0
  12. assets/javascripts/templates/sidebar_tmpl.coffee +2 −14
  13. assets/javascripts/views/content/content.coffee +8 −5
  14. assets/javascripts/views/content/settings_page.coffee +52 −0
  15. assets/javascripts/views/layout/document.coffee +11 −26
  16. assets/javascripts/views/layout/mobile.coffee +42 −10
  17. assets/javascripts/views/layout/resizer.coffee +0 −7
  18. assets/javascripts/views/layout/settings.coffee +80 −0
  19. assets/javascripts/views/search/search.coffee +0 −8
  20. assets/javascripts/views/sidebar/doc_picker.coffee +2 −42
  21. assets/javascripts/views/sidebar/sidebar.coffee +2 −26
  22. assets/stylesheets/application-dark.css.scss +1 −0
  23. assets/stylesheets/application.css.scss +1 −0
  24. assets/stylesheets/components/_content.scss +1 −4
  25. assets/stylesheets/components/_header.scss +3 −5
  26. assets/stylesheets/components/_mobile.scss +20 −19
  27. assets/stylesheets/components/_notice.scss +2 −1
  28. assets/stylesheets/components/_path.scss +1 −1
  29. assets/stylesheets/components/_settings.scss +146 −0
  30. assets/stylesheets/components/_sidebar.scss +5 −162
  31. assets/stylesheets/global/_base.scss +11 −0
  32. assets/stylesheets/global/_icons.scss +8 −18
  33. assets/stylesheets/global/_variables-dark.scss +1 −1
  34. assets/stylesheets/global/_variables.scss +1 −1
  35. lib/app.rb +1 −1
  36. public/icons/ui/check-white/SOURCE +0 −1
  37. public/icons/ui/check-white/check-white.png +0 −0
  38. public/icons/ui/check-white/check-white@2x.png +0 −0
  39. public/icons/ui/check/SOURCE +0 −1
  40. public/icons/ui/check/check.png +0 −0
  41. public/icons/ui/check/check@2x.png +0 −0
  42. public/icons/ui/contract/16.png +0 −0
  43. public/icons/ui/contract/16@2x.png +0 −0
  44. public/icons/ui/expand/16.png +0 −0
  45. public/icons/ui/expand/16@2x.png +0 −0
  46. public/icons/ui/light-white/SOURCE +0 −1
  47. public/icons/ui/light-white/light.png +0 −0
  48. public/icons/ui/light-white/light@2x.png +0 −0
  49. public/icons/ui/light/SOURCE +0 −1
  50. public/icons/ui/light/light.png +0 −0
  51. public/icons/ui/light/light@2x.png +0 −0
  52. public/icons/ui/settings-white/SOURCE +0 −1
  53. public/icons/ui/settings-white/settings-white.png +0 −0
  54. public/icons/ui/settings-white/settings-white@2x.png +0 −0
  55. public/icons/ui/settings/SOURCE +0 −1
  56. public/icons/ui/settings/settings.png +0 −0
  57. public/icons/ui/settings/settings@2x.png +0 −0
  58. public/robots.txt +1 −1
  59. views/app.erb +24 −6
assets/images/icons.png+0−0

Binary file not shown.

assets/images/icons@2x.png+0−0

Binary file not shown.

assets/javascripts/app/appcache.coffee+6−1
@@ -16,21 +16,26 @@ class app.AppCache · 15 unmodified lines
16 16
17 update: ->17 update: ->
18 @notifyUpdate = true18 @notifyUpdate = true
19 @notifyProgress = true
19 try @cache.update() catch20 try @cache.update() catch
20 return21 return
21 22
22 updateInBackground: ->23 updateInBackground: ->
23 @notifyUpdate = false24 @notifyUpdate = false
25 @notifyProgress = false
24 try @cache.update() catch26 try @cache.update() catch
25 return27 return
26 28
27 reload: ->29 reload: ->
28 $.on @cache, 'updateready noupdate error', -> window.location = '/'30 $.on @cache, 'updateready noupdate error', -> window.location = '/'
29 @updateInBackground()31 @updateInBackground()
32 @notifyUpdate = false
33 @notifyProgress = true
34 @cache.update()
30 return35 return
31 36
32 onProgress: (event) =>37 onProgress: (event) =>
33 @trigger 'progress', event38 @trigger 'progress', event if @notifyProgress
34 return39 return
35 40
36 onUpdateReady: =>41 onUpdateReady: =>
assets/javascripts/app/router.coffee+15−10
@@ -2,16 +2,17 @@ class app.Router · 1 unmodified line
2 $.extend @prototype, Events2 $.extend @prototype, Events
3 3
4 @routes: [4 @routes: [
5 ['*', 'before' ]5 ['*', 'before' ]
6 ['/', 'root' ]6 ['/', 'root' ]
7 ['/offline', 'offline' ]7 ['/settings', 'settings' ]
8 ['/about', 'about' ]8 ['/offline', 'offline' ]
9 ['/news', 'news' ]9 ['/about', 'about' ]
10 ['/help', 'help' ]10 ['/news', 'news' ]
11 ['/:doc-:type/', 'type' ]11 ['/help', 'help' ]
12 ['/:doc/', 'doc' ]12 ['/:doc-:type/', 'type' ]
13 ['/:doc/:path(*)', 'entry' ]13 ['/:doc/', 'doc' ]
14 ['*', 'notFound']14 ['/:doc/:path(*)', 'entry' ]
15 ['*', 'notFound' ]
15 ]16 ]
16 17
17 constructor: ->18 constructor: ->
@@ -76,6 +77,10 @@ class app.Router · 58 unmodified lines
76 @triggerRoute 'root'77 @triggerRoute 'root'
77 return78 return
78 79
80 settings: ->
81 @triggerRoute 'settings'
82 return
83
79 offline: ->84 offline: ->
80 @triggerRoute 'offline'85 @triggerRoute 'offline'
81 return86 return
assets/javascripts/app/settings.coffee+3−0
@@ -57,6 +57,9 @@ class app.Settings · 56 unmodified lines
57 @store.set DARK_KEY, !!value57 @store.set DARK_KEY, !!value
58 return58 return
59 59
60 getDark: ->
61 @store.get DARK_KEY
62
60 setLayout: (name, enable) ->63 setLayout: (name, enable) ->
61 layout = (@store.get(LAYOUT_KEY) || '').split(' ')64 layout = (@store.get(LAYOUT_KEY) || '').split(' ')
62 $.arrayDelete(layout, '')65 $.arrayDelete(layout, '')
assets/javascripts/news.json+3−3
@@ -64,7 +64,7 @@ · 63 unmodified lines
64 "New documentations: <a href=\"/erlang/\">Erlang</a> and <a href=\"/tcl_tk/\">Tcl/Tk</a>"64 "New documentations: <a href=\"/erlang/\">Erlang</a> and <a href=\"/tcl_tk/\">Tcl/Tk</a>"
65 ], [65 ], [
66 "2016-01-24",66 "2016-01-24",
67 "&ldquo;Multi-version support&rdquo; has landed!\nClick <a href=\"#\" data-pick-docs>Select documentation</a> to pick which versions to use. More versions will be added in the coming weeks.\nIf you notice any bugs, please report them on <a href=\"https://github.com/Thibaut/devdocs/issues\" target=\"_blank\" rel=\"noopener\">GitHub</a>."67 "&ldquo;Multi-version support&rdquo; has landed!"
68 ], [68 ], [
69 "2015-11-22",69 "2015-11-22",
70 "New documentations: <a href=\"/phoenix/\">Phoenix</a>, <a href=\"/dojo/\">Dojo</a>, <a href=\"/relay/\">Relay</a> and <a href=\"/flow/\">Flow</a>"70 "New documentations: <a href=\"/phoenix/\">Phoenix</a>, <a href=\"/dojo/\">Dojo</a>, <a href=\"/relay/\">Relay</a> and <a href=\"/flow/\">Flow</a>"
@@ -117,10 +117,10 @@ · 46 unmodified lines
117 "New <a href=\"/iojs/\">io.js</a>, <a href=\"/symfony/\">Symfony</a>, <a href=\"/clojure/\">Clojure</a>, <a href=\"/lua/\">Lua</a> and <a href=\"/yii1/\">Yii 1.1</a> documentations"117 "New <a href=\"/iojs/\">io.js</a>, <a href=\"/symfony/\">Symfony</a>, <a href=\"/clojure/\">Clojure</a>, <a href=\"/lua/\">Lua</a> and <a href=\"/yii1/\">Yii 1.1</a> documentations"
118 ], [118 ], [
119 "2015-02-08",119 "2015-02-08",
120 "New dark theme\nClick the icon in the bottom left corner to activate.\n<a href=\"https://github.com/Thibaut/devdocs/issues\" target=\"_blank\" rel=\"noopener\">Feedback</a> welcome :)"120 "New dark theme"
121 ], [121 ], [
122 "2015-01-13",122 "2015-01-13",
123 "<a href=\"/offline\">Offline mode</a> has landed!\nIf you notice any bugs, please report them on <a href=\"https://github.com/Thibaut/devdocs/issues\" target=\"_blank\" rel=\"noopener\">GitHub</a>."123 "<a href=\"/offline\">Offline mode</a> has landed!"
124 ], [124 ], [
125 "2014-12-21",125 "2014-12-21",
126 "New <a href=\"/react/\">React</a>, <a href=\"/rethinkdb/\">RethinkDB</a>, <a href=\"/socketio/\">Socket.IO</a>, <a href=\"/modernizr/\">Modernizr</a> and <a href=\"/bower/\">Bower</a> documentations"126 "New <a href=\"/react/\">React</a>, <a href=\"/rethinkdb/\">RethinkDB</a>, <a href=\"/socketio/\">Socket.IO</a>, <a href=\"/modernizr/\">Modernizr</a> and <a href=\"/bower/\">Bower</a> documentations"
assets/javascripts/templates/notice_tmpl.coffee+1−1
@@ -6,4 +6,4 @@ app.templates.singleDocNotice = (doc) -> · 5 unmodified lines
6 6
7app.templates.disabledDocNotice = ->7app.templates.disabledDocNotice = ->
8 notice """ <strong>This documentation is disabled.</strong>8 notice """ <strong>This documentation is disabled.</strong>
9 To enable it, click <a href="#" class="_notice-link" data-pick-docs>Select documentation</a>. """9 To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. """
assets/javascripts/templates/notif_tmpl.coffee+1−1
@@ -47,7 +47,7 @@ app.templates.notifUpdates = (docs, disabledDocs) -> · 46 unmodified lines
47 for doc in disabledDocs47 for doc in disabledDocs
48 html += "<li>#{doc.name}"48 html += "<li>#{doc.name}"
49 html += " <code>&rarr;</code> #{doc.release}" if doc.release49 html += " <code>&rarr;</code> #{doc.release}" if doc.release
50 html += """<span class="_notif-info"><a data-pick-docs>Enable</a></span>"""50 html += """<span class="_notif-info"><a href="/settings">Enable</a></span>"""
51 html += '</ul></div>'51 html += '</ul></div>'
52 52
53 notif 'Updates', "#{html}</div>"53 notif 'Updates', "#{html}</div>"
assets/javascripts/templates/pages/offline_tmpl.coffee+1−1
@@ -35,7 +35,7 @@ app.templates.offlinePage = (docs) -> """ · 34 unmodified lines
35 <dt>How do I uninstall/reset the app?35 <dt>How do I uninstall/reset the app?
36 <dd>Click <a href="#" data-behavior="reset">here</a>.36 <dd>Click <a href="#" data-behavior="reset">here</a>.
37 <dt>Why aren't all documentations listed above?37 <dt>Why aren't all documentations listed above?
38 <dd>You have to <a href="#" data-pick-docs>enable</a> them first.38 <dd>You have to <a href="/settings">enable</a> them first.
39 </dl>39 </dl>
40"""40"""
41 41
assets/javascripts/templates/pages/root_tmpl.coffee.erb+12−12
@@ -32,14 +32,14 @@ app.templates.intro = """ · 31 unmodified lines
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:
34 <ol class="_intro-list">34 <ol class="_intro-list">
35 <li>To enable more docs, click <a class="_intro-link" data-pick-docs>Select documentation</a> in the bottom left corner35 <li>Open the <a href="/settings">Preferences</a> to enable more docs and customize the UI.
36 <li>You don't have to use your mouse &mdash; see the list of <a href="/help#shortcuts">keyboard shortcuts</a>36 <li>You don't have to use your mouse &mdash; see the list of <a href="/help#shortcuts">keyboard shortcuts</a>.
37 <li>The search supports fuzzy matching (e.g. "bgcp" brings up "background-clip")37 <li>The search supports fuzzy matching (e.g. "bgcp" brings up "background-clip").
38 <li>To search a specific documentation, type its name (or an abbreviation), then Tab38 <li>To search a specific documentation, type its name (or an abbr.), then Tab.
39 <li>You can search using your browser's address bar &mdash; <a href="/help#browser_search">learn how</a>39 <li>You can search using your browser's address bar &mdash; <a href="/help#browser_search">learn how</a>.
40 <li>DevDocs works <a href="/offline">offline</a>, on mobile, and can be installed on <a href="https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe">Chrome</a>.40 <li>DevDocs works <a href="/offline">offline</a>, on mobile, and can be installed on <a href="https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe">Chrome</a>.
41 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>41 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.
42 <li>DevDocs is free and <a href="https://github.com/Thibaut/devdocs">open source</a>42 <li>DevDocs is free and <a href="https://github.com/Thibaut/devdocs">open source</a>.
43 <iframe class="_github-btn" src="//ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>43 <iframe class="_github-btn" src="//ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
44 <li>If you like the app, please consider supporting the project on <a href="https://gratipay.com/devdocs/">Gratipay</a>. Thanks!44 <li>If you like the app, please consider supporting the project on <a href="https://gratipay.com/devdocs/">Gratipay</a>. Thanks!
45 </ol>45 </ol>
@@ -54,11 +54,11 @@ app.templates.mobileIntro = """ · 8 unmodified lines
54 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.54 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.
55 Here's what you should know before you start:55 Here's what you should know before you start:
56 <ol class="_intro-list">56 <ol class="_intro-list">
57 <li>To pick your docs, click <a data-pick-docs>Select documentation</a> at the bottom of the menu57 <li>Pick your docs in the <a href="/settings">Preferences</a>.
58 <li>The search supports fuzzy matching (e.g. "bgcp" matches "background-clip")58 <li>The search supports fuzzy matching.
59 <li>To search a specific documentation, type its name (or an abbreviation), then Space59 <li>To search a specific documentation, type its name (or an abbr.), then Space.
60 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>60 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.
61 <li>DevDocs is <a href="https://github.com/Thibaut/devdocs">open source</a>61 <li>DevDocs is <a href="https://github.com/Thibaut/devdocs">open source</a>.
62 </ol>62 </ol>
63 <p>Happy coding!63 <p>Happy coding!
64 <a class="_intro-hide" data-hide-intro>Stop showing this message</a>64 <a class="_intro-hide" data-hide-intro>Stop showing this message</a>
assets/javascripts/templates/pages/settings_tmpl.coffee+27−0
@@ -0,0 +1,27 @@
1app.templates.settingsPage = (settings) -> """
2 <h1 class="_lined-heading">Preferences</h1>
3
4 <div class="_settings-fieldset">
5 <h2 class="_settings-legend">General:</h2>
6
7 <div class="_settings-inputs">
8 <label class="_settings-label">
9 <input type="checkbox" name="dark" value="1"#{if settings.dark then ' checked' else ''}>Enable dark theme
10 </label>
11 <label class="_settings-label _settings-max-width">
12 <input type="checkbox" name="layout" value="_max-width"#{if settings['_max-width'] then ' checked' else ''}>Enable fixed-width layout
13 </label>
14 <label class="_settings-label">
15 <input type="checkbox" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar
16 </label>
17 </div>
18 </div>
19
20 <div class="_settings-fieldset">
21 <h2 class="_settings-legend">Advanced:</h2>
22
23 <div class="_settings-inputs">
24 <a href="#" class="_settings-link" data-behavior="reset">Reset all settings and data</a>
25 </div>
26 </div>
27"""
assets/javascripts/templates/sidebar_tmpl.coffee+2−14
@@ -30,7 +30,7 @@ templates.sidebarResult = (entry) -> · 29 unmodified lines
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 href="#" 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="/settings" class="_list-note-link">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
@@ -58,19 +58,7 @@ templates.sidebarDisabledList = (html) -> · 21 unmodified lines
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}" tabindex="-1"><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.docPickerNote = """
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>
63 <a href="https://trello.com/b/6BmTulfx/devdocs-documentation" class="_list-link" target="_blank" rel="noopener">Vote for new documentation</a>63 <a href="https://trello.com/b/6BmTulfx/devdocs-documentation" class="_list-link" target="_blank" rel="noopener">Vote for new documentation</a>
64 """64 """
65
66sidebarFooter = (html) -> """<div class="_sidebar-footer">#{html}</div>"""
67
68templates.sidebarSettings = ->
69 sidebarFooter """
70 <button type="button" class="_sidebar-footer-link _sidebar-footer-edit" data-pick-docs>Select documentation</button>
71 <button type="button" class="_sidebar-footer-link _sidebar-footer-light" title="Toggle light" data-light>Toggle light</button>
72 <button type="button" class="_sidebar-footer-link _sidebar-footer-layout" title="Toggle layout" data-layout>Toggle layout</button>
73 """
74
75templates.sidebarSave = ->
76 sidebarFooter """<a class="_sidebar-footer-link _sidebar-footer-save" role="button">Save</a>"""
assets/javascripts/views/content/content.coffee+8−5
@@ -23,11 +23,12 @@ class app.views.Content extends app.View · 22 unmodified lines
23 @scrollMap = {}23 @scrollMap = {}
24 @scrollStack = []24 @scrollStack = []
25 25
26 @rootPage = new app.views.RootPage26 @rootPage = new app.views.RootPage
27 @staticPage = new app.views.StaticPage27 @staticPage = new app.views.StaticPage
28 @offlinePage = new app.views.OfflinePage28 @settingsPage = new app.views.SettingsPage
29 @typePage = new app.views.TypePage29 @offlinePage = new app.views.OfflinePage
30 @entryPage = new app.views.EntryPage30 @typePage = new app.views.TypePage
31 @entryPage = new app.views.EntryPage
31 32
32 @entryPage33 @entryPage
33 .on 'loading', @onEntryLoading34 .on 'loading', @onEntryLoading
@@ -148,6 +149,8 @@ class app.views.Content extends app.View · 114 unmodified lines
148 @show @entryPage149 @show @entryPage
149 when 'type'150 when 'type'
150 @show @typePage151 @show @typePage
152 when 'settings'
153 @show @settingsPage
151 when 'offline'154 when 'offline'
152 @show @offlinePage155 @show @offlinePage
153 else156 else
assets/javascripts/views/content/settings_page.coffee+52−0
@@ -0,0 +1,52 @@
1class app.views.SettingsPage extends app.View
2 LAYOUTS = ['_max-width', '_sidebar-hidden']
3 SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
4
5 @className: '_static'
6
7 @events:
8 change: 'onChange'
9
10 render: ->
11 @html @tmpl('settingsPage', @currentSettings())
12 return
13
14 currentSettings: ->
15 settings = {}
16 settings.dark = app.settings.getDark()
17 settings[layout] = app.settings.hasLayout(layout) for layout in LAYOUTS
18 settings
19
20 getTitle: ->
21 'Preferences'
22
23 toggleDark: (enable) ->
24 css = $('link[rel="stylesheet"][data-alt]')
25 alt = css.getAttribute('data-alt')
26 css.setAttribute('data-alt', css.getAttribute('href'))
27 css.setAttribute('href', alt)
28 app.settings.setDark(enable)
29 app.appCache?.updateInBackground()
30 return
31
32 toggleLayout: (layout, enable) ->
33 app.el.classList[if enable then 'add' else 'remove'](layout) unless layout is SIDEBAR_HIDDEN_LAYOUT
34 app.settings.setLayout(layout, enable)
35 app.appCache?.updateInBackground()
36 return
37
38 onChange: (event) =>
39 input = event.target
40 switch input.name
41 when 'dark'
42 @toggleDark input.checked
43 when 'layout'
44 @toggleLayout input.value, input.checked
45 return
46
47 onRoute: (route) =>
48 if app.isSingleDoc()
49 window.location = "/#/#{route.path}"
50 else
51 @render()
52 return
assets/javascripts/views/layout/document.coffee+11−26
@@ -1,7 +1,4 @@
1class app.views.Document extends app.View1class app.views.Document extends app.View
2 MAX_WIDTH_LAYOUT = '_max-width'
3 SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
4
5 @el: document2 @el: document
6 3
7 @events:4 @events:
@@ -13,44 +10,32 @@ class app.views.Document extends app.View · 5 unmodified lines
13 superLeft: 'onBack'10 superLeft: 'onBack'
14 superRight: 'onForward'11 superRight: 'onForward'
15 12
13 @routes:
14 after: 'afterRoute'
15
16 init: ->16 init: ->
17 @addSubview @menu = new app.views.Menu,17 @addSubview @menu = new app.views.Menu,
18 @addSubview @sidebar = new app.views.Sidebar18 @addSubview @sidebar = new app.views.Sidebar
19 @addSubview @resizer = new app.views.Resizer if app.views.Resizer.isSupported()19 @addSubview @resizer = new app.views.Resizer if app.views.Resizer.isSupported()
20 @addSubview @content = new app.views.Content20 @addSubview @content = new app.views.Content
21 @addSubview @path = new app.views.Path unless app.isSingleDoc() or app.isMobile()21 @addSubview @path = new app.views.Path unless app.isSingleDoc() or app.isMobile()
22 @settings = new app.views.Settings unless app.isSingleDoc()
22 23
23 $.on document.body, 'click', @onClick24 $.on document.body, 'click', @onClick
24 25
25 @activate()26 @activate()
26 return27 return
27 28
28 toggleLight: ->
29 css = $('link[rel="stylesheet"][data-alt]')
30 alt = css.getAttribute('data-alt')
31 css.setAttribute('data-alt', css.getAttribute('href'))
32 css.setAttribute('href', alt)
33 app.settings.setDark(alt.indexOf('dark') > 0)
34 app.appCache?.updateInBackground()
35 return
36
37 toggleLayout: ->
38 wantsMaxWidth = !app.el.classList.contains(MAX_WIDTH_LAYOUT)
39 app.el.classList[if wantsMaxWidth then 'add' else 'remove'](MAX_WIDTH_LAYOUT)
40 app.settings.setLayout(MAX_WIDTH_LAYOUT, wantsMaxWidth)
41 app.appCache?.updateInBackground()
42 return
43
44 toggleSidebarLayout: ->
45 shouldHide = !app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
46 app.el.classList[if shouldHide then 'add' else 'remove'](SIDEBAR_HIDDEN_LAYOUT)
47 app.settings.setLayout(SIDEBAR_HIDDEN_LAYOUT, shouldHide)
48 app.appCache?.updateInBackground()
49 return
50
51 setTitle: (title) ->29 setTitle: (title) ->
52 @el.title = if title then "DevDocs — #{title}" else 'DevDocs API Documentation'30 @el.title = if title then "DevDocs — #{title}" else 'DevDocs API Documentation'
53 31
32 afterRoute: (route) =>
33 if route is 'settings'
34 @settings?.activate()
35 else
36 @settings?.deactivate()
37 return
38
54 onVisibilityChange: =>39 onVisibilityChange: =>
55 return unless @el.visibilityState is 'visible'40 return unless @el.visibilityState is 'visible'
56 @delay ->41 @delay ->
assets/javascripts/views/layout/mobile.coffee+42−10
@@ -2,9 +2,10 @@ class app.views.Mobile extends app.View · 1 unmodified line
2 @className: '_mobile'2 @className: '_mobile'
3 3
4 @elements:4 @elements:
5 body: 'body'5 body: 'body'
6 content: '._container'6 content: '._container'
7 sidebar: '._sidebar'7 sidebar: '._sidebar'
8 docPicker: '._settings ._sidebar'
8 9
9 @routes:10 @routes:
10 after: 'afterRoute'11 after: 'afterRoute'
@@ -34,7 +35,6 @@ class app.views.Mobile extends app.View · 23 unmodified lines
34 init: ->35 init: ->
35 FastClick.attach @body36 FastClick.attach @body
36 37
37 $.on @body, 'click', @onClick
38 $.on $('._search'), 'touchend', @onTapSearch38 $.on $('._search'), 'touchend', @onTapSearch
39 39
40 @toggleSidebar = $('button[data-toggle-sidebar]')40 @toggleSidebar = $('button[data-toggle-sidebar]')
@@ -49,6 +49,14 @@ class app.views.Mobile extends app.View · 8 unmodified lines
49 @forward.removeAttribute('hidden')49 @forward.removeAttribute('hidden')
50 $.on @forward, 'click', @onClickForward50 $.on @forward, 'click', @onClickForward
51 51
52 @docPickerTab = $('a[data-tab="doc-picker"]')
53 @docPickerTab.removeAttribute('hidden')
54 $.on @docPickerTab, 'click', @onClickDocPickerTab
55
56 @settingsTab = $('a[data-tab="settings"]')
57 @settingsTab.removeAttribute('hidden')
58 $.on @settingsTab, 'click', @onClickSettingsTab
59
52 app.document.sidebar.search60 app.document.sidebar.search
53 .on 'searching', @showSidebar61 .on 'searching', @showSidebar
54 62
@@ -82,11 +90,6 @@ class app.views.Mobile extends app.View · 27 unmodified lines
82 isSidebarShown: ->90 isSidebarShown: ->
83 @sidebar.style.display isnt 'none'91 @sidebar.style.display isnt 'none'
84 92
85 onClick: (event) =>
86 if event.target.hasAttribute 'data-pick-docs'
87 @showSidebar()
88 return
89
90 onClickBack: =>93 onClickBack: =>
91 history.back()94 history.back()
92 95
@@ -97,12 +100,41 @@ class app.views.Mobile extends app.View · 4 unmodified lines
97 if @isSidebarShown() then @hideSidebar() else @showSidebar()100 if @isSidebarShown() then @hideSidebar() else @showSidebar()
98 return101 return
99 102
103 onClickDocPickerTab: (event) =>
104 $.stopEvent(event)
105 @showDocPicker()
106 return
107
108 onClickSettingsTab: (event) =>
109 $.stopEvent(event)
110 @showSettings()
111 return
112
113 showDocPicker: ->
114 @docPickerTab.classList.add 'active'
115 @settingsTab.classList.remove 'active'
116 @docPicker.style.display = 'block'
117 @content.style.display = 'none'
118 return
119
120 showSettings: ->
121 @docPickerTab.classList.remove 'active'
122 @settingsTab.classList.add 'active'
123 @docPicker.style.display = 'none'
124 @content.style.display = 'block'
125 return
126
100 onTapSearch: =>127 onTapSearch: =>
101 window.scrollTo 0, 0128 window.scrollTo 0, 0
102 129
103 afterRoute: =>130 afterRoute: (route) =>
104 @hideSidebar()131 @hideSidebar()
105 132
133 if route is 'settings'
134 @showDocPicker()
135 else
136 @content.style.display = 'block'
137
106 if page.canGoBack()138 if page.canGoBack()
107 @back.removeAttribute('disabled')139 @back.removeAttribute('disabled')
108 else140 else
assets/javascripts/views/layout/resizer.coffee+0−7
@@ -1,12 +1,9 @@
1class app.views.Resizer extends app.View1class app.views.Resizer extends app.View
2 @className: '_resizer'2 @className: '_resizer'
3 @attributes:
4 title: 'Click to toggle sidebar on/off'
5 3
6 @events:4 @events:
7 dragstart: 'onDragStart'5 dragstart: 'onDragStart'
8 dragend: 'onDragEnd'6 dragend: 'onDragEnd'
9 click: 'onClick'
10 7
11 @isSupported: ->8 @isSupported: ->
12 'ondragstart' of document.createElement('div') and !app.isMobile()9 'ondragstart' of document.createElement('div') and !app.isMobile()
@@ -34,10 +31,6 @@ class app.views.Resizer extends app.View · 21 unmodified lines
34 app.appCache?.updateInBackground()31 app.appCache?.updateInBackground()
35 return32 return
36 33
37 onClick: ->
38 app.document.toggleSidebarLayout()
39 return
40
41 onDragStart: (event) =>34 onDragStart: (event) =>
42 @style.removeAttribute('disabled')35 @style.removeAttribute('disabled')
43 event.dataTransfer.effectAllowed = 'link'36 event.dataTransfer.effectAllowed = 'link'
assets/javascripts/views/layout/settings.coffee+80−0
@@ -0,0 +1,80 @@
1class app.views.Settings extends app.View
2 SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
3
4 @el: '._settings'
5
6 @elements:
7 sidebar: '._sidebar'
8 saveBtn: 'button[type="submit"]'
9 backBtn: 'button[data-back]'
10
11 @events:
12 submit: 'onSubmit'
13 click: 'onClick'
14 focus: 'onFocus'
15
16 @shortcuts:
17 enter: 'onEnter'
18
19 init: ->
20 @addSubview @docPicker = new app.views.DocPicker
21 return
22
23 activate: ->
24 if super
25 @render()
26 app.el.classList.remove(SIDEBAR_HIDDEN_LAYOUT)
27 app.appCache?.on 'progress', @onAppCacheProgress
28 return
29
30 deactivate: ->
31 if super
32 @resetClass()
33 @docPicker.detach()
34 app.el.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
35 app.appCache?.off 'progress', @onAppCacheProgress
36 return
37
38 render: ->
39 @docPicker.appendTo @sidebar
40 @refreshElements()
41 @addClass '_in'
42 return
43
44 save: ->
45 unless @saving
46 @saving = true
47 docs = @docPicker.getSelectedDocs()
48 app.settings.setDocs(docs)
49 @saveBtn.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026'
50 disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)
51 disabledDocs.uninstall ->
52 app.db.migrate()
53 app.reload()
54 return
55
56 onEnter: =>
57 @save()
58 return
59
60 onSubmit: (event) =>
61 event.preventDefault()
62 @save()
63 return
64
65 onClick: (event) =>
66 return if event.which isnt 1
67 if event.target is @backBtn
68 $.stopEvent(event)
69 app.router.show '/'
70 return
71
72 onFocus: (event) =>
73 $.scrollTo event.target, @el, 'continuous', bottomGap: 2
74 return
75
76 onAppCacheProgress: (event) =>
77 if event.lengthComputable
78 percentage = Math.round event.loaded * 100 / event.total
79 @saveBtn.textContent = "Downloading\u2026 (#{percentage}%)"
80 return
assets/javascripts/views/search/search.coffee+0−8
@@ -51,14 +51,6 @@ class app.views.Search extends app.View · 50 unmodified lines
51 @autoFocus()51 @autoFocus()
52 return52 return
53 53
54 disable: ->
55 @input.setAttribute('disabled', 'disabled')
56 return
57
58 enable: ->
59 @input.removeAttribute('disabled')
60 return
61
62 onReady: =>54 onReady: =>
63 @value = ''55 @value = ''
64 @delay @onInput56 @delay @onInput
assets/javascripts/views/sidebar/doc_picker.coffee+2−42
@@ -1,18 +1,9 @@
1class app.views.DocPicker extends app.View1class app.views.DocPicker extends app.View
2 @className: '_list _list-picker'2 @className: '_list _list-picker'
3 @attributes:
4 role: 'form'
5
6 @elements:
7 saveLink: '._sidebar-footer-save'
8 3
9 @events:4 @events:
10 click: 'onClick'
11 mousedown: 'onMouseDown'5 mousedown: 'onMouseDown'
12 6
13 @shortcuts:
14 enter: 'onEnter'
15
16 init: ->7 init: ->
17 @addSubview @listFold = new app.views.ListFold(@el)8 @addSubview @listFold = new app.views.ListFold(@el)
18 return9 return
@@ -20,14 +11,12 @@ class app.views.DocPicker extends app.View · 1 unmodified line
20 activate: ->11 activate: ->
21 if super12 if super
22 @render()13 @render()
23 app.appCache?.on 'progress', @onAppCacheProgress
24 $.on @el, 'focus', @onDOMFocus, true14 $.on @el, 'focus', @onDOMFocus, true
25 return15 return
26 16
27 deactivate: ->17 deactivate: ->
28 if super18 if super
29 @empty()19 @empty()
30 app.appCache?.off 'progress', @onAppCacheProgress
31 $.off @el, 'focus', @onDOMFocus, true20 $.off @el, 'focus', @onDOMFocus, true
32 @focusEl = null21 @focusEl = null
33 return22 return
@@ -43,8 +32,7 @@ class app.views.DocPicker extends app.View · 9 unmodified lines
43 else32 else
44 html += @tmpl('sidebarLabel', doc, checked: app.docs.contains(doc))33 html += @tmpl('sidebarLabel', doc, checked: app.docs.contains(doc))
45 34
46 @html html + @tmpl('sidebarPickerNote') + @tmpl('sidebarSave')35 @html html + @tmpl('docPickerNote')
47 @refreshElements()
48 36
49 $.requestAnimationFrame =>37 $.requestAnimationFrame =>
50 @addClass '_in'38 @addClass '_in'
@@ -68,31 +56,13 @@ class app.views.DocPicker extends app.View · 17 unmodified lines
68 super56 super
69 return57 return
70 58
71 save: ->
72 unless @saving
73 @saving = true
74 docs = @getSelectedDocs()
75 app.settings.setDocs(docs)
76 @saveLink.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026'
77 disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)
78 disabledDocs.uninstall ->
79 app.db.migrate()
80 app.reload()
81 return
82
83 getSelectedDocs: ->59 getSelectedDocs: ->
84 for input in @findAllByTag 'input' when input?.checked60 for input in @findAllByTag 'input' when input?.checked
85 input.name61 input.name
86 62
87 onClick: (event) =>
88 return if event.which isnt 1
89 if event.target is @saveLink
90 $.stopEvent(event)
91 @save()
92 return
93
94 onMouseDown: =>63 onMouseDown: =>
95 @mouseDown = Date.now()64 @mouseDown = Date.now()
65 return
96 66
97 onDOMFocus: (event) =>67 onDOMFocus: (event) =>
98 target = event.target68 target = event.target
@@ -112,13 +82,3 @@ class app.views.DocPicker extends app.View · 13 unmodified lines
112 @delay -> $('input', target.nextElementSibling).focus()82 @delay -> $('input', target.nextElementSibling).focus()
113 @focusEl = target83 @focusEl = target
114 return84 return
115
116 onEnter: =>
117 @save()
118 return
119
120 onAppCacheProgress: (event) =>
121 if event.lengthComputable
122 percentage = Math.round event.loaded * 100 / event.total
123 @saveLink.textContent = "Downloading\u2026 (#{percentage}%)"
124 return
assets/javascripts/views/sidebar/sidebar.coffee+2−26
@@ -22,10 +22,8 @@ class app.views.Sidebar extends app.View · 21 unmodified lines
22 22
23 @results = new app.views.Results @, @search23 @results = new app.views.Results @, @search
24 @docList = new app.views.DocList24 @docList = new app.views.DocList
25 @docPicker = new app.views.DocPicker unless app.isSingleDoc()
26 25
27 app.on 'ready', @onReady26 app.on 'ready', @onReady
28 $.on document, 'click', @onGlobalClick if @docPicker
29 return27 return
30 28
31 display: ->29 display: ->
@@ -45,23 +43,18 @@ class app.views.Sidebar extends app.View · 13 unmodified lines
45 @render()43 @render()
46 @view.activate()44 @view.activate()
47 @restoreScrollPosition()45 @restoreScrollPosition()
48 if view is @docPicker then @search.disable() else @search.enable()
49 return46 return
50 47
51 render: ->48 render: ->
52 @html @view49 @html @view
53 @append @tmpl('sidebarSettings') if @view is @docList and @docPicker
54 return50 return
55 51
56 showDocList: ->52 showDocList: ->
57 @showView @docList53 @showView @docList
58 return54 return
59 55
60 showDocPicker: =>
61 @showView @docPicker
62 return
63
64 showResults: =>56 showResults: =>
57 @display()
65 @showView @results58 @showView @results
66 return59 return
67 60
@@ -101,7 +94,6 @@ class app.views.Sidebar extends app.View · 33 unmodified lines
101 return94 return
102 95
103 onSearching: =>96 onSearching: =>
104 @display()
105 @showResults()97 @showResults()
106 return98 return
107 99
@@ -124,23 +116,6 @@ class app.views.Sidebar extends app.View · 16 unmodified lines
124 if event.target.hasAttribute? 'data-reset-list'116 if event.target.hasAttribute? 'data-reset-list'
125 $.stopEvent(event)117 $.stopEvent(event)
126 @onAltR()118 @onAltR()
127 else if event.target.hasAttribute? 'data-light'
128 $.stopEvent(event)
129 document.activeElement?.blur()
130 app.document.toggleLight()
131 else if event.target.hasAttribute? 'data-layout'
132 $.stopEvent(event)
133 document.activeElement?.blur()
134 app.document.toggleLayout()
135 return
136
137 onGlobalClick: (event) =>
138 return if event.which isnt 1
139 if event.target.hasAttribute? 'data-pick-docs'
140 $.stopEvent(event)
141 @showDocPicker()
142 else if @view is @docPicker
143 @showDocList() unless $.hasChild @el, event.target
144 return119 return
145 120
146 onAltR: =>121 onAltR: =>
@@ -157,3 +132,4 @@ class app.views.Sidebar extends app.View · 10 unmodified lines
157 onDocEnabled: ->132 onDocEnabled: ->
158 @docList.onEnabled()133 @docList.onEnabled()
159 @reset()134 @reset()
135 return
assets/stylesheets/application-dark.css.scss+1−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
20 'components/header',20 'components/header',
21 'components/notif',21 'components/notif',
22 'components/sidebar',22 'components/sidebar',
23 'components/settings',
23 'components/content',24 'components/content',
24 'components/page',25 'components/page',
25 'components/fail',26 'components/fail',
assets/stylesheets/application.css.scss+1−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
20 'components/header',20 'components/header',
21 'components/notif',21 'components/notif',
22 'components/sidebar',22 'components/sidebar',
23 'components/settings',
23 'components/content',24 'components/content',
24 'components/page',25 'components/page',
25 'components/fail',26 'components/fail',
assets/stylesheets/components/_content.scss+1−4
@@ -12,7 +12,7 @@ · 11 unmodified lines
12 12
13 @media #{$mediumScreen} { margin-left: $sidebarMediumWidth; }13 @media #{$mediumScreen} { margin-left: $sidebarMediumWidth; }
14 14
15 ._sidebar-hidden & { margin-left: $sidebarHiddenWidth; }15 ._sidebar-hidden & { margin-left: 0; }
16}16}
17 17
18._content {18._content {
@@ -283,7 +283,6 @@ · 264 unmodified lines
283}283}
284 284
285._docs-label {285._docs-label {
286 display: block;
287 overflow: hidden;286 overflow: hidden;
288 margin: 1px 0;287 margin: 1px 0;
289 padding: .375rem .5rem;288 padding: .375rem .5rem;
@@ -292,8 +291,6 @@ · 2 unmodified lines
292 display: inline-block;291 display: inline-block;
293 vertical-align: top;292 vertical-align: top;
294 margin: .25rem;293 margin: .25rem;
295 width: 1rem;
296 height: 1rem;
297 }294 }
298}295}
299 296
assets/stylesheets/components/_header.scss+3−5
@@ -12,6 +12,8 @@ · 11 unmodified lines
12 height: $headerHeight;12 height: $headerHeight;
13 background: $headerBackground;13 background: $headerBackground;
14 border-bottom: 1px solid $headerBorder;14 border-bottom: 1px solid $headerBorder;
15 border-right: 1px solid $headerBorder;
16 @extend %border-box;
15 @extend %user-select-none;17 @extend %user-select-none;
16 18
17 @media #{$mediumScreen} { width: $sidebarMediumWidth; }19 @media #{$mediumScreen} { width: $sidebarMediumWidth; }
@@ -55,17 +57,13 @@ · 37 unmodified lines
55// Menu57// Menu
56//58//
57 59
58._menu-btn {
59 border-right: 1px solid $headerBorder;
60}
61
62._menu {60._menu {
63 position: absolute;61 position: absolute;
64 z-index: 1;62 z-index: 1;
65 top: .25rem;63 top: .25rem;
66 right: .25rem;64 right: .25rem;
67 width: 8rem;65 width: 8rem;
68 height: calc(11.5rem + 1px);66 height: calc(13.75rem + 1px);
69 font-size: .875rem;67 font-size: .875rem;
70 background: $contentBackground;68 background: $contentBackground;
71 border: 1px solid $headerBorder;69 border: 1px solid $headerBorder;
assets/stylesheets/components/_mobile.scss+20−19
@@ -11,30 +11,42 @@ · 10 unmodified lines
11 body { -ms-overflow-style: -ms-autohiding-scrollbar; }11 body { -ms-overflow-style: -ms-autohiding-scrollbar; }
12 12
13 ._app, ._content { overflow: visible; }13 ._app, ._content { overflow: visible; }
14 ._app { padding-top: $headerHeight; }14
15 ._container { margin: 0; }15 ._container, ._sidebar {
16 margin: 0;
17 padding-top: $headerHeight;
18 }
16 19
17 ._content {20 ._content {
18 position: static;21 position: static;
19 height: auto;22 height: auto;
20 margin: 0;23 margin: 0;
21 padding: .75rem 1rem 2.5rem;24 padding: .75rem 1rem 2rem;
22 25
23 &:before { content: none; }26 &:before { content: none; }
24 }27 }
25 28
26 ._booting:before, ._content-loading:before { font-size: 3rem; }29 ._booting:before, ._content-loading:before { font-size: 3rem; }
27 30
28 // Header31 ._header, ._footer {
29
30 ._header {
31 position: fixed;32 position: fixed;
32 max-width: 100vw;33 width: 100%;
33 }34 }
34 35
36 ._header, ._list, ._footer {
37 width: 100%;
38 border-right: 0;
39 box-shadow: none;
40 }
41
42 ._settings { position: static; }
43 ._settings ._sidebar { padding-bottom: $headerHeight; }
44 ._settings-tabs { display: block; }
45
46 // Header
47
35 ._header-btn { width: 2.5rem; }48 ._header-btn { width: 2.5rem; }
36 ._header-btn[hidden] { display: block; }49 ._header-btn[hidden] { display: block; }
37 ._menu-btn { border-right: 0; }
38 50
39 ._search {51 ._search {
40 padding-right: .125rem;52 padding-right: .125rem;
@@ -53,8 +65,6 @@ · 12 unmodified lines
53 overflow: visible;65 overflow: visible;
54 }66 }
55 67
56 ._header, ._list, ._sidebar-footer { width: 100%; }
57
58 ._list-item {68 ._list-item {
59 white-space: normal;69 white-space: normal;
60 word-wrap: break-word;70 word-wrap: break-word;
@@ -72,15 +82,6 @@ · 11 unmodified lines
72 }82 }
73 }83 }
74 84
75 ._list-link { display: none; }
76
77 ._sidebar-footer { box-shadow: none; }
78
79 ._sidebar-footer-save {
80 margin: 0;
81 box-shadow: 0 1px $noteGreenBorder, 0 -1px $noteGreenBorder;
82 }
83
84 // Notice85 // Notice
85 86
86 ._notice {87 ._notice {
assets/stylesheets/components/_notice.scss+2−1
@@ -11,7 +11,8 @@ · 10 unmodified lines
11 11
12 @media #{$mediumScreen} { left: $sidebarMediumWidth; }12 @media #{$mediumScreen} { left: $sidebarMediumWidth; }
13 13
14 ._sidebar-hidden & { left: $sidebarHiddenWidth; }14 ._sidebar-hidden & { left: 0; }
15
15 ~ ._container { padding-bottom: 2.5rem; }16 ~ ._container { padding-bottom: 2.5rem; }
16}17}
17 18
assets/stylesheets/components/_path.scss+1−1
@@ -13,7 +13,7 @@ · 12 unmodified lines
13 13
14 @media #{$mediumScreen} { left: $sidebarMediumWidth; }14 @media #{$mediumScreen} { left: $sidebarMediumWidth; }
15 15
16 ._sidebar-hidden & { left: $sidebarHiddenWidth; }16 ._sidebar-hidden & { left: 0; }
17 17
18 ~ ._container { padding-bottom: 2rem; }18 ~ ._container { padding-bottom: 2rem; }
19 a:focus { outline: 0; }19 a:focus { outline: 0; }
assets/stylesheets/components/_settings.scss+146−0
@@ -0,0 +1,146 @@
1//
2// Settings
3//
4
5._settings {
6 display: none;
7 position: absolute;
8 top: 0;
9 bottom: 0;
10 z-index: $headerZ;
11
12 &._in { display: block; }
13
14 ._sidebar {
15 bottom: $headerHeight;
16
17 ._sidebar-hidden & { display: block; }
18 }
19
20 ._header {
21 justify-content: space-between;
22 }
23}
24
25._settings-fieldset {
26 display: flex;
27 margin: 1.5rem 0;
28 line-height: 1.5rem;
29}
30
31._settings-legend {
32 flex: 0 1 10rem;
33 margin: 0;
34 padding-right: .5rem;
35 line-height: inherit;
36 font-size: inherit;
37 font-weight: $boldFontWeight;
38 text-align: right;
39 @extend %border-box;
40}
41
42._settings-inputs {
43 flex: 1 1 20rem;
44}
45
46._settings-label {
47 margin: 0 0 .375rem;
48
49 > small {
50 display: block;
51 color: $textColorLight;
52 margin-left: 1.75rem;
53 }
54
55 input[type=checkbox] {
56 display: inline-block;
57 vertical-align: top;
58 margin: .25rem .375rem;
59 }
60}
61
62@media (max-width: $maxWidth) {
63 ._settings-max-width { display: none; }
64}
65
66._settings-link {
67 display: inline-block;
68 vertical-align: top;
69 margin-left: .375rem;
70
71 &[data-behavior=reset] {
72 font-size: .75rem;
73 color: $textColorRed;
74 }
75}
76
77._footer {
78 position: absolute;
79 z-index: $headerZ;
80 bottom: 0;
81 left: 0;
82 width: $sidebarWidth;
83 height: $headerHeight;
84 background: $noteGreenBackground;
85 border-top: 1px solid $noteGreenBorder;
86 border-right: 1px solid $noteGreenBorder;
87 @extend %border-box;
88 @extend %user-select-none;
89
90 @media #{$mediumScreen} { width: $sidebarMediumWidth; }
91}
92
93._settings-btn {
94 display: block;
95 height: 100%;
96 line-height: 1.5rem;
97 padding: 0 .75rem;
98 font-size: .875rem;
99 font-weight: $boldFontWeight;
100 color: inherit;
101 text-align: left;
102 cursor: pointer;
103 @extend %border-box;
104
105 > svg {
106 display: inline-block;
107 vertical-align: top;
108 width: 1.5rem;
109 height: 1.5rem;
110 margin-right: .125rem;
111 fill: currentColor;
112 pointer-events: none;
113 }
114}
115
116._save-btn { width: 100%; }
117
118//
119// Header tabs
120//
121
122._settings-tabs {
123 display: none; // mobile only
124 margin-right: .5rem;
125 line-height: $headerHeight;
126}
127
128._settings-tab {
129 position: relative;
130 display: inline-block;
131 vertical-align: top;
132 padding: 0 .75rem;
133 cursor: pointer;
134
135 &, &:hover {
136 color: $textColorLight;
137 text-decoration: none;
138 }
139
140 &.active {
141 color: $textColor;
142 font-weight: $boldFontWeight;
143 text-decoration: none;
144 box-shadow: inset 0 -2px $linkColor;
145 }
146}
assets/stylesheets/components/_sidebar.scss+5−162
@@ -8,13 +8,13 @@ · 7 unmodified lines
8 top: $headerHeight;8 top: $headerHeight;
9 bottom: 0;9 bottom: 0;
10 left: 0;10 left: 0;
11 margin-top: 1px;
12 overflow-x: hidden;11 overflow-x: hidden;
13 overflow-y: scroll;12 overflow-y: scroll;
14 background: $sidebarBackground;13 background: $sidebarBackground;
15 background-clip: content-box;14 background-clip: content-box;
16 -webkit-overflow-scrolling: touch;15 -webkit-overflow-scrolling: touch;
17 -ms-overflow-style: none; // IE 10 doesn't support pointer-events16 -ms-overflow-style: none; // IE 10 doesn't support pointer-events
17 @extend %border-box;
18 @extend %user-select-none;18 @extend %user-select-none;
19 19
20 &::-webkit-scrollbar { -webkit-appearance: none; width: 10px; }20 &::-webkit-scrollbar { -webkit-appearance: none; width: 10px; }
@@ -48,26 +48,7 @@ · 27 unmodified lines
48 width: 3px;48 width: 3px;
49 cursor: col-resize;49 cursor: col-resize;
50 50
51 ._sidebar-hidden & {51 ._sidebar-hidden & { display: none; }
52 left: 0;
53 margin-left: 0;
54 background: $headerBackground;
55 border-right: 1px solid $headerBorder;
56 width: 8px;
57 cursor: pointer;
58
59 &:before {
60 content: '';
61 position: absolute;
62 top: 50%;
63 left: 3px;
64 margin-top: -.5rem;
65 width: 1px;
66 height: 1rem;
67 border-left: 1px solid $textColorLighter;
68 border-right: 1px solid $textColorLighter;
69 }
70 }
71}52}
72 53
73//54//
@@ -84,10 +65,7 @@ · 10 unmodified lines
84 65
85 @media #{$mediumScreen} { width: $sidebarMediumWidth; }66 @media #{$mediumScreen} { width: $sidebarMediumWidth; }
86 67
87 ._sidebar > & {68 ._sidebar > & { min-height: 100%; }
88 min-height: 100%;
89 padding-bottom: 3.5rem;
90 }
91 69
92 a:focus { outline: 0; }70 a:focus { outline: 0; }
93}71}
@@ -342,7 +320,7 @@ · 248 unmodified lines
342._list-picker {320._list-picker {
343 > ._list, > ._list-item {321 > ._list, > ._list-item {
344 opacity: 0;322 opacity: 0;
345 transition: .2s;323 transition: opacity .2s;
346 }324 }
347 &._in { > ._list, > ._list-item { opacity: 1; } }325 &._in { > ._list, > ._list-item { opacity: 1; } }
348 326
@@ -354,14 +332,11 @@ · 5 unmodified lines
354 position: absolute;332 position: absolute;
355 top: .5rem;333 top: .5rem;
356 right: .75rem;334 right: .75rem;
357 width: 1rem;
358 height: 1rem;
359 transition: .2s;
360}335}
361 336
362._list-link {337._list-link {
363 display: block;338 display: block;
364 margin-top: .75rem;339 padding: .75rem 0;
365 font-size: .8125rem;340 font-size: .8125rem;
366 text-align: center;341 text-align: center;
367 @extend %external-link;342 @extend %external-link;
@@ -369,135 +344,3 @@ · 1 unmodified line
369 &:after { visibility: hidden; }344 &:after { visibility: hidden; }
370 &:hover:after { visibility: visible; }345 &:hover:after { visibility: visible; }
371}346}
372
373//
374// Footer
375//
376
377._sidebar-footer {
378 position: fixed;
379 bottom: 0;
380 left: 0;
381 width: $sidebarWidth;
382 background: $sidebarBackground;
383 box-shadow: inset -1px 0 $sidebarBorder;
384 -webkit-transform: translateZ(0);
385 transform: translateZ(0);
386
387 @media #{$mediumScreen} { width: $sidebarMediumWidth; }
388
389 ._max-width & {
390 left: calc(50% - #{$maxWidth} / 2);
391 @media (max-width: #{$maxWidth}) { left: 0; }
392 }
393
394 &:before {
395 content: '';
396 position: absolute;
397 bottom: 100%;
398 left: 0;
399 right: 1px;
400 height: 1em;
401 background-image: -webkit-linear-gradient(top, rgba($sidebarBackground, 0), rgba($sidebarBackground, .95));
402 background-image: linear-gradient(to bottom, rgba($sidebarBackground, 0), rgba($sidebarBackground, .95));
403 pointer-events: none;
404 }
405}
406
407._sidebar-footer-link {
408 position: relative;
409 display: block;
410 overflow: hidden;
411 height: 2.5rem;
412 line-height: 1rem;
413 padding: .75rem .25rem .75rem .75rem;
414 font-size: .875em;
415 cursor: pointer;
416 @extend %border-box;
417
418 &, &:hover {
419 color: inherit;
420 text-decoration: none;
421 }
422
423 &:before {
424 float: left;
425 margin-right: .625rem;
426 @extend %icon;
427 }
428}
429
430._sidebar-footer-edit {
431 display: inline-block;
432
433 @if $style == 'dark' {
434 &:before { @extend %icon-settings-white; }
435 } @else {
436 &:before { @extend %icon-settings; }
437 }
438}
439
440._sidebar-footer-light {
441 float: right;
442 width: 2rem;
443 padding: 0;
444 opacity: .65;
445 @extend %hide-text;
446
447 &:before {
448 float: none;
449 position: absolute;
450 top: .75rem;
451 left: .25rem;
452
453 @if $style == 'dark' {
454 @extend %icon-light-white;
455 } @else {
456 @extend %icon-light;
457 }
458 }
459}
460
461._sidebar-footer-layout {
462 float: right;
463 width: 2rem;
464 padding: 0;
465 opacity: .65;
466 @extend %hide-text;
467
468 &:before {
469 float: none;
470 position: absolute;
471 top: .75rem;
472 left: .375rem;
473 @if $style == 'dark' {
474 @extend %icon-expand-white;
475 } @else {
476 @extend %icon-expand;
477 }
478
479 ._max-width & {
480 @if $style == 'dark' {
481 @extend %icon-contract-white;
482 } @else {
483 @extend %icon-contract;
484 }
485 }
486 }
487
488 @media (max-width: #{$maxWidth + .1rem}) { display: none; }
489}
490
491._sidebar-footer-save {
492 margin-right: 1px;
493 font-weight: $boldFontWeight;
494 background: $noteGreenBackground;
495 box-shadow: inset 0 1px $noteGreenBorder,
496 1px 0 $noteGreenBorder;
497
498 @if $style == 'dark' {
499 &:before { @extend %icon-check-white; }
500 } @else {
501 &:before { @extend %icon-check; }
502 }
503}
assets/stylesheets/global/_base.scss+11−0
@@ -166,6 +166,11 @@ section, main { · 165 unmodified lines
166 outline: 0;166 outline: 0;
167}167}
168 168
169label {
170 display: block;
171 @extend %user-select-none;
172}
173
169input, button {174input, button {
170 margin: 0;175 margin: 0;
171 font-family: inherit;176 font-family: inherit;
@@ -175,6 +180,12 @@ input, button { · 3 unmodified lines
175 @extend %border-box;180 @extend %border-box;
176}181}
177 182
183input[type=checkbox] {
184 width: 1rem;
185 height: 1rem;
186 cursor: pointer;
187}
188
178button {189button {
179 padding: 0;190 padding: 0;
180 background: none;191 background: none;
assets/stylesheets/global/_icons.scss+8−18
@@ -4,7 +4,7 @@ · 3 unmodified lines
4 width: 1rem;4 width: 1rem;
5 height: 1rem;5 height: 1rem;
6 background-image: image-url('icons.png');6 background-image: image-url('icons.png');
7 background-size: 4rem 6rem;7 background-size: 4rem 3rem;
8}8}
9 9
10@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {10@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
@@ -35,23 +35,13 @@ · 24 unmodified lines
35%icon-search { background-position: -1rem 0; }35%icon-search { background-position: -1rem 0; }
36%icon-link { background-position: -2.25rem -.25rem; }36%icon-link { background-position: -2.25rem -.25rem; }
37%icon-clear { background-position: -3rem 0; }37%icon-clear { background-position: -3rem 0; }
38%icon-settings { background-position: 0 -1rem; }38%icon-path { background-position: 0 -1rem; }
39%icon-check { background-position: -1rem -1rem; }39%icon-search-white { background-position: -1rem -1rem; }
40%icon-path { background-position: 0 -2rem; }40%icon-dir-white { background-position: -2rem -1rem; }
41%icon-search-white { background-position: -1rem -2rem; }41%icon-link-white { background-position: -3.25rem -1.25rem; }
42%icon-dir-white { background-position: -2rem -2rem; }42%icon-clipboard { background-position: 0 -2rem; }
43%icon-link-white { background-position: -3.25rem -2.25rem; }43%icon-clipboard-white { background-position: -1rem -2rem; }
44%icon-settings-white { background-position: 0 -3rem; }44%icon-close-white { background-position: -2rem -2rem; }
45%icon-check-white { background-position: -1rem -3rem; }
46%icon-light { background-position: -2rem -3rem; }
47%icon-light-white { background-position: -3rem -3rem; }
48%icon-expand { background-position: 0 -4rem; }
49%icon-contract { background-position: -1rem -4rem; }
50%icon-expand-white { background-position: -2rem -4rem; }
51%icon-contract-white { background-position: -3rem -4rem; }
52%icon-clipboard { background-position: 0 -5rem; }
53%icon-clipboard-white { background-position: -1rem -5rem; }
54%icon-close-white { background-position: -2rem -5rem; }
55 45
56._icon-codeceptjs:before { background-position: -3rem 0; }46._icon-codeceptjs:before { background-position: -3rem 0; }
57._icon-codeception:before { background-position: -4rem 0; }47._icon-codeception:before { background-position: -4rem 0; }
assets/stylesheets/global/_variables-dark.scss+1−1
@@ -9,7 +9,6 @@ $maxWidth: 80rem; · 8 unmodified lines
9$headerHeight: 3rem;9$headerHeight: 3rem;
10$sidebarWidth: 20rem;10$sidebarWidth: 20rem;
11$sidebarMediumWidth: 16rem;11$sidebarMediumWidth: 16rem;
12$sidebarHiddenWidth: 9px;
13 12
14$contentBackground: #33373a;13$contentBackground: #33373a;
15$documentBackground: $contentBackground;14$documentBackground: $contentBackground;
@@ -17,6 +16,7 @@ $documentBackground: $contentBackground; · 1 unmodified line
17$textColor: #cbd0d0;16$textColor: #cbd0d0;
18$textColorLight: #9da5ad;17$textColorLight: #9da5ad;
19$textColorLighter: #77787a;18$textColorLighter: #77787a;
19$textColorRed: #f44336;
20 20
21$inputFocusBorder: false;21$inputFocusBorder: false;
22 22
assets/stylesheets/global/_variables.scss+1−1
@@ -9,7 +9,6 @@ $maxWidth: 80rem; · 8 unmodified lines
9$headerHeight: 3rem;9$headerHeight: 3rem;
10$sidebarWidth: 20rem;10$sidebarWidth: 20rem;
11$sidebarMediumWidth: 16rem;11$sidebarMediumWidth: 16rem;
12$sidebarHiddenWidth: 9px;
13 12
14$contentBackground: #fff;13$contentBackground: #fff;
15$documentBackground: #fafafa;14$documentBackground: #fafafa;
@@ -17,6 +16,7 @@ $documentBackground: #fafafa; · 1 unmodified line
17$textColor: #333;16$textColor: #333;
18$textColorLight: #666;17$textColorLight: #666;
19$textColorLighter: #888;18$textColorLighter: #888;
19$textColorRed: #f44336;
20 20
21$inputFocusBorder: #35b5f4;21$inputFocusBorder: #35b5f4;
22 22
lib/app.rb+1−1
@@ -235,7 +235,7 @@ class App < Sinatra::Application · 234 unmodified lines
235 erb :index235 erb :index
236 end236 end
237 237
238 %w(offline about news help).each do |page|238 %w(settings offline about news help).each do |page|
239 get "/#{page}" do239 get "/#{page}" do
240 if supports_js_redirection?240 if supports_js_redirection?
241 redirect_via_js "/#{page}"241 redirect_via_js "/#{page}"
public/icons/ui/check-white/SOURCE+0−1
@@ -1 +0,0 @@
1http://happytodesign.com/hicons/
public/icons/ui/check-white/check-white.png+0−0

Binary file not shown.

public/icons/ui/check-white/check-white@2x.png+0−0

Binary file not shown.

public/icons/ui/check/SOURCE+0−1
@@ -1 +0,0 @@
1http://happytodesign.com/hicons/
public/icons/ui/check/check.png+0−0

Binary file not shown.

public/icons/ui/check/check@2x.png+0−0

Binary file not shown.

public/icons/ui/contract/16.png+0−0

Binary file not shown.

public/icons/ui/contract/16@2x.png+0−0

Binary file not shown.

public/icons/ui/expand/16.png+0−0

Binary file not shown.

public/icons/ui/expand/16@2x.png+0−0

Binary file not shown.

public/icons/ui/light-white/SOURCE+0−1
@@ -1 +0,0 @@
1http://www.entypo.com/
public/icons/ui/light-white/light.png+0−0

Binary file not shown.

public/icons/ui/light-white/light@2x.png+0−0

Binary file not shown.

public/icons/ui/light/SOURCE+0−1
@@ -1 +0,0 @@
1http://www.entypo.com/
public/icons/ui/light/light.png+0−0

Binary file not shown.

public/icons/ui/light/light@2x.png+0−0

Binary file not shown.

public/icons/ui/settings-white/SOURCE+0−1
@@ -1 +0,0 @@
1http://gemicon.net/
public/icons/ui/settings-white/settings-white.png+0−0

Binary file not shown.

public/icons/ui/settings-white/settings-white@2x.png+0−0

Binary file not shown.

public/icons/ui/settings/SOURCE+0−1
@@ -1 +0,0 @@
1http://gemicon.net/
public/icons/ui/settings/settings.png+0−0

Binary file not shown.

public/icons/ui/settings/settings@2x.png+0−0

Binary file not shown.

public/robots.txt+1−1
@@ -1,2 +1,2 @@
1User-agent: *1User-agent: *
2Allow: /2Disallow: /settings
views/app.erb+24−6
@@ -1,7 +1,7 @@
1<div class="_app<%= " #{app_layout}" if app_layout %>" role="application">1<div class="_app<%= " #{app_layout}" if app_layout %>" role="application">
2 <header class="_header" role="banner">2 <header class="_header" role="banner">
3 <button type="button" aria-label="Toggle navigation" class="_header-btn" data-toggle-sidebar hidden>3 <button type="button" aria-label="Toggle navigation" class="_header-btn" data-toggle-sidebar hidden>
4 <svg viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></svg>4 <svg viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" /></svg>
5 </button>5 </button>
6 <form class="_search" role="search">6 <form class="_search" role="search">
7 <input type="search" name="q" class="_search-input" placeholder="Search&hellip;" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="30" aria-label="Search">7 <input type="search" name="q" class="_search-input" placeholder="Search&hellip;" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="30" aria-label="Search">
@@ -9,17 +9,18 @@ · 1 unmodified line
9 <div class="_search-tag"></div>9 <div class="_search-tag"></div>
10 </form>10 </form>
11 <button type="button" aria-label="Back" class="_header-btn" data-back hidden>11 <button type="button" aria-label="Back" class="_header-btn" data-back hidden>
12 <svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></svg>12 <svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /></svg>
13 </button>13 </button>
14 <button type="button" aria-label="Forward" class="_header-btn _forward-btn" data-forward hidden>14 <button type="button" aria-label="Forward" class="_header-btn _forward-btn" data-forward hidden>
15 <svg viewBox="0 0 24 24"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"></svg>15 <svg viewBox="0 0 24 24"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" /></svg>
16 </button>16 </button>
17 <button type="button" aria-label="Toggle menu" title="Toggle menu" class="_header-btn _menu-btn" data-toggle-menu>17 <button type="button" aria-label="Toggle menu" title="Toggle menu" class="_header-btn _menu-btn" data-toggle-menu>
18 <svg viewBox="0 0 24 24"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></svg>18 <svg viewBox="0 0 24 24"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /></svg>
19 </button>19 </button>
20 <nav class="_menu" role="navigation">20 <nav class="_menu" role="navigation">
21 <h1 class="_menu-title"><a href="/" class="_menu-title-link">DevDocs</a></h1>21 <h1 class="_menu-title"><a href="/" class="_menu-title-link">DevDocs</a></h1>
22 <a href="/offline" class="_menu-link">Offline</a>22 <a href="/settings" class="_menu-link">Preferences</a>
23 <a href="/offline" class="_menu-link">Offline Data</a>
23 <a href="/news" class="_menu-link">Changelog</a>24 <a href="/news" class="_menu-link">Changelog</a>
24 <a href="/help" class="_menu-link">Help</a>25 <a href="/help" class="_menu-link">Help</a>
25 <a href="/about" class="_menu-link">About</a>26 <a href="/about" class="_menu-link">About</a>
@@ -37,10 +38,27 @@ · 11 unmodified lines
37 <div class="_container" role="document">38 <div class="_container" role="document">
38 <main class="_content _content-loading" role="main"></main>39 <main class="_content _content-loading" role="main"></main>
39 </div>40 </div>
41 <form class="_settings" id="settings">
42 <div class="_header">
43 <button type="button" aria-label="Back" class="_settings-btn" data-back>
44 <svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /></svg> Back
45 </button>
46 <nav class="_settings-tabs">
47 <a class="_settings-tab active" data-tab="doc-picker" hidden>Docs</a>
48 <a class="_settings-tab" data-tab="settings" hidden>Settings</a>
49 </nav>
50 </div>
51 <div class="_sidebar"></div>
52 <div class="_footer">
53 <button type="submit" class="_settings-btn _save-btn">
54 <svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" /></svg> Save
55 </button>
56 </div>
57 </form>
40</div>58</div>
41<style data-size="<%= app_size %>" data-resizer>59<style data-size="<%= app_size %>" data-resizer>
42 ._container { margin-left: <%= app_size %>; }60 ._container { margin-left: <%= app_size %>; }
43 ._header, ._list, ._sidebar-footer { width: <%= app_size %>; }61 ._header, ._list, ._footer { width: <%= app_size %>; }
44 ._list-hover.clone { min-width: <%= app_size %>; }62 ._list-hover.clone { min-width: <%= app_size %>; }
45 ._notice, ._path, ._resizer { left: <%= app_size %>; }63 ._notice, ._path, ._resizer { left: <%= app_size %>; }
46</style>64</style>

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t