/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-b859c78 | Cube Dev Team */
import { extractStyles } from "../../../tasty/utils/styles.js";
import { BASE_STYLES, COLOR_STYLES, OUTER_STYLES } from "../../../tasty/styles/list.js";
import { tasty } from "../../../tasty/tasty.js";
import { useLayoutEffect as useLayoutEffect$1 } from "../../../utils/react/useLayoutEffect.js";
import { mergeProps as mergeProps$1 } from "../../../utils/react/mergeProps.js";
import { useCombinedRefs } from "../../../utils/react/useCombinedRefs.js";
import { useEventBus } from "../../../utils/react/useEventBus.js";
import { useEvent } from "../../../_internal/hooks/use-event.js";
import { useFocus as useFocus$1 } from "../../../utils/react/interactions.js";
import { DisplayTransition } from "../../helpers/DisplayTransition/DisplayTransition.js";
import { Portal } from "../../portal/Portal.js";
import { LoadingIcon } from "../../../icons/LoadingIcon.js";
import { useProviderProps } from "../../../provider.js";
import { _CollectionItem } from "../../CollectionItem.js";
import { CloseIcon } from "../../../icons/CloseIcon.js";
import { DirectionIcon } from "../../../icons/DirectionIcon.js";
import { generateRandomId } from "../../../utils/random.js";
import { useFormProps } from "../../form/Form/Form.js";
import { useFieldProps } from "../../form/Form/use-field/use-field-props.js";
import { wrapWithField } from "../../form/wrapper.js";
import { ListBox } from "../ListBox/ListBox.js";
import { chainRaf } from "../../../utils/raf.js";
import { InvalidIcon } from "../../shared/InvalidIcon.js";
import { ValidIcon } from "../../shared/ValidIcon.js";
import { DEFAULT_INPUT_STYLES, INPUT_WRAPPER_STYLES } from "../TextInput/TextInputBase.js";
import React, { cloneElement, forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { useFilter, useKeyboard, useOverlay, useOverlayPosition } from "react-aria";
import { Section, useListState } from "react-stately";

//#region src/components/fields/ComboBox/ComboBox.tsx
const ComboBoxWrapperElement = tasty({
	qa: "ComboBoxWrapper",
	styles: INPUT_WRAPPER_STYLES
});
const InputElement = tasty({
	as: "input",
	styles: DEFAULT_INPUT_STYLES
});
const ComboBoxOverlayWrapper = tasty({
	qa: "ComboBoxOverlayWrapper",
	styles: {
		position: "absolute",
		zIndex: 1e3
	}
});
const ComboBoxOverlayElement = tasty({
	qa: "ComboBoxOverlay",
	styles: {
		display: "grid",
		gridRows: "1sf",
		gridColumns: "1sf",
		width: "$overlay-min-width max-content 50vw",
		height: "initial max-content (50vh - 5x)",
		overflow: "auto",
		background: "#white",
		radius: "1cr",
		shadow: true,
		padding: "0",
		border: "#border",
		hide: {
			"": false,
			hidden: 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": .001
		},
		"$overlay-min-width": "min 30x"
	}
});
const PROP_STYLES = [
	...BASE_STYLES,
	...OUTER_STYLES,
	...COLOR_STYLES
];
function useComboBoxState({ selectedKey, defaultSelectedKey, inputValue, defaultInputValue, comboBoxId }) {
	const { emit, on } = useEventBus();
	const [internalSelectedKey, setInternalSelectedKey] = useState(defaultSelectedKey ?? null);
	const [internalInputValue, setInternalInputValue] = useState(defaultInputValue ?? "");
	const isControlledKey = selectedKey !== void 0;
	const isControlledInput = inputValue !== void 0;
	const effectiveSelectedKey = isControlledKey ? selectedKey : internalSelectedKey;
	const effectiveInputValue = isControlledInput ? inputValue : internalInputValue;
	const [isPopoverOpen, setIsPopoverOpen] = useState(false);
	useEffect(() => {
		return on("popover:open", (data) => {
			if (data.menuId !== comboBoxId && isPopoverOpen) setIsPopoverOpen(false);
		});
	}, [
		on,
		comboBoxId,
		isPopoverOpen
	]);
	useEffect(() => {
		if (isPopoverOpen) emit("popover:open", { menuId: comboBoxId });
	}, [
		isPopoverOpen,
		emit,
		comboBoxId
	]);
	return {
		effectiveSelectedKey,
		effectiveInputValue,
		isPopoverOpen,
		setIsPopoverOpen,
		setInternalSelectedKey,
		setInternalInputValue,
		isControlledKey,
		isControlledInput
	};
}
function useComboBoxFiltering({ effectiveInputValue, filter }) {
	const [isFilterActive, setIsFilterActive] = useState(false);
	const { contains } = useFilter({ sensitivity: "base" });
	const textFilterFn = useMemo(() => filter === false ? () => true : filter || contains, [filter, contains]);
	return {
		filterFn: useCallback((nodes) => {
			const term = effectiveInputValue.trim();
			if (!isFilterActive || !term) return nodes;
			return [...nodes].map((node) => {
				if (node.type === "section" && node.childNodes) {
					const filteredChildren = [...node.childNodes].filter((child) => textFilterFn(child.textValue || "", term));
					if (filteredChildren.length === 0) return null;
					return {
						...node,
						childNodes: filteredChildren,
						hasChildNodes: true
					};
				}
				return textFilterFn(node.textValue || "", term) ? node : null;
			}).filter(Boolean);
		}, [
			isFilterActive,
			effectiveInputValue,
			textFilterFn
		]),
		isFilterActive,
		setIsFilterActive
	};
}
function useCompositeFocus({ wrapperRef, popoverRef, onFocus, onBlur, isDisabled }) {
	const wasInsideRef = useRef(false);
	const rafRef = useRef(null);
	const checkFocus = useCallback(() => {
		if (isDisabled) return;
		const activeElement = document.activeElement;
		const isInside = (wrapperRef.current?.contains(activeElement) ?? false) || (popoverRef.current?.contains(activeElement) ?? false);
		if (isInside !== wasInsideRef.current) {
			wasInsideRef.current = isInside;
			if (isInside) onFocus?.();
			else onBlur?.();
		}
	}, [
		wrapperRef,
		popoverRef,
		onFocus,
		onBlur,
		isDisabled
	]);
	const handleFocusOrBlur = useCallback((e) => {
		if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
		rafRef.current = requestAnimationFrame(() => {
			rafRef.current = null;
			checkFocus();
		});
	}, [checkFocus]);
	useEffect(() => {
		return () => {
			if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
		};
	}, []);
	return { compositeFocusProps: {
		onFocus: handleFocusOrBlur,
		onBlur: handleFocusOrBlur
	} };
}
function useComboBoxKeyboard({ isPopoverOpen, listStateRef, hasResults, allowsCustomValue, effectiveInputValue, isClearable, onSelectionChange, onClearValue, onOpenPopover, onClosePopover, inputRef, setIsFilterActive, onKeyDown }) {
	const { keyboardProps } = useKeyboard({ onKeyDown: (e) => {
		onKeyDown?.(e);
		if (e.key === "ArrowDown" || e.key === "ArrowUp") {
			e.preventDefault();
			if (!isPopoverOpen) {
				if (!hasResults) setIsFilterActive(false);
				onOpenPopover();
				return;
			}
			const listState = listStateRef.current;
			if (!listState) return;
			const { selectionManager, collection, disabledKeys } = listState;
			const collectVisibleKeys = (nodes, out) => {
				for (const node of nodes) if (node.type === "item") {
					if (!disabledKeys?.has(node.key)) out.push(node.key);
				} else if (node.childNodes) collectVisibleKeys(node.childNodes, out);
			};
			const visibleKeys = [];
			collectVisibleKeys(collection, visibleKeys);
			if (visibleKeys.length === 0) return;
			const isArrowDown = e.key === "ArrowDown";
			const currentKey = selectionManager.focusedKey;
			let nextKey = null;
			if (currentKey == null) nextKey = isArrowDown ? visibleKeys[0] : visibleKeys[visibleKeys.length - 1];
			else {
				const currentIndex = visibleKeys.indexOf(currentKey);
				if (currentIndex !== -1) {
					const newIndex = currentIndex + (isArrowDown ? 1 : -1);
					if (newIndex >= 0 && newIndex < visibleKeys.length) nextKey = visibleKeys[newIndex];
				} else nextKey = isArrowDown ? visibleKeys[0] : visibleKeys[visibleKeys.length - 1];
			}
			if (nextKey != null) {
				if (listState.lastFocusSourceRef) listState.lastFocusSourceRef.current = "keyboard";
				selectionManager.setFocusedKey(nextKey);
			}
		} else if (e.key === "Enter") {
			if (isPopoverOpen) {
				const listState = listStateRef.current;
				if (listState) {
					const keyToSelect = listState.selectionManager.focusedKey;
					if (keyToSelect != null) {
						e.preventDefault();
						listState.selectionManager.select(keyToSelect, e);
						onClosePopover();
						inputRef.current?.focus();
						return;
					}
				}
			}
			if (!hasResults) {
				if (allowsCustomValue) {
					const value = effectiveInputValue.trim();
					onSelectionChange(value ?? null);
					if (isPopoverOpen || !value) e.preventDefault();
				} else {
					e.preventDefault();
					onSelectionChange(null);
				}
				return;
			}
			if ((effectiveInputValue || "").trim() === "") {
				e.preventDefault();
				onSelectionChange(null);
				return;
			}
		} else if (e.key === "Escape") {
			if (isPopoverOpen) {
				e.preventDefault();
				onClosePopover();
				inputRef.current?.focus();
			} else if (effectiveInputValue && isClearable) {
				e.preventDefault();
				onClearValue();
			}
		} else if (e.key === "Home" || e.key === "End") {
			if (isPopoverOpen) {
				e.preventDefault();
				const listState = listStateRef.current;
				if (!listState) return;
				const { selectionManager, collection, disabledKeys } = listState;
				const collectVisibleKeys = (nodes, out) => {
					for (const node of nodes) if (node.type === "item") {
						if (!disabledKeys?.has(node.key)) out.push(node.key);
					} else if (node.childNodes) collectVisibleKeys(node.childNodes, out);
				};
				const visibleKeys = [];
				collectVisibleKeys(collection, visibleKeys);
				if (visibleKeys.length === 0) return;
				const targetKey = e.key === "Home" ? visibleKeys[0] : visibleKeys[visibleKeys.length - 1];
				if (listState.lastFocusSourceRef) listState.lastFocusSourceRef.current = "keyboard";
				selectionManager.setFocusedKey(targetKey);
			}
		}
	} });
	return { keyboardProps };
}
const ComboBoxInput = forwardRef(function ComboBoxInput({ qa, inputRef, id, value, placeholder, isDisabled, isReadOnly, autoFocus, autoComplete, size, mods, inputStyles, keyboardProps, focusProps, onChange, onFocus, isPopoverOpen, hasResults, comboBoxId, listStateRef }, ref) {
	return /* @__PURE__ */ jsx(InputElement, {
		ref: useCombinedRefs(ref, inputRef),
		qa,
		id,
		type: "text",
		value,
		placeholder,
		isDisabled,
		readOnly: isReadOnly,
		autoFocus,
		autoComplete,
		"data-autofocus": autoFocus ? "" : void 0,
		onChange,
		onFocus,
		onBlur: focusProps.onBlur,
		...keyboardProps,
		styles: inputStyles,
		mods,
		"data-size": size,
		"data-input-type": "combobox",
		role: "combobox",
		"aria-expanded": isPopoverOpen && hasResults,
		"aria-haspopup": "listbox",
		"aria-controls": isPopoverOpen && hasResults ? `ComboBoxListBox-${comboBoxId}` : void 0,
		"aria-activedescendant": isPopoverOpen && hasResults && listStateRef.current?.selectionManager.focusedKey != null ? `ListBoxItem-${listStateRef.current?.selectionManager.focusedKey}` : void 0
	});
});
function ComboBoxOverlay({ isOpen, triggerRef, popoverRef, listBoxRef, direction, shouldFlip, overlayOffset, containerPadding, comboBoxWidth, comboBoxId, overlayStyles, listBoxStyles, optionStyles, sectionStyles, headingStyles, effectiveSelectedKey, isDisabled, disabledKeys, items, children, listStateRef, onSelectionChange, onClose, label, ariaLabel, compositeFocusProps, filter, size = "medium" }) {
	const { overlayProps: overlayPositionProps, placement, updatePosition } = useOverlayPosition({
		targetRef: triggerRef,
		overlayRef: popoverRef,
		placement: `${direction} start`,
		shouldFlip,
		isOpen,
		offset: overlayOffset,
		containerPadding
	});
	const { overlayProps: overlayBehaviorProps } = useOverlay({
		onClose,
		shouldCloseOnBlur: true,
		isOpen,
		isDismissable: true,
		shouldCloseOnInteractOutside: (el) => {
			const menuTriggerEl = el.closest("[data-popover-trigger]");
			if (!menuTriggerEl) return true;
			if (menuTriggerEl === triggerRef?.current) return true;
			return false;
		}
	}, popoverRef);
	useLayoutEffect$1(() => {
		if (isOpen && updatePosition) return chainRaf(() => {
			updatePosition();
		}, 3);
	}, [isOpen]);
	const placementDirection = placement?.split(" ")[0] || direction;
	return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(DisplayTransition, {
		isShown: isOpen,
		children: ({ phase, isShown, ref: transitionRef }) => /* @__PURE__ */ jsx(ComboBoxOverlayWrapper, {
			...mergeProps$1(overlayPositionProps, overlayBehaviorProps, compositeFocusProps),
			ref: popoverRef,
			style: overlayPositionProps.style,
			children: /* @__PURE__ */ jsx(ComboBoxOverlayElement, {
				ref: transitionRef,
				"data-placement": placementDirection,
				"data-phase": phase,
				mods: {
					open: isShown,
					hidden: phase === "unmounted"
				},
				styles: overlayStyles,
				style: { "--overlay-min-width": comboBoxWidth ? `${comboBoxWidth}px` : void 0 },
				children: /* @__PURE__ */ jsx(ListBox, {
					ref: listBoxRef,
					focusOnHover: true,
					disableSelectionToggle: true,
					id: `ComboBoxListBox-${comboBoxId}`,
					"aria-label": ariaLabel || (typeof label === "string" ? label : "Options"),
					selectedKey: effectiveSelectedKey,
					selectionMode: "single",
					isDisabled,
					disabledKeys,
					shouldUseVirtualFocus: true,
					items,
					filter,
					styles: listBoxStyles,
					optionStyles,
					sectionStyles,
					headingStyles,
					stateRef: listStateRef,
					size: "medium",
					shape: "popover",
					onSelectionChange,
					children
				})
			})
		})
	}) });
}
const ComboBox = forwardRef(function ComboBox(props, ref) {
	props = useProviderProps(props);
	props = useFormProps(props);
	props = useFieldProps(props, { valuePropsMapper: ({ value, onChange }) => {
		return {
			selectedKey: value ?? null,
			onSelectionChange(val) {
				onChange(val);
			}
		};
	} });
	let { qa, label, extra, labelStyles, isRequired, necessityIndicator, validationState, id, icon, prefix, isDisabled, autoFocus, autoComplete = "off", wrapperRef, inputRef, triggerRef, popoverRef, listBoxRef, isLoading, inputStyles, optionStyles, triggerStyles, listBoxStyles, overlayStyles, suffix, hideTrigger, message, description, size = "medium", direction = "bottom", shouldFlip = true, popoverTrigger = "input", suffixPosition = "before", filter, styles, labelSuffix, selectedKey, defaultSelectedKey, inputValue, defaultInputValue, onInputChange, isClearable, onClear, placeholder, allowsCustomValue, shouldCommitOnBlur = true, clearOnBlur, items, children: renderChildren, sectionStyles, headingStyles, isReadOnly, overlayOffset = 8, containerPadding = 8, onSelectionChange: externalOnSelectionChange, sortSelectedToTop: sortSelectedToTopProp, onOpenChange, onFocus, onBlur, onKeyDown, form, ...otherProps } = props;
	const comboBoxId = useMemo(() => generateRandomId(), []);
	const { effectiveSelectedKey, effectiveInputValue, isPopoverOpen, setIsPopoverOpen, setInternalSelectedKey, setInternalInputValue, isControlledKey, isControlledInput } = useComboBoxState({
		selectedKey,
		defaultSelectedKey,
		inputValue,
		defaultInputValue,
		comboBoxId
	});
	const sortSelectedToTopExplicit = sortSelectedToTopProp !== void 0;
	const sortSelectedToTop = sortSelectedToTopProp ?? (items ? true : false);
	const cachedItemsOrder = useRef(null);
	const selectionWhenClosed = useRef(null);
	styles = extractStyles(otherProps, PROP_STYLES, styles);
	ref = useCombinedRefs(ref);
	wrapperRef = useCombinedRefs(wrapperRef);
	inputRef = useCombinedRefs(inputRef);
	triggerRef = useCombinedRefs(triggerRef);
	popoverRef = useCombinedRefs(popoverRef);
	listBoxRef = useCombinedRefs(listBoxRef);
	const sortedItems = useCallback(() => {
		if (!items) return items;
		if (!sortSelectedToTop) return items;
		if (cachedItemsOrder.current) return cachedItemsOrder.current;
		if (sortSelectedToTopExplicit && !items) {
			console.warn("ComboBox: sortSelectedToTop only works with the items prop. Sorting will be skipped when using JSX children.");
			return items;
		}
		const selectedKey = isPopoverOpen ? effectiveSelectedKey : selectionWhenClosed.current;
		if (!selectedKey) return items;
		const itemsArray = Array.isArray(items) ? items : Array.from(items);
		const selectedItem = itemsArray.find((item) => {
			const key = item?.key ?? item?.id;
			return key != null && String(key) === String(selectedKey);
		});
		if (!selectedItem) return items;
		const sorted = [selectedItem, ...itemsArray.filter((item) => {
			const key = item?.key ?? item?.id;
			return key == null || String(key) !== String(selectedKey);
		})];
		if (isPopoverOpen) cachedItemsOrder.current = sorted;
		return sorted;
	}, [
		items,
		sortSelectedToTop,
		sortSelectedToTopExplicit,
		effectiveSelectedKey,
		isPopoverOpen
	])();
	let children = renderChildren;
	const renderFn = renderChildren;
	if (sortedItems && typeof renderFn === "function") try {
		children = Array.from(sortedItems).map((item, idx) => {
			const rendered = renderFn(item);
			if (React.isValidElement(rendered) && rendered.key == null) return React.cloneElement(rendered, { key: rendered?.key ?? item?.key ?? idx });
			return rendered;
		});
	} catch {}
	useEffect(() => {
		cachedItemsOrder.current = null;
	}, [items]);
	useEffect(() => {
		if (!isPopoverOpen) {
			selectionWhenClosed.current = effectiveSelectedKey != null ? String(effectiveSelectedKey) : null;
			cachedItemsOrder.current = null;
		}
	}, [isPopoverOpen, effectiveSelectedKey]);
	useEffect(() => {
		onOpenChange?.(isPopoverOpen);
	}, [isPopoverOpen]);
	const { filterFn, isFilterActive, setIsFilterActive } = useComboBoxFiltering({
		effectiveInputValue,
		filter
	});
	const localCollectionState = useListState({
		children,
		items: sortedItems,
		selectionMode: "none"
	});
	const { isFocused, focusProps } = useFocus$1({ isDisabled });
	const checkInputValidity = useCallback(() => {
		if (!effectiveInputValue.trim()) return {
			isValid: false,
			singleMatchKey: null,
			filteredCount: 0
		};
		const filteredNodes = filterFn(localCollectionState.collection);
		const filteredItems = [];
		for (const node of filteredNodes) if (node.type === "section" && node.childNodes) {
			for (const child of node.childNodes) if (child.type === "item") filteredItems.push({
				key: child.key,
				textValue: child.textValue || ""
			});
		} else if (node.type === "item") filteredItems.push({
			key: node.key,
			textValue: node.textValue || ""
		});
		const filteredCount = filteredItems.length;
		const exactMatch = filteredItems.find((item) => item.textValue.toLowerCase() === effectiveInputValue.trim().toLowerCase());
		if (exactMatch) return {
			isValid: true,
			singleMatchKey: exactMatch.key,
			filteredCount
		};
		if (filteredCount === 1) return {
			isValid: true,
			singleMatchKey: filteredItems[0].key,
			filteredCount
		};
		return {
			isValid: false,
			singleMatchKey: null,
			filteredCount
		};
	}, [
		effectiveInputValue,
		filterFn,
		localCollectionState.collection
	]);
	const handleCompositeBlur = useEvent(() => {
		if (allowsCustomValue) {
			if (shouldCommitOnBlur && effectiveInputValue && effectiveSelectedKey == null) {
				externalOnSelectionChange?.(effectiveInputValue);
				if (!isControlledKey) setInternalSelectedKey(effectiveInputValue);
				onBlur?.();
				setIsFilterActive(false);
				return;
			}
			if (!String(effectiveInputValue).trim()) {
				externalOnSelectionChange?.(null);
				if (!isControlledKey) setInternalSelectedKey(null);
				if (!isControlledInput) setInternalInputValue("");
				onInputChange?.("");
				onBlur?.();
				setIsFilterActive(false);
				return;
			}
		}
		if (!allowsCustomValue) {
			const { isValid, singleMatchKey } = checkInputValidity();
			if (isValid && singleMatchKey != null && singleMatchKey !== effectiveSelectedKey) {
				const label = getItemLabel(singleMatchKey);
				if (!isControlledKey) setInternalSelectedKey(singleMatchKey);
				if (!isControlledInput) setInternalInputValue(label);
				onInputChange?.(label);
				externalOnSelectionChange?.(singleMatchKey);
				onBlur?.();
				setIsFilterActive(false);
				return;
			}
			if (!isValid) {
				const trimmedInput = effectiveInputValue.trim();
				if (clearOnBlur || !trimmedInput) {
					externalOnSelectionChange?.(null);
					if (!isControlledKey) setInternalSelectedKey(null);
					if (!isControlledInput) setInternalInputValue("");
					onInputChange?.("");
					onBlur?.();
					setIsFilterActive(false);
					return;
				}
				const nextValue = effectiveSelectedKey != null ? getItemLabel(effectiveSelectedKey) : "";
				if (!isControlledInput) setInternalInputValue(nextValue);
				onInputChange?.(nextValue);
				onBlur?.();
				setIsFilterActive(false);
				return;
			}
		}
		const nextValue = effectiveSelectedKey != null ? getItemLabel(effectiveSelectedKey) : "";
		if (!isControlledInput) setInternalInputValue(nextValue);
		onInputChange?.(nextValue);
		onBlur?.();
		setIsFilterActive(false);
	});
	const { compositeFocusProps } = useCompositeFocus({
		wrapperRef,
		popoverRef,
		onFocus,
		onBlur: handleCompositeBlur,
		isDisabled
	});
	let isInvalid = validationState === "invalid";
	let validation = cloneElement(isInvalid ? InvalidIcon : ValidIcon);
	const listStateRef = useRef(null);
	const focusInitAttemptsRef = useRef(0);
	const getItemLabel = useCallback((key) => {
		return (localCollectionState?.collection?.getItem(key))?.textValue || String(key);
	}, [localCollectionState?.collection]);
	const handleSelectionChange = useEvent((selection) => {
		const key = Array.isArray(selection) ? selection[0] : selection;
		if (!isControlledKey) setInternalSelectedKey(key ?? null);
		if (key != null) {
			setIsFilterActive(false);
			const label = getItemLabel(key);
			if (!isControlledInput) setInternalInputValue(label);
			onInputChange?.(label);
		} else {
			if (!isControlledInput) setInternalInputValue("");
			onInputChange?.("");
			setIsFilterActive(false);
		}
		externalOnSelectionChange?.(key);
		setIsPopoverOpen(false);
		setTimeout(() => {
			inputRef.current?.focus();
		}, 0);
	});
	const handleInputChange = useEvent((e) => {
		const value = e.target.value;
		if (!isControlledInput) setInternalInputValue(value);
		onInputChange?.(value);
		setIsFilterActive(value.trim().length > 0);
		if (allowsCustomValue && effectiveSelectedKey != null) {
			if (!isControlledKey) setInternalSelectedKey(null);
			externalOnSelectionChange?.(null);
		}
		if (popoverTrigger !== "manual" && value && !isPopoverOpen) setIsPopoverOpen(true);
	});
	const [hasInitialized, setHasInitialized] = useState(false);
	useEffect(() => {
		if (hasInitialized || isControlledInput) return;
		if (defaultInputValue !== void 0) {
			setInternalInputValue(defaultInputValue);
			setHasInitialized(true);
			return;
		}
		if (defaultSelectedKey) {
			setInternalInputValue(getItemLabel(defaultSelectedKey));
			setHasInitialized(true);
		}
	}, [
		hasInitialized,
		isControlledInput,
		defaultInputValue,
		defaultSelectedKey,
		getItemLabel,
		children
	]);
	const lastSyncedSelectedKey = useRef(void 0);
	useEffect(() => {
		if (!isControlledKey || isControlledInput) return;
		if (lastSyncedSelectedKey.current !== void 0 && lastSyncedSelectedKey.current === effectiveSelectedKey) return;
		lastSyncedSelectedKey.current = effectiveSelectedKey;
		setInternalInputValue(effectiveSelectedKey != null ? getItemLabel(effectiveSelectedKey) : "");
	}, [
		isControlledKey,
		isControlledInput,
		effectiveSelectedKey,
		getItemLabel
	]);
	const handleInputFocus = useEvent((e) => {
		focusProps.onFocus?.(e);
		if (popoverTrigger === "focus" && !isPopoverOpen) setIsPopoverOpen(true);
	});
	const handleInputBlur = useEvent((e) => {
		focusProps.onBlur?.(e);
	});
	let showClearButton = isClearable && (allowsCustomValue ? effectiveInputValue !== "" : effectiveSelectedKey != null) && !isDisabled && !isReadOnly;
	const hasResults = useMemo(() => {
		if (!children) return false;
		if (!Array.isArray(children) && children === null) return false;
		if (localCollectionState?.collection) {
			const filteredNodes = filterFn(localCollectionState.collection);
			return Array.from(filteredNodes).flatMap((node) => {
				if (node.type === "section" && node.childNodes) return [...node.childNodes];
				return [node];
			}).length > 0;
		}
		return Array.isArray(children) ? children.length > 0 : true;
	}, [
		children,
		localCollectionState?.collection,
		filterFn
	]);
	let clearValue = useEvent(() => {
		if (!isControlledInput) setInternalInputValue("");
		onInputChange?.("");
		if (!isControlledKey) setInternalSelectedKey(null);
		externalOnSelectionChange?.(null);
		if (isPopoverOpen) setIsPopoverOpen(false);
		inputRef.current?.focus();
		onClear?.();
	});
	const { keyboardProps } = useComboBoxKeyboard({
		isPopoverOpen,
		listStateRef,
		hasResults,
		allowsCustomValue,
		effectiveInputValue,
		isClearable,
		onSelectionChange: handleSelectionChange,
		onClearValue: clearValue,
		onOpenPopover: () => setIsPopoverOpen(true),
		onClosePopover: () => setIsPopoverOpen(false),
		inputRef,
		setIsFilterActive,
		onKeyDown
	});
	if (icon) {
		icon = /* @__PURE__ */ jsx("div", {
			"data-element": "InputIcon",
			children: icon
		});
		if (prefix) prefix = /* @__PURE__ */ jsxs(Fragment$1, { children: [icon, prefix] });
		else prefix = icon;
	}
	let mods = useMemo(() => ({
		invalid: isInvalid,
		valid: validationState === "valid",
		disabled: isDisabled,
		hovered: false,
		focused: isFocused,
		loading: isLoading,
		prefix: !!prefix,
		suffix: true,
		clearable: showClearButton
	}), [
		isInvalid,
		validationState,
		isDisabled,
		isFocused,
		isLoading,
		prefix,
		showClearButton
	]);
	const comboBoxWidth = wrapperRef?.current?.offsetWidth;
	const shouldShowPopover = Boolean(isPopoverOpen && hasResults);
	useEffect(() => {
		if (isPopoverOpen && !hasResults) setIsPopoverOpen(false);
	}, [isPopoverOpen, hasResults]);
	const ensureInitialFocus = useCallback(() => {
		if (!shouldShowPopover) return;
		const listState = listStateRef.current;
		if (!listState) return;
		const { selectionManager, collection, disabledKeys, lastFocusSourceRef } = listState;
		if (!selectionManager || !collection) return;
		const collectFirstKey = () => {
			for (const node of collection) if (node.type === "item") {
				if (!disabledKeys?.has(node.key)) return node.key;
			} else if (node.childNodes) {
				for (const child of node.childNodes) if (child.type === "item" && !disabledKeys?.has(child.key)) return child.key;
			}
			return null;
		};
		if (lastFocusSourceRef) lastFocusSourceRef.current = "keyboard";
		const currentFocusedKey = selectionManager.focusedKey;
		let needsRefocus = false;
		if (currentFocusedKey == null) needsRefocus = true;
		else if (!(collection.getItem(currentFocusedKey) != null)) needsRefocus = true;
		if (needsRefocus) {
			let keyToFocus = null;
			if (effectiveSelectedKey != null && collection.getItem(effectiveSelectedKey) != null) keyToFocus = effectiveSelectedKey;
			else keyToFocus = collectFirstKey();
			if (keyToFocus != null) selectionManager.setFocusedKey(keyToFocus);
		}
	}, [shouldShowPopover, effectiveSelectedKey]);
	useLayoutEffect$1(() => {
		if (!shouldShowPopover) return;
		focusInitAttemptsRef.current = 0;
		const tick = () => {
			if (!shouldShowPopover) return;
			ensureInitialFocus();
			focusInitAttemptsRef.current += 1;
			if (focusInitAttemptsRef.current < 8 && listStateRef.current?.selectionManager?.focusedKey == null) requestAnimationFrame(tick);
		};
		requestAnimationFrame(() => requestAnimationFrame(tick));
	}, [shouldShowPopover, ensureInitialFocus]);
	const comboBoxField = /* @__PURE__ */ jsxs(ComboBoxWrapperElement, {
		ref: wrapperRef,
		mods,
		styles,
		style: { zIndex: isFocused ? 1 : "initial" },
		"data-size": size,
		...compositeFocusProps,
		children: [
			prefix ? /* @__PURE__ */ jsx("div", {
				"data-element": "Prefix",
				children: prefix
			}) : null,
			/* @__PURE__ */ jsx(ComboBoxInput, {
				qa: qa || "ComboBox",
				inputRef,
				id,
				value: effectiveInputValue,
				placeholder,
				isDisabled,
				isReadOnly,
				autoFocus,
				autoComplete,
				size,
				mods,
				inputStyles,
				keyboardProps,
				focusProps: {
					...focusProps,
					onBlur: handleInputBlur
				},
				isPopoverOpen,
				hasResults,
				comboBoxId,
				listStateRef,
				onChange: handleInputChange,
				onFocus: handleInputFocus
			}),
			/* @__PURE__ */ jsxs("div", {
				"data-element": "Suffix",
				children: [
					suffixPosition === "before" ? suffix : null,
					validationState || isLoading ? /* @__PURE__ */ jsxs(Fragment$1, { children: [validationState && !isLoading ? validation : null, isLoading ? /* @__PURE__ */ jsx(LoadingIcon, {}) : null] }) : null,
					suffixPosition === "after" ? suffix : null,
					showClearButton && /* @__PURE__ */ jsx(_CollectionItem.Action, {
						icon: /* @__PURE__ */ jsx(CloseIcon, {}),
						size,
						theme: validationState === "invalid" ? "danger" : void 0,
						qa: "ComboBoxClearButton",
						"data-no-trigger": hideTrigger ? "" : void 0,
						"aria-label": "Clear value",
						onPress: clearValue
					}),
					!hideTrigger ? /* @__PURE__ */ jsx(_CollectionItem.Action, {
						ref: triggerRef,
						"data-popover-trigger": true,
						icon: /* @__PURE__ */ jsx(DirectionIcon, { to: isPopoverOpen ? "up" : "down" }),
						qa: "ComboBoxTrigger",
						mods: {
							pressed: isPopoverOpen,
							disabled: isDisabled,
							loading: isLoading
						},
						"data-size": size,
						isDisabled,
						styles: triggerStyles,
						"aria-expanded": isPopoverOpen,
						"aria-haspopup": "listbox",
						"aria-label": "Show options",
						onPress: () => {
							if (!isDisabled) {
								const willOpen = !isPopoverOpen;
								setIsPopoverOpen(willOpen);
								if (willOpen) {
									inputRef.current?.focus();
									if (!hasResults) setIsFilterActive(false);
								}
							}
						}
					}) : null
				]
			}),
			/* @__PURE__ */ jsx(ComboBoxOverlay, {
				isOpen: shouldShowPopover,
				triggerRef: wrapperRef,
				popoverRef,
				listBoxRef,
				direction,
				shouldFlip,
				overlayOffset,
				containerPadding,
				comboBoxWidth,
				comboBoxId,
				overlayStyles,
				listBoxStyles,
				optionStyles,
				sectionStyles,
				headingStyles,
				effectiveSelectedKey,
				isDisabled,
				disabledKeys: props.disabledKeys,
				items: sortedItems,
				listStateRef,
				label,
				ariaLabel: props["aria-label"],
				compositeFocusProps,
				filter: filterFn,
				size,
				onSelectionChange: handleSelectionChange,
				onClose: () => setIsPopoverOpen(false),
				children
			})
		]
	});
	const { children: _, ...propsWithoutChildren } = props;
	return wrapWithField(comboBoxField, ref, propsWithoutChildren);
});
ComboBox.Item = _CollectionItem;
ComboBox.Section = Section;
Object.defineProperty(ComboBox, "cubeInputType", {
	value: "ComboBox",
	enumerable: false,
	configurable: false
});

//#endregion
export { ComboBox };
//# sourceMappingURL=ComboBox.js.map