# useListFocus

## Usage

Manages keyboard navigation within a linear list following WAI-ARIA menu, listbox, and toolbar patterns. Supports arrow key navigation (vertical, horizontal, or both), Home/End for boundaries, optional wrap-around, RTL, and Escape to close. Opt into `hasRovingTabIndex` for composite widgets (toolbars, segmented controls, tab strips) that own a single tab stop. Suitable for dropdown menus, toolbars, and any 1D focusable list.

Attach `listRef` to the container, `handleKeyDown` to its keys, and `handleFocus` to its focus event. `handleFocus` is a no-op unless `hasRovingTabIndex` is on, so it is always safe to attach.

```ts
import { useListFocus } from '@astryx-vue/core'
```

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Set `orientation` to `'horizontal'` for toolbars and tab bars, `'vertical'` for dropdown menus. |
| Do | Provide an `onEscape` callback for menus/dropdowns to return focus to the trigger. |
| Do | Enable `hasRovingTabIndex` (and `hasCaretGuard` when the widget can contain text inputs) for toolbar-style composites that should be a single tab stop. |
| Don't | Use for 2D grid navigation; prefer `useGridRovingFocus` for grids and calendars. |

## Parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | `UseListFocusOptions` | `{}` | Configuration object for the list focus behavior. All fields are optional. |
| `options.itemSelector` | `string` | `'[role="menuitem"]'` | Selector for the focusable items within the list. |
| `options.boundarySelector` | `string` | — | Selector identifying a list boundary, for lists that contain nested lists of the same kind (e.g. a menu with submenu flyouts). When set, item collection and key handling are scoped to this level's own container. Typically `'[role="menu"]'`. |
| `options.wrap` | `boolean` | `true` | Whether arrow navigation wraps around at the ends. |
| `options.onEscape` | `() => void` | — | Callback when Escape is pressed (e.g. close the menu). Supplying it also consumes the key (`preventDefault`); without it Escape passes through to the surrounding layer. |
| `options.orientation` | `'horizontal' \| 'vertical' \| 'both'` | `'vertical'` | Navigation orientation. `'horizontal'` uses ArrowLeft/ArrowRight, `'vertical'` uses ArrowUp/ArrowDown, `'both'` accepts all four arrows. A horizontal list follows the container's writing direction, so RTL swaps the two arrow keys. |
| `options.hasHomeEnd` | `boolean` | `true` | Whether Home/End jump to the first/last enabled item. |
| `options.hasPaging` | `boolean` | `false` | Whether PageDown/PageUp page through a scrolling list: PageDown moves to the last enabled item fully visible in the container, and pressed there again one viewport further; PageUp mirrors it. Neither wraps; in a list that does not scroll they act as End/Home. |
| `options.hasRovingTabIndex` | `boolean` | `false` | Opt into roving-tabindex ownership: the hook stamps a single tab stop (one item `tabindex="0"`, the rest `-1`), repairs it as items mount, unmount, or toggle disabled, and moves it with arrow navigation. When false, the hook only moves focus and never touches `tabindex`. |
| `options.hasCaretGuard` | `boolean` | `false` | When true, arrow keys are not stolen from a nested text input/textarea whose caret is not at the boundary in the direction of travel (or that has a selection), and are never stolen from a nested contenteditable. Preserves inline text editing within the list. |

## Returns

| Name | Type | Description |
| --- | --- | --- |
| `listRef` | `Ref` | Bind to the list container element. |
| `handleKeyDown` | `(event: KeyboardEvent) => void` | Key down handler to attach to the list container. |
| `handleFocus` | `(event: FocusEvent) => void` | Focus handler for the container. Keeps the roving tab stop in sync when `hasRovingTabIndex` is enabled; a no-op otherwise, so it is always safe to attach. |
| `focusItem` | `(index: number) => void` | Focus a specific item by index, clamped to the valid range. |
| `focusFirst` | `() => boolean` | Focus the first enabled item. Returns true when an item was focused. |
| `focusLast` | `() => boolean` | Focus the last enabled item. Returns true when an item was focused. |
| `ownsEvent` | `(event: KeyboardEvent) => boolean` | Whether a key event belongs to this list level rather than a nested list sharing the same `boundarySelector`. Always true when no `boundarySelector` is set. Use it to guard consumer-added key handling (Enter/Space, typeahead). |
| `getItems` | `() => HTMLElement[]` | This level's focusable items in DOM order, already scoped by `boundarySelector` and with hidden ones dropped. Build typeahead targets from this instead of re-querying. |
