import{type TemplateResult,type PropertyValues}from'lit';import{LyraElement,type LyraEventDetailSnapshot}from'../../../internal/lyra-element.js';import type{LyraSize}from'../../../internal/variants.js';import type{LyraSelectionDirection}from'../../../internal/shared-unions.js';import type{LyraOption}from'./option.class.js';import'./option.class.js';import type{PlaceStrategy,PlaceSync}from'../../../internal/positioner.js';import{type FormOwnerValue}from'../../../internal/form-associated.js';import type{LyraPickerDetailValue,LyraPickerValue}from'../../../internal/picker-value.js';export type OptionFilter=(option:LyraOption,query:string)=>boolean;export type LyraComboboxPlacement='top'|'bottom';export type LyraComboboxTagRenderer=(option:LyraOption,index:number)=>unknown; /** What a `validators` entry may return: nothing/`true` passes, a string is the message, `false` is * a generic failure, and an object of {@linkcode ValidityStateFlags} names the flags to raise. */ export type LyraComboboxValidatorResult=void|boolean|string|ValidityStateFlags; /** Result shape accepted from object validators used by the upstream form-control contract. */ export interface LyraComboboxObjectValidatorResult{message:string;isValid:boolean;invalidKeys:Exclude[];} /** Structural compatibility shape for an object validator. The `never` callback input is * intentional: it lets an array typed by another custom-element package remain assignable while * Lyra invokes the callback with this host at runtime. Author new Lyra validators with the * strongly typed function or `validate()` branches of {@linkcode LyraComboboxValidator}. */ export interface LyraComboboxObjectValidator{ /** Host attributes that trigger a fresh validity check when they change. */ observedAttributes?:string[];checkValidity:(input:never)=>LyraComboboxObjectValidatorResult;message?:string|((input:never)=>string);}export type LyraComboboxValidator=((value:string|string[],input:LyraCombobox)=>LyraComboboxValidatorResult)|{validate(value:string|string[],input:LyraCombobox):LyraComboboxValidatorResult;}|LyraComboboxObjectValidator;export interface ComboboxSourceRow{readonly value:string;readonly label:string;readonly sub?:string; /** Optional decorative leading visual. Its rendered subtree is inert and aria-hidden. */ readonly icon?:unknown; /** Leading adornment, mirroring ``'s `start`/`prefix` slots. Inert and aria-hidden, * like `icon`, so it never joins the option's accessible name. */ readonly start?:unknown; /** Trailing adornment, mirroring ``'s `end`/`suffix` slots. */ readonly end?:unknown; /** Optional trailing metadata badge. */ readonly badge?:string|number; /** Spoken option label when the visible row needs additional context. */ readonly accessibleLabel?:string; /** Opaque application payload retained in `selectedRows`. */ readonly data?:unknown;readonly dotColor?:string;readonly group?:string;readonly disabled?:boolean;} /** Bounded async response envelope. `total` is the provider-side match count before its own cap. */ export interface ComboboxSourceResult{readonly rows:readonly ComboboxSourceRow[];readonly total?:number;} /** Async row provider for a remote-backed combobox. Receives the current query and an options bag * carrying an `AbortSignal` and the component's hard row ceiling. Forward the signal to * `fetch(url, { signal })` and honor `limit` when practical. A legacy bare row array remains valid; * `{ rows, total }` exposes provider-side truncation truthfully. * The options parameter is required by the exported type so implementations can consume the * cancellation signal. A one-parameter `(query) => …` function remains assignable under * TypeScript's ordinary function-parameter compatibility rules. */ export type ComboboxSource=(query:string,options:{signal:AbortSignal;limit:number;})=>Promise;export type LyraComboboxSelectionDirection=LyraSelectionDirection; /** Detail of `lr-filter`: the in-progress filter text, never the committed selection. */ export interface ComboboxFilterDetail{value:string;}export type{LyraPickerDetailValue,LyraPickerValue};export interface LyraComboboxEventMap{'lr-invalid':CustomEvent;'lr-show':CustomEvent;'lr-after-show':CustomEvent;'lr-hide':CustomEvent;'lr-after-hide':CustomEvent;'lr-clear':CustomEvent;'lr-create':CustomEvent<{inputValue:string;}>;'lr-filter':CustomEvent; /** `detail.data` is index-aligned with `detail.value`: `data[i]` is the opaque `data` payload * behind `value[i]` (light-DOM `` or an async source row's own `data`), by * reference and never deep-cloned, or `undefined` for a value resolving to no live row/option. */ 'lr-change':CustomEvent;readonly data:readonly unknown[];}>>;'lr-activate':CustomEvent<{value:string;}>;'lr-source-error':CustomEvent<{error:unknown;query:string;}>;'lr-retry':CustomEvent;input:InputEvent|CustomEvent;readonly data:readonly unknown[];}>>;change:CustomEvent;readonly data:readonly unknown[];}>>;blur:FocusEvent;focus:FocusEvent;} /** * Stable per-event aliases, so a host can name one event's type without restating the detail * schema (or re-deriving it from `LyraComboboxEventMap`). Each narrows with the same `Multiple` * parameter the component does: `LyraComboboxChangeEvent`'s `detail.value` is a `string`. */ export type LyraComboboxChangeEvent =LyraComboboxEventMap['lr-change'];export type LyraComboboxSourceErrorEvent=LyraComboboxEventMap['lr-source-error']; /** * `` — a filterable single/multi select that combines a text * input with a listbox. Mirrors the core `` API under `lr-`. * * Options are `` children. Emits native-style `change`/`input` * (like Web Awesome) plus `lr-show`/`lr-hide`/`lr-clear`. * Enter commits the highlighted option while the listbox has one; with nothing highlighted it * performs the implicit form submission a native text field would (see * `internal/submit-on-enter.ts` — the internal input is in a shadow root and has no form owner, so * the platform can never do it here). * Standard size tiers share their outer control height with sibling Lyra controls; the decorative * expand icon scales inside that allocation without creating an independent action target. * If local options or async rows change while a row is keyboard-active, the active descendant * clamps to the nearest enabled survivor and clears when none remain. * An async `source` failure renders as a disabled listbox row, not a shadow-root live region; each * current post-mount rejection appends the localized `comboboxLoadError` message to the shared * light-DOM assertive announcement sink. Raw caught error text is never exposed to users. * The floating listbox is a nonmodal shared-overlay-manager entry. Visual stack order, Escape, * outside-pointer dismissal, and focus handoff are therefore owned by only the newest Lyra * overlay, including when another popup such as `lr-color-picker` remains open underneath it. * The editable combobox input exposes explicit stateful `aria-invalid`: visible error chrome wins * immediately, while intrinsic/custom invalidity is exposed only after interaction. * * Host `aria-describedby` targets supplement internal hint/error guidance on the semantic * control, including live target replacement and document adoption. Removing label, hint, or * error attributes safely omits their content while retaining native null property readback. * Mounted option `selected` writes immediately update the live value and submission silently; * reset defaults stay independent, and later default changes preserve a dirty selection. * * Composing keys stay with text editing. Inert source options and their inert ancestors are * unavailable through popup rows; named option adornment mutations refresh their presentation. * Single mode exposes one selected occurrence while retaining backing multiple-selection history. * * Assigning `undefined`/`null` to `value` clears the selection; every string, including `''`, is * instead a candidate value resolved against the current local options/async rows -- an * `` (or a matching row) is legitimate and now round-trips like any other. A * committed value matching no current option/row (a stale value, or a programmatic assignment with * a typo) still commits rather than being dropped, but renders with a dashed/italic * `[part='unknown-value']` badge instead of silently passing the raw string off as an ordinary * label -- see `isUnknownValue()`. Suppressed while an async `source` fetch has never yet resolved * for this element, and never shown for an `allowCustomValue` commit, which is a sanctioned * unmatched value, not a stale one. Over that same unresolved window the raw value itself is * withheld too -- the trigger (and any `multiple`-mode tag for the same value) shows the * `loadingText` placeholder instead of the raw string, since it is not yet knowable whether the * value is even unmatched. * * @customElement lr-combobox * @slot - `` elements. * @slot label - Custom label content. * @slot hint - Custom hint content. * @slot error - Custom error content. * @slot start - Adornment at the inline-start of the trigger row, before the selected-value tags * and the filter input. Slotted content is decorative chrome, not an option: `collectOptions()` * only ever collects `` elements from the default slot. * @slot end - Adornment after the filter input and the built-in clear action, and before the * expand icon — so consumer content never sits outboard of the dropdown chevron. * @slot clear-icon - Replaces the clear button's built-in icon. * @slot expand-icon - Replaces the dropdown indicator's built-in icon. * @event {CustomEvent; readonly data: readonly unknown[]; }>>} change - The selection changed through user * interaction. A bubbling, composed, non-cancelable event carrying `detail: { value, data }` (the * new committed selection: a string in single mode, a string[] in `multiple` mode; `data` is * index-aligned with `value` -- `data[i]` is the opaque `data` payload of the row/option behind * `value[i]`, by reference and never deep-cloned, or `undefined` for a value that resolves to no * live row/option -- see `isUnknownValue()`). * @event {InputEvent | CustomEvent; readonly data: readonly unknown[]; }>>} input - The user typed in the * filter or changed the selection. Text edits expose the original InputEvent (no `value` detail); * selection changes emit a bubbling, composed, non-cancelable event carrying `detail: { value, data }`. * @event {CustomEvent; readonly data: readonly unknown[]; }>>} lr-change - Prefixed compatibility alias fired * after `input` and `change` on the same selection change, mirroring ``'s `lr-change`. * `detail: { value, data }`. Not fired for typing or a programmatic `value` assignment. * @event lr-activate - Fired on every activation of an available listbox row -- a click, or * Enter on the active row -- whether or not the selection actually moved. `detail: { value }` * carries the activated option's own value, always a single string even in `multiple` mode. * Bubbling and composed, so a host outside the shadow tree receives it. Not cancelable: it is a * notification that the user picked a row, not a veto point, and nothing in this component * branches on it. In single-select mode, re-picking the already-selected row is the case * `change`/`lr-change` deliberately stay silent for -- "re-run that filter" is a real intent -- * and it is otherwise unobservable, because the rows live in this shadow root, so a retargeted * `click` names no option and a keyboard commit produces no click at all. When an activation does * move the selection, `input`/`change`/`lr-change` are emitted first. Not fired for typing, for a * committed custom value that matches no row, for the clear button, or for a programmatic `value` * assignment. * @event lr-source-error - An async `source` call rejected. `detail: { error, query }` carries the * raw rejection, so a host can log or report it (the rendered copy stays localized and never * shows it), plus the exact query string that call was made with -- the rejected call's own * query, not necessarily `this.query`/`inputValue`, which may have moved on (or been cleared by * closing the listbox) by the time the rejection settles. Not cancelable — the failure has * already happened and the error row is already what rendered, so there is nothing to veto. * @event lr-retry - The failed-load state's `[part='retry-button']` was activated. Cancelable — * the built-in action calls `refresh()`, and `preventDefault()` leaves the failure on screen for * a host that owns its own retry timing. * @method refresh - `refresh(): void` — re-runs the current `source` query without changing the * source's identity, its debounce controller, or its delay. Queues for the next open when the * listbox is closed; does nothing without a `source`. * @slot source-error - Replaces the built-in failed-`source` state, retry control included. Named * apart from the form-control `error` slot deliberately: they are different failures and a * control has to be able to show both. * @event lr-show - The listbox is about to open, however `open` became true. Cancelable — * `preventDefault()` leaves it closed and the reflected attribute untouched. * @event lr-after-show - The listbox finished opening and its transition settled. * @event lr-hide - The listbox is about to close, however `open` became false. Conditionally * cancelable: connected transitions can be vetoed on the same terms as `lr-show`; an * already-removed element closing on disconnect cannot honour a veto. A connected veto also * preserves the live filter query, active option, and async rows exactly. * @event lr-after-hide - The listbox finished closing and its transition settled. * @event lr-clear - The value was cleared. * @event {CustomEvent<{ inputValue: string }>} lr-create - Cancelable request to create a * nonmatching input value. Prevent the event to supply a normalized option/value yourself. * @event {CustomEvent} lr-filter - The in-progress filter text changed * through user input. `detail.value` is the live filter string, which is not the same thing as the * host's `value` (the committed selection). User-input only: typing and the clear button announce * it. Programmatic edits, including `setRangeText()`, are silent. `setRangeText()` preserves * the replaced filter text and synchronizes its query/options without changing selected value. * Picking a row, resetting the form, and dismissing the listbox also update the filter silently. * @event {FocusEvent} blur - Re-dispatched from the internal native input as a bubbling, composed, * non-cancelable event. * @event {FocusEvent} focus - Re-dispatched from the internal native input as a bubbling, composed, * non-cancelable event. * @event lr-invalid - The combobox failed a validity check. Cancelable: calling * `preventDefault()` also cancels the native `invalid` event behind it, suppressing the * browser's own validation bubble so an app can present the failure its own way. * @csspart form-control - The outer wrapper around label, combobox, listbox, error and hint. * @csspart form-control-label - The `