Add core:focus-next/focus-previous

Focus now cycles between elements with a positive tabindex on
tab and shift-tab.
This commit is contained in:
Kevin Sawicki & Nathan Sobo
2013-05-16 15:00:55 -07:00
parent 76e7161608
commit 001183245e
3 changed files with 134 additions and 2 deletions

View File

@@ -1,4 +1,5 @@
$ = require 'jquery'
{$$} = require 'space-pen'
fsUtils = require 'fs-utils'
{less} = require 'less'
WindowEventHandler = require 'window-event-handler'
@@ -235,3 +236,81 @@ describe "Window", ->
ChildProcess.spawn.reset()
$("<a href='#scroll-me'>link</a>").appendTo(document.body).click().remove()
expect(ChildProcess.spawn).not.toHaveBeenCalled()
describe "core:focus-next and core:focus-previous", ->
describe "when there is no currently focused element", ->
it "focuses the element with the lowest/highest tabindex", ->
elements = $$ ->
@div =>
@button tabindex: 2
@input tabindex: 1
elements.attachToDom()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=1]:focus")).toExist()
$(":focus").blur()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=2]:focus")).toExist()
describe "when a tabindex is set on the currently focused element", ->
it "focuses the element with the next highest tabindex", ->
elements = $$ ->
@div =>
@input tabindex: 1
@button tabindex: 2
@button tabindex: 5
@input tabindex: -1
@input tabindex: 3
@button tabindex: 7
elements.attachToDom()
elements.find("[tabindex=1]").focus()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=2]:focus")).toExist()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=3]:focus")).toExist()
elements.focus().trigger "core:focus-next"
expect(elements.find("[tabindex=5]:focus")).toExist()
elements.focus().trigger "core:focus-next"
expect(elements.find("[tabindex=7]:focus")).toExist()
elements.focus().trigger "core:focus-next"
expect(elements.find("[tabindex=1]:focus")).toExist()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=7]:focus")).toExist()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=5]:focus")).toExist()
elements.focus().trigger "core:focus-previous"
expect(elements.find("[tabindex=3]:focus")).toExist()
elements.focus().trigger "core:focus-previous"
expect(elements.find("[tabindex=2]:focus")).toExist()
elements.focus().trigger "core:focus-previous"
expect(elements.find("[tabindex=1]:focus")).toExist()
it "skips disabled elements", ->
elements = $$ ->
@div =>
@input tabindex: 1
@button tabindex: 2, disabled: 'disabled'
@input tabindex: 3
elements.attachToDom()
elements.find("[tabindex=1]").focus()
elements.trigger "core:focus-next"
expect(elements.find("[tabindex=3]:focus")).toExist()
elements.trigger "core:focus-previous"
expect(elements.find("[tabindex=1]:focus")).toExist()

View File

@@ -67,10 +67,10 @@
# allow standard input fields to work correctly
'input:not(.hidden-input)':
'tab': 'core:focus-next'
'shift-tab': 'core:focus-previous'
'left': 'native!'
'right': 'native!'
'tab': 'native!'
'shift-tab': 'native!'
'shift-left': 'native!'
'shift-right': 'native!'
'backspace': 'native!'
@@ -81,3 +81,7 @@
'meta-x': 'native!'
'meta-c': 'native!'
'meta-v': 'native!'
'button':
'tab': 'core:focus-next'
'shift-tab': 'core:focus-previous'

View File

@@ -15,6 +15,9 @@ class WindowEventHandler
window.close()
@subscribeToCommand $(window), 'window:reload', => reload()
@subscribeToCommand $(document), 'core:focus-next', @focusNext
@subscribeToCommand $(document), 'core:focus-previous', @focusPrevious
@subscribe $(document), 'keydown', keymap.handleKeyEvent
@subscribe $(document), 'drop', onDrop
@@ -31,4 +34,50 @@ class WindowEventHandler
require('child_process').spawn('open', [location])
false
eachTabIndexedElement: (callback) ->
for element in $('[tabindex]')
element = $(element)
continue if element.attr('disabled')
tabIndex = parseInt(element.attr('tabindex'))
continue unless tabIndex >= 0
callback(element, tabIndex)
focusNext: =>
focusedTabIndex = parseInt($(':focus').attr('tabindex')) or -Infinity
nextElement = null
nextTabIndex = Infinity
lowestElement = null
lowestTabIndex = Infinity
@eachTabIndexedElement (element, tabIndex) ->
if tabIndex < lowestTabIndex
lowestTabIndex = tabIndex
lowestElement = element
if focusedTabIndex < tabIndex < nextTabIndex
nextTabIndex = tabIndex
nextElement = element
(nextElement ? lowestElement).focus()
focusPrevious: =>
focusedTabIndex = parseInt($(':focus').attr('tabindex')) or Infinity
previousElement = null
previousTabIndex = -Infinity
highestElement = null
highestTabIndex = -Infinity
@eachTabIndexedElement (element, tabIndex) ->
if tabIndex > highestTabIndex
highestTabIndex = tabIndex
highestElement = element
if focusedTabIndex > tabIndex > previousTabIndex
previousTabIndex = tabIndex
previousElement = element
(previousElement ? highestElement).focus()
_.extend WindowEventHandler.prototype, Subscriber