v0ltoz/react-datetimepicker

View on GitHub
src/lib/ranges/RangeButton.jsx

Summary

Maintainability
C
1 day
Test Coverage
import React from 'react';
import ReactDOM from 'react-dom';
import '../style/DateTimeRange.css';
import PropTypes from 'prop-types';
import { addFocusStyle } from '../utils/StyleUtils';
import { rangeButtonSelectedStyle, rangeButtonStyle } from '../utils/TimeFunctionUtils';

class RangeButton extends React.Component {
  constructor(props) {
    super(props);

    if (props.index === props.selectedRange) {
      this.state = {
        style: rangeButtonSelectedStyle(),
      };
    } else {
      this.state = {
        style: rangeButtonStyle(),
      };
    }

    this.mouseEnter = this.mouseEnter.bind(this);
    this.mouseLeave = this.mouseLeave.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.keyDown = this.keyDown.bind(this);
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.props !== prevProps) {
      let focused = this.props.focused[this.props.index];
      if (this.props.index === this.props.selectedRange || focused) {
        this.setRangeSelectedStyle();
      } else {
        this.setRangeButtonStyle();
      }
    }

    let isComponentViewing = this.props.index === this.props.viewingIndex;
    let focused = this.props.focused;
    let focusedOnARange = false;
    for (let i = 0; i < focused.length; i++) {
      if (focused[i] === true) {
        focusedOnARange = true;
        break;
      }
    }
    // If the component we are currently on is the selected viewing component
    // and we are focused on it according to our focused matrix.
    // Then add an event listener for this button and set it as focused
    if (isComponentViewing && focusedOnARange) {
      document.addEventListener('keydown', this.keyDown, false);
      this.button.focus();
    }
  }

  setRangeSelectedStyle() {
    let style;
    if (this.props.style && this.props.style.customRangeSelected) {
      style = Object.assign(rangeButtonSelectedStyle(), this.props.style.customRangeSelected);
    } else {
      style = rangeButtonSelectedStyle();
    }
    this.setState({
      style: style,
    });
  }

  setRangeButtonStyle() {
    let style;
    if (this.props.style && this.props.style.customRangeButtons) {
      style = Object.assign(rangeButtonStyle(), this.props.style.customRangeButtons);
    } else {
      style = rangeButtonStyle();
    }
    this.setState({
      style: style,
    });
  }

  mouseEnter() {
    // Set hover style
    this.setRangeSelectedStyle();
  }

  mouseLeave(focused) {
    let isFocused;
    if (typeof focused === 'boolean') {
      isFocused = focused;
    } else {
      isFocused = this.state.focused;
    }
    let isSelected = this.props.index === this.props.selectedRange;
    // If not selected and not focused then on mouse leave set to normal style
    if (!isSelected && !isFocused) {
      this.setRangeButtonStyle();
    }
  }

  onFocus() {
    this.setState({ focused: true });
    this.props.setFocusedCallback(this.props.index, true);
    this.mouseEnter(true);
  }

  onBlur() {
    this.setState({ focused: false });
    this.props.setFocusedCallback(this.props.index, false);
    this.mouseLeave(false);
    document.removeEventListener('keydown', this.keyDown, false);
  }

  keyDown(e) {
    let componentFocused = document.activeElement === ReactDOM.findDOMNode(this.button);
    // Up Key
    if (e.keyCode === 38 && componentFocused) {
      e.preventDefault();
      this.props.viewingIndexChangeCallback(this.props.index - 1);
    }
    // Down Key
    else if (e.keyCode === 40 && componentFocused) {
      e.preventDefault();
      this.props.viewingIndexChangeCallback(this.props.index + 1);
    }
    // Space Bar and Enter
    else if (e.keyCode === 32 || e.keyCode === 13) {
      this.props.rangeSelectedCallback(this.props.index, this.props.label);
    }
  }

  render() {
    let isViewingIndex = this.props.viewingIndex === this.props.index;
    let tabIndex;
    if (isViewingIndex) {
      tabIndex = 0;
    } else {
      tabIndex = -1;
    }
    let style = {};
    style = addFocusStyle(this.state.focused, style);
    style = Object.assign(style, this.state.style);
    return (
      <div
        ref={button => {
          this.button = button;
        }}
        id={"rangeButton" + this.props.index}
        onMouseEnter={this.mouseEnter}
        onMouseLeave={this.mouseLeave}
        onFocus={this.onFocus}
        onBlur={this.onBlur}
        tabIndex={tabIndex}
        style={style}
        onMouseDown={() => {
          this.props.rangeSelectedCallback(this.props.index, this.props.label);
          this.onFocus();
        }}
      >
        <div className="rangebuttontextstyle">{this.props.label}</div>
      </div>
    );
  }
}

RangeButton.propTypes = {
  selectedRange: PropTypes.number.isRequired,
  rangeSelectedCallback: PropTypes.func.isRequired,
  viewingIndexChangeCallback: PropTypes.func.isRequired,
  setFocusedCallback: PropTypes.func.isRequired,
  index: PropTypes.number.isRequired,
  viewingIndex: PropTypes.number.isRequired,
  label: PropTypes.string.isRequired,
  focused: PropTypes.array.isRequired,
  style: PropTypes.object,
};
export default RangeButton;