Closes #88.
Unified · Split
| @@ -2,6 +2,7 @@ class app.Settings · 1 unmodified line | |||
| 2 | SETTINGS_KEY = 'settings' | 2 | SETTINGS_KEY = 'settings' |
| 3 | DOCS_KEY = 'docs' | 3 | DOCS_KEY = 'docs' |
| 4 | DARK_KEY = 'dark' | 4 | DARK_KEY = 'dark' |
| 5 | SIZE_KEY = 'size' | ||
| 5 | 6 | ||
| 6 | @defaults: -> | 7 | @defaults: -> |
| 7 | count: 0 | 8 | count: 0 |
| @@ -59,8 +60,15 @@ class app.Settings · 51 unmodified lines | |||
| 59 | catch | 60 | catch |
| 60 | return | 61 | return |
| 61 | 62 | ||
| 63 | setSize: (value) -> | ||
| 64 | try | ||
| 65 | Cookies.set SIZE_KEY, value, path: '/', expires: 1e8 | ||
| 66 | catch | ||
| 67 | return | ||
| 68 | |||
| 62 | reset: -> | 69 | reset: -> |
| 63 | try Cookies.expire DOCS_KEY | 70 | try Cookies.expire DOCS_KEY |
| 64 | try Cookies.expire DARK_KEY | 71 | try Cookies.expire DARK_KEY |
| 72 | try Cookies.expire SIZE_KEY | ||
| 65 | try @store.del(SETTINGS_KEY) | 73 | try @store.del(SETTINGS_KEY) |
| 66 | return | 74 | return |
| @@ -1,5 +1,8 @@ | |||
| 1 | [ | 1 | [ |
| 2 | [ "2015-02-15", | 2 | [ "2015-02-16", |
| 3 | "The sidebar is now resizable (drag & drop)." | ||
| 4 | ], [ | ||
| 5 | "2015-02-15", | ||
| 3 | "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" | 6 | "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" |
| 4 | ], [ | 7 | ], [ |
| 5 | "2015-02-08", | 8 | "2015-02-08", |
| @@ -10,6 +10,7 @@ class app.views.Document extends app.View · 9 unmodified lines | |||
| 10 | init: -> | 10 | init: -> |
| 11 | @addSubview @nav = new app.views.Nav, | 11 | @addSubview @nav = new app.views.Nav, |
| 12 | @addSubview @sidebar = new app.views.Sidebar | 12 | @addSubview @sidebar = new app.views.Sidebar |
| 13 | @addSubview @resizer = new app.views.Resizer if app.views.Resizer.isSupported() | ||
| 13 | @addSubview @content = new app.views.Content | 14 | @addSubview @content = new app.views.Content |
| 14 | @addSubview @path = new app.views.Path unless app.isSingleDoc() or app.isMobile() | 15 | @addSubview @path = new app.views.Path unless app.isSingleDoc() or app.isMobile() |
| 15 | 16 | ||
| @@ -0,0 +1,46 @@ | |||
| 1 | class app.views.Resizer extends app.View | ||
| 2 | @className: '_resizer' | ||
| 3 | |||
| 4 | @events: | ||
| 5 | dragstart: 'onDragStart' | ||
| 6 | dragend: 'onDragEnd' | ||
| 7 | drag: 'onDrag' | ||
| 8 | |||
| 9 | @isSupported: -> | ||
| 10 | 'ondragstart' of document.createElement('div') and !app.isMobile() | ||
| 11 | |||
| 12 | init: -> | ||
| 13 | @el.setAttribute('draggable', 'true') | ||
| 14 | @appendTo $('._app') | ||
| 15 | |||
| 16 | @style = $('style[data-resizer]') | ||
| 17 | @size = @style.getAttribute('data-size') | ||
| 18 | return | ||
| 19 | |||
| 20 | MIN = 250 | ||
| 21 | MAX = 600 | ||
| 22 | |||
| 23 | resize: (newSize) -> | ||
| 24 | return unless newSize > 0 | ||
| 25 | newSize = Math.min(Math.max(Math.round(newSize), MIN), MAX) | ||
| 26 | app.settings.setSize(newSize) | ||
| 27 | newSize = "#{newSize}px" | ||
| 28 | @style.innerHTML = @style.innerHTML.replace(new RegExp(@size, 'g'), newSize) | ||
| 29 | @size = newSize | ||
| 30 | return | ||
| 31 | |||
| 32 | onDragStart: (event) => | ||
| 33 | @style.removeAttribute('disabled') | ||
| 34 | event.dataTransfer.effectAllowed = 'link' | ||
| 35 | event.dataTransfer.setData('text/plain', '') | ||
| 36 | return | ||
| 37 | |||
| 38 | onDrag: (event) => | ||
| 39 | return if @lastDrag and @lastDrag > Date.now() - 50 | ||
| 40 | @lastDrag = Date.now() | ||
| 41 | @resize event.clientX | ||
| 42 | return | ||
| 43 | |||
| 44 | onDragEnd: (event) => | ||
| 45 | @resize event.screenX - window.screenX | ||
| 46 | return | ||
| @@ -35,6 +35,17 @@ · 34 unmodified lines | |||
| 35 | a:focus { outline: 0; } | 35 | a:focus { outline: 0; } |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | ._resizer { | ||
| 39 | position: absolute; | ||
| 40 | z-index: $sidebarZ + 1; | ||
| 41 | top: $headerHeight; | ||
| 42 | bottom: 0; | ||
| 43 | left: $sidebarWidth; | ||
| 44 | margin-left: -2px; | ||
| 45 | width: 3px; | ||
| 46 | cursor: col-resize; | ||
| 47 | } | ||
| 48 | |||
| 38 | // | 49 | // |
| 39 | // List | 50 | // List |
| 40 | // | 51 | // |
| @@ -136,6 +136,10 @@ class App < Sinatra::Application · 135 unmodified lines | |||
| 136 | dark: stylesheet_path('application-dark') | 136 | dark: stylesheet_path('application-dark') |
| 137 | } | 137 | } |
| 138 | end | 138 | end |
| 139 | |||
| 140 | def size | ||
| 141 | @size ||= cookies[:size].nil? ? '18rem' : "#{cookies[:size]}px" | ||
| 142 | end | ||
| 139 | end | 143 | end |
| 140 | 144 | ||
| 141 | before do | 145 | before do |
| @@ -20,6 +20,17 @@ class AppTest < MiniTest::Spec · 19 unmodified lines | |||
| 20 | assert last_response.redirect? | 20 | assert last_response.redirect? |
| 21 | assert_equal 'http://example.org/', last_response['Location'] | 21 | assert_equal 'http://example.org/', last_response['Location'] |
| 22 | end | 22 | end |
| 23 | |||
| 24 | it "sets default size" do | ||
| 25 | get '/' | ||
| 26 | assert_includes last_response.body, 'data-size="18rem"' | ||
| 27 | end | ||
| 28 | |||
| 29 | it "sets size from cookie" do | ||
| 30 | set_cookie('size=42') | ||
| 31 | get '/' | ||
| 32 | assert_includes last_response.body, 'data-size="42px"' | ||
| 33 | end | ||
| 23 | end | 34 | end |
| 24 | 35 | ||
| 25 | describe "/[static-page]" do | 36 | describe "/[static-page]" do |
| @@ -44,3 +44,9 @@ · 43 unmodified lines | |||
| 44 | a.src="//secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0]; | 44 | a.src="//secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0]; |
| 45 | b.parentNode.insertBefore(a,b)}(); | 45 | b.parentNode.insertBefore(a,b)}(); |
| 46 | </script><% end %> | 46 | </script><% end %> |
| 47 | <style data-size="<%= size %>" data-resizer disabled> | ||
| 48 | ._container { margin-left: <%= size %>; } | ||
| 49 | ._search, ._list, ._sidebar-footer { width: <%= size %>; } | ||
| 50 | ._list-hover.clone { min-width: <%= size %>; } | ||
| 51 | ._notice, ._path, ._resizer { left: <%= size %>; } | ||
| 52 | </style> | ||