import { Host, h, Fragment, } from "@stencil/core";
import closeIcon from "../../assets/close-icon.svg";
import { isSlotUsed, checkResizeObserver, onComponentRequiredPropUndefined, getSlotElements, } from "../../utils/helpers";
/**
 * @slot dialog-controls - Content will be place at the bottom of the dialog.
 * @slot heading - Content will be placed at the top of the dialog.
 * @slot label - Content will be placed above the dialog heading.
 * @slot alert - Content will be placed at the top of the content area of the dialog.
 */
export class Dialog {
    constructor() {
        this.contentAreaMutationObserver = null;
        this.DATA_GETS_FOCUS = "data-gets-focus";
        this.DIALOG_CONTROLS = "dialog-controls";
        this.dialogHeight = 0;
        this.focusedElementIndex = 0;
        this.IC_TEXT_FIELD = "IC-TEXT-FIELD";
        this.IC_ACCORDION = "IC-ACCORDION";
        this.IC_ACCORDION_GROUP = "IC-ACCORDION-GROUP";
        this.IC_CHECKBOX = "IC-CHECKBOX";
        this.IC_SEARCH_BAR = "IC-SEARCH-BAR";
        this.resizeObserver = null;
        this.dialogRendered = false;
        this.fadeIn = false;
        /**
         * If set to `false`, the dialog will not close when the backdrop is clicked.
         */
        this.closeOnBackdropClick = true;
        /**
         * If 'true', sets the 'primary' or rightmost button to the destructive variant. Stops initial focus being set on the 'primary' or rightmost default or slotted button.
         */
        this.destructive = false;
        /**
         * Sets the dismiss label tooltip and aria label.
         */
        this.dismissLabel = "Dismiss";
        /**
         * If set to `true`, the content area max height and overflow properties are removed allowing the dialog to stretch below the fold.
         * This prop also prevents popover elements from being cut off within the content area.
         */
        this.disableHeightConstraint = false;
        /**
         * If set to `true`, the content area width property is removed, allowing content to take the full width of the dialog when using the large variant.
         */
        this.disableWidthConstraint = false;
        /**
         * If `true`, the close button will not be displayed.
         */
        this.hideCloseButton = false;
        /**
         * If set to `true`, default button controls will not be shown, but slotted dialog controls will still be displayed.
         */
        this.hideDefaultControls = false;
        /**
         * If `true`, the dialog will be displayed.
         */
        this.open = false;
        /**
         * Sets the maximum and minimum height and width for the dialog.
         */
        this.size = "small";
        /**
         * Sets the theme color to the dark or light theme color. "inherit" will set the color based on the system settings or ic-theme component.
         */
        this.theme = "inherit";
        this.dialogOpened = () => {
            var _a, _b;
            this.dialogRendered = true;
            if (this.disableHeightConstraint) {
                (_a = this.dialogEl) === null || _a === void 0 ? void 0 : _a.show();
            }
            else {
                (_b = this.dialogEl) === null || _b === void 0 ? void 0 : _b.showModal();
            }
            setTimeout(() => {
                this.fadeIn = true;
                /**
                 * This is required to set scroll back to top if:
                 * - dialog content goes below the fold
                 * - is closed using cancel or confirm and reopened.
                 *
                 * Without this, the scroll bar will start from the dialog's last scroll-x coordinate.
                 */
                if (this.backdropEl &&
                    this.disableHeightConstraint &&
                    this.backdropEl.scrollTop !== 0) {
                    this.backdropEl.scrollTop = 0;
                }
            }, 10);
            setTimeout(() => {
                this.setInitialFocus();
                checkResizeObserver(this.runResizeObserver);
            }, 75);
            setTimeout(() => {
                this.icDialogOpened.emit();
            }, 80);
        };
        this.runResizeObserver = () => {
            if (this.dialogEl) {
                this.resizeObserver = new ResizeObserver(() => {
                    clearTimeout(this.resizeTimeout);
                    this.resizeTimeout = window.setTimeout(this.resizeObserverCallback, 80);
                });
                this.resizeObserver.observe(this.dialogEl);
            }
        };
        this.resizeObserverCallback = () => {
            if (this.dialogEl && this.dialogEl.clientHeight !== this.dialogHeight) {
                this.dialogHeight = this.dialogEl.clientHeight;
            }
        };
        this.refreshInteractiveElementsOnSlotChange = () => {
            var _a, _b, _c;
            const contentWrapper = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("#dialog-content");
            if (contentWrapper) {
                this.contentArea = contentWrapper.querySelector("slot");
                // Detect changes to slotted elements
                (_b = this.contentArea) === null || _b === void 0 ? void 0 : _b.addEventListener("slotchange", this.getInteractiveElements);
                this.contentAreaMutationObserver = new MutationObserver(() => {
                    this.getInteractiveElements();
                });
                // Detect changes to children of slotted elements
                (_c = getSlotElements(contentWrapper)) === null || _c === void 0 ? void 0 : _c.forEach((el) => {
                    var _a;
                    (_a = this.contentAreaMutationObserver) === null || _a === void 0 ? void 0 : _a.observe(el, {
                        childList: true,
                        subtree: true,
                    });
                });
            }
        };
        this.removeSlotChangeListener = () => {
            var _a;
            if (this.contentArea) {
                this.contentArea.removeEventListener("slotchange", this.getInteractiveElements);
                (_a = this.contentAreaMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
            }
        };
        this.setInitialFocus = () => {
            this.sourceElement = document.activeElement;
            this.focusedElementIndex = this.interactiveElementList
                ? this.interactiveElementList.findIndex((element) => element.hasAttribute(this.DATA_GETS_FOCUS))
                : 0;
            this.focusElement(this.interactiveElementList[this.focusedElementIndex]);
        };
        this.getFocusedElementIndex = () => {
            var _a;
            for (let i = 0; i < this.interactiveElementList.length; i++) {
                if (this.interactiveElementList[i] ===
                    (((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) || document.activeElement)) {
                    this.focusedElementIndex = i;
                }
            }
        };
        this.closeIconClick = () => {
            this.open = false;
        };
        this.getInteractiveElements = () => {
            var _a;
            this.interactiveElementList = Array.from(((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll("ic-button")) || []);
            const slottedInteractiveElements = Array.from(this.el.querySelectorAll(`a[href], button, input:not(.ic-input), textarea, select, details, [tabindex]:not([tabindex="-1"]),
          ic-button, ic-checkbox, ic-select, ic-search-bar, ic-tab-group, 
          ic-back-to-top, ic-breadcrumb, ic-chip[dismissible="true"], ic-footer-link, ic-link, ic-navigation-button,
          ic-navigation-item, ic-switch, ic-text-field, ic-accordion-group, ic-accordion, ic-date-input, ic-date-picker`));
            if (slottedInteractiveElements.length > 0) {
                if (slottedInteractiveElements[0].slot !== this.DIALOG_CONTROLS) {
                    slottedInteractiveElements[0].setAttribute(this.DATA_GETS_FOCUS, "");
                }
                else if (!this.destructive) {
                    slottedInteractiveElements[slottedInteractiveElements.length - 1].setAttribute(this.DATA_GETS_FOCUS, "");
                }
            }
            for (let i = 0; i < slottedInteractiveElements.length; i++) {
                this.interactiveElementList.splice(1 + i, 0, slottedInteractiveElements[i]);
            }
        };
        this.getNextFocusEl = (focusedElementIndex) => this.interactiveElementList[focusedElementIndex];
        this.onTabKeyPress = (shiftKey) => {
            this.getFocusedElementIndex();
            if (this.interactiveElementList[this.focusedElementIndex].tagName ===
                this.IC_SEARCH_BAR) {
                return false;
            }
            this.setFocusIndexBasedOnShiftKey(shiftKey);
            this.loopNextFocusIndexIfLastElement();
            this.focusElement(this.getNextFocusEl(this.focusedElementIndex), shiftKey);
            return true;
        };
        this.shouldSkipElement = (element) => {
            const isHidden = getComputedStyle(element).visibility === "hidden" ||
                element.offsetHeight === 0 ||
                element.hasAttribute("disabled") ||
                (element.tagName === this.IC_ACCORDION_GROUP &&
                    element.hasAttribute("single-expansion"));
            const radioEl = element.closest("ic-radio-option");
            return (isHidden ||
                (element.getAttribute("type") === "radio" &&
                    !!radioEl &&
                    !radioEl.hasAttribute("selected")));
        };
        this.focusElement = (element, shiftKey = false) => {
            let nextFocusEl = element;
            if (this.shouldSkipElement(element)) {
                this.setFocusIndexBasedOnShiftKey(shiftKey);
                this.loopNextFocusIndexIfLastElement();
                nextFocusEl = this.getNextFocusEl(this.focusedElementIndex);
                this.focusElement(nextFocusEl, shiftKey);
            }
            else {
                switch (element.tagName) {
                    case this.IC_ACCORDION_GROUP:
                    case this.IC_ACCORDION:
                    case this.IC_SEARCH_BAR:
                    case this.IC_TEXT_FIELD:
                    case this.IC_CHECKBOX:
                        element.setFocus();
                        break;
                    default:
                        element.focus();
                }
            }
        };
        this.renderDialog = () => {
            const { hideDefaultControls, size, heading, label, destructive, dismissLabel, hideCloseButton, disableHeightConstraint, disableWidthConstraint, closeIconClick, DIALOG_CONTROLS, } = this;
            const controlsSlotUsed = isSlotUsed(this.el, DIALOG_CONTROLS);
            return (h("dialog", { class: {
                    dialog: true,
                    [`${size}`]: true,
                    "disable-height-constraint": !!disableHeightConstraint,
                    "disable-width-constraint": !!disableWidthConstraint,
                }, "aria-labelledby": "dialog-label dialog-heading", "aria-describedby": "dialog-alert dialog-content", ref: (el) => (this.dialogEl = el) }, h("div", { class: "heading-area" }, h("div", { class: "heading-content" }, h("div", { class: "label" }, h("slot", { name: "label" }, h("ic-typography", { variant: "label", id: "dialog-label" }, label))), h("div", { class: "heading" }, h("slot", { name: "heading" }, h("ic-typography", { variant: "h4", id: "dialog-heading" }, heading)))), !hideCloseButton && (h("ic-button", { class: "close-icon", variant: "icon", innerHTML: closeIcon, "aria-label": dismissLabel, onClick: closeIconClick, "data-gets-focus": destructive || (hideDefaultControls && !controlsSlotUsed)
                    ? ""
                    : null }))), h("div", { class: "content-area" }, isSlotUsed(this.el, "alert") && h("slot", { name: "alert" }), h("div", { id: "dialog-content" }, h("slot", null))), (controlsSlotUsed || !hideDefaultControls) && (h("div", { class: {
                    [DIALOG_CONTROLS]: true,
                } }, controlsSlotUsed ? (h("slot", { name: DIALOG_CONTROLS })) : (h(Fragment, null, h("ic-button", { variant: "tertiary", onClick: () => this.cancelDialog(), class: "dialog-control-button", "data-gets-focus": null }, "Cancel"), h("ic-button", { variant: destructive ? "destructive" : "primary", onClick: () => this.confirmDialog(), class: "dialog-control-button", "data-gets-focus": "" }, "Confirm")))))));
        };
    }
    watchOpenHandler() {
        if (this.open) {
            this.dialogOpened();
        }
        else {
            this.fadeIn = false;
            if (this.resizeObserver !== null) {
                this.resizeObserver.disconnect();
            }
            setTimeout(() => {
                var _a, _b;
                this.dialogRendered = false;
                (_a = this.dialogEl) === null || _a === void 0 ? void 0 : _a.close();
                (_b = this.sourceElement) === null || _b === void 0 ? void 0 : _b.focus();
                this.dialogHeight = 0;
                this.icDialogClosed.emit();
            }, 80);
        }
    }
    disconnectedCallback() {
        this.removeSlotChangeListener();
    }
    componentDidLoad() {
        this.getInteractiveElements();
        this.refreshInteractiveElementsOnSlotChange();
        if (this.open) {
            this.dialogOpened();
        }
        !isSlotUsed(this.el, "heading") &&
            onComponentRequiredPropUndefined([{ prop: this.heading, propName: "heading" }], "Dialog");
    }
    componentDidRender() {
        document.body.style.overflow =
            getComputedStyle(this.el).display !== "none" &&
                this.disableHeightConstraint
                ? "hidden"
                : "auto";
    }
    handleKeyboard(ev) {
        if (this.dialogRendered) {
            switch (ev.key) {
                case "Tab":
                    if (this.onTabKeyPress(ev.shiftKey)) {
                        ev.preventDefault();
                    }
                    break;
                case "Escape":
                    if (!ev.repeat) {
                        this.open = false;
                    }
                    ev.stopImmediatePropagation();
                    break;
            }
        }
    }
    handleClick(ev) {
        if (this.dialogEl &&
            this.closeOnBackdropClick &&
            ev.composedPath().indexOf(this.dialogEl) <= 0) {
            const { top, height, left, width } = this.dialogEl.getBoundingClientRect();
            const isInDialog = top <= ev.clientY &&
                ev.clientY <= top + height &&
                left <= ev.clientX &&
                ev.clientX <= left + width;
            if (!isInDialog) {
                this.open = false;
            }
        }
    }
    /**
     * Cancels the dialog. Used by the default 'Cancel' button or can be called manually to trigger cancelling of dialog.
     */
    async cancelDialog() {
        this.icDialogCancelled.emit();
        this.open = false;
    }
    /**
     * Confirms the dialog. Used by the default 'Confirm' button or can be called manually to trigger confirming of dialog.
     */
    async confirmDialog() {
        this.icDialogConfirmed.emit();
    }
    loopNextFocusIndexIfLastElement() {
        if (this.focusedElementIndex > this.interactiveElementList.length - 1)
            this.focusedElementIndex = 0;
        else if (this.focusedElementIndex < 0) {
            this.focusedElementIndex = this.interactiveElementList.length - 1;
        }
    }
    setFocusIndexBasedOnShiftKey(shiftKey) {
        if (shiftKey) {
            this.focusedElementIndex -= 1;
        }
        else {
            this.focusedElementIndex += 1;
        }
    }
    render() {
        const { dialogRendered, disableHeightConstraint, fadeIn, theme } = this;
        return (h(Host, { key: '11ce388b78e337682fcd54494a076789d488b86b', class: {
                "ic-dialog-hidden": !dialogRendered,
                "ic-dialog-fade-in": fadeIn,
                "disable-height-constraint": !!disableHeightConstraint,
                [`ic-theme-${theme}`]: theme !== "inherit",
            } }, disableHeightConstraint ? (h("div", { class: "backdrop", ref: (el) => (this.backdropEl = el) }, this.renderDialog())) : (this.renderDialog())));
    }
    static get is() { return "ic-dialog"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["ic-dialog.css"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["ic-dialog.css"]
        };
    }
    static get properties() {
        return {
            "closeOnBackdropClick": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If set to `false`, the dialog will not close when the backdrop is clicked."
                },
                "getter": false,
                "setter": false,
                "attribute": "close-on-backdrop-click",
                "reflect": false,
                "defaultValue": "true"
            },
            "destructive": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If 'true', sets the 'primary' or rightmost button to the destructive variant. Stops initial focus being set on the 'primary' or rightmost default or slotted button."
                },
                "getter": false,
                "setter": false,
                "attribute": "destructive",
                "reflect": false,
                "defaultValue": "false"
            },
            "dismissLabel": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Sets the dismiss label tooltip and aria label."
                },
                "getter": false,
                "setter": false,
                "attribute": "dismiss-label",
                "reflect": false,
                "defaultValue": "\"Dismiss\""
            },
            "disableHeightConstraint": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If set to `true`, the content area max height and overflow properties are removed allowing the dialog to stretch below the fold.\nThis prop also prevents popover elements from being cut off within the content area."
                },
                "getter": false,
                "setter": false,
                "attribute": "disable-height-constraint",
                "reflect": false,
                "defaultValue": "false"
            },
            "disableWidthConstraint": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If set to `true`, the content area width property is removed, allowing content to take the full width of the dialog when using the large variant."
                },
                "getter": false,
                "setter": false,
                "attribute": "disable-width-constraint",
                "reflect": false,
                "defaultValue": "false"
            },
            "hideCloseButton": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the close button will not be displayed."
                },
                "getter": false,
                "setter": false,
                "attribute": "hide-close-button",
                "reflect": false,
                "defaultValue": "false"
            },
            "hideDefaultControls": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If set to `true`, default button controls will not be shown, but slotted dialog controls will still be displayed."
                },
                "getter": false,
                "setter": false,
                "attribute": "hide-default-controls",
                "reflect": false,
                "defaultValue": "false"
            },
            "heading": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Sets the heading for the dialog."
                },
                "getter": false,
                "setter": false,
                "attribute": "heading",
                "reflect": false
            },
            "label": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Sets the optional label for the dialog which appears above the heading."
                },
                "getter": false,
                "setter": false,
                "attribute": "label",
                "reflect": false
            },
            "open": {
                "type": "boolean",
                "mutable": true,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the dialog will be displayed."
                },
                "getter": false,
                "setter": false,
                "attribute": "open",
                "reflect": true,
                "defaultValue": "false"
            },
            "size": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "\"small\" | \"medium\" | \"large\"",
                    "resolved": "\"large\" | \"medium\" | \"small\" | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Sets the maximum and minimum height and width for the dialog."
                },
                "getter": false,
                "setter": false,
                "attribute": "size",
                "reflect": false,
                "defaultValue": "\"small\""
            },
            "theme": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "IcThemeMode",
                    "resolved": "\"dark\" | \"inherit\" | \"light\" | undefined",
                    "references": {
                        "IcThemeMode": {
                            "location": "import",
                            "path": "../../utils/types",
                            "id": "src/utils/types.ts::IcThemeMode"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Sets the theme color to the dark or light theme color. \"inherit\" will set the color based on the system settings or ic-theme component."
                },
                "getter": false,
                "setter": false,
                "attribute": "theme",
                "reflect": false,
                "defaultValue": "\"inherit\""
            }
        };
    }
    static get states() {
        return {
            "dialogRendered": {},
            "fadeIn": {}
        };
    }
    static get events() {
        return [{
                "method": "icDialogCancelled",
                "name": "icDialogCancelled",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Cancelation event emitted when default 'Cancel' button clicked or 'cancelDialog' method is called."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "icDialogClosed",
                "name": "icDialogClosed",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when dialog has closed."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "icDialogConfirmed",
                "name": "icDialogConfirmed",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Confirmation event emitted when default 'Confirm' primary button clicked or 'confirmDialog' method is called."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "icDialogOpened",
                "name": "icDialogOpened",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when dialog has opened."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }];
    }
    static get methods() {
        return {
            "cancelDialog": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Cancels the dialog. Used by the default 'Cancel' button or can be called manually to trigger cancelling of dialog.",
                    "tags": []
                }
            },
            "confirmDialog": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Confirms the dialog. Used by the default 'Confirm' button or can be called manually to trigger confirming of dialog.",
                    "tags": []
                }
            }
        };
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "open",
                "methodName": "watchOpenHandler"
            }];
    }
    static get listeners() {
        return [{
                "name": "keydown",
                "method": "handleKeyboard",
                "target": "document",
                "capture": false,
                "passive": false
            }, {
                "name": "click",
                "method": "handleClick",
                "target": undefined,
                "capture": false,
                "passive": false
            }];
    }
}
//# sourceMappingURL=ic-dialog.js.map
