UNPKG

apeman-react-range

Version:
263 lines (225 loc) 6.02 kB
/** * Range input component. * @class ApRange */ 'use strict' import React, {PropTypes as types} from 'react' import classnames from 'classnames' import ReactDOM from 'react-dom' import chopcal from 'chopcal' import rangecal from 'rangecal' import ApRangeHandle from './ap_range_handle' import ApRangeLabel from './ap_range_label' import {ApTouchable} from 'apeman-react-touchable' /** @lends ApRange */ const ApRange = React.createClass({ // -------------------- // Specs // -------------------- propTypes: { /** Range from value */ from: types.number, /** Range to value */ to: types.number, /** Min value for range from */ min: types.number, /** Max value for range to */ max: types.number, /** Step for value */ step: types.number, /** Handler for change */ onChange: types.func, barOnly: types.bool }, statics: {}, getInitialState () { const s = this let { props } = s return { minX: 0, maxX: 1200, fromX: 0, toX: 1200, fromValue: props.from, toValue: props.to } }, getDefaultProps () { return { from: 25, to: 75, min: 0, max: 100, step: 0.01, barOnly: false } }, render () { const s = this const { state, props } = s return ( <div className={ classnames('ap-range', props.className) }> <div className="ap-range-inner"> { s._renderLabel(props.min) } <div className="ap-range-bar-wrap"> <ApTouchable onTap={s.rangeBarDidTap}> <div className="ap-range-bar"> <div className="ap-range-bar-bg"></div> <div className="ap-range-bar-highlight" style={ { left: state.fromX, width: (state.toX - state.fromX) } }> </div> </div> </ApTouchable> <ApRangeHandle onMove={ s.rangeFromHandleDidMove } shouldMove={ s.shouldRangeFromHandleMove } x={ state.fromX } minX={ state.minX } maxX={ state.maxX } className="ap-range-handle-from"/> <ApRangeHandle onMove={ s.rangeToHandleDidMove } shouldMove={ s.shouldRangeToHandleMove } x={ state.toX } minX={ state.minX } maxX={ state.maxX } className="ap-range-handle-to"/> </div> { s._renderLabel(props.max) } </div> </div> ) }, // -------------------- // Lifecycle // -------------------- componentDidMount () { const s = this window.addEventListener('resize', s.resizeRange) s.resizeRange() s.resetRangeValues() }, componentWillReceiveProps (nextProps) { const s = this s.resetRangeValues() }, componentWillUnmount () { const s = this window.removeEventListener('resize', s.resizeRange) }, // ------------------ // Helper // ------------------ resizeRange (e) { const s = this let state = s.state let w = ReactDOM.findDOMNode(s).offsetWidth let minX = 0 let maxX = w let fromRate = s._rateWithValue(state.fromValue) let toRate = s._rateWithValue(state.toValue) s.setState({ minX: minX, maxX: maxX, fromX: rangecal.value(minX, maxX, fromRate), toX: rangecal.value(minX, maxX, toRate) }) }, rangeBarDidTap () { }, rangeFromHandleDidMove (e) { const s = this let fromValue = s._valueWithX(e.detail.x) s.setRangeValues(fromValue, s.state.toValue, true) }, rangeToHandleDidMove (e) { const s = this let toValue = s._valueWithX(e.detail.x) s.setRangeValues(s.state.fromValue, toValue, false) }, shouldRangeFromHandleMove () { const s = this return true }, shouldRangeToHandleMove () { const s = this return true }, resetRangeValues () { const s = this setTimeout(function () { let state = s.state s.setRangeValues(state.fromValue, state.toValue, true) }, 0) }, setRangeValues (fromValue, toValue, forwarding) { const s = this let { state, props } = s let { minX, maxX } = state let step = props.step if (toValue < fromValue) { if (forwarding) { toValue = fromValue } else { fromValue = toValue } } let fromRate = s._rateWithValue(fromValue) let toRate = s._rateWithValue(toValue) s.setState({ fromValue: fromValue, toValue: toValue, fromX: rangecal.value(minX, maxX, fromRate), toX: rangecal.value(minX, maxX, toRate) }) fromValue = chopcal.round(fromValue, step) toValue = chopcal.round(toValue, step) let duplicate = (s._fromValue === fromValue) && (s._toValue === toValue) if (duplicate) { return } s._fromValue = fromValue s._toValue = toValue if (props.onChange) { props.onChange( fromValue, toValue, { element: s } ) } }, // ------------------ // Private // ------------------ _rateWithValue (value) { const s = this let { min, max } = s.props value = rangecal.round(min, max, value) return chopcal.round(rangecal.rate(min, max, value), 0.01) }, _valueWithRate (rate) { const s = this let { min, max } = s.props let value = chopcal.round(rangecal.value(min, max, rate), 0.01) return rangecal.round(min, max, value) }, _valueWithX (x) { const s = this let { minX, maxX } = s.state let rate = rangecal.rate(minX, maxX, x + 2) return s._valueWithRate(rate) }, _renderLabel (value) { const s = this let { props } = s if (props.barOnly) { return null } return ( <ApRangeLabel value={ value }/> ) } }) export default ApRange