"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.Autocomplete = void 0;
var _react = _interopRequireWildcard(require("react"));
var _Button = _interopRequireDefault(require("../Button/Button"));
var _DropdownMenu = _interopRequireDefault(require("../Dropdown/DropdownMenu"));
var _classnames = _interopRequireDefault(require("classnames"));
var _mergeRefs = _interopRequireDefault(require("../utilities/mergeRefs"));
var _useId = _interopRequireDefault(require("../utilities/useId"));
var _config = require("../config");
var _utils = require("./utils");
var _i18n = require("../i18n");
var _reactAria = require("../react-aria");
var _excluded = ["ariaClearLabel", "autoCompleteLabel", "autoFocus", "children", "className", "clearInputText", "clearSearchButton", "focusTrigger", "inputRef", "items", "itemToString", "label", "labelId", "loading", "loadingMessage", "noResultsMessage", "onChange", "onInputValueChange"]; // from react-aria
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(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; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : 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); 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 = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
// from react-stately

/**
 * The Autocomplete component wraps a TextField component and turns it into a combobox,
 * where a user can type into the text field and see matching results. They can then
 * select one of these results from the list, which will trigger an `onChange` event on
 * the Autocomplete.
 *
 * The two event handlers that should be used when this is a controlled component are
 * `onChange` and `onInputValueChange`. They are defined on the Autocomplete component
 * and not its child TextField component.
 *
 * As the user types and `onInputValueChange` is called, you should be supplying relevant
 * results to the Autocomplete through the `items` prop. The `items` prop is an array of
 * objects. Passing an empty array will show a "No results" message. If you do not yet
 * want to show results—for instance, because they haven't typed enough characters yet to
 * make a database call—the `items` prop should remain be undefined. If you are still
 * loading the results, use the `loading` boolean prop to display the loading message to
 * the user.
 *
 * For information about how and when to use this component,
 * [refer to its full documentation page](https://design.cms.gov/components/autocomplete/).
 */
var Autocomplete = function Autocomplete(props) {
  var _textField$props$labe;
  var id = (0, _useId.default)('autocomplete--', props.id);
  var menuId = "".concat(id, "__menu");
  var ariaClearLabel = props.ariaClearLabel,
    autoCompleteLabel = props.autoCompleteLabel,
    autoFocus = props.autoFocus,
    children = props.children,
    className = props.className,
    clearInputText = props.clearInputText,
    clearSearchButton = props.clearSearchButton,
    focusTrigger = props.focusTrigger,
    userInputRef = props.inputRef,
    items = props.items,
    itemToString = props.itemToString,
    menuHeading = props.label,
    menuHeadingId = props.labelId,
    loading = props.loading,
    loadingMessage = props.loadingMessage,
    noResultsMessage = props.noResultsMessage,
    onChange = props.onChange,
    onInputValueChange = props.onInputValueChange,
    autocompleteProps = _objectWithoutProperties(props, _excluded);

  // Determine what we'll show based on state
  var reactStatelyItems = [];
  var statusMessage;
  if (items !== null && items !== void 0 && items.length) {
    reactStatelyItems = (0, _utils.renderReactStatelyItems)(items, itemToString);
  } else if (loading) {
    // If we're waiting for results to load, show the non-selected message
    statusMessage = (0, _utils.renderStatusMessage)(loadingMessage !== null && loadingMessage !== void 0 ? loadingMessage : (0, _i18n.t)('autocomplete.loadingMessage'));
  } else if (items) {
    // If we have no results (empty array), show the non-selected message
    statusMessage = (0, _utils.renderStatusMessage)(noResultsMessage !== null && noResultsMessage !== void 0 ? noResultsMessage : (0, _i18n.t)('autocomplete.noResultsMessage'));
  }
  var textField = (0, _utils.getTextFieldChild)(children);
  var size = textField.props.size;
  var labelId = (_textField$props$labe = textField.props.labelId) !== null && _textField$props$labe !== void 0 ? _textField$props$labe : "".concat(id, "__label");
  var state = (0, _reactAria.useComboBoxState)(_objectSpread(_objectSpread({}, autocompleteProps), {}, {
    allowsCustomValue: true,
    allowsEmptyCollection: true,
    children: reactStatelyItems,
    inputValue: textField.props.value,
    onInputChange: onInputValueChange ? function (value) {
      onInputValueChange(value);
    } : undefined,
    onSelectionChange: onChange ? function (selectedKey) {
      var selectedItem = items ? items.find(function (item) {
        return selectedKey === item.id;
      }) : undefined;
      // We don't call onChange when the user deletes text, even though react-aria will call
      // this function with `null` if the input is cleared out. This is to maintain backwards
      // compatability, but we could consider changing this behavior in the future. If we
      // decide to remove this check, we can also remove the explicit `onChange` call in the
      // clear button handler.
      if (selectedItem) {
        onChange(selectedItem);
      }
    } : undefined
  }));
  var inputRef = (0, _react.useRef)();
  var listBoxRef = (0, _react.useRef)();
  var wrapperRef = (0, _react.useRef)();
  var useComboboxProps = (0, _reactAria.useComboBox)(_objectSpread(_objectSpread({}, autocompleteProps), {}, {
    name: textField.props.name,
    label: textField.props.label,
    isDisabled: textField.props.disabled,
    inputRef: inputRef,
    listBoxRef: listBoxRef,
    popoverRef: listBoxRef
  }), state);

  // The display of bottom placed errorMessages in TextField breaks the Autocomplete's UI design.
  // Add errorMessageClassName to fix the styles for bottom placed errors
  var bottomError = (textField.props.errorPlacement === 'bottom' || (0, _config.config)().errorPlacementDefault === 'bottom') && textField.props.errorMessage != null;
  var errorMessageClassName = (0, _classnames.default)(textField.props.errorMessageClassName, bottomError && 'ds-c-autocomplete__error-message', bottomError && clearSearchButton && 'ds-c-autocomplete__error-message-clear-btn');
  var textFieldProps = (0, _utils.removeUndefined)(_objectSpread(_objectSpread({}, useComboboxProps.inputProps), {}, {
    autoComplete: autoCompleteLabel,
    autoFocus: autoFocus || focusTrigger,
    'aria-activedescendant': useComboboxProps.inputProps['aria-activedescendant'] ? (0, _utils.getActiveDescendant)(id, state, items) : undefined,
    'aria-controls': menuId,
    'aria-labelledby': labelId,
    errorMessageClassName: errorMessageClassName,
    id: id,
    labelId: labelId,
    inputRef: (0, _mergeRefs.default)([inputRef, userInputRef]),
    // Restores previous functionality where if you had typed characters into the text
    // field to get results and then blur away and come back, it'll open the results
    // list again without having to press anything on the keyboard.
    onFocus: function onFocus(event) {
      var _useComboboxProps$inp, _useComboboxProps$inp2, _textField$props$onFo, _textField$props;
      (_useComboboxProps$inp = (_useComboboxProps$inp2 = useComboboxProps.inputProps).onFocus) === null || _useComboboxProps$inp === void 0 ? void 0 : _useComboboxProps$inp.call(_useComboboxProps$inp2, event);
      (_textField$props$onFo = (_textField$props = textField.props).onFocus) === null || _textField$props$onFo === void 0 ? void 0 : _textField$props$onFo.call(_textField$props, event);
      state.open();
    },
    // Allow the user to continue to attach their own event handlers to the TextField.
    // The following event handlers would normally be overwritten by useCombobox.
    onChange: function onChange(event) {
      var _useComboboxProps$inp3, _useComboboxProps$inp4, _textField$props$onCh, _textField$props2;
      (_useComboboxProps$inp3 = (_useComboboxProps$inp4 = useComboboxProps.inputProps).onChange) === null || _useComboboxProps$inp3 === void 0 ? void 0 : _useComboboxProps$inp3.call(_useComboboxProps$inp4, event);
      (_textField$props$onCh = (_textField$props2 = textField.props).onChange) === null || _textField$props$onCh === void 0 ? void 0 : _textField$props$onCh.call(_textField$props2, event);
    },
    onBlur: function onBlur(event) {
      var _useComboboxProps$inp5, _useComboboxProps$inp6, _textField$props$onBl, _textField$props3;
      (_useComboboxProps$inp5 = (_useComboboxProps$inp6 = useComboboxProps.inputProps).onBlur) === null || _useComboboxProps$inp5 === void 0 ? void 0 : _useComboboxProps$inp5.call(_useComboboxProps$inp6, event);
      (_textField$props$onBl = (_textField$props3 = textField.props).onBlur) === null || _textField$props$onBl === void 0 ? void 0 : _textField$props$onBl.call(_textField$props3, event);
    },
    onTouchEnd: function onTouchEnd(event) {
      var _useComboboxProps$inp7, _useComboboxProps$inp8, _textField$props$onTo, _textField$props4;
      (_useComboboxProps$inp7 = (_useComboboxProps$inp8 = useComboboxProps.inputProps).onTouchEnd) === null || _useComboboxProps$inp7 === void 0 ? void 0 : _useComboboxProps$inp7.call(_useComboboxProps$inp8, event);
      (_textField$props$onTo = (_textField$props4 = textField.props).onTouchEnd) === null || _textField$props$onTo === void 0 ? void 0 : _textField$props$onTo.call(_textField$props4, event);
    },
    onKeyDown: function onKeyDown(event) {
      var _useComboboxProps$inp9, _useComboboxProps$inp10, _textField$props$onKe, _textField$props5;
      (_useComboboxProps$inp9 = (_useComboboxProps$inp10 = useComboboxProps.inputProps).onKeyDown) === null || _useComboboxProps$inp9 === void 0 ? void 0 : _useComboboxProps$inp9.call(_useComboboxProps$inp10, event);
      (_textField$props$onKe = (_textField$props5 = textField.props).onKeyDown) === null || _textField$props$onKe === void 0 ? void 0 : _textField$props$onKe.call(_textField$props5, event);
    }
  }));
  var rootClassName = (0, _classnames.default)('ds-c-autocomplete', className);
  return /*#__PURE__*/_react.default.createElement("div", {
    className: rootClassName,
    ref: wrapperRef
  }, /*#__PURE__*/_react.default.cloneElement(textField, textFieldProps), (state.isOpen && reactStatelyItems.length > 0 || state.isFocused && statusMessage) && /*#__PURE__*/_react.default.createElement(_DropdownMenu.default, _extends({}, useComboboxProps.listBoxProps, {
    componentClass: "ds-c-autocomplete",
    heading: menuHeading,
    labelId: menuHeadingId,
    menuId: menuId,
    rootId: id,
    size: size,
    state: state,
    triggerRef: wrapperRef,
    listBoxRef: listBoxRef
  }), statusMessage), clearSearchButton && /*#__PURE__*/_react.default.createElement(_Button.default, {
    "aria-label": ariaClearLabel !== null && ariaClearLabel !== void 0 ? ariaClearLabel : (0, _i18n.t)('autocomplete.ariaClearLabel'),
    className: "ds-u-padding-right--0 ds-c-autocomplete__clear-btn",
    onClick: function onClick() {
      state.setSelectedKey(null);
      state.setInputValue('');
      if (state.selectedKey) {
        onChange === null || onChange === void 0 ? void 0 : onChange(null);
      }
    },
    size: "small",
    variation: "ghost",
    disabled: textField.props.disabled
  }, clearInputText !== null && clearInputText !== void 0 ? clearInputText : (0, _i18n.t)('autocomplete.clearInputText')));
};
exports.Autocomplete = Autocomplete;
Autocomplete.defaultProps = {
  autoCompleteLabel: 'off',
  clearSearchButton: true
};
var _default = Autocomplete;
exports.default = _default;