"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _lodashEs = require("lodash-es");
var _dropdown = require("../dropdown");
var _dropdownMenuContent = _interopRequireWildcard(require("./dropdown-menu-content"));
var _dropdownTrigger = _interopRequireDefault(require("./dropdown-trigger"));
var _button = require("../button");
var _types = require("../dropdown/types");
var _tooltip = require("../tooltip");
var _useTreeNavigation = require("../hooks/use-tree-navigation");
var _useReturnFocusOnClose = require("../hooks/use-return-focus-on-close");
var _useTranslation = require("../hooks/use-translation");
var _utils = require("./utils");
var _defaultLinkComponent = require("./default-link-component");
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 SEARCHABLE_FIELDS = {
  title: 'title',
  subtext: 'subtext',
  label: 'label' // for radios
};
const MIN_SEARCHABLE_ITEMS = 10;
let optionsId = 0;
function getSelectedItem(collection, selectedKey) {
  return collection.find(option => option.key === selectedKey);
}
function getFlattenedMultilevelOptions(collection) {
  return collection.flatMap(option => option.subItems ? option.subItems : option);
}
function getTotalNumberOfSearchableItems(options, footerOption, isSectioned) {
  function countSearchableItems(collection) {
    return collection.reduce((memo, option) => {
      const searchableFieldsCount = Object.values(SEARCHABLE_FIELDS).reduce((acc, value) => {
        if (option[value]) {
          return acc + 1;
        }
        return acc;
      }, 0);
      return memo + searchableFieldsCount + countSearchableItems(option.radioOptions || []);
    }, 0);
  }
  let numberOfSearchableItems = isSectioned ? countSearchableItems((0, _utils.getFlattenedSubsectionOptions)(options)) : countSearchableItems(options);
  if (footerOption) {
    // footer option should only have title
    numberOfSearchableItems++;
  }
  return numberOfSearchableItems;
}
function matchBy(param, query) {
  return param?.toLowerCase().includes(query.trim().toLowerCase());
}
function filterBy(option, query) {
  // we should be filtering by visible items
  return option.isShown !== false && (matchBy(String(option[SEARCHABLE_FIELDS.title]), query) || matchBy(String(option[SEARCHABLE_FIELDS.subtext]), query) || matchBy(String(option[SEARCHABLE_FIELDS.label]), query));
}
function getFilteredOptions(options, query) {
  return options.reduce((memo, option) => {
    let filteredOption;
    if (option.subOptions) {
      const filteredSubOptions = getFilteredOptions(option.subOptions, query);
      filteredOption = filteredSubOptions.length && {
        ...option,
        subOptions: filteredSubOptions
      };
    } else if (option.radioOptions) {
      const filteredRadioOptions = option.radioOptions.filter(radioOption => filterBy(radioOption, query));
      filteredOption = filteredRadioOptions.length && {
        ...option,
        radioOptions: filteredRadioOptions
      };
    } else if (typeof option.title !== 'string' && option.searchableValue) {
      // title can be "node", in this case 'searchableValue' is being passed along with 'title'
      filteredOption = matchBy(option.searchableValue, query) && option;
    } else if (typeof option.title !== 'string' && !option.searchableValue && !option.radioOptions) {
      // eslint-disable-next-line no-console
      console.error('for search to work properly with React nodes, searchableValue must be provided');
    } else if (option.title || option.subtext) {
      // sectioned options also have titles
      // but we are only searching by their children
      filteredOption = filterBy(option, query) && option;
    }
    if (filteredOption) {
      memo.push(filteredOption);
    }
    return memo;
  }, []);
}
/** Powerful dropdown component aimed at supporting standard dropdown usages in the app
 * [CAPITALIZATION] Use title case for labels, no colon at the end
 * [CAPITALIZATION] Use sentence case for selection
 * @midnight-gray-supported
 * @alpine-light-supported
 */
const DropdownMenu = ({
  options: originalOptions,
  footerOption: originalFooterOption,
  triggerId = '',
  triggerTestId = '',
  triggerTourId,
  contentTestId = '',
  wrapperContentTestId = '',
  selectedItemTestId = '',
  defaultTriggerText = '',
  labelText = '',
  helperText = '',
  noResultsText,
  selectedKey,
  contentClassName = '',
  triggerClassName = '',
  triggerIsIcon = false,
  triggerTooltip = '',
  triggerAriaLabelText = '',
  triggerTooltipPlacement = _tooltip.TOOLTIP_PLACEMENT_TYPES.BOTTOM_END,
  triggerDynamicContentWrapperClassName,
  triggerDynamicContent,
  triggerAccentType = _button.ACCENT_TYPES.COMMAND,
  triggerTooltipDelayType = _tooltip.TOOLTIP_DELAY_TYPES.SLOW,
  triggerOnClickHandler = () => {},
  triggerTooltipDocLink,
  triggerHideChevron = false,
  triggerTabIndex,
  triggerButtonSize,
  placement = _types.DROPDOWN_PLACEMENT_TYPES.BOTTOM_END,
  selectbox,
  isSearchable = true,
  searchPlaceholderText,
  searchAutoComplete = 'off',
  isDisabled = false,
  hasErrored = false,
  hasSuccess = false,
  hasWarnings = false,
  isOptional = false,
  optionalLabelText,
  minSearchableItems = MIN_SEARCHABLE_ITEMS,
  onSelect = () => {},
  onSearch,
  setDefaultOptionsListOnClose = true,
  destroyOnScroll = false,
  isMultiLevel = false,
  isMultiLevelSectioned = false,
  required = false,
  onOpen,
  onClose,
  renderCustomDropdownHeader = () => null,
  searchQuery: searchQueryValue = '',
  dropdownProps = {},
  dropdownMenuContentProps = {},
  LinkComponent = _defaultLinkComponent.DefaultLinkComponent
}) => {
  const {
    t
  } = (0, _useTranslation.useTranslation)();
  const triggerUniqueId = (0, _react.useMemo)(() => (0, _lodashEs.uniqueId)('dropmenu-trigger-'), []);
  const [isOpen, setIsOpen] = (0, _react.useState)(false);
  const [isKeyboardNavigationDisabled, setIsKeyboardNavigationDisabled] = (0, _react.useState)(true);
  const triggerOnClickCallback = (_, isKeyEvent) => {
    if (isKeyEvent) {
      setIsKeyboardNavigationDisabled(false);
    } else {
      setIsKeyboardNavigationDisabled(true);
    }
    triggerOnClickHandler(_, isKeyEvent);
  };
  const dropdownContentId = (0, _react.useMemo)(() => `dropmenu-container-${optionsId++}`, []);
  const triggerOptions = {
    defaultTriggerText,
    triggerClassName,
    triggerIsIcon,
    triggerDynamicContentWrapperClassName,
    triggerDynamicContent,
    triggerOnClickHandler: triggerOnClickCallback,
    triggerId: triggerId || triggerTestId || triggerUniqueId,
    triggerTestId,
    triggerTourId,
    triggerTooltip,
    triggerTooltipPlacement,
    triggerHideChevron,
    triggerTooltipDocLink,
    triggerAccentType,
    triggerTooltipDelayType,
    triggerAriaLabelText,
    labelText,
    selectedItemTestId,
    helperText,
    required,
    triggerTabIndex,
    dropdownContentId,
    triggerButtonSize
  };
  const options = (0, _react.useMemo)(() => (0, _utils.getOptionsWithPrivateKey)(originalOptions), [originalOptions]);
  const footerOption = (0, _react.useMemo)(() => originalFooterOption ? (0, _utils.getFooterOptionWithPrivateKey)(originalFooterOption) : null, [originalFooterOption]);
  const [searchQuery, setSearchQuery] = (0, _react.useState)(searchQueryValue);
  const [renderedOptions, setRenderedOptions] = (0, _react.useState)(options);
  const isSectioned = !!options.length && options.every(option => option.subOptions);
  const selectedItem = !isSectioned ? getSelectedItem(getFlattenedMultilevelOptions(options), selectedKey) : getSelectedItem((0, _utils.getFlattenedSubsectionOptions)(options), selectedKey);
  const totalNumberOfSearchableItems = getTotalNumberOfSearchableItems(options, footerOption, isSectioned);

  // When options were updated with onSelect and shouldNotCloseDropdown was passed,
  // we need to update the rendered list. especially viable for checkbox and radio options
  (0, _react.useEffect)(() => {
    // If using an API connected search, we don't want to cause an infinite loop
    // since a search already refreshes the list of options.
    if (!onSearch) {
      handleSearch();
    }
    if (onSearch) {
      setRenderedOptions(options);
    }
  }, [options]);
  (0, _react.useEffect)(() => {
    setSearchQuery(searchQueryValue);
  }, [searchQueryValue]);
  function setDefaultOptionsList() {
    if (onSearch) {
      // clean query for onSearch if it was set and DropdownMenu was closed
      onSearch('');
      return;
    }
    setRenderedOptions(options);
  }
  function handleSearch(query) {
    if (onSearch) {
      onSearch(query);
      setSearchQuery(query ?? '');
    } else {
      if (!query || !query.length) {
        return setDefaultOptionsList();
      }
      const filteredOptions = getFilteredOptions(options, query);
      setRenderedOptions(filteredOptions);
    }
  }
  const showSearch = isSearchable && !isMultiLevel && !isMultiLevelSectioned && (totalNumberOfSearchableItems > minSearchableItems || !!onSearch);
  const showNoResults = !renderedOptions.length;
  const onSelectWrapper = (option, ...args) => {
    if (option?.privateKey) {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const {
        privateKey,
        ...omittedOption
      } = option;
      return onSelect(omittedOption, ...args);
    }
    return onSelect(option, ...args);
  };
  const fitWidthToRef = Boolean(selectbox) && (typeof selectbox !== 'boolean' ? selectbox?.fitWidthToSelector || Boolean(selectbox) : Boolean(selectbox));
  const adapter = (0, _react.useMemo)(() => (0, _dropdownMenuContent.getMenuTreeNavigationAdapter)({
    isExpandedByDefault: false
  }), []);
  const treeNavigationOptions = (0, _react.useMemo)(() => {
    let navigatebleleOptions = !isSectioned ? renderedOptions : (0, _utils.getFlattenedSubsectionOptions)(renderedOptions);
    const result = navigatebleleOptions.reduce((prev, curr) => {
      let toAdd = [curr];
      if (curr.radioOptions) {
        // add DropdownMenuOption for each radioOption for keyboard navigation
        toAdd = [];
        curr.radioOptions.forEach(ropt => {
          toAdd.push({
            ...ropt,
            groupIdentifier: curr.groupIdentifier,
            activeItemId: curr.activeItemId,
            disabled: curr.disabled
          });
        });
      }
      return [...prev, ...toAdd];
    }, []);
    navigatebleleOptions = result;
    let treeOptions = footerOption ? [...navigatebleleOptions, footerOption] : navigatebleleOptions;
    if (showSearch) {
      const searchNode = {
        id: _dropdownMenuContent.SEARCH_ID,
        children: [],
        parent: null,
        parents: [],
        label: '',
        isDisabled: false,
        isExpanded: false,
        isExpandable: false,
        isFocused: true,
        isLeaf: true
      };
      treeOptions = [searchNode, ...treeOptions];
    }
    return treeOptions;
  }, [showSearch, isSectioned, renderedOptions, footerOption, isOpen]);

  // last focused element is not limited to only trigger, it can also be a table cell
  (0, _useReturnFocusOnClose.useReturnFocusOnClose)({
    isShown: isOpen
  });
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_useTreeNavigation.TreeNavigationProvider, {
    adapter: adapter,
    tree: treeNavigationOptions,
    isDisabled: isKeyboardNavigationDisabled,
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dropdown.Dropdown, {
      ...dropdownProps,
      placement: placement,
      fitWidthToRef: fitWidthToRef,
      destroyOnScroll: destroyOnScroll,
      isDisabled: isDisabled,
      hasCustomFocusHandling: true,
      onClose: () => {
        if (setDefaultOptionsListOnClose) {
          setDefaultOptionsList();
        }
        setIsKeyboardNavigationDisabled(true);
        onClose?.();
      },
      contentClassName: (0, _classnames.default)(contentClassName, {
        'selectbox-dropdown-content': Boolean(selectbox)
      }),
      onOpen: openedWithKeyboard => {
        setIsOpen(openedWithKeyboard);
        onOpen?.(openedWithKeyboard);
      },
      testId: wrapperContentTestId,
      id: dropdownContentId,
      renderAnchor: ({
        ref,
        onClick,
        isOpen: open
      }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_dropdownTrigger.default, {
        ref: ref,
        onClick: onClick,
        isOpen: open,
        isDisabled: isDisabled,
        hasErrored: hasErrored,
        hasSuccess: hasSuccess,
        hasWarnings: hasWarnings,
        selectedItem: selectedItem,
        options: options,
        selectbox: selectbox,
        triggerOptions: triggerOptions,
        isSectioned: isSectioned,
        isOptional: isOptional,
        optionalLabelText: optionalLabelText ?? t('Optional')
      }),
      children: ({
        closeDropdown
      }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_dropdownMenuContent.default, {
        ...dropdownMenuContentProps,
        options: renderedOptions,
        footerOption: footerOption,
        onSelect: onSelectWrapper,
        isSectioned: isSectioned,
        isMultiLevel: isMultiLevel,
        isMultiLevelSectioned: isMultiLevelSectioned,
        closeDropdown: (...args) => {
          closeDropdown(...args);
          setIsOpen(false);
        },
        searchId: _dropdownMenuContent.SEARCH_ID,
        handleSearch: handleSearch,
        showSearch: showSearch,
        searchPlaceholderText: searchPlaceholderText ?? t('Search'),
        searchAutoComplete: searchAutoComplete,
        searchQuery: searchQuery,
        showNoResults: showNoResults,
        noResultsText: noResultsText ?? t('No search results found.'),
        selectedKey: selectedKey,
        selectedItem: selectedItem,
        contentTestId: contentTestId,
        renderCustomHeader: renderCustomDropdownHeader,
        labelledBy: triggerOptions.triggerId,
        LinkComponent: LinkComponent
      })
    })
  });
};
var _default = exports.default = DropdownMenu;