Keyboard Navigation Manager
The Keyboard Navigation feature provides a comprehensive system for managing focus and enabling accessible keyboard navigation within a group of interactive elements. It allows users to navigate through a list, grid, or collection of items using arrow keys, and optionally, type-ahead functionality. This system is composed of two main directives: LmnKeyboardNavigationManagerDirective
which orchestrates the navigation logic, and LmnKeyboardNavigationItemDirective
which marks individual elements as navigable items within a managed group.
Technical Overview
This feature relies on the interplay between a manager directive and one or more item directives.
LmnKeyboardNavigationManagerDirective
([lmnKeyboardNavigationManager]
): This directive is applied to a container element that groups several navigable items. It listens for keyboard events (primarily arrow keys and alphanumeric keys for type-ahead) on its host element or a specified scopeElement
. Based on its configuration (direction, wrapping, navigation mode), it determines the next item to activate and focus. It manages the tabindex
of its child items, ensuring only the currently "active" item is in the tab order (in 'arrows' mode) or all items are tabbable (in 'tab' mode). It handles the logic for linear navigation, grid navigation (based on row/column attributes on items), and type-ahead matching.
LmnKeyboardNavigationItemDirective
([lmnKeyboardNavigationItem]
): This directive is applied to each individual element that should be part of the keyboard-navigable group managed by a parent LmnKeyboardNavigationManagerDirective
. It marks the element and provides metadata to the manager, such as its disabled state, row/column position (for grid navigation), and a label for type-ahead matching. It sets necessary ARIA-like data attributes on the host element (or a specified target
element) to reflect its state (e.g., active, disabled) and its role within the navigation group. Each item is assigned a unique ID.
The system uses helper functions (e.g., parseCoordinates
) and models (e.g., LmnKeyboardNavigationManagerDirection
, LmnKeyboardNavigationManagerState
) to define behavior and state. Keycodes are defined in keycodes.ts
.
Key Features
- Arrow Key Navigation: Supports navigation using Up, Down, Left, and Right arrow keys.
- Configurable Direction: Navigation can be configured as 'vertical', 'horizontal', or 'both' (for grid-like structures).
- Wrapping: Supports wrapping navigation from the last item to the first, and vice-versa.
- Grid Navigation: Items can be assigned
row
andcolumn
attributes for 2D navigation. - Type-Ahead: Allows users to type characters to quickly jump to items whose
typeAheadLabel
matches the typed string (with a configurable debounce interval). - Navigation Modes:
'arrows'
: Only the active item hastabindex="0"
, others havetabindex="-1"
. Arrow keys move focus.'tab'
: All items havetabindex="0"
, allowing standard tab navigation between them. Arrow key navigation might still be active depending on other settings.
- Disabled Items: Navigation skips items marked as disabled.
- Focus Management: Automatically manages
tabindex
and focuses the active item. - State Management: The manager can be initialized with a default active item/state.
- Accessibility: Applies data attributes to items to signify their role and state (e.g.,
data-lmn-keyboard-navigation-item
,data-lmn-keyboard-navigation-item-active
). - Modifier Key Handling: By default, navigation is ignored if modifier keys (Ctrl, Alt, Meta, Shift) are pressed, but this can be configured with
allowedModifierKeys
. - Scope Control: Navigation can be scoped to a specific element if
scopeElement
is provided to the manager. - RTL Support: Arrow key direction for horizontal navigation adapts to the current page direction (LTR/RTL).
When to Use
Use the Keyboard Navigation Manager and Item directives when:
- You have a list, menu, grid, or any collection of interactive elements that should be navigable using a keyboard in a roving tabindex pattern.
- You need to implement accessible keyboard navigation for custom components like dropdowns, tabs, toolbars, or tree views.
- You want to provide type-ahead functionality for quickly selecting items in a long list.
- You need to manage focus within a group of elements to ensure a predictable and accessible user experience.
Implementation
LmnKeyboardNavigationManagerDirective
Apply this directive to the container element that groups the navigable items.
Selector: [lmnKeyboardNavigationManager]
Inputs:
lmnKeyboardNavigationManagerActive
(aliasactive
): Optional (boolean
, default:true
). Enables or disables the keyboard navigation functionality for the group.lmnKeyboardNavigationManagerDirection
(aliasdirection
): Optional (LmnKeyboardNavigationManagerDirection
, default:'vertical'
). Defines the primary navigation direction:'vertical'
,'horizontal'
, or'both'
.lmnKeyboardNavigationManagerWrap
(aliaswrap
): Optional (boolean
, default:true
). Iftrue
, navigation wraps around from the last item to the first and vice-versa.lmnKeyboardNavigationManagerTypeAhead
(aliastypeAhead
): Optional (boolean
, default:true
). Enables or disables type-ahead functionality.lmnKeyboardNavigationManagerTypeAheadDebounceInterval
(aliastypeAheadDebounceInterval
): Optional (number
, default:300
ms). The debounce interval for type-ahead input.lmnKeyboardNavigationManagerAllowedModifierKeys
(aliasallowedModifierKeys
): Optional (LmnModifierKey[]
, default:[]
). An array of modifier keys (e.g.,'ctrlKey'
,'shiftKey'
) that, if pressed along with a navigation key, will still allow navigation to occur.lmnKeyboardNavigationManagerAllowCrossKeysNavigation
(aliasallowCrossKeysNavigation
): Optional (boolean
, default:true
). Iffalse
and direction is 'vertical', left/right arrows won't navigate. Iffalse
and direction is 'horizontal', up/down arrows won't navigate.lmnKeyboardNavigationManagerNavigationMode
(aliasnavigationMode
): Optional (LmnKeyboardNavigationManagerNavigationMode
, default:'arrows'
). Defines the navigation interaction model:'arrows'
: Roving tabindex with arrow keys.'tab'
: All items are tabbable.
lmnKeyboardNavigationManagerDefaultState
(aliasdefaultState
): Optional (LmnKeyboardNavigationManagerState
). Allows setting an initial active item byactiveItemIndex
(for linear) oractiveItemRow
andactiveItemColumn
(for grid).lmnKeyboardNavigationManagerScopeElement
(aliasscopeElement
): Optional (HTMLElement | (() => HTMLElement)
). If provided, item querying is scoped to this element's direct descendants instead of the manager's host element.
Public Methods:
setActiveItem(index: number, focus: boolean = false): void
: Programmatically sets the active item by its index within the collection of navigable items. Optionally focuses the item.
Host Attributes:
data-lmn-keyboard-navigation-manager
: Set to'true'
or'false'
reflecting theactive
input.
LmnKeyboardNavigationItemDirective
Apply this directive to each individual interactive element within the group managed by LmnKeyboardNavigationManagerDirective
.
Selector: [lmnKeyboardNavigationItem]
Inputs:
lmnKeyboardNavigationItemTarget
(aliastarget
): Optional (HTMLElement
, default: the host element). The actual HTML element that should receive focus and ARIA-like attributes. Useful if the directive is on a wrapper.lmnKeyboardNavigationItemActive
(aliasactive
): Optional (boolean
, default:true
). Indicates if the item is currently considered active (not to be confused with the manager's active item, more like an enabled state for participation).lmnKeyboardNavigationItemDisabled
(aliasdisabled
): Optional (boolean
, default:false
). Iftrue
, this item will be skipped during navigation.lmnKeyboardNavigationItemRow
(aliasrow
): Optional (number
, default:0
). The row index for grid navigation.lmnKeyboardNavigationItemColumn
(aliascolumn
): Optional (number
, default:0
). The column index for grid navigation.lmnKeyboardNavigationItemTypeAheadLabel
(aliastypeAheadLabel
): Optional (string
, default: innerText of the host element). The string used for matching during type-ahead navigation.
Host Attributes (applied to the target
element):
tabindex
: Dynamically set to'-1'
or'0'
by the manager directive.data-lmn-keyboard-navigation-item
: Presence indicates the element is a navigable item.data-lmn-keyboard-navigation-item-active
: Reflects theactive
input ('true'
or'false'
).data-lmn-keyboard-navigation-item-disabled
: Reflects thedisabled
input ('true'
or'false'
).data-lmn-keyboard-navigation-item-row
: Reflects therow
input.data-lmn-keyboard-navigation-item-column
: Reflects thecolumn
input.data-lmn-keyboard-navigation-item-type-ahead-label
: Reflects thetypeAheadLabel
input.data-lmn-keyboard-navigation-item-id
: A unique auto-generated ID for the item.
Related Models and Types
LmnKeyboardNavigationManagerDirection
:'horizontal' | 'vertical' | 'both'
LmnKeyboardNavigationManagerTarget
: Describes the intended navigation movement (e.g.,'next-one-axis'
,'previous-vertical'
).LmnKeyboardNavigationManagerNavigationMode
:'arrows' | 'tab'
LmnKeyboardNavigationManagerState
:{ activeItemIndex?: number; activeItemRow?: number; activeItemColumn?: number; }
LmnModifierKey
:'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey'