<commit_before>/**
 * Copyright IBM Corp. 2016, 2018
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import cx from 'classnames';
import Downshift from 'downshift';
import PropTypes from 'prop-types';
import React, { useEffect, useState, useRef } from 'react';
import { settings } from 'carbon-components';
import {
  Checkmark16,
  WarningAltFilled16,
  WarningFilled16,
} from '@carbon/icons-react';
import ListBox, { PropTypes as ListBoxPropTypes } from '../ListBox';
import { ListBoxTrigger, ListBoxSelection } from '../ListBox/next';
import { match, keys } from '../../internal/keyboard';
import setupGetInstanceId from '../../tools/setupGetInstanceId';
import { mapDownshiftProps } from '../../tools/createPropAdapter';
import mergeRefs from '../../tools/mergeRefs';

const { prefix } = settings;

const defaultItemToString = (item) => {
  if (typeof item === 'string') {
    return item;
  }

  return item && item.label;
};

const defaultShouldFilterItem = () => true;

const getInputValue = ({
  initialSelectedItem,
  inputValue,
  itemToString,
  selectedItem,
}) => {
  if (selectedItem) {
    return itemToString(selectedItem);
  }
  // TODO: consistent `initialSelectedItem` behavior with other listbox components in v11
  if (initialSelectedItem) {
    return itemToString(initialSelectedItem);
  }

  return inputValue || '';
};

const findHighlightedIndex = ({ items, itemToString }, inputValue) => {
  if (!inputValue) {
    return -1;
  }

  const searchValue = inputValue.toLowerCase();

  for (let i = 0; i < items.length; i++) {
    const item = itemToString(items[i]).toLowerCase();
    if (item.indexOf(searchValue) !== -1) {
      return i;
    }
  }

  return -1;
};

const getInstanceId = setupGetInstanceId();

const ComboBox = (props) => {
  const {
    ariaLabel,
    className: containerClassName,
    direction,
    disabled,
    downshiftProps,
    helperText,
    id,
    initialSelectedItem,
    invalid,
    invalidText,
    items,
    itemToElement,
    itemToString,
    light,
    onChange,
    onInputChange,
    onToggleClick, // eslint-disable-line no-unused-vars
    placeholder,
    selectedItem,
    shouldFilterItem,
    size,
    titleText,
    translateWithId,
    type, // eslint-disable-line no-unused-vars
    warn,
    warnText,
    ...rest
  } = props;

  const textInput = useRef();
  const comboBoxInstanceId = getInstanceId();
  const [inputValue, setInputValue] = useState(
    getInputValue({
      initialSelectedItem,
      inputValue: '',
      itemToString,
      selectedItem,
    })
  );
  const [prevSelectedItem, setPrevSelectedItem] = useState(null);
  const [doneInitialSelectedItem, setDoneInitialSelectedItem] = useState(null);

  if (!doneInitialSelectedItem || prevSelectedItem !== selectedItem) {
    setDoneInitialSelectedItem(true);
    setPrevSelectedItem(selectedItem);
    setInputValue(
      getInputValue({
        initialSelectedItem,
        inputValue,
        itemToString,
        selectedItem,
      })
    );
  }

  const filterItems = (items, itemToString, inputValue) =>
    items.filter((item) =>
      shouldFilterItem({
        item,
        itemToString,
        inputValue,
      })
    );

  const handleOnChange = (selectedItem) => {
    if (onChange) {
      onChange({ selectedItem });
    }
  };

  const handleOnInputValueChange = (inputValue) => {
    setInputValue(inputValue || '');
  };

  useEffect(() => {
    if (onInputChange) {
      onInputChange(inputValue);
    }
  });

  const handleSelectionClear = () => {
    if (textInput?.current) {
      textInput.current.focus();
    }<commit_msg>fix(ComboBox): add missing dependencies to useEffect (#8308)<commit_after>/**
 * Copyright IBM Corp. 2016, 2018
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import cx from 'classnames';
import Downshift from 'downshift';
import PropTypes from 'prop-types';
import React, { useEffect, useState, useRef } from 'react';
import { settings } from 'carbon-components';
import {
  Checkmark16,
  WarningAltFilled16,
  WarningFilled16,
} from '@carbon/icons-react';
import ListBox, { PropTypes as ListBoxPropTypes } from '../ListBox';
import { ListBoxTrigger, ListBoxSelection } from '../ListBox/next';
import { match, keys } from '../../internal/keyboard';
import setupGetInstanceId from '../../tools/setupGetInstanceId';
import { mapDownshiftProps } from '../../tools/createPropAdapter';
import mergeRefs from '../../tools/mergeRefs';

const { prefix } = settings;

const defaultItemToString = (item) => {
  if (typeof item === 'string') {
    return item;
  }

  return item && item.label;
};

const defaultShouldFilterItem = () => true;

const getInputValue = ({
  initialSelectedItem,
  inputValue,
  itemToString,
  selectedItem,
}) => {
  if (selectedItem) {
    return itemToString(selectedItem);
  }
  // TODO: consistent `initialSelectedItem` behavior with other listbox components in v11
  if (initialSelectedItem) {
    return itemToString(initialSelectedItem);
  }

  return inputValue || '';
};

const findHighlightedIndex = ({ items, itemToString }, inputValue) => {
  if (!inputValue) {
    return -1;
  }

  const searchValue = inputValue.toLowerCase();

  for (let i = 0; i < items.length; i++) {
    const item = itemToString(items[i]).toLowerCase();
    if (item.indexOf(searchValue) !== -1) {
      return i;
    }
  }

  return -1;
};

const getInstanceId = setupGetInstanceId();

const ComboBox = (props) => {
  const {
    ariaLabel,
    className: containerClassName,
    direction,
    disabled,
    downshiftProps,
    helperText,
    id,
    initialSelectedItem,
    invalid,
    invalidText,
    items,
    itemToElement,
    itemToString,
    light,
    onChange,
    onInputChange,
    onToggleClick, // eslint-disable-line no-unused-vars
    placeholder,
    selectedItem,
    shouldFilterItem,
    size,
    titleText,
    translateWithId,
    type, // eslint-disable-line no-unused-vars
    warn,
    warnText,
    ...rest
  } = props;

  const textInput = useRef();
  const comboBoxInstanceId = getInstanceId();
  const [inputValue, setInputValue] = useState(
    getInputValue({
      initialSelectedItem,
      inputValue: '',
      itemToString,
      selectedItem,
    })
  );
  const [prevSelectedItem, setPrevSelectedItem] = useState(null);
  const [doneInitialSelectedItem, setDoneInitialSelectedItem] = useState(null);
  const savedOnInputChange = useRef(onInputChange);

  if (!doneInitialSelectedItem || prevSelectedItem !== selectedItem) {
    setDoneInitialSelectedItem(true);
    setPrevSelectedItem(selectedItem);
    setInputValue(
      getInputValue({
        initialSelectedItem,
        inputValue,
        itemToString,
        selectedItem,
      })
    );
  }

  const filterItems = (items, itemToString, inputValue) =>
    items.filter((item) =>
      shouldFilterItem({
        item,
        itemToString,
        inputValue,
      })
    );

  const handleOnChange = (selectedItem) => {
    if (onChange) {
      onChange({ selectedItem });
    }
  };

  const handleOnInputValueChange = (inputValue) => {
    setInputValue(inputValue || '');
  };

  useEffect(() => {
    savedOnInputChange.current = onInputChange;
  }, [onInputChange]);

  useEffect(() => {
    if (savedOnInputChange.current) {
      savedOnInputChange.current(inputValue);
    }
  }, [inputValue]);

  const handleSelectionClear = () => {
    if (textInput?.current) {
      textInput.current.focus();
    }
{'fraction_non_alphanumeric': 0.12304866850321396, 'fraction_numerical': 0.006296733569460842, 'mean_word_length': 3.244988864142539, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '523874', 'n_tokens_mistral': 2276, 'n_tokens_neox': 2239, 'n_words': 542}