Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 65 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
hiddenKeyUX,
hotkeyKeyUX,
jumpKeyUX,
menuKeyUX,
focusGroupKeyUX,
pressKeyUX,
startKeyUX
} from 'keyux'
Expand All @@ -55,7 +55,7 @@ const overrides = {}

startKeyUX(window, [
hotkeyKeyUX(overrides),
menuKeyUX(),
focusGroupKeyUX(),
pressKeyUX('is-pressed'),
jumpKeyUX(),
hiddenKeyUX()
Expand Down Expand Up @@ -217,13 +217,69 @@ with arrow navigation.
Users will use <kbd>Tab</kbd> to get inside the menu, and will use either arrows or <kbd>Home</kbd>,
<kbd>End</kbd> or an item name to navigate inside.

To enable this feature, call `menuKeyUX`.
To enable this feature, call `focusGroupKeyUX`.

```js
import { menuKeyUX } from 'keyux'
import { focusGroupKeyUX } from 'keyux'

startKeyUX(window, [
menuKeyUX()
focusGroupKeyUX()
])
```


### Listbox

The [`role="listbox"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role)
is used for lists from which a user may select one or
more items which are static and, unlike HTML <select> elements, may contain images.

```html
<ul role="listbox">
<li tabindex="0" role="option">Pizza</li>
<li tabindex="0" role="option">Sushi</li>
<li tabindex="0" role="option">Ramen</li>
</ul>
```

Users will use <kbd>Tab</kbd> to get inside the menu, and will use either arrows or <kbd>Home</kbd>,
<kbd>End</kbd> or an item name to navigate inside.

To enable this feature, call `focusGroupKeyUX`.

```js
import { focusGroupKeyUX } from 'keyux'

startKeyUX(window, [
focusGroupKeyUX()
])
```


### Tablist

The [`role="tablist"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tablist_role)
identifies the element that serves as the container for a set of tabs.
The tab content should be marked by `[role="tabpanel']`.

```html
<div role="tablist">
<button role="tab">Home</button>
<button role="tab">About</button>
<button role="tab">Contact</button>
</div>
```

Users will use <kbd>Tab</kbd> to get inside the menu, and will use either arrows or <kbd>Home</kbd>,
<kbd>End</kbd>.

To enable this feature, call `focusGroupKeyUX`.

```js
import { focusGroupKeyUX } from 'keyux'

startKeyUX(window, [
focusGroupKeyUX()
])
```

Expand Down Expand Up @@ -260,10 +316,10 @@ You can add `aria-controls` to `<input>` to make the focus jump on <kbd>Enter</k
To enable this feature, call `jumpKeyUX`.

```js
import { menuKeyUX, jumpKeyUX } from 'keyux'
import { focusGroupKeyUX, jumpKeyUX } from 'keyux'

startKeyUX(window, [
menuKeyUX(),
focusGroupKeyUX(),
jumpKeyUX()
])
```
Expand Down Expand Up @@ -294,10 +350,10 @@ have to set `tabindex="-1"` manually.
To enable this feature, call `hiddenKeyUX`.

```js
import { menuKeyUX, jumpKeyUX, hiddenKeyUX } from 'keyux'
import { focusGroupKeyUX, jumpKeyUX, hiddenKeyUX } from 'keyux'

startKeyUX(window, [
menuKeyUX(),
focusGroupKeyUX(),
jumpKeyUX(),
hiddenKeyUX()
])
Expand Down
59 changes: 42 additions & 17 deletions menu.js → focus-group.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export function menuKeyUX(options) {
const ROLES = {
'menuitem': ["menu", "menubar"],
'option': ["listbox"],
'tab': ["tablist"]
}

export function focusGroupKeyUX(options) {
return window => {
let inMenu = false
let inGroup = false
let typingDelayMs = options?.searchDelayMs || 300
let lastTyped = 0
let searchPrefix = ''
Expand All @@ -11,21 +17,41 @@ export function menuKeyUX(options) {
current.tabIndex = -1
}

function findGroupNodeByEventTarget(eventTarget) {
let itemRole = eventTarget.role
let groupRoles = ROLES[itemRole]
if (!groupRoles) return null

for (let role of groupRoles) {
let node = eventTarget.closest(`[role=${role}]`)
if (node) return node
}
}

function isHorizontalOrientation(group) {
let ariaOrientation = group.getAttribute('aria-orientation')
if (ariaOrientation === "vertical") return false
if (ariaOrientation === "horizontal") return true

let role = group.role
return role === "menubar" || role === "tablist";
}


function keyDown(event) {
if (event.target.role !== 'menuitem') {
let group = findGroupNodeByEventTarget(event.target);

if (!group) {
stop()
return
}

let menu = event.target.closest('[role="menu"]')
if (!menu) return

let items = menu.querySelectorAll('[role="menuitem"]')
let items = group.querySelectorAll(`[role=${event.target.role}]`)
let index = Array.from(items).indexOf(event.target)

let nextKey = 'ArrowDown'
let prevKey = 'ArrowUp'
if (menu.getAttribute('aria-orientation') === 'horizontal') {
if (isHorizontalOrientation(group)) {
if (window.document.dir === 'rtl') {
nextKey = 'ArrowLeft'
prevKey = 'ArrowRight'
Expand All @@ -47,7 +73,7 @@ export function menuKeyUX(options) {
} else if (event.key === 'End') {
event.preventDefault()
focus(event.target, items[items.length - 1])
} else if (event.key.length === 1) {
} else if (event.key.length === 1 && group.role !== "tablist") {
let now = Date.now()
if (now - lastTyped <= typingDelayMs) {
searchPrefix += event.key.toLowerCase()
Expand All @@ -70,26 +96,25 @@ export function menuKeyUX(options) {
}

function stop() {
inMenu = false
inGroup = false
window.removeEventListener('keydown', keyDown)
}

function focusIn(event) {
if (event.target.role === 'menuitem') {
let menu = event.target.closest('[role="menu"]')
if (!menu) return
let group = findGroupNodeByEventTarget(event.target);
if (group) {

if (!inMenu) {
inMenu = true
if (!inGroup) {
inGroup = true
window.addEventListener('keydown', keyDown)
}
let items = menu.querySelectorAll('[role="menuitem"]')
let items = group.querySelectorAll(`[role=${event.target.role}]`)
for (let item of items) {
if (item !== event.target) {
item.setAttribute('tabindex', -1)
}
}
} else if (inMenu) {
} else if (inGroup) {
stop()
}
}
Expand Down
12 changes: 6 additions & 6 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface KeyUXModule {
(window: MinimalWindow): () => void
}

export interface MenuKeyUXOptions {
export interface FocusGroupKeyUXOptions {
/**
* Maximum allowed pause between key presses when searching
* for a list item by name. Default is 300.
Expand All @@ -55,14 +55,14 @@ export function hotkeyKeyUX(overrides?: HotkeyOverride): KeyUXModule
* Add arrow-navigation on `role="menu"`.
*
* ```js
* import { startKeyUX, menuKeyUX } from 'keyux'
* import { startKeyUX, focusGroupKeyUX } from 'keyux'
*
* startKeyUX(window, [
* menuKeyUX()
* focusGroupKeyUX()
* ])
* ```
*/
export function menuKeyUX(options?: MenuKeyUXOptions): KeyUXModule
export function focusGroupKeyUX(options?: FocusGroupKeyUXOptions): KeyUXModule

/**
* Add pressed style on button activation from keyboard.
Expand Down Expand Up @@ -111,15 +111,15 @@ export function hiddenKeyUX(): KeyUXModule
* import {
* startKeyUX,
* hotkeyKeyUX,
* menuKeyUX,
* focusGroupKeyUX,
* pressKeyUX,
* jumpKeyUX,
* hiddenKeyUX
* } from 'keyux'
*
* startKeyUX(window, [
* hotkeyKeyUX(),
* menuKeyUX(),
* focusGroupKeyUX(),
* pressKeyUX('is-pressed'),
* jumpKeyUX(),
* hiddenKeyUX()
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export * from './focus-group.js'
export * from './hotkey.js'
export * from './hidden.js'
export * from './press.js'
export * from './menu.js'
export * from './jump.js'

export function startKeyUX(window, plugins) {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@
{
"name": "All modules",
"import": {
"./index.js": "{ startKeyUX, hotkeyKeyUX, pressKeyUX, menuKeyUX, jumpKeyUX, hiddenKeyUX, likelyWithKeyboard, getHotKeyHint }"
"./index.js": "{ startKeyUX, hotkeyKeyUX, pressKeyUX, focusGroupKeyUX, jumpKeyUX, hiddenKeyUX, likelyWithKeyboard, getHotKeyHint }"
},
"limit": "1737 B"
"limit": "1853 B"
}
],
"clean-publish": {
Expand Down
13 changes: 13 additions & 0 deletions test/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@
border-radius: 2px;
margin: 0.5em 0;
}
.tablist_container {
margin-top: 2em;
margin-bottom: 2em;
}
.tablist_tab {
margin-right: 0.5em;
}
.tabcontent {
display: none;
}
.tabcontent--current {
display: block;
}
</style>
</head>
<body>
Expand Down
Loading