useGridFocus @astryx-svelte/core v0.3.0 · hooks

Usage

Manages keyboard navigation within a 2D grid following the WAI-ARIA grid pattern. Supports arrow keys for cell-to-cell navigation, Home/End for row boundaries, Ctrl+Home/Ctrl+End for grid boundaries, and Page Up/Down for custom callbacks (e.g., month navigation in calendars). Boundary navigation callbacks allow cross-grid navigation.
ts
import { hooks } from '@astryx-svelte/core/hooks';

Best practices

GuidancePractices
DoUse for calendar date grids: wire onPageUp/onPageDown to month navigation and onNavigateBefore/onNavigateAfter for cross-month arrow key navigation.
DoAttach both gridRef and handleKeyDown to the grid container element.
DoFor roving-tabindex grids (e.g. Calendar), set hasRovingTabIndex: true and attach handleFocus to the container onFocus; seed one focus target with tabindex=0 and the hook repairs and moves it.
Don'tUse for simple linear lists; prefer useListFocus for 1D navigation.

Parameters

ParameterTypeDefaultDescription
options Required () => UseGridFocusOptionsConfiguration object for grid focus behavior.
options.columns Required numberNumber of columns in the grid. Used for up/down navigation (moves by this many cells).
options.cellSelector string'button:not([disabled]), [tabindex]:not([tabindex="-1"])'Selector for cells within the grid. Should match ALL cell positions in DOM order (including disabled/empty) so grid geometry is preserved.
options.isCellFocusable (cell: HTMLElement) => booleanPredicate for whether a matched cell can receive focus. Omit to treat every matched cell as focusable.
options.getFocusTarget (cell: HTMLElement) => HTMLElement | nullResolves the element to focus for a cell, e.g. a button inside a role="gridcell" wrapper. Omit to focus the cell itself.
options.onNavigateBefore (column: number, offset: number) => voidCallback when navigation would go before the first cell. Receives the column index and offset (1 for horizontal, columns for vertical).
options.onNavigateAfter (column: number, offset: number) => voidCallback when navigation would go after the last cell. Receives the column index and offset.
options.onPageUp () => voidCallback for Page Up key (e.g., navigate to previous month in calendars).
options.onPageDown () => voidCallback for Page Down key (e.g., navigate to next month in calendars).
options.isRtl booleanundefined (auto-detect from the container's computed direction)Swap ArrowLeft/ArrowRight so horizontal navigation follows visual direction in right-to-left contexts. When omitted, auto-detected from the container computed direction on keydown.
options.hasRovingTabIndex booleanfalseOwn a single roving tab stop across the grid: one focusable cell (its resolved focus target) carries tabindex="0", the rest -1. Stamped/repaired on render and moved with arrow navigation. Attach the returned handleFocus to the container onFocus.

Returns

FieldTypeDefaultDescription
gridRef HTMLElement | nullRef to attach to the grid container element.

Element references are attachments here. The port returns an Attachment<HTMLElement> named attach…; spread it onto the element instead of assigning a ref.Upstream declares React.RefObject<HTMLElement | null>.

handleKeyDown (event: KeyboardEvent) => voidKey down handler to attach to the grid container.
handleFocus (event: FocusEvent) => voidFocus handler for the grid container. Keeps the roving tab stop in sync when hasRovingTabIndex is enabled; a no-op otherwise, so always safe to attach.
focusCell (index: number) => voidFocus a specific cell by index (clamped to valid range).
focusFirst () => voidFocus the first focusable cell in the grid.
focusLast () => voidFocus the last focusable cell in the grid.