UNPKG

nw-react-slider--bki

Version:
311 lines (271 loc) 10 kB
'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var React = require('react'); var ReactDOM = require('react-dom'); var isFunction = require('lodash/isFunction'); var Draggable = require('react-draggable'); var isUndefined = require('lodash/isUndefined'); var throttle = require('lodash/throttle'); module.exports = React.createClass({ displayName: 'core-slider', propTypes: { value: React.PropTypes.number, min: React.PropTypes.number, max: React.PropTypes.number, ticks: React.PropTypes.bool, tickShape: React.PropTypes.string, onChange: React.PropTypes.func, onDragStart: React.PropTypes.func, onDragEnd: React.PropTypes.func, markerLabel: React.PropTypes.array, markerLabelPosition: React.PropTypes.string, fill: React.PropTypes.bool }, getDefaultProps: function getDefaultProps() { return { value: 0, min: 0, max: 10, ticks: false }; }, getInitialState: function getInitialState() { return { position: undefined, value: this.props.value, dragging: false }; }, componentWillReceiveProps: function componentWillReceiveProps(nextProps, nextState) { var newValue; // keep state up to date with passed in props if (this.state.value !== nextProps.value) { newValue = this.getBoundValue(nextProps, nextProps.value); this.setState({ value: newValue }); this.setHandlePosition(nextProps, newValue); } // if min or max changes, have to reposition the handle if (this.props.min !== nextProps.min || this.props.max !== nextProps.max) { newValue = this.getBoundValue(nextProps, newValue || this.state.value); this.setState({ value: newValue }); this.setHandlePosition(nextProps, newValue); } }, componentWillUpdate: function componentWillUpdate(nextProps, nextState) { if (isUndefined(this.state.position) && !isUndefined(nextState.position)) { this.props.onChange(nextState.value, nextState.position); } }, shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) { // Don't alter the component while dragging is occurring return !nextState.dragging; }, componentDidMount: function componentDidMount() { this.updateTrackWidth(); this.updateTrackWidth = throttle(this.updateTrackWidth, 100, { leading: false }); window.addEventListener('resize', this.updateTrackWidth); }, componentWillUnmount: function componentWillUnmount() { window.removeEventListener('resize', this.updateTrackWidth); }, getBoundValue: function getBoundValue(props, value) { var newValue = value; if (newValue < props.min) { newValue = props.min; } else if (newValue > props.max) { newValue = props.max; } return newValue; }, updateTrackWidth: function updateTrackWidth() { var track = ReactDOM.findDOMNode(this.refs.track); if (!track) { return; } var trackWidth = track.offsetWidth; this.setState({ trackWidth: trackWidth }, this.setHandlePosition); }, componentDidUpdate: function componentDidUpdate() { // after a render, ensure that draggable is in correct position this.refs.drag && this.refs.drag.setState({ clientX: this.state.position }); }, setHandlePosition: function setHandlePosition() { var props = arguments.length <= 0 || arguments[0] === undefined ? this.props : arguments[0]; var value = arguments.length <= 1 || arguments[1] === undefined ? this.state.value : arguments[1]; var position = this.state.trackWidth / (props.max - props.min) * (value - props.min); this.setState({ position: position }); if (isFunction(this.props.onChange)) { this.props.onChange(value, position); } }, updateValueFromPosition: function updateValueFromPosition(newPosition) { var currentPosition = newPosition; var value, position; if (this.props.max === this.props.min) { value = this.props.min; position = this.state.trackWidth / 2; } else { // find the two closest ticks to the current position var currentPercent = currentPosition / this.state.trackWidth * 100; var percentStep = 100 / (this.props.max - this.props.min); var closestSmallerValue = Math.floor(currentPercent / percentStep); var closestLargerValue = closestSmallerValue + 1; var bestMatchPercent, bestMatchTick; // determine which of the two values is closest if (currentPercent - closestSmallerValue * percentStep <= closestLargerValue * percentStep - currentPercent) { bestMatchTick = closestSmallerValue; bestMatchPercent = bestMatchTick * percentStep; } else { bestMatchTick = closestLargerValue; bestMatchPercent = bestMatchTick * percentStep; } // update the value and position value = this.props.min + bestMatchTick; position = this.state.trackWidth * (bestMatchPercent / 100); } // fire change event if callback exists if (isFunction(this.props.onChange)) { var rtposition = position; if (this.state.dragging) { rtposition = currentPosition; } this.props.onChange(value, rtposition); } // Although set state is async, pushing its invocation as late as possible this.setState({ value: value, position: position }); return position; }, cumulativeOffset: function cumulativeOffset(element) { // determine the overall offset of the element by crawling up the DOM, borrowed from Prototype.js var top = 0; var left = 0; do { top += element.offsetTop || 0; left += element.offsetLeft || 0; element = element.offsetParent; } while (element); return { top: top, left: left }; }, clickOnTrack: function clickOnTrack(event) { var clickFromLeft = event.clientX - this.cumulativeOffset(event.target).left; this.updateValueFromPosition(clickFromLeft); }, handleUp: function handleUp(event, ui) { var position = this.state.position; // Do we have a drag end hook ? if (isFunction(this.props.onDragEnd)) { this.props.onDragEnd(position); } this.setState({ dragging: false }); this.updateValueFromPosition(position); }, handleDown: function handleDown(event, ui) { // Do we have a drag start hook ? if (isFunction(this.props.onDragStart)) { this.props.onDragStart(this.state.position); } this.setState({ dragging: true }); }, dragging: function dragging(event, ui) { var pos = this.refs.drag.state.clientX || 0; this.updateValueFromPosition(pos); event.preventDefault(); }, renderTicks: function renderTicks() { if (!this.props.ticks) return React.createElement('span', null); var elements = []; var min = this.props.min; var max = this.props.max; var percentStep = 100 / (max - min); // Don't render ticks if it is too high. Will crash the browser and the ticks become useless if (max - min < 200) { for (var i = min + 1; i < max; i++) { var style = { left: percentStep * (i - min) + '%' }; elements.push(React.createElement('span', { key: 'tick' + i, className: 'slider__tick', style: style })); } } return React.createElement( 'div', { key: 'ticks', className: 'slider__ticks slider__ticks--' + this.props.tickShape, onClick: this.clickOnTrack }, elements ); }, renderMarkers: function renderMarkers() { if (!this.props.markerLabel) return React.createElement('span', null); var elements = []; var _props = this.props; var min = _props.min; var max = _props.max; var markers = _props.markerLabel; var percentStep = 100 / (max - min); for (var i in markers) { var style = { left: percentStep * (markers[i].value - min) + '%' }; if (markers[i].value <= max && markers[i].value >= min && max - min < 200) { // don't render a tick for this marker if ticks are already being rendered elements.push(React.createElement( 'div', { key: 'marker' + i, className: 'slider__marker marker', style: style }, React.createElement( 'p', { className: 'marker__label' }, markers[i].label ) )); } } return React.createElement( 'div', { key: 'markers', className: 'slider__markers slider__markers--' + this.props.markerLabelPosition, onClick: this.clickOnTrack }, elements ); }, renderFill: function renderFill() { if (!this.props.fill) { return; } return React.createElement('div', { className: 'slider__fill', style: { right: this.state.trackWidth - this.state.position + 'px' } }); }, render: function render() { var draggableProps, draggable; if (!isUndefined(this.state.position)) { draggableProps = { axis: 'x', handle: '.slider__handle', bounds: { left: 0, right: this.state.trackWidth }, start: { x: this.state.position, y: 0 }, onStop: this.handleUp, onStart: this.handleDown, onDrag: this.dragging }; draggable = React.createElement( Draggable, _extends({ ref: 'drag', key: 'draggable' }, draggableProps), React.createElement('span', { ref: 'handle', className: 'slider__handle' }) ); } return React.createElement( 'div', { ref: 'slider', className: 'slider' }, draggable, React.createElement( 'div', { ref: 'track', className: 'slider__track', onClick: this.clickOnTrack }, this.renderTicks(), this.renderMarkers(), this.renderFill() ) ); } }); //# sourceMappingURL=slider-core.js.map