/**
 * @license MIT
 * author: Cube Dev Team
 * @cube-dev/ui-kit v0.0.0-canary-5e810c3
 * Released under the MIT license.
 */

import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
import { cloneElement, useEffect, useMemo, useRef, } from 'react';
import { DismissButton, FocusScope, HiddenSelect, useButton, useHover, useListBox, useListBoxSection, useOption, useOverlay, useOverlayPosition, useSelect, } from 'react-aria';
import { Section as BaseSection, useSelectState } from 'react-stately';
import { useEvent } from '../../../_internal/index.js';
import { CloseIcon, DirectionIcon, LoadingIcon } from '../../../icons/index.js';
import { useProviderProps } from '../../../provider.js';
import { BASE_STYLES, COLOR_STYLES, extractStyles, OUTER_STYLES, tasty, } from '../../../tasty/index.js';
import { chainRaf } from '../../../utils/raf.js';
import { generateRandomId } from '../../../utils/random.js';
import { forwardRefWithGenerics, mergeProps, useCombinedRefs, useLayoutEffect, } from '../../../utils/react/index.js';
import { useFocus } from '../../../utils/react/interactions.js';
import { useEventBus } from '../../../utils/react/useEventBus.js';
import { ItemAction } from '../../actions/index.js';
import { StyledDivider as ListDivider, StyledSectionHeading as ListSectionHeading, StyledSection as ListSectionWrapper, } from '../../actions/Menu/styled.js';
import { CollectionItem, filterCollectionItemProps, } from '../../CollectionItem.js';
import { Item } from '../../content/Item/index.js';
import { Text } from '../../content/Text.js';
import { useFieldProps, useFormProps, wrapWithField } from '../../form/index.js';
import { DisplayTransition } from '../../helpers/index.js';
import { Portal } from '../../portal/index.js';
import { InvalidIcon } from '../../shared/InvalidIcon.js';
import { ValidIcon } from '../../shared/ValidIcon.js';
const SelectWrapperElement = tasty({
    qa: 'SelectWrapper',
    styles: {
        display: 'grid',
        position: 'relative',
        radius: true,
        fill: {
            '': '#white',
            disabled: '#dark.04',
            'theme=special': '#clear',
        },
        color: {
            '': '#dark.85',
            focused: '#dark.85',
            invalid: '#danger-text',
            disabled: '#dark.30',
        },
    },
});
export const ListBoxElement = tasty({
    qa: 'ListBox',
    as: 'ul',
    styles: {
        display: 'flex',
        gap: '1bw',
        flow: 'column',
        margin: '0',
        padding: '0',
        listStyle: 'none',
        scrollbar: 'styled',
    },
});
// Use Item for options to unify item visuals and reduce custom styling
const OptionItem = tasty(Item, {
    as: 'li',
    disableActionsFocus: true,
    qa: 'Option',
    styles: {
        '$inline-compensation': '0px',
    },
});
const SelectOverlayWrapper = tasty({
    qa: 'SelectOverlayWrapper',
    styles: {
        position: 'absolute',
        zIndex: 1000,
    },
});
const OverlayElement = tasty({
    qa: 'SelectOverlay',
    styles: {
        width: 'min $overlay-min-width',
        display: 'grid',
        gridRows: '1sf',
        height: 'initial max-content (50vh - 5x)',
        overflow: 'auto',
        background: '#white',
        radius: '1cr',
        shadow: true,
        padding: '.5x',
        border: true,
        boxSizing: 'border-box',
        transition: 'translate $transition ease-out, scale $transition ease-out, theme $transition ease-out',
        translate: {
            '': '0 0',
            'open & [data-placement="top"]': '0 0',
            '!open & [data-placement="top"]': '0 1x',
            'open & ([data-placement="bottom"] | ![data-placement])': '0 0',
            '!open & ([data-placement="bottom"] | ![data-placement])': '0 -1x',
        },
        transformOrigin: {
            '': 'top center',
            '[data-placement="top"]': 'bottom center',
        },
        scale: {
            '': '1 1',
            '!open': '1 .9',
        },
        opacity: {
            '': 1,
            '!open': 0.001,
        },
    },
});
const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES];
function Select(props, ref) {
    props = useProviderProps(props);
    props = useFormProps(props);
    props = useFieldProps(props, {
        defaultValidationTrigger: 'onChange',
        valuePropsMapper: ({ value, onChange }) => ({
            selectedKey: value ?? null,
            onSelectionChange: onChange,
        }),
    });
    let { qa, label, extra, icon, rightIcon, labelStyles, isRequired, necessityIndicator, validationState, prefix, isDisabled = props.isLoading || false, autoFocus, inputProps, triggerRef, popoverRef, listBoxRef, isLoading, loadingIndicator, overlayOffset = 8, inputStyles, triggerStyles, optionStyles, listBoxStyles, overlayStyles, suffix, message, triggerDescription, descriptionPlacement, hotkeys, direction = 'bottom', shouldFlip = true, containerPadding = 8, placeholder, tooltip, size = 'medium', shape, styles, type = 'outline', theme = 'default', labelSuffix, suffixPosition = 'before', isClearable, onOpenChange, form, ...otherProps } = props;
    let state = useSelectState(props);
    // Generate a unique ID for this select instance
    const selectId = useMemo(() => generateRandomId(), []);
    // Get event bus for menu synchronization
    const { emit, on } = useEventBus();
    // Listen for other menus opening and close this one if needed
    useEffect(() => {
        const unsubscribe = on('popover:open', (data) => {
            // If another menu is opening and this select is open, close this one
            if (data.menuId !== selectId && state.isOpen) {
                state.close();
            }
        });
        return unsubscribe;
    }, [on, selectId, state]);
    // Emit event when this select opens
    useEffect(() => {
        if (state.isOpen) {
            emit('popover:open', { menuId: selectId });
        }
    }, [state.isOpen, emit, selectId]);
    // Call onOpenChange when open state changes
    useEffect(() => {
        onOpenChange?.(state.isOpen);
    }, [state.isOpen]);
    styles = extractStyles(otherProps, PROP_STYLES, styles);
    ref = useCombinedRefs(ref);
    triggerRef = useCombinedRefs(triggerRef);
    popoverRef = useCombinedRefs(popoverRef);
    listBoxRef = useCombinedRefs(listBoxRef);
    let { labelProps, triggerProps, valueProps, menuProps } = useSelect(props, state, triggerRef);
    let { overlayProps, placement, updatePosition } = useOverlayPosition({
        targetRef: triggerRef,
        overlayRef: popoverRef,
        scrollRef: listBoxRef,
        placement: `${direction} end`,
        shouldFlip: shouldFlip,
        isOpen: state.isOpen,
        onClose: state.close,
        offset: overlayOffset,
        containerPadding: containerPadding,
    });
    let { isFocused, focusProps } = useFocus({ isDisabled }, true);
    let { hoverProps, isHovered } = useHover({ isDisabled });
    // Get props for the button based on the trigger props from useSelect
    let { buttonProps } = useButton(triggerProps, triggerRef);
    let isInvalid = validationState === 'invalid';
    let validationIcon = isInvalid ? InvalidIcon : ValidIcon;
    let validation = cloneElement(validationIcon);
    // Clear button logic
    let hasSelection = state.selectedItem != null;
    let showClearButton = isClearable && hasSelection && !isDisabled && !props.isReadOnly;
    // Clear function
    let clearValue = useEvent(() => {
        props.onSelectionChange?.(null);
        state.setSelectedKey(null);
        // Close the popup if it's open
        if (state.isOpen) {
            state.close();
        }
        // Return focus to the trigger for better UX
        triggerRef.current?.focus?.();
        props.onClear?.();
    });
    let triggerWidth = triggerRef?.current?.offsetWidth;
    const showPlaceholder = !!placeholder?.trim() && !state.selectedItem;
    const modifiers = useMemo(() => ({
        invalid: isInvalid,
        valid: validationState === 'valid',
        disabled: isDisabled,
        loading: isLoading,
        hovered: isHovered,
        focused: isFocused,
        placeholder: showPlaceholder,
        prefix: !!prefix,
        suffix: true,
    }), [
        validationState,
        isDisabled,
        isLoading,
        isHovered,
        isFocused,
        showPlaceholder,
        prefix,
    ]);
    suffix = useMemo(() => {
        if (!suffix && !validationState) {
            return null;
        }
        return (_jsxs(_Fragment, { children: [suffix, validationState ? validation : null] }));
    }, [suffix, validationState, validation]);
    let selectField = (_jsxs(SelectWrapperElement, { mods: modifiers, styles: styles, "data-size": size, "data-type": type, "data-theme": theme, children: [_jsx(HiddenSelect, { state: state, triggerRef: triggerRef, label: props.label, name: props.name }), _jsx(Item, { as: "button", qa: qa || 'Select', "data-input-type": "select", ...mergeProps(buttonProps, hoverProps, focusProps), ref: triggerRef, "data-popover-trigger": true, styles: { ...inputStyles, ...triggerStyles }, theme: theme, size: size, shape: shape, 
                // Ensure this button never submits a surrounding form in tests or runtime
                htmlType: "button", 
                // Preserve visual variant via data attribute instead of conflicting with HTML attribute
                type: type, mods: modifiers, prefix: prefix, suffix: suffix, icon: icon, rightIcon: rightIcon !== undefined ? (rightIcon) : showClearButton ? (_jsx(ItemAction, { icon: _jsx(CloseIcon, {}), theme: validationState === 'invalid' ? 'danger' : undefined, qa: "SelectClearButton", mods: { pressed: false }, onPress: clearValue })) : isLoading ? (_jsx(LoadingIcon, {})) : (_jsx(DirectionIcon, { to: state.isOpen ? 'up' : 'down' })), description: triggerDescription, descriptionPlacement: descriptionPlacement, hotkeys: hotkeys, tooltip: tooltip, labelProps: valueProps, children: state.selectedItem ? (state.selectedItem.rendered) : placeholder ? (_jsx(Text.Placeholder, { children: placeholder })) : null }), _jsx(ListBoxPopup, { ...menuProps, popoverRef: popoverRef, listBoxRef: listBoxRef, overlayProps: overlayProps, placement: placement, updatePosition: updatePosition, state: state, listBoxStyles: listBoxStyles, overlayStyles: overlayStyles, optionStyles: optionStyles, minWidth: triggerWidth, triggerRef: triggerRef, isDisabled: isDisabled })] }));
    return wrapWithField(selectField, ref, mergeProps({
        ...props,
    }, { labelProps }));
}
export function ListBoxPopup({ state, popoverRef, listBoxRef, listBoxStyles, overlayStyles, optionStyles, overlayProps: parentOverlayProps, shouldUseVirtualFocus = false, placement, updatePosition, minWidth, size = 'small', triggerRef, isDisabled, ...otherProps }) {
    // For trigger+popover components, map 'small' size to 'medium' for list items
    // while preserving 'medium' and 'large' sizes
    const listItemSize = size === 'small' ? 'medium' : size;
    // Update position when overlay opens
    useLayoutEffect(() => {
        if (state.isOpen && updatePosition) {
            // Use triple RAF to ensure layout is complete before positioning
            // This gives enough time for the DisplayTransition and content to render
            return chainRaf(() => {
                updatePosition();
            }, 3);
        }
    }, [state.isOpen]);
    // Get props for the listbox
    let { listBoxProps } = useListBox({
        autoFocus: state.focusStrategy || true,
        shouldUseVirtualFocus,
        ...otherProps,
    }, state, listBoxRef);
    // Handle events that should cause the popup to close,
    // e.g. blur, clicking outside, or pressing the escape key.
    let { overlayProps } = useOverlay({
        onClose: () => state.close(),
        shouldCloseOnBlur: true,
        isOpen: state.isOpen,
        isDismissable: true,
        shouldCloseOnInteractOutside: (el) => {
            const menuTriggerEl = el.closest('[data-popover-trigger]');
            // If no menu trigger was clicked, allow closing
            if (!menuTriggerEl)
                return true;
            // If the same trigger that opened this select was clicked, allow closing
            if (menuTriggerEl === triggerRef?.current)
                return true;
            // Otherwise, don't close (let event mechanism handle it)
            return false;
        },
    }, popoverRef);
    // Extract primary placement direction for consistent styling
    const placementDirection = placement?.split(' ')[0] || 'bottom';
    // Wrap in <FocusScope> so that focus is restored back to the
    // trigger when the popup is closed. In addition, add hidden
    // <DismissButton> components at the start and end of the list
    // to allow screen reader users to dismiss the popup easily.
    return (_jsx(Portal, { children: _jsx(DisplayTransition, { isShown: state.isOpen && !isDisabled, children: ({ phase, isShown, ref: transitionRef }) => (_jsx(SelectOverlayWrapper, { ...overlayProps, ...parentOverlayProps, ref: popoverRef, style: parentOverlayProps?.style, children: _jsx(OverlayElement, { ref: transitionRef, "data-placement": placementDirection, "data-phase": phase, mods: {
                        open: isShown,
                    }, styles: overlayStyles, style: {
                        '--overlay-min-width': minWidth ? `${minWidth}px` : 'initial',
                    }, children: _jsxs(FocusScope, { restoreFocus: true, children: [_jsx(DismissButton, { onDismiss: () => state.close() }), (() => {
                                const renderedItems = [];
                                let isFirstSection = true;
                                for (const item of state.collection) {
                                    if (item.type === 'section') {
                                        if (!isFirstSection) {
                                            renderedItems.push(_jsx(ListDivider, { as: "li", role: "separator", "aria-orientation": "horizontal" }, `divider-${String(item.key)}`));
                                        }
                                        renderedItems.push(_jsx(SelectSection, { item: item, state: state, optionStyles: optionStyles, sectionStyles: undefined, shouldUseVirtualFocus: shouldUseVirtualFocus, size: listItemSize }, item.key));
                                        isFirstSection = false;
                                    }
                                    else {
                                        renderedItems.push(_jsx(Option, { item: item, state: state, styles: optionStyles, shouldUseVirtualFocus: shouldUseVirtualFocus, size: listItemSize }, item.key));
                                    }
                                }
                                return (_jsx(ListBoxElement, { styles: listBoxStyles, ...listBoxProps, ref: listBoxRef, children: renderedItems }));
                            })(), _jsx(DismissButton, { onDismiss: () => state.close() })] }) }) })) }) }));
}
function Option({ item, state, styles, shouldUseVirtualFocus, size }) {
    let ref = useRef(null);
    let isDisabled = state.disabledKeys.has(item.key);
    let isSelected = state.selectionManager.isSelected(item.key);
    let isVirtualFocused = state.selectionManager.focusedKey === item.key;
    let { optionProps, isPressed, labelProps, descriptionProps } = useOption({
        key: item.key,
        isDisabled,
        isSelected,
        shouldSelectOnPressUp: true,
        shouldFocusOnHover: true,
        shouldUseVirtualFocus,
    }, state, ref);
    // Handle focus events, so we can apply highlighted
    // style to the focused option
    let { isFocused, focusProps } = useFocus({ isDisabled });
    // Filter out service props - all remaining props can be passed to Item
    const filteredItemProps = filterCollectionItemProps(item.props);
    return (_jsx(OptionItem, { ...mergeProps(optionProps, focusProps, filteredItemProps), ref: ref, mods: {
            listboxitem: true,
            selected: isSelected,
            focused: shouldUseVirtualFocus ? isVirtualFocused : isFocused,
            disabled: isDisabled,
            pressed: isPressed,
        }, "data-size": size, styles: {
            ...styles,
            ...filteredItemProps.styles,
        }, labelProps: labelProps, descriptionProps: descriptionProps, defaultTooltipPlacement: "right", children: item.rendered }));
}
function SelectSection(props) {
    const { item, state, optionStyles, headingStyles, sectionStyles, shouldUseVirtualFocus, size, } = props;
    const heading = item.rendered;
    const { itemProps, headingProps, groupProps } = useListBoxSection({
        heading,
        'aria-label': item['aria-label'],
    });
    return (_jsxs(ListSectionWrapper, { ...itemProps, styles: sectionStyles, children: [heading && (_jsx(ListSectionHeading, { ...headingProps, size: size, styles: { ...headingStyles, '$inline-compensation': '0px' }, children: heading })), _jsx(ListBoxElement, { ...groupProps, mods: { section: true }, children: [...item.childNodes]
                    .filter((node) => state.collection.getItem(node.key))
                    .map((node) => (_jsx(Option, { item: node, state: state, styles: optionStyles, shouldUseVirtualFocus: shouldUseVirtualFocus, size: size }, node.key))) })] }));
}
const _Select = forwardRefWithGenerics(Select);
_Select.cubeInputType = 'Select';
const SelectSectionComponent = Object.assign(BaseSection, {
    displayName: 'Section',
});
const __Select = Object.assign(_Select, {
    Item: CollectionItem,
    Section: SelectSectionComponent,
});
__Select.displayName = 'Select';
export { __Select as Select };
export { useSelectState };

        