SolalDR/data-gui

View on GitHub
src/components/group.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { html, property, customElement, css, query } from 'lit-element'
import { BaseGroup, BaseGroupConstructor } from '@/core/group'

/**
 * @category Constructor
 */
export interface GroupConstructor extends BaseGroupConstructor {
  opened?: boolean
}
@customElement('gui-group')
export class Group extends BaseGroup {
  /**
   * @ignore
   */
  @property() private computedClass: string = ''
  /**
   * @ignore
   */
  @property() private computedStyle: string = ''

  /**
   * @ignore
   */
  @query('.group-body') private groupElement: HTMLElement

  constructor({ name = null, opened = false }: GroupConstructor = {}) {
    super({ name })
    this.opened = opened
  }

  private onClick() {
    if (this.childrenControllers.length) {
      this.opened = !this.opened  
    }
  }

  /**
   * Create a new child group
   */
  group(descriptor: GroupConstructor) {
    const group = new Group(descriptor)
    group.addEventListener('collapse', event => {
      this.updateComputed()
    })
    this.childrenControllers.push(group)
    this.requestUpdateInternal()
    return group
  }

  private updateComputed() {
    this.computedClass = this.opened ? 'group group--open' : 'group'
    const height = this.groupElement
      ? this.groupElement.offsetHeight + 40
      : 1000
    this.computedStyle = `--group-height: ${height}px;`
  }

  /**
   * @ignore
   */
  render() {
    this.updateComputed()

    const collapseIcon = this.childrenControllers.length > 0 
      ? html`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" strokeWidth="5" strokeLinecap="square" strokeLinejoin="arcs"><path d="M18 15l-6-6-6 6" /></svg>` 
      : ''

    return html`
      <div class=${this.computedClass} style=${this.computedStyle}>
        <div class="group-header" @click=${this.onClick}>
          <div class="group-label">
            <span class="group-name">${this.name}</span>
            ${collapseIcon}
          </div>
          <slot></slot>
        </div>
        <div class="group-body">
          ${this.childrenControllers}
        </div>
      </div>
    `
  }

  /**
   * @ignore
   */
  public static styles = css`
    /*minify*/
    :host {
      margin-top: 20px;
      --group-height: 1000px;
      --duration: 0.5s;
    }
    .group-name {
      display: flex;
      flex-direction: column;
      justify-content: center;
      height: var(--item-height);
      font-size: 1em;
      letter-spacing: 1px;
      color: var(--color-primary);
      padding-left: 9px;
    }
    .group-header {
      margin: auto;
      display: flex;
      justify-content: space-between;
      border-bottom: 1px solid var(--color-bg-secondary);
    }
    .group-label {
      position: relative;
      display: flex;
    }
    .group-header svg {
      /* position: absolute; */
      /* right: 10px; */
      /* top: 50%; */
      transform: scaleY(-1);
      height: var(--item-height);
      width: 15px;
      transition: transform 0.4s ease;
      stroke: var(--color-primary);
      left: 5px;
      position: relative;
    }

    .group {
      max-height: var(--item-height);
      overflow: hidden;
    }

    .group .group-body {
      opacity: 0;
      transform: translateX(-5px);
      transition-duration: 0.6s;
      transition-property: transform, opacity;
      transition-delay: 0s;
      transition-timing-function: cubic-bezier(0, 0, 0, 1.03);
      border-left: 2px solid var(--color-primary);
    }

    .group--open {
      overflow: visible;
      max-height: none;
    }

    .group--open .group-body {
      opacity: 1;
      transform: scaleY(1);
      transition-delay: 100ms;
    }
    .group--open svg {
      transform: rotate(0deg);
    }
  `
}