"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;

var _lodash = require("lodash");

var _propTypes = _interopRequireDefault(require("prop-types"));

var _react = _interopRequireDefault(require("react"));

var _accessibility = require("../../utils/accessibility");

var _Input = _interopRequireDefault(require("../Input"));

var _class, _class2, _temp;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var INITIAL_SEARCH_PAGE_SIZE = 5;
var INPUT_DEBOUNCE_PERIOD = 120;

var SearchPicker = (0, _accessibility.keyboardTracker)(_class = (_temp = _class2 =
/*#__PURE__*/
function (_React$Component) {
  _inherits(SearchPicker, _React$Component);

  function SearchPicker() {
    var _getPrototypeOf2;

    var _this;

    _classCallCheck(this, SearchPicker);

    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(SearchPicker)).call.apply(_getPrototypeOf2, [this].concat(args)));

    _defineProperty(_assertThisInitialized(_this), "componentDidMount", function () {
      var _this$props = _this.props,
          searchFunction = _this$props.searchFunction,
          searchOptions = _this$props.searchOptions,
          setOnItemSelect = _this$props.setOnItemSelect,
          supportedTypes = _this$props.supportedTypes;
      setOnItemSelect(function () {
        return _this.handleItemSelected;
      });
      searchFunction(_objectSpread({}, searchOptions, {
        types: supportedTypes,
        query: "",
        per_page: INITIAL_SEARCH_PAGE_SIZE
      })).then(function (results) {
        // Make this Promise catch if the results are not an array
        _this.verifyResultOrThrow(results);

        _this.setState({
          defaultResults: results
        });
      })["finally"](function () {
        var searchQuery = _this.state.searchQuery; // Always set loading false as long as searchQuery is empty

        if (!searchQuery) {
          _this.setState({
            isLoading: false
          });
        }
      });
    });

    _defineProperty(_assertThisInitialized(_this), "componentDidUpdate", function () {
      var items = _this.getAvailableEntities();

      _this.props.setItemCount(items.length);
    });

    _defineProperty(_assertThisInitialized(_this), "state", {
      currentSearch: null,
      defaultResults: [],
      isLoading: true,
      results: null,
      searchQuery: ""
    });

    _defineProperty(_assertThisInitialized(_this), "handleItemSelected", function (index) {
      var onItemSelected = _this.props.onItemSelected;
      var searchQuery = _this.state.searchQuery;
      onItemSelected(index, _this.getAvailableEntities(), searchQuery);
    });

    _defineProperty(_assertThisInitialized(_this), "handleOnInput", function (event) {
      var value = event.target.value;

      _this.setState({
        searchQuery: value
      });

      if (value) {
        _this.handleOnInputDebounced(value);
      } else {
        var currentSearch = _this.state.currentSearch;

        if (currentSearch) {
          currentSearch.reject("Search interrupted by empty search query.");
        }

        _this.setState({
          isLoading: false,
          results: null,
          currentSearch: null
        });
      }
    });

    _defineProperty(_assertThisInitialized(_this), "executeNewSearch", function (query) {
      return new Promise(function (resolve, reject) {
        var _this$props2 = _this.props,
            searchFunction = _this$props2.searchFunction,
            searchOptions = _this$props2.searchOptions,
            supportedTypes = _this$props2.supportedTypes;
        var currentSearch = _this.state.currentSearch;

        if (currentSearch) {
          currentSearch.reject("Search interrupted by new search query: ".concat(query));
        }

        _this.setState({
          currentSearch: {
            resolve: resolve,
            reject: reject
          },
          isLoading: true
        });

        searchFunction(_objectSpread({}, searchOptions, {
          types: supportedTypes,
          query: query.toLowerCase()
        })).then(resolve, reject);
      });
    });

    _defineProperty(_assertThisInitialized(_this), "handleOnInputDebounced", (0, _lodash.debounce)(function (query) {
      _this.executeNewSearch(query).then(function (results) {
        var searchQuery = _this.state.searchQuery;

        _this.setState({
          isLoading: false,
          currentSearch: null
        }); // Ensure the query wasn't killed while waiting for the result.


        if (searchQuery) {
          _this.setState({
            results: results
          });
        }
      }, function (e) {
        /** Swallow rejections */
      });
    }, !!process.env.JEST_WORKER_ID ? 0 : INPUT_DEBOUNCE_PERIOD));

    _defineProperty(_assertThisInitialized(_this), "getAvailableEntities", function () {
      var _this$props3 = _this.props,
          selectedEntityIds = _this$props3.selectedEntityIds,
          filterAvailableEntities = _this$props3.filterAvailableEntities;

      if (typeof filterAvailableEntities === "function") {
        return filterAvailableEntities(_this.getResultSet(), selectedEntityIds);
      }

      return _this.getResultSet().filter(function (result) {
        return !selectedEntityIds.includes(result.id);
      });
    });

    _defineProperty(_assertThisInitialized(_this), "getResultSet", function () {
      var _this$state = _this.state,
          results = _this$state.results,
          defaultResults = _this$state.defaultResults;
      return results || defaultResults;
    });

    _defineProperty(_assertThisInitialized(_this), "getResultsText", function () {
      var supportedTypes = _this.props.supportedTypes;
      var _this$state2 = _this.state,
          isLoading = _this$state2.isLoading,
          searchQuery = _this$state2.searchQuery;

      var resultCount = _this.getAvailableEntities().length;

      var summaryNoun = supportedTypes.length > 1 ? "entities" : "".concat(supportedTypes[0], "s");
      var summary = "Recently created ".concat(summaryNoun);

      if (isLoading) {
        summary = "Searching for \"".concat(summaryNoun, "\" matching \"").concat(searchQuery, "\"");
      } else if (searchQuery) {
        if (resultCount) {
          summary = "Found ".concat(resultCount, " ").concat(summaryNoun, " matching \"").concat(searchQuery, "\"");
        } else {
          summary = "No ".concat(summaryNoun, " found matching \"").concat(searchQuery, "\"");
        }
      }

      return {
        summary: summary
      };
    });

    _defineProperty(_assertThisInitialized(_this), "verifyResultOrThrow", function (results) {
      if (!Array.isArray(results)) {
        throw new Error("SearchPicker props.searchFunction resolved with a value that is not an Array.");
      }
    });

    _defineProperty(_assertThisInitialized(_this), "renderInput", function () {
      var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var _this$props4 = _this.props,
          handleKeyDown = _this$props4.handleKeyDown,
          supportedTypes = _this$props4.supportedTypes;
      var searchQuery = _this.state.searchQuery;

      var _assertThisInitialize = _assertThisInitialized(_this),
          handleOnInput = _assertThisInitialize.handleOnInput;

      var placeholderNoun = supportedTypes.map(function (i) {
        return "".concat(i, "s");
      }).join(",");
      return _react["default"].createElement(_Input["default"], _extends({
        isFilter: true,
        onChange: handleOnInput,
        placeholder: "Search for ".concat(placeholderNoun),
        testSection: "filter-".concat(placeholderNoun, "-input"),
        type: "search",
        value: searchQuery,
        onKeyDown: handleKeyDown
      }, props));
    });

    return _this;
  }

  _createClass(SearchPicker, [{
    key: "render",

    /**
     * A simple render method that passes relevant state and controls to the
     * children.
     * @returns {React.Component}
     */
    value: function render() {
      var _this$props5 = this.props,
          children = _this$props5.children,
          currentFauxFocusIndex = _this$props5.currentFauxFocusIndex;
      var _this$state3 = this.state,
          searchQuery = _this$state3.searchQuery,
          isLoading = _this$state3.isLoading;
      var renderInput = this.renderInput,
          handleOnInputDebounced = this.handleOnInputDebounced;
      var availableEntities = this.getAvailableEntities();
      return children({
        availableEntities: availableEntities,
        isLoading: isLoading,
        renderInput: renderInput,
        searchQuery: searchQuery,
        makeSearchRequest: handleOnInputDebounced,
        currentFauxFocusIndex: currentFauxFocusIndex,
        resultsText: this.getResultsText()
      });
    }
  }]);

  return SearchPicker;
}(_react["default"].Component), _defineProperty(_class2, "propTypes", {
  /**
   * A render function that is invoked with the following info:
   *   availableEntities {Array<Entity>} - The list of entities available
   *                     based on the current input. If no query term is
   *                     entered, the default search collection is used.
   *
   *   isLoading         {Boolean} - Whether or not a search is in progress.
   *   renderInput       {Function<props>} - A function to render the <Input>
   *   searchQuery       {String} - The current search term
   *   currentFauxFocusIndex {Number} - Index of the item currently selected.
   *   resultsText       {Object} - An object containing search result
   *                     summaries based on the the types provided
   *                     See this.getResultsText
   *
   */
  children: _propTypes["default"].func.isRequired,

  /**
   * Which index in the list is currently selected.
   * via @keyboardTracker
   */
  currentFauxFocusIndex: _propTypes["default"].number.isRequired,

  /**
   * This function can be set when custom search results filtering is required
   * It will override the internal search results filtering
   *
   * @param {Array} searchResults
   * @param {Array} selectedEntityIds
   * @param {Array} filteredSearchResults
   */
  filterAvailableEntities: _propTypes["default"].func,

  /**
   * A handler for the input element event onKeyDown
   * via @keyboardTracker
   */
  handleKeyDown: _propTypes["default"].func.isRequired,

  /**
   * The element ref to use for capturing keyboard input.
   * via @keyboardTracker
   */
  keyboardRef: _propTypes["default"].shape({
    current: _propTypes["default"].elementType
  }),

  /**
   * Handler to call when an item is selected via the keyboard.
   */
  onItemSelected: _propTypes["default"].func.isRequired,

  /**
   * The function to invoke to execute an authenticated public API search.
   * Different consumers will have different methods of authenticating.
   *
   * @param {Object} requestConfig
   * @return {Promise}
   */
  searchFunction: _propTypes["default"].func.isRequired,

  /**
   * Additional options to pass to the search function when a search is
   * executed. Type and query will be overridden by this component.
   */
  searchOptions: _propTypes["default"].object,

  /**
   * An optional list of ids which should be considered "selected".
   * If provided, these ids will be filtered out of the list.
   */
  selectedEntityIds: _propTypes["default"].array,

  /**
   * Sets the number of items for the list index tracker.
   * via @keyboardTracker
   */
  setItemCount: _propTypes["default"].func,

  /**
   * Sets the item select callback fn for the list index tracker.
   * via @keyboardTracker
   */
  setOnItemSelect: _propTypes["default"].func,

  /* The entity types we should query search for. */
  supportedTypes: _propTypes["default"].array.isRequired
}), _defineProperty(_class2, "defaultProps", {
  searchOptions: {},
  selectedEntityIds: [],
  setItemCount: _lodash.noop,
  setOnItemSelect: _lodash.noop
}), _temp)) || _class;

var _default = SearchPicker;
exports["default"] = _default;