text: import React from 'react';
  import PropTypes from 'prop-types';
  
  import { asNumber, guessType } from '../../utils';
  
  const nums = new Set(['number', 'integer']);
  
  /**
   * This is a silly limitation in the DOM where option change event values are
   * always retrieved as strings.
   */
  function processValue(schema, value) {
    // "enum" is a reserved word, so only "type" and "items" can be destructured
    const { type, items } = schema;
    if (value === '') {
      return undefined;
    } else if (type === 'array' && items && nums.has(items.type)) {
      return value.map(asNumber);
    } else if (type === 'boolean') {
      return value === 'true';
    } else if (type === 'number') {
      return asNumber(value);
    }
  
    // If type is undefined, but an enum is present, try and infer the type from
    // the enum values
    if (schema.enum) {
      if (schema.enum.every(x => guessType(x) === 'number')) {
        return asNumber(value);
      } else if (schema.enum.every(x => guessType(x) === 'boolean')) {
        return value === 'true';
      }
    }
  
    return value;
  }
  
  function getValue(event, multiple) {
    if (multiple) {
      return [].slice
        .call(event.target.options)
        .filter(o => o.selected)
        .map(o => o.value);
    }
  
    return event.target.value;
  }
  
  function SelectWidget(props) {
    const {
      schema,
      id,
      options,
      value,
      required,
      disabled,
      readonly,
      multiple,
      autofocus,
      onChange,
      onBlur,
      onFocus,
      placeholder,
    } = props;
    const { enumOptions, enumDisabled, hideEmpty } = options;
    const emptyValue = multiple ? [] : '';
    return (
      <select
        autoFocus={autofocus}
        className="form-control"
        disabled={disabled || readonly}
        id={id}
        multiple={multiple}
        onBlur={
          onBlur &&
          (event => {
            const newValue = getValue(event, multiple);
            onBlur(id, processValue(schema, newValue));
          })
        }
        onChange={event => {
          const newValue = getValue(event, multiple);
          onChange(processValue(schema, newValue));
        }}
        onFocus={
          onFocus &&
          (event => {
            const newValue = getValue(event, multiple);
            onFocus(id, processValue(schema, newValue));
          })
        }
        required={required}
        value={typeof value === 'undefined' ? emptyValue : value}
      >
        {!multiple && schema.default === undefined && !hideEmpty && <option value="">{placeholder}</option>}
        {enumOptions.map(({ value, label }, i) => {
          const disabled = enumDisabled && enumDisabled.indexOf(value) !== -1;
          return (
            <option key={i} disabled={disabled} value={value}>
              {label}
            </option>
          );
        })}
      </select>
    );
  }
  
  SelectWidget.defaultProps = {
    autofocus: false,
  };
  
  if (process.env.NODE_ENV !== 'production') {
    SelectWidget.propTypes = {
      autofocus: PropTypes.bool,
      disabled: PropTypes.bool,
      id: PropTypes.string.isRequired,
      multiple: PropTypes.bool,
      onBlur: PropTypes.func,
      onChange: PropTypes.func,
      onFocus: PropTypes.func,
      options: PropTypes.shape({
        enumOptions: PropTypes.array,
        hideEmpty: PropTypes.bool,
      }).isRequired,
      readonly: PropTypes.bool,
      required: PropTypes.bool,
      schema: PropTypes.object.isRequired,
      value: PropTypes.any,
    };
  }
  
  export default SelectWidget;
  