Search documentation

Type to search the documentation.

Core

Collection

A registration store with DOM-order items, an active item, roving or active-descendant focus and typeahead — the engine under Select, Listbox, Combobox, Menu, Tabs and Tree.

CollectionStore<T extends CollectionItem> keeps a signal of registered items sorted in DOM order, tracks one active item, and implements next/previous/first/last/typeahead on top of it. Primitives register their options with it; headers, group labels and separators are never registered, so they are skipped by navigation automatically.

import { CollectionStore, type CollectionItem } from '@quartz-headless/core';

interface Row extends CollectionItem {
  id: string;
}

const store = new CollectionStore<Row>(
  { focusStrategy: 'aria-activedescendant', orientation: 'vertical' },
  document,
);
store.register({ id: 'a', element: () => document.getElementById('a'), label: 'Apple' });
store.next();
store.activeId(); // signal<string | null>

Item contract

Field Meaning
id Stable unique id (also the element id for aria-activedescendant)
element Element or accessor; defines DOM order and receives focus (roving mode)
disabled Boolean or accessor; disabled items are never active or reachable
label Typeahead text; defaults to the element text

Behaviour

  • Registration re-sorts by DOM order, so conditionally rendered options appear in their visual position.
  • Unregistering the active item moves activity to the nearest enabled neighbour (next sibling first).
  • focusStrategy: 'roving-tabindex' moves DOM focus (activeTabIndex(id) gives the tabindex); 'aria-activedescendant' keeps focus on a host and only updates activeId.
  • handleKeydown(event) maps arrows (direction aware on the horizontal axis), Home/End and single-character typeahead.
  • Typeahead ignores a no-match key rather than dropping the user's position; the buffer expires after typeaheadTimeoutMs.

See the generated API reference for every export.