import { Component, Element, Host, Prop, Watch, h } from '@stencil/core';
import { arrow, computePosition } from '@floating-ui/dom';
import { guid, queryElementRoots } from '../../utils';
/** @slot slot for tooltip text */
export class Tooltip {
  constructor() {
    /** Determines Tooltip visibility. */
    // eslint-disable-next-line @stencil/strict-mutable
    this.open = false;
    /** Sets tooltip placement in relation to reference element. */
    this.placement = 'top';
    //--------------------------------------------------------------------------
    //
    //  Private Methods
    //
    //--------------------------------------------------------------------------
    this.assignEventListeners = (id, add = true) => {
      Object.entries({
        blur: this.hide,
        focus: this.show,
        mouseenter: this.show,
        mouseleave: this.hide,
      }).forEach(([event, listener]) => {
        this.getAttachToElement(id)[`${add ? 'add' : 'remove'}EventListener`](event, listener);
      });
      Object.entries({
        mouseenter: this.show,
        mouseleave: this.hide,
      }).forEach(([event, listener]) => {
        this.host[`${add ? 'add' : 'remove'}EventListener`](event, listener);
      });
      document.body[`${add ? 'add' : 'remove'}EventListener`]('keydown', this.onKeyDown);
    };
    this.attachNub = () => {
      this.nub = this.host.shadowRoot.querySelector('span.nub');
      if (!this.nub) {
        this.nub = document.createElement('span');
        this.nub.classList.add('nub');
        this.host.shadowRoot.querySelector('.inner-container').appendChild(this.nub);
      }
    };
    this.getAttachToElement = (id) => (id && queryElementRoots(this.host, `#${id}`)) || this.host.parentElement;
    this.hide = () => {
      this.open = false;
    };
    this.onKeyDown = (event) => {
      if (event.key === 'Escape')
        this.open = false;
    };
    this.show = () => {
      this.updateTooltipPosition();
      this.open = true;
    };
    this.updateAriaDescribedBy = (id, add = true) => {
      const attachToElement = this.getAttachToElement(id);
      const ariaDescribedBy = attachToElement.getAttribute('aria-describedBy');
      add
        ? !(ariaDescribedBy === null || ariaDescribedBy === void 0 ? void 0 : ariaDescribedBy.includes(this.host.id)) &&
          attachToElement.setAttribute('aria-describedby', `${ariaDescribedBy} ${this.host.id}`)
        : attachToElement.setAttribute('aria-describedBy', `${ariaDescribedBy.split(this.host.id)[0]}`);
    };
    this.updateTooltipPosition = () => {
      const attachToElement = this.getAttachToElement(this.for);
      if (attachToElement) {
        this.attachNub();
        const computePositionOptions = {
          middleware: [arrow({ element: this.nub })],
          placement: this.placement,
        };
        computePosition(attachToElement, this.host, computePositionOptions).then(({ middlewareData, x, y }) => {
          const { x: arrowX, y: arrowY } = middlewareData.arrow;
          const staticSide = {
            bottom: 'top',
            left: 'right',
            right: 'left',
            top: 'bottom',
          }[this.placement.split('-')[0]];
          Object.assign(this.nub.style, {
            bottom: '',
            left: arrowX !== undefined ? `${arrowX}px` : '',
            right: '',
            top: arrowY !== undefined ? `${arrowY}px` : '',
            [staticSide]: '-.25rem',
          });
          Object.assign(this.host.style, {
            left: `${x}px`,
            top: `${y}px`,
          });
        });
      }
    };
  }
  /**
   * TODO: expand placement to include additional options from floating-ui:
   *       i.e. import { Placement } from '@floating-ui/core/src/types';
   */
  //--------------------------------------------------------------------------
  //
  //  Prop Watchers
  //
  //--------------------------------------------------------------------------
  forChanged(newValue, oldValue) {
    /** Remove event listeners on last attached element and add them to newly referenced element */
    this.assignEventListeners(newValue);
    this.assignEventListeners(oldValue, false);
    this.updateAriaDescribedBy(newValue);
    this.updateAriaDescribedBy(newValue, false);
  }
  //--------------------------------------------------------------------------
  //
  //  Lifecycle Methods
  //
  //--------------------------------------------------------------------------
  componentDidRender() {
    if (this.open)
      // Using setTimeout to allow Tooltip to render visibly and hopefully final
      // layout painting of control element before trying to update its position
      setTimeout(() => {
        this.updateTooltipPosition();
      }, 500);
  }
  connectedCallback() {
    this.assignEventListeners(this.for);
    this.updateAriaDescribedBy(this.for);
  }
  disconnectedCallback() {
    this.assignEventListeners(this.for, false);
  }
  render() {
    return (h(Host, { id: this.host.id || `abds-tooltip-${guid()}`, role: "tooltip" },
      h("div", { class: "inner-container" },
        h("slot", null))));
  }
  static get is() { return "abds-tooltip"; }
  static get encapsulation() { return "shadow"; }
  static get originalStyleUrls() { return {
    "$": ["tooltip.css"]
  }; }
  static get styleUrls() { return {
    "$": ["tooltip.css"]
  }; }
  static get properties() { return {
    "for": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "ID selector to attach tooltip to. If unset Tooltip will try to attach to parent element."
      },
      "attribute": "for",
      "reflect": true
    },
    "open": {
      "type": "boolean",
      "mutable": true,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Determines Tooltip visibility."
      },
      "attribute": "open",
      "reflect": true,
      "defaultValue": "false"
    },
    "placement": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "BasePlacement",
        "resolved": "\"bottom\" | \"left\" | \"right\" | \"top\"",
        "references": {
          "BasePlacement": {
            "location": "import",
            "path": "@floating-ui/core/src/types"
          }
        }
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Sets tooltip placement in relation to reference element."
      },
      "attribute": "placement",
      "reflect": true,
      "defaultValue": "'top'"
    }
  }; }
  static get elementRef() { return "host"; }
  static get watchers() { return [{
      "propName": "for",
      "methodName": "forChanged"
    }]; }
}
//# sourceMappingURL=tooltip.js.map