<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import MomentPropTypes from 'react-moment-proptypes';

class TimeSelect extends React.Component {
  static propTypes = {
    value: PropTypes.shape({
      hour: PropTypes.number,
      minute: PropTypes.number,
    }).isRequired,
    onChange: PropTypes.func.isRequired,
    timespan: PropTypes.shape({
      start: MomentPropTypes.momentObj,
      finish: MomentPropTypes.momentObj,
    }).isRequired,
    children: PropTypes.node,
  };

  static defaultProps = {
    children: null,
  };

  inputChanged = (event) => {
    const newValue = event.target.value;

    if (newValue && newValue !== '') {
      this.props.onChange(
        { ...this.props.value, [event.target.name]: parseInt(newValue, 10) },
      );
    } else {
      this.props.onChange(
        { ...this.props.value, [event.target.name]: null },
      );
    }
  }

  render = () => {
    const { value, timespan } = this.props;

    const hourOptions = [];
    let hourOffset = 0;
    while (timespan.start.clone().add(hourOffset, 'hours').isBefore(timespan.finish)) {
      const now = timespan.start.clone().add(hourOffset, 'hours');
      const dayDiff = now.diff(timespan.start.clone().startOf('day'), 'days');
      let description = `${now.hour()}`;
      if (dayDiff > 0) {
        description += ` (+${dayDiff} ${dayDiff > 1 ? 'days' : 'day'})`;
      }

      hourOptions.push(
        <option key={hourOffset} value={hourOffset + timespan.start.hour()}>
          {description}
        </option>,
      );
      hourOffset += 1;
    }

    const minuteOptions = [0, 15, 30, 45].map(minute => (
      <option key={minute} value={minute}>{minute.toString(10).padStart(2, '0')}</option>
    ));

    return (
      <div className="form-inline">
        <label className="sr-only">Hour</label>
        <select
          className="form-control mr-1"
          name="hour"
          value={value.hour == null ? '' : value.hour}
          onChange={this.inputChanged}
        >
          <option />
          {hourOptions}
        </select>

        <span className="mx-1">:</span>

        <label className="sr-only">Minute</label>
        <select
          className="form-control mr-1"
          name="minute"
          value={value.minute == null ? '' : value.minute}
          onChange={this.inputChanged}
        >
          <option />
          {minuteOptions}
        </select>

        {this.props.children}
      </div>
    );
  }
}

export default TimeSelect;<commit_msg>Deduplicate and label things<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import MomentPropTypes from 'react-moment-proptypes';
import { enableUniqueIds } from 'react-html-id';

class TimeSelect extends React.Component {
  static propTypes = {
    value: PropTypes.shape({
      hour: PropTypes.number,
      minute: PropTypes.number,
    }).isRequired,
    onChange: PropTypes.func.isRequired,
    timespan: PropTypes.shape({
      start: MomentPropTypes.momentObj,
      finish: MomentPropTypes.momentObj,
    }).isRequired,
    children: PropTypes.node,
  };

  static defaultProps = {
    children: null,
  };

  constructor(props) {
    super(props);
    enableUniqueIds(this);
  }

  inputChanged = (event) => {
    const newValue = event.target.value;

    if (newValue && newValue !== '') {
      this.props.onChange(
        { ...this.props.value, [event.target.name]: parseInt(newValue, 10) },
      );
    } else {
      this.props.onChange(
        { ...this.props.value, [event.target.name]: null },
      );
    }
  }

  render = () => {
    const { value, timespan } = this.props;

    const hourOptions = [];
    let hourOffset = 0;
    while (timespan.start.clone().add(hourOffset, 'hours').isBefore(timespan.finish)) {
      const now = timespan.start.clone().add(hourOffset, 'hours');
      const dayDiff = now.diff(timespan.start.clone().startOf('day'), 'days');
      let description = `${now.hour()}`;
      if (dayDiff > 0) {
        description += ` (+${dayDiff} ${dayDiff > 1 ? 'days' : 'day'})`;
      }

      hourOptions.push(
        <option key={hourOffset} value={hourOffset + timespan.start.hour()}>
          {description}
        </option>,
      );
      hourOffset += 1;
    }

    const minuteOptions = [0, 15, 30, 45].map(minute => (
      <option key={minute} value={minute}>{minute.toString(10).padStart(2, '0')}</option>
    ));

    const [hourSelect, minuteSelect] = [
      ['Hour', 'hour', hourOptions],
      ['Minute', 'minute', minuteOptions],
    ].map(([label, name, options]) => {
      const selectId = this.nextUniqueId();

      return [
        <label key={`label_${name}`} className="sr-only" htmlFor={selectId}>{label}</label>,
        <select
          key={`select_${name}`}
          id={selectId}
          className="form-control mr-1"
          name={name}
          value={value[name] == null ? '' : value[name]}
          onChange={this.inputChanged}
        >
          <option />
          {options}
        </select>,
      ];
    });

    return (
      <div className="form-inline">
        {hourSelect}
        <span className="mx-1">:</span>
        {minuteSelect}

        {this.props.children}
      </div>
    );
  }
}

export default TimeSelect;
{'fraction_non_alphanumeric': 0.16244326777609683, 'fraction_numerical': 0.007375189107413011, 'mean_word_length': 2.3947368421052633, 'pattern_counts': {'":': 0, '<': 34, '<?xml version=': 0, '>': 45, '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': '2760647', 'n_tokens_mistral': 1652, 'n_tokens_neox': 1622, 'n_words': 310}