diff --git a/.changeset/tame-ads-heal.md b/.changeset/tame-ads-heal.md new file mode 100644 index 00000000..484f8a03 --- /dev/null +++ b/.changeset/tame-ads-heal.md @@ -0,0 +1,5 @@ +--- +"bits-ui": patch +--- + +Radio Group: allow both arrow up/down and left/right per WAI-ARIA spec diff --git a/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts b/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts index 44f46ec4..8af6d621 100644 --- a/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts +++ b/packages/bits-ui/src/lib/bits/radio-group/radio-group.svelte.ts @@ -119,7 +119,7 @@ class RadioGroupItemState { }; #onkeydown = (e: KeyboardEvent) => { - this.#root.rovingFocusGroup.handleKeydown(this.#ref.current, e); + this.#root.rovingFocusGroup.handleKeydown(this.#ref.current, e, true); }; #tabIndex = $derived.by(() => this.#root.rovingFocusGroup.getTabIndex(this.#ref.current)); diff --git a/packages/bits-ui/src/lib/internal/use-roving-focus.svelte.ts b/packages/bits-ui/src/lib/internal/use-roving-focus.svelte.ts index 31e73586..4093c3b9 100644 --- a/packages/bits-ui/src/lib/internal/use-roving-focus.svelte.ts +++ b/packages/bits-ui/src/lib/internal/use-roving-focus.svelte.ts @@ -74,7 +74,11 @@ export function useRovingFocus(props: UseRovingFocusProps) { items[0]?.focus(); } - function handleKeydown(node: HTMLElement | null | undefined, e: KeyboardEvent) { + function handleKeydown( + node: HTMLElement | null | undefined, + e: KeyboardEvent, + both: boolean = false + ) { const rootNode = document.getElementById(props.rootNodeId.current); if (!rootNode || !node) return; @@ -94,6 +98,13 @@ export function useRovingFocus(props: UseRovingFocusProps) { [kbd.END]: items.length - 1, }; + if (both) { + const altNextKey = nextKey === kbd.ARROW_DOWN ? kbd.ARROW_RIGHT : kbd.ARROW_DOWN; + const altPrevKey = prevKey === kbd.ARROW_UP ? kbd.ARROW_LEFT : kbd.ARROW_UP; + keyToIndex[altNextKey] = currentIndex + 1; + keyToIndex[altPrevKey] = currentIndex - 1; + } + let itemIndex = keyToIndex[e.key]; if (itemIndex === undefined) return; e.preventDefault();