Is it possible to make React-like compound components in lit 2.0?

680 views Asked by At

I tried to build compound components with Lit 2.0 but passing data to slots as attributes seems impossible.

  <my-accordion>
    <my-accordion-title>Title</my-accordion-title>
    <my-accordion-content>Content</my-accordion-content>
  </my-accordion

How I can pass "extended" propery to custom elements slots?

Here is my custom elements:

@customElement("my-accordion")
export class MyAccordion extends LitElement {

  @property()
  extended: boolean = false;

  toggleExtend(){
     this.extended = !this.extended
  }

  render() {
    return html`
      <div @click=${this.toggleExtend}>
        <slot .extended=${this.extended}></slot>
      </div>
    `;
  }
}


@customElement("my-accordion-title")
export class MyAccordionTitle extends LitElement {
  // want to access parent node extended property here
  render() {
    return html`
      <div>
        <slot></slot>
      </div>
    `;
  }
}
1

There are 1 answers

2
Benny Powers On BEST ANSWER

To assign the extended property to slotted children, get the slot element's assignedElements() array.

@customElement("my-accordion")
export class MyAccordion extends LitElement {

  @property()
  extended: boolean = false;

  toggleExtend(){
     this.extended = !this.extended
  }

  updated(changed: PropertyValues<this>) {
    if (changed.has('extended'))
      this.extendedChanged()
  }

  extendedChanged() {
    for (const child of this.slot.assignedElements()) {
      if (child instanceof MyAccordionTitle)
        child.extended = this.extended;
    }
  }

  @query('slot') slot: HTMLSlotElement | null;
  
  render() {
    return html`
      <div @click=${this.toggleExtend}>
        <slot></slot>
      </div>
    `;
  }
}


@customElement("my-accordion-title")
export class MyAccordionTitle extends LitElement {
  // want to access parent node extended property here
  render() {
    return html`
      <div>
        <slot></slot>
      </div>
    `;
  }
}

NB: When assigning click listeners to <div> and other non-interactive elements, there are many accessibility issues involved. It's usually recommended therefore to use a <button> element, or in your case maybe a <details> and <summary>