<commit_before>import React, { useRef, useMemo } from 'react'
import PropTypes from 'prop-types'
import { asField } from 'informed'
import { compose } from 'redux'
import { injectIntl, intlShape } from 'react-intl'
import styled, { withTheme } from 'styled-components'
import Downshift from 'downshift'
import { Box, Flex } from 'rebass'
import system from '@rebass/components'
import Check from 'components/Icon/Check'
import AngleUp from 'components/Icon/AngleUp'
import AngleDown from 'components/Icon/AngleDown'
import Text from './Text'
import { BasicInput } from './Input'
import messages from './messages'

const SelectOptionList = styled.ul`
  padding: 0;
  margin-top: 4px;
  position: absolute;
  z-index: 2;
  width: 100%;
  max-height: 20rem;
  overflow-y: auto;
  overflow-x: hidden;
  outline: 0;
  transition: opacity 0.1s ease;
  background-color: ${props => props.theme.colors.secondaryColor};
  border-radius: 5px;
  box-shadow: 0 3px 4px 0 rgba(30, 30, 30, 0.5);
`

const SelectOptionItem = styled(
  system(
    {
      extend: Box,
      as: 'li',
      p: 2,
    },
    'space',
    'color'
  )
)`
  outline: none;
  cursor: pointer;
`

const getIconStyles = props => `
  margin-left: -${props.width + 16}px;
  width: ${props.width}px;
  pointer-events: none;
  color: ${props.color || props.theme.colors.gray};
`

const ArrowIconClosed = styled(AngleDown)`
  ${props => getIconStyles(props)}
`

const ArrowIconOpen = styled(AngleUp)`
  ${props => getIconStyles(props)}
`

const StyledInput = styled(BasicInput)`
  input {
    cursor: pointer;
    color: transparent;
    text-shadow: 0 0 0 ${props => props.color || props.theme.colors.primaryText};
  }
`

const itemToString = item => (item ? item.value : '')

const getInitialSelectedItem = (items, initialSelectedItem) => {
  return initialSelectedItem
    ? {
        initialInputValue: itemToString(initialSelectedItem),
        initialSelectedItem: items.find(i => i.key === initialSelectedItem),
      }
    : {}
}

/**
 * Internationalizes select items
 *
 * @param {Array} items
 * @callback messageMapper key=>intl message mapper
 * @param {intlShape} intl
 * @returns {Array}
 */
function useIntl(items, messageMapper, intl) {
  return useMemo(() => {
    // if messageMapper is not set just original items array
    // in this case items should already contain value prop for each key
    if (!messageMapper) {
      return items
    }
    return items.map(({ key }) => {
      return { key, value: intl.formatMessage({ ...messageMapper(key) }) }
    })
  }, [items, messageMapper, intl])
}

function Select(props) {
  const { intl, messageMapper } = props
  const inputRef = useRef(null)
  // eslint-disable-next-line react/destructuring-assignment
  const items = useIntl(props.items, messageMapper, intl)
  const blurInput = () => inputRef.current && inputRef.current.blur()

  const renderSelectOptions = (highlightedIndex, selectedItem, getItemProps) => {
    const { theme } = props

    return items.map((item, index) => (
      <SelectOptionItem
        key={item.key}
        {...getItemProps({
          key: item.key,
          index,
          item,
        })}
        bg={highlightedIndex === index ? theme.colors.primaryColor : null}
        p={2}
      >
        <Flex alignItems="center" pr={2}>
          <Text color="superGreen" textAlign="center" width="20px">
            {selectedItem.key === item.key && <Check height="0.95em" />}
          </Text>
          <Text>{item.value}</Text>
        </Flex>
      </SelectOptionItem>
    ))
  }

  const {
    fieldApi,
    fieldState,
    iconSize,
    theme,
    color,
    onValueSelected,
    initialSelectedItem: initialSelectedItemOriginal,
    ...rest
  } = props
  const { setValue, setTouched } = fieldApi

  const { initialInputValue, initialSelectedItem } = getInitialSelectedItem(
    items,
    initialSelectedItemOriginal
  )

  return (
    <Downshift
      initialInputValue={initialInputValue}
      initialSelectedItem={initialSelectedItem}
      itemToString={itemToString}
      // When an item is selected, set the item in the Informed form state.
      onInputValueChange={(inputValue, stateAndHelpers) => {
        if (inputValue && inputValue !== itemToString(stateAndHelpers.selectedItem)) {
          fieldApi.setValue(itemToString(stateAndHelpers.selectedItem))
        }
      }}
      onSelect={item => {
        setValue(item.value)
        setTouched(true)
        if (onValueSelected) {
          onValueSelected(item.key)
        }
        blurInput()
      }}
    >
      {({
        getInputProps,
        getItemProps,
        getMenuProps,
        isOpen,
        highlightedIndex,
        selectedItem,
        closeMenu,
        openMenu,
        toggleMenu,
      }) => {
        const getInitialValue = () => {
          if (selectedItem) {
            return selectedItem.value<commit_msg>refactor: skip onSelect handler if no item is selected<commit_after>import React, { useRef, useMemo } from 'react'
import PropTypes from 'prop-types'
import { asField } from 'informed'
import { compose } from 'redux'
import { injectIntl, intlShape } from 'react-intl'
import styled, { withTheme } from 'styled-components'
import Downshift from 'downshift'
import { Box, Flex } from 'rebass'
import system from '@rebass/components'
import Check from 'components/Icon/Check'
import AngleUp from 'components/Icon/AngleUp'
import AngleDown from 'components/Icon/AngleDown'
import Text from './Text'
import { BasicInput } from './Input'
import messages from './messages'

const SelectOptionList = styled.ul`
  padding: 0;
  margin-top: 4px;
  position: absolute;
  z-index: 2;
  width: 100%;
  max-height: 20rem;
  overflow-y: auto;
  overflow-x: hidden;
  outline: 0;
  transition: opacity 0.1s ease;
  background-color: ${props => props.theme.colors.secondaryColor};
  border-radius: 5px;
  box-shadow: 0 3px 4px 0 rgba(30, 30, 30, 0.5);
`

const SelectOptionItem = styled(
  system(
    {
      extend: Box,
      as: 'li',
      p: 2,
    },
    'space',
    'color'
  )
)`
  outline: none;
  cursor: pointer;
`

const getIconStyles = props => `
  margin-left: -${props.width + 16}px;
  width: ${props.width}px;
  pointer-events: none;
  color: ${props.color || props.theme.colors.gray};
`

const ArrowIconClosed = styled(AngleDown)`
  ${props => getIconStyles(props)}
`

const ArrowIconOpen = styled(AngleUp)`
  ${props => getIconStyles(props)}
`

const StyledInput = styled(BasicInput)`
  input {
    cursor: pointer;
    color: transparent;
    text-shadow: 0 0 0 ${props => props.color || props.theme.colors.primaryText};
  }
`

const itemToString = item => (item ? item.value : '')

const getInitialSelectedItem = (items, initialSelectedItem) => {
  return initialSelectedItem
    ? {
        initialInputValue: itemToString(initialSelectedItem),
        initialSelectedItem: items.find(i => i.key === initialSelectedItem),
      }
    : {}
}

/**
 * Internationalizes select items
 *
 * @param {Array} items
 * @callback messageMapper key=>intl message mapper
 * @param {intlShape} intl
 * @returns {Array}
 */
function useIntl(items, messageMapper, intl) {
  return useMemo(() => {
    // if messageMapper is not set just original items array
    // in this case items should already contain value prop for each key
    if (!messageMapper) {
      return items
    }
    return items.map(({ key }) => {
      return { key, value: intl.formatMessage({ ...messageMapper(key) }) }
    })
  }, [items, messageMapper, intl])
}

function Select(props) {
  const { intl, messageMapper } = props
  const inputRef = useRef(null)
  // eslint-disable-next-line react/destructuring-assignment
  const items = useIntl(props.items, messageMapper, intl)
  const blurInput = () => inputRef.current && inputRef.current.blur()

  const renderSelectOptions = (highlightedIndex, selectedItem, getItemProps) => {
    const { theme } = props

    return items.map((item, index) => (
      <SelectOptionItem
        key={item.key}
        {...getItemProps({
          key: item.key,
          index,
          item,
        })}
        bg={highlightedIndex === index ? theme.colors.primaryColor : null}
        p={2}
      >
        <Flex alignItems="center" pr={2}>
          <Text color="superGreen" textAlign="center" width="20px">
            {selectedItem.key === item.key && <Check height="0.95em" />}
          </Text>
          <Text>{item.value}</Text>
        </Flex>
      </SelectOptionItem>
    ))
  }

  const {
    fieldApi,
    fieldState,
    iconSize,
    theme,
    color,
    onValueSelected,
    initialSelectedItem: initialSelectedItemOriginal,
    ...rest
  } = props
  const { setValue, setTouched } = fieldApi

  const { initialInputValue, initialSelectedItem } = getInitialSelectedItem(
    items,
    initialSelectedItemOriginal
  )

  return (
    <Downshift
      initialInputValue={initialInputValue}
      initialSelectedItem={initialSelectedItem}
      itemToString={itemToString}
      // When an item is selected, set the item in the Informed form state.
      onInputValueChange={(inputValue, stateAndHelpers) => {
        if (inputValue && inputValue !== itemToString(stateAndHelpers.selectedItem)) {
          fieldApi.setValue(itemToString(stateAndHelpers.selectedItem))
        }
      }}
      onSelect={item => {
        if (!item) {
          return
        }
        setValue(item.value)
        setTouched(true)
        if (onValueSelected) {
          onValueSelected(item.key)
        }
        blurInput()
      }}
    >
      {({
        getInputProps,
        getItemProps,
        getMenuProps,
        isOpen,
        highlightedIndex,
        selectedItem,
        closeMenu,
        openMenu,
        toggleMenu,
      }) => {
        const getInitialValue = () => {
          if (selectedItem) {
            return selectedItem.value
{'fraction_non_alphanumeric': 0.1238452948939194, 'fraction_numerical': 0.0075119277230737995, 'mean_word_length': 2.8484375, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 59, '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': '3724026', 'n_tokens_mistral': 3049, 'n_tokens_neox': 2968, 'n_words': 728}