import{type PropertyValues,type TemplateResult}from'lit';import{LyraElement}from'../../../internal/lyra-element.js';import{type BreakpointBasis}from'../../../internal/orientation-breakpoint.js';import type{LyraOrientation}from'../../../internal/shared-unions.js';export type LyraStepState='pending'|'current'|'completed'|'error';export interface LyraStepperOrientationChangeDetail{orientation:LyraOrientation;}export interface LyraStepItem{ /** Stable business identity for this step. Duplicate IDs are valid occurrences and are * disambiguated by their zero-based collection index. */ stepId:string; /** Nonblank text used as the step's visible and accessible name. */ label:string; /** Progress state. Availability is independent so a disabled step retains its progress. */ state:LyraStepState; /** Excludes this step from activation and roving focus without changing `state`. */ disabled?:boolean; /** Optional native `title` tooltip for this step -- e.g. explaining why a disabled step is * locked. Renders on the step's button, or on the non-interactive item that replaces it while * `readonly`. Omit for no `title` attribute at all (not an empty string). */ title?:string; /** Optional leading topic glyph for this step (e.g. a payment icon on a "Payment" step) -- * same `LyraPaletteItem`/`MentionItem`/`LyraSegmentedItem` precedent: intentionally general content * (a `TemplateResult`, an emoji string, etc.), not a square-icon-only field. Rendered * additionally to, never instead of, the state-driven index chip/checkmark -- the icon * identifies the step's topic, the chip/checkmark identifies its state. It is inert and hidden * from assistive technology, so it never supplies a second action or accessible name. */ icon?:unknown;}export interface LyraStepperEventMap{'lr-step-select':CustomEvent<{stepId:string;index:number;}>;'lr-stepper-orientation-change':CustomEvent;} /** * `` — ordered multi-step wizard/form navigation: label + index per step, * current/completed/locked/error state, click-to-jump. First-party invention (no Web Awesome * equivalent). Fully data-driven and controlled, like `lr-table`'s `columns`/`rows` -- it never * mutates `steps` itself; a click or Enter/Space on a non-disabled step fires a non-cancelable * `lr-step-select`, and the host decides whether/how `steps` changes in response. * * An opt-in `orientationBreakpoint` (unset by default -- no behavior change) makes the effective * layout/navigation axis respond to a measured inline size instead of only the authored * `orientation`: below that width (a pixel number or a `px`/`rem`/`em` CSS length), `narrowOrientation` * becomes effective; at/above it, `orientation` does. This mirrors ``'s identically-named * `orientationBreakpoint`/`narrowOrientation`/`orientationBreakpointBasis` contract. Under the default * `orientationBreakpointBasis="container"` the breakpoint is measured on this stepper's own * `[part="base"]` inline size via `ResizeObserver`, so a stepper placed in a narrow split pane or * dialog still responds correctly even in a wide window; `orientationBreakpointBasis="viewport"` * instead evaluates `matchMedia('(max-width: )')`, needed when the stepper has a fixed * width in a row that stacks at a shared breakpoint. The effective axis is exposed via the * `effectiveOrientation` getter, a `data-effective-orientation` host attribute (only present while * the breakpoint feature is active), and `lr-stepper-orientation-change`. * * An opt-in `readonly` (false by default -- no behavior change) turns the same data into a passive * progress display: every step renders as a non-interactive item rather than a button, so no step * takes a tab stop and no `lr-step-select` is emitted. (A read-only horizontal strip that actually * overflows moves the single tab stop onto its own scroll container instead, so its off-screen * steps stay keyboard-reachable.) It is deliberately *not* a disabled * treatment -- `disabled` says "you may not do this", read-only says "there is nothing to do here" * -- so read-only steps keep normal opacity and every state glyph, current-step marker and * `--lr-stepper-*` custom property. * * @customElement lr-stepper * @event lr-step-select - Fired on click, or Enter/Space while focused, on a non-`disabled` * step. Never fired while `readonly`. `detail: { stepId, index }`; the index disambiguates * legitimate duplicate step IDs. * Not cancelable: this component is fully controlled (mirrors * `lr-table`'s `columns`/`rows` contract) and takes no default action of its own on selection * (it never mutates `steps`), so there is no real veto point for `preventDefault()` to gate. * @event lr-stepper-orientation-change - `detail: { orientation }`, fired when an enabled * `orientationBreakpoint` changes the effective layout/navigation axis. * @csspart base - The root wrapper, and the horizontally scrolling track. Takes `tabindex="0"` * only while `readonly` and genuinely overflowing, so an otherwise keyboard-unreachable * read-only strip can still be scrolled; style that state with `::part(base):focus-visible`. * @csspart step-item - The `role="listitem"` wrapper for one step. * @csspart step - A single step button, or a non-interactive `
` carrying the same part while * `readonly`. * @csspart step-icon - Optional inert, aria-hidden leading topic glyph supplied by the item's * `icon` field; content may have a natural aspect ratio and is not restricted to a square icon. * Rendered additionally to, never instead of, `step-index`/`step-check`. * @csspart step-index - The numbered index chip, shown for `pending`/`current`/`error` steps. * @csspart step-check - The completed-checkmark glyph, shown for `completed` steps instead of `step-index`. * @csspart step-label - The step's label text. * @cssprop [--lr-scroll-fade-size=2rem] - Width of each decorative horizontal overflow fade. * Pointer-state hooks use inline `var()` fallbacks rather than a `:host` declaration, so they * inherit from the stepper or any ancestor without retheming another state. * @cssprop [--lr-stepper-hover-bg=var(--lr-color-brand-quiet)] - Background of a hovered, * non-disabled step. * @cssprop [--lr-stepper-hover-color=var(--lr-color-text)] - Text color of a hovered, * non-disabled step. * @cssprop [--lr-stepper-active-bg=color-mix(in oklab, var(--lr-color-brand-quiet), var(--lr-color-mix-partner) var(--lr-color-mix-active))] - Background of a pressed, non-disabled step. * @cssprop [--lr-stepper-active-color=var(--lr-color-text)] - Text color of a pressed, * non-disabled step. * @cssprop [--lr-stepper-current-color=var(--lr-color-text)] - Text color of the `current` step. * Declared as an inline `var()` fallback (never on `:host`), so setting it on the element or an * ancestor recolors only the current step without hijacking the library-wide `--lr-color-text` token. * @cssprop [--lr-stepper-current-font-weight=var(--lr-font-weight-semibold)] - Font weight of the * `current` step's label. `::part(step)[data-state='current']` is invalid CSS (an attribute * selector cannot follow `::part`), so this is the only way to change just the current step's * boldness without hijacking the library-wide `--lr-font-weight-semibold` token. * @cssprop [--lr-stepper-error-color=var(--lr-color-danger)] - Text color of an `error` step. * @cssprop [--lr-stepper-current-index-bg=var(--lr-color-brand)] - Background of the `current` step's * numbered index chip (`step-index`). * @cssprop [--lr-stepper-current-index-color=var(--lr-color-on-brand)] - Text color of the `current` * step's numbered index chip. * @status stable * @since 4.0.0 */ export declare class LyraStepper extends LyraElement{static styles:import("lit").CSSResultGroup[]; /** Ordered step data. Never mutated by this component -- see the class doc's controlled- * component contract. Empty (the default) renders nothing. Each step's optional `title` * renders as a native `title` tooltip on that step -- e.g. to explain why a `disabled` step is * locked. */ private effectiveSteps;get steps():readonly LyraStepItem[];set steps(value:readonly LyraStepItem[]); /** `'horizontal'` (the default) lays steps out in a row (Left/Right, RTL-aware, to navigate); * `'vertical'` stacks them (Up/Down navigate instead, no RTL swap needed). The *authored* axis * used at/above `orientationBreakpoint` (or always, when that's unset) -- see * `effectiveOrientation` for the live axis actually in effect. */ orientation:LyraOrientation; /** Opt-in inline-size breakpoint, measured on `[part="base"]`. Below it, `narrowOrientation` * becomes effective instead of `orientation`. Unset (the default): no behavior change, the * authored `orientation` always applies. * * Accepts a bare pixel number (`500`, `'500'`), an explicit `px` length (`'500px'`), a `rem` * length (`'31.25rem'`) or an `em` length (`'3em'`). Under the default * `orientationBreakpointBasis="container"`, `rem` resolves against the **document root**'s * computed font size -- the rule a `@container` query follows, and *not* a `@media` query's -- * while `em` resolves against this element's own computed font size. The length is re-resolved * on every measurement, never cached, so browser zoom, a user font-size preference or an app * base-size token change are picked up with no invalidation step. To stay in step with a * sibling `@media (max-width: …rem)` rule, use `orientationBreakpointBasis="viewport"`, which * hands the length to the browser instead; see that property for why the two differ. * * Any other value -- `%`, `vw`, `calc()`, `'auto'`, an unparseable string -- behaves exactly as * unset (no responsive observation at all), rather than as an armed breakpoint that can never * be crossed. Set `orientationBreakpointBasis="viewport"` for a viewport-relative breakpoint * instead. */ orientationBreakpoint?:number|string; /** Which box `orientationBreakpoint` measures. `'container'` (the default) observes this * stepper's own `[part="base"]` inline size via `ResizeObserver`, comparing strictly `<`. * `'viewport'` instead evaluates `matchMedia('(max-width: )')`, which is inclusive * (`<=`) -- native `max-width` semantics, deliberately, so the crossing point matches a CSS * `@media` rule authored with the same length exactly. * * A stepper given a fixed width in a row layout cannot react to that row stacking by measuring * itself -- its own width never changes. That case requires `'viewport'`, which also lets the * browser resolve a `rem` breakpoint with real `@media` semantics. */ orientationBreakpointBasis:BreakpointBasis; /** Layout/navigation axis used below `orientationBreakpoint`. */ narrowOrientation:LyraOrientation; /** When true, allows step labels to wrap when the effective orientation is vertical. The * default preserves the single-line labels used by the original stepper contract; horizontal * labels remain single-line even when this property is enabled. */ wrapLabels:boolean; /** Turns the strip into a passive progress display. Each step renders as a non-interactive * `role="listitem"` item instead of a button: no `tabindex`, no `aria-disabled`, no click or * Enter/Space activation, and therefore no `lr-step-select` at all. Everything that describes * *progress* is kept untouched -- the index chip, the completed checkmark, the optional topic * icon, the per-step `title`, `aria-current="step"` on the current step, and every * `--lr-stepper-*` custom property. * * Deliberately not `disabled` styling: `disabled` says "you may not do this", read-only says * "there is nothing to do here", so a read-only step keeps normal opacity and simply loses its * pointer cursor (the same bargain `lr-slider` and `lr-rating` strike for their own `readonly`). * A per-step `disabled` flag is inert while read-only for the same reason -- there is no * activation left for it to gate, so it contributes no dimming either. * * No *step* takes a tab stop: roving tabindex exists to give a composite *control* exactly one * entry point, and a passive list is not a control. The one exception is the scroll container * itself -- a read-only horizontal strip that genuinely overflows gives `[part="base"]` * `tabindex="0"` so its hidden steps stay reachable by keyboard (see `syncScrollTabStop()`); * a strip that fits, or a vertical one, still takes no tab stop at all. Unset (the default) is * byte-for-byte the previous behavior. */ readonly:boolean; /** Accessible name for the `role="list"` step strip. Attribute-reflects from a host-level * `aria-label` so a plain-markup consumer gets ARIA-name forwarding without setting a JS * property. Unset, the list renders without an `aria-label` (the role carries no localized * default name). */ accessibleLabel:string|null;private _effectiveOrientation;private resizeObserver?;private resizeObservedElement?;private resizeObserverOwnerDocument?;private resizeObserverGeneration;private pendingStepFocus?;private baseEl?; /** Best-known inline size before `baseEl` exists (or as a fallback while it's momentarily * unmeasured) -- seeded from a real reading of the host's own box in `connectedCallback()` * (see its comment) so the very first render already classifies correctly under the default * `'container'` basis, instead of falling back to the always-'wide' `Number.POSITIVE_INFINITY` * sentinel until the `ResizeObserver`'s own necessarily async first callback lands. */ private measuredInlineSize; /** Owns breakpoint resolution, basis selection, and the viewport `MediaQueryList` lifecycle * (including teardown on disconnect) -- see `OrientationBreakpointController`. */ private orientationBreakpoints; /** Gates the horizontal [part="base"] edge fade on the track genuinely overflowing, with * one-sided/RTL-aware logical-edge state -- see --lr-scroll-fade-size and stepper.styles.ts. * Independent of the orientation controller above: the vertical rules zero the mask out * regardless of this attribute. Stored (rather than a bare statement-expression call) so * `updated()` can register each step on the controller's own `ResizeObserver` via * `observeExtra()` below -- a step's own intrinsic content (a longer localized label, an icon * loading in) can grow scrollWidth without [part="base"]'s own border box changing at all. * The `onResize` hook re-syncs the read-only scroll tab stop from inside this controller's own * (already-measured) `ResizeObserver` callback, which is the one path that flips overflow * without a host update for `updated()` to ride on -- see `syncScrollTabStop()`. */ private scrollOverflow; /** The live layout/navigation axis after applying `orientationBreakpoint` -- identical to * `orientation` whenever that's unset. See the class doc. */ get effectiveOrientation():LyraOrientation;connectedCallback():void;disconnectedCallback():void;adoptedCallback():void;protected firstUpdated(changed:PropertyValues):void;protected willUpdate(changed:PropertyValues):void;protected updated(changed:PropertyValues):void; /** Gives the horizontally scrolling `[part="base"]` its own tab stop while, and only while, it * both is `readonly` and genuinely overflows. * * A read-only strip renders every step as a plain `
`, so it has no tabbable descendant of * any kind. A scroll container with content to scroll and nothing tabbable inside is * unreachable by keyboard -- the WCAG 2.1.1 failure axe reports as * `scrollable-region-focusable` -- so the container itself takes the stop. `role="list"` and * its optional `aria-label` are unchanged, and `onKeyDown()` already returns early while * read-only, so this is a pure scroll stop: arrow keys scroll the box natively and no roving * semantics come back with it. * * Both halves of the gate matter. While interactive, the roving tabindex already guarantees * exactly one tabbable step inside, and a second stop on the container would make every * stepper cost two tabs instead of one. While read-only but fitting, there is nothing to * scroll to, so a stop there would be dead weight -- the same "only when it actually * overflows" bargain the edge fade above already strikes, and what the browsers' own * keyboard-focusable-scrollers behavior does. The vertical axis is excluded outright because * its rules set `overflow: visible` on both axes (stepper.styles.ts), so that box never * scrolls whatever `scrollWidth` reports. * * Written as an attribute rather than through the template because overflow is measured, not * rendered: the controller flips it from a `ResizeObserver`/content change with no state Lit * could re-render from, and `[part="base"]` is a stable node across every re-render. Mirrors * the `syncAriaDescribedByElements()` call in `updated()` on this same element. */ private syncScrollTabStop; /** Classifies a measured inline size into the effective layout/navigation axis and, only on an * actual transition, applies it -- mirrors ``'s identically-shaped * `updateEffectiveOrientation()`. `emitOnChange` is false for the property-driven re-derivation * in `willUpdate()` (except a genuine viewport-basis transition after the first update -- see * `willUpdate()`'s own doc comment) and true for the `ResizeObserver`/`matchMedia` callbacks' * own fresh measurements. */ private updateEffectiveOrientation; /** Creates (idempotently) and (re-)observes `[part="base"]` -- a no-op until `baseEl` exists (see * `firstUpdated()`/`connectedCallback()`). */ private armResizeObserver;private resetResizeObserver;private selectStep;private focusStep;private onKeyDown;render():TemplateResult;}declare global{interface HTMLElementTagNameMap{'lr-stepper':LyraStepper;}}