UNPKG

react-widgets

Version:
244 lines (195 loc) 7.49 kB
/** React.DOM */ var React = require('react') , cx = require('../util/cx') , _ = require('lodash') , mergeIntoProps = require('../util/transferProps').mergeIntoProps , directions = require('../util/constants').directions , Input = require('./number-input'); var btn = require('../common/btn') , propTypes = { value: React.PropTypes.number, onChange: React.PropTypes.func, min: React.PropTypes.number, max: React.PropTypes.number, step: React.PropTypes.number, culture: React.PropTypes.string, format: React.PropTypes.string, parse: React.PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.string), React.PropTypes.string, React.PropTypes.func ]), disabled: React.PropTypes.oneOfType([ React.PropTypes.bool, React.PropTypes.oneOf(['disabled']) ]), readOnly: React.PropTypes.oneOfType([ React.PropTypes.bool, React.PropTypes.oneOf(['readOnly']) ]), messages: React.PropTypes.shape({ increment: React.PropTypes.string, decrement: React.PropTypes.string }) }; module.exports = React.createClass({ displayName: 'NumberPicker', mixins: [ require('../mixins/PureRenderMixin'), require('../mixins/RtlParentContextMixin'), ], propTypes: propTypes, getDefaultProps: function(){ return { format: 'd', min: -Infinity, max: Infinity, step: 1, messages: { increment: 'increment value', decrement: 'decrement value' } } }, getInitialState: function(){ return { focused: false, active: false, } }, render: function(){ var self = this , val = this.inRangeValue(this.props.value) //console.log('render', this.state.focused) return mergeIntoProps( _.omit(this.props, _.keys(propTypes)), React.DOM.div({ref: "element", onKeyDown: this._maybeHandle(this._keyDown), onFocus: this._maybeHandle(_.partial(this._focus, true), true), onBlur: _.partial(this._focus, false), tabIndex: "-1", className: cx({ 'rw-number-picker': true, 'rw-widget': true, 'rw-state-focus': this.state.focused, 'rw-state-disabled': this.props.disabled, 'rw-state-readonly': this.props.readOnly, 'rw-rtl': this.isRtl() })}, React.DOM.span({className: "rw-select"}, btn({ tabIndex: "-1", className: cx({ 'rw-state-active': this.state.active === directions.UP}), onMouseDown: this._maybeHandle(_.partial(self._mouseDown, directions.UP)), onMouseUp: this._maybeHandle(_.partial(this._mouseUp, directions.UP)), onClick: this._maybeHandle(_.partial(this._focus, true)), disabled: val === this.props.max || this.props.disabled, 'aria-disabled': val === this.props.max || this.props.disabled}, React.DOM.i({className: "rw-i rw-i-caret-up"}, React.DOM.span({className: "rw-sr"}, this.props.messages.increment)) ), btn({ tabIndex: "-1", className: cx({ 'rw-state-active': this.state.active === directions.DOWN}), onMouseDown: this._maybeHandle(_.partial(self._mouseDown, directions.DOWN)), onMouseUp: this._maybeHandle(_.partial(this._mouseUp, directions.DOWN)), onClick: this._maybeHandle(_.partial(this._focus, true)), disabled: val === this.props.min || this.props.disabled, 'aria-disabled': val === this.props.min || this.props.disabled}, React.DOM.i({className: "rw-i rw-i-caret-down"}, React.DOM.span({className: "rw-sr"}, this.props.messages.decrement)) ) ), Input({ ref: "input", value: val, editing: this.state.focused, format: this.props.format, role: "spinbutton", min: this.props.min, 'aria-valuenow': val, 'aria-valuemin': _.isFinite(this.props.min) ? this.props.min : '', 'aria-valuemax': _.isFinite(this.props.max) ? this.props.max : '', 'aria-disabled': this.props.disabled, 'aria-readonly': this.props.readonly, disabled: this.props.disabled, readOnly: this.props.readOnly, onChange: this.change, onKeyDown: this.props.onKeyDown}) ) ) }, //allow for styling, focus stealing keeping me from the normal what have you _mouseDown: function(dir) { var self = this , val = dir === directions.UP ? (this.props.value || 0) + this.props.step : (this.props.value || 0) - this.props.step val = this.inRangeValue(val) this.setState({ active: dir }) this.change(val); if( !((dir === directions.UP && val === this.props.max) || (dir === directions.DOWN && val === this.props.min))) { if(!this.interval) this.interval = setInterval(this._mouseDown, 500, dir) } else this._mouseUp() }, _mouseUp: function(direction, e ){ this.setState({ active: false }) clearInterval(this.interval) this.interval = null; }, _focus: function(focused, e){ var self = this; clearTimeout(self.timer) self.timer = setTimeout(function(){ var el = self.refs.input.getDOMNode() focused && el.focus() if( focused !== self.state.focused) self.setState({ focused: focused }) }, 0) }, _keyDown: function(e){ var key = e.key; if ( key === 'End' && _.isFinite(this.props.max)) this.change(this.props.max) else if ( key === 'Home' && _.isFinite(this.props.min)) this.change(this.props.min) else if ( key === 'ArrowDown' ){ e.preventDefault() this.decrement() } else if ( key === 'ArrowUp' ){ e.preventDefault() this.increment() } }, _maybeHandle: function(handler, disabledOnly){ if ( !(this.props.disabled || (!disabledOnly &&this.props.readOnly))) return handler }, increment: function() { this.change(this.inRangeValue((this.props.value || 0) + this.props.step)) }, decrement: function(){ this.change(this.inRangeValue((this.props.value || 0) - this.props.step)) }, change: function(val){ var change = this.props.onChange val = this.inRangeValue(val) if ( change && this.props.value !== val ) change(val) }, inRangeValue: function(value){ if( !_.isFinite(this.props.min) && value == null || value === '' ) return value return Math.max( Math.min(value, this.props.max) , this.props.min) }, _id: function(suffix){ this._id_ || (this._id_ = _.uniqueId('rw_')) return (this.props.id || this._id_) + suffix }, })