ListComponent
Accessible list component with keyboard navigation, single- and multi-selection, and flexible delegation of click- and selection-handling to the parent.
The component renders as an ARIA listbox and delegates keyboard focus tracking to Angular CDK’s ActiveDescendantKeyManager. Content is projected via [yuvListItem]-attributed children (see ListItemDirective).
Key Features:
-
Single and multi-selection (with Shift / Ctrl modifier support)
-
Full keyboard navigation (Arrow keys, Space, Enter, Escape)
-
Horizontal and vertical layout via the
horizontalhost attribute -
Auto-selection on initialization via the
autoSelectinput -
Selection guarding via
preventChangeUntilcallback -
Optional delegation of click and selection handling to the parent component
-
Accessible:
role="listbox", active-descendant focus management,aria-selectedon items
Basic usage:
Example :
<yuv-list (itemSelect)="onItemSelect($event)">
<div yuvListItem>Entry #1</div>
<div yuvListItem>Entry #2</div>
</yuv-list>Multi-selection with Shift/Ctrl:
Example :
<yuv-list [multiselect]="true" (itemSelect)="onSelect($event)">
@for (item of items; track item.id) {
<div yuvListItem>{{ item.label }}</div>
}
</yuv-list>Host Attributes (set declaratively in the template):
-
selectOnEnter— treat the Enter key as a selection trigger (in addition to Space) -
horizontal— switch key navigation to left/right arrows instead of up/down
Example :
<!-- Horizontal list, Enter key selects -->
<yuv-list horizontal selectOnEnter (itemSelect)="onSelect($event)">
<button yuvListItem>A</button>
<button yuvListItem>B</button>
</yuv-list>Component Metadata
Section titled “Component Metadata”Selector: yuv-list
Standalone: Yes
Implements: OnDestroy
Inputs
Section titled “Inputs”autoSelect
Section titled “autoSelect”Type: boolean, BooleanInput
Default Value: false, \{ transform: (value: BooleanInput) => coerceBooleanProperty(value) \}
Automatically selects an item when the list is first rendered.The selection logic runs once per content-children change (see #itemsEffect) and follows this priority order:1. The first non-disabled item that already carries the selected attribute.2. If no such item exists and autoSelect is true, the item at index 0.Accepts any truthy string value in addition to a real boolean because the input is also consumable as a plain HTML attribute (<yuv-list autoSelect>).
disableSelection
Section titled “disableSelection”Type: boolean
Default Value: false
Puts the list into a display-only mode where no item can be selected.When true, all selection paths are blocked: clicks, keyboard shortcuts (Space, Enter, Escape), and programmatic calls to select(), multiSelect(), and clear() are all no-ops. The list still renders normally and keyboard navigation still moves the focus indicator.
multiselect
Section titled “multiselect”Type: boolean
Default Value: false
Enables multi-selection mode.When true, the user can hold Shift to range-select items between the last selected item and the clicked one, or hold Ctrl to toggle individual items in and out of the selection. The programmatic multiSelect() method is also only effective when this input is true.
preventChangeUntil
Section titled “preventChangeUntil”Type: () => boolean
Default Value: () => false
Guard function that temporarily blocks all selection changes.Provide a factory that returns a predicate; as long as that predicate returns true, any attempt to change the selection (via click, keyboard, or programmatic API) is silently ignored. Useful when the parent has unsaved changes tied to the current selection and needs to prevent the user from accidentally navigating away.Example :
// Block selection while a save is in progresspreventChangeUntil = () => () => this.isSaving();selfHandleClick
Section titled “selfHandleClick”Type: boolean
Default Value: false
Disables the built-in click-to-select behavior, letting the parent component decide when select() is called.By default the list attaches an onClick handler to every ListItemDirective that calls select() with the correct Shift/Ctrl modifier flags. Set this input to true to suppress that wiring — the parent must then call select() imperatively in response to whatever interaction it chooses (e.g. a single-click that is distinguished from a double-click by ClickDoubleDirective).
selfHandleSelection
Section titled “selfHandleSelection”Type: boolean
Default Value: false
Delegates visual selection and focus state rendering to the parent component.When false (default), yuv-list applies .selected and .active CSS classes to items via ListItemDirective signals, so the list stylesheet controls the look. When true, those signals are still updated but the host gets the self-handle-selection CSS class, which the parent can use to opt into its own visual treatment — useful when item templates have custom selected/focused styling.
Outputs
Section titled “Outputs”itemFocus
Section titled “itemFocus”Type: number
Emits the zero-based index of the item that received keyboard focus (i.e. the active descendant managed by ActiveDescendantKeyManager).Note that focus and selection are independent: navigating with arrow keys moves focus without changing the selection until Space (or Enter when selectOnEnter is set) is pressed.
itemSelect
Section titled “itemSelect”Type: number[]
Emits the current selection as an array of zero-based item indices whenever the selection changes — via click, keyboard, or programmatic API calls.An empty array signals that the selection has been cleared.Example :
onItemSelect(indices: number[]): void { this.selectedItems = indices.map(i => this.items[i]);}Properties
Section titled “Properties”horizontal
Section titled “horizontal”Type: boolean
Default Value: (inject(new HostAttributeToken('horizontal'), { optional: true }) || 'false') === 'true'
When true, switches the ActiveDescendantKeyManager to horizontal mode so that the Left / Right arrow keys navigate between items instead of Up / Down.Resolved once at construction time from the static horizontal host attribute. The text direction of the document is respected automatically (RTL-aware via CDK Directionality).Set declaratively in the template: <yuv-list horizontal>.
Type: unknown
Default Value: contentChildren(ListItemDirective)
All [yuvListItem] children projected into this list.Queried reactively via contentChildren — every time the projected content changes (items added, removed, or reordered), the #itemsEffect re-runs, rebuilds the key manager, and re-attaches click handlers.
selectOnEnter
Section titled “selectOnEnter”Type: boolean
Default Value: (inject(new HostAttributeToken('selectOnEnter'), { optional: true }) || 'false') === 'true'
When true, pressing Enter triggers item selection in addition to Space.Resolved once at construction time from the static selectOnEnter host attribute. Useful in contexts where Enter has a conventional “confirm” meaning (e.g. search result lists, command palettes).Set declaratively in the template: <yuv-list selectOnEnter>.
Methods
Section titled “Methods”Clears the current selection and resets the active keyboard-focus index.If the selection is already empty, the method is a no-op (no event emitted, no state update). The preventChangeUntil guard is respected — if the guard returns true, the clear is blocked entirely.Also triggered internally when the user presses Escape.Example :
when the parent needs to reset state programmatically without triggering downstream reactions.clear(silent: unknown): voidArguments
Section titled “Arguments”| Name | Type | Description |
|---|---|---|
| silent | unknown |
Transfers DOM focus to the list host element.Calling this ensures that subsequent keyboard events (arrow keys, Space, etc.) are routed to the list’s (keydown) handler. Called internally by setActiveItem(), but also useful from the parent when programmatic focus management is needed (e.g. after closing a dialog that was triggered from a list item).
focus(): voidmultiSelect
Section titled “multiSelect”Programmatically replaces the entire selection with the given indices.Only effective when multiselect is true and disableSelection is false. Out-of-range indices are silently discarded. The resulting selection is sorted ascending before being applied and emitted.Use this when the parent needs to restore a previously saved multi-selection state, e.g. after navigation or on component re-initialization.
multiSelect(index: number[]): voidArguments
Section titled “Arguments”| Name | Type | Description |
|---|---|---|
| index | number[] |
scrollToTop
Section titled “scrollToTop”Smoothly scrolls the list container back to the top.Useful after programmatically refreshing the list contents when the user may have scrolled far down and the new result set should be shown from the beginning.
scrollToTop(): voidselect
Section titled “select”Selects the item at the given index, optionally extending or toggling the existing selection using modifier-key semantics.* No modifiers (default): replaces the current selection with the single item.* shiftKey = true (multiselect only): range-selects all items between the last selected index and index (inclusive of neither endpoint, matching typical OS list behavior).* ctrlKey = true (multiselect only): toggles index in the selection without affecting the rest.Index is clamped to [0, items.length - 1]. Negative values and calls while disableSelection is true are ignored. The preventChangeUntil guard is also respected.Emits itemSelect after updating the visual state.
select(index: number, shiftKey: unknown, ctrlKey: unknown): voidArguments
Section titled “Arguments”| Name | Type | Description |
|---|---|---|
| index | number | |
| shiftKey | unknown | |
| ctrlKey | unknown |
setActiveItem
Section titled “setActiveItem”Moves keyboard focus to the item at the given index and selects it.Combines three operations in one call: updating the CDK key manager’s active descendant (which drives the ARIA active-descendant attribute and the visual focus ring), selecting the item, and transferring DOM focus to the list host element so that subsequent keyboard events are processed correctly.Safe to call before the key manager is initialized — the guard at the top of the method prevents errors during early lifecycle phases.
setActiveItem(index: number): voidArguments
Section titled “Arguments”| Name | Type | Description |
|---|---|---|
| index | number |
shiftSelectionBy
Section titled “shiftSelectionBy”Shifts all selected and focused item indices by the given offset.Use this when items are prepended or inserted at the beginning of the list so that the existing selection and keyboard-focus position stay attached to the same logical items after the index space shifts. A positive offset moves indices forward (items were inserted before the selection); a negative offset moves them backward (items were removed before the selection).Does not emit itemSelect — the selection indices change structurally, not because the user chose different items.
shiftSelectionBy(offset: number): voidArguments
Section titled “Arguments”| Name | Type | Description |
|---|---|---|
| offset | number |