/** biome-ignore-all lint/a11y/noNoninteractiveTabindex: focus traps are required to keep users focus within the modal */
import { Host, h, } from "@stencil/core";
import { animationTo, KEYFRAMES } from "../../shared/utils/animation";
import { isNestedInIfxComponent } from "../../shared/utils/dom-utils";
import { isFocusable, isHidden, queryShadowRoot, } from "../../shared/utils/focus-trap";
import { detectFramework } from "../..//shared/utils/framework-detection";
import { trackComponent } from "../../shared/utils/tracking";
export class IfxModal {
    hostElement;
    /**
     * Controls the visibility of the modal. Can be used for both declarative and programmatic control.
     */
    opened = false;
    /**
     * The title text displayed in the modal header. This should be a concise description of the modal's purpose.
     */
    caption = "Modal Title";
    /**
     * Provides an accessible label for the modal caption, enhancing screen reader support. If not provided, the `caption` prop will be used as the accessible name.
     */
    captionAriaLabel;
    /**
     * Determines whether clicking on the overlay (backdrop) will close the modal.
     */
    closeOnOverlayClick = true;
    /**
     * Defines the visual style of the modal, indicating its purpose or importance.
     */
    variant = "default";
    /**
     * Specifies the size of the modal, allowing it to adapt to different content needs and screen sizes.
     */
    size = "s";
    /**
     * Allows the display of a specific icon in the modal header when the variant is set to an alert type.
     * Refer to the [Icon Library](https://infineon.github.io/infineon-design-system-stencil/storybook/?path=/docs/icon-library--development) for available icons.
     */
    alertIcon = "";
    /**
     * Controls the visibility of the close button in the modal header.
     */
    showCloseButton = true;
    /**
     * Provides an accessible label for the close button, enhancing screen reader support. If not provided, a default label of "Close modal" will be used.
     */
    closeButtonAriaLabel = "Close modal";
    /**
     * Emitted when the modal finishes opening and the opening animation completes. No additional data is provided with this event.
     */
    ifxOpen;
    /**
     * Emitted when the modal finishes closing and the closing animation completes. No additional data is provided with this event.
     */
    ifxClose;
    /**
     * Emitted immediately when the `opened` state changes (before animations).
     * The event detail contains `{ opened: boolean }` with the new state.
     * Use this event for two-way binding (v-model in Vue, [(ngModel)] in Angular).
     */
    ifxOpenedChange;
    showModal = this.opened || false;
    slotButtonsPresent = false;
    modalContainer;
    focusableElements = [];
    closeButton;
    resizeTimeout;
    handleResize = () => {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = setTimeout(() => {
            if (this.showModal) {
                this.handleComponentOverflow();
            }
        }, 100);
    };
    async componentDidLoad() {
        if (!isNestedInIfxComponent(this.hostElement)) {
            const framework = detectFramework();
            trackComponent("ifx-modal", await framework);
        }
        // Query all focusable elements and store them in `focusableElements`.
        // Needed for the "focus trap" functionality.
        this.focusableElements = queryShadowRoot(this.hostElement.shadowRoot, (el) => isHidden(el) || el.matches("[data-focus-trap-edge]"), isFocusable);
        window.addEventListener("resize", this.handleResize);
        if (this.opened) {
            this.open();
        }
    }
    disconnectedCallback() {
        window.removeEventListener("resize", this.handleResize);
    }
    componentWillRender() {
        if (this.showModal) {
            this.handleComponentOverflow();
        }
    }
    async handleComponentOverflow() {
        const modalContentContainer = this.hostElement.shadowRoot.querySelector(".modal-content-container");
        if (this.showModal &&
            (await this.isModalContentContainerHeightReachedViewport())) {
            modalContentContainer.classList.add("no-overflow");
        }
        else if (modalContentContainer?.classList.contains("no-overflow")) {
            modalContentContainer?.classList.remove("no-overflow");
        }
    }
    getFirstFocusableElement() {
        return this.focusableElements[0];
    }
    getLastFocusableElement() {
        return this.focusableElements[this.focusableElements.length - 1];
    }
    handleTopFocus = () => {
        this.attemptFocus(this.getLastFocusableElement());
    };
    handleBottomFocus = () => {
        this.attemptFocus(this.getFirstFocusableElement());
    };
    attemptFocus(element) {
        if (element == null) {
            setTimeout(() => {
                //wait until DOM is fully loaded
                this.closeButton.focus();
            });
            return;
        }
        setTimeout(() => {
            //wait until DOM is fully loaded
            element.focus();
        }, 0);
    }
    open() {
        this.showModal = true;
        try {
            const anim = animationTo(this.modalContainer, KEYFRAMES.fadeIn, {
                duration: 200,
            });
            anim.addEventListener("finish", () => {
                // Setting focus on last item and removing immediately
                // so, on tab press first element is focused
                setTimeout(() => {
                    this.getLastFocusableElement()?.focus();
                    this.getLastFocusableElement()?.blur();
                }, 0);
                this.ifxOpen.emit();
            });
            this.hostElement.addEventListener("keydown", this.handleKeypress);
        }
        catch (_err) {
            this.ifxOpen.emit();
        }
    }
    close() {
        try {
            const anim = animationTo(this.modalContainer, KEYFRAMES.fadeOut, {
                duration: 200,
            });
            anim.addEventListener("finish", () => {
                this.showModal = false;
                this.ifxClose.emit();
            });
            this.hostElement.removeEventListener("keydown", this.handleKeypress);
        }
        catch (_err) {
            this.showModal = false;
            this.ifxClose.emit();
        }
    }
    handleKeypress = (event) => {
        if (!this.showModal) {
            return;
        }
        if (event.key === "Escape") {
            this.doBeforeClose("ESCAPE_KEY");
        }
    };
    doBeforeClose(trigger) {
        const triggers = [];
        triggers.push(trigger);
        const prevented = triggers.some((event) => event.defaultPrevented);
        if (!prevented) {
            this.opened = false;
        }
    }
    openedChanged(newValue) {
        this.ifxOpenedChange.emit({ opened: newValue });
        if (newValue === true) {
            this.open();
        }
        else {
            this.close();
        }
    }
    /**
     * Public method to programmatically open the modal.
     */
    async openModal() {
        this.opened = true;
    }
    /**
     * Public method to programmatically close the modal.
     */
    async closeModal() {
        this.opened = false;
    }
    handleOverlayClick() {
        if (this.closeOnOverlayClick) {
            this.doBeforeClose("BACKDROP");
        }
    }
    handleContentUpdate(e) {
        const slotElement = e.target;
        const nodes = slotElement.assignedNodes();
        if (nodes.length > 0) {
            nodes.forEach((node) => {
                if (node.observer) {
                    node.observer.disconnect();
                    delete node.observer;
                }
                const observer = new MutationObserver((mutationsList, _) => {
                    for (const mutation of mutationsList) {
                        if (mutation.type === "childList") {
                            if (this.showModal) {
                                this.handleComponentOverflow();
                            }
                        }
                    }
                });
                observer.observe(node, {
                    attributes: true,
                    childList: true,
                    subtree: true,
                });
                node.observer = observer;
            });
        }
    }
    handleButtonsSlotChange(e) {
        if (e.currentTarget.assignedElements()[0]?.childElementCount > 0) {
            this.slotButtonsPresent = true;
        }
        else {
            this.slotButtonsPresent = false;
        }
    }
    isModalContentContainerHeightReachedViewport() {
        //Adding timeout for proper height detection on Edge browser
        return new Promise((resolve) => {
            setTimeout(() => {
                const modalContent = this.hostElement.shadowRoot.querySelector(".modal-content");
                const modalContentHeight = modalContent.offsetHeight;
                const viewportHeight = window.innerHeight;
                const extraMarginForEdgeBrowser = 3;
                resolve(modalContentHeight + extraMarginForEdgeBrowser >=
                    viewportHeight * 0.9);
            }, 100);
        });
    }
    render() {
        const isAlertVariant = this.variant !== "default";
        return (h(Host, { key: '22d0036bdca11ad60db9fb1ba3d3a1b4ac259f11' }, h("div", { key: '96b120ea1b33f7d04a1e1469d76c27aea714dcc1', ref: (el) => {
                this.modalContainer = el;
            }, class: `modal-container ${this.showModal ? "open" : ""}` }, h("div", { key: '36c10c6fe27076acebba362f21e6f33540a7bdd9', class: "modal-overlay", role: "presentation", onClick: () => this.handleOverlayClick() }), h("div", { key: 'b7391d4d245695c20d74168ce43b1698e8719599', "data-focus-trap-edge": true, role: "none", onFocus: this.handleTopFocus, tabIndex: 0 }), h("div", { key: 'fdfb65ca8c8ebec5afc2e011d8c50c4c912ec03b', class: `modal-content-container ${this.size}`, role: "dialog", "aria-modal": "true", "aria-label": this.captionAriaLabel }, isAlertVariant ? (h("div", { class: `modal-icon-container ${this.variant === "alert-brand" ? "" : "danger"}` }, this.alertIcon ? h("ifx-icon", { icon: this.alertIcon }) : null)) : null, h("div", { key: 'f161000a7fd01d981427fc06ca38b5fdc963657d', class: "modal-content" }, h("div", { key: '96b19d385c85f3c959e65b7c7423aa739fbbe5d6', class: "modal-header" }, h("h2", { key: 'daa677e7c0d4456c9979d9166fe41abf042c374a', class: "modal-caption" }, this.caption), this.showCloseButton && (
        // biome-ignore lint/a11y/noStaticElementInteractions: ifx-icon-button is a functional button element
        h("ifx-icon-button", { key: '534a813313dffd98276b3ef1c36d8f61e40dcc47', class: "modal-close-button", ref: (el) => (this.closeButton = el), icon: "cross-16", variant: "tertiary", onClick: () => this.doBeforeClose("CLOSE_BUTTON"), ariaLabel: this.closeButtonAriaLabel }))), h("div", { key: '8d04769e35ca7e7d819471bc5c6c682762ddd618', class: "modal-body" }, h("slot", { key: '5aa3cf89c187a85d33f94ce512bbf1fcc9532de7', name: "content", onSlotchange: (e) => this.handleContentUpdate(e) })), h("div", { key: '0c5680c8e72193ae45d7c3e599e2cfe2fa79f60d', class: `modal-footer ${this.slotButtonsPresent ? "buttons-present" : ""}` }, h("slot", { key: 'c1ae781515ffac6096d7165828fea5c7eb76c61b', name: "buttons", onSlotchange: (e) => this.handleButtonsSlotChange(e) })))), h("div", { key: 'a2807f8ad9227e78be34ebed5c5a8193dac9282e', "data-focus-trap-edge": true, role: "none", onFocus: this.handleBottomFocus, tabIndex: 0 }))));
    }
    static get is() { return "ifx-modal"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["modal.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["modal.css"]
        };
    }
    static get properties() {
        return {
            "opened": {
                "type": "boolean",
                "mutable": true,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Controls the visibility of the modal. Can be used for both declarative and programmatic control."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "opened",
                "defaultValue": "false"
            },
            "caption": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The title text displayed in the modal header. This should be a concise description of the modal's purpose."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "caption",
                "defaultValue": "\"Modal Title\""
            },
            "captionAriaLabel": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string | null",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Provides an accessible label for the modal caption, enhancing screen reader support. If not provided, the `caption` prop will be used as the accessible name."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "caption-aria-label"
            },
            "closeOnOverlayClick": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Determines whether clicking on the overlay (backdrop) will close the modal."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "close-on-overlay-click",
                "defaultValue": "true"
            },
            "variant": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "\"default\" | \"alert-brand\" | \"alert-danger\"",
                    "resolved": "\"alert-brand\" | \"alert-danger\" | \"default\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Defines the visual style of the modal, indicating its purpose or importance."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "variant",
                "defaultValue": "\"default\""
            },
            "size": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "\"s\" | \"m\" | \"l\"",
                    "resolved": "\"l\" | \"m\" | \"s\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Specifies the size of the modal, allowing it to adapt to different content needs and screen sizes."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "size",
                "defaultValue": "\"s\""
            },
            "alertIcon": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Allows the display of a specific icon in the modal header when the variant is set to an alert type.\nRefer to the [Icon Library](https://infineon.github.io/infineon-design-system-stencil/storybook/?path=/docs/icon-library--development) for available icons."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "alert-icon",
                "defaultValue": "\"\""
            },
            "showCloseButton": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Controls the visibility of the close button in the modal header."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "show-close-button",
                "defaultValue": "true"
            },
            "closeButtonAriaLabel": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Provides an accessible label for the close button, enhancing screen reader support. If not provided, a default label of \"Close modal\" will be used."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "close-button-aria-label",
                "defaultValue": "\"Close modal\""
            }
        };
    }
    static get states() {
        return {
            "showModal": {},
            "slotButtonsPresent": {}
        };
    }
    static get events() {
        return [{
                "method": "ifxOpen",
                "name": "ifxOpen",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the modal finishes opening and the opening animation completes. No additional data is provided with this event."
                },
                "complexType": {
                    "original": "any",
                    "resolved": "any",
                    "references": {}
                }
            }, {
                "method": "ifxClose",
                "name": "ifxClose",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the modal finishes closing and the closing animation completes. No additional data is provided with this event."
                },
                "complexType": {
                    "original": "any",
                    "resolved": "any",
                    "references": {}
                }
            }, {
                "method": "ifxOpenedChange",
                "name": "ifxOpenedChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted immediately when the `opened` state changes (before animations).\nThe event detail contains `{ opened: boolean }` with the new state.\nUse this event for two-way binding (v-model in Vue, [(ngModel)] in Angular)."
                },
                "complexType": {
                    "original": "{ opened: boolean }",
                    "resolved": "{ opened: boolean; }",
                    "references": {}
                }
            }];
    }
    static get methods() {
        return {
            "openModal": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Public method to programmatically open the modal.",
                    "tags": []
                }
            },
            "closeModal": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Public method to programmatically close the modal.",
                    "tags": []
                }
            }
        };
    }
    static get elementRef() { return "hostElement"; }
    static get watchers() {
        return [{
                "propName": "opened",
                "methodName": "openedChanged"
            }];
    }
}
//# sourceMappingURL=modal.js.map
