// packages/components/src/combobox-control/index.tsx
import clsx from "clsx";
import { __, _n, sprintf } from "@wordpress/i18n";
import { Component, useState, useMemo, useRef, useEffect } from "@wordpress/element";
import { useInstanceId } from "@wordpress/compose";
import { speak } from "@wordpress/a11y";
import { closeSmall } from "@wordpress/icons";
import { InputWrapperFlex } from "./styles.js";
import TokenInput from "../form-token-field/token-input.js";
import SuggestionsList from "../form-token-field/suggestions-list.js";
import BaseControl from "../base-control/index.js";
import Button from "../button/index.js";
import { FlexBlock } from "../flex/index.js";
import withFocusOutside from "../higher-order/with-focus-outside/index.js";
import { useControlledValue } from "../utils/hooks/index.js";
import { normalizeTextString } from "../utils/strings.js";
import { useDeprecated36pxDefaultSizeProp } from "../utils/use-deprecated-props.js";
import { withIgnoreIMEEvents } from "../utils/with-ignore-ime-events.js";
import { maybeWarnDeprecated36pxSize } from "../utils/deprecated-36px-size.js";
import Spinner from "../spinner/index.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
var noop = () => {
};
var DetectOutside = withFocusOutside(class extends Component {
  handleFocusOutside(event) {
    this.props.onFocusOutside(event);
  }
  render() {
    return this.props.children;
  }
});
var getIndexOfMatchingSuggestion = (selectedSuggestion, matchingSuggestions) => selectedSuggestion === null ? -1 : matchingSuggestions.indexOf(selectedSuggestion);
function ComboboxControl(props) {
  var _currentOption$label;
  const {
    __next40pxDefaultSize = false,
    value: valueProp,
    label,
    options,
    onChange: onChangeProp,
    onFilterValueChange = noop,
    hideLabelFromVision,
    help,
    allowReset = true,
    className,
    isLoading = false,
    messages = {
      selected: __("Item selected.")
    },
    __experimentalRenderItem,
    expandOnFocus = true,
    placeholder
  } = useDeprecated36pxDefaultSizeProp(props);
  const [value, setValue] = useControlledValue({
    value: valueProp,
    onChange: onChangeProp
  });
  const currentOption = options.find((option) => option.value === value);
  const currentLabel = (_currentOption$label = currentOption?.label) !== null && _currentOption$label !== void 0 ? _currentOption$label : "";
  const instanceId = useInstanceId(ComboboxControl, "combobox-control");
  const [selectedSuggestion, setSelectedSuggestion] = useState(currentOption || null);
  const [isExpanded, setIsExpanded] = useState(false);
  const [inputHasFocus, setInputHasFocus] = useState(false);
  const [inputValue, setInputValue] = useState("");
  const inputContainer = useRef(null);
  const matchingSuggestions = useMemo(() => {
    const startsWithMatch = [];
    const containsMatch = [];
    const match = normalizeTextString(inputValue);
    options.forEach((option) => {
      const index = normalizeTextString(option.label).indexOf(match);
      if (index === 0) {
        startsWithMatch.push(option);
      } else if (index > 0) {
        containsMatch.push(option);
      }
    });
    return startsWithMatch.concat(containsMatch);
  }, [inputValue, options]);
  const onSuggestionSelected = (newSelectedSuggestion) => {
    if (newSelectedSuggestion.disabled) {
      return;
    }
    setValue(newSelectedSuggestion.value);
    speak(messages.selected, "assertive");
    setSelectedSuggestion(newSelectedSuggestion);
    setInputValue("");
    setIsExpanded(false);
  };
  const handleArrowNavigation = (offset = 1) => {
    const index = getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions);
    let nextIndex = index + offset;
    if (nextIndex < 0) {
      nextIndex = matchingSuggestions.length - 1;
    } else if (nextIndex >= matchingSuggestions.length) {
      nextIndex = 0;
    }
    setSelectedSuggestion(matchingSuggestions[nextIndex]);
    setIsExpanded(true);
  };
  const onKeyDown = withIgnoreIMEEvents((event) => {
    let preventDefault = false;
    if (event.defaultPrevented) {
      return;
    }
    switch (event.code) {
      case "Enter":
        if (selectedSuggestion) {
          onSuggestionSelected(selectedSuggestion);
          preventDefault = true;
        }
        break;
      case "ArrowUp":
        handleArrowNavigation(-1);
        preventDefault = true;
        break;
      case "ArrowDown":
        handleArrowNavigation(1);
        preventDefault = true;
        break;
      case "Escape":
        setIsExpanded(false);
        setSelectedSuggestion(null);
        preventDefault = true;
        break;
      default:
        break;
    }
    if (preventDefault) {
      event.preventDefault();
    }
  });
  const onBlur = () => {
    setInputHasFocus(false);
  };
  const onFocus = () => {
    setInputHasFocus(true);
    if (expandOnFocus) {
      setIsExpanded(true);
    }
    onFilterValueChange("");
    setInputValue("");
  };
  const onClick = () => {
    setIsExpanded(true);
  };
  const onFocusOutside = () => {
    setIsExpanded(false);
  };
  const onInputChange = (event) => {
    const text = event.value;
    setInputValue(text);
    onFilterValueChange(text);
    if (inputHasFocus) {
      setIsExpanded(true);
    }
  };
  const handleOnReset = () => {
    setValue(null);
    inputContainer.current?.focus();
  };
  const handleResetStopPropagation = (event) => {
    event.stopPropagation();
  };
  useEffect(() => {
    const hasMatchingSuggestions = matchingSuggestions.length > 0;
    const hasSelectedMatchingSuggestions = getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions) > 0;
    if (hasMatchingSuggestions && !hasSelectedMatchingSuggestions) {
      setSelectedSuggestion(matchingSuggestions[0]);
    }
  }, [matchingSuggestions, selectedSuggestion]);
  useEffect(() => {
    const hasMatchingSuggestions = matchingSuggestions.length > 0;
    if (isExpanded) {
      const message = hasMatchingSuggestions ? sprintf(
        /* translators: %d: number of results. */
        _n("%d result found, use up and down arrow keys to navigate.", "%d results found, use up and down arrow keys to navigate.", matchingSuggestions.length),
        matchingSuggestions.length
      ) : __("No results.");
      speak(message, "polite");
    }
  }, [matchingSuggestions, isExpanded]);
  maybeWarnDeprecated36pxSize({
    componentName: "ComboboxControl",
    __next40pxDefaultSize,
    size: void 0
  });
  return /* @__PURE__ */ _jsx(DetectOutside, {
    onFocusOutside,
    children: /* @__PURE__ */ _jsx(BaseControl, {
      className: clsx(className, "components-combobox-control"),
      label,
      id: `components-form-token-input-${instanceId}`,
      hideLabelFromVision,
      help,
      children: /* @__PURE__ */ _jsxs("div", {
        className: "components-combobox-control__suggestions-container",
        tabIndex: -1,
        onKeyDown,
        children: [/* @__PURE__ */ _jsxs(InputWrapperFlex, {
          __next40pxDefaultSize,
          children: [/* @__PURE__ */ _jsx(FlexBlock, {
            children: /* @__PURE__ */ _jsx(TokenInput, {
              className: "components-combobox-control__input",
              instanceId,
              ref: inputContainer,
              placeholder,
              value: isExpanded ? inputValue : currentLabel,
              onFocus,
              onBlur,
              onClick,
              isExpanded,
              selectedSuggestionIndex: getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions),
              onChange: onInputChange
            })
          }), isLoading && /* @__PURE__ */ _jsx(Spinner, {}), allowReset && Boolean(value) && !isExpanded && /* @__PURE__ */ _jsx(Button, {
            size: "small",
            icon: closeSmall,
            onClick: handleOnReset,
            onKeyDown: handleResetStopPropagation,
            label: __("Reset")
          })]
        }), isExpanded && !isLoading && /* @__PURE__ */ _jsx(SuggestionsList, {
          instanceId,
          match: {
            label: inputValue,
            value: ""
          },
          displayTransform: (suggestion) => suggestion.label,
          suggestions: matchingSuggestions,
          selectedIndex: getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions),
          onHover: setSelectedSuggestion,
          onSelect: onSuggestionSelected,
          scrollIntoView: true,
          __experimentalRenderItem
        })]
      })
    })
  });
}
var combobox_control_default = ComboboxControl;
export {
  combobox_control_default as default
};
//# sourceMappingURL=index.js.map
