import { Component, Element, Host, Listen, Prop, State, Watch, h } from '@stencil/core';
import { clearAllBodyScrollLocks, disableBodyScroll, enableBodyScroll, nextTick } from '../../utils';
import { getFocusableChildren } from '../../utils/get-focusable-children';
var FocusCycleDirection;
(function (FocusCycleDirection) {
  FocusCycleDirection[FocusCycleDirection["Forward"] = 0] = "Forward";
  FocusCycleDirection[FocusCycleDirection["Backward"] = 1] = "Backward";
})(FocusCycleDirection || (FocusCycleDirection = {}));
/**
 * @slot modal-body - Modifies modal-body slot content.
 * @slot modal-footer - Modifies modal-footer slot content.
 */
export class Modal {
  constructor() {
    /** Sets open/closed state. */
    // eslint-disable-next-line @stencil/strict-mutable
    this.open = false;
    /** Sets header text color. */
    this.palette = 'basic';
    this.focusCycleDirection = FocusCycleDirection.Forward; // fwd or back
    //--------------------------------------------------------------------------
    //
    //  Private Methods
    //
    //--------------------------------------------------------------------------
    this.closeModal = () => {
      this.open = false;
      this.returnFocusEl && this.returnFocus();
    };
    this.focusNextFocusableElement = async () => {
      var _a, _b;
      await nextTick();
      if (this.focusCycleDirection === FocusCycleDirection.Forward) {
        (_a = this.closeButtonEl) === null || _a === void 0 ? void 0 : _a.focus();
      }
      else {
        const focusableElements = [this.closeButtonEl, ...getFocusableChildren(this.host)];
        (_b = focusableElements[focusableElements.length - 1]) === null || _b === void 0 ? void 0 : _b.focus();
      }
    };
    this.observeBottomIntersection = async (entries) => {
      for (const entry of entries) {
        this.isBodyBottomOverflown = !entry.isIntersecting;
      }
    };
    this.observeTopIntersection = async (entries) => {
      for (const entry of entries) {
        this.isBodyTopOverflown = !entry.isIntersecting;
      }
    };
    this.returnFocus = () => {
      if (typeof this.returnFocusEl.setFocus !== 'function' || typeof this.returnFocusEl.focus !== 'function')
        return;
      this.returnFocusEl.setFocus() || this.returnFocusEl.focus();
    };
    this.wrapFocus = () => {
      this.focusGateStartEl.focus();
    };
  }
  //--------------------------------------------------------------------------
  //
  //  Prop Watchers
  //
  //--------------------------------------------------------------------------
  handleOpenStateChange(newOpen, oldOpen) {
    if (newOpen === true) {
      disableBodyScroll(this.host);
      this.returnFocusEl = document.activeElement;
    }
    if (newOpen === false && oldOpen !== undefined) {
      enableBodyScroll(this.host);
    }
  }
  getSlotByName(slotName) {
    return [...this.host.children].filter((slot) => slot.getAttribute('slot') === slotName)[0] || undefined;
  }
  initIntersectionObserver() {
    if (!this.modalBodyEl)
      this.modalBodyEl = this.getSlotByName('modal-body');
    /*
    Using an intersection observer rather than scroll event for performance reasons:
    see https://css-tricks.com/an-explanation-of-how-the-intersection-observer-watches/#aa-performance-intersection-observer-versus-scroll-events

    Threshold can take a number or an array of numbers; it represents the visible percentage of the target element relative to the root.
    The IntersectionObserver reports each time a threshold number is crossed.
    https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#thresholds
    */
    const threshold = [0, 0.5, 1];
    if (this.modalBodyEl) {
      this.bottomObserver = new IntersectionObserver(this.observeBottomIntersection, {
        root: this.modalBodyWrapperEl,
        threshold,
      });
      this.topObserver = new IntersectionObserver(this.observeTopIntersection, {
        root: this.modalBodyWrapperEl,
        threshold,
      });
      this.bottomObserver.observe(this.modalBodyBottomEl);
      this.topObserver.observe(this.modalBodyTopEl);
    }
  }
  //--------------------------------------------------------------------------
  //
  //  Event Listeners
  //
  //--------------------------------------------------------------------------
  handleKeyup(event) {
    if (this.open && event.key === 'Escape') {
      this.closeModal();
    }
    if (this.open && event.key === 'Shift') {
      this.focusCycleDirection = FocusCycleDirection.Forward;
    }
  }
  handleKeydown(event) {
    if (this.open && event.key === 'Shift') {
      this.focusCycleDirection = FocusCycleDirection.Backward;
    }
  }
  //--------------------------------------------------------------------------
  //
  //  Lifecycle Methods
  //
  //--------------------------------------------------------------------------
  componentDidLoad() {
    this.handleOpenStateChange(this.open);
    this.initIntersectionObserver();
  }
  componentDidRender() {
    this.focusNextFocusableElement();
  }
  componentWillLoad() {
    if (!this.host.hasAttribute('aria-labelledby') && this.headerText) {
      this.host.setAttribute('aria-label', this.headerText);
    }
    this.hasFooter = this.getSlotByName('modal-footer') !== undefined;
  }
  disconnectedCallback() {
    clearAllBodyScrollLocks();
    this.bottomObserver.disconnect();
    this.topObserver.disconnect();
  }
  render() {
    return (h(Host, { "aria-hidden": this.open ? 'false' : 'true', palette: this.palette, role: "dialog", style: this.open ? { display: 'flex' } : { display: 'none' } },
      h("div", { class: "backdrop" }),
      h("div", { id: "focus-gate-start", onFocus: this.focusNextFocusableElement, ref: (el) => {
          this.focusGateStartEl = el;
        }, tabindex: "0" }),
      h("div", { class: "modal" },
        h("div", { class: { 'modal-header-wrapper': true, 'text-danger': this.palette === 'danger' } },
          this.icon && (h("abds-icon", { class: "decorative-icon", icon: this.icon, size: "2xl", "stroke-width": "medium" })),
          this.headerText,
          h("button", { "aria-label": "close modal dialog", class: "close-button", onClick: this.closeModal, ref: (el) => {
              this.closeButtonEl = el;
            } },
            h("abds-icon", { icon: "remove", size: "2xl", "stroke-width": "semibold" }))),
        h("div", { class: {
            'modal-body-wrapper': true,
            'mask-bottom': this.isBodyBottomOverflown === true,
            'mask-top': this.isBodyTopOverflown === true,
            'rounded-b': this.hasFooter === false,
          }, ref: (el) => (this.modalBodyWrapperEl = el) },
          h("div", { class: "modal-body-top-el", ref: (el) => (this.modalBodyTopEl = el) }),
          h("slot", { name: "modal-body" }),
          h("div", { class: "modal-body-bottom-el", ref: (el) => (this.modalBodyBottomEl = el) })),
        this.hasFooter && (h("div", { class: "modal-footer-wrapper" },
          h("slot", { name: "modal-footer" })))),
      h("div", { id: "focus-gate-end", onFocus: this.wrapFocus, tabindex: "0" })));
  }
  static get is() { return "abds-modal"; }
  static get encapsulation() { return "shadow"; }
  static get originalStyleUrls() { return {
    "$": ["modal.css"]
  }; }
  static get styleUrls() { return {
    "$": ["modal.css"]
  }; }
  static get properties() { return {
    "headerText": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Sets header text."
      },
      "attribute": "header-text",
      "reflect": false
    },
    "icon": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Adds an abds-icon on left side of header text."
      },
      "attribute": "icon",
      "reflect": true
    },
    "open": {
      "type": "boolean",
      "mutable": true,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Sets open/closed state."
      },
      "attribute": "open",
      "reflect": true,
      "defaultValue": "false"
    },
    "palette": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "ModalPalette",
        "resolved": "\"basic\" | \"danger\"",
        "references": {
          "ModalPalette": {
            "location": "import",
            "path": "./types"
          }
        }
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Sets header text color."
      },
      "attribute": "palette",
      "reflect": true,
      "defaultValue": "'basic'"
    }
  }; }
  static get states() { return {
    "hasFooter": {},
    "isBodyBottomOverflown": {},
    "isBodyTopOverflown": {}
  }; }
  static get elementRef() { return "host"; }
  static get watchers() { return [{
      "propName": "open",
      "methodName": "handleOpenStateChange"
    }]; }
  static get listeners() { return [{
      "name": "keyup",
      "method": "handleKeyup",
      "target": "window",
      "capture": false,
      "passive": false
    }, {
      "name": "keydown",
      "method": "handleKeydown",
      "target": "window",
      "capture": false,
      "passive": false
    }]; }
}
//# sourceMappingURL=modal.js.map