import { Host, h } from '@stencil/core';
/**
 * Outputs a list of selectable options inside of a `<bs-dropdown>` component.
 * Use `<bs-dropdown-option>` as children components.
 *
 * CSS Transition triggers on mount (unless `enterControlled` is set
 * to `true`). See `bs-transition`
 * for more information on how transitions are handled internally.
 *
 * The parent container of this element should be `position: relative`. A
 * convenience class `.bs-dropdown-parent` exists which applies this style
 * (along with `display: inline-block`).
 *
 * Toggle element should have `aria-expanded = true` when the dropdown is
 * visible.
 *
 * Content of the dropdown should have an ID which matches the toggle element's
 * `aria-controls` attribute.
 */
export class BsDropdownWithOptions {
  constructor() {
    /** Track which option is focused */
    this.focusedIndex = -1;
    /** Track selected option */
    this.selectedIndex = -1;
    /** Track whether the dropdown is currently visible. */
    this.isVisible = false;
    this.toggleId = undefined;
    this.containerId = undefined;
    this.optionsId = undefined;
    this.width = undefined;
    this.top = false;
    this.center = false;
    this.label = undefined;
    this.selectedValue = undefined;
    this.enterControlled = false;
    this.enterTrigger = false;
    this.leaveTrigger = false;
    this.activeDescendantId = undefined;
  }
  /** Pass down initial props */
  componentDidLoad() {
    this.optionEls.forEach((el, index) => {
      el.setAttribute('index', String(index));
      // Stencil Vue wrappers have an issue with camelCase props currently
      // So, sending both attributes
      el.setAttribute('options-id', this.optionsId);
      el.setAttribute('optionsid', this.optionsId);
      // Set selected option
      const value = el.getAttribute('value');
      if (value === this.selectedValue) {
        this.selectedIndex = index;
        el.setAttribute('selected', 'true');
      }
    });
  }
  /** Get the dropdown option elements. */
  get optionEls() {
    if (!this._optionEls) {
      const els = this.optionsEl.querySelectorAll('bs-dropdown-option');
      this._optionEls = Array.from(els);
    }
    return this._optionEls;
  }
  /** Removes focus from the currently focused option, if applicable */
  removeCurrentFocus() {
    if (this.focusedIndex > -1) {
      this.optionEls[this.focusedIndex].removeAttribute('focused');
    }
  }
  /** Handles all applicable navigation key presses for dropdown accessibility */
  handleKeyDown(ev) {
    if (this.isVisible) {
      switch (ev.key) {
        case 'ArrowDown':
          this.traverse('down');
          ev.preventDefault();
          break;
        case 'ArrowUp':
          this.traverse('up');
          ev.preventDefault();
          break;
        case 'Home':
          this.traverse('home');
          ev.preventDefault();
          break;
        case 'End':
          this.traverse('end');
          ev.preventDefault();
          break;
        case 'Tab':
          // Prevent tab from doing anything while dropdown is open
          ev.preventDefault();
          break;
      }
    }
  }
  /** Traverses focus of the option list via keyboard navigation */
  traverse(direction) {
    let newIndex;
    switch (direction) {
      case 'up':
        newIndex = (this.focusedIndex - 1 < 0) ?
          this.optionEls.length - 1 :
          this.focusedIndex - 1;
        break;
      case 'down':
        newIndex = (this.focusedIndex + 1) % this.optionEls.length;
        break;
      case 'home':
        newIndex = 0;
        break;
      case 'end':
        newIndex = this.optionEls.length - 1;
        break;
    }
    this.removeCurrentFocus();
    this.optionEls[newIndex].setAttribute('focused', 'true');
    this.focusedIndex = newIndex;
  }
  /** Handles moving focus on mouse hover */
  handleMouseFocus(ev) {
    if (this.isVisible) {
      this.removeCurrentFocus();
      this.focusedIndex = ev.detail;
    }
  }
  /**
   * Handles selection by updating various internal states and emitting to
   * parent
   */
  handleSelect(ev) {
    this.activeDescendantId = ev.detail.id;
    this.selectedIndex = ev.detail.index;
    this.optselected.emit(ev.detail.value);
    this.removeCurrentFocus();
    this.close.emit();
  }
  /** Dropdown has fully rendered */
  handleEnterEnd() {
    this.isVisible = true;
    // Focus a selected item only after open animation plays
    if (this.selectedIndex > -1) {
      this.focusedIndex = this.selectedIndex;
      this.optionEls[this.focusedIndex].setAttribute('focused', 'true');
    }
    this.enterend.emit();
  }
  handleClose(ev) {
    ev.stopPropagation();
    this.close.emit();
  }
  render() {
    return (h(Host, null, h("bs-dropdown", { width: this.width, top: this.top, center: this.center, toggleId: this.toggleId, containerId: this.containerId, enterControlled: this.enterControlled, enterTrigger: this.enterTrigger, leaveTrigger: this.leaveTrigger, onClose: (e) => this.handleClose(e), onEnterend: () => this.handleEnterEnd(), onLeaveend: () => this.leaveend.emit() }, h("ul", { class: "bs-dropdown-options", role: "listbox", id: this.optionsId, "aria-label": this.label || undefined, "aria-activedescendant": this.activeDescendantId || undefined, ref: el => this.optionsEl = el }, h("slot", null)))));
  }
  static get is() { return "bs-dropdown-with-options"; }
  static get originalStyleUrls() {
    return {
      "$": ["bs-dropdown-with-options.scss"]
    };
  }
  static get styleUrls() {
    return {
      "$": ["bs-dropdown-with-options.css"]
    };
  }
  static get properties() {
    return {
      "toggleId": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": true,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "ex",
              "text": "basic-dropdown-toggle-id"
            }],
          "text": "Id of the toggle element. Will be used for positioning."
        },
        "attribute": "toggle-id",
        "reflect": false
      },
      "containerId": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": true,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Id of the container for the dropdown. Will be used for positioning. The\ncontainer may live anywhere on the DOM (in the case of teleporting the\ndropdown to a higher DOM level). This container must have\n`position: relative`."
        },
        "attribute": "container-id",
        "reflect": false
      },
      "optionsId": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": true,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "ex",
              "text": "basic-options-id"
            }],
          "text": "Id of the options list. This should also be set on the toggle element's\n`aria-controls` attribute and the label's `for` attribute."
        },
        "attribute": "options-id",
        "reflect": false
      },
      "width": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "'sm'|'md'|'lg'|'content'|'toggle'",
          "resolved": "\"content\" | \"lg\" | \"md\" | \"sm\" | \"toggle\"",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Desired width of the dropdown."
        },
        "attribute": "width",
        "reflect": false,
        "defaultValue": "undefined"
      },
      "top": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Whether or not the dropdown should be positioned above the toggle element."
        },
        "attribute": "top",
        "reflect": false,
        "defaultValue": "false"
      },
      "center": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Whether or not the dropdown should be horizontally centered relative to the\ntoggle element."
        },
        "attribute": "center",
        "reflect": false,
        "defaultValue": "false"
      },
      "label": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": true,
        "docs": {
          "tags": [],
          "text": "Optionally provide a label for the dropdown which `aria-label` will get\nset to. This is used as an alternative to setting the `for` attribute on a\nlabel element when there is no visible label."
        },
        "attribute": "label",
        "reflect": false
      },
      "selectedValue": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": true,
        "docs": {
          "tags": [],
          "text": "The option to be selected. This should match the desired option's\n`value` prop."
        },
        "attribute": "selected-value",
        "reflect": false
      },
      "enterControlled": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Whether or not the enter transition should be controlled manually. Useful\nwhen the enter transition needs to be triggered manually (e.g. \\`v-show\\`\nin Vue)."
        },
        "attribute": "enter-controlled",
        "reflect": false,
        "defaultValue": "false"
      },
      "enterTrigger": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Only used when \\`enterControlled\\` is \\`true\\`. When ready to play the\nenter transition, set this to \\`true\\`."
        },
        "attribute": "enter-trigger",
        "reflect": false,
        "defaultValue": "false"
      },
      "leaveTrigger": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "If desired, leave transition needs to be triggered manually. Set this to\n\\`true\\` when ready to fire leave transition."
        },
        "attribute": "leave-trigger",
        "reflect": false,
        "defaultValue": "false"
      }
    };
  }
  static get states() {
    return {
      "activeDescendantId": {}
    };
  }
  static get events() {
    return [{
        "method": "optselected",
        "name": "optselected",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emits the value of an option when selected."
        },
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        }
      }, {
        "method": "close",
        "name": "close",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emits when a close event has occurred. The consumer must handle\naccordingly (e.g. triggering leave transition, removing from DOM,\nre-focusing toggle, etc.)."
        },
        "complexType": {
          "original": "any",
          "resolved": "any",
          "references": {}
        }
      }, {
        "method": "enterend",
        "name": "enterend",
        "bubbles": false,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emits when the enter transition has ended. The element should be fully\nrendered by now and may be manipulated (e.g. setting focus)."
        },
        "complexType": {
          "original": "any",
          "resolved": "any",
          "references": {}
        }
      }, {
        "method": "leaveend",
        "name": "leaveend",
        "bubbles": false,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Only emits when \\`leaveTrigger\\` is toggled and the leave transition has\nended. The element can be fully removed from the DOM now."
        },
        "complexType": {
          "original": "any",
          "resolved": "any",
          "references": {}
        }
      }];
  }
  static get elementRef() { return "hostEl"; }
  static get listeners() {
    return [{
        "name": "keydown",
        "method": "handleKeyDown",
        "target": "window",
        "capture": false,
        "passive": false
      }, {
        "name": "optmouseenter",
        "method": "handleMouseFocus",
        "target": undefined,
        "capture": false,
        "passive": false
      }, {
        "name": "optselect",
        "method": "handleSelect",
        "target": undefined,
        "capture": false,
        "passive": false
      }];
  }
}
//# sourceMappingURL=bs-dropdown-with-options.js.map
