import { css, html } from "lit"; import { BaseElement } from "../../core/base-element.ts"; /** * CFTab - Individual tab button component used within cf-tab-list * * @element cf-tab * * @attr {string} value - Unique identifier for the tab * @attr {boolean} disabled - Whether the tab is disabled * @attr {boolean} selected - Whether the tab is currently selected * * @slot - Default slot for tab label content * * @fires tab-click - Fired when tab is clicked with detail: { tab } * * @example * Profile */ export class CFTab extends BaseElement { static override properties = { value: { type: String, reflect: true }, disabled: { type: Boolean }, selected: { type: Boolean }, }; static override styles = [ BaseElement.baseStyles, css` :host { display: inline-flex; } .tab { display: inline-flex; align-items: center; justify-content: center; white-space: nowrap; border-radius: var(--cf-theme-border-radius, var(--cf-border-radius-md)); padding: var(--cf-spacing-2) var(--cf-spacing-3); font-size: var(--cf-font-size-sm); font-weight: var(--cf-font-weight-medium); transition: all var(--cf-transition-duration-fast) var(--cf-transition-timing-ease); cursor: pointer; background: transparent; border: none; color: var(--cf-theme-color-text-muted, #6b7280); font-family: inherit; position: relative; } .tab:hover:not(:disabled) { color: var(--cf-theme-color-text, #111827); } .tab:focus-visible { outline: 2px solid var(--cf-theme-color-primary, var(--cf-colors-primary-500)); outline-offset: 2px; } .tab:disabled { opacity: 0.5; cursor: not-allowed; } .tab[data-selected="true"] { color: var(--cf-theme-color-text, #111827); } /* Indicator for selected state */ .tab[data-selected="true"]::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background-color: var( --cf-theme-color-primary, var(--cf-colors-primary-500) ); } /* Vertical orientation styles */ :host-context(cf-tab-list[orientation="vertical"]) .tab[data-selected="true"]::after { top: 0; bottom: 0; left: -1px; right: auto; width: 2px; height: auto; } /* ===== Chip variant styles ===== */ /* Suppress underline indicator in chip mode */ :host([data-variant="chip"]) .tab[data-selected="true"]::after { display: none; } /* Base chip tab styling */ :host([data-variant="chip"]) .tab { border-radius: var(--cf-border-radius-full, 9999px); padding: var(--cf-pill-sm-padding-v, 2px) var(--cf-pill-sm-padding-h, 10px); font-size: var(--cf-pill-sm-font-size, var(--cf-size-sm-font-size, 11px)); line-height: var( --cf-pill-sm-line-height, var(--cf-size-sm-line-height, 16px) ); min-height: var(--cf-pill-sm-min-height, var(--cf-size-sm-height, 24px)); color: var(--cf-theme-color-text-muted, #6b7280); background: transparent; border: 1px solid transparent; transition: background-color var(--cf-transition-duration-fast, 150ms) var(--cf-transition-timing-ease, ease), border-color var(--cf-transition-duration-fast, 150ms) var(--cf-transition-timing-ease, ease), color var(--cf-transition-duration-fast, 150ms) var(--cf-transition-timing-ease, ease); } /* Hover on unselected chip tab */ :host([data-variant="chip"]) .tab:hover:not(:disabled):not([data-selected="true"]) { background: var( --cf-theme-color-surface-hover, var(--cf-colors-gray-200, #eceef1) ); color: var(--cf-theme-color-text, #111827); } /* Selected chip tab - filled pill appearance */ :host([data-variant="chip"]) .tab[data-selected="true"] { background: var( --cf-theme-color-surface, var(--cf-colors-gray-100, #f2f3f6) ); border: 1px solid var( --cf-theme-color-border, var(--cf-colors-gray-300, #d5d7dd) ); color: var(--cf-theme-color-text, var(--cf-colors-gray-900, #16181d)); font-weight: var(--cf-font-weight-medium, 500); } `, ]; declare value: string; declare disabled: boolean; declare selected: boolean; private _button: HTMLButtonElement | null = null; constructor() { super(); this.value = ""; this.disabled = false; this.selected = false; } get button(): HTMLButtonElement | null { if (!this._button) { this._button = this.shadowRoot?.querySelector(".tab") as HTMLButtonElement || null; } return this._button; } override connectedCallback() { super.connectedCallback(); // Set ARIA attributes this.setAttribute("role", "tab"); this.updateAriaAttributes(); } override updated( changedProperties: Map, ) { super.updated(changedProperties); if ( changedProperties.has("selected") || changedProperties.has("disabled") ) { this.updateAriaAttributes(); } } override render() { return html` `; } private updateAriaAttributes(): void { this.setAttribute("aria-selected", String(this.selected)); this.setAttribute("aria-disabled", String(this.disabled)); this.setAttribute( "tabindex", this.selected && !this.disabled ? "0" : "-1", ); } private handleClick = (event: Event): void => { if (this.disabled) { event.preventDefault(); event.stopPropagation(); return; } // Emit custom event for parent tabs component this.emit("tab-click", { tab: this }); }; /** * Focus the tab button */ override focus(): void { this.button?.focus(); } /** * Blur the tab button */ override blur(): void { this.button?.blur(); } }