import { Host, h } from '@stencil/core';
/**
 * Outputs a dropdown which is a design pattern that conserves screen space by
 * letting users display some content without flooding the user with extra
 * content on the page.
 *
 * This component uses `BsTransition` (see [usage examples](../?path=/docs/web-components-bs-transition--docs#usage-examples)).
 *
 * ### Accessibility Notes
 * Toggle element should have:
 * - `aria-expanded = true` when the dropdown is visible.
 * - `aria-controls` attribute which matches dropdown content's ID.
 */
export class BsDropdown {
  constructor() {
    this.isVisible = false;
    this.width = undefined;
    this.top = false;
    this.center = false;
    this.toggleId = undefined;
    this.containerId = undefined;
    this.helperText = undefined;
    this.helperDescription = undefined;
    this.enterControlled = false;
    this.enterTrigger = false;
    this.leaveTrigger = false;
  }
  componentDidLoad() {
    if (this.containerId) {
      this.containerEl = document.getElementById(this.containerId);
    }
    this.toggleEl = document.getElementById(this.toggleId);
    this.setPosition();
    this.setWidth();
  }
  componentDidUpdate() {
    this.setPosition();
    this.setWidth();
  }
  /**
   * Dynamically set position based on toggle element. Doing this rather than
   * relying on CSS since the dropdown's container could live anywhere on the
   * DOM (in the case of teleporting the dropdown to a higher DOM level).
   *
   * Use custom properties rather than explicit properties to allow for
   * us to ignore them on mobile.
   */
  setPosition() {
    const containerRect = this.containerEl.getBoundingClientRect();
    const toggleRect = this.toggleEl.getBoundingClientRect();
    const dropdownRect = this.dropdownDiv.getBoundingClientRect();
    // Calculate top/bottom position relative to target container
    if (this.top) {
      const bottom = containerRect.bottom - toggleRect.bottom;
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-top', 'auto');
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-bottom', `${bottom + toggleRect.height + 8}px`);
    }
    else {
      const top = toggleRect.top - containerRect.top;
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-top', `${top + toggleRect.height + 8}px`);
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-bottom', 'auto');
    }
    // Calculate left position relative to target container
    const left = toggleRect.left - containerRect.left;
    if (this.center) {
      const offset = (toggleRect.width - dropdownRect.width) / 2;
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-left', `${left + offset}px`);
    }
    else {
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-left', `${left}px`);
    }
  }
  /**
   * Special case when width is "toggle".
   * Dynamically set width based on toggle element. Doing this rather than
   * relying on CSS since the dropdown's container could live anywhere on the
   * DOM (in the case of teleporting the dropdown to a higher DOM level).
   */
  setWidth() {
    if (this.width === 'toggle') {
      const toggleRect = this.toggleEl.getBoundingClientRect();
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-width', `${toggleRect.width}px`);
    }
    else {
      this.dropdownDiv.style.setProperty('--dropdown-wscreen-width', '');
    }
  }
  /** Close the dropdown on Escape key. */
  handleEscape(ev) {
    if (this.isVisible && ev.key === 'Escape') {
      this.close.emit();
    }
  }
  /** Close the dropdown when clicking outside of it or the toggle element. */
  handleClickOutside(ev) {
    if (this.isVisible) {
      const toggleEl = document.getElementById(this.toggleId);
      if (!this.dropdownDiv.contains(ev.target) &&
        !toggleEl.contains(ev.target)) {
        this.close.emit();
      }
    }
  }
  handleEnterEnd() {
    this.isVisible = true;
    this.enterend.emit();
  }
  render() {
    // Don't set data-width in the special 'toggle' width case
    const width = this.width === 'toggle' ? undefined : this.width;
    const dropdownJsx = (h("bs-transition", { enterAttribute: "data-shown", enterDuration: 75, enterControlled: this.enterControlled, enterTrigger: this.enterTrigger, leaveTrigger: this.leaveTrigger, onEnterend: () => this.handleEnterEnd(), onLeaveend: () => this.leaveend.emit() }, h("div", { class: "bs-dropdown", "data-width": width, ref: (el) => this.dropdownDiv = el }, this.helperText && (h("header", null, h("h3", null, this.helperText), this.helperDescription && h("p", null, this.helperDescription))), h("slot", null))));
    return (h(Host, null, this.containerId && dropdownJsx, !this.containerId && (h("div", { style: { position: 'relative' }, ref: (el) => this.containerEl = el }, dropdownJsx))));
  }
  static get is() { return "bs-dropdown"; }
  static get originalStyleUrls() {
    return {
      "$": ["bs-dropdown.scss"]
    };
  }
  static get styleUrls() {
    return {
      "$": ["bs-dropdown.css"]
    };
  }
  static get properties() {
    return {
      "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"
      },
      "toggleId": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": true,
        "optional": false,
        "docs": {
          "tags": [],
          "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": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Optional ID of the container for the dropdown. Will be used as the base for\npositioning. The container may live anywhere on the DOM (in the case of\nteleporting the dropdown to a higher DOM level). This container must have\n`position: relative`. If not given, the dropdown uses its own baked-in\nrelative container."
        },
        "attribute": "container-id",
        "reflect": false,
        "defaultValue": "undefined"
      },
      "helperText": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": true,
        "docs": {
          "tags": [],
          "text": "Helper text will be displayed in the header on mobile screens only."
        },
        "attribute": "helper-text",
        "reflect": false
      },
      "helperDescription": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": true,
        "docs": {
          "tags": [],
          "text": "Helper desription will be displayed in the header below the main helper text on mobile screens only."
        },
        "attribute": "helper-description",
        "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 events() {
    return [{
        "method": "close",
        "name": "close",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emits when a close event has occurred. These events include Escape key\nbeing pressed or a user clicking outside of the dropdown area. The consumer\nmust handle accordingly (e.g. hiding the dropdown, setting `tabindex = -1`\non elements, 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": "handleEscape",
        "target": "window",
        "capture": false,
        "passive": false
      }, {
        "name": "click",
        "method": "handleClickOutside",
        "target": "window",
        "capture": false,
        "passive": false
      }];
  }
}
//# sourceMappingURL=bs-dropdown.js.map
