"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.OptionsListPopoverSuggestions = void 0;
var _react = _interopRequireWildcard(require("react"));
var _uiTheme = require("@kbn/ui-theme");
var _eui = require("@elastic/eui");
var _types = require("../types");
var _options_list_strings = require("./options_list_strings");
var _options_list_embeddable = require("../embeddable/options_list_embeddable");
var _options_list_popover_empty_message = require("./options_list_popover_empty_message");
var _options_list_popover_suggestion_badge = require("./options_list_popover_suggestion_badge");
var _use_field_formatter = require("../../hooks/use_field_formatter");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

const OptionsListPopoverSuggestions = ({
  showOnlySelected,
  loadMoreSuggestions
}) => {
  var _optionsList$select;
  const optionsList = (0, _options_list_embeddable.useOptionsList)();
  const searchString = optionsList.select(state => state.componentState.searchString);
  const availableOptions = optionsList.select(state => state.componentState.availableOptions);
  const totalCardinality = optionsList.select(state => state.componentState.totalCardinality);
  const invalidSelections = optionsList.select(state => state.componentState.invalidSelections);
  const fieldSpec = optionsList.select(state => state.componentState.field);
  const sort = optionsList.select(state => state.explicitInput.sort);
  const fieldName = optionsList.select(state => state.explicitInput.fieldName);
  const hideExists = optionsList.select(state => state.explicitInput.hideExists);
  const singleSelect = optionsList.select(state => state.explicitInput.singleSelect);
  const existsSelected = optionsList.select(state => state.explicitInput.existsSelected);
  const selectedOptions = optionsList.select(state => state.explicitInput.selectedOptions);
  const dataViewId = optionsList.select(state => state.output.dataViewId);
  const isLoading = (_optionsList$select = optionsList.select(state => state.output.loading)) !== null && _optionsList$select !== void 0 ? _optionsList$select : false;
  const listRef = (0, _react.useRef)(null);
  const fieldFormatter = (0, _use_field_formatter.useFieldFormatter)({
    dataViewId,
    fieldSpec
  });
  const canLoadMoreSuggestions = (0, _react.useMemo)(() => totalCardinality ? (availableOptions !== null && availableOptions !== void 0 ? availableOptions : []).length < Math.min(totalCardinality, _types.MAX_OPTIONS_LIST_REQUEST_SIZE) : false, [availableOptions, totalCardinality]);

  // track selectedOptions and invalidSelections in sets for more efficient lookup
  const selectedOptionsSet = (0, _react.useMemo)(() => new Set(selectedOptions), [selectedOptions]);
  const invalidSelectionsSet = (0, _react.useMemo)(() => new Set(invalidSelections), [invalidSelections]);
  const suggestions = (0, _react.useMemo)(() => {
    return showOnlySelected ? selectedOptions : availableOptions !== null && availableOptions !== void 0 ? availableOptions : [];
  }, [availableOptions, selectedOptions, showOnlySelected]);
  const existsSelectableOption = (0, _react.useMemo)(() => {
    if (hideExists || !existsSelected && (showOnlySelected || (suggestions === null || suggestions === void 0 ? void 0 : suggestions.length) === 0)) return;
    return {
      key: 'exists-option',
      checked: existsSelected ? 'on' : undefined,
      label: _options_list_strings.OptionsListStrings.controlAndPopover.getExists(),
      className: 'optionsList__existsFilter',
      'data-test-subj': 'optionsList-control-selection-exists'
    };
  }, [suggestions, existsSelected, showOnlySelected, hideExists]);
  const [selectableOptions, setSelectableOptions] = (0, _react.useState)([]); // will be set in following useEffect
  (0, _react.useEffect)(() => {
    /* This useEffect makes selectableOptions responsive to search, show only selected, and clear selections */
    const options = (suggestions !== null && suggestions !== void 0 ? suggestions : []).map(suggestion => {
      var _fieldFormatter, _suggestion;
      if (typeof suggestion === 'string') {
        // this means that `showOnlySelected` is true, and doc count is not known when this is the case
        suggestion = {
          value: suggestion
        };
      }
      return {
        key: suggestion.value,
        label: (_fieldFormatter = fieldFormatter(suggestion.value)) !== null && _fieldFormatter !== void 0 ? _fieldFormatter : suggestion.value,
        checked: selectedOptionsSet !== null && selectedOptionsSet !== void 0 && selectedOptionsSet.has(suggestion.value) ? 'on' : undefined,
        'data-test-subj': `optionsList-control-selection-${suggestion.value}`,
        className: showOnlySelected && invalidSelectionsSet.has(suggestion.value) ? 'optionsList__selectionInvalid' : 'optionsList__validSuggestion',
        append: !showOnlySelected && (_suggestion = suggestion) !== null && _suggestion !== void 0 && _suggestion.docCount ? /*#__PURE__*/_react.default.createElement(_options_list_popover_suggestion_badge.OptionsListPopoverSuggestionBadge, {
          documentCount: suggestion.docCount
        }) : undefined
      };
    });
    if (canLoadMoreSuggestions) {
      options.push({
        key: 'loading-option',
        className: 'optionslist--loadingMoreGroupLabel',
        label: _options_list_strings.OptionsListStrings.popover.getLoadingMoreMessage(),
        isGroupLabel: true
      });
    } else if (options.length === _types.MAX_OPTIONS_LIST_REQUEST_SIZE) {
      options.push({
        key: 'no-more-option',
        className: 'optionslist--endOfOptionsGroupLabel',
        label: _options_list_strings.OptionsListStrings.popover.getAtEndOfOptionsMessage(),
        isGroupLabel: true
      });
    }
    setSelectableOptions(existsSelectableOption ? [existsSelectableOption, ...options] : options);
  }, [suggestions, availableOptions, showOnlySelected, selectedOptionsSet, invalidSelectionsSet, existsSelectableOption, canLoadMoreSuggestions, fieldFormatter]);
  const loadMoreOptions = (0, _react.useCallback)(() => {
    var _listRef$current;
    const listbox = (_listRef$current = listRef.current) === null || _listRef$current === void 0 ? void 0 : _listRef$current.querySelector('.euiSelectableList__list');
    if (!listbox) return;
    const {
      scrollTop,
      scrollHeight,
      clientHeight
    } = listbox;
    if (scrollTop + clientHeight >= scrollHeight - parseInt(_uiTheme.euiThemeVars.euiSizeXXL, 10)) {
      // reached the "bottom" of the list, where euiSizeXXL acts as a "margin of error" so that the user doesn't
      // have to scroll **all the way** to the bottom in order to load more options
      loadMoreSuggestions(totalCardinality !== null && totalCardinality !== void 0 ? totalCardinality : _types.MAX_OPTIONS_LIST_REQUEST_SIZE);
    }
  }, [loadMoreSuggestions, totalCardinality]);
  (0, _react.useEffect)(() => {
    const container = listRef.current;
    if (!isLoading && canLoadMoreSuggestions) {
      container === null || container === void 0 ? void 0 : container.addEventListener('scroll', loadMoreOptions, true);
      return () => {
        container === null || container === void 0 ? void 0 : container.removeEventListener('scroll', loadMoreOptions, true);
      };
    }
  }, [loadMoreOptions, isLoading, canLoadMoreSuggestions]);
  (0, _react.useEffect)(() => {
    var _listRef$current2;
    // scroll back to the top when changing the sorting or the search string
    const listbox = (_listRef$current2 = listRef.current) === null || _listRef$current2 === void 0 ? void 0 : _listRef$current2.querySelector('.euiSelectableList__list');
    listbox === null || listbox === void 0 ? void 0 : listbox.scrollTo({
      top: 0
    });
  }, [sort, searchString]);
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
    ref: listRef
  }, /*#__PURE__*/_react.default.createElement(_eui.EuiSelectable, {
    options: selectableOptions,
    renderOption: option => {
      return /*#__PURE__*/_react.default.createElement(_eui.EuiHighlight, {
        search: option.key === 'exists-option' ? '' : searchString.value
      }, option.label);
    },
    listProps: {
      onFocusBadge: false
    },
    "aria-label": _options_list_strings.OptionsListStrings.popover.getSuggestionsAriaLabel(fieldName, selectableOptions.length),
    emptyMessage: /*#__PURE__*/_react.default.createElement(_options_list_popover_empty_message.OptionsListPopoverEmptyMessage, {
      showOnlySelected: showOnlySelected
    }),
    onChange: (newSuggestions, _, changedOption) => {
      var _changedOption$key;
      const key = (_changedOption$key = changedOption.key) !== null && _changedOption$key !== void 0 ? _changedOption$key : changedOption.label;
      setSelectableOptions(newSuggestions);
      // the order of these checks matters, so be careful if rearranging them
      if (key === 'exists-option') {
        optionsList.dispatch.selectExists(!Boolean(existsSelected));
      } else if (showOnlySelected || selectedOptionsSet.has(key)) {
        optionsList.dispatch.deselectOption(key);
      } else if (singleSelect) {
        optionsList.dispatch.replaceSelection(key);
      } else {
        optionsList.dispatch.selectOption(key);
      }
    }
  }, list => list)));
};
exports.OptionsListPopoverSuggestions = OptionsListPopoverSuggestions;