import { __decorate } from "tslib";
import { attr, nullableNumberConverter, observable } from '@ni/fast-element';
import { DesignSystem, FoundationElement } from '@ni/fast-foundation';
import { eventChange, keyArrowDown, keyArrowUp, keyEscape } from '@ni/fast-web-utilities';
import { ButtonAppearance, ButtonAppearanceVariant, MenuButtonPosition } from './types';
import { styles } from './styles';
import { template } from './template';
/**
 * A nimble-styled menu button control.
 */
export class MenuButton extends FoundationElement {
    constructor() {
        super(...arguments);
        this.appearance = ButtonAppearance.outline;
        this.disabled = false;
        this.contentHidden = false;
        /**
         * Specifies whether or not the menu is open.
         */
        this.open = false;
        /**
         * Configures where the menu should be placed relative to the button that opens the menu.
         */
        this.position = MenuButtonPosition.auto;
        /**
         * Used to maintain the internal state of whether the last menu item should be focused instead
         * of the first menu item the next time the menu is opened.
         */
        this.focusLastItemWhenOpened = false;
        this.menuChangeHandler = () => {
            this.setOpen(false);
            this.toggleButton.focus();
        };
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        if (this.region) {
            this.region.removeEventListener(eventChange, this.menuChangeHandler);
        }
    }
    toggleButtonChanged(_prev, _next) {
        if (this.region && this.toggleButton) {
            this.region.anchorElement = this.toggleButton;
        }
    }
    regionChanged(prev, _next) {
        if (prev) {
            prev.removeEventListener(eventChange, this.menuChangeHandler);
        }
        if (this.region) {
            if (this.toggleButton) {
                this.region.anchorElement = this.toggleButton;
            }
            this.region.addEventListener(eventChange, this.menuChangeHandler, {
                capture: true
            });
        }
    }
    openChanged(_prev, _next) {
        if (this.toggleButton) {
            this.toggleButton.checked = this.open;
        }
        if (!this.open) {
            // Only fire an event here if the menu is changing to being closed. Otherwise,
            // wait until the menu is actually opened before firing the event.
            const eventDetail = {
                oldState: true,
                newState: false
            };
            this.$emit('toggle', eventDetail);
        }
    }
    regionLoadedHandler() {
        if (this.focusLastItemWhenOpened) {
            this.focusLastMenuItem();
            this.focusLastItemWhenOpened = false;
        }
        else {
            this.focusMenu();
        }
        const eventDetail = {
            oldState: false,
            newState: true
        };
        this.$emit('toggle', eventDetail);
    }
    focusoutHandler(e) {
        if (!this.open) {
            return true;
        }
        const focusTarget = e.relatedTarget;
        if (!this.contains(focusTarget)
            && !this.getMenu()?.contains(focusTarget)) {
            this.setOpen(false);
            return false;
        }
        return true;
    }
    toggleButtonCheckedChangeHandler(e) {
        this.setOpen(this.toggleButton.checked);
        // Don't bubble the 'change' event from the toggle button because
        // the menu button has its own 'toggle' event.
        e.stopPropagation();
        return false;
    }
    toggleButtonKeyDownHandler(e) {
        switch (e.key) {
            case keyArrowUp:
                this.focusLastItemWhenOpened = true;
                this.setOpen(true);
                return false;
            case keyArrowDown:
                this.setOpen(true);
                return false;
            default:
                return true;
        }
    }
    menuKeyDownHandler(e) {
        switch (e.key) {
            case keyEscape:
                this.setOpen(false);
                this.toggleButton.focus();
                return false;
            default:
                return true;
        }
    }
    setOpen(newValue) {
        if (this.open === newValue) {
            return;
        }
        const eventDetail = {
            oldState: this.open,
            newState: newValue
        };
        this.$emit('beforetoggle', eventDetail);
        this.open = newValue;
    }
    getMenu() {
        // Get the menu that is slotted within the menu-button, taking into account
        // that it may be nested within multiple 'slot' elements, such as when used
        // within a table.
        if (!this.slottedMenus || this.slottedMenus.length === 0) {
            return undefined;
        }
        let currentItem = this.slottedMenus[0];
        while (currentItem) {
            if (currentItem.getAttribute('role') === 'menu') {
                return currentItem;
            }
            if (this.isSlotElement(currentItem)) {
                const firstNode = currentItem.assignedNodes()[0];
                if (firstNode instanceof HTMLElement) {
                    currentItem = firstNode;
                }
                else {
                    currentItem = undefined;
                }
            }
            else {
                return undefined;
            }
        }
        return undefined;
    }
    isSlotElement(element) {
        return element?.nodeName === 'SLOT';
    }
    focusMenu() {
        this.getMenu()?.focus();
    }
    focusLastMenuItem() {
        const menuItems = this.getMenu()?.querySelectorAll('[role=menuitem]');
        if (menuItems && menuItems.length > 0) {
            const lastMenuItem = menuItems[menuItems.length - 1];
            lastMenuItem.focus();
        }
    }
}
__decorate([
    attr
], MenuButton.prototype, "appearance", void 0);
__decorate([
    attr({ attribute: 'appearance-variant' })
], MenuButton.prototype, "appearanceVariant", void 0);
__decorate([
    attr({ mode: 'boolean' })
], MenuButton.prototype, "disabled", void 0);
__decorate([
    attr({ attribute: 'content-hidden', mode: 'boolean' })
], MenuButton.prototype, "contentHidden", void 0);
__decorate([
    attr({ attribute: 'tabindex', converter: nullableNumberConverter })
], MenuButton.prototype, "tabIndex", void 0);
__decorate([
    attr({ mode: 'boolean' })
], MenuButton.prototype, "open", void 0);
__decorate([
    attr({ attribute: 'position' })
], MenuButton.prototype, "position", void 0);
__decorate([
    observable
], MenuButton.prototype, "toggleButton", void 0);
__decorate([
    observable
], MenuButton.prototype, "region", void 0);
__decorate([
    observable
], MenuButton.prototype, "slottedMenus", void 0);
const nimbleMenuButton = MenuButton.compose({
    baseName: 'menu-button',
    template,
    styles,
    shadowOptions: {
        delegatesFocus: true
    }
});
DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleMenuButton());
export const menuButtonTag = 'nimble-menu-button';
//# sourceMappingURL=index.js.map