import { css, html } from "lit"; import { BaseElement } from "../../core/base-element.ts"; import { oneOf } from "../../core/property-guards.ts"; const tabListOrientations = ["horizontal", "vertical"] as const; const tabListVariants = ["underline", "chip"] as const; /** * CFTabList - Container component for tab buttons * * @element cf-tab-list * * @attr {string} orientation - Layout orientation: "horizontal" | "vertical" (default: "horizontal") * @attr {string} variant - Visual style variant: "underline" | "chip" (default: "underline") * * @slot - Default slot for cf-tab elements * * @example * * Tab 1 * Tab 2 * * * @example * * Tab 1 * Tab 2 * */ export class CFTabList extends BaseElement { static override styles = [ BaseElement.baseStyles, css` :host { --cf-tab-list-border-radius: var( --cf-theme-border-radius, var(--cf-border-radius-md) ); --cf-tab-list-color-surface: var(--cf-theme-color-surface, #f1f5f9); display: flex; flex-shrink: 1; min-width: 0; max-width: 100%; box-sizing: border-box; } :host([orientation="vertical"]) { flex-shrink: 0; max-width: none; } .tab-list { display: inline-flex; align-items: center; justify-content: center; border-radius: var(--cf-tab-list-border-radius, var(--cf-border-radius-md)); background-color: var(--cf-tab-list-color-surface, #f1f5f9); padding: var(--cf-spacing-1); height: 2.5rem; gap: 0.125rem; max-width: 100%; min-width: 0; box-sizing: border-box; } .tab-list[data-orientation="horizontal"] { flex-direction: row; justify-content: flex-start; width: 100%; min-width: 0; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; scrollbar-width: none; flex-wrap: nowrap; } /* Hide scrollbar for webkit browsers */ .tab-list[data-orientation="horizontal"]::-webkit-scrollbar { display: none; } /* Prevent tabs from collapsing inside scroll container */ .tab-list[data-orientation="horizontal"] ::slotted(cf-tab) { flex-shrink: 0; } /* Chip variant container */ .tab-list[data-variant="chip"] { background-color: transparent; border-radius: 0; padding: 0; height: auto; gap: var(--cf-spacing-2, 0.5rem); } .tab-list[data-orientation="vertical"] { flex-direction: column; height: auto; align-items: stretch; } /* Ensure proper spacing for vertical tabs */ .tab-list[data-orientation="vertical"] ::slotted(cf-tab) { width: 100%; justify-content: flex-start; } `, ]; static override properties = { orientation: { type: String, reflect: true }, variant: { type: String, reflect: true }, }; declare orientation: "horizontal" | "vertical"; declare variant: "underline" | "chip"; constructor() { super(); this.orientation = "horizontal"; this.variant = "underline"; } override connectedCallback() { super.connectedCallback(); // Set ARIA attributes this.setAttribute("role", "tablist"); this.setAttribute("aria-orientation", this.orientation); } override firstUpdated() { const slot = this.shadowRoot?.querySelector("slot"); slot?.addEventListener("slotchange", () => this._propagateVariant()); this._propagateVariant(); } override updated(changedProperties: Map) { super.updated(changedProperties); if (changedProperties.has("orientation")) { this.setAttribute("aria-orientation", this.orientation); } if (changedProperties.has("variant")) { this._propagateVariant(); } } protected override willUpdate( changedProperties: Map, ): void { super.willUpdate(changedProperties); if ( changedProperties.has("orientation") || changedProperties.has("variant") ) { this.orientation = oneOf( this.orientation, tabListOrientations, "horizontal", ); this.variant = oneOf(this.variant, tabListVariants, "underline"); } } /** Push variant down to child cf-tab elements so they can style without :host-context */ private _propagateVariant(): void { const slot = this.shadowRoot?.querySelector("slot"); const tabs = slot?.assignedElements() .filter((el) => el.tagName === "CF-TAB") ?? []; for (const tab of tabs) { if (this.variant === "underline") { tab.removeAttribute("data-variant"); } else { tab.setAttribute("data-variant", this.variant); } } } override render() { return html`
`; } }