"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _dropdown = require("../dropdown");
var _typeaheadOptionList = _interopRequireWildcard(require("./typeahead-option-list"));
var _itemComponent = require("./item-component");
var _formField = require("../form-field");
var _message = require("../message");
var _input = require("../input");
var _useElementsSize = require("../hooks/use-elements-size");
var _useVisibleOptions = require("./use-visible-options");
var _useTranslation = require("../hooks/use-translation");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const defaultItem = /*#__PURE__*/(0, _jsxRuntime.jsx)(_itemComponent.ItemComponents.DefaultItem, {
  nameKey: "title"
});
let typeaheadId = 0;

/** Searchable select
 * [CAPITALIZATION] Use sentence case for options, label, and input text. Do not end with ellipses (...).
 * @midnight-gray-supported
 * @alpine-light-supported
 */
function Typeahead({
  defaultValue = '',
  onSearch = () => {},
  optionComponent = defaultItem,
  OptionList = _typeaheadOptionList.default,
  onBlur = () => {},
  onKeyDown = () => {},
  onClear = () => {},
  onSelect = () => {},
  onDelete = () => {},
  searchFields = [],
  placement = _dropdown.DROPDOWN_PLACEMENT_TYPES.BOTTOM_START,
  options = [],
  items = [],
  optionId,
  optionLabel = 'label',
  isMultiSelect = false,
  focused = false,
  isTouched = false,
  forceShowAllOnFocus = false,
  showAllOnFocus = false,
  isDisabled = false,
  disabledInfo,
  isRequired = false,
  isOptional = false,
  isClearable = false,
  optionalLabelText,
  handleFocus = () => {},
  placeholder = '',
  containerRef,
  inputRef: inputRefProp,
  helperTextPosition,
  testId = 'typeahead-input',
  id = '',
  label = '',
  labelTestId,
  ariaLabel,
  validity,
  validationTestId,
  asyncValidation,
  labelIcon,
  labelIconTooltipText = '',
  labelIconTestId = '',
  dropdownContentClass,
  wrapperClass,
  fieldClass,
  helperText,
  autoComplete = 'on',
  keepInvalidText = false,
  formatSearchValue,
  className,
  autoFocus = false,
  noItemsText,
  shouldCallOnSelectWithOldValue = false
}) {
  const {
    t
  } = (0, _useTranslation.useTranslation)();
  const listId = (0, _react.useMemo)(() => `typeahead-scrollable-container-${typeaheadId++}`, []);
  const conditionallyUsedInputRef = (0, _react.useRef)(null);
  const inputRef = inputRefProp || conditionallyUsedInputRef;
  const [searchValue, onSearchValueChange] = (0, _react.useState)(defaultValue);
  const [isOpen, toggleDropdown] = (0, _react.useState)(false);
  const [isShowAll, toggleShowAll] = (0, _react.useState)(false);
  const [openedWithKeyboard, setOpenedWithKeyboard] = (0, _react.useState)(false);
  const [isFocus, setFocus] = (0, _react.useState)(focused);
  const [touched, setIsTouched] = (0, _react.useState)(isTouched);
  const [active, setActive] = (0, _react.useState)(null);
  const validityType = validity?.type;
  const isValidationFailed = validityType === _message.MESSAGE_TYPES.ERROR;
  const isValidationSucceeded = validityType === _message.MESSAGE_TYPES.SUCCESS;
  const isValidationWarned = validityType === _message.MESSAGE_TYPES.WARNING;
  const validityDisplayLocation = validity?.displayLocation ?? _formField.MESSAGE_DISPLAY_LOCATION.INLINE;
  const showValidationResult = Boolean(validity?.value);

  // Multiple select typeahead requires to take width of container element
  const anchorRef = containerRef || inputRef;
  const [width] = (0, _useElementsSize.useElementsSize)(anchorRef, true);
  const openDropdown = () => toggleDropdown(true);
  const closeDropdown = () => {
    setOpenedWithKeyboard(false);
    toggleDropdown(false);
  };
  const handleSelect = (option, isLastAvailableOption = false) => {
    if (isMultiSelect) {
      // If option SHOULD close dropdown OR selected option is the last
      // option available, clear search value.
      if (!option?.shouldNotCloseDropdown || isLastAvailableOption) {
        onSearchValueChange('');
      }
    } else {
      setFocus(false);
      if (option) {
        onSearchValueChange(String(option[optionLabel]));
      }
    }
    if (isMultiSelect) {
      onSelect(option);
    } else if (String(option?.[optionLabel]) !== defaultValue || shouldCallOnSelectWithOldValue) {
      onSelect(option);
    }
    setIsTouched(true);
    if (!option?.shouldNotCloseDropdown) {
      toggleShowAll(false);
      closeDropdown();
      setActive(null);
    }
    if (!isMultiSelect) {
      handleFocus(false);
    }
  };
  const handleChange = event => {
    onSearchValueChange(event.target.value);
    onSearch(event.target.value);

    // Toggle off all results when the user types
    toggleShowAll(false);
    openDropdown();
    setIsTouched(true);
  };
  const handleKeyDown = event => {
    const {
      key,
      target
    } = event;

    // eslint-disable-next-line default-case
    switch (key) {
      case 'ArrowDown':
        if (!searchValue) {
          toggleShowAll(true);
          setOpenedWithKeyboard(true);
          openDropdown();
        }
        break;
      case 'Backspace':
        {
          if (isMultiSelect && !target.value && items.length) {
            const item = [...items].pop();
            if (item) {
              onDelete(item, items.length - 1);
            }
          }
          break;
        }
      case 'Tab':
        // Release the focus if tabbing away
        handleExit();
        break;
    }
    onKeyDown(event, searchValue);
  };
  function handleExit() {
    const isExact = !!searchValue && options.find(option => searchFields.some(key => option[key] === searchValue));
    if (!keepInvalidText) {
      if (isExact) {
        handleSelect(isExact);
      } else {
        onSearchValueChange('');
        onSearch('');
        onClear();
        if (!isMultiSelect) {
          handleSelect();
        }
      }
    }

    // Remove focus from typeahead container & input
    handleFocus(false);
    setFocus(false);
    closeDropdown();
  }
  const handleClear = () => {
    onSearchValueChange('');
    handleSelect();
    onClear();
  };
  const showClearButton = isClearable && searchValue !== '' && !isDisabled;
  const handleClickElsewhere = (0, _react.useCallback)(event => {
    // Handle focus to false if the click is outside of the typeahead container
    if (!anchorRef.current?.contains(event?.target)) {
      if (isFocus || isMultiSelect) {
        inputRef.current?.blur();
        handleExit();
      }
    }
  },
  // searchValue in dependency array needed for proper clear logic
  [isFocus, anchorRef, inputRef, searchValue]);
  (0, _react.useEffect)(() => {
    // Click handler on elsewhere to not trigger toggleFocus after dropdown selection.
    window.addEventListener('click', handleClickElsewhere);
    return () => {
      window.removeEventListener('click', handleClickElsewhere);
    };
  });
  (0, _react.useEffect)(() => {
    if (!isDisabled) {
      // Focus within input field if they click a surrounding element. This is implemented in the multi select typeahead
      if (focused) {
        inputRef.current?.focus();
        if (!!searchValue) {
          openDropdown();
        }
      }
      if ((showAllOnFocus && searchValue === '' || forceShowAllOnFocus) && isFocus) {
        toggleShowAll(true);
        openDropdown();
      }
    }
  }, [isDisabled, showAllOnFocus, forceShowAllOnFocus, isFocus, searchValue]);
  (0, _react.useEffect)(() => {
    if (!focused) {
      inputRef.current?.blur();
      closeDropdown();
    }
    setFocus(focused);
  }, [focused]);
  (0, _react.useEffect)(() => {
    onSearchValueChange(defaultValue || '');
  }, [defaultValue]);
  (0, _react.useEffect)(() => setIsTouched(isTouched), [isTouched]);
  const PopperWrapper = (0, _react.useMemo)(() => /*#__PURE__*/(0, _jsxRuntime.jsx)(_dropdown.PopperDropdownWrapper, {
    closeDropdown: e => {
      handleClickElsewhere(e);
    },
    anchorRef: anchorRef.current,
    open: isOpen,
    contentClassName: (0, _classnames.default)('typeahead-dropdown-content', dropdownContentClass),
    placement: placement,
    wrapperContentTestId: "typeahead-dropdown-content",
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)(OptionList, {
      optionComponent: optionComponent,
      searchFields: searchFields,
      closeDropdown: closeDropdown,
      searchValue: formatSearchValue && formatSearchValue(searchValue) || searchValue,
      handleSelect: handleSelect,
      handleMakeActive: activeId => setActive(activeId),
      inputRef: inputRef,
      optionId: optionId,
      optionLabel: optionLabel,
      options: options,
      width: width,
      showAll: isShowAll,
      openedWithKeyboard: openedWithKeyboard,
      items: items,
      listId: listId,
      noItemsText: noItemsText
    })
  }), [isOpen, width, searchValue, options, isShowAll, handleClickElsewhere]);
  const {
    isShowOptions
  } = (0, _useVisibleOptions.useVisibleOptions)({
    formatSearchValue,
    searchValue,
    options,
    searchFields,
    isShowAll
  });
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
    children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_input.Input, {
      required: isRequired,
      label: label,
      isDisabled: isDisabled,
      disabledInfo: disabledInfo,
      isOptional: isOptional,
      optionalLabelText: optionalLabelText ?? t('Optional'),
      labelTestId: labelTestId,
      labelIcon: labelIcon,
      labelIconTooltipText: labelIconTooltipText,
      labelIconTestId: labelIconTestId,
      helperText: helperText,
      helperTextPosition: helperTextPosition,
      placeholder: placeholder,
      "test-id": testId,
      ref: inputRef,
      value: searchValue,
      onBlur: () => {
        // clear state if input was cleared through ref
        if (inputRef?.current?.value === '' && searchValue !== '') {
          handleExit();
        }
        onBlur(searchValue);
      },
      onChange: handleChange,
      asyncValidation: asyncValidation,
      autoFocus: autoFocus,
      onFocus: () => {
        setFocus(true);
        handleFocus(true);
        setIsTouched(true);
      },
      onClear: showClearButton ? handleClear : undefined,
      onKeyDown: handleKeyDown,
      id: id,
      autoComplete: autoComplete,
      className: (0, _classnames.default)(className, 'typeahead-input-group'),
      wrapperClass: wrapperClass,
      fieldClass: (0, _classnames.default)(fieldClass, {
        'is-failed': touched && showValidationResult && isValidationFailed,
        'is-succeeded': touched && showValidationResult && isValidationSucceeded,
        'is-warned': touched && showValidationResult && isValidationWarned,
        'is-empty': searchValue === '' && touched && isRequired
      }),
      "aria-haspopup": "listbox",
      "aria-label": ariaLabel ?? undefined,
      ...(isOpen && active && {
        'aria-activedescendant': (0, _typeaheadOptionList.getOptionId)(active)
      }),
      ...(isShowOptions && {
        role: 'combobox',
        'aria-owns': listId,
        'aria-controls': listId,
        'aria-expanded': isOpen,
        'aria-autocomplete': 'list'
      })
    }), touched && validity && showValidationResult && validityDisplayLocation === _formField.MESSAGE_DISPLAY_LOCATION.INLINE && /*#__PURE__*/(0, _jsxRuntime.jsx)(_formField.ValidityMessages, {
      validationTestId: validationTestId,
      validity: validity,
      fieldId: id
    }), PopperWrapper]
  });
}
var _default = exports.default = Typeahead;