"use client";
import { useCallback, useRef } from "react";
import { useKeyboardMovementProvider } from "../movement/useKeyboardMovementProvider.js";
import { BELOW_CENTER_ANCHOR } from "../positioning/constants.js";
import { getTransitionCallbacks } from "../transition/getTransitionCallbacks.js";
import { useEnsuredId } from "../useEnsuredId.js";
import { useEnsuredRef } from "../useEnsuredRef.js";
import { useEnsuredState } from "../useEnsuredState.js";
import { tryToSubmitRelatedForm } from "./utils.js";
const noop = ()=>{
// do nothing
};
/**
 * @since 6.0.0
 */ export const getNonDisabledOptions = (container)=>[
        ...container.querySelectorAll('[role="option"]:not([aria-disabled])')
    ];
/**
 * @since 6.0.0
 */ export function useCombobox(options) {
    const { form, popup = "listbox", onClick = noop, onFocus, onKeyDown, searchable, multiselect, isNegativeOneAllowed, loopable, disabled, visible: propVisible, setVisible: propSetVisible, defaultVisible = false, comboboxId: propComboboxId, comboboxRef: propComboboxRef, popupId: propPopupId, popupRef: propPopupRef, onFocusChange = noop, extendKeyDown = noop, getFocusableElements = getNonDisabledOptions, getEnterDefaultFocusedIndex, getDefaultFocusedIndex } = options;
    const [visible, setVisible] = useEnsuredState({
        name: "visible",
        value: propVisible,
        setValue: propSetVisible,
        defaultValue: defaultVisible
    });
    const show = useCallback(()=>{
        setVisible(true);
    }, [
        setVisible
    ]);
    const hide = useCallback(()=>{
        setVisible(false);
    }, [
        setVisible
    ]);
    const popupId = useEnsuredId(propPopupId, "combobox-popup");
    const comboboxId = useEnsuredId(propComboboxId, "combobox");
    const [popupRef, popupRefCallback] = useEnsuredRef(propPopupRef);
    const focusLastRef = useRef(false);
    const { nodeRef: comboboxRef, movementProps, movementContext, currentFocusIndex, activeDescendantId, setActiveDescendantId } = useKeyboardMovementProvider({
        ref: propComboboxRef,
        onFocus,
        onKeyDown,
        onClick (event) {
            onClick(event);
            if (disabled) {
                return;
            }
            show();
        },
        extendKeyDown (movementData) {
            extendKeyDown({
                ...movementData,
                show,
                hide,
                visible,
                focusLastRef
            });
            const { event } = movementData;
            if (event.isPropagationStopped()) {
                return;
            }
            if (visible) {
                switch(event.key){
                    case "Tab":
                        // do not stop propagation for tab so that shift+tab works correctly in dialogs
                        hide();
                        break;
                    case "Escape":
                        event.stopPropagation();
                        hide();
                        break;
                    case "Enter":
                        event.preventDefault();
                        break;
                }
                // while visible, always use the default keyboard movement behavior
                return;
            }
            switch(event.key){
                case "ArrowUp":
                case "ArrowDown":
                    event.preventDefault();
                    event.stopPropagation();
                    focusLastRef.current = event.key === "ArrowUp";
                    show();
                    break;
                case "Enter":
                    tryToSubmitRelatedForm(event, form);
                    break;
            }
        },
        disabled,
        loopable,
        searchable,
        onFocusChange,
        programmatic: true,
        includeDisabled: false,
        tabIndexBehavior: "virtual",
        getFocusableElements (container, programmatic) {
            const popup = popupRef.current;
            if (!popup) {
                return [];
            }
            return getFocusableElements(popup || container, programmatic);
        },
        isNegativeOneAllowed,
        getDefaultFocusedIndex
    });
    const popupProps = {
        "aria-multiselectable": multiselect || undefined,
        id: popupId,
        ref: popupRefCallback,
        role: popup
    };
    return {
        show,
        hide,
        visible,
        setVisible,
        focusLast: focusLastRef,
        popupRef,
        popupProps,
        comboboxRef,
        comboboxProps: {
            ...movementProps,
            "aria-controls": popupId,
            "aria-disabled": disabled || undefined,
            "aria-expanded": visible,
            "aria-haspopup": popup,
            id: comboboxId,
            role: "combobox"
        },
        movementProps,
        movementContext,
        currentFocusIndex,
        activeDescendantId,
        setActiveDescendantId,
        getMenuProps (props = {}) {
            const { sheetProps, disableTransition, onEnter, onEntering, onEntered = noop, onExited, onExiting, onExit } = props;
            // Chrome does not trigger the scrollIntoView behavior correctly while
            // using a scale transition, so need to trigger this on the entered flow
            // to really make sure the item is in view. An alternative would be to
            // implement a custom scrollIntoView behavior again like the previous
            // versions of react-md, but this is less lines of code
            const attemptScroll = ()=>{
                const activeOption = document.getElementById(activeDescendantId);
                if (activeOption) {
                    activeOption.scrollIntoView({
                        block: "nearest"
                    });
                }
            };
            const onEnterOnce = ()=>{
                const popup = popupRef.current;
                if (!popup) {
                    attemptScroll();
                    return;
                }
                const focusables = getFocusableElements(popup, true);
                const index = getEnterDefaultFocusedIndex({
                    focusLast: focusLastRef.current,
                    focusables,
                    currentFocusIndex: currentFocusIndex.current
                });
                focusLastRef.current = false;
                currentFocusIndex.current = index;
                const option = focusables[index];
                if (!option) {
                    return;
                }
                onFocusChange({
                    index,
                    element: option
                });
                option.scrollIntoView({
                    block: "nearest"
                });
                setActiveDescendantId(option.id || "");
            };
            return {
                anchor: BELOW_CENTER_ANCHOR,
                width: "min",
                fixedTo: comboboxRef,
                ...props,
                ...popupProps,
                visible,
                onRequestClose: hide,
                ...getTransitionCallbacks({
                    disableTransition,
                    onEnter,
                    onEntered: (appearing)=>{
                        onEntered(appearing);
                        attemptScroll();
                    },
                    onEntering,
                    onEnterOnce,
                    onExit,
                    onExiting,
                    onExited,
                    onExitOnce: ()=>{
                        // since the menu is unmounted or set to hidden while not visible, need
                        // to clear the aria-activedescendant and current focus index when
                        // hiding
                        currentFocusIndex.current = -1;
                        setActiveDescendantId("");
                    }
                }),
                sheetProps: {
                    ...sheetProps,
                    ...getTransitionCallbacks({
                        ...sheetProps,
                        onEnterOnce,
                        disableTransition: sheetProps?.disableTransition ?? disableTransition
                    })
                }
            };
        }
    };
}

//# sourceMappingURL=useCombobox.js.map