/** * Copyright Aquera Inc 2025 * * This source code is licensed under the BSD-3-Clause license found in the * LICENSE file in the root directory of this source tree. */ import { html, CSSResultArray, TemplateResult, PropertyValues } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { styles } from './nile-inline-sidebar-item.css'; import NileElement from '../internal/nile-element'; import { classMap } from 'lit/directives/class-map.js'; /** * Nile inline sidebar item. * * @tag nile-inlinesidebar-item * * @attr active - Marks the item as active (selected). * @attr disabled - Marks the item as disabled (non-interactive). * @attr href - Optional URL to navigate to when the item is clicked. * * @slot - Default slot for the item text or content. * * @fires nile-select - Emitted when the item is clicked (not disabled). * detail: { item: this, href?: string } */ @customElement('nile-inline-sidebar-item') export class NileInlineSidebarItem extends NileElement { @property({ type: Boolean, reflect: true }) active = false; @property({ type: Boolean, reflect: true }) disabled = false; @property({ type: String }) href?: string; @property({ type: Boolean, reflect: true }) tooltip = false; @property({ type: String, reflect: true, attribute: true }) variant?: 'minimal' | 'rich' = 'minimal'; /** Links this item to a `nile-inline-sidebar-panel` by matching its `name` property. */ @property({ type: String, reflect: true }) panel?: string; public static get styles(): CSSResultArray { return [styles]; } firstUpdated(changedProperties: PropertyValues) { super.firstUpdated(changedProperties); this.syncBodyAndHeaderStates(); } updated(changedProperties: PropertyValues) { super.updated(changedProperties); if (changedProperties.has('disabled') || changedProperties.has('active')) { this.syncBodyAndHeaderStates(); } } private syncBodyAndHeaderStates() { const elements = [ this.querySelector('[slot="body"]'), this.querySelector('[slot="header"]') ] as (HTMLElement | null)[]; const props = { disabled: this.disabled, active: this.active }; elements.forEach(el => { if (!el) return; Object.entries(props).forEach(([key, value]) => { if (key in el) { (el as any)[key] = value; } }); }); } connectedCallback() { super.connectedCallback(); if (!this.hasAttribute('tabindex')) { this.setAttribute('tabindex', this.disabled ? '-1' : '0'); } if (!this.hasAttribute('role')) { this.setAttribute('role', 'menuitem'); } this.addEventListener('keydown', this._handleHostKeyDown); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this._handleHostKeyDown); } private _handleHostKeyDown = (e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this._select(e); } }; private _select(e: Event) { if (this.disabled) { e.preventDefault(); e.stopPropagation(); return; } const parent = this.closest('nile-inline-sidebar'); if (parent) { const allItems = parent.querySelectorAll('nile-inline-sidebar-item'); allItems.forEach((item) => (item as NileInlineSidebarItem).active = item === this); } const text = this.textContent?.trim() || ''; this.dispatchEvent( new CustomEvent('nile-click', { detail: { item: this, href: this.href, text }, bubbles: true, composed: true, }), ); if (this.href) { window.location.href = this.href; } } private handleClick(e: Event) { this._select(e); } public render(): TemplateResult { return html` `; } } export default NileInlineSidebarItem; declare global { interface HTMLElementTagNameMap { 'nile-inline-sidebar-item': NileInlineSidebarItem; } }