Newer
Older
iuav-ui / src / components / accordion / accordion.ts
/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/

import {LitElement, html, css, unsafeCSS} from 'lit';
import {customElement, property, query} from 'lit/decorators.js';
import { breakpoints } from '../../breakpoints';

@customElement('iu-accordion')

export class Accordion extends LitElement {
  static override styles = css`
  :host{
    --iu-comp-bg: transparent;
    --iu-comp-color: var(--iu-color-black);
    --iu-comp-bg-hover: var(--iu-color-black);
    --iu-comp-color-hover: var(--iu-color-white);
    --iu-comp-border-color: var(--iu-color-grey-200);
    --iu-comp-border-color-hover: var(--iu-color-grey-500);
  }
  :host:has(.accordion.is-open){
    border-bottom: 1px solid var(--iu-comp-border-color-hover);
  }
  .accordion{
    border-top: 1px solid var(--iu-comp-border-color);
    cursor: pointer;
    color: var(--iu-comp-color);
    background: var(--iu-comp-bg);
  }
  .accordion.is-open,
  .accordion.is-open button,
  :host(:hover), 
  :host(:hover) button{
    color: var(--iu-comp-color-hover);
    background: var(--iu-comp-bg-hover);
  }
  .accordion.is-open svg{
    transform: rotate(45deg);
  }
  button{
    color: var(--iu-comp-color);
    background: var(--iu-comp-bg);
    cursor: pointer;
    border: 0;
    position: relative;
    font: var(--iu-f-2);
    text-align: left;
    width: 100%;
    padding: 4px 0;
    display: flex;
    align-items: center;
    text-rendering: geometricPrecision;
  }
  .size-2 button{
    padding: 0;
    font: var(--iu-f-lg);
  }
  svg{
    transition: transform .2s ease;
  }
  .size-1 svg{
    width: 36px;
    height: 36px;
    margin-right: .625rem;
    margin-top: -2px;
  }
  .size-2 svg{
    width: 50px;
    height: 50px;
    margin-right: .9375rem;
    margin-top: -2px;
  }
  @media ${unsafeCSS(breakpoints.xl)} {
    button{
    }
  }
  .content{
    display: none;
    color: var(--iu-comp-color);
    padding-top: var(--iu-spacing-2);
    padding-bottom: var(--iu-spacing-6);
    padding-left: calc(3.125rem + .9375rem);
    padding-right: var(--iu-grid-gutter);
  }
  .content.is-open{
    display: block;
  }
  .content.columns-2{
    columns: 2;
    gap: var(--iu-grid-gutter);
  }
  ::slotted(*){
    --iu-comp-color: var(--iu-color-white);
    --iu-comp-border-color: var(--iu-color-grey-500);
    --iu-comp-bg-hover: var(--iu-color-white);
    --iu-comp-color-hover: var(--iu-color-black);
    margin-bottom: 0;
    color: var(--iu-color-white) !important;
  }
  `;
  
  @property() text : string = '';
  @property() index : number = 0;
  @property({ type: Boolean, reflect: true}) columns : boolean = false;
  @property({ type: Boolean, reflect: true}) isOpen : boolean = false;
  @property() controlled : boolean = false;
  @property({type: Number}) size = 1;
  
  @query('slot') slotElement!: HTMLSlotElement;
  
  toggleAccordion(){
    //grouped with other accordion
    if (this.controlled) {        
      this.dispatchEvent(new CustomEvent('iu-toggle-accordion', {
        detail: {index: this.index },
        bubbles: true,
        composed: true
      }))
    } else {
      //stand alone component
      this.isOpen = !this.isOpen;
    }
    this.classList.toggle('is-open');
  }
  
  // make sure that every component inside this has <iu-container> set 
  // to nested to disable padding left on the container itself
  private updateSlottedElements() {
    // Get all slotted elements
    const slottedElements = this.slotElement.assignedElements({ flatten: true });
    slottedElements.forEach((slot) => {
      if (slot instanceof HTMLElement) {
        // Set the `nested` attribute or property
        slot.setAttribute('nested', 'true');
      }
    });
  }
  
  override firstUpdated() {
    super.firstUpdated();
    this.updateSlottedElements();
  }
  
  override render() {
    return html`
      <div class="accordion size-${this.size} ${this.isOpen ? 'is-open' : ''}">
        <button aria-expanded="false" @click=${this.toggleAccordion}>
          <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><path fill-rule="evenodd" clip-rule="evenodd" d="M37.5 26h-25v-2h25v2Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M24 37.5v-25h2v25h-2Z" fill="currentColor"/></svg>
          ${this.text}
        </button>
        <div class="content ${this.isOpen ? 'is-open' : ''} ${this.columns ? 'columns-2' : ''}">
          <slot></slot>
        </div>
      </div>
    `;
  }
  
}

declare global {
  interface HTMLElementTagNameMap {
    'iu-accordion': Accordion;
  }
}