import { __esDecorate, __runInitializers } from "tslib";
import { randomString } from '@api-client/core/lib/math/Random.js';
import { html, LitElement } from 'lit';
import { property, queryAssignedElements, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { positionOverlay } from '../../../lib/ElementPositioning.js';
import * as ScrollHelper from '../../../lib/ScrollHelper.js';
const itemRole = ['menuitem', 'menuitemcheckbox', 'menuitemradio'];
let UiDropdownList = (() => {
    let _classSuper = LitElement;
    let _triggers_decorators;
    let _triggers_initializers = [];
    let _triggers_extraInitializers = [];
    let _dropdowns_decorators;
    let _dropdowns_initializers = [];
    let _dropdowns_extraInitializers = [];
    let _triggerId_decorators;
    let _triggerId_initializers = [];
    let _triggerId_extraInitializers = [];
    let _menuId_decorators;
    let _menuId_initializers = [];
    let _menuId_extraInitializers = [];
    let _open_decorators;
    let _open_initializers = [];
    let _open_extraInitializers = [];
    let _verticalAlign_decorators;
    let _verticalAlign_initializers = [];
    let _verticalAlign_extraInitializers = [];
    let _horizontalAlign_decorators;
    let _horizontalAlign_initializers = [];
    let _horizontalAlign_extraInitializers = [];
    let _noOverlap_decorators;
    let _noOverlap_initializers = [];
    let _noOverlap_extraInitializers = [];
    let _closeOnOutsideClick_decorators;
    let _closeOnOutsideClick_initializers = [];
    let _closeOnOutsideClick_extraInitializers = [];
    let _matchTriggerWidth_decorators;
    let _matchTriggerWidth_initializers = [];
    let _matchTriggerWidth_extraInitializers = [];
    let _closeOnTab_decorators;
    let _closeOnTab_initializers = [];
    let _closeOnTab_extraInitializers = [];
    let _overlayPositioning_decorators;
    let _overlayPositioning_initializers = [];
    let _overlayPositioning_extraInitializers = [];
    return class UiDropdownList extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _triggers_decorators = [queryAssignedElements()];
            _dropdowns_decorators = [queryAssignedElements({ slot: 'dropdown' })];
            _triggerId_decorators = [state()];
            _menuId_decorators = [state()];
            _open_decorators = [property({ type: Boolean, reflect: true })];
            _verticalAlign_decorators = [property({ type: String, reflect: true })];
            _horizontalAlign_decorators = [property({ type: String, reflect: true })];
            _noOverlap_decorators = [property({ type: Boolean, reflect: true })];
            _closeOnOutsideClick_decorators = [property({ type: Boolean })];
            _matchTriggerWidth_decorators = [property({ type: Boolean })];
            _closeOnTab_decorators = [property({ type: Boolean })];
            _overlayPositioning_decorators = [state()];
            __esDecorate(this, null, _triggers_decorators, { kind: "accessor", name: "triggers", static: false, private: false, access: { has: obj => "triggers" in obj, get: obj => obj.triggers, set: (obj, value) => { obj.triggers = value; } }, metadata: _metadata }, _triggers_initializers, _triggers_extraInitializers);
            __esDecorate(this, null, _dropdowns_decorators, { kind: "accessor", name: "dropdowns", static: false, private: false, access: { has: obj => "dropdowns" in obj, get: obj => obj.dropdowns, set: (obj, value) => { obj.dropdowns = value; } }, metadata: _metadata }, _dropdowns_initializers, _dropdowns_extraInitializers);
            __esDecorate(this, null, _triggerId_decorators, { kind: "accessor", name: "triggerId", static: false, private: false, access: { has: obj => "triggerId" in obj, get: obj => obj.triggerId, set: (obj, value) => { obj.triggerId = value; } }, metadata: _metadata }, _triggerId_initializers, _triggerId_extraInitializers);
            __esDecorate(this, null, _menuId_decorators, { kind: "accessor", name: "menuId", static: false, private: false, access: { has: obj => "menuId" in obj, get: obj => obj.menuId, set: (obj, value) => { obj.menuId = value; } }, metadata: _metadata }, _menuId_initializers, _menuId_extraInitializers);
            __esDecorate(this, null, _open_decorators, { kind: "accessor", name: "open", static: false, private: false, access: { has: obj => "open" in obj, get: obj => obj.open, set: (obj, value) => { obj.open = value; } }, metadata: _metadata }, _open_initializers, _open_extraInitializers);
            __esDecorate(this, null, _verticalAlign_decorators, { kind: "accessor", name: "verticalAlign", static: false, private: false, access: { has: obj => "verticalAlign" in obj, get: obj => obj.verticalAlign, set: (obj, value) => { obj.verticalAlign = value; } }, metadata: _metadata }, _verticalAlign_initializers, _verticalAlign_extraInitializers);
            __esDecorate(this, null, _horizontalAlign_decorators, { kind: "accessor", name: "horizontalAlign", static: false, private: false, access: { has: obj => "horizontalAlign" in obj, get: obj => obj.horizontalAlign, set: (obj, value) => { obj.horizontalAlign = value; } }, metadata: _metadata }, _horizontalAlign_initializers, _horizontalAlign_extraInitializers);
            __esDecorate(this, null, _noOverlap_decorators, { kind: "accessor", name: "noOverlap", static: false, private: false, access: { has: obj => "noOverlap" in obj, get: obj => obj.noOverlap, set: (obj, value) => { obj.noOverlap = value; } }, metadata: _metadata }, _noOverlap_initializers, _noOverlap_extraInitializers);
            __esDecorate(this, null, _closeOnOutsideClick_decorators, { kind: "accessor", name: "closeOnOutsideClick", static: false, private: false, access: { has: obj => "closeOnOutsideClick" in obj, get: obj => obj.closeOnOutsideClick, set: (obj, value) => { obj.closeOnOutsideClick = value; } }, metadata: _metadata }, _closeOnOutsideClick_initializers, _closeOnOutsideClick_extraInitializers);
            __esDecorate(this, null, _matchTriggerWidth_decorators, { kind: "accessor", name: "matchTriggerWidth", static: false, private: false, access: { has: obj => "matchTriggerWidth" in obj, get: obj => obj.matchTriggerWidth, set: (obj, value) => { obj.matchTriggerWidth = value; } }, metadata: _metadata }, _matchTriggerWidth_initializers, _matchTriggerWidth_extraInitializers);
            __esDecorate(this, null, _closeOnTab_decorators, { kind: "accessor", name: "closeOnTab", static: false, private: false, access: { has: obj => "closeOnTab" in obj, get: obj => obj.closeOnTab, set: (obj, value) => { obj.closeOnTab = value; } }, metadata: _metadata }, _closeOnTab_initializers, _closeOnTab_extraInitializers);
            __esDecorate(this, null, _overlayPositioning_decorators, { kind: "accessor", name: "overlayPositioning", static: false, private: false, access: { has: obj => "overlayPositioning" in obj, get: obj => obj.overlayPositioning, set: (obj, value) => { obj.overlayPositioning = value; } }, metadata: _metadata }, _overlayPositioning_initializers, _overlayPositioning_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #triggers_accessor_storage = __runInitializers(this, _triggers_initializers, void 0);
        get triggers() { return this.#triggers_accessor_storage; }
        set triggers(value) { this.#triggers_accessor_storage = value; }
        #dropdowns_accessor_storage = (__runInitializers(this, _triggers_extraInitializers), __runInitializers(this, _dropdowns_initializers, void 0));
        get dropdowns() { return this.#dropdowns_accessor_storage; }
        set dropdowns(value) { this.#dropdowns_accessor_storage = value; }
        #triggerId_accessor_storage = (__runInitializers(this, _dropdowns_extraInitializers), __runInitializers(this, _triggerId_initializers, `ui-trigger-${randomString(4)}`));
        get triggerId() { return this.#triggerId_accessor_storage; }
        set triggerId(value) { this.#triggerId_accessor_storage = value; }
        #menuId_accessor_storage = (__runInitializers(this, _triggerId_extraInitializers), __runInitializers(this, _menuId_initializers, `ui-menu-${randomString(4)}`
        /**
         * Whether the menu is opened.
         * @attribute
         */
        ));
        get menuId() { return this.#menuId_accessor_storage; }
        set menuId(value) { this.#menuId_accessor_storage = value; }
        #open_accessor_storage = (__runInitializers(this, _menuId_extraInitializers), __runInitializers(this, _open_initializers, false
        /**
         * The vertical (y-axis) alignment of the dropdown content.
         * - top
         * - bottom
         * - middle
         *
         * No value means the natural position according to the box model.
         * @attribute
         */
        ));
        /**
         * Whether the menu is opened.
         * @attribute
         */
        get open() { return this.#open_accessor_storage; }
        set open(value) { this.#open_accessor_storage = value; }
        #verticalAlign_accessor_storage = (__runInitializers(this, _open_extraInitializers), __runInitializers(this, _verticalAlign_initializers, void 0));
        /**
         * The vertical (y-axis) alignment of the dropdown content.
         * - top
         * - bottom
         * - middle
         *
         * No value means the natural position according to the box model.
         * @attribute
         */
        get verticalAlign() { return this.#verticalAlign_accessor_storage; }
        set verticalAlign(value) { this.#verticalAlign_accessor_storage = value; }
        #horizontalAlign_accessor_storage = (__runInitializers(this, _verticalAlign_extraInitializers), __runInitializers(this, _horizontalAlign_initializers, void 0));
        /**
         * The horizontal (x-axis) alignment of the dropdown content.
         * - top
         * - bottom
         * - middle
         *
         * No value means the natural position according to the box model.
         * @attribute
         */
        get horizontalAlign() { return this.#horizontalAlign_accessor_storage; }
        set horizontalAlign(value) { this.#horizontalAlign_accessor_storage = value; }
        #noOverlap_accessor_storage = (__runInitializers(this, _horizontalAlign_extraInitializers), __runInitializers(this, _noOverlap_initializers, void 0));
        /**
         * Affects the `horizontal` and `vertical` positioning so that the target element does not overlap with the anchor.
         * For example, when the `vertical` align is `top`, the top edge of the target will be rendered at the bottom
         * of the anchor down to the edge of the viewport.
         * @attribute
         */
        get noOverlap() { return this.#noOverlap_accessor_storage; }
        set noOverlap(value) { this.#noOverlap_accessor_storage = value; }
        #closeOnOutsideClick_accessor_storage = (__runInitializers(this, _noOverlap_extraInitializers), __runInitializers(this, _closeOnOutsideClick_initializers, void 0));
        /**
         * When set it closes the opened list when registering a click outside the list.
         * @attribute
         */
        get closeOnOutsideClick() { return this.#closeOnOutsideClick_accessor_storage; }
        set closeOnOutsideClick(value) { this.#closeOnOutsideClick_accessor_storage = value; }
        #matchTriggerWidth_accessor_storage = (__runInitializers(this, _closeOnOutsideClick_extraInitializers), __runInitializers(this, _matchTriggerWidth_initializers, void 0));
        /**
         * When set it makes the drop-down to match the width of the trigger.
         * Be careful as this may crop the content when the trigger is not wide enough.
         * @attribute
         */
        get matchTriggerWidth() { return this.#matchTriggerWidth_accessor_storage; }
        set matchTriggerWidth(value) { this.#matchTriggerWidth_accessor_storage = value; }
        #closeOnTab_accessor_storage = (__runInitializers(this, _matchTriggerWidth_extraInitializers), __runInitializers(this, _closeOnTab_initializers, void 0));
        /**
         * When set it closes the drop-down when `tab` button is pressed.
         * This is not a default behavior since the drop-down content can have its own logic
         * related to tab index.
         */
        get closeOnTab() { return this.#closeOnTab_accessor_storage; }
        set closeOnTab(value) { this.#closeOnTab_accessor_storage = value; }
        /**
         * The first element located in the default slot.
         */
        get trigger() {
            const { triggers } = this;
            if (!triggers || !triggers.length) {
                return null;
            }
            const [button] = triggers;
            return button;
        }
        /**
         * The first element located in the "dropdown" slot.
         */
        get dropdown() {
            const { dropdowns } = this;
            if (!dropdowns || !dropdowns.length) {
                return null;
            }
            const [content] = dropdowns;
            return content;
        }
        #overlayPositioning_accessor_storage = (__runInitializers(this, _closeOnTab_extraInitializers), __runInitializers(this, _overlayPositioning_initializers, void 0));
        get overlayPositioning() { return this.#overlayPositioning_accessor_storage; }
        set overlayPositioning(value) { this.#overlayPositioning_accessor_storage = value; }
        constructor() {
            super();
            this.dropdownOpenHandler = this.dropdownOpenHandler.bind(this);
            this.scrollHandler = this.scrollHandler.bind(this);
            this.clickHandler = this.clickHandler.bind(this);
        }
        connectedCallback() {
            super.connectedCallback();
            window.addEventListener('dropdownopen', this.dropdownOpenHandler);
            window.addEventListener('click', this.clickHandler, { capture: true });
            ScrollHelper.addListeners(this, this.scrollHandler);
            this.setAttribute('aria-haspopup', 'menu');
            this.setAttribute('aria-expanded', 'false');
        }
        disconnectedCallback() {
            super.disconnectedCallback();
            window.removeEventListener('dropdownopen', this.dropdownOpenHandler);
            window.removeEventListener('click', this.clickHandler, { capture: true });
            ScrollHelper.removeListeners(this);
        }
        dropdownOpenHandler(e) {
            if (e.composedPath()[0] === this) {
                return;
            }
            this.close();
        }
        willUpdate(cp) {
            super.willUpdate(cp);
            if ((cp.has('noOverlap') || cp.has('verticalAlign') || cp.has('horizontalAlign') || cp.has('open')) && this.open) {
                this.computePositioning();
            }
        }
        updated(cp) {
            super.updated(cp);
            if (cp.has('open')) {
                this.toggleOpened();
                this.updateExpanded();
                // this.manageTabindex()
            }
        }
        updateExpanded() {
            this.setAttribute('aria-expanded', String(this.open));
        }
        triggerChanged() {
            const { trigger } = this;
            if (!trigger) {
                return;
            }
            this.decorateTrigger(trigger);
        }
        dropdownChanged() {
            const { dropdown } = this;
            if (!dropdown) {
                return;
            }
            this.decorateDropdown(dropdown);
        }
        /**
         * Decorates the trigger with aria attributes.
         */
        decorateTrigger(button) {
            button.setAttribute('aria-controls', this.menuId);
            button.setAttribute('tabindex', '0');
            if (button.id) {
                this.triggerId = button.id;
            }
            else {
                button.id = this.triggerId;
            }
        }
        decorateDropdown(list) {
            if (!list.hasAttribute('role')) {
                list.setAttribute('role', 'menu');
            }
            if (!list.hasAttribute('aria-labelledby')) {
                list.setAttribute('aria-labelledby', this.triggerId);
            }
            list.setAttribute('tabindex', '-1');
            list.id = this.menuId;
        }
        triggerClickHandler() {
            this.open = !this.open;
            this.notifyOpen();
        }
        triggerKeyDownHandler(e) {
            if (['Enter', 'Space', 'ArrowDown', 'ArrowUp'].includes(e.code)) {
                e.preventDefault();
                this.open = true;
                this.notifyOpen();
            }
        }
        contentKeyDownHandler(e) {
            if (e.defaultPrevented) {
                return;
            }
            if (e.code === 'Escape') {
                this.close();
            }
            else if (e.code === 'Tab') {
                if (this.closeOnTab) {
                    this.close();
                }
            }
            else if (['Enter', 'Space'].includes(e.code)) {
                this.activate(e);
            }
        }
        contentClickHandler(e) {
            this.activate(e);
        }
        close() {
            this.open = false;
            this.notifyOpen();
        }
        contentCloseHandler(e) {
            e.stopPropagation();
            this.close();
        }
        /**
         * Since the container for the dropdown content is positioned as fixed
         * we need to position the dropdown according to the configured rules.
         */
        computePositioning() {
            const { trigger, dropdown } = this;
            if (!trigger || !dropdown) {
                return;
            }
            this.overlayPositioning = positionOverlay(dropdown, trigger, {
                horizontal: this.horizontalAlign,
                vertical: this.verticalAlign,
                noOverlap: this.noOverlap,
                constrain: true,
                constrainPaddingY: 20,
                matchAnchorWidth: this.matchTriggerWidth,
            });
        }
        scrollHandler() {
            if (this.open) {
                this.computePositioning();
            }
        }
        clickHandler(e) {
            if (!this.open || !this.closeOnOutsideClick) {
                return;
            }
            const inside = e.composedPath().some((i) => i === this);
            if (inside) {
                return;
            }
            this.close();
        }
        toggleOpened() {
            const { open } = this;
            if (open) {
                this.handleOpened();
            }
            else {
                this.handleClosed();
            }
        }
        /**
         * When set, it is the last active child of the dropdown
         * before the tabindex was removed from it. It is used to restore focus
         * when the dropdown is re-opened.
         */
        lastActiveChild = __runInitializers(this, _overlayPositioning_extraInitializers);
        /**
         * When the list is closed, the tabindex of the dropdown is removed to prevent
         * it from being focused while invisible.
         */
        manageTabindex() {
            const { dropdown } = this;
            if (!dropdown) {
                return;
            }
            if (this.open) {
                if (this.lastActiveChild) {
                    this.lastActiveChild.setAttribute('tabindex', '0');
                    this.lastActiveChild.focus();
                    this.lastActiveChild = undefined;
                }
                else {
                    // If no last active child, focus the dropdown itself
                    dropdown.setAttribute('tabindex', '0');
                }
            }
            else {
                // const activeChild = dropdown.querySelector(':focus') || dropdown
                // this.lastActiveChild = activeChild as HTMLElement
                // activeChild.removeAttribute('tabindex')
                // this.trigger?.focus()
            }
        }
        handleOpened() {
            const { trigger, dropdown } = this;
            if (trigger) {
                trigger.removeAttribute('tabindex');
            }
            if (this.lastActiveChild) {
                this.lastActiveChild.setAttribute('tabindex', '0');
                this.lastActiveChild.focus();
                this.lastActiveChild = undefined;
            }
            else if (dropdown) {
                dropdown.setAttribute('tabindex', '0');
                dropdown.focus();
            }
        }
        handleClosed() {
            const { trigger, dropdown } = this;
            if (trigger) {
                trigger.setAttribute('tabindex', '0');
                trigger.focus();
            }
            if (dropdown) {
                const activeChild = dropdown.querySelector('[tabindex="0"]');
                if (activeChild) {
                    this.lastActiveChild = activeChild;
                    activeChild.removeAttribute('tabindex');
                }
                dropdown.removeAttribute('tabindex');
            }
        }
        activate(e) {
            if (e.defaultPrevented) {
                return;
            }
            const path = e.composedPath();
            let item;
            while (!item) {
                const next = path.shift();
                if (next === this) {
                    break;
                }
                if (next.nodeType !== Node.ELEMENT_NODE) {
                    continue;
                }
                if (itemRole.includes(next.getAttribute('role') || '')) {
                    item = next;
                }
            }
            if (!item) {
                return;
            }
            const event = new CustomEvent('select', {
                cancelable: true,
                // composed: true,
                detail: {
                    item,
                },
            });
            this.dispatchEvent(event);
            if (event.defaultPrevented) {
                return;
            }
            this.close();
        }
        notifyOpen() {
            this.dispatchEvent(new Event('open'));
            if (this.open) {
                this.dispatchEvent(new Event('dropdownopen', {
                    bubbles: true,
                    composed: true,
                    cancelable: true,
                }));
            }
        }
        render() {
            const classes = {
                container: true,
                open: this.open,
            };
            return html ` <div class="${classMap(classes)}">${this.renderTrigger()} ${this.renderContent()}</div> `;
        }
        renderTrigger() {
            return html `
      <div class="trigger" @click="${this.triggerClickHandler}" @keydown="${this.triggerKeyDownHandler}">
        <slot @slotchange="${this.triggerChanged}"></slot>
      </div>
    `;
        }
        renderContent() {
            const contentStyles = this.overlayPositioning || {};
            return html `
      <div class="content" style="${styleMap(contentStyles)}">
        <slot
          name="dropdown"
          @slotchange="${this.dropdownChanged}"
          @keydown="${this.contentKeyDownHandler}"
          @click="${this.contentClickHandler}"
          @close="${this.contentCloseHandler}"
        ></slot>
      </div>
    `;
        }
    };
})();
/**
 * An overlay list rendered over a button.
 *
 * Examples:
 *
 * <ui-dropdown-list>
 *  <ui-button color="filled">Click me</ui-button>
 *  <ui-list slot="dropdown" role="menu">
 *    <ui-list-item role="menuitem">Item 1</ui-list-item>
 *    <ui-list-item role="menuitem">Item 2</ui-list-item>
 *  </ui-list>
 * </ui-dropdown-list>
 *
 * @slot - The default slot for the dropdown trigger (button)
 * @slot dropdown - The slot for the list.
 * @fires select - Custom event with the selected item on the `detail.item` when the user selected an item.
 *                 When the event is cancelled then there's no side effects (closing the dropdown)
 * @fires dropdownopen - An event informing other dropdowns that this one was opened and other should close.
 * @fires open - An event dispatched when the open state change through a user interaction
 */
export default UiDropdownList;
//# sourceMappingURL=UiDropdownList.js.map