import { h, } from "@stencil/core";
import { createRandomString } from "../../util/random-string";
import { zipObject } from "lodash-es";
import { portalContains } from "../portal/contains";
import { ESCAPE } from "../../util/keycodes";
import { focusTriggerElement } from "../../util/focus-trigger-element";
/**
 * A popover is an impermanent layer that is displayed on top of other content
 * when user taps an element that triggers the popover. This element can be
 * practically anything, a button, piece of text, and icon, etc.
 *
 * Popovers are nonmodal, thus they don't have a semi-transparent backdrop layer
 * that darkens the content below them. Also for the same reason, they can be
 * dismissed by tapping or clicking another part of the screen, but not by a
 * clicking a button or link on the popover itself.
 *
 * ## Usage
 * Use a popover to show **options** or **information** related to the trigger
 * onscreen. A typical use case for popovers is a tooltip, in which you show
 * help text or contextual information to users.
 *
 * Popovers are most appropriate on larger screens but can be used on smaller
 * screens too, as long as their content is responsive and takes into account
 * the context they are displayed on.
 *
 * When a popover is displayed, interactions with other controls are blocked,
 * until user dismisses the popover.
 *
 *
 * ## Layout
 * Popovers has only one slot in which you can import a custom web-component.
 *
 * :::note
 * You must make sure that web-components that you import into the slot has
 * a `width: 100%;` style so that it can horizontally stretch across the popover.
 *
 * However, `width` of the popover can be controlled by specifying a CSS variable
 * of `--popover-surface-width`. If you don't specify any width, the popover
 * will grow as wide as its content.
 * :::
 *
 * :::important
 * Do not make a popover too big. They should never take over the entire screen.
 * If your content is that big, you should probably be using a Modal instead.
 * :::
 *
 * @slot - Content to put inside the surface
 * @exampleComponent limel-example-popover-basic
 * @exampleComponent limel-example-popover-trigger-interaction
 * @exampleComponent limel-example-popover-styling
 */
export class Popover {
    constructor() {
        /**
         * True if the content within the popover should be visible
         */
        this.open = false;
        this.shouldRestoreFocusOnClose = false;
        this.handleGlobalKeyPress = (event) => {
            if (event.key !== ESCAPE) {
                return;
            }
            event.stopPropagation();
            event.preventDefault();
            this.requestCloseAndRestoreFocus();
        };
        this.requestCloseAndRestoreFocus = () => {
            this.shouldRestoreFocusOnClose = true;
            this.close.emit();
        };
        this.setTriggerRef = (elm) => {
            this.triggerSlot = elm;
        };
        this.focusTrigger = () => {
            var _a, _b;
            const trigger = (_b = (_a = this.triggerSlot) === null || _a === void 0 ? void 0 : _a.assignedElements()) === null || _b === void 0 ? void 0 : _b[0];
            focusTriggerElement(trigger);
        };
        this.setTriggerAttributes = (element) => {
            const attributes = {
                'aria-haspopup': true,
                'aria-expanded': this.open,
                'aria-controls': this.portalId,
                role: 'button',
            };
            for (const [key, value] of Object.entries(attributes)) {
                if (value) {
                    element.setAttribute(key, String(value));
                }
                else {
                    element.removeAttribute(key);
                }
            }
        };
        this.portalId = createRandomString();
        this.globalClickListener = this.globalClickListener.bind(this);
    }
    watchOpen() {
        this.setupGlobalHandlers();
        if (!this.open && this.shouldRestoreFocusOnClose) {
            this.shouldRestoreFocusOnClose = false;
            setTimeout(this.focusTrigger, 0);
        }
    }
    componentWillLoad() {
        this.setupGlobalHandlers();
    }
    componentDidRender() {
        if (!this.triggerSlot) {
            return;
        }
        // eslint-disable-next-line unicorn/no-array-for-each
        this.triggerSlot.assignedElements().forEach(this.setTriggerAttributes);
    }
    setupGlobalHandlers() {
        if (this.open) {
            document.addEventListener('click', this.globalClickListener, {
                capture: true,
            });
            document.addEventListener('keydown', this.handleGlobalKeyPress);
        }
        else {
            document.removeEventListener('click', this.globalClickListener);
            document.removeEventListener('keydown', this.handleGlobalKeyPress);
        }
    }
    render() {
        const cssProperties = this.getCssProperties();
        const popoverZIndex = getComputedStyle(this.host).getPropertyValue('--popover-z-index');
        return (h("div", { key: '7bc94b0e636ab16a04e76947731eb815f9a33e38', class: "trigger-anchor" }, h("slot", { key: 'e35b20eb6e38755cd50f71ada0413efcc79ce9e0', name: "trigger", ref: this.setTriggerRef }), h("limel-portal", { key: '397107a1f1fa9f796f0801da60df81084a0862ea', visible: this.open, containerId: this.portalId, containerStyle: { 'z-index': popoverZIndex }, openDirection: this.openDirection }, h("limel-popover-surface", { key: 'f63f011a8d529a68b535fcbed4685a5bb508dded', contentCollection: this.host.children, style: cssProperties }))));
    }
    globalClickListener(event) {
        const element = event.target;
        const clickedInside = portalContains(this.host, element);
        if (this.open && !clickedInside) {
            event.stopPropagation();
            event.preventDefault();
            this.close.emit();
        }
    }
    getCssProperties() {
        const propertyNames = [
            '--popover-surface-width',
            '--popover-body-background-color',
            '--popover-border-radius',
            '--popover-box-shadow',
        ];
        const style = getComputedStyle(this.host);
        const values = propertyNames.map((property) => {
            return style.getPropertyValue(property);
        });
        return zipObject(propertyNames, values);
    }
    static get is() { return "limel-popover"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["popover.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["popover.css"]
        };
    }
    static get properties() {
        return {
            "open": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "True if the content within the popover should be visible"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "open",
                "defaultValue": "false"
            },
            "openDirection": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "OpenDirection",
                    "resolved": "\"bottom\" | \"bottom-end\" | \"bottom-start\" | \"left\" | \"left-end\" | \"left-start\" | \"right\" | \"right-end\" | \"right-start\" | \"top\" | \"top-end\" | \"top-start\"",
                    "references": {
                        "OpenDirection": {
                            "location": "import",
                            "path": "../menu/menu.types",
                            "id": "src/components/menu/menu.types.ts::OpenDirection",
                            "referenceLocation": "OpenDirection"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Decides the popover's location in relation to its trigger"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "open-direction"
            }
        };
    }
    static get events() {
        return [{
                "method": "close",
                "name": "close",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emits an event when the component is closing"
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }];
    }
    static get elementRef() { return "host"; }
    static get watchers() {
        return [{
                "propName": "open",
                "methodName": "watchOpen"
            }];
    }
}
