import { PropertyValues } from 'lit'; import { SpectrumElement } from '../../element/index.js'; import { KeyboardActivation, TabDensity, TabsDirection } from './Tabs.types.js'; /** * Base class for a tabbed interface container. * * Manages selection state, tab/panel association, ARIA wiring, * keyboard navigation, and event dispatching. Concrete classes * supply the stylesheet, render template, and visual behaviors * such as the selection indicator. * * Public API follows the Spectrum 2 / Spectrum Design–aligned surface * from the tabs migration plan: `keyboard-activation` and `density` * instead of legacy `auto`, `compact`, `quiet`, `emphasized`, and * t-shirt `size` attributes. * * @slot - Tab items (elements with `role="tab"`) * @slot tab-panel - Tab panel content (elements with `role="tabpanel"`) * * @fires change - The selected tab has changed. Cancelable — * calling `preventDefault()` reverts the selection. */ export declare abstract class TabsBase extends SpectrumElement { /** * @internal * * Valid direction values for validation and CEM/stories. */ static readonly VALID_DIRECTIONS: readonly TabsDirection[]; /** * @internal */ static readonly VALID_KEYBOARD_ACTIVATIONS: readonly KeyboardActivation[]; /** * @internal */ static readonly VALID_DENSITIES: readonly TabDensity[]; /** * Whether selection follows keyboard focus (`automatic`, default) or the * user must press Enter or Space to activate (`manual`). Prefer `manual` * when tab panels are expensive to render or not fully present in the DOM. * * @see https://w3c.github.io/aria-practices/#kbd_selection_follows_focus */ get keyboardActivation(): KeyboardActivation; set keyboardActivation(value: string); /** @internal */ private _keyboardActivation; /** * Layout density: `regular` (default) or `compact` (reduced tab spacing). */ get density(): TabDensity; set density(value: string); /** @internal */ private _density; /** * The layout direction of the tab list: `horizontal` (default) or `vertical`. * * @default 'horizontal' */ get direction(): TabsDirection; set direction(value: TabsDirection); /** @internal */ private _direction; /** * Whether the entire tab list is disabled. When `true`, * `aria-disabled="true"` is applied to the tablist element and * all interaction is suppressed. */ disabled: boolean; /** * Accessible label for the tablist. Rendered as `aria-label` on the * element with `role="tablist"` in the concrete template. */ accessibleLabel: string; /** * The `tab-id` of the currently selected tab. Setting this property * updates which tab appears selected and which panel is visible. */ selected: string; /** @internal */ private static readonly INDICATOR_BASE_SIZE; /** * @internal * * Inline style applied to the selection indicator element. * Computed from the selected tab's position and dimensions. */ protected selectionIndicatorStyle: string; /** * @internal * * Suppresses the transition on the very first indicator placement * so it doesn't animate from the origin. */ protected shouldAnimate: boolean; /** * @internal * * Cached list of tab elements managed by this container. Updated * via `handleTabSlotChange`. */ private _tabs; /** * @internal * * Manages roving tabindex and arrow-key / Home / End focus movement within * the tab list. Direction is kept in sync with `this.direction` via * `setOptions` in `willUpdate`. Disabled tabs remain in the navigation * sequence (per APG) but are not activatable. * * `getItems` returns an empty array when the container is disabled so all * tabs lose their tab stop, matching the `aria-disabled` tablist behavior. */ private readonly _navigation; /** * Handles `focusgroupNavigationActiveChange` events dispatched by * `_navigation`. In automatic activation mode, selects the newly focused * tab (unless it is disabled). * * Only `source: 'keyboard'` (arrow keys, Home/End) and `source: 'focus'` * (pointer click, Tab-key entry) represent a real focus move that * "selection follows focus" should react to. `'refresh'` and * `'programmatic'` fire when `_navigation` re-parks the roving tab stop * without anyone actually moving focus there — for example on mount or when * re-enabling after `disabled` — and must not trigger a spurious selection * or `change` event. */ private readonly _handleNavigationActiveChange; /** * Whether an assigned node is treated as a tab. `role="tab"` is set in * each tab's `firstUpdated`, so `slotchange` may run before that — accept * known tab host tag names so the tab list and selection indicator sync. */ private static isTabSlotNode; /** * Called by the concrete class when the default slot's content * changes. Rebuilds the internal tab list and syncs selection * state. */ protected handleTabSlotChange(event: Event): void; /** * Called by the concrete class when the `tab-panel` slot's content * changes. Wires up `aria-controls` / `aria-labelledby` between * each tab and its panel. */ protected handlePanelSlotChange(event: Event): void; /** * Click handler bound to the tablist wrapper in the concrete * template. Activates the clicked tab. */ protected handleClick(event: Event): void; /** * Keyboard handler for Enter and Space activation. Arrow-key, Home, and * End navigation is handled by `FocusgroupNavigationController` in capture * phase on the host element. */ protected handleKeyDown(event: KeyboardEvent): void; /** * Attempts to select the given tab element. Dispatches a cancelable * `change` event — if the consumer calls `preventDefault()`, the * selection reverts to the previous value. */ private selectTarget; /** * Synchronizes the `selected` property on each child tab to match * `this.selected`, then parks the roving tab stop on the selected tab via * `_navigation.setActiveItem`. Tabindex management is otherwise owned by * `FocusgroupNavigationController` — for example, `_navigation.refresh()` * resets all tabindex values when the tab list or `disabled` state changes. */ private updateCheckedState; /** * Wires up cross-element ARIA relationships between tabs and * panels. Each tab gets `aria-controls` pointing at its panel's * `id`, and each panel gets `aria-labelledby` pointing at its * tab's `id`. */ private managePanels; /** * @internal * * Recalculates the selection indicator's position and size based * on the currently selected tab element. Uses CSS transforms for * smooth animation between tab positions. * * The indicator is a fixed-size element (100px base) that gets * `scaleX`/`scaleY` to match the selected tab's width/height, * and `translateX`/`translateY` to match its offset position. */ protected updateSelectionIndicator: () => Promise; /** @internal */ private _resizeObserver?; /** * @internal * * Watches for `dir` attribute changes so the indicator recalculates * when writing direction flips at runtime. `getComputedStyle` in * `updateSelectionIndicator` resolves inherited direction correctly, * but nothing else observes it: a `dir` flip on the document root or * an ancestor doesn't resize this element, so the `ResizeObserver` * below won't catch it. Mirrors the `dir`-watching pattern in * `placement-controller.ts`. */ private _directionObserver?; protected willUpdate(changes: PropertyValues): void; connectedCallback(): void; disconnectedCallback(): void; /** * Focuses the selected tab, or the first tab when none is selected yet. * Slotted tabs live in the light DOM; this is more reliable than relying * only on shadow `delegatesFocus` across browsers and test harnesses. */ focus(options?: FocusOptions): void; protected firstUpdated(changes: PropertyValues): void; /** * Waits for all child tab elements to finish their update cycle * before resolving so layout-dependent callers see stable geometry. */ getUpdateComplete(): Promise; }