"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { cnb } from "cnbuilder";
import { useEffect, useMemo, useRef, useState } from "react";
import { IconRotator } from "../icon/IconRotator.js";
import { getIcon } from "../icon/iconConfig.js";
import { Menu } from "../menu/Menu.js";
import { findMatchIndex } from "../movement/findMatchIndex.js";
import { KeyboardMovementProvider, useKeyboardMovementProvider } from "../movement/useKeyboardMovementProvider.js";
import { isSearchableEvent } from "../movement/utils.js";
import { BELOW_CENTER_ANCHOR } from "../positioning/constants.js";
import { useEnsuredId } from "../useEnsuredId.js";
import { useEnsuredRef } from "../useEnsuredRef.js";
import { useToggle } from "../useToggle.js";
import { loop } from "../utils/loop.js";
import { SelectValue } from "./SelectValue.js";
import { TextField } from "./TextField.js";
import { getFormConfig } from "./formConfig.js";
import { select } from "./selectStyles.js";
import { extractOptionsFromChildren } from "./selectUtils.js";
import { ListboxProvider } from "./useListboxProvider.js";
import { triggerManualChangeEvent, tryToSubmitRelatedForm } from "./utils.js";
const EMPTY_STRING = "";
const noop = ()=>{
// do nothing
};
const getNonDisabledOptions = (container)=>[
        ...container.querySelectorAll('[role="option"]:not([aria-disabled])')
    ];
/**
 * **Client Component**
 *
 * @remarks \@since 6.0.0 Rewritten with a new API.
 */ export function Select(props) {
    const { id: propId, className, active = false, inputRef: propInputRef, inputClassName, menuProps = {}, containerProps: propContainerProps = {}, icon: propIcon, value, defaultValue, theme: propTheme, onChange = noop, leftAddon, rightAddon: propRightAddon, disableValueAddon = false, disableSelectedIcon = false, children, ...remaining } = props;
    const { disabled = false, form } = props;
    const id = useEnsuredId(propId, "select");
    const containerId = useEnsuredId(propContainerProps.id, "select-container");
    const icon = getIcon("dropdown", propIcon);
    const theme = getFormConfig("theme", propTheme);
    const { toggled: visible, enable: show, disable: hide } = useToggle();
    const [inputRef, inputRefCallback] = useEnsuredRef(propInputRef);
    const [containerRef, containerRefCallback] = useEnsuredRef(propContainerProps.ref);
    const [menuRef, menuRefCallback] = useEnsuredRef(menuProps.nodeRef);
    const [currentValue, setCurrentValue] = useState(()=>{
        if (typeof defaultValue !== "undefined") {
            return defaultValue;
        }
        return typeof value !== "undefined" ? value : EMPTY_STRING;
    });
    const initialValue = useRef(currentValue);
    useEffect(()=>{
        const select = inputRef.current;
        if (!select) {
            return;
        }
        const formElement = select.closest("form") || form && document.getElementById(form) || null;
        if (!formElement) {
            return;
        }
        const handleReset = ()=>{
            triggerManualChangeEvent(select, initialValue.current);
        };
        formElement.addEventListener("reset", handleReset);
        return ()=>{
            formElement.removeEventListener("reset", handleReset);
        };
    }, [
        form,
        inputRef
    ]);
    const { options, searchValues, currentOption, currentIndex } = extractOptionsFromChildren(children, typeof value === "undefined" ? currentValue : value);
    const totalOptions = options.length - 1;
    let rightAddon = propRightAddon;
    if (typeof rightAddon === "undefined" && icon) {
        rightAddon = /*#__PURE__*/ _jsx(IconRotator, {
            rotated: visible,
            children: icon
        });
    }
    const listboxContext = useMemo(()=>({
            inputRef,
            currentValue: typeof value === "undefined" ? currentValue : value,
            disableSelectedIcon
        }), [
        currentValue,
        disableSelectedIcon,
        inputRef,
        value
    ]);
    // TODO: Need to update this to support editable listboxes where these props
    // would go to the input element instead of the container
    const a11yProps = {
        "aria-haspopup": "listbox",
        "aria-expanded": visible,
        role: "combobox",
        tabIndex: disabled ? -1 : 0
    };
    const { movementProps, movementContext, currentFocusIndex, setActiveDescendantId } = useKeyboardMovementProvider({
        onFocus: propContainerProps.onFocus,
        onClick (event) {
            propContainerProps.onClick?.(event);
            if (disabled) {
                return;
            }
            show();
        },
        onKeyDown (event) {
            propContainerProps.onKeyDown?.(event);
            if (disabled) {
                return;
            }
            if (visible) {
                if (event.key === "Escape" || event.key === "Tab") {
                    event.stopPropagation();
                    hide();
                }
                return;
            }
            if (isSearchableEvent(event)) {
                event.stopPropagation();
                const nextIndex = findMatchIndex({
                    value: event.key,
                    values: searchValues,
                    startIndex: event.shiftKey ? -1 : currentIndex
                });
                if (nextIndex !== -1) {
                    triggerManualChangeEvent(inputRef.current, options[nextIndex].value);
                }
                return;
            }
            switch(event.key){
                case " ":
                    event.preventDefault();
                    event.stopPropagation();
                    show();
                    break;
                case "Enter":
                    tryToSubmitRelatedForm(event, form);
                    break;
                case "Home":
                    event.preventDefault();
                    event.stopPropagation();
                    if (currentIndex !== 0) {
                        triggerManualChangeEvent(inputRef.current, options[0].value);
                    }
                    break;
                case "End":
                    event.preventDefault();
                    event.stopPropagation();
                    if (currentIndex !== totalOptions) {
                        triggerManualChangeEvent(inputRef.current, options[totalOptions].value);
                    }
                    break;
                case "ArrowDown":
                case "ArrowUp":
                    {
                        event.preventDefault();
                        event.stopPropagation();
                        const increment = event.key === "ArrowDown";
                        if (currentIndex === -1 && !increment) {
                            // this matches the native select behavior where it will do
                            // nothing if there is no current value
                            return;
                        }
                        const nextIndex = loop({
                            max: totalOptions,
                            value: currentIndex,
                            minmax: true,
                            increment
                        });
                        triggerManualChangeEvent(inputRef.current, options[nextIndex].value);
                        break;
                    }
            }
        },
        loopable: false,
        searchable: true,
        programmatic: true,
        includeDisabled: false,
        tabIndexBehavior: "virtual",
        getDefaultFocusedIndex (focusOptions) {
            if (typeof menuProps.getDefaultFocusedIndex === "function") {
                return menuProps.getDefaultFocusedIndex(focusOptions);
            }
            const val = typeof value === "undefined" ? currentValue : value;
            return options.findIndex((option)=>option.value === val);
        },
        getFocusableElements () {
            const menu = menuRef.current;
            if (!menu) {
                return [];
            }
            return [
                ...menu.querySelectorAll('[role="option"]:not([aria-disabled])')
            ];
        }
    });
    const containerProps = {
        ...propContainerProps,
        ...movementProps,
        ...a11yProps,
        ref: containerRefCallback
    };
    const { onEntering, onEntered, onExiting, onExited, disableTransition } = menuProps;
    const handleMounting = (callback = noop, skipped)=>(appearing)=>{
            callback(appearing);
            const menu = menuRef.current;
            if (!menu || skipped) {
                return;
            }
            // Since the keyboard movement behavior is tied to the
            // `TextFieldContainer` or `input` element instead of the menu for this
            // widget, the focus index and active descendant must manually be updated
            // whenever the menu becomes visible. Without this, no items will be
            // focused until the first keyboard event that would move focus
            const val = typeof value === "undefined" ? currentValue : value;
            const focusables = getNonDisabledOptions(menu);
            const index = Math.max(0, options.findIndex((option)=>option.value === val));
            focusables[index].scrollIntoView({
                block: "nearest"
            });
            currentFocusIndex.current = index;
            setActiveDescendantId(focusables[index]?.id || "");
        };
    const handleUnmounting = (callback = noop, skipped = false)=>()=>{
            callback();
            if (!skipped) {
                // 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("");
            }
        };
    return /*#__PURE__*/ _jsx(ListboxProvider, {
        value: listboxContext,
        children: /*#__PURE__*/ _jsxs(KeyboardMovementProvider, {
            value: movementContext,
            children: [
                /*#__PURE__*/ _jsx(TextField, {
                    ...remaining,
                    "aria-hidden": true,
                    id: id,
                    ref: inputRefCallback,
                    containerProps: containerProps,
                    type: "text",
                    tabIndex: -1,
                    theme: theme,
                    value: value,
                    defaultValue: defaultValue,
                    active: active || visible,
                    leftAddon: leftAddon,
                    rightAddon: rightAddon,
                    className: cnb("rmd-select-container", className),
                    inputClassName: select({
                        theme,
                        className: inputClassName
                    }),
                    onChange: (event)=>{
                        onChange(event);
                        if (typeof value !== "undefined") {
                            return;
                        }
                        const nextValue = event.currentTarget.value;
                        const valueAsNumber = parseFloat(nextValue);
                        const nextOption = options.find((option)=>// need to compare both here since
                            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                            // @ts-expect-error
                            option.value === nextValue || option.value === valueAsNumber);
                        setCurrentValue(nextOption ? nextOption.value : initialValue.current);
                    },
                    children: /*#__PURE__*/ _jsx(SelectValue, {
                        disableAddon: disableValueAddon,
                        ...currentOption
                    })
                }),
                /*#__PURE__*/ _jsx(Menu, {
                    "aria-labelledby": containerId,
                    anchor: BELOW_CENTER_ANCHOR,
                    role: "listbox",
                    width: "min",
                    ...menuProps,
                    ref: menuRefCallback,
                    visible: visible,
                    fixedTo: containerRef,
                    onRequestClose: hide,
                    onEntering: handleMounting(onEntering, false),
                    onEntered: handleMounting(onEntered, !disableTransition),
                    onExiting: handleUnmounting(onExiting, false),
                    onExited: handleUnmounting(onExited, !disableTransition),
                    sheetProps: {
                        ...menuProps.sheetProps,
                        onEntering: handleMounting(menuProps.sheetProps?.onEntering, false),
                        onEntered: handleMounting(menuProps.sheetProps?.onEntered, !disableTransition)
                    },
                    children: children
                })
            ]
        })
    });
}

//# sourceMappingURL=Select.js.map