tests / browser (chrome, chromium) (push) Successful in 3m34s
tests / browser (safari, webkit) (push) Successful in 6m2s
tests / lint (push) Successful in 59s
tests / feature (8.4) (push) Successful in 1m6s
tests / feature (8.5) (push) Successful in 1m9s
tests / browser (firefox, firefox) (push) Successful in 4m20s
M3 Expressive top app bars (small, centered, medium and large flexible, search) that collapse with CSS sticky, docked and floating toolbars, primary and secondary tabs with a view-transition indicator, section navigation, the account menu and theme toggles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
40 lines
1.6 KiB
JavaScript
40 lines
1.6 KiB
JavaScript
/**
|
|
* `materialToolbar`: the arrow keys move focus between the controls of an `<x-toolbar>`, as
|
|
* WAI-ARIA's toolbar pattern has them (left and right, or up and down when it is vertical; mirrored
|
|
* on a right-to-left page; Home and End to its ends). Every control stays in the Tab order, so a
|
|
* control added by a Livewire render is reachable without any bookkeeping.
|
|
*/
|
|
const CONTROLS = 'button:not([disabled]), a[href], input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
|
|
document.addEventListener('alpine:init', () => {
|
|
window.Alpine.data('materialToolbar', (vertical = false) => ({
|
|
move(event) {
|
|
const keys = vertical ? { ArrowUp: -1, ArrowDown: 1 } : { ArrowLeft: -1, ArrowRight: 1 }
|
|
const controls = [...this.$root.querySelectorAll(CONTROLS)].filter((control) => control.getClientRects().length > 0)
|
|
const at = controls.indexOf(document.activeElement)
|
|
|
|
if (at === -1 || controls.length === 0) {
|
|
return
|
|
}
|
|
|
|
let next = null
|
|
|
|
if (event.key === 'Home') {
|
|
next = controls[0]
|
|
} else if (event.key === 'End') {
|
|
next = controls.at(-1)
|
|
} else if (event.key in keys) {
|
|
const rtl = !vertical && getComputedStyle(this.$root).direction === 'rtl'
|
|
const by = keys[event.key] * (rtl ? -1 : 1)
|
|
|
|
next = controls[(at + by + controls.length) % controls.length]
|
|
}
|
|
|
|
if (next) {
|
|
event.preventDefault()
|
|
next.focus()
|
|
}
|
|
},
|
|
}))
|
|
})
|