mahaplatform/reframe

View on GitHub
src/components/carousel/carousel.js

Summary

Maintainability
C
1 day
Test Coverage
import React from 'react'
import PropTypes from 'prop-types'

class Carousel extends React.Component {

  static propTypes = {
    active: PropTypes.number,
    direction: PropTypes.string,
    infinite: PropTypes.bool,
    slides: PropTypes.array,
    total: PropTypes.number,
    onSetTotal: PropTypes.func,
    onGoto: PropTypes.func,
    onNext: PropTypes.func,
    onPrevious: PropTypes.func
  }

  static defaultProps = {
    infinite: true,
    slides: []
  }

  _swipe = {}

  state = {
    active: 0,
    transitioning: false
  }

  render() {
    const { active, infinite, slides, total } = this.props
    return (
      <div className="reframe-carousel">
        { total > 0 &&
          <div { ...this._getTheatre() }>
            { total > 1 && (infinite || active > 0) &&
              <div className="reframe-carousel-previous" onClick={ this._handlePrevious.bind(this) }>
                <i className="fa fa-fw fa-chevron-left" />
              </div>
            }
            <div className="reframe-carousel-slides">
              { slides.map((slide, index) => (
                <div key={`slide_${index}`} className={ this._getSlideClass(index) }>
                  { slide }
                </div>
              ))}
            </div>
            { total > 1 && (infinite || active < total - 1) &&
              <div className="reframe-carousel-next" onClick={ this._handleNext.bind(this) }>
                <i className="fa fa-fw fa-chevron-right" />
              </div>
            }
          </div>
        }
        { total > 1 &&
          <div className="reframe-carousel-pagination">
            { [...Array(total)].map((i, index) => (
              <div key={`button_${index}`} className={ this._getButtonClass(index) } onClick={ this._handleGoto.bind(this, index) } />
            ))}
          </div>
        }
      </div>
    )
  }

  componentDidMount() {
    const { slides, onSetTotal } = this.props
    onSetTotal(slides.length)
  }

  componentDidUpdate(prevProps) {
    const { active, slides, onSetTotal } = this.props
    if(slides.length !== prevProps.slides.length) {
      onSetTotal(slides.length)
    }
    if(active != prevProps.active) {
      setTimeout(() => this.setState({ transitioning: true }), 100)
      setTimeout(() => this.setState({ active, transitioning: false }), 600)
    }
  }

  _getSlideClass(index) {
    const curr = this.state.active
    const next = this.props.active
    const { direction } = this.props
    const { transitioning } = this.state
    const classes = ['reframe-carousel-slide']
    const right = next !== curr && direction === 'right'
    const left = next !== curr && direction === 'left'
    if(index === curr) classes.push('active')
    if(left && index === next) classes.push('next')
    if(right && index === next) classes.push('prev')
    if(transitioning && left && (index === next || index === curr)) classes.push('left')
    if(transitioning && right && (index === next || index === curr)) classes.push('right')
    return classes.join(' ')
  }

  _getButtonClass(index) {
    const { active } = this.props
    const classes = ['reframe-carousel-pagination-button']
    if(index === active) classes.push('active')
    return classes.join(' ')
  }

  _getTheatre() {
    return {
      className: 'reframe-carousel-theatre',
      onTouchStart: this._handleTouchStart.bind(this),
      onTouchMove: this._handleTouchMove.bind(this),
      onTouchEnd: this._handleTouchEnd.bind(this)
    }
  }

  _handlePrevious() {
    this.props.onPrevious()
  }

  _handleNext() {
    this.props.onNext()
  }

  _handleGoto(index) {
    this.props.onGoto(index)
  }

  _handleTouchStart(e) {
    this._swipe = { x: e.touches[0].clientX }
  }

  _handleTouchMove(e) {
    if (e.changedTouches && e.changedTouches.length) {
      this._swipe.swiping = true
    }
  }

  _handleTouchEnd(e) {
    const touch = e.changedTouches[0]
    const dist = touch.clientX - this._swipe.x
    if (this._swipe.swiping && Math.abs(dist) > 30 ) {
      if(dist > 0) this.props.onPrevious()
      if(dist < 0) this.props.onNext()
    }
    this._swipe = {}
  }

}

export default Carousel