'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

const index = require('./index-7f52f1a2.js');

const isEnterKey = (event) => event.key === 'Enter';
const isEscapeKey = (event) => event.key === 'Escape';
const isArrowDownKey = (event) => event.key === 'ArrowDown' || event.key === 'Down';
const isArrowUpKey = (event) => event.key === 'ArrowUp' || event.key === 'Up';

const balSelectCss = "@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.sc-bal-select-h{display:block}.sc-bal-select-h{display:block}.is-turned-up.sc-bal-select{rotate:180deg}.multiple-typeahead.sc-bal-select{margin:10px 20px 13px 20px}";

const Select = class {
  constructor(hostRef) {
    index.registerInstance(this, hostRef);
    this.balChange = index.createEvent(this, "balChange", 7);
    this.balInput = index.createEvent(this, "balInput", 7);
    this.balBlur = index.createEvent(this, "balBlur", 7);
    this.balFocus = index.createEvent(this, "balFocus", 7);
    this.balClick = index.createEvent(this, "balClick", 7);
    this.balKeyPress = index.createEvent(this, "balKeyPress", 7);
    this.balCancel = index.createEvent(this, "balCancel", 7);
    this.isDropdownOpen = false;
    this.labelToScrollTo = '';
    this.focusIndex = 0;
    /**
     * If `true` multiple option can be selected
     */
    this.multiple = false;
    /**
     * If `true` the filtering of the options is done outside of the component.
     */
    this.noFilter = false;
    /**
     * The tabindex of the control.
     */
    this.balTabindex = 0;
    /**
     * If `true` the component uses the whole width.
     */
    this.expanded = false;
    /**
     * Set this to `true` when the component is placed on a dark background.
     */
    this.inverted = false;
    /**
     * If `true` the component is diabled.
     */
    this.disabled = false;
    /**
     * If `true` the user can search by typing into the input field.
     */
    this.typeahead = false;
    /**
     * If `true` the component shows a loading spinner and sets the input to readonly.
     */
    this.loading = false;
    /**
     * Defines the placeholder of the input element.
     */
    this.placeholder = '';
    /**
     * Defines the placeholder of the input filter element.
     */
    this.filterPlaceholder = '';
    /**
     * Defines the height of the dropdown list.
     */
    this.scrollable = 250;
    /**
     * Selected option value.
     */
    this.value = [];
  }
  /**
   * Opens the dropdown
   */
  async open() {
    if (this.disabled) {
      return undefined;
    }
    this.dropdownElement.open();
  }
  /**
   * Closes the dropdown
   */
  async close() {
    if (this.disabled) {
      return undefined;
    }
    this.dropdownElement.close();
  }
  /**
   * Selects an option
   */
  async select(option) {
    var _a;
    this.focusIndex = this.childOptions.findIndex(el => el.value === option.value);
    if (this.multiple) {
      if (this.value.includes(option.value)) {
        for (var i = 0; i < this.value.length; i++) {
          if (this.value[i] === option.value) {
            this.value.splice(i, 1);
          }
        }
      }
      else {
        this.value = [...this.value, option.value];
      }
      this.inputElement.value = this.childOptions
        .filter(option => this.value.includes(option.value))
        .map(option => option.label)
        .join(', ');
    }
    else {
      this.value = [option.value];
      this.inputElement.value = option === null || option === void 0 ? void 0 : option.label;
      await ((_a = this.dropdownElement) === null || _a === void 0 ? void 0 : _a.close());
    }
    this.balChange.emit(this.value);
    this.updateOptionProps();
  }
  /**
   * Sets the value to null and resets the value of the input.
   */
  async clear() {
    this.value = [];
    this.inputElement.value = '';
    this.focusIndex = 0;
    this.clearFocus();
    this.updateOptionProps();
  }
  /**
   * Sets the focus on the input element
   */
  async setFocus() {
    if (this.inputElement) {
      this.inputElement.focus();
    }
  }
  /**
   * *Internal* - Used to update option changes
   */
  async sync() {
    this.updateOptionProps();
  }
  keyDownHandler(event) {
    if (this.isDropdownOpen && (isArrowDownKey(event) || isArrowUpKey(event))) {
      event.stopPropagation();
      event.preventDefault();
    }
  }
  componentWillLoad() {
    this.updateOptionProps();
  }
  valueWatcher() {
    this.updateOptionProps();
  }
  get childOptions() {
    return Array.from(this.element.querySelectorAll('bal-select-option'));
  }
  get hasChildren() {
    return this.childOptions.length > 0;
  }
  async onInputClick(event) {
    var _a;
    if (this.disabled) {
      return undefined;
    }
    this.balClick.emit(event);
    if (!this.typeahead || this.multiple) {
      await ((_a = this.dropdownElement) === null || _a === void 0 ? void 0 : _a.toggle());
    }
    if (this.typeahead && this.multiple && this.isDropdownOpen) {
      setTimeout(() => this.inputFilterElement.focus(), 100);
    }
  }
  onDropdownChange(event) {
    this.isDropdownOpen = event.detail;
    this.updateOptionProps();
    event.stopPropagation();
  }
  onInput(event) {
    const inputValue = event.target.value;
    this.value = [];
    this.focusIndex = 0;
    this.updateOptionProps();
    if (this.typeahead && !this.multiple && inputValue.length === 0) {
      this.dropdownElement.close();
    }
    if (this.typeahead && inputValue.length > 0) {
      this.dropdownElement.open();
    }
    event.preventDefault();
    event.stopPropagation();
    this.balChange.emit(this.value);
    this.balInput.emit(inputValue);
  }
  onKeyUp(event) {
    if (isEscapeKey(event)) {
      this.cancel();
    }
    if (this.hasChildren) {
      if (this.isDropdownOpen && (isArrowDownKey(event) || isArrowUpKey(event))) {
        event.preventDefault();
        event.stopPropagation();
        this.navigateWithArrowKey(event);
      }
    }
  }
  onKeyPress(event) {
    this.balKeyPress.emit(event);
    if (!this.typeahead && event.key.length === 1) {
      this.focusOptionByLabel(event.key);
    }
    if (isEnterKey(event)) {
      if (!this.isDropdownOpen) {
        this.open();
      }
      else {
        this.selectFocused();
      }
    }
  }
  get inputElementValue() {
    return this.inputElement ? this.inputElement.value : '';
  }
  get inputFilterElementValue() {
    return this.inputFilterElement ? this.inputFilterElement.value : '';
  }
  async updateOptionProps() {
    const inputValue = this.multiple && this.typeahead ? this.inputFilterElementValue : this.inputElementValue;
    this.clearFocus();
    this.childOptions.forEach((option, index) => {
      if (!this.noFilter && this.typeahead) {
        const didMatch = this.compareForFilter(`${option.label}` || '', `${inputValue}`);
        option.setAttribute('hidden', `${!didMatch}`);
      }
      else {
        option.setAttribute('hidden', `${!this.isDropdownOpen}`);
      }
      const isSelected = !!this.value && this.value.includes(option.value);
      option.setAttribute('selected', `${isSelected}`);
      const isFocused = this.focusIndex === index;
      option.setAttribute('focused', `${isFocused}`);
    });
    this.moveFocus();
  }
  async moveFocus() {
    const focusedElement = this.childOptions.find(el => el.focused);
    if (focusedElement && this.dropdownElement) {
      const dropdownContentElement = await this.dropdownElement.getContentElement();
      // up
      const topOfOption = focusedElement.offsetTop;
      const topOfDropdownContent = dropdownContentElement.scrollTop;
      if (topOfOption < topOfDropdownContent) {
        dropdownContentElement.scrollTop = topOfOption;
      }
      // down
      const bottomOfOption = focusedElement.offsetTop + focusedElement.clientHeight;
      const bottomOfDropdownContent = dropdownContentElement.scrollTop + dropdownContentElement.clientHeight;
      if (bottomOfOption > bottomOfDropdownContent) {
        dropdownContentElement.scrollTop = dropdownContentElement.scrollTop + focusedElement.clientHeight;
      }
    }
  }
  compareForFilter(text, input) {
    text = text.toLocaleLowerCase();
    input = input.toLocaleLowerCase();
    return text.indexOf(input) >= 0;
  }
  startsWithForFilter(text, input) {
    text = text.toLocaleLowerCase();
    input = input.toLocaleLowerCase();
    return text.startsWith(input);
  }
  navigateWithArrowKey(event) {
    const indexOfFocusedElement = this.childOptions.findIndex(el => el.focused);
    if (indexOfFocusedElement < 0) {
      this.childOptions[0].focused = true;
    }
    else {
      if (isArrowDownKey(event)) {
        this.navigateOneOptionDown(indexOfFocusedElement);
      }
      else {
        this.navigateOneOptionUp(indexOfFocusedElement);
      }
    }
  }
  clearFocus() {
    this.childOptions.forEach(el => (el.focused = false));
  }
  nextOptionIndex(index, lastIndex) {
    if (index >= lastIndex) {
      return lastIndex;
    }
    const nextOption = this.childOptions[index];
    if (nextOption.hidden) {
      return this.nextOptionIndex(index + 1, lastIndex);
    }
    else {
      return index;
    }
  }
  previousOptionIndex(index) {
    if (index <= 0) {
      return 0;
    }
    const previousOption = this.childOptions[index];
    if (previousOption.hidden) {
      return this.previousOptionIndex(index - 1);
    }
    else {
      return index;
    }
  }
  navigateOneOptionDown(indexOfFocusedElement) {
    const lastIndex = this.childOptions.length - 1;
    const nextIndex = this.nextOptionIndex(indexOfFocusedElement + 1, lastIndex);
    this.focusIndex = nextIndex <= lastIndex ? nextIndex : indexOfFocusedElement;
    this.updateOptionProps();
  }
  navigateOneOptionUp(indexOfFocusedElement) {
    const nextIndex = this.previousOptionIndex(indexOfFocusedElement - 1);
    this.focusIndex = nextIndex >= 0 ? nextIndex : indexOfFocusedElement;
    this.updateOptionProps();
  }
  async cancel() {
    this.balCancel.emit();
    this.close();
    this.labelToScrollTo = '';
    this.unfocusAllOptions();
    await this.scrollTo(0);
  }
  async selectFocused() {
    const focusedElement = this.childOptions.find(o => o.focused);
    if (focusedElement) {
      const option = await focusedElement.getOption();
      this.select(option);
    }
  }
  focusOptionByLabel(key) {
    this.labelToScrollTo = this.labelToScrollTo + key;
    clearTimeout(this.clearScrollToValue);
    this.clearScrollToValue = setTimeout(() => {
      this.scrollToLabel(this.labelToScrollTo);
    }, 600);
  }
  async scrollToLabel(label) {
    const optionElement = this.childOptions.find(o => this.startsWithForFilter(o.label, label));
    if (optionElement) {
      this.focusOptionElement(optionElement);
      await this.scrollTo(optionElement.offsetTop);
    }
    this.labelToScrollTo = '';
  }
  async scrollTo(scrollTop) {
    const dropdownContentElement = await this.dropdownElement.getContentElement();
    dropdownContentElement.scrollTop = scrollTop;
  }
  unfocusAllOptions() {
    this.childOptions.find(o => (o.focused = false));
  }
  focusOptionElement(optionElement) {
    this.unfocusAllOptions();
    optionElement.focused = true;
  }
  render() {
    return (index.h(index.Host, { role: "listbox" }, index.h("bal-dropdown", { expanded: this.expanded, scrollable: this.scrollable, onBalCollapse: e => this.onDropdownChange(e), ref: el => (this.dropdownElement = el) }, this.renderControl(), this.renderFilter(), index.h("slot", null))));
  }
  renderControl() {
    return (index.h("div", { class: "control has-icons-right", slot: "trigger" }, this.renderInput(), index.h("bal-icon", { class: { 'is-hidden': this.loading }, size: "medium", "is-right": true, turn: !this.loading && !this.typeahead && this.isDropdownOpen, color: this.inverted ? 'white' : 'blue', name: this.typeahead && !this.multiple ? 'search' : 'caret-down' })));
  }
  renderInput() {
    return (index.h("input", { class: {
        'input': true,
        'clickable': true,
        'is-inverted': this.inverted,
      }, readonly: !(this.typeahead && !this.multiple), autoComplete: "off", disabled: this.disabled, placeholder: this.placeholder, tabindex: this.balTabindex, onInput: e => this.onInput(e), onClick: e => this.onInputClick(e), onKeyPress: e => this.onKeyPress(e), onKeyUp: e => this.onKeyUp(e), onBlur: e => this.balBlur.emit(e), onFocus: e => this.balFocus.emit(e), ref: el => (this.inputElement = el) }));
  }
  renderFilter() {
    if (!this.multiple || !this.typeahead) {
      return '';
    }
    return (index.h("div", { class: "multiple-typeahead control has-icons-right" }, index.h("input", { class: "input", autoComplete: "off", placeholder: this.filterPlaceholder, onInput: e => this.onInput(e), onKeyPress: e => this.onKeyPress(e), onKeyUp: e => this.onKeyUp(e), onBlur: e => this.balBlur.emit(e), onFocus: e => this.balFocus.emit(e), ref: el => (this.inputFilterElement = el) }), index.h("bal-icon", { class: { 'is-hidden': this.loading }, size: "medium", "is-right": true, turn: !this.loading && !this.typeahead && this.isDropdownOpen, color: "blue", name: "search" })));
  }
  get element() { return index.getElement(this); }
  static get watchers() { return {
    "value": ["valueWatcher"]
  }; }
};
Select.style = balSelectCss;

exports.bal_select = Select;
