import { __decorate } from 'tslib';
import { property } from 'lit/decorators.js';
import { createRef } from 'lit/directives/ref.js';
import { autoUpdate, offset, flip, shift, computePosition } from '@floating-ui/dom';
import SgdsElement from './sgds-element.js';
import genId from '../utils/generateId.js';

const ARROW_DOWN = "ArrowDown";
const ARROW_UP = "ArrowUp";
const ESC = "Escape";
/**
 * @event sgds-show - Emitted event when show instance is called
 * @event sgds-after-show - Emitted event when dropdown has been made visible to the user and CSS transitions have completed
 * @event sgds-hide - Emitted event when hide instance is called
 * @event sgds-after-hide - Emitted event when dropdown has hidden to the user and CSS transitions have completed
 */
class DropdownElement extends SgdsElement {
    constructor() {
        // static styles = SgdsElement.styles;
        super(...arguments);
        /** @internal */
        this.myDropdown = createRef();
        /** @internal Unique id generated for the dropdown menu */
        this.dropdownMenuId = genId("dropdown-menu", "div");
        /** @internal Controls auto-flipping of menu */
        this.noFlip = false;
        /** @internal When true, aligns right edge of menu with right edge of button */
        this.menuAlignRight = false;
        /** @internal The drop position of menu relative to the toggle button */
        this.drop = "down";
        /**  Additional configuration to pass to Floating UI. */
        this.floatingOpts = {};
        /** When true, dropdown menu shows on first load */
        this.menuIsOpen = false;
        /** Controls the close behaviour of dropdown menu. By default menu auto-closes when SgdsDropdownItem or area outside dropdown is clicked */
        this.close = "default";
        /** Disables the dropdown toggle */
        this.disabled = false;
        /** Makes the input readonly. */
        this.readonly = false;
        /** @internal Reference to the floating menu element */
        this.menuRef = createRef();
        this._handleClickOutOfElement = (e) => {
            if (!this.menuIsOpen)
                return;
            if (!e.composedPath().includes(this)) {
                this.hideMenu(true);
            }
        };
    }
    connectedCallback() {
        super.connectedCallback();
        if (this.close !== "inside") {
            document.addEventListener("click", this._handleClickOutOfElement);
        }
        this.addEventListener("keydown", this._handleKeyboardMenuEvent);
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        document.removeEventListener("click", this._handleClickOutOfElement);
        this.removeEventListener("keydown", this._handleKeyboardMenuEvent);
        if (this._cleanupAutoUpdate) {
            this._cleanupAutoUpdate();
            this._cleanupAutoUpdate = undefined;
        }
    }
    firstUpdated(changedProperties) {
        super.firstUpdated(changedProperties);
        // Optionally open menu on first load
        if (this.menuIsOpen) {
            this.updateFloatingPosition();
        }
    }
    /** When invoked, opens the dropdown menu */
    async showMenu() {
        if (this.disabled || this.menuIsOpen)
            return;
        this.menuIsOpen = true;
        this.emit("sgds-show");
        await this.updateFloatingPosition();
        this.emit("sgds-after-show");
        if (this.myDropdown.value && this.menuRef.value) {
            this._cleanupAutoUpdate = autoUpdate(this.myDropdown.value, this.menuRef.value, () => this.updateFloatingPosition());
        }
    }
    /** When invoked, hides the dropdown menu */
    hideMenu(isOutside) {
        if (!this.menuIsOpen)
            return;
        this.emit("sgds-hide", { detail: { isOutside } });
        this.menuIsOpen = false;
        setTimeout(() => this.emit("sgds-after-hide"), 0);
        if (this._cleanupAutoUpdate) {
            this._cleanupAutoUpdate();
            this._cleanupAutoUpdate = undefined;
        }
    }
    toggleMenu() {
        if (this.menuIsOpen) {
            this.hideMenu();
        }
        else {
            this.showMenu();
        }
    }
    _handleKeyboardMenuEvent(e) {
        if (this.readonly)
            return;
        switch (e.key) {
            case ARROW_DOWN:
            case ARROW_UP:
                e.preventDefault();
                if (!this.menuIsOpen)
                    this.showMenu();
                break;
            case ESC:
                this.hideMenu();
                break;
        }
    }
    mergeMiddleware(defaults, custom) {
        const getType = (mw) => { var _a; return (mw === null || mw === void 0 ? void 0 : mw.name) || ((_a = mw === null || mw === void 0 ? void 0 : mw.constructor) === null || _a === void 0 ? void 0 : _a.name); };
        const customTypes = custom.map(getType);
        const merged = defaults
            .map(def => {
            const type = getType(def);
            const customIdx = customTypes.indexOf(type);
            return customIdx !== -1 ? custom[customIdx] : def;
        })
            .concat(custom.filter(c => !defaults.some(def => getType(def) === getType(c))));
        return merged;
    }
    async updateFloatingPosition() {
        if (!this.myDropdown.value || !this.menuRef.value)
            return;
        let placement = "bottom-start";
        switch (this.drop) {
            case "up":
                placement = this.menuAlignRight ? "top-end" : "top-start";
                break;
            case "right":
                placement = "right-start";
                break;
            case "left":
                placement = "left-start";
                break;
            case "down":
                placement = this.menuAlignRight ? "bottom-end" : "bottom-start";
                break;
            default:
                placement = "bottom-start";
                break;
        }
        const defaultMiddleware = [offset(8), !this.noFlip ? flip() : undefined, shift()].filter(Boolean);
        let middleware = defaultMiddleware;
        if (Array.isArray(this.floatingOpts.middleware) && this.floatingOpts.middleware.length > 0) {
            middleware = this.mergeMiddleware(defaultMiddleware, this.floatingOpts.middleware.filter(Boolean));
        }
        const opts = Object.assign(Object.assign({ strategy: "fixed", placement }, this.floatingOpts), { middleware });
        const { x, y, strategy, placement: computedPlacement } = await computePosition(this.myDropdown.value, this.menuRef.value, opts);
        this.menuRef.value.setAttribute("data-placement", computedPlacement);
        Object.assign(this.menuRef.value.style, {
            position: strategy,
            left: `${x}px`,
            top: `${y}px`
        });
    }
}
__decorate([
    property({ type: Boolean, state: true })
], DropdownElement.prototype, "noFlip", void 0);
__decorate([
    property({ type: Boolean, reflect: true, state: true })
], DropdownElement.prototype, "menuAlignRight", void 0);
__decorate([
    property({ type: String, reflect: true, state: true })
], DropdownElement.prototype, "drop", void 0);
__decorate([
    property({ type: Object })
], DropdownElement.prototype, "floatingOpts", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], DropdownElement.prototype, "menuIsOpen", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], DropdownElement.prototype, "disabled", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], DropdownElement.prototype, "readonly", void 0);

export { DropdownElement };
//# sourceMappingURL=dropdown-element.js.map
