/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { proxyCustomElement, HTMLElement, createEvent, Build, Host } from '@stencil/core/internal/client';
import { f as focusInContainer } from './active-element.js';
import { P as Popover } from './popover.js';
import { a as getNearestTabbableElement } from './tabbable.js';
import { t as transformTag, h } from './renderer.js';

const dropdownCss = ":host,*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}@media (prefers-reduced-motion: reduce){:host,*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}:host,*,*::before,*::after{box-sizing:border-box}[hidden]{display:none !important}@media (prefers-reduced-motion: reduce){:host,*,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important}}:host{--width:auto;--border:none;--border-radius:0px;--background:var(--nano-color-neutral-75);--padding:0 0;--overflow:auto;--dropdown-z-index:var(--nano-z-index-dropdown);--min-width:0;--max-height:none;--box-shadow:var(--nano-shadow-l2);--menu-height:calc(100vh - 200px);display:inline-block}.dropdown{position:relative}.dropdown__trigger{display:block}.dropdown__positioner{position:absolute;z-index:var(--dropdown-z-index);min-inline-size:var(--min-width);border:0;background:none;padding:0;overflow:visible;color:unset}.dropdown__positioner::backdrop{display:none}.dropdown__positioner[popover],.dropdown__positioner:popover-open{inset:unset}@media (width <= 35.9375em){.dropdown__positioner{z-index:100}}.dropdown__panel{padding:var(--padding);background:var(--background);min-inline-size:var(--min-width);inline-size:var(--width);border:var(--border);border-radius:var(--border-radius);color:currentcolor;box-shadow:var(--box-shadow);opacity:0;transition:var(--nano-transition-x-fast) ease opacity, var(--nano-transition-x-fast) ease transform, var(--nano-transition-fast) ease min-block-size;min-block-size:20px;overflow:var(--overflow);max-block-size:var(--max-height);box-sizing:content-box !important}.dropdown__panel.loading{overflow-y:hidden}.dropdown__panel.top{transform:translateY(-20px) translateZ(0)}.dropdown__panel.bottom{transform:translateY(20px) translateZ(0)}.dropdown__positioner.popover-visible .dropdown__panel{opacity:1;transform:translateY(0) translateZ(0)}.dropdown__panel ::slotted(.nano-menu){max-block-size:var(--menu-height)}.dropdown__accessible-title{clip-path:inset(50%);block-size:1px;overflow:hidden;position:absolute;white-space:nowrap;inline-size:1px}";

let dropDownIds = 0;
const Dropdown = /*@__PURE__*/ proxyCustomElement(class Dropdown extends HTMLElement {
    constructor() {
        super();
        this.__registerHost();
        this.__attachShadow();
        this.nanoShow = createEvent(this, "nanoShow", 7);
        this.nanoAfterShow = createEvent(this, "nanoAfterShow", 7);
        this.nanoHide = createEvent(this, "nanoHide", 7);
        this.nanoAfterHide = createEvent(this, "nanoAfterHide", 7);
    }
    dropdownId = `dropdown-${dropDownIds++}`;
    labelId = this.dropdownId + '-title';
    ignoreOpenWatcher = false;
    panel;
    popover;
    trigger;
    positioner;
    accessibleTrigger;
    didLoad = false;
    focusFirstEle = true;
    get menu() {
        return this.host.querySelector(transformTag('nano-menu'));
    }
    get host() { return this; }
    /** Determines if the dropdown should open automatically when the trigger is clicked */
    autoOpen = true;
    /** Indicates whether or not the dropdown is open. You can use this in lieu of the show/hide methods. */
    open = false;
    handleOpenChange() {
        if (this.ignoreOpenWatcher)
            return;
        if (this.open)
            this.show();
        else
            this.hide();
        if (this.accessibleTrigger) {
            this.accessibleTrigger.setAttribute('aria-expanded', this.open.toString());
            return;
        }
        this.updateAccessibleTrigger();
    }
    /**
     * Determines whether the dropdown should hide when a menu item is selected.
     */
    closeOnSelect = true;
    /**
     * The dropdown will close when the user interacts outside of this element (e.g. clicking).
     */
    containingElement;
    /**
     * If you don't want to use the trigger slot, you can use this to 'tether' the dropdown to another element.
     * And you will still need to control when the component is opened (using show() / hide() or the `open` prop)
     */
    tetherTo = null;
    handleTetherToChange() {
        this.createPopover();
        this.updateAccessibleTrigger();
    }
    /**
     * The preferred placement of the dropdown panel. Note that the actual placement may vary as needed to keep the panel
     * inside of the viewport.
     */
    placement = 'bottom-start';
    /**
     * The distance in pixels from which to offset the panel away from its trigger.
     */
    distance = 5;
    /**
     * The distance in pixels from which to offset the panel along its trigger.
     */
    skidding = 0;
    /**
     * Enable this option to prevent the panel from being clipped when the component is placed inside a container with
     * `overflow: auto|scroll`.
     */
    hoist = false;
    handlePopoverOptionsChange() {
        if (!this.popover)
            return;
        this.popover.setOptions({
            strategy: this.hoist ? 'fixed' : 'absolute',
            placement: this.placement,
            skidding: this.skidding,
            distance: this.distance,
        });
    }
    /**
     * Title used to describe the dropdown content for accessibility
     */
    dialogTitle;
    // Events
    /**
     * Emitted when the dropdown opens. Calling `event.preventDefault()` will prevent it from being opened.
     */
    nanoShow;
    /**
     * Emitted after the dropdown opens and all transitions are complete.
     */
    nanoAfterShow;
    /**
     * Emitted when the dropdown closes. Calling `event.preventDefault()` will prevent it from being closed.
     */
    nanoHide;
    /**
     * Emitted after the dropdown closes and all transitions are complete.
     */
    nanoAfterHide;
    // this is a massive hack to make stencil expose the `EventEmitter` type in the final build
    // because the `Dropdown` class is used as a type in `nano-datalist`
    /** @internal */
    eventType;
    // Listeners
    secondaryOpen(ev) {
        if (!ev.detail.secondaryMenu)
            return;
        ev.stopPropagation();
        this.panel.classList.add('loading');
        this.panel.addEventListener('transitionend', () => {
            this.panel.classList.remove('loading');
        }, { once: true });
        this.panel.style.minHeight =
            ev.detail.secondaryMenu.scrollHeight + 'px';
    }
    secondaryClose(ev) {
        if (!ev.detail.secondaryMenu)
            return;
        ev.stopPropagation();
        if (!ev.target.parentElement)
            return;
        this.panel.style.minHeight =
            ev.target.parentElement.scrollHeight + 'px';
    }
    handlePanelSelect(event) {
        const target = event.target;
        // Hide the dropdown when a menu item is selected
        if (this.closeOnSelect &&
            target.tagName.toLowerCase() === transformTag('nano-menu'))
            this.hide();
    }
    // Methods
    /** Shows the dropdown panel */
    async show() {
        this.ignoreOpenWatcher = true;
        this.open = true;
        const nanoShow = this.nanoShow.emit();
        if (nanoShow.defaultPrevented) {
            this.open = false;
            this.ignoreOpenWatcher = false;
            return;
        }
        if (typeof this.positioner?.showPopover === 'function') {
            this.hoist = true;
            this.popover.show();
            this.positioner.showPopover();
        }
        else {
            this.popover.show();
        }
        this.ignoreOpenWatcher = false;
        document.addEventListener('mousedown', this.handleDocumentMouseDown);
        document.addEventListener('keydown', this.handleDocumentKeyDown);
    }
    /** Hides the dropdown panel */
    async hide() {
        this.ignoreOpenWatcher = true;
        this.open = false;
        const nanoHide = this.nanoHide.emit();
        if (nanoHide.defaultPrevented) {
            this.open = true;
            this.ignoreOpenWatcher = false;
            return;
        }
        if (this.popover)
            this.popover.hide();
        this.ignoreOpenWatcher = false;
        document.removeEventListener('mousedown', this.handleDocumentMouseDown);
        document.removeEventListener('keydown', this.handleDocumentKeyDown);
    }
    // Private methods
    afterHide() {
        if (typeof this.positioner?.hidePopover === 'function') {
            this.positioner.hidePopover();
        }
        this.panel.scrollTop = 0;
        if (this.accessibleTrigger &&
            focusInContainer(this.containingElement, this.trigger)) {
            this.accessibleTrigger.focus();
        }
    }
    togglePanel = () => {
        if (!this.autoOpen)
            return;
        if (this.open)
            this.hide();
        else
            this.show();
    };
    // Slotted triggers can be arbitrary content, but we need to link them to the dropdown panel with `aria-haspopup` and
    // `aria-expanded`. These must be applied to the "accessible trigger" (the tabbable portion of the trigger element
    // that gets slotted in) so screen readers will understand them. The accessible trigger could be the slotted element,
    updateAccessibleTrigger() {
        if (!this.didLoad)
            return;
        let accessibleTrigger;
        this.accessibleTrigger = null;
        if (!this.tetherTo) {
            const assignedElements = Array.from(this.host.querySelectorAll('[slot="trigger"]'));
            accessibleTrigger = assignedElements.map(getNearestTabbableElement)[0];
        }
        else {
            accessibleTrigger = getNearestTabbableElement(this.tetherTo);
        }
        if (accessibleTrigger && !accessibleTrigger.getAttribute('aria-haspopup')) {
            accessibleTrigger.setAttribute('aria-haspopup', 'true');
            accessibleTrigger.setAttribute('aria-expanded', this.open ? 'true' : 'false');
            this.accessibleTrigger = accessibleTrigger;
        }
    }
    // Handlers
    handleDocumentKeyDown(event) {
        // Close when escape is pressed
        if (event.key === 'Escape') {
            this.hide();
            return;
        }
        // Close when tabbing results in the focus leaving the dropdown element
        if (event.key === 'Tab') {
            setTimeout(() => {
                if (!focusInContainer(this.containingElement, this.trigger, event)) {
                    this.hide();
                    return;
                }
            });
        }
        else {
            // If a menu is present, focus on it when certain keys are pressed
            if (this.menu &&
                ((this.placement.match(/bottom|top/) &&
                    ['ArrowDown', 'ArrowUp'].includes(event.key)) ||
                    (this.placement.match(/right|left/) &&
                        ['ArrowRight', 'ArrowLeft'].includes(event.key)))) {
                // must have menu item, must have pressed down, must be open and must not have focus within dd
                if (!this.open ||
                    focusInContainer(this.containingElement, this.trigger, event)) {
                    if (this.containingElement.tagName === 'NANO-DROPDOWN' &&
                        this.containingElement.placement.match(/right|left/) &&
                        this.trigger.getRootNode()?.host?.tagName ===
                            'NANO-DROPDOWN') {
                        // if the dropdown is inside another dropdown, we need to close the inner one
                        this.hide();
                        return;
                    }
                    return;
                }
                event.preventDefault();
                this.menu.setFocus();
                this.focusFirstEle = true;
                return;
            }
            // All other keys focus the menu and pass the event through to menu (necessary for type-to-search to work)
            const ePath = event.composedPath();
            if (this.menu && ePath.length && !ePath.find((el) => el !== this.menu)) {
                this.menu.dispatchEvent(new KeyboardEvent(event.type, event));
                return;
            }
        }
    }
    handleDocumentMouseDown(event) {
        // Close when clicking outside of the close element
        if (!focusInContainer(this.containingElement, this.trigger, event)) {
            this.hide();
            return;
        }
    }
    handleTriggerKeyDown = (event) => {
        // Open the panel when pressing down or up while focused on the trigger
        if ((this.placement.match(/bottom|top/) &&
            ['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(event.key)) ||
            (this.placement.match(/right|left/) &&
                ['ArrowRight', 'ArrowLeft', ' ', 'Enter'].includes(event.key))) {
            this.show();
            event.preventDefault();
            event.stopPropagation();
            if (this.menu) {
                this.menu.setFocus();
                this.focusFirstEle = true;
            }
        }
    };
    handleTriggerSlotChange = () => {
        this.updateAccessibleTrigger();
    };
    createPopover() {
        if ((!this.tetherTo && !this.trigger) || !this.positioner)
            return;
        if (this.popover) {
            if (this.open)
                this.hide();
            this.popover.destroy();
            this.popover = null;
        }
        this.popover = new Popover(this.tetherTo || this.trigger, this.positioner, {
            strategy: this.hoist ? 'fixed' : 'absolute',
            placement: this.placement,
            distance: this.distance,
            skidding: this.skidding,
            transitionElement: this.panel,
            onAfterHide: () => {
                this.nanoAfterHide.emit();
            },
            onAfterShow: () => {
                this.nanoAfterShow.emit();
            },
            onTransitionEnd: () => {
                if (!this.open) {
                    this.afterHide();
                }
                else if (this.menu) {
                    this.menu.showActiveElement();
                    if (this.focusFirstEle) {
                        this.menu.setFocus();
                        this.focusFirstEle = false;
                    }
                }
            },
        });
        // Show on init if open
        if (this.open) {
            this.show();
        }
    }
    isOverflowing() {
        if (Build.isServer)
            return false;
        else
            return ['auto', 'scroll'].includes(window.getComputedStyle(this.host).getPropertyValue('--overflow'));
    }
    // Stencil hooks
    connectedCallback() {
        this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
        this.handleDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
        if (!this.containingElement)
            this.containingElement = this.host;
    }
    componentDidLoad() {
        this.didLoad = true;
        this.createPopover();
        setTimeout(() => this.updateAccessibleTrigger(), 100);
    }
    disconnectedCallback() {
        if (this.host.isConnected)
            this.hide();
        if (this.popover)
            this.popover.destroy();
    }
    render() {
        return (h(Host, { key: 'c0ed4b8b03d27f7437c148c8df305886c6cf29d9', class: "nano-dropdown" }, h("div", { key: '27165586e2caa99ba746e7db3aa1574f0e9d1576', part: "base", id: this.dropdownId, class: {
                dropdown: true,
                'dropdown--open': this.open,
            } }, h("span", { key: 'b401a4d341e786ae4fe8c54adf06f8da1be4f531', part: "trigger", class: "dropdown__trigger", ref: (el) => (this.trigger = el), onKeyDown: this.handleTriggerKeyDown, onClick: this.togglePanel }, h("slot", { key: 'bd61e0f2d5e164b21d3ecba81c63ec72b0b7cc6f', name: "trigger", onSlotchange: this.handleTriggerSlotChange })), h("div", { key: 'e0cb2a8823490b70f6aa01073d3873fa785fd763', ref: (el) => (this.positioner = el), class: "dropdown__positioner", popover: "manual", id: this.dropdownId + '-positioner' }, h("div", { key: '3f1b7e3911cd7616ff85d042fc0d1e3e358f36c2', ref: (el) => (this.panel = el), part: "panel", class: `dropdown__panel ${this.placement.split('-').join(' ')}`, "aria-hidden": !this.open, role: this.host.getAttribute('role') === 'group' ? 'group' : 'dialog', "aria-modal": this.host.getAttribute('role') === 'group' ? undefined : 'true', "aria-labelledby": this.dialogTitle ? this.labelId : undefined, tabIndex: this.isOverflowing() ? 0 : undefined }, this.dialogTitle && (h("span", { key: '19cc4dd331a4c2cbdc73a05687681cb2830fff0c', id: this.labelId, class: "dropdown__accessible-title", "aria-live": this.host.getAttribute('role') === 'group'
                ? undefined
                : 'polite' }, this.dialogTitle)), h("slot", { key: '5da1881086c31481463b3b7244552e17ee66d997' }))))));
    }
    static get watchers() { return {
        "open": ["handleOpenChange"],
        "tetherTo": ["handleTetherToChange"],
        "placement": ["handlePopoverOptionsChange"],
        "distance": ["handlePopoverOptionsChange"],
        "skidding": ["handlePopoverOptionsChange"],
        "hoist": ["handlePopoverOptionsChange"]
    }; }
    static get style() { return dropdownCss; }
}, [257, "nano-dropdown", {
        "autoOpen": [4, "auto-open"],
        "open": [1540],
        "closeOnSelect": [4, "close-on-select"],
        "containingElement": [1040, "containing-element"],
        "tetherTo": [16, "tether-to"],
        "placement": [1],
        "distance": [2],
        "skidding": [2],
        "hoist": [1028],
        "dialogTitle": [1, "dialog-title"],
        "eventType": [16, "event-type"],
        "show": [64],
        "hide": [64]
    }, [[0, "nanoOpen", "secondaryOpen"], [0, "nanoClose", "secondaryClose"], [0, "nanoSelect", "handlePanelSelect"]], {
        "open": ["handleOpenChange"],
        "tetherTo": ["handleTetherToChange"],
        "placement": ["handlePopoverOptionsChange"],
        "distance": ["handlePopoverOptionsChange"],
        "skidding": ["handlePopoverOptionsChange"],
        "hoist": ["handlePopoverOptionsChange"]
    }]);
function defineCustomElement() {
    if (typeof customElements === "undefined") {
        return;
    }
    const components = ["nano-dropdown"];
    components.forEach(tagName => { switch (tagName) {
        case "nano-dropdown":
            if (!customElements.get(tagName)) {
                customElements.define(tagName, Dropdown);
            }
            break;
    } });
}

export { Dropdown as D, defineCustomElement as d };
