<commit_before>import React from 'react';
import cx from 'classnames';
import _  from './util/_';
import filter from './util/filter';
import Popup           from './Popup';
import Btn             from './WidgetButton';
import Input           from './ComboboxInput';
import compat          from './util/compat';
import CustomPropTypes from './util/propTypes';
import PlainList       from './List';
import GroupableList   from './ListGroupable';
import validateList    from './util/validateListInterface';
import createUncontrolledWidget from 'uncontrollable';
import { dataItem, dataText, dataIndexOf } from './util/dataHelpers';
import { widgetEditable, isDisabled, isReadOnly } from './util/interaction';
import { instanceId, notify, isFirstFocusedRender } from './util/widgetHelpers';

let defaultSuggest = f => f === true ? 'startsWith' : f ? f : 'eq'

let { omit, pick } = _;

let propTypes = {
      //-- controlled props -----------
      value:          React.PropTypes.any,
      onChange:       React.PropTypes.func,
      open:           React.PropTypes.bool,
      onToggle:       React.PropTypes.func,
      //------------------------------------

      itemComponent:  CustomPropTypes.elementType,
      listComponent:  CustomPropTypes.elementType,

      groupComponent: CustomPropTypes.elementType,
      groupBy:        CustomPropTypes.accessor,

      data:           React.PropTypes.array,
      valueField:     React.PropTypes.string,
      textField:      CustomPropTypes.accessor,
      name:           React.PropTypes.string,

      onSelect:       React.PropTypes.func,

      autoFocus:      React.PropTypes.bool,
      disabled:       CustomPropTypes.disabled.acceptsArray,
      readOnly:       CustomPropTypes.readOnly.acceptsArray,

      suggest:        CustomPropTypes.filter,
      filter:         CustomPropTypes.filter,

      busy:           React.PropTypes.bool,

      dropUp:         React.PropTypes.bool,
      duration:       React.PropTypes.number, //popup

      placeholder:    React.PropTypes.string,

      messages:       React.PropTypes.shape({
        open:         CustomPropTypes.message,
        emptyList:    CustomPropTypes.message,
        emptyFilter:  CustomPropTypes.message
      })
    };

var ComboBox = React.createClass({

  displayName: 'ComboBox',

  mixins: [
    require('./mixins/TimeoutMixin'),
    require('./mixins/DataFilterMixin'),
    require('./mixins/PopupScrollToMixin'),
    require('./mixins/RtlParentContextMixin'),
    require('./mixins/AriaDescendantMixin')('input'),
    require('./mixins/FocusMixin')({
      willHandle(focused) {
        // not suggesting anymore
        !focused && this.refs.input.accept()
      },
      didHandle(focused) {
        if (!focused) this.close()
      }
    })
  ],

  propTypes: propTypes,

  getInitialState(){
    var { value, data, valueField } = this.props
      , items = this.process(data, value)
      , idx   = dataIndexOf(items, value, valueField);

    return {
      selectedItem:  items[idx],
      focusedItem:   items[!~idx ? 0 : idx],
      processedData: items,
      open:          false
    }
  },

  getDefaultProps(){
    return {
      data: [],
      value: '',
      open: false,
      suggest: false,
      filter: false,
      delay: 500,

      messages: msgs(),
      ariaActiveDescendantKey: 'combobox'
    }
  },

  componentDidUpdate() {
    this.refs.list && validateList(this.refs.list)
  },

  shouldComponentUpdate(nextProps, nextState){
    var isSuggesting = this.refs.input && this.refs.input.isSuggesting()
      , stateChanged = !_.isShallowEqual(nextState, this.state)
      , valueChanged = !_.isShallowEqual(nextProps, this.props)

    return isSuggesting || stateChanged || valueChanged
  },

  componentWillReceiveProps(nextProps) {
    let { value, data, valueField, textField } = nextProps

    var rawIdx = dataIndexOf(data, value, valueField)
      , valueItem = rawIdx === -1 ? nextProps.value : nextProps.data[rawIdx]
      , isSuggesting = this.refs.input && this.refs.input.isSuggesting()
      , items = this.process(
          nextProps.data
        , nextProps.value
        , (rawIdx === -1 || isSuggesting) && dataText(valueItem, textField) )

      , idx = dataIndexOf(items, value, valueField)
      , focused = this.filterIndexOf(items, dataText(valueItem, textField));

    this._searchTerm = '';

    this.setState({
      processedData:  items,
      selectedItem:   items[idx],
      focusedItem:    items[idx === -1
        ? focused !== -1 ? focused : 0 // focus the closest match
        : idx]
    })
  },

  render(){
    let {
        className, tabIndex, filter, suggest
      , valueField, textField, groupBy
      , messages, data, busy, dropUp, name, autoFocus
      , placeholder, value, open
      , listComponent: List } = this.props;

    List = List || (groupBy && GroupableList) || PlainList

    let elementProps = omit(this.props, Object.keys(propTypes));
    let listProps    = pick(this.props, Object.keys(List.propTypes));
    let popupProps   = pick(this.props, Object.keys(Popup.propTypes));

    let { focusedItem, selectedItem, focused } = this.state;

    let items = this._data()
      , disabled = isDisabled(this.props)
      , readOnly = isReadOnly(this.props)
      , valueItem = dataItem(data, value, valueField) // take value from the raw data
      , inputID = instanceId(this, '_input')
      , listID = instanceId(this, '_listbox')
      , completeType = suggest
          ? filter ? 'both' : 'inline'
          : filter ? 'list' : '';

    let shouldRenderList = isFirstFocusedRender(this) || open;

    messages = msgs(messages)

    return (
      <div
        {...elementProps}
        ref="element"
        onKeyDown={this._keyDown}
        onBlur={this.handleBlur}
        onFocus={this.handleFocus}
        tabIndex={'-1'}
        className={cx(className, 'rw-combobox', 'rw-widget', {
          'rw-state-focus':     focused,
          'rw-state-disabled':  disabled,
          'rw-state-readonly':  readOnly,
          'rw-rtl':             this.isRtl(),

          ['rw-open' + (dropUp ? '-up' : '')]: open
         })}
      >
        <Btn
          tabIndex='-1'
          className='rw-select'
          onClick={this.toggle}
          disabled={!!(disabled || readOnly)}
        >
          <i className={cx('rw-i rw-i-caret-down', {'rw-loading': busy})}>
            <span className="rw-sr">
              { _.result(messages.open, this.props) }
            </span>
          </i>
        </Btn>
        <Input
          ref='input'
          id={inputID}
          autoFocus={autoFocus}
          tabIndex={tabIndex}
          suggest={suggest}
          name={name}
          role='combobox'
          aria-owns={listID}
          aria-busy={!!busy}
          aria-autocomplete={completeType}
          aria-expanded={open}
          aria-haspopup={true}
          placeholder={placeholder}
          disabled={disabled}
          readOnly={readOnly}
          className='rw-input'
          value={dataText(valueItem, textField) }
          onChange={this._inputTyping}
          onKeyDown={this._inputKeyDown}<commit_msg>removed className from being passed. ComoboboxInput already appends the rw-input class to the input<commit_after>import React from 'react';
import cx from 'classnames';
import _  from './util/_';
import filter from './util/filter';
import Popup           from './Popup';
import Btn             from './WidgetButton';
import Input           from './ComboboxInput';
import compat          from './util/compat';
import CustomPropTypes from './util/propTypes';
import PlainList       from './List';
import GroupableList   from './ListGroupable';
import validateList    from './util/validateListInterface';
import createUncontrolledWidget from 'uncontrollable';
import { dataItem, dataText, dataIndexOf } from './util/dataHelpers';
import { widgetEditable, isDisabled, isReadOnly } from './util/interaction';
import { instanceId, notify, isFirstFocusedRender } from './util/widgetHelpers';

let defaultSuggest = f => f === true ? 'startsWith' : f ? f : 'eq'

let { omit, pick } = _;

let propTypes = {
      //-- controlled props -----------
      value:          React.PropTypes.any,
      onChange:       React.PropTypes.func,
      open:           React.PropTypes.bool,
      onToggle:       React.PropTypes.func,
      //------------------------------------

      itemComponent:  CustomPropTypes.elementType,
      listComponent:  CustomPropTypes.elementType,

      groupComponent: CustomPropTypes.elementType,
      groupBy:        CustomPropTypes.accessor,

      data:           React.PropTypes.array,
      valueField:     React.PropTypes.string,
      textField:      CustomPropTypes.accessor,
      name:           React.PropTypes.string,

      onSelect:       React.PropTypes.func,

      autoFocus:      React.PropTypes.bool,
      disabled:       CustomPropTypes.disabled.acceptsArray,
      readOnly:       CustomPropTypes.readOnly.acceptsArray,

      suggest:        CustomPropTypes.filter,
      filter:         CustomPropTypes.filter,

      busy:           React.PropTypes.bool,

      dropUp:         React.PropTypes.bool,
      duration:       React.PropTypes.number, //popup

      placeholder:    React.PropTypes.string,

      messages:       React.PropTypes.shape({
        open:         CustomPropTypes.message,
        emptyList:    CustomPropTypes.message,
        emptyFilter:  CustomPropTypes.message
      })
    };

var ComboBox = React.createClass({

  displayName: 'ComboBox',

  mixins: [
    require('./mixins/TimeoutMixin'),
    require('./mixins/DataFilterMixin'),
    require('./mixins/PopupScrollToMixin'),
    require('./mixins/RtlParentContextMixin'),
    require('./mixins/AriaDescendantMixin')('input'),
    require('./mixins/FocusMixin')({
      willHandle(focused) {
        // not suggesting anymore
        !focused && this.refs.input.accept()
      },
      didHandle(focused) {
        if (!focused) this.close()
      }
    })
  ],

  propTypes: propTypes,

  getInitialState(){
    var { value, data, valueField } = this.props
      , items = this.process(data, value)
      , idx   = dataIndexOf(items, value, valueField);

    return {
      selectedItem:  items[idx],
      focusedItem:   items[!~idx ? 0 : idx],
      processedData: items,
      open:          false
    }
  },

  getDefaultProps(){
    return {
      data: [],
      value: '',
      open: false,
      suggest: false,
      filter: false,
      delay: 500,

      messages: msgs(),
      ariaActiveDescendantKey: 'combobox'
    }
  },

  componentDidUpdate() {
    this.refs.list && validateList(this.refs.list)
  },

  shouldComponentUpdate(nextProps, nextState){
    var isSuggesting = this.refs.input && this.refs.input.isSuggesting()
      , stateChanged = !_.isShallowEqual(nextState, this.state)
      , valueChanged = !_.isShallowEqual(nextProps, this.props)

    return isSuggesting || stateChanged || valueChanged
  },

  componentWillReceiveProps(nextProps) {
    let { value, data, valueField, textField } = nextProps

    var rawIdx = dataIndexOf(data, value, valueField)
      , valueItem = rawIdx === -1 ? nextProps.value : nextProps.data[rawIdx]
      , isSuggesting = this.refs.input && this.refs.input.isSuggesting()
      , items = this.process(
          nextProps.data
        , nextProps.value
        , (rawIdx === -1 || isSuggesting) && dataText(valueItem, textField) )

      , idx = dataIndexOf(items, value, valueField)
      , focused = this.filterIndexOf(items, dataText(valueItem, textField));

    this._searchTerm = '';

    this.setState({
      processedData:  items,
      selectedItem:   items[idx],
      focusedItem:    items[idx === -1
        ? focused !== -1 ? focused : 0 // focus the closest match
        : idx]
    })
  },

  render(){
    let {
        className, tabIndex, filter, suggest
      , valueField, textField, groupBy
      , messages, data, busy, dropUp, name, autoFocus
      , placeholder, value, open
      , listComponent: List } = this.props;

    List = List || (groupBy && GroupableList) || PlainList

    let elementProps = omit(this.props, Object.keys(propTypes));
    let listProps    = pick(this.props, Object.keys(List.propTypes));
    let popupProps   = pick(this.props, Object.keys(Popup.propTypes));

    let { focusedItem, selectedItem, focused } = this.state;

    let items = this._data()
      , disabled = isDisabled(this.props)
      , readOnly = isReadOnly(this.props)
      , valueItem = dataItem(data, value, valueField) // take value from the raw data
      , inputID = instanceId(this, '_input')
      , listID = instanceId(this, '_listbox')
      , completeType = suggest
          ? filter ? 'both' : 'inline'
          : filter ? 'list' : '';

    let shouldRenderList = isFirstFocusedRender(this) || open;

    messages = msgs(messages)

    return (
      <div
        {...elementProps}
        ref="element"
        onKeyDown={this._keyDown}
        onBlur={this.handleBlur}
        onFocus={this.handleFocus}
        tabIndex={'-1'}
        className={cx(className, 'rw-combobox', 'rw-widget', {
          'rw-state-focus':     focused,
          'rw-state-disabled':  disabled,
          'rw-state-readonly':  readOnly,
          'rw-rtl':             this.isRtl(),

          ['rw-open' + (dropUp ? '-up' : '')]: open
         })}
      >
        <Btn
          tabIndex='-1'
          className='rw-select'
          onClick={this.toggle}
          disabled={!!(disabled || readOnly)}
        >
          <i className={cx('rw-i rw-i-caret-down', {'rw-loading': busy})}>
            <span className="rw-sr">
              { _.result(messages.open, this.props) }
            </span>
          </i>
        </Btn>
        <Input
          ref='input'
          id={inputID}
          autoFocus={autoFocus}
          tabIndex={tabIndex}
          suggest={suggest}
          name={name}
          role='combobox'
          aria-owns={listID}
          aria-busy={!!busy}
          aria-autocomplete={completeType}
          aria-expanded={open}
          aria-haspopup={true}
          placeholder={placeholder}
          disabled={disabled}
          readOnly={readOnly}
          value={dataText(valueItem, textField) }
          onChange={this._inputTyping}
          onKeyDown={this._inputKeyDown}
{'fraction_non_alphanumeric': 0.13221070210841276, 'fraction_numerical': 0.0015308607612553057, 'mean_word_length': 2.2545289855072466, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, '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': '1826150', 'n_tokens_mistral': 4221, 'n_tokens_neox': 4129, 'n_words': 845}