import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
import { cx } from '@emotion/css';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useCombobox } from 'downshift';
import React__default, { useId, useMemo, useCallback } from 'react';
import { t } from '@grafana/i18n';
import { useStyles2 } from '../../themes/ThemeContext.mjs';
import { Icon } from '../Icon/Icon.mjs';
import { AutoSizeInput } from '../Input/AutoSizeInput.mjs';
import { Input } from '../Input/Input.mjs';
import { Portal } from '../Portal/Portal.mjs';
import { ComboboxList } from './ComboboxList.mjs';
import { SuffixIcon } from './SuffixIcon.mjs';
import { itemToString } from './filter.mjs';
import { getComboboxStyles, MENU_OPTION_HEIGHT, MENU_OPTION_HEIGHT_DESCRIPTION } from './getComboboxStyles.mjs';
import { useComboboxFloat } from './useComboboxFloat.mjs';
import { useOptions } from './useOptions.mjs';
import { isNewGroup } from './utils.mjs';

"use strict";
const noop = () => {
};
const VIRTUAL_OVERSCAN_ITEMS = 4;
const Combobox = (props) => {
  const {
    options: allOptions,
    onChange,
    value: valueProp,
    placeholder: placeholderProp,
    isClearable,
    // this should be default false, but TS can't infer the conditional type if you do
    createCustomValue = false,
    id,
    width,
    minWidth,
    maxWidth,
    "aria-labelledby": ariaLabelledBy,
    "data-testid": dataTestId,
    autoFocus,
    onBlur,
    disabled,
    portalContainer,
    invalid,
    prefixIcon
  } = props;
  const value = typeof valueProp === "object" ? valueProp == null ? void 0 : valueProp.value : valueProp;
  const baseId = useId().replace(/:/g, "--");
  const {
    options: filteredOptions,
    groupStartIndices,
    updateOptions,
    asyncLoading,
    asyncError
  } = useOptions(props.options, createCustomValue);
  const isAsync = typeof allOptions === "function";
  const selectedItemIndex = useMemo(() => {
    if (isAsync) {
      return null;
    }
    if (valueProp === void 0 || valueProp === null) {
      return null;
    }
    const index = allOptions.findIndex((option) => option.value === value);
    if (index === -1) {
      return null;
    }
    return index;
  }, [valueProp, allOptions, value, isAsync]);
  const selectedItem = useMemo(() => {
    if (valueProp === void 0 || valueProp === null) {
      return null;
    }
    if (selectedItemIndex !== null && !isAsync) {
      return allOptions[selectedItemIndex];
    }
    return typeof valueProp === "object" ? valueProp : { value: valueProp, label: valueProp.toString() };
  }, [selectedItemIndex, isAsync, valueProp, allOptions]);
  const menuId = `${baseId}-downshift-menu`;
  const labelId = `${baseId}-downshift-label`;
  const styles = useStyles2(getComboboxStyles);
  const rangeExtractor = useCallback(
    (range) => {
      const startIndex = Math.max(0, range.startIndex - range.overscan);
      const endIndex = Math.min(filteredOptions.length - 1, range.endIndex + range.overscan);
      const rangeToReturn = Array.from({ length: endIndex - startIndex + 1 }, (_, i) => startIndex + i);
      const firstDisplayedOption = filteredOptions[rangeToReturn[0]];
      if (firstDisplayedOption == null ? void 0 : firstDisplayedOption.group) {
        const groupStartIndex = groupStartIndices.get(firstDisplayedOption.group);
        if (groupStartIndex !== void 0 && groupStartIndex < rangeToReturn[0]) {
          rangeToReturn.unshift(groupStartIndex);
        }
      }
      return rangeToReturn;
    },
    [filteredOptions, groupStartIndices]
  );
  const rowVirtualizer = useVirtualizer({
    count: filteredOptions.length,
    getScrollElement: () => scrollRef.current,
    estimateSize: (index) => {
      const firstGroupItem = isNewGroup(filteredOptions[index], index > 0 ? filteredOptions[index - 1] : void 0);
      const hasDescription = "description" in filteredOptions[index];
      const hasGroup = "group" in filteredOptions[index];
      let itemHeight = MENU_OPTION_HEIGHT;
      if (hasDescription) {
        itemHeight = MENU_OPTION_HEIGHT_DESCRIPTION;
      }
      if (firstGroupItem && hasGroup) {
        itemHeight += MENU_OPTION_HEIGHT;
      }
      return itemHeight;
    },
    overscan: VIRTUAL_OVERSCAN_ITEMS,
    rangeExtractor
  });
  const {
    isOpen,
    highlightedIndex,
    getInputProps,
    getMenuProps,
    getItemProps,
    selectItem
  } = useCombobox({
    menuId,
    labelId,
    inputId: id,
    items: filteredOptions,
    itemToString,
    selectedItem,
    // Don't change downshift state in the onBlahChange handlers. Instead, use the stateReducer to make changes.
    // Downshift calls change handlers on the render after so you can get sync/flickering issues if you change its state
    // in them.
    // Instead, stateReducer is called in the same tick as state changes, before that state is committed and rendered.
    onSelectedItemChange: ({ selectedItem: selectedItem2 }) => {
      if (isClearable) {
        onChange(selectedItem2);
      } else if (selectedItem2 !== null) {
        onChange(selectedItem2);
      }
    },
    defaultHighlightedIndex: selectedItemIndex != null ? selectedItemIndex : 0,
    scrollIntoView: () => {
    },
    onIsOpenChange: ({ isOpen: isOpen2, inputValue }) => {
      if (isOpen2 && inputValue === "") {
        updateOptions(inputValue);
      }
    },
    onHighlightedIndexChange: ({ highlightedIndex: highlightedIndex2, type }) => {
      if (type !== useCombobox.stateChangeTypes.MenuMouseLeave) {
        rowVirtualizer.scrollToIndex(highlightedIndex2);
      }
    },
    onStateChange: ({ inputValue: newInputValue, type, selectedItem: newSelectedItem }) => {
      switch (type) {
        case useCombobox.stateChangeTypes.InputChange:
          updateOptions(newInputValue != null ? newInputValue : "");
          break;
        default:
          break;
      }
    },
    stateReducer(state, actionAndChanges) {
      let { changes } = actionAndChanges;
      const menuBeingOpened = state.isOpen === false && changes.isOpen === true;
      const menuBeingClosed = state.isOpen === true && changes.isOpen === false;
      if (menuBeingOpened && changes.inputValue === state.inputValue) {
        changes = {
          ...changes,
          inputValue: ""
        };
      }
      if (menuBeingClosed) {
        if (changes.selectedItem) {
          changes = {
            ...changes,
            inputValue: itemToString(changes.selectedItem)
          };
        } else if (changes.inputValue !== "") {
          changes = {
            ...changes,
            inputValue: ""
          };
        }
      }
      return changes;
    }
  });
  const { inputRef, floatingRef, floatStyles, scrollRef } = useComboboxFloat(filteredOptions, isOpen);
  const isAutoSize = width === "auto";
  const InputComponent = isAutoSize ? AutoSizeInput : Input;
  const placeholder = (isOpen ? itemToString(selectedItem) : null) || placeholderProp;
  const loading = props.loading || asyncLoading;
  const inputSuffix = /* @__PURE__ */ jsxs(Fragment, { children: [
    value !== void 0 && value === (selectedItem == null ? void 0 : selectedItem.value) && isClearable && /* @__PURE__ */ jsx(
      Icon,
      {
        name: "times",
        className: styles.clear,
        title: t("combobox.clear.title", "Clear value"),
        tabIndex: 0,
        role: "button",
        onClick: () => {
          selectItem(null);
        },
        onKeyDown: (e) => {
          if (e.key === "Enter" || e.key === " ") {
            selectItem(null);
          }
        }
      }
    ),
    /* @__PURE__ */ jsx(SuffixIcon, { isLoading: loading || false, isOpen })
  ] });
  const { Wrapper, wrapperProps } = isAutoSize ? {
    Wrapper: "div",
    wrapperProps: { className: styles.adaptToParent }
  } : { Wrapper: React__default.Fragment };
  return /* @__PURE__ */ jsxs(Wrapper, { ...wrapperProps, children: [
    /* @__PURE__ */ jsx(
      InputComponent,
      {
        width: isAutoSize ? void 0 : width,
        ...isAutoSize ? { minWidth, maxWidth } : {},
        autoFocus,
        onBlur,
        prefix: prefixIcon && /* @__PURE__ */ jsx(Icon, { name: prefixIcon }),
        disabled,
        invalid,
        className: styles.input,
        suffix: inputSuffix,
        ...getInputProps({
          ref: inputRef,
          onChange: noop,
          // Empty onCall to avoid TS error https://github.com/downshift-js/downshift/issues/718
          "aria-labelledby": ariaLabelledBy,
          // Label should be handled with the Field component
          placeholder,
          "data-testid": dataTestId
        })
      }
    ),
    /* @__PURE__ */ jsx(Portal, { root: portalContainer, children: /* @__PURE__ */ jsx(
      "div",
      {
        className: cx(styles.menu, !isOpen && styles.menuClosed),
        style: {
          ...floatStyles,
          pointerEvents: "auto"
          // Override container's pointer-events: none
        },
        ...getMenuProps({
          ref: floatingRef,
          "aria-labelledby": ariaLabelledBy
        }),
        children: isOpen && /* @__PURE__ */ jsx(
          ComboboxList,
          {
            loading,
            options: filteredOptions,
            highlightedIndex,
            selectedItems: selectedItem ? [selectedItem] : [],
            scrollRef,
            getItemProps,
            error: asyncError
          }
        )
      }
    ) })
  ] });
};

export { Combobox, VIRTUAL_OVERSCAN_ITEMS };
//# sourceMappingURL=Combobox.mjs.map
