refactor(sw): share roving focus and outside pointer handling
- components/shared/roving-focus.ts (moveRovingFocus): the same Arrow / Home / End algorithm of SWFooter, SWToolGroup, the SWViewerHeader tabs and SWHomeMenu; each caller passes its own keys, wrap and select behavior. - components/shared/use-outside-pointer.ts (useOutsidePointerDown): the same pointerdown effect of the SWHeader dropdowns, layer menu, document title and More. Escape, focus return and positioning stay in each component. Both modules are internal: the package entry and the public declarations are unchanged.
This commit is contained in:
1 parent
af7f98d66b
commit
7cc3aa1ff8
10 files changed
+55
-69
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { forwardRef, useId, useLayoutEffect, useRef, useState, type ButtonHTMLAttributes, type HTMLAttributes, type KeyboardEvent, type ReactNode } from "react"
|
||||
import { moveRovingFocus } from "../shared/roving-focus"
|
||||
import { SWViewerMenu } from "./SWViewerMenu"
|
||||
import { SWViewerSwitch } from "./SWViewerControls"
|
||||
import { SWViewerIcon } from "./SWViewerIcon"
|
||||
@@ -111,15 +112,7 @@ function MenuButton({ item, iconOnly, onCommand }: { item: Extract<SWViewerHeade
|
||||
function Tabs({ item, onCommand }: { item: Extract<SWViewerHeaderItem, { type: "tabs" }>; onCommand: (c: SWViewerCommand) => void }) {
|
||||
const select = (value: string) => { item.onValueChange?.(value); onCommand({ id: item.id, itemId: item.id, value }) }
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
const keys = ["ArrowRight", "ArrowLeft", "ArrowDown", "ArrowUp", "Home", "End"]
|
||||
if (!keys.includes(event.key)) return
|
||||
const buttons = Array.from(event.currentTarget.querySelectorAll<HTMLButtonElement>('[role="radio"]:not(:disabled)'))
|
||||
const index = buttons.indexOf(document.activeElement as HTMLButtonElement)
|
||||
if (index < 0) return
|
||||
event.preventDefault()
|
||||
const next = event.key === "Home" ? 0 : event.key === "End" ? buttons.length - 1 : (index + (event.key === "ArrowRight" || event.key === "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length
|
||||
buttons[next].focus()
|
||||
buttons[next].click()
|
||||
moveRovingFocus(event, Array.from(event.currentTarget.querySelectorAll<HTMLButtonElement>('[role="radio"]:not(:disabled)')), { vertical: true, select: true })
|
||||
}
|
||||
const current = item.options.some(option => option.value === item.value) ? item.value : item.options.find(option => !option.disabled)?.value
|
||||
return (
|
||||
|
||||
Reference in new issue
Block a user