简体   繁体   中英

Toggle all stencil.js components of the same kind when clicked

I am creating a toolbar, that will be used for text editing.

Each item in the toolbar is being created with this code:

import {Component, h, Prop, Event, EventEmitter} from '@stencil/core';

@Component({
  tag: 'spa-toolbar-item',
  styleUrl: 'spa-toolbar-item.scss',
})

export class spaToolbarItem {
  @Prop({reflectToAttr: true, mutable: true}) toggle: boolean = false;
  @Prop({reflectToAttr: true}) type: string;
  @Event() onToggle: EventEmitter;

  toggleComponent(): void {
    this.toggle = !this.toggle;
    this.onToggle.emit({visible: this.toggle});
  }

  render() {
    return [
      <button onClick={() => this.toggleComponent()}>
        <slot/>
      </button>,
      <div class={this.toggle ? 'is-active' : null}>
        {this.type == 'font'
          ? <div class={this.type}>
            <span class="arial">Arial</span>
            <span class="georgia">Georgia</span>
            <span class="palatino">Palatino</span>
            <span class="tahoma">Tahoma</span>
            <span class="times">Times New Roman</span>
            <span class="helvetica">Helvetica</span>
            <span class="courier">Courier New</span>
            <span class="lucida">Lucida Sans Typewriter</span>
            <span class="verdana">Verdana</span>
          </div>
          : null}
      </div>
    ]
  }
}

This works as expected, however, I want all other dropdown menus to close when one item is clicked, so basically 'this.toggle' needs to be applied for all non-active dropdowns. How do I do that?

So the idea is to apply addEventListener on your document and also on the component itself (the Host - dropdown). For that you can use a lifecyclehook like componentdidload .

import {Component, h, Prop, Event, EventEmitter} from '@stencil/core';

@Component({
  tag: 'spa-toolbar-item',
  styleUrl: 'spa-toolbar-item.scss',
})

export class spaToolbarItem {
  @Prop({reflectToAttr: true, mutable: true}) toggle: boolean = false;
  @Prop({reflectToAttr: true}) type: string;
  @Event() onToggle: EventEmitter;

private hostElement;
private buttonElement;
  closeDropdown(){
    this.toggle = false;
  }
  toggleComponent(): void {
    this.toggle = !this.toggle;
    this.onToggle.emit({visible: this.toggle});
  }

render() {
 return 
 <Host ref={(el) => this.hostElement = el}>
   //everything you had before in the return value
 </Host>
}

 componentDidLoad(){
  document.addEventListener("click", ()=>{this.clickManager("document");}, true)
  this.hostElement.addEventListener("click", ()=>{this.clickManager("dropdown");}, true)
 }

 private clickManager(identifier){
   if(identifier == "dropdown"){
     //the clicked dropdown -> active dropdown
     this.toggleComponent();
     return;
   }
   //all non active Dropdowns
   this.closeDropdown();
 }

}

As you can see we have two events that are calling the clickManager. The first one is the document and the second one is the dropdown you clicked on. Of course you have to remove the onclick event on the button but this code doesn't work as expected. Now you can decide what happens if you click in the dropdown and what happens with all the dropdowns that are non active.

That is the basic concept feel free to adapt and change if required.

Thanks Christian, I ended up using this solution underneath.

import {Component, h, Prop, Host, Element} from '@stencil/core';

@Component({
  tag: 'spa-toolbar-item',
  styleUrl: 'spa-toolbar-item.scss',
})

export class spaToolbarItem {
  @Prop({reflectToAttr: true, mutable: true}) isOpen: string = 'false';
  @Prop({reflectToAttr: true}) type: string;
  @Element() el: HTMLElement;

  openDropdown() {
    function closeDropdown() {
      let dropdowns = document.querySelectorAll('spa-toolbar-item[is-open]');
      dropdowns.forEach(item => {
        item.setAttribute('is-open', 'false')
      });
    }

    if (this.el.getAttribute('is-open') == 'true') {
      closeDropdown();
      return
    }

    if (this.el.getAttribute('is-open') == 'false') {
      closeDropdown();
      this.el.setAttribute('is-open', 'true')
    }
  }

  render() {
    return (
      <Host>
        <button onClick={this.openDropdown.bind(this)}>
          <slot/>
        </button>
        <div class={this.type}>
          {this.type == 'font'
            ? <div>
              <span class="arial">Arial</span>
              <span class="georgia">Georgia</span>
              <span class="palatino">Palatino</span>
              <span class="tahoma">Tahoma</span>
              <span class="times">Times New Roman</span>
              <span class="helvetica">Helvetica</span>
              <span class="courier">Courier New</span>
              <span class="lucida">Lucida Sans Typewriter</span>
              <span class="verdana">Verdana</span>
            </div>
            : null}
        </div>
      </Host>
    )
  }
}

what you can do with pure javascript is, iterate throught all the elements with the class is-active, and set the properties of toggle of each one to 0 (or whatever the way you're doing it)

example:

new Array(document.getElementsByClassName('is-active')).forEach(function(e){/*Set toggle property to 0*/});

what this do is, search for all the elements with the is-active ,iterate throught them, and set a function, in this function the parameter e is equal to the current element, you can set your property there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM