'use strict';

var Icons = require('@transferwise/icons');
var clsx = require('clsx');
var clamp = require('lodash.clamp');
var debounce = require('lodash.debounce');
var React = require('react');
var reactIntl = require('react-intl');
var Chip = require('../chips/Chip.js');
var documentIosClick = require('../common/domHelpers/documentIosClick.js');
require('../common/theme.js');
require('../common/direction.js');
require('../common/propsValues/control.js');
require('../common/propsValues/breakpoint.js');
var size = require('../common/propsValues/size.js');
require('../common/propsValues/typography.js');
require('../common/propsValues/width.js');
require('../common/propsValues/type.js');
require('../common/propsValues/dateMode.js');
require('../common/propsValues/monthFormat.js');
require('../common/propsValues/position.js');
require('../common/propsValues/layouts.js');
require('../common/propsValues/status.js');
var sentiment = require('../common/propsValues/sentiment.js');
require('../common/propsValues/profileType.js');
require('../common/propsValues/variant.js');
require('../common/propsValues/scroll.js');
require('../common/propsValues/markdownNodeType.js');
require('../common/fileType.js');
require('../common/closeButton/CloseButton.messages.js');
var jsxRuntime = require('react/jsx-runtime');
var InlineAlert = require('../inlineAlert/InlineAlert.js');
var contexts = require('../inputs/contexts.js');
var TypeaheadInput = require('./typeaheadInput/TypeaheadInput.js');
var TypeaheadOption = require('./typeaheadOption/TypeaheadOption.js');
var Typeahead_messages = require('./Typeahead.messages.js');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var clamp__default = /*#__PURE__*/_interopDefault(clamp);
var debounce__default = /*#__PURE__*/_interopDefault(debounce);
var React__default = /*#__PURE__*/_interopDefault(React);

const DEFAULT_MIN_QUERY_LENGTH = 3;
const SEARCH_DELAY = 200;
class Typeahead extends React.Component {
  static defaultProps = {
    allowNew: false,
    autoFillOnBlur: true,
    autoFocus: false,
    chipSeparators: [],
    clearable: true,
    initialValue: [],
    inputAutoComplete: 'new-password',
    minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
    multiple: false,
    searchDelay: SEARCH_DELAY,
    showSuggestions: true,
    showNewEntry: true,
    size: size.Size.MEDIUM,
    validateChip: () => true
  };
  optionRefs;
  constructor(props) {
    super(props);
    const {
      searchDelay,
      initialValue,
      multiple
    } = this.props;
    this.handleSearchDebounced = debounce__default.default(this.handleSearch, searchDelay);
    const initialQuery = !multiple && initialValue.length > 0 ? initialValue[0].label : '';
    this.state = {
      selected: initialValue,
      errorState: false,
      query: initialQuery,
      keyboardFocusedOptionIndex: null,
      optionsShown: false,
      isFocused: false
    };
    this.optionRefs = [];
  }
  handleSearchDebounced;
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.multiple !== this.props.multiple) {
      this.setState(previousState => {
        const {
          selected
        } = previousState;
        if (!nextProps.multiple && selected.length > 0) {
          return {
            query: selected[0].label,
            selected: [selected[0]]
          };
        }
        return {
          selected: previousState.selected,
          query: ''
        };
      });
    }
  }
  componentWillUnmount() {
    this.handleSearchDebounced.cancel();
  }
  handleOnFocus = () => {
    this.showMenu();
    this.props.onFocus?.();
  };
  onOptionSelected = (event, item) => {
    event.preventDefault();
    this.selectItem(item);
  };
  handleOnChange = event => {
    const {
      optionsShown,
      selected
    } = this.state;
    const {
      multiple,
      onInputChange
    } = this.props;
    if (!optionsShown) {
      this.showMenu();
    }
    const query = event.target.value;
    if (!multiple && selected.length > 0) {
      this.updateSelectedValue([]);
    }
    this.setState({
      query
    }, () => {
      this.handleSearchDebounced(query);
      if (onInputChange) {
        onInputChange(query);
      }
    });
  };
  handleOnPaste = event => {
    const {
      allowNew,
      multiple,
      chipSeparators
    } = this.props;
    const {
      selected
    } = this.state;
    if (allowNew && multiple && chipSeparators.length > 0) {
      event.preventDefault();
      const value = event.clipboardData.getData('text');
      if (value) {
        const regex = new RegExp(chipSeparators.join('|'));
        const pastedChips = value.split(regex).map(label => ({
          label: label.trim()
        })).filter(chip => chip.label);
        this.updateSelectedValue([...selected, ...pastedChips]);
      }
    }
  };
  handleOnKeyDown = event => {
    const {
      showSuggestions,
      allowNew,
      multiple,
      chipSeparators,
      options
    } = this.props;
    const {
      keyboardFocusedOptionIndex,
      query,
      selected
    } = this.state;
    const chipsMode = !showSuggestions && allowNew && multiple;
    if (chipsMode && ['Enter', 'Tab', ...chipSeparators].includes(event.key) && query.trim()) {
      event.preventDefault();
      this.selectItem({
        label: query
      });
    } else {
      switch (event.key) {
        case 'ArrowDown':
          event.preventDefault();
          this.moveFocusedOption(1);
          break;
        case 'ArrowUp':
          event.preventDefault();
          this.moveFocusedOption(-1);
          break;
        case 'Enter':
          event.preventDefault();
          if (keyboardFocusedOptionIndex != null && options[keyboardFocusedOptionIndex]) {
            this.selectItem(options[keyboardFocusedOptionIndex]);
          } else if (allowNew && query.trim()) {
            this.selectItem({
              label: query
            });
          }
          break;
        case 'Backspace':
          if (multiple && !query && selected.length > 0) {
            this.updateSelectedValue(selected.slice(0, -1));
          }
          break;
      }
    }
  };
  moveFocusedOption(offset) {
    this.setState(previousState => {
      const {
        keyboardFocusedOptionIndex
      } = previousState;
      const {
        options
      } = this.props;
      let index = 0;
      if (keyboardFocusedOptionIndex !== null) {
        index = clamp__default.default(keyboardFocusedOptionIndex + offset, 0, options.length - 1);
      }
      const optionRef = this.optionRefs[index];
      if (optionRef?.current) {
        optionRef.current.focus(); // Set focus on the option element
      }
      return {
        keyboardFocusedOptionIndex: index
      };
    });
  }
  selectItem = item => {
    const {
      multiple
    } = this.props;
    let selected = [...this.state.selected];
    let query;
    if (multiple) {
      selected.push(item);
      query = '';
    } else {
      selected = [item];
      query = item.label;
    }
    this.updateSelectedValue(selected);
    if (!item.keepFocusOnSelect) {
      this.hideMenu();
    }
    if (item.clearQueryOnSelect) {
      query = '';
    }
    this.setState({
      query
    });
  };
  handleSearch = query => {
    const {
      onSearch
    } = this.props;
    if (onSearch) {
      onSearch(query);
    }
    this.setState(previousState => ({
      keyboardFocusedOptionIndex: previousState.keyboardFocusedOptionIndex === null ? null : 0
    }));
  };
  handleDocumentClick = () => {
    if (this.state.optionsShown) {
      this.hideMenu();
      const {
        allowNew,
        onBlur,
        autoFillOnBlur
      } = this.props;
      const {
        query
      } = this.state;
      this.setState({
        isFocused: false
      });
      if (allowNew && autoFillOnBlur && query.trim()) {
        this.selectItem({
          label: query
        });
      }
      if (onBlur) {
        onBlur();
      }
    }
  };
  showMenu = () => {
    this.setState({
      isFocused: true,
      optionsShown: true
    }, () => {
      documentIosClick.addClickClassToDocumentOnIos();
      document.addEventListener('click', this.handleDocumentClick, false);
    });
  };
  hideMenu = () => {
    this.setState({
      optionsShown: false,
      keyboardFocusedOptionIndex: null
    }, () => {
      documentIosClick.removeClickClassFromDocumentOnIos();
      document.removeEventListener('click', this.handleDocumentClick, false);
    });
  };
  updateSelectedValue = selected => {
    const {
      onChange,
      validateChip
    } = this.props;
    const errorState = selected.some(chip => !validateChip(chip));
    this.setState({
      selected,
      errorState
    }, () => {
      onChange([...selected]);
    });
  };
  clear = event => {
    event.preventDefault();
    if (this.state.selected.length > 0) {
      this.updateSelectedValue([]);
    }
    this.setState({
      query: ''
    });
  };
  removeChip = option => {
    const {
      selected
    } = this.state;
    if (selected.length > 0) {
      this.updateSelectedValue([...selected.filter(selectedOption => selectedOption !== option)]);
    }
  };
  renderChip = (option, idx) => {
    const valid = this.props.validateChip?.(option);
    return /*#__PURE__*/jsxRuntime.jsx(Chip, {
      label: option.label,
      className: clsx.clsx('m-t-1', {
        'has-error': !valid,
        'np-chip--valid': valid
      }),
      onRemove: () => this.removeChip(option)
    }, idx);
  };
  renderMenu = ({
    footer,
    options,
    id,
    keyboardFocusedOptionIndex,
    query,
    allowNew,
    showNewEntry,
    dropdownOpen
  }) => {
    const optionsToRender = [...options];
    if (allowNew && query.trim() && options.every(option => option.label.trim().toUpperCase() !== query.trim().toUpperCase()) && showNewEntry) {
      optionsToRender.push({
        label: query
      });
    }
    return /*#__PURE__*/jsxRuntime.jsx("div", {
      className: clsx.clsx('dropdown btn-group btn-block', {
        open: dropdownOpen
      }),
      id: `menu-${id}`,
      children: (!!optionsToRender.length || footer) && /*#__PURE__*/jsxRuntime.jsxs("ul", {
        className: "dropdown-menu",
        role: "menu",
        children: [optionsToRender.map((option, idx) => {
          const ref = /*#__PURE__*/React__default.default.createRef();
          this.optionRefs[idx] = ref;
          return /*#__PURE__*/jsxRuntime.jsx(TypeaheadOption, {
            ref: ref,
            query: query,
            option: option,
            selected: keyboardFocusedOptionIndex === idx,
            id: String(option.label.replace(/\s+/g, '-')),
            onClick: event => {
              this.onOptionSelected(event, option);
            }
          }, `${option.label}${idx.toString()}`);
        }), footer]
      })
    });
  };
  render() {
    const {
      inputAttributes,
      id: idProp,
      placeholder,
      multiple,
      size,
      addon,
      name,
      clearable,
      allowNew,
      footer,
      showSuggestions,
      showNewEntry,
      options,
      minQueryLength,
      autoFocus,
      maxHeight,
      alert,
      inputAutoComplete
    } = this.props;
    const id = idProp ?? inputAttributes?.id;
    const {
      errorState,
      query,
      selected,
      optionsShown,
      keyboardFocusedOptionIndex
    } = this.state;
    const clearButton = clearable && (query || selected.length > 0);
    const dropdownOpen = optionsShown && showSuggestions && query.length >= minQueryLength;
    const menu = this.renderMenu({
      footer,
      options,
      id,
      keyboardFocusedOptionIndex,
      query,
      allowNew,
      showNewEntry,
      dropdownOpen
    });
    const alertType = alert?.type ?? sentiment.Sentiment.NEUTRAL;
    const hasError = errorState || alert && alertType === sentiment.Sentiment.ERROR;
    const displayAlert = !errorState && alert || alert && alertType === sentiment.Sentiment.ERROR;
    const hasWarning = displayAlert && alertType === sentiment.Sentiment.WARNING;
    const hasInfo = displayAlert && alertType === sentiment.Sentiment.NEUTRAL;
    return (
      /*#__PURE__*/
      /* eslint-disable-next-line jsx-a11y/click-events-have-key-events */
      jsxRuntime.jsx("div", {
        role: "group",
        ...inputAttributes,
        id: id,
        className: clsx.clsx('typeahead', `typeahead-${size}`, {
          'typeahead--has-value': selected.length > 0,
          'typeahead--empty': selected.length === 0,
          'typeahead--multiple': multiple,
          open: dropdownOpen
        }),
        onClick: documentIosClick.stopPropagation,
        children: /*#__PURE__*/jsxRuntime.jsxs("div", {
          className: clsx.clsx('form-group', {
            'has-error': hasError,
            'has-warning': hasWarning,
            'has-info': hasInfo
          }),
          children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
            className: clsx.clsx(`input-group input-group-${size}`, {
              'input-group--has-error': hasError
            }),
            children: [addon && /*#__PURE__*/jsxRuntime.jsx("span", {
              className: "input-group-addon input-group-addon--search",
              children: addon
            }), /*#__PURE__*/jsxRuntime.jsx(TypeaheadInput, {
              autoFocus,
              multiple,
              dropdownOpen,
              placeholder,
              selected,
              maxHeight,
              id: id,
              name: name,
              value: query,
              typeaheadId: id,
              renderChip: this.renderChip,
              autoComplete: inputAutoComplete,
              ariaActivedescendant: keyboardFocusedOptionIndex !== null && options[keyboardFocusedOptionIndex] ? `option-${options[keyboardFocusedOptionIndex].label.replace(/\s+/g, '-')}` : undefined,
              onChange: this.handleOnChange,
              onKeyDown: this.handleOnKeyDown,
              onFocus: this.handleOnFocus,
              onPaste: this.handleOnPaste
            }), clearButton && /*#__PURE__*/jsxRuntime.jsx("div", {
              className: "input-group-addon",
              children: /*#__PURE__*/jsxRuntime.jsx("button", {
                type: "button",
                className: "btn-unstyled",
                "aria-label": this.props?.intl?.formatMessage(Typeahead_messages.clearLabel),
                onClick: this.clear,
                children: /*#__PURE__*/jsxRuntime.jsx(Icons.Cross, {})
              })
            })]
          }), displayAlert ? /*#__PURE__*/jsxRuntime.jsx(InlineAlert, {
            type: alert.type,
            children: alert.message
          }) : menu]
        })
      })
    );
  }
}
var Typeahead$1 = reactIntl.injectIntl(contexts.withInputAttributes(Typeahead, {
  nonLabelable: true
}));

module.exports = Typeahead$1;
//# sourceMappingURL=Typeahead.js.map
