Newer
Older
iuav-ui / src / components / mobile-menu / mobile-menu-item.ts
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;
  }
}