@llselect/core

The package entry: the two select classes, their settings types, and the SVG icon builders. Language packs live in @llselect/core/i18n.

Select classes

abstract LLSelectBase

Defined in: base.ts:643

Abstract base for all select variants. Owns DOM scaffolding, ARIA wiring, positioning, keyboard navigation, lazy popup-list rendering, and outside-click handling. Subclasses (LLSelectSingle, LLSelectMultiple) own chosen-state, decide what happens on item click, and customise the trigger text via renderTriggerContent.

Extended by

Type Parameters

Type Parameter Default type Description
T unknown item value type. Use unknown (default) only when you intend to narrow inside templates / handlers; usually pass a concrete type like string or your domain object.
GroupKey string the group key type itemToGroupKeyFn returns (null from that fn means "this item is in no group"). Defaults to string. Open it to objects only together with groupKeyCompareFn; see DESIGN.md "Data model".
S extends LLSelectBaseSettings<T, GroupKey> LLSelectBaseSettings<T, GroupKey> the resolved settings type, for subclasses that EXTEND the settings bag. Plain use never passes it. A subclass declares extends LLSelectBase<T, GroupKey, MySettings> and this.settings is typed MySettings; the constructor's subclassSettings param then only accepts exactly the extra fields.

Items

getItems()

getItems(): readonly T[]

Defined in: base.ts:1270

Return the current item list, in data order (as passed to setItems).

  • The rendered list may differ in order and content: see getVisibleItems.
  • Returns the LIVE internal array, typed read-only. Do not mutate it (TS blocks it; plain-JS callers must treat it as frozen).
  • Structural mutation would silently bypass chosen-state reconciliation, re-filtering, and re-render. Replace the list via setItems instead.
  • Mutating item OBJECTS + rerender() is the supported in-place path.
Returns

readonly T[]

setItems()

setItems(items): void

Defined in: base.ts:1358

Replace the item list.

  • The input is shallow-copied; later external mutation does not affect the select.
  • Items MUST be unique under compareFn (it defines item identity, and the selection is a set). Duplicates render stale selection DOM; the default compareFn warns once per page, a custom compareFn is the caller's responsibility (not scanned, to keep large lists cheap).
  • If the popup is open, it re-renders now. While closed, the DOM is built lazily on the next open().
  • Both shipped variants re-render the trigger content from onItemsChanged (the multiple count total, custom content that reads items).
  • LLSelectMultiple's triggerDisplay: 'tags' mode is opt-in; 'count' is the default.
  • When triggerDisplay is 'tags', that content render is one chip per chosen item, unless createTriggerContentElFn replaces the content.
  • Subclasses may reconcile chosen-state via onItemsChanged (e.g. single mode drops a chosen value that is no longer in the list).
Parameters
Parameter Type
items readonly T[]
Returns

void

getVisibleItems()

getVisibleItems(): readonly T[]

Defined in: base.ts:2605

Return the items the popup list renders, in display order.

items                (setItems)
  |  gather          (only with grouping on; result cached until setItems)
  v
display base list
  |  filter query    (only while a query is active)
  v
visible items        (this method's return value)
  |  render
  v
DOM rows
  • If no filter query is active (including while closed), it returns the full list, gathered per gatherGroups when grouping is on.
  • If a filter query is active, it returns the matching subset.
  • Disabled items are included. They render (grayed); only actions skip them (keyboard focus, the choose-all row's subset).
  • Returns the LIVE internal array, typed read-only. Never mutate it (see getItems).
  • Subclasses use it too (e.g. for selection-by-index), and may override it to add a step: LLSelectMultiple does for hideChosenRows.
Returns

readonly T[]

Open & close

open()

open(): void

Defined in: base.ts:1057

Open the popup. Builds item elements lazily, attaches the positioner (which auto-closes if the trigger is scrolled out of view), wires the outside-click handler, and moves keyboard focus into the item list. No-op if already open.

Returns

void

close()

close(): void

Defined in: base.ts:1138

Close the popup. Detaches positioner and outside-click listener, clears the item DOM, and resets focused-item state. No-op if already closed.

Focus return is decided automatically: when filterable: true and DOM focus is still on the filter input at the moment of close (Esc on empty filter, single-select pick, click on non-focusable area outside), focus is returned to the trigger. Tab-away and outside clicks on focusable elements have already moved focus elsewhere, so we leave it alone.

Returns

void

toggle()

toggle(): void

Defined in: base.ts:1240

Open if closed, close if open.

Returns

void

isOpened()

isOpened(): boolean

Defined in: base.ts:1256

Whether the popup is currently open.

  • Pairs with isDisabled() (state read via method).
  • The same state is mirrored on the DOM as CSS hooks: data-state="open|closed" on the trigger, classIdMap.openClass on the root.
Returns

boolean

Trigger

setPlaceholder()

setPlaceholder(placeholder): void

Defined in: base.ts:1331

Replace the trigger placeholder text at runtime. It is one of the two settings with a runtime setter (the other is setUiTranslationPack); both are copy. The rule: LLSelectBaseSettings.

  • null = fall back to the pack default (uiTranslationPack.triggerPlaceholder), mirroring an unset constructor placeholder. An explicit value keeps winning over later setUiTranslationPack calls, exactly like the constructor input.
  • Takes effect immediately. Visible only while nothing is chosen - the placeholder never renders otherwise (the trigger is still re-rendered, which also refreshes the hidden accessible-value mirror).
Parameters
Parameter Type
placeholder string | null
Returns

void

Filtering

getFilterQuery()

getFilterQuery(): string

Defined in: base.ts:2635

Return the filter input's current query, exactly the string passed to filterFn.

  • It is '' while the popup is closed, the filter is inactive, or the input is empty.
  • It resets on close: each open cycle starts empty.
Returns

string

Disabling

setDisabled()

setDisabled(value): void

Defined in: base.ts:1391

Enable or disable the whole control. Disabled: the trigger gets aria-disabled + data-disabled (never the native disabled attribute, which would suppress the hover / focus events a tooltip needs), opening is blocked, an open popup closes, and the trigger leaves the tab order unless focusableWhenDisabled is set. Stored as state, mirroring setItems / setChosenItems (this design keeps mutable state out of settings).

Parameters
Parameter Type
value boolean
Returns

void

isDisabled()

isDisabled(): boolean

Defined in: base.ts:1402

Whether the whole control is disabled.

Returns

boolean

i18n

getUiTranslationPack()

getUiTranslationPack(): Readonly<LLSelectUiTranslationPack>

Defined in: base.ts:1283

Return the resolved UI strings: the built-in English defaults merged with the uiTranslationPack setting.

  • Reuse these in your own UI instead of keeping a second translation source. For example, a tag remove button tooltip: sel.getUiTranslationPack().tagRemoveButtonAriaLabel(label).
  • Returns the LIVE object. Treat it as immutable, like getItems.
Returns

Readonly<LLSelectUiTranslationPack>

setUiTranslationPack()

setUiTranslationPack(uiTranslationPack): void

Defined in: base.ts:1304

Replace the UI-translation pack at runtime, so switching language needs no re-new.

  • One of the two settings with a runtime setter (the other is setPlaceholder); both are copy. The rule: LLSelectBaseSettings.
  • The pack is resolved exactly like the constructor's: merged over the built-in English pack, NOT over the previously set pack.
  • An explicit constructor placeholder keeps winning over the new pack's triggerPlaceholder.
  • Re-renders the trigger and the open popup.
  • Also re-applies the pack-owned attributes rerender() cannot reach: the filter input placeholder and its fallback aria-label.
  • The clear button needs no such step: rerender() rebuilds it, and the rebuild reads the new pack - unless a createTriggerClearButtonEl override returns the previous element, which then owns the label.
Parameters
Parameter Type
uiTranslationPack Partial<LLSelectUiTranslationPack>
Returns

void

Lifecycle

Constructor

new LLSelectBase<T, GroupKey, S>(targetEl, settings?, subclassSettings?): LLSelectBase<T, GroupKey, S>

Defined in: base.ts:825

Parameters
Parameter Type Description
targetEl HTMLElement mount element. Becomes rootEl; its existing children are wiped and replaced with the trigger + popup structure. Pre-set classes / id / data-* attributes on this element are preserved.
settings? LLSelectSettingsInputOf<S> optional partial settings. Missing fields use defaults (LLSelectBaseSettings).
subclassSettings? Omit<S, keyof LLSelectBaseSettings<T, GroupKey>> for subclasses that EXTEND the settings bag: their own fields, already resolved (defaults applied). Typed by the class's S param, so it accepts exactly the extra fields and nothing else. Merged into this.settings right here, so the bag is complete before any base construction code (e.g. createFilterInputEl reading the pack) can read it.
Returns

LLSelectBase<T, GroupKey, S>

rerender()

rerender(): void

Defined in: base.ts:1205

Rebuild the trigger and (while open) the popup list from current state.

  • Use it after mutating item OBJECTS in place (e.g. users[0].name = 'X'). The library cannot detect that on its own.
  • It re-derives the display order (the gatherGroups gather).
  • While the filter is active, it re-runs the filter against the current item text.
  • The refresh is purely visual: it does NOT fire onChange and does NOT run onItemsChanged.
  • Orchestrator: composes renderTrigger + renderPopupList; touches no DOM directly.
Returns

void

destroy()

destroy(): void

Defined in: base.ts:1225

Tear down the instance: close the popup (which detaches every document / window listener and the positioner), unwire labelEl (click listener removed, a minted id removed), remove the library's class and inline styles from the caller's mount element, and empty it. Idempotent. The instance must not be used afterwards.

  • REQUIRED before discarding an instance that might be OPEN (framework wrappers: call this on unmount) - skipping it there leaks the outside-click / focusout / scroll / resize listeners.
  • Discarding a CLOSED instance without destroy() leaks nothing; it only leaves the root class and overflow-anchor style on the mount.
Returns

void

DOM elements

rootEl

readonly rootEl: HTMLElement

Defined in: base.ts:650

The caller-passed mount element, now decorated as the select's root. Library does not replace this node, so the caller's original reference, id, and data-* attributes stay valid.

triggerEl

readonly triggerEl: HTMLElement

Defined in: base.ts:660

The interactive trigger element. Receives focus, click, and keydown events; carries aria-expanded, aria-controls, and data-state="open|closed". Its role depends on the filter mode: combobox while the filter is inactive (it then also hosts aria-activedescendant) and button while a filterable popup is open (the filter input hosts aria-activedescendant). See docs/llm/A11Y.md.

triggerContentEl

readonly triggerContentEl: HTMLElement

Defined in: base.ts:667

Inner span inside the trigger where text/tags are written. Subclasses' renderTriggerContent writes here so the sibling arrow slot is preserved across re-renders.

popupEl

readonly popupEl: HTMLElement

Defined in: base.ts:684

The outer popup wrapper. Has no ARIA role itself - it just hosts the popup chrome (future: filter input, toggle-all control) and the inner popupListEl. Hidden via the hidden attribute when closed; positioned via inline styles by the positioner when open.

popupListEl

readonly popupListEl: HTMLElement

Defined in: base.ts:692

The inner element with role="listbox", holding the item children. Sits inside popupEl so siblings (filter input, toggle-all) can live above it without violating ARIA's "listbox children must be options" rule. The trigger's aria-controls points to this element.

classIdMap

readonly classIdMap: LLSelectClassIdMap

Defined in: base.ts:697

Resolved class names and ids for this instance.

triggerValueEl

protected readonly triggerValueEl: HTMLElement

Defined in: base.ts:676

Hidden root-level span (sibling of the trigger) mirroring the current value as plain text. Kept in sync by commitTriggerContentToDom; referenced by the filterable-mode aria-labelledby chain (see classIdMap.triggerValueId). Outside the trigger so triggerEl.textContent stays exactly the visible content.

Subclassing: reactions

onOpened()

protected onOpened(): void

Defined in: base.ts:1438

Subclass hook: called once after the popup finishes opening. Default no-op. The onOpen setting fires alongside this (both run) - hook for subclass logic, setting for consumer notification.

Returns

void

onClosed()

protected onClosed(): void

Defined in: base.ts:1443

Subclass hook: called once after the popup finishes closing. Pairs with the onClose setting (both run).

Returns

void

onChosenChanged()

protected onChosenChanged(): void

Defined in: base.ts:1450

Subclass hook: called after the chosen state actually changed, right before the variant's onChange setting fires (hook first, both run - same pairing as onOpened / onClosed). Default no-op.

Returns

void

onItemsChanged()

protected onItemsChanged(): void

Defined in: base.ts:1457

Called after setItems finishes. Override to reconcile state that depends on the item list (e.g. clear a chosen value that disappeared). Default no-op.

Returns

void

onItemActivated()

protected onItemActivated(_item): void

Defined in: base.ts:1939

Called when an item is activated (click or keyboard select). Default no-op; subclasses implement their selection behaviour (single mode picks and closes, multiple mode toggles and keeps the popup open).

Parameters
Parameter Type
_item T
Returns

void

withUserChangeSource()

protected withUserChangeSource<R>(fn): R

Defined in: base.ts:1959

Run fn with chosen-state changes attributed to the user. The library wraps exactly its pointer / keyboard entry points with it - option activation, the tag remove button, the clear button, the choose-all row; everything else reports 'api'.

Type Parameters
Type Parameter
R
Parameters
Parameter Type
fn () => R
Returns

R

onLeadingRowActivated()

protected onLeadingRowActivated(): void

Defined in: base.ts:1974

Subclass hook: the leading row was activated - Enter while it is focused (subclasses also wire their row's click handler to this). Default no-op.

Returns

void

Subclassing: rendering

renderTrigger()

protected renderTrigger(): void

Defined in: base.ts:1495

Orchestrator: composes the clear-button rebuild + renderTriggerContent + renderTriggerArrow to (re)build the whole trigger from state; touches no DOM directly. Subclasses normally override renderTriggerContent, not this.

  • Runs on every change of the chosen value, the placeholder or the pack.
  • rerender() runs it too.
  • While clearable is on, the first run builds the clear button and every later run swaps it for whatever createTriggerClearButtonEl returns: a fresh button from the base implementation, like the arrow. An override may return the previous element; it is then kept in place.
  • If the button is rebuilt and the old one held focus - on it or inside its icon - the rebuilt BUTTON gets it.
  • If the builder returned the same element, nothing was rebuilt, and focus goes back to the node that held it, if that node is still inside the button and can take focus; otherwise focus stays on the button.
  • setItems runs renderTriggerContent alone, because only the content reads the list (the multiple count total, a custom createTriggerContentElFn's items).
  • A setItems that drops the chosen entry runs the whole trigger.
  • Runs once from the LLSelectSingle / LLSelectMultiple constructor, right after super().
  • On that first run a FURTHER subclass's own fields are still undefined: JS runs a subclass's field initializers only after its super constructor returns (virtual-call-in-constructor). An override of a trigger method (renderTriggerContent, or a create*El it calls) that reads such a field sees undefined there.
  • The recipe: put construction-time configuration in the typed subclassSettings constructor param. this.settings is complete before any construction code runs.
  • For genuine instance state, tolerate defaults during construction, or call rerender() at the end of your own constructor.
  • The popup-list methods do NOT run here; they wait for open(). See DESIGN.md "Customization model".
Returns

void

commitTriggerContentToDom()

protected commitTriggerContentToDom(content, plainTextValue?): void

Defined in: base.ts:1518

Write the trigger's content slot (triggerContentEl), replacing whatever was there; the sibling arrow slot is untouched. The single DOM-writing primitive behind every renderTriggerContent path.

  • string -> set as textContent (plain text, NOT parsed as HTML). Used for the default placeholder / itemToString text / count summary.
  • HTMLElement -> inserted as-is via replaceChildren; caller owns the node. Used for whatever the createTriggerContentElFn setting returned.
  • Also mirrors the value into the hidden triggerValueEl (the accessible name source): the string itself, else plainTextValue, else the element's textContent. Pass plainTextValue whenever the element contains labelled controls (tag remove buttons) or icon-only content - the mirror is what AT announces as the field's value. Called by renderTriggerContent - the base default and the LLSelectSingle / LLSelectMultiple overrides.
Parameters
Parameter Type
content string | HTMLElement
plainTextValue? string
Returns

void

syncEmptyStateToDom()

protected syncEmptyStateToDom(): void

Defined in: base.ts:1535

Mirror the empty/filled state onto the trigger's data-empty attribute ("true" when isEmpty(), else "false"). A CSS / AT styling hook, independent of the rendered content. Called by the subclass renderTriggerContent overrides.

Returns

void

renderTriggerContent()

protected renderTriggerContent(): void

Defined in: base.ts:1558

Orchestrator: composes the *ToDom primitives to (re)build the trigger's content slot from state; touches no DOM directly. Override in subclasses to display the chosen value(s); this base default commits the placeholder and the empty flag. Always write via commitTriggerContentToDom (content) and syncEmptyStateToDom (the data-empty flag), never triggerContentEl directly, so the sibling arrow slot is always preserved.

Returns

void

createTriggerArrowContentEl()

protected createTriggerArrowContentEl(state): HTMLElement | SVGElement | null

Defined in: base.ts:1581

Trigger arrow element for the given open state.

  • Default reads createTriggerArrowContentElFn; null (setting unset, or returned for a state) = no arrow for that state.
  • Override only when extending; for one-off arrows pass the setting. Mirrors createTriggerClearButtonEl / createItemContentEl.
Parameters
Parameter Type
state { isOpened: boolean; }
state.isOpened boolean
Returns

HTMLElement | SVGElement | null

renderPopupList()

protected renderPopupList(): void

Defined in: base.ts:1611

Orchestrator: composes createItemEl (build) + computePopupSegments + commitPopupSegmentsToDom (write) to rebuild the popup list from getVisibleItems(); touches no DOM directly. Called by open() and by setItems() while open. Also clamps focusedIndex if the list shrank and re-applies focus visuals.

  • Extend it by wrapping: override, do your work before or after, then call super.renderPopupList(). The ui-select bridge frees its row scopes this way. Or override one of the methods it calls through this: createItemEl, createPopupListLeadingRowEl, itemToGroupKey, getVisibleItems.
  • Its other internals stay private on purpose. They re-establish the itemEls[i] <-> getVisibleItems()[i] alignment as one unit, so no subclass can leave keyboard nav or aria-activedescendant half-synced.
Returns

void

createGroupEl()

protected createGroupEl(key, index, items, itemEls): HTMLElement

Defined in: base.ts:1710

Build a detached group container: role="group" named by groupKeyToString, an aria-hidden visible label element, then the group's item elements. The label content comes from createGroupLabelContentEl (rich header) when non-null, else the plain label text. aria-disabled + data-disabled when the group is disabled. Override for full control of the group element (mirrors createItemEl).

Parameters
Parameter Type Description
key GroupKey the group's key
index number group index in the current render; builds a stable id
items readonly T[] the group's items (for rich content / counts)
itemEls HTMLElement[] the group's already-built option elements
Returns

HTMLElement

createGroupLabelContentEl()

protected createGroupLabelContentEl(key, itemsInGroup): HTMLElement | null

Defined in: base.ts:1744

Group header -> its visible content element (icon / count badge / rich markup). Mirrors createItemContentEl.

  • Default reads createGroupLabelContentElFn, else null so createGroupEl uses plain text from groupKeyToString.
  • The group's accessible name stays groupKeyToString (container aria-label); this fills only the visible, aria-hidden label content.
  • Override only when extending; for one-off rich headers pass the setting.
Parameters
Parameter Type
key GroupKey
itemsInGroup readonly T[]
Returns

HTMLElement | null

replacePopupListItemElInDom()

protected replacePopupListItemElInDom(item): void

Defined in: base.ts:1759

Re-render a single item's element in place instead of rebuilding the whole popup list. The DOM work is O(1) regardless of list size, so flipping one selection in a 10k-item list does not recreate 10k nodes (the lookup to find the item is O(n), but that is a cheap comparison loop next to DOM mutation). No-op if the popup is closed or the item is not in the current list. Used by multi-select toggle.

Parameters
Parameter Type
item T
Returns

void

createItemEl()

protected createItemEl(item, index): HTMLElement

Defined in: base.ts:1792

Build the DOM element for one item. The base implementation sets id, role="option", a click handler, and fills the visible content via createItemContentEl (which reads createItemContentElFn), falling back to textContent from itemToString. When the content is custom (non-null), the option's aria-label is set from itemToString so the accessible name stays the plain itemToString text. For one-off rich content (icons etc.) prefer the createItemContentElFn setting; override this only to control the whole element (tag, extra wiring).

Parameters
Parameter Type Description
item T the item value
index number index in this.items; used to build a stable id so aria-activedescendant can point to this element across re-renders.
Returns

HTMLElement

createItemContentEl()

protected createItemContentEl(item): HTMLElement | null

Defined in: base.ts:1852

Item -> the visible content of its list row (icon + text etc.).

  • Default reads createItemContentElFn, else null so createItemEl uses the plain-text default from itemToString.
  • Override only when extending; for one-off rich content pass the setting.
Parameters
Parameter Type
item T
Returns

HTMLElement | null

createPopupListLeadingRowEl()

protected createPopupListLeadingRowEl(): HTMLElement | null

Defined in: base.ts:1950

Optional non-item role="option" row pinned at the TOP of the listbox: inside the arrow-key ring (ArrowUp from the first item reaches it, Home lands on it, up-actions clamp there) but never inside itemEls, so the itemEls[i] <-> getVisibleItems()[i] alignment is untouched. Rebuilt on every renderPopupList. Base default: null = no leading row. LLSelectMultiple builds its choose-all row here (chooseAllRow setting).

Returns

HTMLElement | null

replaceLeadingRowElInDom()

protected replaceLeadingRowElInDom(): void

Defined in: base.ts:2029

Rebuild the leading row in place (tri-state / text refresh) without touching the item elements - O(1) DOM work, mirroring replacePopupListItemElInDom. Falls back to a full renderPopupList when the row becomes inapplicable (builder returns null). No-op while closed or when no leading row is rendered.

Returns

void

createTriggerClearButtonEl()

protected createTriggerClearButtonEl(): HTMLElement

Defined in: base.ts:2410

Build the clear (x) button for the clearable trigger slot.

  • The library owns the button, its click (stops propagation so it never toggles the popup, then clearSelection; a no-op while the control is disabled) and its aria-label (text from uiTranslationPack.triggerClearButtonAriaLabel).
  • createTriggerClearButtonContentElFn optionally fills the icon; else the theme's CSS glyph draws it.
  • The theme hides the button via data-empty while nothing is chosen.
  • It runs on every trigger render (renderTrigger).
  • The first run builds the button.
  • For LLSelectSingle / LLSelectMultiple and their subclasses, that first run is the variant constructor's render, right after super(), so it happens before a FURTHER subclass's field initializers.
  • A direct LLSelectBase subclass gets the button on its first trigger render (its own renderTrigger() call, or rerender() / setPlaceholder); until then the trigger is unrendered and there is no button.
  • Every later run (a value change, setPlaceholder, setUiTranslationPack, rerender()) swaps the button in place, unless this method returns the previous element - then it stays in place.
  • The base implementation returns a fresh element each run, so, like the arrow, nothing put on it from outside survives a render; customize it here.
  • An override that returns the same element every time owns everything the rebuild would otherwise refresh on it, including its aria-label after setUiTranslationPack.
  • An override that reads subclass fields calls rerender() at the end of its constructor, like every other trigger method.
Returns

HTMLElement

createTriggerClearButtonContentEl()

protected createTriggerClearButtonContentEl(): HTMLElement | SVGElement | null

Defined in: base.ts:2537

Clear button's visible content (its x icon).

  • Default reads createTriggerClearButtonContentElFn; null (setting unset, or returned) = no icon - the theme's CSS glyph draws the x.
  • Override only when extending; for one-off icons pass the setting.
Returns

HTMLElement | SVGElement | null

createPopupListNoResultsContentEl()

protected createPopupListNoResultsContentEl(query): HTMLElement | null

Defined in: base.ts:2712

The no-results message's visible content (rich empty-state).

  • Default reads createPopupListNoResultsContentElFn; null (setting unset, or returned) = plain text from uiTranslationPack.popupListNoResults.
  • Override only when extending; for one-off content pass the setting.
Parameters
Parameter Type
query string
Returns

HTMLElement | null

Subclassing: semantics

isEmpty()

protected isEmpty(): boolean

Defined in: base.ts:1545

Whether the control currently has no selection (drives data-empty). Base default is always true (the base trigger only shows the placeholder); LLSelectSingle / LLSelectMultiple override it.

Returns

boolean

itemToString()

protected itemToString(item): string

Defined in: base.ts:1841

Map an item to its display string. The library calls this everywhere it needs an item's text: list rows, the single trigger text, default filter.

  • Default reads the itemToStringFn setting, else String(item).
  • Configure via itemToStringFn (no subclass needed).
  • Override only when extending (a new select type); your override replaces the default. For HTML content, subclass createItemEl.
Parameters
Parameter Type
item T
Returns

string

isItemEffectivelyDisabled()

protected isItemEffectivelyDisabled(item): boolean

Defined in: base.ts:1865

Whether item is effectively disabled - by itemDisabledFn, or because its group is disabled (groupDisabledFn). Group-disabled layers on top, so every item-disabled behavior (no selection, keyboard skip, aria) covers grouped items with no extra code. False when neither applies. The whole-control disabled state (isDisabled()) is a separate layer, not part of this answer.

Parameters
Parameter Type
item T
Returns

boolean

itemToGroupKey()

protected itemToGroupKey(item): GroupKey | null

Defined in: base.ts:1883

Map an item to its group key, or null when it belongs to no group. The authoritative method: rendering, the gatherGroups gather, and the disabled layer all resolve keys through this method, so an override drives them all - returning keys turns grouping on even with the setting unset (all-null keys = flat list). An override reading external state must call rerender() after that state changes (same contract as mutating item objects).

  • Default reads itemToGroupKeyFn, else null (grouping off).
  • Override only when extending; configure via the setting.
Parameters
Parameter Type
item T
Returns

GroupKey | null

groupKeyToString()

protected groupKeyToString(key): string

Defined in: base.ts:1892

Map a group key to its header display text.

  • Default reads groupKeyToStringFn, else String(key).
Parameters
Parameter Type
key GroupKey
Returns

string

isGroupDisabled()

protected isGroupDisabled(key): boolean

Defined in: base.ts:1900

Whether the whole group key is disabled per groupDisabledFn (false when unset).

Parameters
Parameter Type
key GroupKey
Returns

boolean

clearSelection()

protected clearSelection(): void

Defined in: base.ts:2551

Empty the selection (invoked by the clear button). Base is a no-op; single clears to undefined, multiple to []. Goes through the normal setters, so onChange fires with the empty value. The wipe is total - chosen disabled items are cleared too (native <select> parity; unchooseAll is the enabled-only bulk op).

Returns

void

matchesQuery()

protected matchesQuery(item, query): boolean

Defined in: base.ts:2647

Per-item match predicate for the filter input.

  • Default reads filterFn; else case-insensitive substring on itemToString.
  • Override only when extending (subclass-wide custom matching); for a one-off match rule pass the setting.
Parameters
Parameter Type
item T
query string
Returns

boolean

Subclassing: focus

findNextEnabledIndex()

protected findNextEnabledIndex(start, step, list): number

Defined in: base.ts:1910

First enabled index scanning from start (inclusive) by step (+1 / -1). Returns -1 if no enabled item lies in that direction. Used to skip disabled items during keyboard nav and initial focus.

Parameters
Parameter Type
start number
step number
list readonly T[]
Returns

number

focusInitial()

protected focusInitial(): void

Defined in: base.ts:1982

Decide which item to focus when the popup opens. Default focuses the first item (or no-op if the list is empty). Override to focus the currently chosen item, last-used item, etc.

Returns

void

setFocusedIndex()

protected setFocusedIndex(index): void

Defined in: base.ts:1994

Move keyboard focus to the item at index. The value is clamped to [-1, items.length-1]; pass -1 to clear focus. Updates the focused class, aria-activedescendant, and scrolls the item into view. No-op if the clamped value equals the current focused index.

Parameters
Parameter Type
index number
Returns

void

focusLeadingRow()

protected focusLeadingRow(): boolean

Defined in: base.ts:2012

Move keyboard focus onto the leading row. Returns whether the row is now focused (false = none is rendered, nothing changed). The item focus is cleared (focusedIndex becomes -1). Protected so a subclass can wire its leading row's click to focus-then-activate (mirroring how item clicks call setFocusedIndex before onItemActivated) and use it in focusInitial (the leading row is the listbox's FIRST option).

Returns

boolean

computeTypeaheadClosedStartIndex()

protected computeTypeaheadClosedStartIndex(_list): number

Defined in: base.ts:2347

The option the closed-state typeahead treats as current, as an index into list, when the typed character is the keystroke that opens the popup.

  • The search starts AFTER this option: the opening keystroke is always a one-character buffer, because close() and a refused open() both empty the buffer.
  • Default -1: no current option, so the first match from the top wins.
  • The focus focusInitial parks on open is a convenience, not a selection - it must not shift this search.
  • Single mode overrides this with the chosen item's index, so a typed initial cycles past the current selection like a native <select>.
  • Search internals: findTypeaheadIndex in keyboard.ts.
Parameters
Parameter Type
_list readonly T[]
Returns

number

State (protected)

settings

protected readonly settings: S

Defined in: base.ts:707

Resolved settings (defaults applied) - ONE bag for the whole hierarchy. Typed by the class's S param: a subclass that extends the settings passes its resolved extra fields through the constructor's subclassSettings param, and this field is S with no re-typing (see LLSelectSingle / LLSelectMultiple).

items

protected items: T[] = []

Defined in: base.ts:718

Current item list. Defensive copy of what setItems was given.

focusedIndex

protected focusedIndex: number = -1

Defined in: base.ts:726

Index (into items) of the currently keyboard-focused item, or -1 when nothing is focused (closed popup, or no items).

changeSource

protected changeSource: LLSelectChangeSource = 'api'

Defined in: base.ts:736

Who the change being applied right now is attributed to; the variants' onChange firing reads it. Set to 'user' by withUserChangeSource and consumed (reset to 'api') by the first onChange fired, so a nested api-driven change inside an onChange handler reports 'api'.


LLSelectMultiple

Defined in: multiple.ts:186

Multi-selection select. Clicking an item toggles its membership in the chosen-items set and keeps the popup open. Each item DOM gets aria-selected="true|false"; the popup list gets aria-multiselectable="true".

Default trigger display is a count summary ("3 / 10 selected" / "All N selected" / placeholder when empty). Pass createTriggerContentElFn (or subclass renderTriggerContent) to customise (e.g. tag chips).

Extends

Type Parameters

Type Parameter Default type Description
T unknown item type.
GroupKey string group key type of itemToGroupKeyFn; see LLSelectBase.
S extends LLSelectMultipleSettings<T, GroupKey> LLSelectMultipleSettings<T, GroupKey> resolved settings type, for subclasses extending the settings bag; see LLSelectBase.

Selection

getChosenItems()

getChosenItems(): readonly T[]

Defined in: multiple.ts:236

Return the currently chosen items (insertion order).

Returns

readonly T[]

setChosenItems()

setChosenItems(items): void

Defined in: multiple.ts:252

Replace the entire chosen-items list.

  • The input is copied, and duplicates (per compareFn) collapse to their first occurrence: the chosen items are a set.
  • Fires onChange only when the new list differs from the current one. The comparison is order-sensitive: chosen order is visible state (tags render in it).
  • It ignores disabled state: it can add and drop disabled items, unlike the choose* bulk ops. Assigning to a native <select> behaves the same.
Parameters
Parameter Type
items readonly T[]
Returns

void

isChosen()

isChosen(item): boolean

Defined in: multiple.ts:277

Whether the given item is currently chosen (via compareFn).

Parameters
Parameter Type
item T
Returns

boolean

toggleItem()

toggleItem(item): void

Defined in: multiple.ts:306

Toggle the membership of item in the chosen-items set. Adds at the end if not present; removes if present. Fires onChange.

  • While hideChosenRows is on, the popup list is rebuilt so the row leaves or re-enters it.
Parameters
Parameter Type
item T
Returns

void

chooseAll()

chooseAll(): void

Defined in: multiple.ts:340

Choose every enabled item.

  • It acts on enabled items only, like every choose* bulk op. Bulk ops mirror clicking, and clicking cannot reach disabled items.
  • Already-chosen disabled items are preserved. To change disabled items too, use setChosenItems.
  • Fires onChange only when the chosen items actually change.
Returns

void

unchooseAll()

unchooseAll(): void

Defined in: multiple.ts:352

Unchoose every enabled item.

  • Already-chosen disabled items are preserved. Bulk ops mirror clicking, and clicking cannot reach disabled items.
  • Two paths DO drop them: the clear button, and setChosenItems([]).
  • Fires onChange only when the chosen items actually change.
Returns

void

toggleAll()

toggleAll(): void

Defined in: multiple.ts:363

Toggle between "all enabled chosen" and "none chosen".

  • It ignores disabled items, like every choose* bulk op.
  • This is NOT the in-popup choose-all row's action. The row acts on the visible enabled subset only: see toggleAllVisible.
Returns

void

toggleAllVisible()

toggleAllVisible(): void

Defined in: multiple.ts:390

Toggle the visible enabled items between all-chosen and all-unchosen.

  • This is the choose-all row's action (the chooseAllRow setting) as a public method. The row delegates here.
  • Acts on exactly the items that satisfy all of the following:
    • Visible: the item matches the active filter query. If no query is active, every item is visible. This is the same list as getVisibleItems.
    • Enabled: not disabled via itemDisabledFn, and not in a disabled group.
  • If all of them are already chosen, it unchooses exactly those.
  • Otherwise, it chooses the ones still missing.
  • Choices outside that set (filtered-out or disabled) are preserved either way.
  • If no filter query is active, the acted-on set is every enabled item, the same scope as toggleAll.
  • Fires onChange only when the chosen items actually change.
  • The acted-on set is computed by the overridable method getVisibleEnabledItems, shared with the choose-all row.
Returns

void

Items

getItems()

getItems(): readonly T[]

Defined in: base.ts:1270

Return the current item list, in data order (as passed to setItems).

  • The rendered list may differ in order and content: see getVisibleItems.
  • Returns the LIVE internal array, typed read-only. Do not mutate it (TS blocks it; plain-JS callers must treat it as frozen).
  • Structural mutation would silently bypass chosen-state reconciliation, re-filtering, and re-render. Replace the list via setItems instead.
  • Mutating item OBJECTS + rerender() is the supported in-place path.
Returns

readonly T[]

Inherited from

LLSelectBase.getItems

setItems()

setItems(items): void

Defined in: base.ts:1358

Replace the item list.

  • The input is shallow-copied; later external mutation does not affect the select.
  • Items MUST be unique under compareFn (it defines item identity, and the selection is a set). Duplicates render stale selection DOM; the default compareFn warns once per page, a custom compareFn is the caller's responsibility (not scanned, to keep large lists cheap).
  • If the popup is open, it re-renders now. While closed, the DOM is built lazily on the next open().
  • Both shipped variants re-render the trigger content from onItemsChanged (the multiple count total, custom content that reads items).
  • LLSelectMultiple's triggerDisplay: 'tags' mode is opt-in; 'count' is the default.
  • When triggerDisplay is 'tags', that content render is one chip per chosen item, unless createTriggerContentElFn replaces the content.
  • Subclasses may reconcile chosen-state via onItemsChanged (e.g. single mode drops a chosen value that is no longer in the list).
Parameters
Parameter Type
items readonly T[]
Returns

void

Inherited from

LLSelectBase.setItems

getVisibleItems()

getVisibleItems(): readonly T[]

Defined in: multiple.ts:444

Return the items the popup list renders, in display order.

base visible items   (LLSelectBase.getVisibleItems: gather + filter)
  |  minus chosen    (only while hideChosenRows is on; result cached)
  v
visible items        (this method's return value)
  • Identical to the base behavior, minus the chosen items while hideChosenRows is on.
  • While hideChosenRows is on and something is chosen, it returns a cached fresh array, not the live internal one.
  • The subtraction recomputes only when the base list or the chosen set changed. Calls in between return the same cached array.
Returns

readonly T[]

Overrides

LLSelectBase.getVisibleItems

Open & close

open()

open(): void

Defined in: base.ts:1057

Open the popup. Builds item elements lazily, attaches the positioner (which auto-closes if the trigger is scrolled out of view), wires the outside-click handler, and moves keyboard focus into the item list. No-op if already open.

Returns

void

Inherited from

LLSelectBase.open

close()

close(): void

Defined in: base.ts:1138

Close the popup. Detaches positioner and outside-click listener, clears the item DOM, and resets focused-item state. No-op if already closed.

Focus return is decided automatically: when filterable: true and DOM focus is still on the filter input at the moment of close (Esc on empty filter, single-select pick, click on non-focusable area outside), focus is returned to the trigger. Tab-away and outside clicks on focusable elements have already moved focus elsewhere, so we leave it alone.

Returns

void

Inherited from

LLSelectBase.close

toggle()

toggle(): void

Defined in: base.ts:1240

Open if closed, close if open.

Returns

void

Inherited from

LLSelectBase.toggle

isOpened()

isOpened(): boolean

Defined in: base.ts:1256

Whether the popup is currently open.

  • Pairs with isDisabled() (state read via method).
  • The same state is mirrored on the DOM as CSS hooks: data-state="open|closed" on the trigger, classIdMap.openClass on the root.
Returns

boolean

Inherited from

LLSelectBase.isOpened

Trigger

setPlaceholder()

setPlaceholder(placeholder): void

Defined in: base.ts:1331

Replace the trigger placeholder text at runtime. It is one of the two settings with a runtime setter (the other is setUiTranslationPack); both are copy. The rule: LLSelectBaseSettings.

  • null = fall back to the pack default (uiTranslationPack.triggerPlaceholder), mirroring an unset constructor placeholder. An explicit value keeps winning over later setUiTranslationPack calls, exactly like the constructor input.
  • Takes effect immediately. Visible only while nothing is chosen - the placeholder never renders otherwise (the trigger is still re-rendered, which also refreshes the hidden accessible-value mirror).
Parameters
Parameter Type
placeholder string | null
Returns

void

Inherited from

LLSelectBase.setPlaceholder

Filtering

getFilterQuery()

getFilterQuery(): string

Defined in: base.ts:2635

Return the filter input's current query, exactly the string passed to filterFn.

  • It is '' while the popup is closed, the filter is inactive, or the input is empty.
  • It resets on close: each open cycle starts empty.
Returns

string

Inherited from

LLSelectBase.getFilterQuery

Disabling

setDisabled()

setDisabled(value): void

Defined in: base.ts:1391

Enable or disable the whole control. Disabled: the trigger gets aria-disabled + data-disabled (never the native disabled attribute, which would suppress the hover / focus events a tooltip needs), opening is blocked, an open popup closes, and the trigger leaves the tab order unless focusableWhenDisabled is set. Stored as state, mirroring setItems / setChosenItems (this design keeps mutable state out of settings).

Parameters
Parameter Type
value boolean
Returns

void

Inherited from

LLSelectBase.setDisabled

isDisabled()

isDisabled(): boolean

Defined in: base.ts:1402

Whether the whole control is disabled.

Returns

boolean

Inherited from

LLSelectBase.isDisabled

i18n

getUiTranslationPack()

getUiTranslationPack(): Readonly<LLSelectUiTranslationPack>

Defined in: base.ts:1283

Return the resolved UI strings: the built-in English defaults merged with the uiTranslationPack setting.

  • Reuse these in your own UI instead of keeping a second translation source. For example, a tag remove button tooltip: sel.getUiTranslationPack().tagRemoveButtonAriaLabel(label).
  • Returns the LIVE object. Treat it as immutable, like getItems.
Returns

Readonly<LLSelectUiTranslationPack>

Inherited from

LLSelectBase.getUiTranslationPack

setUiTranslationPack()

setUiTranslationPack(uiTranslationPack): void

Defined in: base.ts:1304

Replace the UI-translation pack at runtime, so switching language needs no re-new.

  • One of the two settings with a runtime setter (the other is setPlaceholder); both are copy. The rule: LLSelectBaseSettings.
  • The pack is resolved exactly like the constructor's: merged over the built-in English pack, NOT over the previously set pack.
  • An explicit constructor placeholder keeps winning over the new pack's triggerPlaceholder.
  • Re-renders the trigger and the open popup.
  • Also re-applies the pack-owned attributes rerender() cannot reach: the filter input placeholder and its fallback aria-label.
  • The clear button needs no such step: rerender() rebuilds it, and the rebuild reads the new pack - unless a createTriggerClearButtonEl override returns the previous element, which then owns the label.
Parameters
Parameter Type
uiTranslationPack Partial<LLSelectUiTranslationPack>
Returns

void

Inherited from

LLSelectBase.setUiTranslationPack

Lifecycle

rerender()

rerender(): void

Defined in: base.ts:1205

Rebuild the trigger and (while open) the popup list from current state.

  • Use it after mutating item OBJECTS in place (e.g. users[0].name = 'X'). The library cannot detect that on its own.
  • It re-derives the display order (the gatherGroups gather).
  • While the filter is active, it re-runs the filter against the current item text.
  • The refresh is purely visual: it does NOT fire onChange and does NOT run onItemsChanged.
  • Orchestrator: composes renderTrigger + renderPopupList; touches no DOM directly.
Returns

void

Inherited from

LLSelectBase.rerender

destroy()

destroy(): void

Defined in: base.ts:1225

Tear down the instance: close the popup (which detaches every document / window listener and the positioner), unwire labelEl (click listener removed, a minted id removed), remove the library's class and inline styles from the caller's mount element, and empty it. Idempotent. The instance must not be used afterwards.

  • REQUIRED before discarding an instance that might be OPEN (framework wrappers: call this on unmount) - skipping it there leaks the outside-click / focusout / scroll / resize listeners.
  • Discarding a CLOSED instance without destroy() leaks nothing; it only leaves the root class and overflow-anchor style on the mount.
Returns

void

Inherited from

LLSelectBase.destroy

Constructor

new LLSelectMultiple<T, GroupKey, S>(targetEl, settings?): LLSelectMultiple<T, GroupKey, S>

Defined in: multiple.ts:206

Build the control inside targetEl.

  • Settings are resolved once here; missing fields get defaults.
  • They are frozen afterwards, except placeholder and uiTranslationPack, which have runtime setters; the rule is at LLSelectBaseSettings.
  • This plain form infers T from a typed callback in settings whose signature contains T (itemToStringFn: (u: User) => ...). With no such callback, pass T explicitly: new LLSelectMultiple<string>(...).
Parameters
Parameter Type
targetEl HTMLElement
settings? LLSelectMultipleSettingsInput<T, GroupKey>
Returns

LLSelectMultiple<T, GroupKey, S>

Overrides

LLSelectBase.constructor

Constructor

new LLSelectMultiple<T, GroupKey, S>(targetEl, settings?, subclassSettings?): LLSelectMultiple<T, GroupKey, S>

Defined in: multiple.ts:212

Subclass form. subclassSettings is the typed pass-through for subclasses that extend the settings bag further; see LLSelectBase's S param.

Parameters
Parameter Type
targetEl HTMLElement
settings? LLSelectSettingsInputOf<S>
subclassSettings? Omit<S, keyof LLSelectMultipleSettings<T, GroupKey>>
Returns

LLSelectMultiple<T, GroupKey, S>

Overrides

LLSelectBase<T, GroupKey, S>.constructor

DOM elements

rootEl

readonly rootEl: HTMLElement

Defined in: base.ts:650

The caller-passed mount element, now decorated as the select's root. Library does not replace this node, so the caller's original reference, id, and data-* attributes stay valid.

Inherited from

LLSelectBase.rootEl

triggerEl

readonly triggerEl: HTMLElement

Defined in: base.ts:660

The interactive trigger element. Receives focus, click, and keydown events; carries aria-expanded, aria-controls, and data-state="open|closed". Its role depends on the filter mode: combobox while the filter is inactive (it then also hosts aria-activedescendant) and button while a filterable popup is open (the filter input hosts aria-activedescendant). See docs/llm/A11Y.md.

Inherited from

LLSelectBase.triggerEl

triggerContentEl

readonly triggerContentEl: HTMLElement

Defined in: base.ts:667

Inner span inside the trigger where text/tags are written. Subclasses' renderTriggerContent writes here so the sibling arrow slot is preserved across re-renders.

Inherited from

LLSelectBase.triggerContentEl

popupEl

readonly popupEl: HTMLElement

Defined in: base.ts:684

The outer popup wrapper. Has no ARIA role itself - it just hosts the popup chrome (future: filter input, toggle-all control) and the inner popupListEl. Hidden via the hidden attribute when closed; positioned via inline styles by the positioner when open.

Inherited from

LLSelectBase.popupEl

popupListEl

readonly popupListEl: HTMLElement

Defined in: base.ts:692

The inner element with role="listbox", holding the item children. Sits inside popupEl so siblings (filter input, toggle-all) can live above it without violating ARIA's "listbox children must be options" rule. The trigger's aria-controls points to this element.

Inherited from

LLSelectBase.popupListEl

classIdMap

readonly classIdMap: LLSelectClassIdMap

Defined in: base.ts:697

Resolved class names and ids for this instance.

Inherited from

LLSelectBase.classIdMap

triggerValueEl

protected readonly triggerValueEl: HTMLElement

Defined in: base.ts:676

Hidden root-level span (sibling of the trigger) mirroring the current value as plain text. Kept in sync by commitTriggerContentToDom; referenced by the filterable-mode aria-labelledby chain (see classIdMap.triggerValueId). Outside the trigger so triggerEl.textContent stays exactly the visible content.

Inherited from

LLSelectBase.triggerValueEl

Subclassing: reactions

onOpened()

protected onOpened(): void

Defined in: base.ts:1438

Subclass hook: called once after the popup finishes opening. Default no-op. The onOpen setting fires alongside this (both run) - hook for subclass logic, setting for consumer notification.

Returns

void

Inherited from

LLSelectBase.onOpened

onClosed()

protected onClosed(): void

Defined in: base.ts:1443

Subclass hook: called once after the popup finishes closing. Pairs with the onClose setting (both run).

Returns

void

Inherited from

LLSelectBase.onClosed

onChosenChanged()

protected onChosenChanged(): void

Defined in: base.ts:1450

Subclass hook: called after the chosen state actually changed, right before the variant's onChange setting fires (hook first, both run - same pairing as onOpened / onClosed). Default no-op.

Returns

void

Inherited from

LLSelectBase.onChosenChanged

withUserChangeSource()

protected withUserChangeSource<R>(fn): R

Defined in: base.ts:1959

Run fn with chosen-state changes attributed to the user. The library wraps exactly its pointer / keyboard entry points with it - option activation, the tag remove button, the clear button, the choose-all row; everything else reports 'api'.

Type Parameters
Type Parameter
R
Parameters
Parameter Type
fn () => R
Returns

R

Inherited from

LLSelectBase.withUserChangeSource

onItemActivated()

protected onItemActivated(item): void

Defined in: multiple.ts:617

Toggle on click. Multi mode keeps the popup open.

Parameters
Parameter Type
item T
Returns

void

Overrides

LLSelectBase.onItemActivated

onLeadingRowActivated()

protected onLeadingRowActivated(): void

Defined in: multiple.ts:696

Activate the choose-all row: delegates to toggleAllVisible.

Returns

void

Overrides

LLSelectBase.onLeadingRowActivated

onItemsChanged()

protected onItemsChanged(): void

Defined in: multiple.ts:730

Re-match the chosen entries against the new list after setItems.

  • Entries the list no longer holds (by compareFn) are dropped and onChange fires for the drop.
  • When the list holds a compareFn-equal but DIFFERENT object (track by style reload: same key, fresh fields), the stored reference is swapped to the list's object. A reference swap is not a logical change, so it does not fire onChange.
  • The trigger content re-renders after every setItems, swap or not.
  • Why: the count summary shows the list total, and a custom createTriggerContentElFn receives items.
  • The arrow re-renders only when a chosen entry is dropped, because that runs the whole trigger.
  • triggerDisplay: 'tags' is opt-in; 'count' is the default.
  • When triggerDisplay is 'tags', that render is one chip per chosen item per setItems, unless createTriggerContentElFn replaces the content.
  • That cost is acceptable: setItems is a bulk call.
Returns

void

Overrides

LLSelectBase.onItemsChanged

Subclassing: rendering

renderTrigger()

protected renderTrigger(): void

Defined in: base.ts:1495

Orchestrator: composes the clear-button rebuild + renderTriggerContent + renderTriggerArrow to (re)build the whole trigger from state; touches no DOM directly. Subclasses normally override renderTriggerContent, not this.

  • Runs on every change of the chosen value, the placeholder or the pack.
  • rerender() runs it too.
  • While clearable is on, the first run builds the clear button and every later run swaps it for whatever createTriggerClearButtonEl returns: a fresh button from the base implementation, like the arrow. An override may return the previous element; it is then kept in place.
  • If the button is rebuilt and the old one held focus - on it or inside its icon - the rebuilt BUTTON gets it.
  • If the builder returned the same element, nothing was rebuilt, and focus goes back to the node that held it, if that node is still inside the button and can take focus; otherwise focus stays on the button.
  • setItems runs renderTriggerContent alone, because only the content reads the list (the multiple count total, a custom createTriggerContentElFn's items).
  • A setItems that drops the chosen entry runs the whole trigger.
  • Runs once from the LLSelectSingle / LLSelectMultiple constructor, right after super().
  • On that first run a FURTHER subclass's own fields are still undefined: JS runs a subclass's field initializers only after its super constructor returns (virtual-call-in-constructor). An override of a trigger method (renderTriggerContent, or a create*El it calls) that reads such a field sees undefined there.
  • The recipe: put construction-time configuration in the typed subclassSettings constructor param. this.settings is complete before any construction code runs.
  • For genuine instance state, tolerate defaults during construction, or call rerender() at the end of your own constructor.
  • The popup-list methods do NOT run here; they wait for open(). See DESIGN.md "Customization model".
Returns

void

Inherited from

LLSelectBase.renderTrigger

commitTriggerContentToDom()

protected commitTriggerContentToDom(content, plainTextValue?): void

Defined in: base.ts:1518

Write the trigger's content slot (triggerContentEl), replacing whatever was there; the sibling arrow slot is untouched. The single DOM-writing primitive behind every renderTriggerContent path.

  • string -> set as textContent (plain text, NOT parsed as HTML). Used for the default placeholder / itemToString text / count summary.
  • HTMLElement -> inserted as-is via replaceChildren; caller owns the node. Used for whatever the createTriggerContentElFn setting returned.
  • Also mirrors the value into the hidden triggerValueEl (the accessible name source): the string itself, else plainTextValue, else the element's textContent. Pass plainTextValue whenever the element contains labelled controls (tag remove buttons) or icon-only content - the mirror is what AT announces as the field's value. Called by renderTriggerContent - the base default and the LLSelectSingle / LLSelectMultiple overrides.
Parameters
Parameter Type
content string | HTMLElement
plainTextValue? string
Returns

void

Inherited from

LLSelectBase.commitTriggerContentToDom

syncEmptyStateToDom()

protected syncEmptyStateToDom(): void

Defined in: base.ts:1535

Mirror the empty/filled state onto the trigger's data-empty attribute ("true" when isEmpty(), else "false"). A CSS / AT styling hook, independent of the rendered content. Called by the subclass renderTriggerContent overrides.

Returns

void

Inherited from

LLSelectBase.syncEmptyStateToDom

createTriggerArrowContentEl()

protected createTriggerArrowContentEl(state): HTMLElement | SVGElement | null

Defined in: base.ts:1581

Trigger arrow element for the given open state.

  • Default reads createTriggerArrowContentElFn; null (setting unset, or returned for a state) = no arrow for that state.
  • Override only when extending; for one-off arrows pass the setting. Mirrors createTriggerClearButtonEl / createItemContentEl.
Parameters
Parameter Type
state { isOpened: boolean; }
state.isOpened boolean
Returns

HTMLElement | SVGElement | null

Inherited from

LLSelectBase.createTriggerArrowContentEl

renderPopupList()

protected renderPopupList(): void

Defined in: base.ts:1611

Orchestrator: composes createItemEl (build) + computePopupSegments + commitPopupSegmentsToDom (write) to rebuild the popup list from getVisibleItems(); touches no DOM directly. Called by open() and by setItems() while open. Also clamps focusedIndex if the list shrank and re-applies focus visuals.

  • Extend it by wrapping: override, do your work before or after, then call super.renderPopupList(). The ui-select bridge frees its row scopes this way. Or override one of the methods it calls through this: createItemEl, createPopupListLeadingRowEl, itemToGroupKey, getVisibleItems.
  • Its other internals stay private on purpose. They re-establish the itemEls[i] <-> getVisibleItems()[i] alignment as one unit, so no subclass can leave keyboard nav or aria-activedescendant half-synced.
Returns

void

Inherited from

LLSelectBase.renderPopupList

createGroupEl()

protected createGroupEl(key, index, items, itemEls): HTMLElement

Defined in: base.ts:1710

Build a detached group container: role="group" named by groupKeyToString, an aria-hidden visible label element, then the group's item elements. The label content comes from createGroupLabelContentEl (rich header) when non-null, else the plain label text. aria-disabled + data-disabled when the group is disabled. Override for full control of the group element (mirrors createItemEl).

Parameters
Parameter Type Description
key GroupKey the group's key
index number group index in the current render; builds a stable id
items readonly T[] the group's items (for rich content / counts)
itemEls HTMLElement[] the group's already-built option elements
Returns

HTMLElement

Inherited from

LLSelectBase.createGroupEl

createGroupLabelContentEl()

protected createGroupLabelContentEl(key, itemsInGroup): HTMLElement | null

Defined in: base.ts:1744

Group header -> its visible content element (icon / count badge / rich markup). Mirrors createItemContentEl.

  • Default reads createGroupLabelContentElFn, else null so createGroupEl uses plain text from groupKeyToString.
  • The group's accessible name stays groupKeyToString (container aria-label); this fills only the visible, aria-hidden label content.
  • Override only when extending; for one-off rich headers pass the setting.
Parameters
Parameter Type
key GroupKey
itemsInGroup readonly T[]
Returns

HTMLElement | null

Inherited from

LLSelectBase.createGroupLabelContentEl

replacePopupListItemElInDom()

protected replacePopupListItemElInDom(item): void

Defined in: base.ts:1759

Re-render a single item's element in place instead of rebuilding the whole popup list. The DOM work is O(1) regardless of list size, so flipping one selection in a 10k-item list does not recreate 10k nodes (the lookup to find the item is O(n), but that is a cheap comparison loop next to DOM mutation). No-op if the popup is closed or the item is not in the current list. Used by multi-select toggle.

Parameters
Parameter Type
item T
Returns

void

Inherited from

LLSelectBase.replacePopupListItemElInDom

createItemContentEl()

protected createItemContentEl(item): HTMLElement | null

Defined in: base.ts:1852

Item -> the visible content of its list row (icon + text etc.).

  • Default reads createItemContentElFn, else null so createItemEl uses the plain-text default from itemToString.
  • Override only when extending; for one-off rich content pass the setting.
Parameters
Parameter Type
item T
Returns

HTMLElement | null

Inherited from

LLSelectBase.createItemContentEl

replaceLeadingRowElInDom()

protected replaceLeadingRowElInDom(): void

Defined in: base.ts:2029

Rebuild the leading row in place (tri-state / text refresh) without touching the item elements - O(1) DOM work, mirroring replacePopupListItemElInDom. Falls back to a full renderPopupList when the row becomes inapplicable (builder returns null). No-op while closed or when no leading row is rendered.

Returns

void

Inherited from

LLSelectBase.replaceLeadingRowElInDom

createTriggerClearButtonEl()

protected createTriggerClearButtonEl(): HTMLElement

Defined in: base.ts:2410

Build the clear (x) button for the clearable trigger slot.

  • The library owns the button, its click (stops propagation so it never toggles the popup, then clearSelection; a no-op while the control is disabled) and its aria-label (text from uiTranslationPack.triggerClearButtonAriaLabel).
  • createTriggerClearButtonContentElFn optionally fills the icon; else the theme's CSS glyph draws it.
  • The theme hides the button via data-empty while nothing is chosen.
  • It runs on every trigger render (renderTrigger).
  • The first run builds the button.
  • For LLSelectSingle / LLSelectMultiple and their subclasses, that first run is the variant constructor's render, right after super(), so it happens before a FURTHER subclass's field initializers.
  • A direct LLSelectBase subclass gets the button on its first trigger render (its own renderTrigger() call, or rerender() / setPlaceholder); until then the trigger is unrendered and there is no button.
  • Every later run (a value change, setPlaceholder, setUiTranslationPack, rerender()) swaps the button in place, unless this method returns the previous element - then it stays in place.
  • The base implementation returns a fresh element each run, so, like the arrow, nothing put on it from outside survives a render; customize it here.
  • An override that returns the same element every time owns everything the rebuild would otherwise refresh on it, including its aria-label after setUiTranslationPack.
  • An override that reads subclass fields calls rerender() at the end of its constructor, like every other trigger method.
Returns

HTMLElement

Inherited from

LLSelectBase.createTriggerClearButtonEl

createTriggerClearButtonContentEl()

protected createTriggerClearButtonContentEl(): HTMLElement | SVGElement | null

Defined in: base.ts:2537

Clear button's visible content (its x icon).

  • Default reads createTriggerClearButtonContentElFn; null (setting unset, or returned) = no icon - the theme's CSS glyph draws the x.
  • Override only when extending; for one-off icons pass the setting.
Returns

HTMLElement | SVGElement | null

Inherited from

LLSelectBase.createTriggerClearButtonContentEl

createPopupListNoResultsContentEl()

protected createPopupListNoResultsContentEl(query): HTMLElement | null

Defined in: base.ts:2712

The no-results message's visible content (rich empty-state).

  • Default reads createPopupListNoResultsContentElFn; null (setting unset, or returned) = plain text from uiTranslationPack.popupListNoResults.
  • Override only when extending; for one-off content pass the setting.
Parameters
Parameter Type
query string
Returns

HTMLElement | null

Inherited from

LLSelectBase.createPopupListNoResultsContentEl

renderTriggerContent()

protected renderTriggerContent(): void

Defined in: multiple.ts:472

Orchestrator: composes syncEmptyStateToDom + commitTriggerContentToDom to (re)build the trigger from state; touches no DOM directly. Default text is a count summary; override (or pass the createTriggerContentElFn setting) to display tags / custom markup / etc.

  • If 0 items are chosen, the text is placeholder.
  • If n > 0, the text is uiTranslationPack.triggerCountSummary(n, total) (English default: "n / total selected", or "All n selected" when all are chosen).
Returns

void

Overrides

LLSelectBase.renderTriggerContent

createTagsEl()

protected createTagsEl(): HTMLElement

Defined in: multiple.ts:498

Build the tag-list element for 'tags' mode: one chip per chosen item. Override for full control of the chip strip (the trigger-level equivalent of overriding createItemEl).

Returns

HTMLElement

createTagEl()

protected createTagEl(item): HTMLElement

Defined in: multiple.ts:516

Build one removable tag chip: its content (from createTagContentEl, else plain itemToString) plus its remove (x) button (from createTagRemoveButtonEl). A chip whose item is effectively disabled gets aria-disabled="true" + tagDisabledClass, and its x turns inert - mirroring a disabled option row. Override for full control of the chip container; override the two sub-parts for content-only / remove-button-only changes.

Parameters
Parameter Type
item T
Returns

HTMLElement

createTagRemoveButtonEl()

protected createTagRemoveButtonEl(item): HTMLElement

Defined in: multiple.ts:548

Build one chip's remove (x) button. The library owns the button + its click (stopPropagation so it never toggles the popup, then toggleItem; a no-op while the whole control OR the item itself is disabled) + tabindex="-1" + aria-label (from itemToTagRemoveButtonAriaLabel). An effectively-disabled item's button also gets aria-disabled="true". createTagRemoveButtonContentElFn optionally fills the icon, else the theme's CSS glyph. Mirrors the clear button's createTriggerClearButtonEl. Override for full control of the button element.

Parameters
Parameter Type
item T
Returns

HTMLElement

createTagRemoveButtonContentEl()

protected createTagRemoveButtonContentEl(item): HTMLElement | SVGElement | null

Defined in: multiple.ts:578

One chip's remove-button visible content (its x icon). Mirrors createTriggerClearButtonContentEl.

  • Default reads createTagRemoveButtonContentElFn; null (setting unset, or returned) = no icon - the theme's CSS glyph draws the x.
  • Override only when extending; for one-off icons pass the setting.
Parameters
Parameter Type
item T
Returns

HTMLElement | SVGElement | null

createTagContentEl()

protected createTagContentEl(item): HTMLElement | null

Defined in: multiple.ts:590

Per-chip visible content in 'tags' mode. Mirrors createItemContentEl. Default reads createTagContentElFn, else null so createTagEl falls back to plain text from itemToString.

Parameters
Parameter Type
item T
Returns

HTMLElement | null

createPopupListLeadingRowEl()

protected createPopupListLeadingRowEl(): HTMLElement | null

Defined in: multiple.ts:638

Build the choose-all row (chooseAllRow setting) as the listbox's leading role="option" row: data-chosen-state="none|some|all" (a CSS styling hook), aria-selected only when ALL visible enabled items are chosen, accessible name + visible text from uiTranslationPack.chooseAllRowText(chosenCount, totalCount) over the visible enabled subset. null when the setting is off or nothing is actionable.

Returns

HTMLElement | null

Overrides

LLSelectBase.createPopupListLeadingRowEl

createChooseAllRowContentEl()

protected createChooseAllRowContentEl(chosenState, chosenCount, totalCount): HTMLElement | null

Defined in: multiple.ts:682

The choose-all row's visible content (rich tri-state). Mirrors createItemContentEl.

  • Default reads createChooseAllRowContentElFn; null (setting unset, or returned) = the default content: plain text from uiTranslationPack.chooseAllRowText.
  • Override only when extending; for one-off content pass the setting.
Parameters
Parameter Type
chosenState LLSelectChosenState
chosenCount number
totalCount number
Returns

HTMLElement | null

createItemEl()

protected createItemEl(item, index): HTMLElement

Defined in: multiple.ts:704

Mark each item with aria-selected reflecting its chosen state.

Parameters
Parameter Type
item T
index number
Returns

HTMLElement

Overrides

LLSelectBase.createItemEl

Subclassing: semantics

itemToString()

protected itemToString(item): string

Defined in: base.ts:1841

Map an item to its display string. The library calls this everywhere it needs an item's text: list rows, the single trigger text, default filter.

  • Default reads the itemToStringFn setting, else String(item).
  • Configure via itemToStringFn (no subclass needed).
  • Override only when extending (a new select type); your override replaces the default. For HTML content, subclass createItemEl.
Parameters
Parameter Type
item T
Returns

string

Inherited from

LLSelectBase.itemToString

isItemEffectivelyDisabled()

protected isItemEffectivelyDisabled(item): boolean

Defined in: base.ts:1865

Whether item is effectively disabled - by itemDisabledFn, or because its group is disabled (groupDisabledFn). Group-disabled layers on top, so every item-disabled behavior (no selection, keyboard skip, aria) covers grouped items with no extra code. False when neither applies. The whole-control disabled state (isDisabled()) is a separate layer, not part of this answer.

Parameters
Parameter Type
item T
Returns

boolean

Inherited from

LLSelectBase.isItemEffectivelyDisabled

itemToGroupKey()

protected itemToGroupKey(item): GroupKey | null

Defined in: base.ts:1883

Map an item to its group key, or null when it belongs to no group. The authoritative method: rendering, the gatherGroups gather, and the disabled layer all resolve keys through this method, so an override drives them all - returning keys turns grouping on even with the setting unset (all-null keys = flat list). An override reading external state must call rerender() after that state changes (same contract as mutating item objects).

  • Default reads itemToGroupKeyFn, else null (grouping off).
  • Override only when extending; configure via the setting.
Parameters
Parameter Type
item T
Returns

GroupKey | null

Inherited from

LLSelectBase.itemToGroupKey

groupKeyToString()

protected groupKeyToString(key): string

Defined in: base.ts:1892

Map a group key to its header display text.

  • Default reads groupKeyToStringFn, else String(key).
Parameters
Parameter Type
key GroupKey
Returns

string

Inherited from

LLSelectBase.groupKeyToString

isGroupDisabled()

protected isGroupDisabled(key): boolean

Defined in: base.ts:1900

Whether the whole group key is disabled per groupDisabledFn (false when unset).

Parameters
Parameter Type
key GroupKey
Returns

boolean

Inherited from

LLSelectBase.isGroupDisabled

matchesQuery()

protected matchesQuery(item, query): boolean

Defined in: base.ts:2647

Per-item match predicate for the filter input.

  • Default reads filterFn; else case-insensitive substring on itemToString.
  • Override only when extending (subclass-wide custom matching); for a one-off match rule pass the setting.
Parameters
Parameter Type
item T
query string
Returns

boolean

Inherited from

LLSelectBase.matchesQuery

getVisibleEnabledItems()

protected getVisibleEnabledItems(): readonly T[]

Defined in: multiple.ts:414

Return the visible enabled subset: the items toggleAllVisible and the choose-all row act on.

  • It is getVisibleItems() minus the effectively disabled items (itemDisabledFn, disabled groups).
  • Both the choose-all row (its counts, tri-state, and click) and toggleAllVisible read this one method, so an override keeps them in agreement. Example: the tree-select demo subclass narrows it to leaf nodes.
Returns

readonly T[]

itemToTagRemoveButtonAriaLabel()

protected itemToTagRemoveButtonAriaLabel(item): string

Defined in: multiple.ts:601

Item -> its remove button's accessible name in 'tags' mode.

  • Default: uiTranslationPack.tagRemoveButtonAriaLabel(itemToString(item)).
  • Override only when extending (e.g. a name from another item field); per-locale text goes through the uiTranslationPack setting.
Parameters
Parameter Type
item T
Returns

string

isEmpty()

protected isEmpty(): boolean

Defined in: multiple.ts:609

No selection iff the chosen set is empty. Drives the trigger's data-empty.

Returns

boolean

Overrides

LLSelectBase.isEmpty

clearSelection()

protected clearSelection(): void

Defined in: multiple.ts:625

Clear button empties the chosen-items set to [].

Returns

void

Overrides

LLSelectBase.clearSelection

Subclassing: focus

findNextEnabledIndex()

protected findNextEnabledIndex(start, step, list): number

Defined in: base.ts:1910

First enabled index scanning from start (inclusive) by step (+1 / -1). Returns -1 if no enabled item lies in that direction. Used to skip disabled items during keyboard nav and initial focus.

Parameters
Parameter Type
start number
step number
list readonly T[]
Returns

number

Inherited from

LLSelectBase.findNextEnabledIndex

setFocusedIndex()

protected setFocusedIndex(index): void

Defined in: base.ts:1994

Move keyboard focus to the item at index. The value is clamped to [-1, items.length-1]; pass -1 to clear focus. Updates the focused class, aria-activedescendant, and scrolls the item into view. No-op if the clamped value equals the current focused index.

Parameters
Parameter Type
index number
Returns

void

Inherited from

LLSelectBase.setFocusedIndex

focusLeadingRow()

protected focusLeadingRow(): boolean

Defined in: base.ts:2012

Move keyboard focus onto the leading row. Returns whether the row is now focused (false = none is rendered, nothing changed). The item focus is cleared (focusedIndex becomes -1). Protected so a subclass can wire its leading row's click to focus-then-activate (mirroring how item clicks call setFocusedIndex before onItemActivated) and use it in focusInitial (the leading row is the listbox's FIRST option).

Returns

boolean

Inherited from

LLSelectBase.focusLeadingRow

computeTypeaheadClosedStartIndex()

protected computeTypeaheadClosedStartIndex(_list): number

Defined in: base.ts:2347

The option the closed-state typeahead treats as current, as an index into list, when the typed character is the keystroke that opens the popup.

  • The search starts AFTER this option: the opening keystroke is always a one-character buffer, because close() and a refused open() both empty the buffer.
  • Default -1: no current option, so the first match from the top wins.
  • The focus focusInitial parks on open is a convenience, not a selection - it must not shift this search.
  • Single mode overrides this with the chosen item's index, so a typed initial cycles past the current selection like a native <select>.
  • Search internals: findTypeaheadIndex in keyboard.ts.
Parameters
Parameter Type
_list readonly T[]
Returns

number

Inherited from

LLSelectBase.computeTypeaheadClosedStartIndex

focusInitial()

protected focusInitial(): void

Defined in: multiple.ts:764

On open, focus the first chosen item (if present and enabled). Otherwise the FIRST OPTION - which is the choose-all row when rendered (A11Y.md: activedescendant points at the first chosen option, else the first option; the row is the topmost option), so keyboard users discover it immediately. Else the first enabled item. Indices are into getVisibleItems().

Returns

void

Overrides

LLSelectBase.focusInitial

State (protected)

settings

protected readonly settings: S

Defined in: base.ts:707

Resolved settings (defaults applied) - ONE bag for the whole hierarchy. Typed by the class's S param: a subclass that extends the settings passes its resolved extra fields through the constructor's subclassSettings param, and this field is S with no re-typing (see LLSelectSingle / LLSelectMultiple).

Inherited from

LLSelectBase.settings

items

protected items: T[] = []

Defined in: base.ts:718

Current item list. Defensive copy of what setItems was given.

Inherited from

LLSelectBase.items

focusedIndex

protected focusedIndex: number = -1

Defined in: base.ts:726

Index (into items) of the currently keyboard-focused item, or -1 when nothing is focused (closed popup, or no items).

Inherited from

LLSelectBase.focusedIndex

changeSource

protected changeSource: LLSelectChangeSource = 'api'

Defined in: base.ts:736

Who the change being applied right now is attributed to; the variants' onChange firing reads it. Set to 'user' by withUserChangeSource and consumed (reset to 'api') by the first onChange fired, so a nested api-driven change inside an onChange handler reports 'api'.

Inherited from

LLSelectBase.changeSource

chosenItems

protected chosenItems: readonly T[] = []

Defined in: multiple.ts:194

Currently chosen items, in insertion order.


LLSelectSingle

Defined in: single.ts:75

Single-selection select. Picking an item replaces any prior chosen item and closes the popup. Use setChosenItem(undefined) to clear the selection.

Extends

Type Parameters

Type Parameter Default type Description
T unknown item type. Supply your own compareFn for non-primitive T.
GroupKey string group key type of itemToGroupKeyFn; see LLSelectBase.
S extends LLSelectSingleSettings<T, GroupKey> LLSelectSingleSettings<T, GroupKey> resolved settings type, for subclasses extending the settings bag; see LLSelectBase.

Selection

getChosenItem()

getChosenItem(): T | undefined

Defined in: single.ts:115

Return the currently chosen item, or undefined if none.

Returns

T | undefined

setChosenItem()

setChosenItem(item): void

Defined in: single.ts:131

Set the chosen item programmatically.

  • undefined clears the choice.
  • Fires onChange only when the item actually differs from the current one (compared via compareFn).
  • Accepts an item that is not (yet) in the items list, for async data flows. If a later setItems does not include it, it is dropped automatically.
  • It does not check disabled state: a disabled item can be chosen programmatically. Native <select> behaves the same.
Parameters
Parameter Type
item T | undefined
Returns

void

Items

getItems()

getItems(): readonly T[]

Defined in: base.ts:1270

Return the current item list, in data order (as passed to setItems).

  • The rendered list may differ in order and content: see getVisibleItems.
  • Returns the LIVE internal array, typed read-only. Do not mutate it (TS blocks it; plain-JS callers must treat it as frozen).
  • Structural mutation would silently bypass chosen-state reconciliation, re-filtering, and re-render. Replace the list via setItems instead.
  • Mutating item OBJECTS + rerender() is the supported in-place path.
Returns

readonly T[]

Inherited from

LLSelectBase.getItems

setItems()

setItems(items): void

Defined in: base.ts:1358

Replace the item list.

  • The input is shallow-copied; later external mutation does not affect the select.
  • Items MUST be unique under compareFn (it defines item identity, and the selection is a set). Duplicates render stale selection DOM; the default compareFn warns once per page, a custom compareFn is the caller's responsibility (not scanned, to keep large lists cheap).
  • If the popup is open, it re-renders now. While closed, the DOM is built lazily on the next open().
  • Both shipped variants re-render the trigger content from onItemsChanged (the multiple count total, custom content that reads items).
  • LLSelectMultiple's triggerDisplay: 'tags' mode is opt-in; 'count' is the default.
  • When triggerDisplay is 'tags', that content render is one chip per chosen item, unless createTriggerContentElFn replaces the content.
  • Subclasses may reconcile chosen-state via onItemsChanged (e.g. single mode drops a chosen value that is no longer in the list).
Parameters
Parameter Type
items readonly T[]
Returns

void

Inherited from

LLSelectBase.setItems

getVisibleItems()

getVisibleItems(): readonly T[]

Defined in: base.ts:2605

Return the items the popup list renders, in display order.

items                (setItems)
  |  gather          (only with grouping on; result cached until setItems)
  v
display base list
  |  filter query    (only while a query is active)
  v
visible items        (this method's return value)
  |  render
  v
DOM rows
  • If no filter query is active (including while closed), it returns the full list, gathered per gatherGroups when grouping is on.
  • If a filter query is active, it returns the matching subset.
  • Disabled items are included. They render (grayed); only actions skip them (keyboard focus, the choose-all row's subset).
  • Returns the LIVE internal array, typed read-only. Never mutate it (see getItems).
  • Subclasses use it too (e.g. for selection-by-index), and may override it to add a step: LLSelectMultiple does for hideChosenRows.
Returns

readonly T[]

Inherited from

LLSelectBase.getVisibleItems

Open & close

open()

open(): void

Defined in: base.ts:1057

Open the popup. Builds item elements lazily, attaches the positioner (which auto-closes if the trigger is scrolled out of view), wires the outside-click handler, and moves keyboard focus into the item list. No-op if already open.

Returns

void

Inherited from

LLSelectBase.open

close()

close(): void

Defined in: base.ts:1138

Close the popup. Detaches positioner and outside-click listener, clears the item DOM, and resets focused-item state. No-op if already closed.

Focus return is decided automatically: when filterable: true and DOM focus is still on the filter input at the moment of close (Esc on empty filter, single-select pick, click on non-focusable area outside), focus is returned to the trigger. Tab-away and outside clicks on focusable elements have already moved focus elsewhere, so we leave it alone.

Returns

void

Inherited from

LLSelectBase.close

toggle()

toggle(): void

Defined in: base.ts:1240

Open if closed, close if open.

Returns

void

Inherited from

LLSelectBase.toggle

isOpened()

isOpened(): boolean

Defined in: base.ts:1256

Whether the popup is currently open.

  • Pairs with isDisabled() (state read via method).
  • The same state is mirrored on the DOM as CSS hooks: data-state="open|closed" on the trigger, classIdMap.openClass on the root.
Returns

boolean

Inherited from

LLSelectBase.isOpened

Trigger

setPlaceholder()

setPlaceholder(placeholder): void

Defined in: base.ts:1331

Replace the trigger placeholder text at runtime. It is one of the two settings with a runtime setter (the other is setUiTranslationPack); both are copy. The rule: LLSelectBaseSettings.

  • null = fall back to the pack default (uiTranslationPack.triggerPlaceholder), mirroring an unset constructor placeholder. An explicit value keeps winning over later setUiTranslationPack calls, exactly like the constructor input.
  • Takes effect immediately. Visible only while nothing is chosen - the placeholder never renders otherwise (the trigger is still re-rendered, which also refreshes the hidden accessible-value mirror).
Parameters
Parameter Type
placeholder string | null
Returns

void

Inherited from

LLSelectBase.setPlaceholder

Filtering

getFilterQuery()

getFilterQuery(): string

Defined in: base.ts:2635

Return the filter input's current query, exactly the string passed to filterFn.

  • It is '' while the popup is closed, the filter is inactive, or the input is empty.
  • It resets on close: each open cycle starts empty.
Returns

string

Inherited from

LLSelectBase.getFilterQuery

Disabling

setDisabled()

setDisabled(value): void

Defined in: base.ts:1391

Enable or disable the whole control. Disabled: the trigger gets aria-disabled + data-disabled (never the native disabled attribute, which would suppress the hover / focus events a tooltip needs), opening is blocked, an open popup closes, and the trigger leaves the tab order unless focusableWhenDisabled is set. Stored as state, mirroring setItems / setChosenItems (this design keeps mutable state out of settings).

Parameters
Parameter Type
value boolean
Returns

void

Inherited from

LLSelectBase.setDisabled

isDisabled()

isDisabled(): boolean

Defined in: base.ts:1402

Whether the whole control is disabled.

Returns

boolean

Inherited from

LLSelectBase.isDisabled

i18n

getUiTranslationPack()

getUiTranslationPack(): Readonly<LLSelectUiTranslationPack>

Defined in: base.ts:1283

Return the resolved UI strings: the built-in English defaults merged with the uiTranslationPack setting.

  • Reuse these in your own UI instead of keeping a second translation source. For example, a tag remove button tooltip: sel.getUiTranslationPack().tagRemoveButtonAriaLabel(label).
  • Returns the LIVE object. Treat it as immutable, like getItems.
Returns

Readonly<LLSelectUiTranslationPack>

Inherited from

LLSelectBase.getUiTranslationPack

setUiTranslationPack()

setUiTranslationPack(uiTranslationPack): void

Defined in: base.ts:1304

Replace the UI-translation pack at runtime, so switching language needs no re-new.

  • One of the two settings with a runtime setter (the other is setPlaceholder); both are copy. The rule: LLSelectBaseSettings.
  • The pack is resolved exactly like the constructor's: merged over the built-in English pack, NOT over the previously set pack.
  • An explicit constructor placeholder keeps winning over the new pack's triggerPlaceholder.
  • Re-renders the trigger and the open popup.
  • Also re-applies the pack-owned attributes rerender() cannot reach: the filter input placeholder and its fallback aria-label.
  • The clear button needs no such step: rerender() rebuilds it, and the rebuild reads the new pack - unless a createTriggerClearButtonEl override returns the previous element, which then owns the label.
Parameters
Parameter Type
uiTranslationPack Partial<LLSelectUiTranslationPack>
Returns

void

Inherited from

LLSelectBase.setUiTranslationPack

Lifecycle

rerender()

rerender(): void

Defined in: base.ts:1205

Rebuild the trigger and (while open) the popup list from current state.

  • Use it after mutating item OBJECTS in place (e.g. users[0].name = 'X'). The library cannot detect that on its own.
  • It re-derives the display order (the gatherGroups gather).
  • While the filter is active, it re-runs the filter against the current item text.
  • The refresh is purely visual: it does NOT fire onChange and does NOT run onItemsChanged.
  • Orchestrator: composes renderTrigger + renderPopupList; touches no DOM directly.
Returns

void

Inherited from

LLSelectBase.rerender

destroy()

destroy(): void

Defined in: base.ts:1225

Tear down the instance: close the popup (which detaches every document / window listener and the positioner), unwire labelEl (click listener removed, a minted id removed), remove the library's class and inline styles from the caller's mount element, and empty it. Idempotent. The instance must not be used afterwards.

  • REQUIRED before discarding an instance that might be OPEN (framework wrappers: call this on unmount) - skipping it there leaks the outside-click / focusout / scroll / resize listeners.
  • Discarding a CLOSED instance without destroy() leaks nothing; it only leaves the root class and overflow-anchor style on the mount.
Returns

void

Inherited from

LLSelectBase.destroy

Constructor

new LLSelectSingle<T, GroupKey, S>(targetEl, settings?): LLSelectSingle<T, GroupKey, S>

Defined in: single.ts:92

Build the control inside targetEl.

  • Settings are resolved once here; missing fields get defaults.
  • They are frozen afterwards, except placeholder and uiTranslationPack, which have runtime setters; the rule is at LLSelectBaseSettings.
  • This plain form infers T from a typed callback in settings whose signature contains T (itemToStringFn: (u: User) => ...). With no such callback, pass T explicitly: new LLSelectSingle<string>(...).
Parameters
Parameter Type
targetEl HTMLElement
settings? LLSelectSingleSettingsInput<T, GroupKey>
Returns

LLSelectSingle<T, GroupKey, S>

Overrides

LLSelectBase.constructor

Constructor

new LLSelectSingle<T, GroupKey, S>(targetEl, settings?, subclassSettings?): LLSelectSingle<T, GroupKey, S>

Defined in: single.ts:98

Subclass form. subclassSettings is the typed pass-through for subclasses that extend the settings bag further; see LLSelectBase's S param.

Parameters
Parameter Type
targetEl HTMLElement
settings? LLSelectSettingsInputOf<S>
subclassSettings? Omit<S, keyof LLSelectSingleSettings<T, GroupKey>>
Returns

LLSelectSingle<T, GroupKey, S>

Overrides

LLSelectBase<T, GroupKey, S>.constructor

DOM elements

rootEl

readonly rootEl: HTMLElement

Defined in: base.ts:650

The caller-passed mount element, now decorated as the select's root. Library does not replace this node, so the caller's original reference, id, and data-* attributes stay valid.

Inherited from

LLSelectBase.rootEl

triggerEl

readonly triggerEl: HTMLElement

Defined in: base.ts:660

The interactive trigger element. Receives focus, click, and keydown events; carries aria-expanded, aria-controls, and data-state="open|closed". Its role depends on the filter mode: combobox while the filter is inactive (it then also hosts aria-activedescendant) and button while a filterable popup is open (the filter input hosts aria-activedescendant). See docs/llm/A11Y.md.

Inherited from

LLSelectBase.triggerEl

triggerContentEl

readonly triggerContentEl: HTMLElement

Defined in: base.ts:667

Inner span inside the trigger where text/tags are written. Subclasses' renderTriggerContent writes here so the sibling arrow slot is preserved across re-renders.

Inherited from

LLSelectBase.triggerContentEl

popupEl

readonly popupEl: HTMLElement

Defined in: base.ts:684

The outer popup wrapper. Has no ARIA role itself - it just hosts the popup chrome (future: filter input, toggle-all control) and the inner popupListEl. Hidden via the hidden attribute when closed; positioned via inline styles by the positioner when open.

Inherited from

LLSelectBase.popupEl

popupListEl

readonly popupListEl: HTMLElement

Defined in: base.ts:692

The inner element with role="listbox", holding the item children. Sits inside popupEl so siblings (filter input, toggle-all) can live above it without violating ARIA's "listbox children must be options" rule. The trigger's aria-controls points to this element.

Inherited from

LLSelectBase.popupListEl

classIdMap

readonly classIdMap: LLSelectClassIdMap

Defined in: base.ts:697

Resolved class names and ids for this instance.

Inherited from

LLSelectBase.classIdMap

triggerValueEl

protected readonly triggerValueEl: HTMLElement

Defined in: base.ts:676

Hidden root-level span (sibling of the trigger) mirroring the current value as plain text. Kept in sync by commitTriggerContentToDom; referenced by the filterable-mode aria-labelledby chain (see classIdMap.triggerValueId). Outside the trigger so triggerEl.textContent stays exactly the visible content.

Inherited from

LLSelectBase.triggerValueEl

Subclassing: reactions

onOpened()

protected onOpened(): void

Defined in: base.ts:1438

Subclass hook: called once after the popup finishes opening. Default no-op. The onOpen setting fires alongside this (both run) - hook for subclass logic, setting for consumer notification.

Returns

void

Inherited from

LLSelectBase.onOpened

onClosed()

protected onClosed(): void

Defined in: base.ts:1443

Subclass hook: called once after the popup finishes closing. Pairs with the onClose setting (both run).

Returns

void

Inherited from

LLSelectBase.onClosed

onChosenChanged()

protected onChosenChanged(): void

Defined in: base.ts:1450

Subclass hook: called after the chosen state actually changed, right before the variant's onChange setting fires (hook first, both run - same pairing as onOpened / onClosed). Default no-op.

Returns

void

Inherited from

LLSelectBase.onChosenChanged

withUserChangeSource()

protected withUserChangeSource<R>(fn): R

Defined in: base.ts:1959

Run fn with chosen-state changes attributed to the user. The library wraps exactly its pointer / keyboard entry points with it - option activation, the tag remove button, the clear button, the choose-all row; everything else reports 'api'.

Type Parameters
Type Parameter
R
Parameters
Parameter Type
fn () => R
Returns

R

Inherited from

LLSelectBase.withUserChangeSource

onLeadingRowActivated()

protected onLeadingRowActivated(): void

Defined in: base.ts:1974

Subclass hook: the leading row was activated - Enter while it is focused (subclasses also wire their row's click handler to this). Default no-op.

Returns

void

Inherited from

LLSelectBase.onLeadingRowActivated

onItemActivated()

protected onItemActivated(item): void

Defined in: single.ts:185

Pick this item as the chosen item and close the popup.

Parameters
Parameter Type
item T
Returns

void

Overrides

LLSelectBase.onItemActivated

onItemsChanged()

protected onItemsChanged(): void

Defined in: single.ts:244

Re-match the chosen item against the new list after setItems.

  • If the list no longer holds it (by compareFn), it is dropped and onChange fires.
  • If the list holds a compareFn-equal but DIFFERENT object (track by style reload: same key, fresh fields), the stored reference is swapped to the list's object. The logical value did not change, so onChange does not fire.
  • The trigger content re-renders after every setItems, because a custom createTriggerContentElFn receives items.
  • The arrow re-renders only when the chosen item is dropped, because that runs the whole trigger.
Returns

void

Overrides

LLSelectBase.onItemsChanged

Subclassing: rendering

renderTrigger()

protected renderTrigger(): void

Defined in: base.ts:1495

Orchestrator: composes the clear-button rebuild + renderTriggerContent + renderTriggerArrow to (re)build the whole trigger from state; touches no DOM directly. Subclasses normally override renderTriggerContent, not this.

  • Runs on every change of the chosen value, the placeholder or the pack.
  • rerender() runs it too.
  • While clearable is on, the first run builds the clear button and every later run swaps it for whatever createTriggerClearButtonEl returns: a fresh button from the base implementation, like the arrow. An override may return the previous element; it is then kept in place.
  • If the button is rebuilt and the old one held focus - on it or inside its icon - the rebuilt BUTTON gets it.
  • If the builder returned the same element, nothing was rebuilt, and focus goes back to the node that held it, if that node is still inside the button and can take focus; otherwise focus stays on the button.
  • setItems runs renderTriggerContent alone, because only the content reads the list (the multiple count total, a custom createTriggerContentElFn's items).
  • A setItems that drops the chosen entry runs the whole trigger.
  • Runs once from the LLSelectSingle / LLSelectMultiple constructor, right after super().
  • On that first run a FURTHER subclass's own fields are still undefined: JS runs a subclass's field initializers only after its super constructor returns (virtual-call-in-constructor). An override of a trigger method (renderTriggerContent, or a create*El it calls) that reads such a field sees undefined there.
  • The recipe: put construction-time configuration in the typed subclassSettings constructor param. this.settings is complete before any construction code runs.
  • For genuine instance state, tolerate defaults during construction, or call rerender() at the end of your own constructor.
  • The popup-list methods do NOT run here; they wait for open(). See DESIGN.md "Customization model".
Returns

void

Inherited from

LLSelectBase.renderTrigger

commitTriggerContentToDom()

protected commitTriggerContentToDom(content, plainTextValue?): void

Defined in: base.ts:1518

Write the trigger's content slot (triggerContentEl), replacing whatever was there; the sibling arrow slot is untouched. The single DOM-writing primitive behind every renderTriggerContent path.

  • string -> set as textContent (plain text, NOT parsed as HTML). Used for the default placeholder / itemToString text / count summary.
  • HTMLElement -> inserted as-is via replaceChildren; caller owns the node. Used for whatever the createTriggerContentElFn setting returned.
  • Also mirrors the value into the hidden triggerValueEl (the accessible name source): the string itself, else plainTextValue, else the element's textContent. Pass plainTextValue whenever the element contains labelled controls (tag remove buttons) or icon-only content - the mirror is what AT announces as the field's value. Called by renderTriggerContent - the base default and the LLSelectSingle / LLSelectMultiple overrides.
Parameters
Parameter Type
content string | HTMLElement
plainTextValue? string
Returns

void

Inherited from

LLSelectBase.commitTriggerContentToDom

syncEmptyStateToDom()

protected syncEmptyStateToDom(): void

Defined in: base.ts:1535

Mirror the empty/filled state onto the trigger's data-empty attribute ("true" when isEmpty(), else "false"). A CSS / AT styling hook, independent of the rendered content. Called by the subclass renderTriggerContent overrides.

Returns

void

Inherited from

LLSelectBase.syncEmptyStateToDom

createTriggerArrowContentEl()

protected createTriggerArrowContentEl(state): HTMLElement | SVGElement | null

Defined in: base.ts:1581

Trigger arrow element for the given open state.

  • Default reads createTriggerArrowContentElFn; null (setting unset, or returned for a state) = no arrow for that state.
  • Override only when extending; for one-off arrows pass the setting. Mirrors createTriggerClearButtonEl / createItemContentEl.
Parameters
Parameter Type
state { isOpened: boolean; }
state.isOpened boolean
Returns

HTMLElement | SVGElement | null

Inherited from

LLSelectBase.createTriggerArrowContentEl

renderPopupList()

protected renderPopupList(): void

Defined in: base.ts:1611

Orchestrator: composes createItemEl (build) + computePopupSegments + commitPopupSegmentsToDom (write) to rebuild the popup list from getVisibleItems(); touches no DOM directly. Called by open() and by setItems() while open. Also clamps focusedIndex if the list shrank and re-applies focus visuals.

  • Extend it by wrapping: override, do your work before or after, then call super.renderPopupList(). The ui-select bridge frees its row scopes this way. Or override one of the methods it calls through this: createItemEl, createPopupListLeadingRowEl, itemToGroupKey, getVisibleItems.
  • Its other internals stay private on purpose. They re-establish the itemEls[i] <-> getVisibleItems()[i] alignment as one unit, so no subclass can leave keyboard nav or aria-activedescendant half-synced.
Returns

void

Inherited from

LLSelectBase.renderPopupList

createGroupEl()

protected createGroupEl(key, index, items, itemEls): HTMLElement

Defined in: base.ts:1710

Build a detached group container: role="group" named by groupKeyToString, an aria-hidden visible label element, then the group's item elements. The label content comes from createGroupLabelContentEl (rich header) when non-null, else the plain label text. aria-disabled + data-disabled when the group is disabled. Override for full control of the group element (mirrors createItemEl).

Parameters
Parameter Type Description
key GroupKey the group's key
index number group index in the current render; builds a stable id
items readonly T[] the group's items (for rich content / counts)
itemEls HTMLElement[] the group's already-built option elements
Returns

HTMLElement

Inherited from

LLSelectBase.createGroupEl

createGroupLabelContentEl()

protected createGroupLabelContentEl(key, itemsInGroup): HTMLElement | null

Defined in: base.ts:1744

Group header -> its visible content element (icon / count badge / rich markup). Mirrors createItemContentEl.

  • Default reads createGroupLabelContentElFn, else null so createGroupEl uses plain text from groupKeyToString.
  • The group's accessible name stays groupKeyToString (container aria-label); this fills only the visible, aria-hidden label content.
  • Override only when extending; for one-off rich headers pass the setting.
Parameters
Parameter Type
key GroupKey
itemsInGroup readonly T[]
Returns

HTMLElement | null

Inherited from

LLSelectBase.createGroupLabelContentEl

replacePopupListItemElInDom()

protected replacePopupListItemElInDom(item): void

Defined in: base.ts:1759

Re-render a single item's element in place instead of rebuilding the whole popup list. The DOM work is O(1) regardless of list size, so flipping one selection in a 10k-item list does not recreate 10k nodes (the lookup to find the item is O(n), but that is a cheap comparison loop next to DOM mutation). No-op if the popup is closed or the item is not in the current list. Used by multi-select toggle.

Parameters
Parameter Type
item T
Returns

void

Inherited from

LLSelectBase.replacePopupListItemElInDom

createItemContentEl()

protected createItemContentEl(item): HTMLElement | null

Defined in: base.ts:1852

Item -> the visible content of its list row (icon + text etc.).

  • Default reads createItemContentElFn, else null so createItemEl uses the plain-text default from itemToString.
  • Override only when extending; for one-off rich content pass the setting.
Parameters
Parameter Type
item T
Returns

HTMLElement | null

Inherited from

LLSelectBase.createItemContentEl

createPopupListLeadingRowEl()

protected createPopupListLeadingRowEl(): HTMLElement | null

Defined in: base.ts:1950

Optional non-item role="option" row pinned at the TOP of the listbox: inside the arrow-key ring (ArrowUp from the first item reaches it, Home lands on it, up-actions clamp there) but never inside itemEls, so the itemEls[i] <-> getVisibleItems()[i] alignment is untouched. Rebuilt on every renderPopupList. Base default: null = no leading row. LLSelectMultiple builds its choose-all row here (chooseAllRow setting).

Returns

HTMLElement | null

Inherited from

LLSelectBase.createPopupListLeadingRowEl

replaceLeadingRowElInDom()

protected replaceLeadingRowElInDom(): void

Defined in: base.ts:2029

Rebuild the leading row in place (tri-state / text refresh) without touching the item elements - O(1) DOM work, mirroring replacePopupListItemElInDom. Falls back to a full renderPopupList when the row becomes inapplicable (builder returns null). No-op while closed or when no leading row is rendered.

Returns

void

Inherited from

LLSelectBase.replaceLeadingRowElInDom

createTriggerClearButtonEl()

protected createTriggerClearButtonEl(): HTMLElement

Defined in: base.ts:2410

Build the clear (x) button for the clearable trigger slot.

  • The library owns the button, its click (stops propagation so it never toggles the popup, then clearSelection; a no-op while the control is disabled) and its aria-label (text from uiTranslationPack.triggerClearButtonAriaLabel).
  • createTriggerClearButtonContentElFn optionally fills the icon; else the theme's CSS glyph draws it.
  • The theme hides the button via data-empty while nothing is chosen.
  • It runs on every trigger render (renderTrigger).
  • The first run builds the button.
  • For LLSelectSingle / LLSelectMultiple and their subclasses, that first run is the variant constructor's render, right after super(), so it happens before a FURTHER subclass's field initializers.
  • A direct LLSelectBase subclass gets the button on its first trigger render (its own renderTrigger() call, or rerender() / setPlaceholder); until then the trigger is unrendered and there is no button.
  • Every later run (a value change, setPlaceholder, setUiTranslationPack, rerender()) swaps the button in place, unless this method returns the previous element - then it stays in place.
  • The base implementation returns a fresh element each run, so, like the arrow, nothing put on it from outside survives a render; customize it here.
  • An override that returns the same element every time owns everything the rebuild would otherwise refresh on it, including its aria-label after setUiTranslationPack.
  • An override that reads subclass fields calls rerender() at the end of its constructor, like every other trigger method.
Returns

HTMLElement

Inherited from

LLSelectBase.createTriggerClearButtonEl

createTriggerClearButtonContentEl()

protected createTriggerClearButtonContentEl(): HTMLElement | SVGElement | null

Defined in: base.ts:2537

Clear button's visible content (its x icon).

  • Default reads createTriggerClearButtonContentElFn; null (setting unset, or returned) = no icon - the theme's CSS glyph draws the x.
  • Override only when extending; for one-off icons pass the setting.
Returns

HTMLElement | SVGElement | null

Inherited from

LLSelectBase.createTriggerClearButtonContentEl

createPopupListNoResultsContentEl()

protected createPopupListNoResultsContentEl(query): HTMLElement | null

Defined in: base.ts:2712

The no-results message's visible content (rich empty-state).

  • Default reads createPopupListNoResultsContentElFn; null (setting unset, or returned) = plain text from uiTranslationPack.popupListNoResults.
  • Override only when extending; for one-off content pass the setting.
Parameters
Parameter Type
query string
Returns

HTMLElement | null

Inherited from

LLSelectBase.createPopupListNoResultsContentEl

renderTriggerContent()

protected renderTriggerContent(): void

Defined in: single.ts:152

Orchestrator: composes syncEmptyStateToDom + commitTriggerContentToDom to (re)build the trigger from state; touches no DOM directly.

  • createTriggerContentElFn is tried first; if it returns null or is unset, the default applies.
  • The default is the chosen item's string, or the placeholder when nothing is chosen.
Returns

void

Overrides

LLSelectBase.renderTriggerContent

createItemEl()

protected createItemEl(item, index): HTMLElement

Defined in: single.ts:175

Mark the chosen option aria-selected="true", the rest "false" (APG select-only).

Parameters
Parameter Type
item T
index number
Returns

HTMLElement

Overrides

LLSelectBase.createItemEl

Subclassing: semantics

itemToString()

protected itemToString(item): string

Defined in: base.ts:1841

Map an item to its display string. The library calls this everywhere it needs an item's text: list rows, the single trigger text, default filter.

  • Default reads the itemToStringFn setting, else String(item).
  • Configure via itemToStringFn (no subclass needed).
  • Override only when extending (a new select type); your override replaces the default. For HTML content, subclass createItemEl.
Parameters
Parameter Type
item T
Returns

string

Inherited from

LLSelectBase.itemToString

isItemEffectivelyDisabled()

protected isItemEffectivelyDisabled(item): boolean

Defined in: base.ts:1865

Whether item is effectively disabled - by itemDisabledFn, or because its group is disabled (groupDisabledFn). Group-disabled layers on top, so every item-disabled behavior (no selection, keyboard skip, aria) covers grouped items with no extra code. False when neither applies. The whole-control disabled state (isDisabled()) is a separate layer, not part of this answer.

Parameters
Parameter Type
item T
Returns

boolean

Inherited from

LLSelectBase.isItemEffectivelyDisabled

itemToGroupKey()

protected itemToGroupKey(item): GroupKey | null

Defined in: base.ts:1883

Map an item to its group key, or null when it belongs to no group. The authoritative method: rendering, the gatherGroups gather, and the disabled layer all resolve keys through this method, so an override drives them all - returning keys turns grouping on even with the setting unset (all-null keys = flat list). An override reading external state must call rerender() after that state changes (same contract as mutating item objects).

  • Default reads itemToGroupKeyFn, else null (grouping off).
  • Override only when extending; configure via the setting.
Parameters
Parameter Type
item T
Returns

GroupKey | null

Inherited from

LLSelectBase.itemToGroupKey

groupKeyToString()

protected groupKeyToString(key): string

Defined in: base.ts:1892

Map a group key to its header display text.

  • Default reads groupKeyToStringFn, else String(key).
Parameters
Parameter Type
key GroupKey
Returns

string

Inherited from

LLSelectBase.groupKeyToString

isGroupDisabled()

protected isGroupDisabled(key): boolean

Defined in: base.ts:1900

Whether the whole group key is disabled per groupDisabledFn (false when unset).

Parameters
Parameter Type
key GroupKey
Returns

boolean

Inherited from

LLSelectBase.isGroupDisabled

matchesQuery()

protected matchesQuery(item, query): boolean

Defined in: base.ts:2647

Per-item match predicate for the filter input.

  • Default reads filterFn; else case-insensitive substring on itemToString.
  • Override only when extending (subclass-wide custom matching); for a one-off match rule pass the setting.
Parameters
Parameter Type
item T
query string
Returns

boolean

Inherited from

LLSelectBase.matchesQuery

isEmpty()

protected isEmpty(): boolean

Defined in: single.ts:167

No selection iff chosenItem is unset. Drives the trigger's data-empty.

Returns

boolean

Overrides

LLSelectBase.isEmpty

clearSelection()

protected clearSelection(): void

Defined in: single.ts:194

Clear button empties the single selection to undefined.

Returns

void

Overrides

LLSelectBase.clearSelection

Subclassing: focus

findNextEnabledIndex()

protected findNextEnabledIndex(start, step, list): number

Defined in: base.ts:1910

First enabled index scanning from start (inclusive) by step (+1 / -1). Returns -1 if no enabled item lies in that direction. Used to skip disabled items during keyboard nav and initial focus.

Parameters
Parameter Type
start number
step number
list readonly T[]
Returns

number

Inherited from

LLSelectBase.findNextEnabledIndex

setFocusedIndex()

protected setFocusedIndex(index): void

Defined in: base.ts:1994

Move keyboard focus to the item at index. The value is clamped to [-1, items.length-1]; pass -1 to clear focus. Updates the focused class, aria-activedescendant, and scrolls the item into view. No-op if the clamped value equals the current focused index.

Parameters
Parameter Type
index number
Returns

void

Inherited from

LLSelectBase.setFocusedIndex

focusLeadingRow()

protected focusLeadingRow(): boolean

Defined in: base.ts:2012

Move keyboard focus onto the leading row. Returns whether the row is now focused (false = none is rendered, nothing changed). The item focus is cleared (focusedIndex becomes -1). Protected so a subclass can wire its leading row's click to focus-then-activate (mirroring how item clicks call setFocusedIndex before onItemActivated) and use it in focusInitial (the leading row is the listbox's FIRST option).

Returns

boolean

Inherited from

LLSelectBase.focusLeadingRow

focusInitial()

protected focusInitial(): void

Defined in: single.ts:203

On open, focus the chosen item (if present and enabled), else the first enabled item. Indices are into getVisibleItems() (the rendered list).

Returns

void

Overrides

LLSelectBase.focusInitial

computeTypeaheadClosedStartIndex()

protected computeTypeaheadClosedStartIndex(list): number

Defined in: single.ts:224

Closed-state typeahead searches relative to the CHOSEN item, like a native <select>: typing its initial cycles to the next match.

  • Returns -1 when nothing is chosen, or the chosen item left the list; the search then starts from the top.
Parameters
Parameter Type
list readonly T[]
Returns

number

Overrides

LLSelectBase.computeTypeaheadClosedStartIndex

State (protected)

settings

protected readonly settings: S

Defined in: base.ts:707

Resolved settings (defaults applied) - ONE bag for the whole hierarchy. Typed by the class's S param: a subclass that extends the settings passes its resolved extra fields through the constructor's subclassSettings param, and this field is S with no re-typing (see LLSelectSingle / LLSelectMultiple).

Inherited from

LLSelectBase.settings

items

protected items: T[] = []

Defined in: base.ts:718

Current item list. Defensive copy of what setItems was given.

Inherited from

LLSelectBase.items

focusedIndex

protected focusedIndex: number = -1

Defined in: base.ts:726

Index (into items) of the currently keyboard-focused item, or -1 when nothing is focused (closed popup, or no items).

Inherited from

LLSelectBase.focusedIndex

changeSource

protected changeSource: LLSelectChangeSource = 'api'

Defined in: base.ts:736

Who the change being applied right now is attributed to; the variants' onChange firing reads it. Set to 'user' by withUserChangeSource and consumed (reset to 'api') by the first onChange fired, so a nested api-driven change inside an onChange handler reports 'api'.

Inherited from

LLSelectBase.changeSource

chosenItem

protected chosenItem: T | undefined = undefined

Defined in: single.ts:80

Currently chosen item, or undefined if none.

Settings

Base

LLSelectOutsideClickBehavior

LLSelectOutsideClickBehavior = "pass-through" | "block"

Defined in: base.ts:30

What happens when the user clicks outside an open popup.

  • 'pass-through' (default): close the popup; the outside click still triggers its normal action (button click, link navigation, etc.).
  • 'block': close the popup only; the outside click is swallowed so no underlying handler or default action fires. Avoids accidental side effects when the user only intended to dismiss the dropdown.

LLSelectBaseSettings

Defined in: base.ts:74

Resolved (defaults applied) settings shared by all select variants. Subclasses (LLSelectSingle, LLSelectMultiple) extend this with their mode-specific options such as onChange.

Settings are frozen after the constructor. What still changes at runtime:

  • State changes by method, and never was a setting: the items (setItems), the chosen value (setChosenItem / setChosenItems), disabled (setDisabled).
  • Two settings have a setter, because they are text: uiTranslationPack (setUiTranslationPack) and placeholder (setPlaceholder).
  • Every other setting is fixed for the instance's lifetime.
  • To change a fixed setting, build a new instance. One build takes about 0.2 ms.
  • If a setting must vary at runtime, use its function form where one exists. filterable: (items) => boolean is re-evaluated on every open (and consulted by closed-state typeahead - see the setting).
Extended by
Type Parameters
Type Parameter Default type
T -
GroupKey string
Items
compareFn

compareFn: (a, b) => boolean

Defined in: base.ts:149

Equality predicate for item values - return true when a and b are the same item.

  • Required for non-primitive T. The default compares by identity: ===, except that NaN equals NaN (SameValueZero, the same rule Set uses), so every code path agrees on what "the same item" means.
  • Used for selection, dedup, and matching the chosen item back to the list.
  • Symmetric: do not depend on which argument is the candidate vs the existing item.
Parameters
Parameter Type
a T
b T
Returns

boolean

itemToStringFn

itemToStringFn: ((item) => string) | null

Defined in: base.ts:295

Item -> display string, without subclassing.

  • null (default) = String(item).
  • Read by the itemToString method's default; used for list text, the single trigger text, the option's accessible name, and the default filter. Inserted as textContent (plain text, NOT parsed as HTML).
  • For rich content (icons etc.), pass createItemContentElFn.
createItemContentElFn

createItemContentElFn: ((item) => HTMLElement | null) | null

Defined in: base.ts:331

Item -> the visible content ELEMENT of its list row, without subclassing.

  • Return an HTMLElement and the library inserts it as-is (you own the node); it becomes the row's visible content.
  • null = plain textContent from itemToString. This is the default, both when the setting is unset and when your function returns null for a particular item.
  • Fills the VISIBLE content only. You never touch aria-*: when this returns an element the library sets the option's aria-label from itemToString, so the accessible name + match text stay owned by itemToString no matter what you render (icon-only, reordered, ...). To make the spoken/matched text differ from the visible content, set the two independently: itemToStringFn for the name/matching, createItemContentElFn for the look.
  • For full control of the option element (tag / wiring), subclass createItemEl instead.
  • Runs per rendered row per render, and re-runs whenever a row is rebuilt: open, filter, setItems, AND chosen-state changes (both modes replace the affected rows in place while the popup is open). Content that reads selection state (e.g. a checkmark on the chosen row via createCheckmarkSvgEl) therefore stays fresh; keep the function cheap.
Example
// List shows an icon + the item text; screen readers announce just that text.
  itemToStringFn: (lang) => lang.name,
  createItemContentElFn: (lang) => {
    const row = document.createElement('span')
    const icon = document.createElement('i')
    icon.className = `mdi mdi-${lang.icon}`
    icon.setAttribute('aria-hidden', 'true') // decorative
    row.append(icon, lang.name)
    return row
  }
Accessible name
ariaLabel

ariaLabel: string | null

Defined in: base.ts:108

Accessible name of the field, like the <label> text of a native <select> (e.g. 'Country').

  • Applied to the trigger, the popup listbox, and (while the filter is active) the filter input; per-mode wiring: docs/llm/A11Y.md "Accessible name".
  • The name resolves by the FIRST set rung, mirroring the W3C accessible-name computation order:
    1. ariaLabelledBy.
    2. ariaLabel (this setting).
    3. labelEl - its element's id becomes the resolved ariaLabelledBy.
    4. None set: the field is unnamed. A combobox requires a name (WAI-ARIA 1.2), so one console.warn per page reports the first offender.
  • null (default): this rung is skipped. An empty or whitespace-only string counts as unset too - the accname computation skips a blank aria-label, and so does the ladder.
ariaLabelledBy

ariaLabelledBy: string | null

Defined in: base.ts:121

Space-separated DOM id(s) of the visible label element(s) naming the field; forwarded as aria-labelledby to the same elements as ariaLabel.

  • Prefer this over ariaLabel when a visible label element exists: the spoken name then always matches the visible text.
  • null (default): not forwarded; see ariaLabel for the naming requirement. An empty or whitespace-only string counts as unset too, same as ariaLabel.
  • Rung 1 of the resolution order (the numbered list at ariaLabel): it wins whenever set, matching the ARIA name computation.
labelEl

labelEl: HTMLElement | null

Defined in: base.ts:137

The widget's visible label element - the one foreign element the library touches. Emulates native <label for> (which cannot target these divs) in both directions:

  • clicking it focuses the trigger (focus ONLY; native <select> does not open on label click, neither does this);
  • it feeds the accessible name as rung 3 of the resolution order (the numbered list at ariaLabel): with neither aria setting given, the label's id becomes the resolved ariaLabelledBy (an id is minted from classIdMap.labelId if the element has none) - a live reference, so later label text changes stay correct.
  • null = no label element; the name ladder just skips this rung.
  • destroy() removes the click listener and a minted id.
Events
onOpen

onOpen: (() => void) | null

Defined in: base.ts:406

Fired right after the popup opens. An open() call that does not actually open the popup (already open, a disabled control, or a trigger scrolled out of view or clipped) does not fire it. Fires in ADDITION to the protected onOpened hook - the setting is for consumers, the hook for subclasses; both run. null (default) = nothing.

onClose

onClose: (() => void) | null

Defined in: base.ts:412

Fired right after the popup closes. A no-op close() does not fire it. Additive with the protected onClosed hook, like onOpen.

Trigger
placeholder

placeholder: string

Defined in: base.ts:89

Text shown in the trigger when nothing is selected. App copy: an explicit value always wins; when unset, the locale default uiTranslationPack.triggerPlaceholder is used ('Please select' in English).

createTriggerArrowContentElFn

createTriggerArrowContentElFn: ((state) => HTMLElement | SVGElement | null) | null

Defined in: base.ts:163

The trigger arrow slot's content ELEMENT (typically a dropdown chevron or triangle). Called whenever the arrow may need to change - including on every open/close - so the returned element can vary with isOpened.

  • fn returns null - no arrow for that state.
  • setting is null (default) - the library adds nothing to the arrow slot.
clearable

clearable: boolean

Defined in: base.ts:188

Whether the trigger shows a clear (x) button that empties the selection.

  • Default false.
  • Clearing sets the empty value: undefined for a single select, [] for a multiple. It goes through the normal setters, so onChange fires with that empty value. There is no separate clear event.
  • The empty value is not configurable, and no library makes it so: "nothing chosen" already exists before the first choice, so the value types carry undefined either way.
  • If your model is a plain type like string and must never hold undefined, pick one of these:
    • Add a real "none" item (for example '' shown as "(none)") and skip clearable. The model then stays string after the first choice, exactly like a native <select> with a placeholder option.
    • Keep clearable and coerce in onChange: item ?? ''.
  • "Clear" means back to empty and the placeholder, never "back to some default option". If you want a default instead, set it yourself in onChange.
  • The button sits in its OWN trigger slot (like the arrow, so it never collides with createTriggerContentElFn), is tabindex="-1", and carries an aria-label. The theme hides it via data-empty while nothing is selected.
createTriggerClearButtonContentElFn

createTriggerClearButtonContentElFn: (() => HTMLElement | SVGElement | null) | null

Defined in: base.ts:195

Content ELEMENT of the clear button (its x icon), mirroring createTriggerArrowContentElFn. null (default) = the theme's CSS glyph. The library always owns the button, its click (clears + stops propagation) and aria; this only fills the icon.

Filtering
filterable

filterable: boolean | ((items) => boolean)

Defined in: base.ts:212

Whether the popup includes a filter input.

  • false (default): never.
  • true: always.
  • Predicate (items) => boolean: conditional - evaluated against the CURRENT full item list each time the popup OPENS (never mid-open; a setItems crossing the threshold applies on the next open). E.g. filterable: (items) => items.length > 10. A printable key pressed while CLOSED also calls the predicate, read-only, to decide whether prefix typeahead may take the key - keep it cheap and side-effect free. The ARIA mode follows the evaluated value per open cycle: active = trigger role="button", focus moves to the input; inactive = exactly like filterable: false (trigger stays role="combobox", focus stays on the trigger). See docs/llm/A11Y.md and docs/llm/DESIGN.md.
filterFn

filterFn: ((item, query) => boolean) | null

Defined in: base.ts:234

Predicate used by the filter input; return true to keep the item. null (default) means the built-in case-insensitive substring match against the item's resolved text (itemToStringFn / itemToString). Pass a custom function for fuzzy / domain-specific matching.

  • query is the RAW input value: not trimmed and not lower-cased. Normalize it yourself (the built-in lower-cases both sides; it does not trim).
  • Not called while the query is empty (an empty box shows every item), but a whitespace-only query (e.g. " ") does call it.
createPopupListNoResultsContentElFn

createPopupListNoResultsContentElFn: ((query) => HTMLElement | null) | null

Defined in: base.ts:251

The no-results message's visible content ELEMENT, without subclassing. Mirrors createItemContentElFn: the library owns the message container (role="status", class, show/hide), this fills its content only.

  • query is the current filter query ('' when the filter is inactive or the list is simply empty), so "Nothing matches " is possible.
  • Return an HTMLElement: inserted as the content (you own it; include real text - the status region announces its TEXT content).
  • null (setting default, or returned): plain text from uiTranslationPack.popupListNoResults. Re-evaluated every time the message is shown (the query may differ). The DOM is refreshed only when the resolved TEXT changes - the status region is keyed on text so it announces once, not per keystroke - so rich content whose visible markup varies while its text stays constant is not re-rendered.
Grouping
itemToGroupKeyFn

itemToGroupKeyFn: ((item) => GroupKey | null) | null

Defined in: base.ts:342

Item -> its group's key (identity), enabling optgroup rendering.

  • null (setting, default): grouping off - flat list, no headers.
  • fn returns null: this item is in no group; renders ungrouped.
  • Contiguous items with an equal key (per groupKeyCompareFn) form one group. By default non-contiguous data is first gathered into display order (gatherGroups); with gatherGroups: false the data must be pre-sorted by group. See docs/llm/DESIGN.md.
gatherGroups

gatherGroups: boolean

Defined in: base.ts:359

Whether the library gathers non-contiguous groups before rendering (true, default). Grouping renders contiguous runs, so scattered items sharing a key would otherwise produce a duplicate header per gap.

  • true: the DISPLAY order is derived via gatherItemsByGroupKey: groups in first-appearance order, within-group order kept, ungrouped (null-key) items in place. The data itself (items / getItems()) is never reordered, and already-contiguous data is detected in one scan and used as-is.
  • false: strict mode - you guarantee the data is pre-sorted by group; a key reappearing after a gap renders a duplicate header and console.warns, so a broken sort is surfaced instead of silently fixed. No effect while grouping is off (every key null).
groupKeyCompareFn

groupKeyCompareFn: ((a, b) => boolean) | null

Defined in: base.ts:369

Equality for two group keys; decides whether items share a group (both the gatherGroups gather and the contiguous-run rendering use it).

  • null (default) = identity, the same rule as the default compareFn (===, plus NaN equals NaN); right for string / number keys.
  • Supply only when GroupKey is an object without usable reference identity.
  • Mirrors compareFn, one level up.
groupKeyToStringFn

groupKeyToStringFn: ((groupKey) => string) | null

Defined in: base.ts:376

Group key -> the header's display text. The i18n customization point: keep keys stable, translate here.

  • null (default) = String(groupKey).
groupDisabledFn

groupDisabledFn: ((groupKey) => boolean) | null

Defined in: base.ts:384

Predicate: is this whole group disabled?

  • null (default) = no group disabled.
  • true = every item in the group is treated as disabled (layers on top of itemDisabledFn).
createGroupLabelContentElFn

createGroupLabelContentElFn: ((groupKey, itemsInGroup) => HTMLElement | null) | null

Defined in: base.ts:397

Group header -> its visible content ELEMENT (icon / count badge / rich markup), without subclassing. Mirrors createItemContentElFn.

  • Return an HTMLElement and the library inserts it as the header's visible content; the group's accessible name stays groupKeyToString (on the container aria-label) and the label element stays aria-hidden.
  • null (default, or returned for a group) = plain text from groupKeyToString.
  • itemsInGroup is the group's items, so you can render "Fruits (4)" or a summary without recomputing the grouping.
outsideClickBehavior

outsideClickBehavior: LLSelectOutsideClickBehavior

Defined in: base.ts:154

See LLSelectOutsideClickBehavior.

popupWidthPolicy

popupWidthPolicy: LLSelectWidthPolicy

Defined in: base.ts:267

How the popup decides its width. Does NOT affect the trigger - trigger width is always whatever your CSS says.

  • 'fit-content' (default): popup width grows to its own content (items, filter input, ...) and never shrinks below the trigger's width - the native <select> dropdown behavior, minus its viewport overflow: auto-shifts and width-clamps when the natural width would not fit. Direction-aware: in an RTL context (getComputedStyle(trigger).direction === 'rtl', read once per open) it right-aligns to the trigger and grows LEFTWARD, the mirror of LTR.
  • 'match-trigger': popup width equals trigger width; long item text wraps inside the popup.
Disabling
itemDisabledFn

itemDisabledFn: ((item) => boolean) | null

Defined in: base.ts:277

Predicate deciding whether an individual item is disabled. null (default) = nothing disabled. A disabled item is not selectable (click / Enter) and is skipped by keyboard nav; it keeps role="option" plus aria-disabled. Re-evaluated on every render (never cached). For a generic T this is the only way to mark items - the library cannot read a disabled field off an unknown type. See docs/llm/DESIGN.md.

focusableWhenDisabled

focusableWhenDisabled: boolean

Defined in: base.ts:285

When the control is disabled via setDisabled(true), whether the trigger stays in the tab order (tabindex="0"). false (default) takes it out (-1). Set true so keyboard / AT users can focus the disabled control to read a "why disabled" tooltip.

i18n
uiTranslationPack

uiTranslationPack: LLSelectUiTranslationPack

Defined in: base.ts:222

Chrome strings (AT labels + generated text): the i18n customization point. Resolved against English: pass a language pack whole (uiTranslationPack: zhTW from @llselect/core/i18n) or override single keys (uiTranslationPack: { ...zhTW, filterInputPlaceholder: '...' }). Key-by-key contract (incl. what null means where allowed): LLSelectUiTranslationPack.

CSS
cssClassPrefix

cssClassPrefix: string

Defined in: base.ts:82

Prefix used for every CSS class and DOM id the library generates (default 'llselect'). NOTE: the shipped themes target the default prefix only - a custom prefix means bringing your own CSS. Reference the resolved names via instance.classIdMap instead of hardcoding strings.


LLSelectSettingsInputOf

LLSelectSettingsInputOf<S> = Partial<Omit<S, "uiTranslationPack">> & object

Defined in: base.ts:423

Constructor input for a resolved settings bag S: every field optional, and uiTranslationPack accepts a PARTIAL pack (missing keys fall back to English). Shared by the base / single / multiple *SettingsInput types; use it for a subclass wrapper that extends the settings bag.

Type Declaration
uiTranslationPack?

optional uiTranslationPack?: Partial<LLSelectUiTranslationPack>

Type Parameters
Type Parameter
S extends object

LLSelectBaseSettingsInput

LLSelectBaseSettingsInput<T, GroupKey> = LLSelectSettingsInputOf<LLSelectBaseSettings<T, GroupKey>>

Defined in: base.ts:433

Constructor-time settings input - every field is optional and missing fields fall back to the library defaults.

Type Parameters
Type Parameter Default type
T -
GroupKey string

Single

LLSelectSingleTriggerContext

Defined in: single.ts:14

Context passed to LLSelectSingleSettings.createTriggerContentElFn.

Type Parameters
Type Parameter
T
Properties
chosenItem

chosenItem: T | undefined

Defined in: single.ts:15

items

items: readonly T[]

Defined in: single.ts:16


LLSelectSingleSettings

Defined in: single.ts:26

Resolved (defaults applied) settings for LLSelectSingle: the base settings plus the single-mode fields - the runtime type of this.settings, one bag built complete in the constructor.

Extends
Type Parameters
Type Parameter Default type
T -
GroupKey string
Items
compareFn

compareFn: (a, b) => boolean

Defined in: base.ts:149

Equality predicate for item values - return true when a and b are the same item.

  • Required for non-primitive T. The default compares by identity: ===, except that NaN equals NaN (SameValueZero, the same rule Set uses), so every code path agrees on what "the same item" means.
  • Used for selection, dedup, and matching the chosen item back to the list.
  • Symmetric: do not depend on which argument is the candidate vs the existing item.
Parameters
Parameter Type
a T
b T
Returns

boolean

Inherited from

LLSelectBaseSettings.compareFn

itemToStringFn

itemToStringFn: ((item) => string) | null

Defined in: base.ts:295

Item -> display string, without subclassing.

  • null (default) = String(item).
  • Read by the itemToString method's default; used for list text, the single trigger text, the option's accessible name, and the default filter. Inserted as textContent (plain text, NOT parsed as HTML).
  • For rich content (icons etc.), pass createItemContentElFn.
Inherited from

LLSelectBaseSettings.itemToStringFn

createItemContentElFn

createItemContentElFn: ((item) => HTMLElement | null) | null

Defined in: base.ts:331

Item -> the visible content ELEMENT of its list row, without subclassing.

  • Return an HTMLElement and the library inserts it as-is (you own the node); it becomes the row's visible content.
  • null = plain textContent from itemToString. This is the default, both when the setting is unset and when your function returns null for a particular item.
  • Fills the VISIBLE content only. You never touch aria-*: when this returns an element the library sets the option's aria-label from itemToString, so the accessible name + match text stay owned by itemToString no matter what you render (icon-only, reordered, ...). To make the spoken/matched text differ from the visible content, set the two independently: itemToStringFn for the name/matching, createItemContentElFn for the look.
  • For full control of the option element (tag / wiring), subclass createItemEl instead.
  • Runs per rendered row per render, and re-runs whenever a row is rebuilt: open, filter, setItems, AND chosen-state changes (both modes replace the affected rows in place while the popup is open). Content that reads selection state (e.g. a checkmark on the chosen row via createCheckmarkSvgEl) therefore stays fresh; keep the function cheap.
Example
// List shows an icon + the item text; screen readers announce just that text.
  itemToStringFn: (lang) => lang.name,
  createItemContentElFn: (lang) => {
    const row = document.createElement('span')
    const icon = document.createElement('i')
    icon.className = `mdi mdi-${lang.icon}`
    icon.setAttribute('aria-hidden', 'true') // decorative
    row.append(icon, lang.name)
    return row
  }
Inherited from

LLSelectBaseSettings.createItemContentElFn

Accessible name
ariaLabel

ariaLabel: string | null

Defined in: base.ts:108

Accessible name of the field, like the <label> text of a native <select> (e.g. 'Country').

  • Applied to the trigger, the popup listbox, and (while the filter is active) the filter input; per-mode wiring: docs/llm/A11Y.md "Accessible name".
  • The name resolves by the FIRST set rung, mirroring the W3C accessible-name computation order:
    1. ariaLabelledBy.
    2. ariaLabel (this setting).
    3. labelEl - its element's id becomes the resolved ariaLabelledBy.
    4. None set: the field is unnamed. A combobox requires a name (WAI-ARIA 1.2), so one console.warn per page reports the first offender.
  • null (default): this rung is skipped. An empty or whitespace-only string counts as unset too - the accname computation skips a blank aria-label, and so does the ladder.
Inherited from

LLSelectBaseSettings.ariaLabel

ariaLabelledBy

ariaLabelledBy: string | null

Defined in: base.ts:121

Space-separated DOM id(s) of the visible label element(s) naming the field; forwarded as aria-labelledby to the same elements as ariaLabel.

  • Prefer this over ariaLabel when a visible label element exists: the spoken name then always matches the visible text.
  • null (default): not forwarded; see ariaLabel for the naming requirement. An empty or whitespace-only string counts as unset too, same as ariaLabel.
  • Rung 1 of the resolution order (the numbered list at ariaLabel): it wins whenever set, matching the ARIA name computation.
Inherited from

LLSelectBaseSettings.ariaLabelledBy

labelEl

labelEl: HTMLElement | null

Defined in: base.ts:137

The widget's visible label element - the one foreign element the library touches. Emulates native <label for> (which cannot target these divs) in both directions:

  • clicking it focuses the trigger (focus ONLY; native <select> does not open on label click, neither does this);
  • it feeds the accessible name as rung 3 of the resolution order (the numbered list at ariaLabel): with neither aria setting given, the label's id becomes the resolved ariaLabelledBy (an id is minted from classIdMap.labelId if the element has none) - a live reference, so later label text changes stay correct.
  • null = no label element; the name ladder just skips this rung.
  • destroy() removes the click listener and a minted id.
Inherited from

LLSelectBaseSettings.labelEl

Events
onOpen

onOpen: (() => void) | null

Defined in: base.ts:406

Fired right after the popup opens. An open() call that does not actually open the popup (already open, a disabled control, or a trigger scrolled out of view or clipped) does not fire it. Fires in ADDITION to the protected onOpened hook - the setting is for consumers, the hook for subclasses; both run. null (default) = nothing.

Inherited from

LLSelectBaseSettings.onOpen

onClose

onClose: (() => void) | null

Defined in: base.ts:412

Fired right after the popup closes. A no-op close() does not fire it. Additive with the protected onClosed hook, like onOpen.

Inherited from

LLSelectBaseSettings.onClose

onChange

onChange: ((chosenItem, previousChosenItem, meta) => void) | null

Defined in: single.ts:38

Fired when the chosen item actually changes (compared via compareFn). Receives the new value and the PREVIOUS one (the snapshot from before this change); undefined means "no selection" on either side. Does NOT fire on construction nor on setChosenItem with an equivalent item. null (default) = no listener.

  • meta.source says who initiated the change: 'user' for a pointer or keyboard interaction inside the widget, 'api' for any programmatic call. See LLSelectChangeMeta.
Trigger
placeholder

placeholder: string

Defined in: base.ts:89

Text shown in the trigger when nothing is selected. App copy: an explicit value always wins; when unset, the locale default uiTranslationPack.triggerPlaceholder is used ('Please select' in English).

Inherited from

LLSelectBaseSettings.placeholder

createTriggerArrowContentElFn

createTriggerArrowContentElFn: ((state) => HTMLElement | SVGElement | null) | null

Defined in: base.ts:163

The trigger arrow slot's content ELEMENT (typically a dropdown chevron or triangle). Called whenever the arrow may need to change - including on every open/close - so the returned element can vary with isOpened.

  • fn returns null - no arrow for that state.
  • setting is null (default) - the library adds nothing to the arrow slot.
Inherited from

LLSelectBaseSettings.createTriggerArrowContentElFn

clearable

clearable: boolean

Defined in: base.ts:188

Whether the trigger shows a clear (x) button that empties the selection.

  • Default false.
  • Clearing sets the empty value: undefined for a single select, [] for a multiple. It goes through the normal setters, so onChange fires with that empty value. There is no separate clear event.
  • The empty value is not configurable, and no library makes it so: "nothing chosen" already exists before the first choice, so the value types carry undefined either way.
  • If your model is a plain type like string and must never hold undefined, pick one of these:
    • Add a real "none" item (for example '' shown as "(none)") and skip clearable. The model then stays string after the first choice, exactly like a native <select> with a placeholder option.
    • Keep clearable and coerce in onChange: item ?? ''.
  • "Clear" means back to empty and the placeholder, never "back to some default option". If you want a default instead, set it yourself in onChange.
  • The button sits in its OWN trigger slot (like the arrow, so it never collides with createTriggerContentElFn), is tabindex="-1", and carries an aria-label. The theme hides it via data-empty while nothing is selected.
Inherited from

LLSelectBaseSettings.clearable

createTriggerClearButtonContentElFn

createTriggerClearButtonContentElFn: (() => HTMLElement | SVGElement | null) | null

Defined in: base.ts:195

Content ELEMENT of the clear button (its x icon), mirroring createTriggerArrowContentElFn. null (default) = the theme's CSS glyph. The library always owns the button, its click (clears + stops propagation) and aria; this only fills the icon.

Inherited from

LLSelectBaseSettings.createTriggerClearButtonContentElFn

createTriggerContentElFn

createTriggerContentElFn: ((ctx) => HTMLElement | null) | null

Defined in: single.ts:53

Render the trigger's content ELEMENT without subclassing - the setting equivalent of overriding renderTriggerContent. Receives the chosen item

  • items (same convention as createItemContentElFn):
  • HTMLElement - inserted into the trigger as-is; you own it. Use this for real markup (icon + text, etc.).
  • fn returns null - use the default for this render: the chosen item's itemToString, or the placeholder when nothing is chosen.
  • setting is null (default) - always use that default rendering. The DEFAULT renderTriggerContent checks it first; a subclass override replaces that default entirely and may ignore the setting - override wins, per DESIGN.md "Customization model".
Filtering
filterable

filterable: boolean | ((items) => boolean)

Defined in: base.ts:212

Whether the popup includes a filter input.

  • false (default): never.
  • true: always.
  • Predicate (items) => boolean: conditional - evaluated against the CURRENT full item list each time the popup OPENS (never mid-open; a setItems crossing the threshold applies on the next open). E.g. filterable: (items) => items.length > 10. A printable key pressed while CLOSED also calls the predicate, read-only, to decide whether prefix typeahead may take the key - keep it cheap and side-effect free. The ARIA mode follows the evaluated value per open cycle: active = trigger role="button", focus moves to the input; inactive = exactly like filterable: false (trigger stays role="combobox", focus stays on the trigger). See docs/llm/A11Y.md and docs/llm/DESIGN.md.
Inherited from

LLSelectBaseSettings.filterable

filterFn

filterFn: ((item, query) => boolean) | null

Defined in: base.ts:234

Predicate used by the filter input; return true to keep the item. null (default) means the built-in case-insensitive substring match against the item's resolved text (itemToStringFn / itemToString). Pass a custom function for fuzzy / domain-specific matching.

  • query is the RAW input value: not trimmed and not lower-cased. Normalize it yourself (the built-in lower-cases both sides; it does not trim).
  • Not called while the query is empty (an empty box shows every item), but a whitespace-only query (e.g. " ") does call it.
Inherited from

LLSelectBaseSettings.filterFn

createPopupListNoResultsContentElFn

createPopupListNoResultsContentElFn: ((query) => HTMLElement | null) | null

Defined in: base.ts:251

The no-results message's visible content ELEMENT, without subclassing. Mirrors createItemContentElFn: the library owns the message container (role="status", class, show/hide), this fills its content only.

  • query is the current filter query ('' when the filter is inactive or the list is simply empty), so "Nothing matches " is possible.
  • Return an HTMLElement: inserted as the content (you own it; include real text - the status region announces its TEXT content).
  • null (setting default, or returned): plain text from uiTranslationPack.popupListNoResults. Re-evaluated every time the message is shown (the query may differ). The DOM is refreshed only when the resolved TEXT changes - the status region is keyed on text so it announces once, not per keystroke - so rich content whose visible markup varies while its text stays constant is not re-rendered.
Inherited from

LLSelectBaseSettings.createPopupListNoResultsContentElFn

Grouping
itemToGroupKeyFn

itemToGroupKeyFn: ((item) => GroupKey | null) | null

Defined in: base.ts:342

Item -> its group's key (identity), enabling optgroup rendering.

  • null (setting, default): grouping off - flat list, no headers.
  • fn returns null: this item is in no group; renders ungrouped.
  • Contiguous items with an equal key (per groupKeyCompareFn) form one group. By default non-contiguous data is first gathered into display order (gatherGroups); with gatherGroups: false the data must be pre-sorted by group. See docs/llm/DESIGN.md.
Inherited from

LLSelectBaseSettings.itemToGroupKeyFn

gatherGroups

gatherGroups: boolean

Defined in: base.ts:359

Whether the library gathers non-contiguous groups before rendering (true, default). Grouping renders contiguous runs, so scattered items sharing a key would otherwise produce a duplicate header per gap.

  • true: the DISPLAY order is derived via gatherItemsByGroupKey: groups in first-appearance order, within-group order kept, ungrouped (null-key) items in place. The data itself (items / getItems()) is never reordered, and already-contiguous data is detected in one scan and used as-is.
  • false: strict mode - you guarantee the data is pre-sorted by group; a key reappearing after a gap renders a duplicate header and console.warns, so a broken sort is surfaced instead of silently fixed. No effect while grouping is off (every key null).
Inherited from

LLSelectBaseSettings.gatherGroups

groupKeyCompareFn

groupKeyCompareFn: ((a, b) => boolean) | null

Defined in: base.ts:369

Equality for two group keys; decides whether items share a group (both the gatherGroups gather and the contiguous-run rendering use it).

  • null (default) = identity, the same rule as the default compareFn (===, plus NaN equals NaN); right for string / number keys.
  • Supply only when GroupKey is an object without usable reference identity.
  • Mirrors compareFn, one level up.
Inherited from

LLSelectBaseSettings.groupKeyCompareFn

groupKeyToStringFn

groupKeyToStringFn: ((groupKey) => string) | null

Defined in: base.ts:376

Group key -> the header's display text. The i18n customization point: keep keys stable, translate here.

  • null (default) = String(groupKey).
Inherited from

LLSelectBaseSettings.groupKeyToStringFn

groupDisabledFn

groupDisabledFn: ((groupKey) => boolean) | null

Defined in: base.ts:384

Predicate: is this whole group disabled?

  • null (default) = no group disabled.
  • true = every item in the group is treated as disabled (layers on top of itemDisabledFn).
Inherited from

LLSelectBaseSettings.groupDisabledFn

createGroupLabelContentElFn

createGroupLabelContentElFn: ((groupKey, itemsInGroup) => HTMLElement | null) | null

Defined in: base.ts:397

Group header -> its visible content ELEMENT (icon / count badge / rich markup), without subclassing. Mirrors createItemContentElFn.

  • Return an HTMLElement and the library inserts it as the header's visible content; the group's accessible name stays groupKeyToString (on the container aria-label) and the label element stays aria-hidden.
  • null (default, or returned for a group) = plain text from groupKeyToString.
  • itemsInGroup is the group's items, so you can render "Fruits (4)" or a summary without recomputing the grouping.
Inherited from

LLSelectBaseSettings.createGroupLabelContentElFn

outsideClickBehavior

outsideClickBehavior: LLSelectOutsideClickBehavior

Defined in: base.ts:154

See LLSelectOutsideClickBehavior.

Inherited from

LLSelectBaseSettings.outsideClickBehavior

popupWidthPolicy

popupWidthPolicy: LLSelectWidthPolicy

Defined in: base.ts:267

How the popup decides its width. Does NOT affect the trigger - trigger width is always whatever your CSS says.

  • 'fit-content' (default): popup width grows to its own content (items, filter input, ...) and never shrinks below the trigger's width - the native <select> dropdown behavior, minus its viewport overflow: auto-shifts and width-clamps when the natural width would not fit. Direction-aware: in an RTL context (getComputedStyle(trigger).direction === 'rtl', read once per open) it right-aligns to the trigger and grows LEFTWARD, the mirror of LTR.
  • 'match-trigger': popup width equals trigger width; long item text wraps inside the popup.
Inherited from

LLSelectBaseSettings.popupWidthPolicy

Disabling
itemDisabledFn

itemDisabledFn: ((item) => boolean) | null

Defined in: base.ts:277

Predicate deciding whether an individual item is disabled. null (default) = nothing disabled. A disabled item is not selectable (click / Enter) and is skipped by keyboard nav; it keeps role="option" plus aria-disabled. Re-evaluated on every render (never cached). For a generic T this is the only way to mark items - the library cannot read a disabled field off an unknown type. See docs/llm/DESIGN.md.

Inherited from

LLSelectBaseSettings.itemDisabledFn

focusableWhenDisabled

focusableWhenDisabled: boolean

Defined in: base.ts:285

When the control is disabled via setDisabled(true), whether the trigger stays in the tab order (tabindex="0"). false (default) takes it out (-1). Set true so keyboard / AT users can focus the disabled control to read a "why disabled" tooltip.

Inherited from

LLSelectBaseSettings.focusableWhenDisabled

i18n
uiTranslationPack

uiTranslationPack: LLSelectUiTranslationPack

Defined in: base.ts:222

Chrome strings (AT labels + generated text): the i18n customization point. Resolved against English: pass a language pack whole (uiTranslationPack: zhTW from @llselect/core/i18n) or override single keys (uiTranslationPack: { ...zhTW, filterInputPlaceholder: '...' }). Key-by-key contract (incl. what null means where allowed): LLSelectUiTranslationPack.

Inherited from

LLSelectBaseSettings.uiTranslationPack

CSS
cssClassPrefix

cssClassPrefix: string

Defined in: base.ts:82

Prefix used for every CSS class and DOM id the library generates (default 'llselect'). NOTE: the shipped themes target the default prefix only - a custom prefix means bringing your own CSS. Reference the resolved names via instance.classIdMap instead of hardcoding strings.

Inherited from

LLSelectBaseSettings.cssClassPrefix


LLSelectSingleSettingsInput

LLSelectSingleSettingsInput<T, GroupKey> = LLSelectSettingsInputOf<LLSelectSingleSettings<T, GroupKey>>

Defined in: single.ts:62

Constructor-time settings input for LLSelectSingle. Every field is optional; missing fields use defaults.

Type Parameters
Type Parameter Default type
T -
GroupKey string

Multiple

LLSelectTriggerDisplay

LLSelectTriggerDisplay = "count" | "tags"

Defined in: multiple.ts:17

Trigger display mode of LLSelectMultiple.


LLSelectChosenState

LLSelectChosenState = "none" | "some" | "all"

Defined in: multiple.ts:25

Tri-state of the choose-all row (also the data-chosen-state attribute value): how much of the VISIBLE enabled subset is currently chosen.


LLSelectMultipleTriggerContext

Defined in: multiple.ts:32

Context passed to LLSelectMultipleSettings.createTriggerContentElFn.

Type Parameters
Type Parameter
T
Properties
chosenItems

chosenItems: readonly T[]

Defined in: multiple.ts:33

items

items: readonly T[]

Defined in: multiple.ts:34


LLSelectMultipleSettings

Defined in: multiple.ts:44

Resolved (defaults applied) settings for LLSelectMultiple: the base settings plus the multi-mode fields - the runtime type of this.settings, one bag built complete in the constructor.

Extends
Type Parameters
Type Parameter Default type
T -
GroupKey string
Items
compareFn

compareFn: (a, b) => boolean

Defined in: base.ts:149

Equality predicate for item values - return true when a and b are the same item.

  • Required for non-primitive T. The default compares by identity: ===, except that NaN equals NaN (SameValueZero, the same rule Set uses), so every code path agrees on what "the same item" means.
  • Used for selection, dedup, and matching the chosen item back to the list.
  • Symmetric: do not depend on which argument is the candidate vs the existing item.
Parameters
Parameter Type
a T
b T
Returns

boolean

Inherited from

LLSelectBaseSettings.compareFn

itemToStringFn

itemToStringFn: ((item) => string) | null

Defined in: base.ts:295

Item -> display string, without subclassing.

  • null (default) = String(item).
  • Read by the itemToString method's default; used for list text, the single trigger text, the option's accessible name, and the default filter. Inserted as textContent (plain text, NOT parsed as HTML).
  • For rich content (icons etc.), pass createItemContentElFn.
Inherited from

LLSelectBaseSettings.itemToStringFn

createItemContentElFn

createItemContentElFn: ((item) => HTMLElement | null) | null

Defined in: base.ts:331

Item -> the visible content ELEMENT of its list row, without subclassing.

  • Return an HTMLElement and the library inserts it as-is (you own the node); it becomes the row's visible content.
  • null = plain textContent from itemToString. This is the default, both when the setting is unset and when your function returns null for a particular item.
  • Fills the VISIBLE content only. You never touch aria-*: when this returns an element the library sets the option's aria-label from itemToString, so the accessible name + match text stay owned by itemToString no matter what you render (icon-only, reordered, ...). To make the spoken/matched text differ from the visible content, set the two independently: itemToStringFn for the name/matching, createItemContentElFn for the look.
  • For full control of the option element (tag / wiring), subclass createItemEl instead.
  • Runs per rendered row per render, and re-runs whenever a row is rebuilt: open, filter, setItems, AND chosen-state changes (both modes replace the affected rows in place while the popup is open). Content that reads selection state (e.g. a checkmark on the chosen row via createCheckmarkSvgEl) therefore stays fresh; keep the function cheap.
Example
// List shows an icon + the item text; screen readers announce just that text.
  itemToStringFn: (lang) => lang.name,
  createItemContentElFn: (lang) => {
    const row = document.createElement('span')
    const icon = document.createElement('i')
    icon.className = `mdi mdi-${lang.icon}`
    icon.setAttribute('aria-hidden', 'true') // decorative
    row.append(icon, lang.name)
    return row
  }
Inherited from

LLSelectBaseSettings.createItemContentElFn

hideChosenRows

hideChosenRows: boolean

Defined in: multiple.ts:126

Hide the rows of chosen items from the popup list.

  • Default false: chosen rows stay listed and show their state.
  • While true, choosing an item removes its row at once and unchoosing puts it back. Tags, the trigger and getChosenItems are unaffected.
  • When every item is chosen, the popup shows the no-results element.
  • With chooseAllRow, the visible subset is always fully unchosen, so the row acts as "choose everything still listed", its tri-state never reaches all-chosen, and it disappears with the last actionable row.
  • Internals: a getVisibleItems subtraction. The default compareFn uses a Set lookup; a custom compareFn costs O(visible x chosen). Either cost is paid once per change of the list or the chosen set - the result is cached between changes. toggleItem swaps its O(1) row replace for a full rebuild.
Accessible name
ariaLabel

ariaLabel: string | null

Defined in: base.ts:108

Accessible name of the field, like the <label> text of a native <select> (e.g. 'Country').

  • Applied to the trigger, the popup listbox, and (while the filter is active) the filter input; per-mode wiring: docs/llm/A11Y.md "Accessible name".
  • The name resolves by the FIRST set rung, mirroring the W3C accessible-name computation order:
    1. ariaLabelledBy.
    2. ariaLabel (this setting).
    3. labelEl - its element's id becomes the resolved ariaLabelledBy.
    4. None set: the field is unnamed. A combobox requires a name (WAI-ARIA 1.2), so one console.warn per page reports the first offender.
  • null (default): this rung is skipped. An empty or whitespace-only string counts as unset too - the accname computation skips a blank aria-label, and so does the ladder.
Inherited from

LLSelectBaseSettings.ariaLabel

ariaLabelledBy

ariaLabelledBy: string | null

Defined in: base.ts:121

Space-separated DOM id(s) of the visible label element(s) naming the field; forwarded as aria-labelledby to the same elements as ariaLabel.

  • Prefer this over ariaLabel when a visible label element exists: the spoken name then always matches the visible text.
  • null (default): not forwarded; see ariaLabel for the naming requirement. An empty or whitespace-only string counts as unset too, same as ariaLabel.
  • Rung 1 of the resolution order (the numbered list at ariaLabel): it wins whenever set, matching the ARIA name computation.
Inherited from

LLSelectBaseSettings.ariaLabelledBy

labelEl

labelEl: HTMLElement | null

Defined in: base.ts:137

The widget's visible label element - the one foreign element the library touches. Emulates native <label for> (which cannot target these divs) in both directions:

  • clicking it focuses the trigger (focus ONLY; native <select> does not open on label click, neither does this);
  • it feeds the accessible name as rung 3 of the resolution order (the numbered list at ariaLabel): with neither aria setting given, the label's id becomes the resolved ariaLabelledBy (an id is minted from classIdMap.labelId if the element has none) - a live reference, so later label text changes stay correct.
  • null = no label element; the name ladder just skips this rung.
  • destroy() removes the click listener and a minted id.
Inherited from

LLSelectBaseSettings.labelEl

Events
onOpen

onOpen: (() => void) | null

Defined in: base.ts:406

Fired right after the popup opens. An open() call that does not actually open the popup (already open, a disabled control, or a trigger scrolled out of view or clipped) does not fire it. Fires in ADDITION to the protected onOpened hook - the setting is for consumers, the hook for subclasses; both run. null (default) = nothing.

Inherited from

LLSelectBaseSettings.onOpen

onClose

onClose: (() => void) | null

Defined in: base.ts:412

Fired right after the popup closes. A no-op close() does not fire it. Additive with the protected onClosed hook, like onOpen.

Inherited from

LLSelectBaseSettings.onClose

onChange

onChange: ((chosenItems, previousChosenItems, meta) => void) | null

Defined in: multiple.ts:58

Fired when the chosen-items set actually changes. Receives the new set and the PREVIOUS one (the snapshot from before this change) - diff them with compareFn to compute added / removed. Does NOT fire on construction nor on a setter call that yields an equivalent set (element-wise compared via compareFn, order-sensitive). null (default) = no listener.

  • meta.source says who initiated the change: 'user' for a pointer or keyboard interaction inside the widget (an option toggle, a tag's remove button, the clear button, the choose-all row), 'api' for any programmatic call. See LLSelectChangeMeta.
Trigger
placeholder

placeholder: string

Defined in: base.ts:89

Text shown in the trigger when nothing is selected. App copy: an explicit value always wins; when unset, the locale default uiTranslationPack.triggerPlaceholder is used ('Please select' in English).

Inherited from

LLSelectBaseSettings.placeholder

createTriggerArrowContentElFn

createTriggerArrowContentElFn: ((state) => HTMLElement | SVGElement | null) | null

Defined in: base.ts:163

The trigger arrow slot's content ELEMENT (typically a dropdown chevron or triangle). Called whenever the arrow may need to change - including on every open/close - so the returned element can vary with isOpened.

  • fn returns null - no arrow for that state.
  • setting is null (default) - the library adds nothing to the arrow slot.
Inherited from

LLSelectBaseSettings.createTriggerArrowContentElFn

clearable

clearable: boolean

Defined in: base.ts:188

Whether the trigger shows a clear (x) button that empties the selection.

  • Default false.
  • Clearing sets the empty value: undefined for a single select, [] for a multiple. It goes through the normal setters, so onChange fires with that empty value. There is no separate clear event.
  • The empty value is not configurable, and no library makes it so: "nothing chosen" already exists before the first choice, so the value types carry undefined either way.
  • If your model is a plain type like string and must never hold undefined, pick one of these:
    • Add a real "none" item (for example '' shown as "(none)") and skip clearable. The model then stays string after the first choice, exactly like a native <select> with a placeholder option.
    • Keep clearable and coerce in onChange: item ?? ''.
  • "Clear" means back to empty and the placeholder, never "back to some default option". If you want a default instead, set it yourself in onChange.
  • The button sits in its OWN trigger slot (like the arrow, so it never collides with createTriggerContentElFn), is tabindex="-1", and carries an aria-label. The theme hides it via data-empty while nothing is selected.
Inherited from

LLSelectBaseSettings.clearable

createTriggerClearButtonContentElFn

createTriggerClearButtonContentElFn: (() => HTMLElement | SVGElement | null) | null

Defined in: base.ts:195

Content ELEMENT of the clear button (its x icon), mirroring createTriggerArrowContentElFn. null (default) = the theme's CSS glyph. The library always owns the button, its click (clears + stops propagation) and aria; this only fills the icon.

Inherited from

LLSelectBaseSettings.createTriggerClearButtonContentElFn

createTriggerContentElFn

createTriggerContentElFn: ((ctx) => HTMLElement | null) | null

Defined in: multiple.ts:72

Render the trigger's content ELEMENT without subclassing - the setting equivalent of overriding renderTriggerContent. Receives the chosen items

  • items (same convention as createItemContentElFn):
  • HTMLElement - inserted into the trigger as-is; you own it. Use this for real markup such as tag chips.
  • fn returns null - use the default for this render (count summary / tags).
  • setting is null (default) - always use that default rendering. The DEFAULT renderTriggerContent checks it first; a subclass override replaces that default entirely and may ignore the setting - override wins, per DESIGN.md "Customization model".
triggerDisplay

triggerDisplay: LLSelectTriggerDisplay

Defined in: multiple.ts:80

Trigger display mode.

  • 'count' (default): a summary like "3 / 10 selected".
  • 'tags': one removable chip per chosen item; its x button removes it. createTriggerContentElFn overrides both (full control wins).
createTagContentElFn

createTagContentElFn: ((item) => HTMLElement | null) | null

Defined in: multiple.ts:97

Item -> the visible content ELEMENT of its tag chip in 'tags' mode, without subclassing. Mirrors createItemContentElFn (the chip is to the trigger what the option content is to the row):

  • Return an HTMLElement and the library inserts it as the chip's content; the library still owns the chip container + the remove (x) button + aria.
  • null (setting default, or returned for an item) = plain text from itemToString.
  • The remove button's accessible name comes from itemToTagRemoveButtonAriaLabel (default Remove <itemToString>) - that is what AT is guaranteed to announce. The chip is a generic <span> (ARIA prohibits naming it), so for icon-only content include your own (visually hidden) text if the chip should be announced as more than its remove button. See docs/llm/A11Y.md "Tags".
createTagRemoveButtonContentElFn

createTagRemoveButtonContentElFn: ((item) => HTMLElement | SVGElement | null) | null

Defined in: multiple.ts:109

Icon ELEMENT of each tag's remove (x) button in 'tags' mode, mirroring createTriggerClearButtonContentElFn (the clear button's icon hook). The library always owns the button, its click (removes the item + stopPropagation), tabindex="-1", and the aria-label accessible name (from itemToTagRemoveButtonAriaLabel); this only fills the decorative icon.

  • Return an HTMLElement / SVGElement: appended inside the button as its icon.
  • null (setting default, or returned for an item): no icon - the theme draws the x via its CSS glyph (.llselect-tag-remove-button:empty::before).
Filtering
filterable

filterable: boolean | ((items) => boolean)

Defined in: base.ts:212

Whether the popup includes a filter input.

  • false (default): never.
  • true: always.
  • Predicate (items) => boolean: conditional - evaluated against the CURRENT full item list each time the popup OPENS (never mid-open; a setItems crossing the threshold applies on the next open). E.g. filterable: (items) => items.length > 10. A printable key pressed while CLOSED also calls the predicate, read-only, to decide whether prefix typeahead may take the key - keep it cheap and side-effect free. The ARIA mode follows the evaluated value per open cycle: active = trigger role="button", focus moves to the input; inactive = exactly like filterable: false (trigger stays role="combobox", focus stays on the trigger). See docs/llm/A11Y.md and docs/llm/DESIGN.md.
Inherited from

LLSelectBaseSettings.filterable

filterFn

filterFn: ((item, query) => boolean) | null

Defined in: base.ts:234

Predicate used by the filter input; return true to keep the item. null (default) means the built-in case-insensitive substring match against the item's resolved text (itemToStringFn / itemToString). Pass a custom function for fuzzy / domain-specific matching.

  • query is the RAW input value: not trimmed and not lower-cased. Normalize it yourself (the built-in lower-cases both sides; it does not trim).
  • Not called while the query is empty (an empty box shows every item), but a whitespace-only query (e.g. " ") does call it.
Inherited from

LLSelectBaseSettings.filterFn

createPopupListNoResultsContentElFn

createPopupListNoResultsContentElFn: ((query) => HTMLElement | null) | null

Defined in: base.ts:251

The no-results message's visible content ELEMENT, without subclassing. Mirrors createItemContentElFn: the library owns the message container (role="status", class, show/hide), this fills its content only.

  • query is the current filter query ('' when the filter is inactive or the list is simply empty), so "Nothing matches " is possible.
  • Return an HTMLElement: inserted as the content (you own it; include real text - the status region announces its TEXT content).
  • null (setting default, or returned): plain text from uiTranslationPack.popupListNoResults. Re-evaluated every time the message is shown (the query may differ). The DOM is refreshed only when the resolved TEXT changes - the status region is keyed on text so it announces once, not per keystroke - so rich content whose visible markup varies while its text stays constant is not re-rendered.
Inherited from

LLSelectBaseSettings.createPopupListNoResultsContentElFn

Grouping
itemToGroupKeyFn

itemToGroupKeyFn: ((item) => GroupKey | null) | null

Defined in: base.ts:342

Item -> its group's key (identity), enabling optgroup rendering.

  • null (setting, default): grouping off - flat list, no headers.
  • fn returns null: this item is in no group; renders ungrouped.
  • Contiguous items with an equal key (per groupKeyCompareFn) form one group. By default non-contiguous data is first gathered into display order (gatherGroups); with gatherGroups: false the data must be pre-sorted by group. See docs/llm/DESIGN.md.
Inherited from

LLSelectBaseSettings.itemToGroupKeyFn

gatherGroups

gatherGroups: boolean

Defined in: base.ts:359

Whether the library gathers non-contiguous groups before rendering (true, default). Grouping renders contiguous runs, so scattered items sharing a key would otherwise produce a duplicate header per gap.

  • true: the DISPLAY order is derived via gatherItemsByGroupKey: groups in first-appearance order, within-group order kept, ungrouped (null-key) items in place. The data itself (items / getItems()) is never reordered, and already-contiguous data is detected in one scan and used as-is.
  • false: strict mode - you guarantee the data is pre-sorted by group; a key reappearing after a gap renders a duplicate header and console.warns, so a broken sort is surfaced instead of silently fixed. No effect while grouping is off (every key null).
Inherited from

LLSelectBaseSettings.gatherGroups

groupKeyCompareFn

groupKeyCompareFn: ((a, b) => boolean) | null

Defined in: base.ts:369

Equality for two group keys; decides whether items share a group (both the gatherGroups gather and the contiguous-run rendering use it).

  • null (default) = identity, the same rule as the default compareFn (===, plus NaN equals NaN); right for string / number keys.
  • Supply only when GroupKey is an object without usable reference identity.
  • Mirrors compareFn, one level up.
Inherited from

LLSelectBaseSettings.groupKeyCompareFn

groupKeyToStringFn

groupKeyToStringFn: ((groupKey) => string) | null

Defined in: base.ts:376

Group key -> the header's display text. The i18n customization point: keep keys stable, translate here.

  • null (default) = String(groupKey).
Inherited from

LLSelectBaseSettings.groupKeyToStringFn

groupDisabledFn

groupDisabledFn: ((groupKey) => boolean) | null

Defined in: base.ts:384

Predicate: is this whole group disabled?

  • null (default) = no group disabled.
  • true = every item in the group is treated as disabled (layers on top of itemDisabledFn).
Inherited from

LLSelectBaseSettings.groupDisabledFn

createGroupLabelContentElFn

createGroupLabelContentElFn: ((groupKey, itemsInGroup) => HTMLElement | null) | null

Defined in: base.ts:397

Group header -> its visible content ELEMENT (icon / count badge / rich markup), without subclassing. Mirrors createItemContentElFn.

  • Return an HTMLElement and the library inserts it as the header's visible content; the group's accessible name stays groupKeyToString (on the container aria-label) and the label element stays aria-hidden.
  • null (default, or returned for a group) = plain text from groupKeyToString.
  • itemsInGroup is the group's items, so you can render "Fruits (4)" or a summary without recomputing the grouping.
Inherited from

LLSelectBaseSettings.createGroupLabelContentElFn

outsideClickBehavior

outsideClickBehavior: LLSelectOutsideClickBehavior

Defined in: base.ts:154

See LLSelectOutsideClickBehavior.

Inherited from

LLSelectBaseSettings.outsideClickBehavior

popupWidthPolicy

popupWidthPolicy: LLSelectWidthPolicy

Defined in: base.ts:267

How the popup decides its width. Does NOT affect the trigger - trigger width is always whatever your CSS says.

  • 'fit-content' (default): popup width grows to its own content (items, filter input, ...) and never shrinks below the trigger's width - the native <select> dropdown behavior, minus its viewport overflow: auto-shifts and width-clamps when the natural width would not fit. Direction-aware: in an RTL context (getComputedStyle(trigger).direction === 'rtl', read once per open) it right-aligns to the trigger and grows LEFTWARD, the mirror of LTR.
  • 'match-trigger': popup width equals trigger width; long item text wraps inside the popup.
Inherited from

LLSelectBaseSettings.popupWidthPolicy

Disabling
itemDisabledFn

itemDisabledFn: ((item) => boolean) | null

Defined in: base.ts:277

Predicate deciding whether an individual item is disabled. null (default) = nothing disabled. A disabled item is not selectable (click / Enter) and is skipped by keyboard nav; it keeps role="option" plus aria-disabled. Re-evaluated on every render (never cached). For a generic T this is the only way to mark items - the library cannot read a disabled field off an unknown type. See docs/llm/DESIGN.md.

Inherited from

LLSelectBaseSettings.itemDisabledFn

focusableWhenDisabled

focusableWhenDisabled: boolean

Defined in: base.ts:285

When the control is disabled via setDisabled(true), whether the trigger stays in the tab order (tabindex="0"). false (default) takes it out (-1). Set true so keyboard / AT users can focus the disabled control to read a "why disabled" tooltip.

Inherited from

LLSelectBaseSettings.focusableWhenDisabled

i18n
uiTranslationPack

uiTranslationPack: LLSelectUiTranslationPack

Defined in: base.ts:222

Chrome strings (AT labels + generated text): the i18n customization point. Resolved against English: pass a language pack whole (uiTranslationPack: zhTW from @llselect/core/i18n) or override single keys (uiTranslationPack: { ...zhTW, filterInputPlaceholder: '...' }). Key-by-key contract (incl. what null means where allowed): LLSelectUiTranslationPack.

Inherited from

LLSelectBaseSettings.uiTranslationPack

CSS
cssClassPrefix

cssClassPrefix: string

Defined in: base.ts:82

Prefix used for every CSS class and DOM id the library generates (default 'llselect'). NOTE: the shipped themes target the default prefix only - a custom prefix means bringing your own CSS. Reference the resolved names via instance.classIdMap instead of hardcoding strings.

Inherited from

LLSelectBaseSettings.cssClassPrefix

Choose-all
chooseAllRow

chooseAllRow: boolean

Defined in: multiple.ts:141

Whether the popup shows a choose-all row (the industry's "select all") as the first option of the listbox.

  • Default false.
  • Activating the row (Enter / click) runs toggleAllVisible: it toggles the visible enabled subset (the matching subset while a filter query is active). The public chooseAll / unchooseAll / toggleAll keep their whole-list semantics.
  • The row is tri-state (none / some / all chosen), carried by the counting text's numbers and the data-chosen-state CSS hook.
  • Its accessible name comes from uiTranslationPack.chooseAllRowText.
  • See docs/llm/A11Y.md "Choose-all".
createChooseAllRowContentElFn

createChooseAllRowContentElFn: ((chosenState, chosenCount, totalCount) => HTMLElement | null) | null

Defined in: multiple.ts:157

The choose-all row's visible content ELEMENT, without subclassing - e.g. a tri-state SVG checkbox (createOutlinedCheckboxSvgEl) + the counting text. Mirrors createItemContentElFn. Only used with chooseAllRow: true.

  • Receives the tri-state and the counts of the visible enabled subset.
  • Return an HTMLElement: inserted as the row's content; the accessible name stays pinned to uiTranslationPack.chooseAllRowText via aria-label, so icon-only content is still announced with the counts.
  • null (setting default, or returned): the default content - just the plain counting text; its numbers carry the tri-state. The library ships no default indicator (consistent with items and the arrow); passing this setting is how one (e.g. createOutlinedCheckboxSvgEl) gets added. See DESIGN.md "Choose-all default: plain counting text".

LLSelectMultipleSettingsInput

LLSelectMultipleSettingsInput<T, GroupKey> = LLSelectSettingsInputOf<LLSelectMultipleSettings<T, GroupKey>>

Defined in: multiple.ts:167

Constructor-time settings input for LLSelectMultiple. Every field is optional; missing fields use defaults.

Type Parameters
Type Parameter Default type
T -
GroupKey string

Pack contract

LLSelectUiTranslationPack

Re-exports LLSelectUiTranslationPack

Icons

Shared

LLSelectIconOptions

Defined in: icons.ts:16

Options accepted by the built-in icon helpers.

Extended by
Properties
size?

optional size?: number

Defined in: icons.ts:18

Width and height of the SVG in pixels. Default 16.

Arrows

createTriangleDownSvgEl()

createTriangleDownSvgEl(opts?): SVGElement

Defined in: icons.ts:42

Solid filled triangle pointing down. Sized to roughly match the chevron's visual weight (MDI's arrow_drop_down path occupies a small portion of its 24x24 viewBox and looks too small next to other icons).

Parameters
Parameter Type
opts LLSelectIconOptions
Returns

SVGElement


createChevronDownSvgEl()

createChevronDownSvgEl(opts?): SVGElement

Defined in: icons.ts:51

Material Design expand_more chevron pointing down (filled outline).

Parameters
Parameter Type
opts LLSelectIconOptions
Returns

SVGElement

Checkmarks & checkboxes

createCheckmarkSvgEl()

createCheckmarkSvgEl(opts?): SVGElement

Defined in: icons.ts:65

Standalone checkmark (no box). Useful as a "selected" indicator in single mode, or as a lightweight chosen marker in multi mode.

Parameters
Parameter Type
opts LLSelectIconOptions
Returns

SVGElement


LLSelectCheckboxState

LLSelectCheckboxState = "unchecked" | "checked" | "indeterminate"

Defined in: icons.ts:80

Visual state of createOutlinedCheckboxSvgEl / createFilledCheckboxSvgEl. indeterminate is the "mixed" / partial state used by a choose-all control (aria-checked="mixed").


LLSelectCheckboxIconOptions

Defined in: icons.ts:100

Options accepted by createOutlinedCheckboxSvgEl and createFilledCheckboxSvgEl.

Extends
Properties
size?

optional size?: number

Defined in: icons.ts:18

Width and height of the SVG in pixels. Default 16.

Inherited from

LLSelectIconOptions.size

state?

optional state?: "some" | "none" | LLSelectCheckboxState | "all"

Defined in: icons.ts:109

Which checkbox state to draw. Accepts the icon vocabulary ('unchecked' | 'checked' | 'indeterminate') or, as a convenience, the choose-all row's chosen-state vocabulary ('none' -> unchecked, 'some' -> indeterminate, 'all' -> checked), so createChooseAllRowContentElFn can pass its state straight through. Default 'unchecked'.


createOutlinedCheckboxSvgEl()

createOutlinedCheckboxSvgEl(opts?): SVGElement

Defined in: icons.ts:126

Outlined checkbox icon: box border with the tick (checked) / dash (indeterminate) drawn inside, all in currentColor; the filled twin is createFilledCheckboxSvgEl. Intended for multi-select item rows and the choose-all control. Decorative only (aria-hidden); the real state is carried by aria-selected on the item or aria-checked on the control.

Parameters
Parameter Type
opts LLSelectCheckboxIconOptions
Returns

SVGElement


createFilledCheckboxSvgEl()

createFilledCheckboxSvgEl(opts?): SVGElement

Defined in: icons.ts:151

Material-look checkbox icon: a solid rounded box with the tick (checked) / dash (indeterminate) cut out; unchecked draws the same outline box as createOutlinedCheckboxSvgEl. Same options, including the chosen-state vocabulary. Decorative only (aria-hidden); the real state is carried by aria-selected on the item or aria-checked on the control.

Parameters
Parameter Type
opts LLSelectCheckboxIconOptions
Returns

SVGElement

Positioning

LLSelectPlacement

LLSelectPlacement = "below" | "above"

Defined in: positioning.ts:18

Whether the floating element sits below or above the anchor.


LLSelectWidthPolicy

LLSelectWidthPolicy = "fit-content" | "match-trigger"

Defined in: positioning.ts:25

How the floating element decides its width. See LLSelectBaseSettings (popupWidthPolicy field) for the user-facing contract.

CSS & DOM

LLSelectClassIdMap

Defined in: base.ts:441

Resolved CSS class names and DOM ids for one instance. Exposed on instance.classIdMap so callers can reuse them in their own CSS or query selectors instead of hard-coding the strings.

Properties

rootClass

rootClass: string

Defined in: base.ts:443

Class on rootEl (the caller-passed mount element).

triggerClass

triggerClass: string

Defined in: base.ts:449

Class on triggerEl (the interactive trigger). Its role is combobox while the filter is inactive and button while a filterable popup is open; see docs/llm/A11Y.md.

triggerContentClass

triggerContentClass: string

Defined in: base.ts:451

Class on the inner span where content (text/tags) is rendered.

triggerArrowClass

triggerArrowClass: string

Defined in: base.ts:453

Class on the inner span where the optional dropdown arrow lives.

triggerClearButtonClass

triggerClearButtonClass: string

Defined in: base.ts:455

Class on the clear (x) button slot in the trigger (clearable).

popupClass

popupClass: string

Defined in: base.ts:457

Class on popupEl (the outer popup wrapper, no ARIA role).

popupListClass

popupListClass: string

Defined in: base.ts:459

Class on popupListEl (the inner element with role="listbox").

popupListNoResultsClass

popupListNoResultsClass: string

Defined in: base.ts:464

Class on the no-results message element (role="status"), shown below the (empty) listbox when the visible item list has zero entries.

chooseAllRowClass

chooseAllRowClass: string

Defined in: base.ts:470

Class on the choose-all leading row (LLSelectMultiple, chooseAllRow setting). Also carries itemClass plus data-chosen-state="none|some|all" for the tri-state visual.

itemClass

itemClass: string

Defined in: base.ts:472

Class on every item element (role="option") inside the popup list.

itemFocusedClass

itemFocusedClass: string

Defined in: base.ts:477

Extra class added to the currently keyboard-focused item element. Use this to style the focused item.

itemDisabledClass

itemDisabledClass: string

Defined in: base.ts:482

Class added to a disabled item element (which also carries aria-disabled="true"). A stable hook for styling / tooltip targeting.

groupClass

groupClass: string

Defined in: base.ts:487

Class on a group container (role="group"). A disabled group's container also carries aria-disabled="true" + data-disabled="true".

groupLabelClass

groupLabelClass: string

Defined in: base.ts:492

Class on the visible group label element (aria-hidden), inside the group container above its items. A hook for styling / sticky headers.

tagsClass

tagsClass: string

Defined in: base.ts:494

Class on the tag-list container in triggerDisplay: 'tags' mode (multi).

tagClass

tagClass: string

Defined in: base.ts:496

Class on one tag chip (triggerDisplay: 'tags').

tagRemoveButtonClass

tagRemoveButtonClass: string

Defined in: base.ts:498

Class on a tag's remove (x) button; aria-label names the item, tabindex="-1".

tagDisabledClass

tagDisabledClass: string

Defined in: base.ts:504

Class on a tag chip whose item is effectively disabled (which also carries aria-disabled="true", and whose x button no longer removes it). Mirrors itemDisabledClass; a stable hook for greying the inert chip.

openClass

openClass: string

Defined in: base.ts:510

Class added to rootEl while the popup is open. Use it as a CSS hook for open-state styling (also available as [data-state='open'] on the trigger).

triggerId

triggerId: string

Defined in: base.ts:512

DOM id of triggerEl. Unique across instances.

labelId

labelId: string

Defined in: base.ts:518

DOM id minted onto the labelEl setting's element when it has none (the resolved ariaLabelledBy then references it). Unique across instances. Unused when labelEl is null or already carries an id.

triggerContentId

triggerContentId: string

Defined in: base.ts:520

DOM id of the trigger content span. Unique across instances.

triggerValueId

triggerValueId: string

Defined in: base.ts:529

DOM id of the hidden plain-text value span (root-level sibling of the trigger). Unique across instances. Referenced by the filterable-mode trigger's aria-labelledby chain so the closed button's accessible name includes the current value as PLAIN TEXT - rich trigger content (tag chips with labelled remove buttons) must not leak control names into the field name (see the ariaLabel / ariaLabelledBy settings).

popupListId

popupListId: string

Defined in: base.ts:534

DOM id of popupListEl (the inner listbox). Unique across instances. Referenced by the trigger's aria-controls attribute.

filterInputClass

filterInputClass: string

Defined in: base.ts:536

Class on the filter input element inside the popup.

filterInputId

filterInputId: string

Defined in: base.ts:538

DOM id of the filter input. Unique across instances.

Events

LLSelectChangeSource

LLSelectChangeSource = "user" | "api"

Defined in: base.ts:42

Who initiated a chosen-state change, delivered to onChange as meta.source.

  • 'user': a pointer or keyboard interaction inside the widget - an option toggle, a tag's remove button, the clear button, the choose-all row.
  • 'api': any programmatic call - setChosenItem / setChosenItems, toggleItem, the choose* bulk ops, setItems reconciliation.

LLSelectChangeMeta

Defined in: base.ts:50

Extra facts about one onChange firing, as the callback's third argument. An object on purpose: future fields can be added without breaking the callback signature.

Properties

source

source: LLSelectChangeSource

Defined in: base.ts:51

Filtering

createHighlightedTextEl()

createHighlightedTextEl(text, query, createMatchElFn?): HTMLElement

Defined in: query-highlight.ts:25

Build a detached <span> of text with every query match wrapped for highlighting.

  • Each case-insensitive occurrence of query becomes a <mark> element; everything else stays plain text nodes.
  • Matching mirrors the built-in filter exactly: toLowerCase on both sides, no trimming, scanned left to right without overlap.
  • If query is '', the span holds the plain text and no <mark>.
  • createMatchElFn replaces the default <mark> builder. It receives the matched text and must return a fully built element: the helper inserts it as-is and does not put the text inside for you.
  • If a custom filterFn drives your matching, the helper cannot know its match ranges: it always marks plain substring occurrences.
  • Intended for createItemContentElFn / createItemContentEl: they re-run on every filter keystroke, so the marks stay in sync with the query. The option's accessible name is unaffected (it comes from itemToString).
  • Rare edge: if lower-casing changes the string's length (a Unicode expansion, e.g. dotted capital I, U+0130), the span degrades to plain unmarked text instead of marking wrong ranges.

Parameters

Parameter Type
text string
query string
createMatchElFn? (matchedText) => HTMLElement

Returns

HTMLElement

Grouping

gatherItemsByGroupKey()

gatherItemsByGroupKey<T, GroupKey>(items, itemToGroupKeyFn, groupKeyCompareFn?): readonly T[]

Defined in: grouping.ts:27

Stable-bucket items so that every group is contiguous, without touching the caller's array.

  • Groups appear in order of each key's FIRST appearance.
  • Within a group, items keep their relative order.
  • Items whose key is null (ungrouped) form their own single-item segment at their walk position; they never merge.
  • Already-contiguous input is detected in one scan and returned AS-IS (the input array itself, no copy); otherwise a new array is returned.

This is exactly what LLSelectBase runs internally while the gatherGroups setting is on (the default). Exported for callers who switch gatherGroups off and gather once themselves (e.g. ahead of many setItems calls on the same data).

Type Parameters

Type Parameter Default type
T -
GroupKey string

Parameters

Parameter Type Description
items readonly T[] the item list to gather
itemToGroupKeyFn (item) => GroupKey | null item to group key; null = the item is in no group
groupKeyCompareFn? ((a, b) => boolean) | null key equality; null / omitted = identity (===, plus NaN equals NaN - the rule the internal Map uses; same contract as the groupKeyCompareFn setting)

Returns

readonly T[]

Metadata

version

const version: "0.0.8" = '0.0.8'

Defined in: index.ts:69

Library version. Mirrors package.json version (smoke-test guarded).