import { css, html, nothing } from "lit";
import { BaseElement } from "../../core/base-element.ts";
/**
* CFTabBarItem - Individual navigation item for cf-tab-bar
*
* @element cf-tab-bar-item
*
* @attr {string} value - Unique identifier for this item
* @attr {string} label - Text label rendered below the icon
* @attr {boolean} hide-label - Hide the text label for icon-only items (default: false).
* @attr {boolean} disabled - Prevents selection and keyboard navigation
* @prop {boolean} selected - Set by parent bar when this item is active
*
* @slot icon - Icon content (emoji, SVG, or glyph), rendered above the label
* @slot - Alternative label content; overrides the label attribute when present
*
* @fires tab-bar-click - Fired when item is clicked with detail: { item }
*/
export class CFTabBarItem extends BaseElement {
static override properties = {
value: { type: String, reflect: true },
label: { type: String, reflect: true },
hideLabel: { type: Boolean, reflect: true, attribute: "hide-label" },
disabled: { type: Boolean, reflect: true },
selected: { type: Boolean },
};
static override styles = [
BaseElement.baseStyles,
css`
:host {
display: inline-flex;
flex: 1;
}
.item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--cf-tab-bar-item-gap, var(--cf-spacing-1, 0.25rem));
width: 100%;
border: none;
background: transparent;
cursor: pointer;
padding: var(--cf-spacing-1, 0.25rem) var(--cf-spacing-2, 0.5rem);
color: var(
--cf-tab-bar-item-color,
var(--cf-theme-color-text-muted, #6b7280)
);
font-family: inherit;
transition: all var(--cf-transition-duration-fast, 150ms)
var(--cf-transition-timing-ease, ease);
}
.item:focus-visible {
outline: 2px solid
var(--cf-theme-color-primary, var(--cf-colors-primary-500));
outline-offset: 2px;
border-radius: var(--cf-border-radius-sm, 0.25rem);
}
.item[data-selected="true"] {
color: var(
--cf-tab-bar-item-color-active,
var(--cf-theme-color-primary, var(--cf-colors-primary-500))
);
}
.item:disabled,
.item[aria-disabled="true"] {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
.icon {
display: flex;
align-items: center;
justify-content: center;
height: var(--cf-tab-bar-item-icon-size, 1.5rem);
}
.label {
font-size: var(
--cf-tab-bar-item-label-size,
var(--cf-font-size-xs, 0.75rem)
);
line-height: 1;
white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) {
.item {
transition: none;
}
}
`,
];
declare value: string;
declare label: string;
declare hideLabel: boolean;
declare disabled: boolean;
declare selected: boolean;
private _button: HTMLButtonElement | null = null;
constructor() {
super();
this.value = "";
this.label = "";
this.hideLabel = false;
this.disabled = false;
this.selected = false;
}
get button(): HTMLButtonElement | null {
if (!this._button) {
this._button =
(this.shadowRoot?.querySelector(".item") as HTMLButtonElement) ||
null;
}
return this._button;
}
override updated(
changedProperties: Map,
) {
super.updated(changedProperties);
if (
changedProperties.has("selected") || changedProperties.has("disabled")
) {
this._updateAriaAttributes();
}
}
override render() {
return html`
`;
}
private _updateAriaAttributes(): void {
const button = this.button;
if (!button) return;
if (this.selected) {
button.setAttribute("aria-current", "page");
} else {
button.removeAttribute("aria-current");
}
if (this.disabled) {
button.setAttribute("aria-disabled", "true");
button.setAttribute("tabindex", "-1");
} else {
button.removeAttribute("aria-disabled");
button.setAttribute("tabindex", this.selected ? "0" : "-1");
}
// Also update data-selected on host for ::part() CSS selectors
if (this.selected) {
this.setAttribute("data-selected", "true");
} else {
this.removeAttribute("data-selected");
}
}
private _handleClick = (event: Event): void => {
if (this.disabled) {
event.preventDefault();
event.stopPropagation();
return;
}
this.emit("tab-bar-click", { item: this });
};
/**
* Focus the inner button
*/
override focus(): void {
this.button?.focus();
}
/**
* Blur the inner button
*/
override blur(): void {
this.button?.blur();
}
}