import { LitElement, html, css } from 'lit'; import { customElement, property, queryAssignedNodes } from 'lit/decorators.js'; @customElement('iu-mobile-menu-item') export class MobileMenuItem extends LitElement { static override styles = css` :host{ display: block; } li{ display: block; width: 100%; } div{ display: flex; border-bottom: 1px solid var(--iu-color-black); } a{ font-weight: bold; width: calc(100% - 50px); display: flex; align-items: center; padding: 0 var(--iu-grid-gutter); border-right: 1px solid var(--iu-color-grey-200); height: 3.125rem; text-decoration: none; color: var(--iu-color-black); box-sizing: border-box; } button{ width: 50px; height: 50px; padding: 0; flex-shrink: 0; background: transparent; border: 0; cursor: pointer; & svg{ width: 50px; height: 50px; } } ul{ list-style-type: none; padding: 0; margin: 0; display: none; & li { & a{ padding-left: var(--iu-spacing-6); font-weight: normal; } } } .is-toggled{ & button{ transform: rotate(180deg); } & ul{ display: block; } } .is-nested{ & div{ border-color: var(--iu-color-grey-200); } & a{ padding-left: var(--iu-spacing-6); } } .is-last{ div{ } } `; @property() href?= ''; @property() text= ''; @property({ type: Array }) items: Array<{ text: string; href: string }> = []; private isToggled: boolean = false; // Track if the menu is expanded // Query slot content @queryAssignedNodes() private slottedElements?: NodeList; private hasSlotted: boolean = false; private handleSlotChange() { this.hasSlotted = !!(this.slottedElements && this.slottedElements.length > 0); this.requestUpdate(); // Ensures the component updates dynamically } private isNestedMenuItem() : boolean { const parent = this.parentElement; return parent?.tagName.toLowerCase() === 'iu-mobile-menu-item'; } private isLastSlottedItem(): boolean { if (!this.assignedSlot) return false; // Not in a slot, return false const assignedElements = this.assignedSlot.assignedNodes().filter(node => node.nodeType === Node.ELEMENT_NODE ) as HTMLElement[]; return assignedElements[assignedElements.length - 1] === this; } private toggleSubmenu(event: Event) { event.stopPropagation(); // Prevent click from bubbling up this.isToggled = !this.isToggled; this.requestUpdate(); } override render() { return html` <li class="${this.isToggled ? 'is-toggled' : ''} ${this.isNestedMenuItem() ? 'is-nested' : ''} ${this.isLastSlottedItem() ? 'is-last' : '' }" aria-expanded="${this.isToggled}" > ${this.items.length > 0 || this.hasSlotted ? html` <div> <a href="${this.href}">${this.text}</a> <button @click="${this.toggleSubmenu}"> <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><path fill-rule="evenodd" clip-rule="evenodd" d="m25 28.637 8.32-7.704 1.36 1.467L25 31.363 15.32 22.4l1.36-1.467L25 28.637Z" fill="#000"/></svg> </button> </div> `: html` <div> <a href="${this.href}">${this.text}</a> </div> `} <!-- Render <ul> only if there are sub-items or slot content --> ${this.items.length > 0 || this.hasSlotted ? html` <ul> ${this.items.map( (item) => html` <li> <div> <a href="${item.href}">${item.text}</a> </div> </li> ` )} <slot @slotchange="${this.handleSlotChange}"></slot> </ul> ` : html`<slot @slotchange="${this.handleSlotChange}"></slot>`} </li> `; } } declare global { interface HTMLElementTagNameMap { 'iu-mobile-menu-item': MobileMenuItem; } }