<commit_before>import React, { PropTypes } from 'react';
import classNames from 'classnames';
import interact from 'interact.js';
import { identity, noop } from 'lodash';
import { CommonPropTypes, PureComponent } from '../../../utils';

import { getDimension, getSnapTargetFunc } from './util';
import style from './style.css';

function getOffset(direction, width) {
  if (direction === 'left') {
    return -Math.round(width / 2);
  } else if (direction === 'right') {
    return Math.round(width / 2);
  }
  return 0;
}

export default class Handle extends PureComponent {
  constructor(props) {
    super(props);

    this.handleRef = this.handleRef.bind(this);
  }

  componentDidMount() {
    this.bindInteract(this.props.snapTarget);
  }

  componentWillReceiveProps(newProps) {
    if (this.props.snapTarget !== newProps.snapTarget ||
        this.props.height !== newProps.height) {
      this.bindInteract(newProps.snapTarget);
    }
  }

  componentWillUnmount() {
    this._interactable.unset();
  }

  bindInteract(snapTarget) {
    if (this._interactable) this._interactable.unset();
    const offset = getOffset(this.props.direction, this._handle.getBoundingClientRect().width);
    this._interactable = interact(this._handle)
      .origin('parent')
      .draggable({
        max: Infinity,
        snap: {
          targets: [getSnapTargetFunc(snapTarget, {
            range: Infinity,
            offset: { x: offset },
          })],
        },
      })
      .styleCursor(false)
      .on('dragmove', this.props.onMove(this.props.name, -offset))
      .on('tap', (event) => { event.target.focus(); });
  }

  handleRef(ref) {
    this._handle = ref;
  }

  render() {
    return (
      <button
        type="button"
        className={classNames(this.props.className, style.flag, style[this.props.direction])}
        style={{ left: getDimension(this.props.position) }}
        ref={this.handleRef}
        onKeyDown={this.props.onKeyDown(this.props.name)}
      >
        {this.props.labelFunc(this.props.label)}
      </button>
    );
  }
}

Handle.propTypes = {
  className: CommonPropTypes.className,
  style: PropTypes.object,
  snapTarget: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.object,
  ]).isRequired,
  direction: PropTypes.oneOf(['left', 'right', 'middle']),
  position: PropTypes.number.isRequired,

  /* label and labelFunc control what is displayed on the handle */
  label: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
  ]).isRequired,
  labelFunc: PropTypes.func,

  /* value key name */
  name: PropTypes.string.isRequired,

  /* events */
  onMove: PropTypes.func.isRequired,
  onKeyDown: PropTypes.func,
};

Handle.defaultProps = {
  direction: 'middle',
  labelFunc: identity,
  onKeyDown: noop,
};<commit_msg>refactor `position` calculation to use `propUpdates` and incorporate `style` prop<commit_after>import React, { PropTypes } from 'react';
import classNames from 'classnames';
import interact from 'interact.js';
import { identity, noop } from 'lodash';
import { CommonPropTypes, PureComponent } from '../../../utils';

import { getDimension, getSnapTargetFunc, stateFromPropUpdates, updateFunc } from './util';
import style from './style.css';

function getOffset(direction, width) {
  if (direction === 'left') {
    return -Math.round(width / 2);
  } else if (direction === 'right') {
    return Math.round(width / 2);
  }
  return 0;
}

export default class Handle extends PureComponent {
  constructor(props) {
    super(props);
    this.state = stateFromPropUpdates(Handle.propUpdates, {}, props, {});

    this.handleRef = this.handleRef.bind(this);
  }

  componentDidMount() {
    this.bindInteract(this.props.snapTarget);
  }

  componentWillReceiveProps(newProps) {
    if (this.props.snapTarget !== newProps.snapTarget ||
        this.props.height !== newProps.height) {
      this.bindInteract(newProps.snapTarget);
    }

    this.setState(stateFromPropUpdates(Handle.propUpdates, this.props, newProps, {}));
  }

  componentWillUnmount() {
    this._interactable.unset();
  }

  bindInteract(snapTarget) {
    if (this._interactable) this._interactable.unset();
    const offset = getOffset(this.props.direction, this._handle.getBoundingClientRect().width);
    this._interactable = interact(this._handle)
      .origin('parent')
      .draggable({
        max: Infinity,
        snap: {
          targets: [getSnapTargetFunc(snapTarget, {
            range: Infinity,
            offset: { x: offset },
          })],
        },
      })
      .styleCursor(false)
      .on('dragmove', this.props.onMove(this.props.name, -offset))
      .on('tap', (event) => { event.target.focus(); });
  }

  handleRef(ref) {
    this._handle = ref;
  }

  render() {
    return (
      <button
        type="button"
        className={classNames(this.props.className, style.flag, style[this.props.direction])}
        style={this.state.style}
        ref={this.handleRef}
        onKeyDown={this.props.onKeyDown(this.props.name)}
      >
        {this.props.labelFunc(this.props.label)}
      </button>
    );
  }
}

Handle.propTypes = {
  className: CommonPropTypes.className,
  style: PropTypes.object,
  snapTarget: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.object,
  ]).isRequired,
  direction: PropTypes.oneOf(['left', 'right', 'middle']),
  position: PropTypes.number.isRequired,

  /* label and labelFunc control what is displayed on the handle */
  label: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
  ]).isRequired,
  labelFunc: PropTypes.func,

  /* value key name */
  name: PropTypes.string.isRequired,

  /* events */
  onMove: PropTypes.func.isRequired,
  onKeyDown: PropTypes.func,
};

Handle.defaultProps = {
  direction: 'middle',
  labelFunc: identity,
  onKeyDown: noop,
};

Handle.propUpdates = {
  position: updateFunc((nextProp, _, nextProps) => {
    return { style: { ...nextProps.style, left: getDimension(nextProp) } };
  }),
};
{'fraction_non_alphanumeric': 0.1451747783910353, 'fraction_numerical': 0.0010035122930255895, 'mean_word_length': 3.1847445766270117, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 10, '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': '5873369', 'n_tokens_mistral': 1876, 'n_tokens_neox': 1871, 'n_words': 358}