import { type LLSelectWidthPolicy } from './positioning.js'; import type { LLSelectUiTranslationPack } from './i18n.js'; /** * 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. * @group Settings * @category Base */ export type LLSelectOutsideClickBehavior = 'pass-through' | 'block'; /** * 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. * @group Events */ export type LLSelectChangeSource = 'user' | 'api'; /** * 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. * @group Events */ export interface LLSelectChangeMeta { source: LLSelectChangeSource; } /** * One app command rendered by the library as a `role="option"` row inside the * popup list: the entries of `popupListLeadingActionRows` / * `popupListTrailingActionRows`. * - The library builds the row element (id, `role="option"`, classes, * `aria-label`, `aria-disabled`, click and Enter); this object supplies * only the text, the optional rich content, the disabled state and what * activation does. * - `textFn` is the accessible name, and the visible text when there is no * custom content. It is re-read on every row render, so copy can follow * app state or a language switch. * - `createContentElFn`: rich visible content; `null` / omitted = plain text * from `textFn`. Put no focusable control inside: the row itself is the * control. * - `disabledFn`: `null` / omitted = never disabled. Re-read on every row * render and again at activation. A disabled row is skipped by the arrow * keys and cannot be activated. * - `onActivate` runs on Enter, on Space while the filter is inactive, and on * click. The library changes nothing else: choosing and closing are this * callback's job (call `setChosenItems` / `close()` yourself). Synchronous * work runs with the change source `'user'`; a change made after an * `await` reports `'api'`. * - Rows are rebuilt on every list render and after every chosen change, so * the three functions may read live state. They receive no parameters: * close over your instance variable. * @group Action rows */ export interface LLSelectPopupListActionRow { textFn: () => string; createContentElFn?: (() => HTMLElement | null) | null; disabledFn?: (() => boolean) | null; onActivate: () => void; } /** * What a settings function may read while the instance is still being built. * - Only two settings functions receive it: `createPopupHeaderContentElFn` * and `createPopupFooterContentElFn`. The trigger content functions also * run during construction, but they get their own data instead and rerun * on every render. * - `new` has not returned yet. The variable that will hold the instance is * still empty. Do not read it here. * - `classIdMap`: the resolved class and id names. * - `uiTranslationPack`: the resolved translation pack. * - If you need the instance, do that work in an event handler on the element * you return. Handlers run after `new` returns. * @group Popup slots */ export interface LLSelectConstructionContext { readonly classIdMap: Readonly; readonly uiTranslationPack: Readonly; } /** * 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). * @group Settings * @category Base */ export interface LLSelectBaseSettings { /** * 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. * @group CSS */ cssClassPrefix: string; /** * 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). * @group Trigger */ placeholder: string; /** * Accessible name of the field, like the `