Skip to content

ListComponent

component

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 horizontal host attribute

  • Auto-selection on initialization via the autoSelect input

  • Selection guarding via preventChangeUntil callback

  • Optional delegation of click and selection handling to the parent component

  • Accessible: role="listbox", active-descendant focus management, aria-selected on 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>

Selector: yuv-list

Standalone: Yes

Implements: OnDestroy

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>).

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.

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.

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();

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).

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.

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.

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]);}

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.

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>.

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): void
NameTypeDescription
silentunknown

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(): void

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[]): void
NameTypeDescription
indexnumber[]

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(): void

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): void
NameTypeDescription
indexnumber
shiftKeyunknown
ctrlKeyunknown

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): void
NameTypeDescription
indexnumber

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): void
NameTypeDescription
offsetnumber