/**
 * Copyright Aquera Inc 2026
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */
var NileContextMenu_1;
import { __decorate } from "tslib";
import { html } from 'lit';
import { customElement, property, query, state } from 'lit/decorators.js';
import { styles } from './nile-context-menu.css';
import NileElement from '../internal/nile-element';
import '../nile-floating-panel';
import '../nile-context-menu-group';
import '../nile-context-menu-item';
import '../nile-context-submenu';
const ITEM_TAG = 'nile-context-menu-item';
const SUBMENU_TAG = 'nile-context-submenu';
const MENU_CONTAINER_CLASS = 'nile-context-menu__menu';
const OBSERVED_ITEM_ATTRS = ['value', 'disabled'];
let proxyIdSeq = 0;
/**
 * Nile context-menu component.
 *
 * @tag nile-context-menu
 *
 * @slot Default menu content.
 *
 * @event nile-change Fired on menu lifecycle and selection. `detail.type` is one of
 *   `'open'`, `'click'`, or `'close'`; remaining fields depend on the type.
 */
let NileContextMenu = NileContextMenu_1 = class NileContextMenu extends NileElement {
    constructor() {
        super(...arguments);
        this.for = '';
        this.trigger = 'right';
        this.skipOn = '';
        this.zIndex = 9999;
        this.items = [];
        this._items = [];
        this._open = false;
        this._openContext = null;
        this._targetEl = null;
        this._previouslyFocused = null;
        this._proxyId = `nile-context-menu-anchor-${++proxyIdSeq}`;
        this._wasDataMode = false;
        this._menuContainerRef = null;
        this._onKeydown = (e) => {
            if (!this._open)
                return;
            switch (e.key) {
                case 'Escape':
                    e.preventDefault();
                    this.close('escape');
                    return;
                case 'Tab':
                    this.close('programmatic');
                    return;
                case 'ArrowDown':
                    e.preventDefault();
                    this._moveFocus(1);
                    return;
                case 'ArrowUp':
                    e.preventDefault();
                    this._moveFocus(-1);
                    return;
                case 'ArrowRight': {
                    const { item } = this._getFocusedItemAnyLevel();
                    const sub = item?.querySelector(`:scope > ${SUBMENU_TAG}`);
                    if (!sub)
                        return;
                    e.preventDefault();
                    sub.openSubmenu?.();
                    requestAnimationFrame(() => {
                        sub.focusFirstItem?.();
                    });
                    return;
                }
                case 'ArrowLeft': {
                    const { item, container } = this._getFocusedItemAnyLevel();
                    if (!item || !container)
                        return;
                    const sub = this._findSubmenuOwning(container);
                    if (!sub)
                        return;
                    e.preventDefault();
                    sub.closeSubmenu?.();
                    sub.parentItem?.focus();
                    return;
                }
                case 'Enter':
                case ' ': {
                    e.preventDefault();
                    const { item } = this._getFocusedItemAnyLevel();
                    if (!item || item.disabled)
                        return;
                    const sub = item.querySelector(`:scope > ${SUBMENU_TAG}`);
                    if (sub) {
                        sub.openSubmenu?.();
                        requestAnimationFrame(() => {
                            const panel = sub.shadowRoot?.querySelector(`.${MENU_CONTAINER_CLASS}`);
                            const first = panel
                                ? this._enabledItemsIn(panel)[0]
                                : undefined;
                            first?.focus();
                        });
                        return;
                    }
                    this._selectItem(item);
                    return;
                }
            }
        };
        this._onScroll = (e) => {
            if (!this._open)
                return;
            const path = e.composedPath();
            // Scroll inside the root menu container?
            if (this._menuContainerRef && path.includes(this._menuContainerRef))
                return;
            // Scroll inside any descendant submenu's portaled panel?
            for (const node of path) {
                if (node instanceof HTMLElement && node.classList?.contains(MENU_CONTAINER_CLASS)) {
                    return;
                }
            }
            this.close('programmatic');
        };
        this._onOutsidePointer = (e) => {
            if (!this._open)
                return;
            const path = e.composedPath();
            if (this._menuContainerRef && path.includes(this._menuContainerRef))
                return;
            for (const node of path) {
                if (node instanceof HTMLElement &&
                    node.classList?.contains(MENU_CONTAINER_CLASS)) {
                    return;
                }
            }
            this.close('outside-click');
        };
        this._onDescendantSelect = (e) => {
            if (e.detail?.type !== 'click')
                return;
            this.close('select');
        };
        this._onPanelShown = (e) => {
            e.stopPropagation();
        };
        this._onMenuMouseOver = (e) => {
            const item = e
                .composedPath()
                .find(node => node instanceof HTMLElement &&
                node.tagName.toLowerCase() === ITEM_TAG);
            if (!item || item.disabled)
                return;
            item.focus();
        };
        this._onMenuClick = (e) => {
            const item = e
                .composedPath()
                .find(node => node instanceof HTMLElement &&
                node.tagName.toLowerCase() === ITEM_TAG);
            if (!item || item.disabled)
                return;
            if (item.querySelector(`:scope > ${SUBMENU_TAG}`))
                return;
            this._selectItem(item);
        };
    }
    static get styles() {
        return [styles];
    }
    get _isDataMode() {
        return Array.isArray(this.items) && this.items.length > 0;
    }
    connectedCallback() {
        super.connectedCallback();
        this._ensureProxy();
        this._resolveTarget();
        this._attachTriggers();
        this._lightObserver = new MutationObserver(() => {
            if (this._isDataMode)
                return;
            this._relocateLightChildren();
        });
        this._lightObserver.observe(this, { childList: true });
    }
    updated(changed) {
        super.updated(changed);
        if ((changed.has('for') || changed.has('trigger') || changed.has('skipOn')) && this._open) {
            this.close('programmatic');
        }
        if (changed.has('for')) {
            this._resolveTarget();
            this._attachTriggers();
        }
        else if (changed.has('trigger') || changed.has('skipOn')) {
            this._attachTriggers();
        }
        if (changed.has('items') && this._menuContainerRef) {
            if (this._isDataMode) {
                this._renderDataItems();
                this._wasDataMode = true;
            }
            else if (this._wasDataMode) {
                this._renderDataItems();
                this._wasDataMode = false;
            }
        }
    }
    _resolveTarget() {
        const value = this.for?.trim();
        if (!value) {
            this._targetEl = null;
            return;
        }
        const root = this.getRootNode() ?? document;
        if (/^[#.\[:]/.test(value)) {
            this._targetEl = root.querySelector(value);
        }
        else if ('getElementById' in root) {
            this._targetEl = root.getElementById(value);
        }
        else {
            this._targetEl = document.getElementById(value);
        }
    }
    get targetElement() {
        return this._targetEl;
    }
    _attachTriggers() {
        this._detachTriggers?.();
        this._detachTriggers = undefined;
        const trigger = this.trigger;
        if (trigger === 'manual')
            return;
        const cleanups = [];
        if (trigger === 'global') {
            const skipSelector = this.skipOn?.trim() ?? '';
            const isInteractive = (el) => {
                if (!el || !skipSelector)
                    return false;
                return !!el.closest(skipSelector);
            };
            const handler = (e) => {
                const me = e;
                if (isInteractive(me.target))
                    return;
                me.preventDefault();
                if (this._open)
                    return;
                this.open({
                    x: me.clientX,
                    y: me.clientY,
                    target: me.target ?? undefined,
                    originalEvent: me,
                });
            };
            window.addEventListener('contextmenu', handler, true);
            cleanups.push(() => window.removeEventListener('contextmenu', handler, true));
            this._detachTriggers = () => cleanups.forEach(fn => fn());
            return;
        }
        const target = this._targetEl;
        if (!target)
            return;
        const wantsContext = trigger === 'right' || trigger === 'both';
        const wantsClick = trigger === 'left' || trigger === 'both';
        if (wantsContext) {
            const handler = (e) => {
                const me = e;
                me.preventDefault();
                this.open({
                    x: me.clientX,
                    y: me.clientY,
                    target,
                    originalEvent: me,
                });
            };
            target.addEventListener('contextmenu', handler);
            cleanups.push(() => target.removeEventListener('contextmenu', handler));
        }
        if (wantsClick) {
            const handler = (e) => {
                const me = e;
                this.open({
                    x: me.clientX,
                    y: me.clientY,
                    target,
                    originalEvent: me,
                });
            };
            target.addEventListener('click', handler);
            cleanups.push(() => target.removeEventListener('click', handler));
        }
        if (cleanups.length === 0)
            return;
        this._detachTriggers = () => cleanups.forEach(fn => fn());
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        NileContextMenu_1._openInstances.delete(this);
        this._open = false;
        this._detachTriggers?.();
        this._detachTriggers = undefined;
        this._lightObserver?.disconnect();
        this._lightObserver = undefined;
        this._menuObserver?.disconnect();
        this._menuObserver = undefined;
        this._menuContainerRef?.removeEventListener('click', this._onMenuClick);
        this._menuContainerRef?.removeEventListener('mouseover', this._onMenuMouseOver);
        this._menuContainerRef?.removeEventListener('nile-change', this._onDescendantSelect);
        document.removeEventListener('pointerdown', this._onOutsidePointer, true);
        document.removeEventListener('keydown', this._onKeydown, true);
        window.removeEventListener('scroll', this._onScroll, true);
        this._proxyEl?.remove();
        this._proxyEl = undefined;
    }
    firstUpdated() {
        this._menuContainerRef = this.renderRoot.querySelector('.nile-context-menu__menu');
        if (this._isDataMode) {
            this._renderDataItems();
            this._wasDataMode = true;
        }
        else {
            this._relocateLightChildren();
        }
        if (this._menuContainerRef) {
            this._menuObserver = new MutationObserver(() => this._parseChildren());
            this._menuObserver.observe(this._menuContainerRef, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: OBSERVED_ITEM_ATTRS,
            });
            this._menuContainerRef.addEventListener('click', this._onMenuClick);
            this._menuContainerRef.addEventListener('mouseover', this._onMenuMouseOver);
            this._menuContainerRef.addEventListener('nile-change', this._onDescendantSelect);
        }
        this._parseChildren();
    }
    _ensureProxy() {
        if (this._proxyEl)
            return;
        const el = document.createElement('div');
        el.id = this._proxyId;
        el.setAttribute('aria-hidden', 'true');
        el.style.cssText =
            'position: fixed; top: 0; left: 0; width: 0; height: 0; pointer-events: none;';
        document.body.appendChild(el);
        this._proxyEl = el;
    }
    _positionProxyAt(x, y) {
        if (!this._proxyEl)
            return;
        this._proxyEl.style.left = `${x}px`;
        this._proxyEl.style.top = `${y}px`;
    }
    _relocateLightChildren() {
        if (!this._menuContainerRef)
            return;
        const kids = Array.from(this.children);
        if (kids.length === 0)
            return;
        for (const kid of kids) {
            this._menuContainerRef.appendChild(kid);
        }
    }
    _renderDataItems() {
        const container = this._menuContainerRef;
        if (!container)
            return;
        while (container.firstChild)
            container.removeChild(container.firstChild);
        for (const entry of this.items) {
            const node = this._createDataNode(entry);
            if (node)
                container.appendChild(node);
        }
    }
    _createDataNode(entry) {
        if (entry.type === 'group') {
            const group = document.createElement('nile-context-menu-group');
            if (entry.name)
                group.label = entry.name;
            for (const item of entry.data ?? []) {
                group.appendChild(this._createItemNode(item));
            }
            return group;
        }
        return null;
    }
    _createItemNode(data) {
        const item = document.createElement('nile-context-menu-item');
        if (data.id)
            item.id = data.id;
        item.value = data.value ?? data.id ?? '';
        if (data.disabled)
            item.disabled = true;
        if (data.icon) {
            const glyph = document.createElement('nile-glyph');
            glyph.setAttribute('slot', 'icon');
            glyph.setAttribute('name', data.icon);
            if (data.iconSet)
                glyph.setAttribute('set', data.iconSet);
            if (data.iconSize)
                glyph.setAttribute('size', data.iconSize);
            if (data.iconMethod)
                glyph.setAttribute('method', data.iconMethod);
            if (data.iconColor)
                glyph.setAttribute('color', data.iconColor);
            item.appendChild(glyph);
        }
        item.appendChild(document.createTextNode(data.label));
        if (data.submenu && data.submenu.length > 0) {
            const submenu = document.createElement('nile-context-submenu');
            for (const entry of data.submenu) {
                const node = this._createDataNode(entry);
                if (node)
                    submenu.appendChild(node);
            }
            item.appendChild(submenu);
        }
        return item;
    }
    _parseChildren() {
        const root = this._menuContainerRef ?? this;
        const all = Array.from(root.querySelectorAll(ITEM_TAG));
        this._items = all.filter(item => !this._isInsideDescendantSubmenu(item, root));
    }
    _isInsideDescendantSubmenu(item, container) {
        let cur = item.parentElement;
        while (cur && cur !== container) {
            if (cur.tagName.toLowerCase() === SUBMENU_TAG)
                return true;
            cur = cur.parentElement;
        }
        return false;
    }
    get menuItems() {
        return this._items;
    }
    getItemByValue(value) {
        return this._items.find(item => item.value === value);
    }
    get isOpen() {
        return this._open;
    }
    open(options) {
        if (this._open)
            return;
        for (const other of NileContextMenu_1._openInstances) {
            if (other !== this)
                other.close('programmatic');
        }
        this._previouslyFocused = document.activeElement;
        this._openContext = {
            x: options.x,
            y: options.y,
            target: options.target ?? null,
            originalEvent: options.originalEvent ?? null,
        };
        this._ensureProxy();
        this._positionProxyAt(options.x, options.y);
        this._open = true;
        NileContextMenu_1._openInstances.add(this);
        document.addEventListener('pointerdown', this._onOutsidePointer, true);
        document.addEventListener('keydown', this._onKeydown, true);
        requestAnimationFrame(() => {
            if (this._open)
                window.addEventListener('scroll', this._onScroll, true);
        });
        this.emit('nile-change', { type: 'open', ...this._openContext });
    }
    close(reason = 'programmatic') {
        if (!this._open)
            return;
        this._open = false;
        this._openContext = null;
        NileContextMenu_1._openInstances.delete(this);
        const subs = this._menuContainerRef?.querySelectorAll(SUBMENU_TAG);
        subs?.forEach(s => s.closeSubmenu?.());
        document.removeEventListener('pointerdown', this._onOutsidePointer, true);
        document.removeEventListener('keydown', this._onKeydown, true);
        window.removeEventListener('scroll', this._onScroll, true);
        const active = document.activeElement;
        const focusInMenu = active === this ||
            this._items.some(item => item === active || item.shadowRoot?.contains(active));
        if (focusInMenu) {
            this._previouslyFocused?.focus?.();
        }
        this._previouslyFocused = null;
        this.emit('nile-change', { type: 'close', reason });
    }
    _enabledItems() {
        return this._items.filter(item => !item.disabled);
    }
    _getFocusedItem() {
        const active = document.activeElement;
        for (const item of this._items) {
            if (item === active || item.shadowRoot?.contains(active))
                return item;
        }
        return null;
    }
    _getFocusedItemAnyLevel() {
        let node = document.activeElement;
        while (node && node.tagName?.toLowerCase() !== ITEM_TAG) {
            const root = node.getRootNode();
            node = root instanceof ShadowRoot ? root.host : node.parentElement;
        }
        if (!node)
            return { item: null, container: null };
        const container = node.closest(`.${MENU_CONTAINER_CLASS}`);
        return { item: node, container };
    }
    _enabledItemsIn(container) {
        const all = Array.from(container.querySelectorAll(ITEM_TAG));
        return all.filter(item => {
            if (item.disabled)
                return false;
            return !this._isInsideDescendantSubmenu(item, container);
        });
    }
    _focusFirstEnabled() {
        const enabled = this._enabledItems();
        enabled[0]?.focus();
    }
    _moveFocus(delta) {
        const { item: focused, container } = this._getFocusedItemAnyLevel();
        const root = container ?? this._menuContainerRef ?? this;
        const enabled = this._enabledItemsIn(root);
        if (enabled.length === 0)
            return;
        const current = focused ? enabled.indexOf(focused) : -1;
        const next = current + delta;
        const wrapped = ((next % enabled.length) + enabled.length) % enabled.length;
        enabled[wrapped].focus();
    }
    _findSubmenuOwning(container) {
        const customElements = window.customElements;
        const ctor = customElements.get(SUBMENU_TAG);
        return ctor?.findByContainer?.(container) ?? null;
    }
    _stop(e) {
        e.stopPropagation();
    }
    _selectItem(item) {
        const detail = {
            id: item.id,
            value: item.value,
            name: (item.textContent ?? '').trim(),
            target: this._openContext?.target ?? null,
            originalEvent: this._openContext?.originalEvent ?? null,
        };
        try {
            item.onSelect?.(detail);
        }
        catch (err) {
            console.error('[nile-context-menu] onSelect callback threw:', err);
        }
        this.emit('nile-change', { type: 'click', ...detail });
        this.close('select');
    }
    render() {
        return html `
      <nile-floating-panel
        for=${this._proxyId}
        trigger="manual"
        placement="bottom-start"
        panelClass="nile-context-menu-panel"
        .zIndex=${this.zIndex}
        ?open=${this._open}
        .interactive=${true}
        .hideOnClick=${false}
        .closeOnEscape=${false}
        .arrow=${'none'}
        .distance=${0}
        @nile-init=${this._stop}
        @nile-destroy=${this._stop}
        @nile-show=${this._stop}
        @nile-hide=${this._stop}
        @nile-after-show=${this._onPanelShown}
        @nile-after-hide=${this._stop}
        @nile-toggle=${this._stop}
        @nile-visibility-change=${this._stop}
      >
        <div class="nile-context-menu__menu" role="menu"></div>
      </nile-floating-panel>
    `;
    }
};
NileContextMenu._openInstances = new Set();
__decorate([
    property({ attribute: true, type: String, reflect: true })
], NileContextMenu.prototype, "for", void 0);
__decorate([
    property({ attribute: true, type: String, reflect: true })
], NileContextMenu.prototype, "trigger", void 0);
__decorate([
    property({ attribute: true, type: String, reflect: true })
], NileContextMenu.prototype, "skipOn", void 0);
__decorate([
    property({ attribute: true, type: Number, reflect: true })
], NileContextMenu.prototype, "zIndex", void 0);
__decorate([
    property({ attribute: false })
], NileContextMenu.prototype, "items", void 0);
__decorate([
    query('nile-floating-panel')
], NileContextMenu.prototype, "_floatingPanel", void 0);
__decorate([
    state()
], NileContextMenu.prototype, "_items", void 0);
__decorate([
    state()
], NileContextMenu.prototype, "_open", void 0);
NileContextMenu = NileContextMenu_1 = __decorate([
    customElement('nile-context-menu')
], NileContextMenu);
export { NileContextMenu };
export default NileContextMenu;
//# sourceMappingURL=nile-context-menu.js.map