diff --git a/4chan_x.user.js b/4chan_x.user.js index a0651bc4b..7a8d4eb84 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1085,11 +1085,13 @@ }); this.el = $.el('div', { className: 'reply dialog', - id: 'menu' + id: 'menu', + tabIndex: 0 }); $.on(this.el, 'click', function(e) { return e.stopPropagation(); }); + $.on(this.el, 'keydown', this.keybinds); return Main.callbacks.push(this.node); }, node: function(post) { @@ -1129,6 +1131,7 @@ $.add(el, entry.el); } } + $.addClass($('.entry', Menu.el), 'focused'); $.on(d, 'click', Menu.close); $.add(d.body, el); mRect = el.getBoundingClientRect(); @@ -1136,19 +1139,77 @@ bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top; bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left; el.style.top = bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight ? bTop + bRect.height + 2 + 'px' : bTop - mRect.height - 2 + 'px'; - return el.style.left = bRect.left + mRect.width < d.documentElement.clientWidth ? bLeft + 'px' : bLeft + bRect.width - mRect.width + 'px'; + el.style.left = bRect.left + mRect.width < d.documentElement.clientWidth ? bLeft + 'px' : bLeft + bRect.width - mRect.width + 'px'; + return el.focus(); }, close: function() { - var el; + var el, focused; el = Menu.el; $.rm(el); + if (focused = $('.focused.entry', el)) { + $.rmClass(focused, 'focused'); + } el.innerHTML = null; el.removeAttribute('style'); delete Menu.lastOpener; return $.off(d, 'click', Menu.close); }, + keybinds: function(e) { + var el, next; + el = $('.focused.entry', Menu.el); + switch (Keybinds.keyCode(e) || e.keyCode) { + case 'Esc': + Menu.lastOpener.focus(); + Menu.close(); + break; + case 13: + case 32: + el.click(); + break; + case 'Up': + if (next = el.previousElementSibling) { + Menu.focus(next); + } + break; + case 'Right': + if (next = el.firstElementChild) { + Menu.focus(next); + } + break; + case 'Down': + if (next = el.nextElementSibling) { + Menu.focus(next); + } + break; + case 'Left': + if ((next = el.parentNode) && next.id !== 'menu') { + Menu.focus(next); + } + break; + default: + return; + } + e.preventDefault(); + return e.stopPropagation(); + }, + focus: function(el) { + var focused; + if (focused = $('.focused.entry', Menu.el)) { + $.rmClass(focused, 'focused'); + } + return $.addClass(el, 'focused'); + }, addEntry: function(entry) { - $.addClass(entry.el, 'entry'); + var el, els, _i, _len; + els = $$('*', entry.el); + els.push(entry.el); + for (_i = 0, _len = els.length; _i < _len; _i++) { + el = els[_i]; + $.addClass(el, 'entry'); + $.on(el, 'focus mouseover', function() { + return Menu.focus(this); + }); + } return Menu.entries.push(entry); } }; @@ -4865,16 +4926,18 @@ a[href="javascript:;"] {\ \ #menu {\ position: absolute;\ + outline: none;\ }\ .entry {\ border-bottom: 1px solid rgba(0, 0, 0, .25);\ display: block;\ + outline: none;\ padding: 3px 4px;\ }\ .entry:last-child {\ border: none;\ }\ -.entry:hover, .entry:focus {\ +.focused.entry {\ background: rgba(255, 255, 255, .33);\ }\ \ diff --git a/script.coffee b/script.coffee index 3b4afdee3..f6d9b1ebf 100644 --- a/script.coffee +++ b/script.coffee @@ -825,7 +825,9 @@ Menu = @el = $.el 'div', className: 'reply dialog' id: 'menu' - $.on @el, 'click', (e) -> e.stopPropagation() + tabIndex: 0 + $.on @el, 'click', (e) -> e.stopPropagation() + $.on @el, 'keydown', @keybinds Main.callbacks.push @node node: (post) -> @@ -861,6 +863,7 @@ Menu = entry.open? post $.add el, entry.el + $.addClass $('.entry', Menu.el), 'focused' $.on d, 'click', Menu.close $.add d.body, el @@ -879,16 +882,55 @@ Menu = bLeft + 'px' else bLeft + bRect.width - mRect.width + 'px' + + el.focus() close: -> {el} = Menu $.rm el + if focused = $ '.focused.entry', el + $.rmClass focused, 'focused' el.innerHTML = null el.removeAttribute 'style' delete Menu.lastOpener $.off d, 'click', Menu.close + keybinds: (e) -> + el = $ '.focused.entry', Menu.el + + switch Keybinds.keyCode(e) or e.keyCode + when 'Esc' + Menu.lastOpener.focus() + Menu.close() + when 13, 32 # 'Enter', 'Space' + el.click() + when 'Up' + if next = el.previousElementSibling + Menu.focus next + when 'Right' + if next = el.firstElementChild + Menu.focus next + when 'Down' + if next = el.nextElementSibling + Menu.focus next + when 'Left' + if (next = el.parentNode) and next.id isnt 'menu' + Menu.focus next + else + return + + e.preventDefault() + e.stopPropagation() + focus: (el) -> + if focused = $ '.focused.entry', Menu.el + $.rmClass focused, 'focused' + $.addClass el, 'focused' + addEntry: (entry) -> - $.addClass entry.el, 'entry' + els = $$ '*', entry.el + els.push entry.el + for el in els + $.addClass el, 'entry' + $.on el, 'focus mouseover', -> Menu.focus @ Menu.entries.push entry Keybinds = @@ -3789,16 +3831,18 @@ a[href="javascript:;"] { #menu { position: absolute; + outline: none; } .entry { border-bottom: 1px solid rgba(0, 0, 0, .25); display: block; + outline: none; padding: 3px 4px; } .entry:last-child { border: none; } -.entry:hover, .entry:focus { +.focused.entry { background: rgba(255, 255, 255, .33); }