<commit_before>// @flow

/*
TODO:
  - autofocus: how to handle multiple elements on page with autofocus={true} ?
*/

import React from 'react';
import { noop, omit, uniqueId } from 'lodash';

type InputEvent = SyntheticInputEvent<HTMLInputElement>;
type EventHandler = (event: InputEvent) => void;

type Props = {
  label: string,
  value: string,
  onBlur: EventHandler,
  onChange: EventHandler,
  onFocus: EventHandler,
};

type State = {
  hasFocus: boolean,
};

const mapPropsToInput = (props: Props) => omit(props, ['label', 'onBlur', 'onFocus']);
class TextField extends React.PureComponent<Props, State> {
  static defaultProps = {
    onBlur: noop,
    onFocus: noop,
  };

  id: string = uniqueId('TextFieldId_');

  handleInputBlur: EventHandler = (event) => {
    this.setState({ hasFocus: false });
    this.props.onBlur(event);
  };

  handleInputFocus: EventHandler = (event) => {
    this.setState({ hasFocus: false });
    this.props.onFocus(event);
  };

  render() {
    const { id, props, state } = this;

    return (
      <div className={state.hasFocus && 'has-focus'}>
        <label htmlFor={id}>
          {props.label}
        </label>
        <input
          {...mapPropsToInput(props)}
          id={id}
          onBlur={this.handleInputBlur}
          onFocus={this.handleInputFocus}
        />
      </div>
    );
  }
}

export default TextField;<commit_msg>Export factory instead of component to support custom styling via classNames mapping<commit_after>// @flow

/*
Features:
  - support "controlled" input only (require `props.value` and `props.onChange`)
  - support custom styling with global CSS or CSS modules
*/

/*
TODO:
  - autofocus: how to handle multiple elements on page with autofocus={true} ?
  - support css-in-js style API
*/

import React from 'react';
import { noop, omit, uniqueId } from 'lodash';

type InputEvent = SyntheticInputEvent<HTMLInputElement>;
type EventHandler = (event: InputEvent) => void;

type Props = {
  label: string,
  value: string,
  onBlur: EventHandler,
  onChange: EventHandler,
  onFocus: EventHandler,
};

type State = {
  hasFocus: boolean,
};

type ClassName = string;
type StateToClassNames = (
  state: State & { hasValue: boolean }
) => {
  container: ClassName,
  label: ClassName,
  input: ClassName,
};

const mapPropsToInput = (props: Props) => omit(props, ['label', 'onBlur', 'onFocus']);
function factory(mapStateToClassNames: StateToClassNames) {
  return class TextField extends React.PureComponent<Props, State> {
    static defaultProps = {
      onBlur: noop,
      onFocus: noop,
    };

    state = {
      hasFocus: false,
    };

    id: string = uniqueId('TextFieldId_');

    handleInputBlur: EventHandler = (event) => {
      this.setState({ hasFocus: false });
      this.props.onBlur(event);
    };

    handleInputFocus: EventHandler = (event) => {
      this.setState({ hasFocus: true });
      this.props.onFocus(event);
    };

    render() {
      const { id, props, state } = this;
      const hasValue = typeof props.value === 'string' && props.value.length > 0;
      const style = mapStateToClassNames({
        hasFocus: state.hasFocus,
        hasValue,
        // error: state.error,
      });

      return (
        <div className={style.container}>
          <label className={style.label} htmlFor={id}>
            {props.label}
          </label>
          <input
            {...mapPropsToInput(props)}
            className={style.input}
            id={id}
            onBlur={this.handleInputBlur}
            onFocus={this.handleInputFocus}
          />
        </div>
      );
    }
  };
}

const defaultTheme: StateToClassNames = ({ hasFocus, hasValue }) =>
  (hasFocus && hasValue
    ? { container: '**container**', label: '**label**', input: '**input**' }
    : { container: 'container', label: 'label', input: 'input' });

export default factory(defaultTheme);
export { factory as customizeTextField };
{'fraction_non_alphanumeric': 0.138395124428644, 'fraction_numerical': 0.00025393600812595224, 'mean_word_length': 2.950852557673019, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 28, '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': '3052221', 'n_tokens_mistral': 1233, 'n_tokens_neox': 1213, 'n_words': 305}