-
-
Save minhhahl/a6185c81aa7ecf31aaffcff927ae3375 to your computer and use it in GitHub Desktop.
React Slider With Range
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
import React, { PropTypes, Component } from 'react' | |
let Fill = ({ | |
start, end, | |
getPositionFromValue | |
}) => | |
<div | |
className='rangeslider__fill' | |
style={{ | |
left: (getPositionFromValue(start) + 10 )+ 'px', | |
width: getPositionFromValue(end - start) + 'px' | |
}} | |
/> | |
export default Fill |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
import React, { PropTypes, Component } from 'react' | |
class Handle extends Component { | |
static defaultProps = { | |
step: 1 | |
} | |
constructor (props, context) { | |
super(props, context) | |
this.state = { | |
limit: 480, | |
grab: 20 | |
} | |
} | |
componentWillReceiveProps(nextProps){ | |
let {sliderPos} = nextProps | |
const handlePos = 40 | |
this.setState({ | |
limit: sliderPos - handlePos | |
}) | |
} | |
handleNoop = (e) => { | |
e.stopPropagation() | |
e.preventDefault() | |
} | |
handleStart = () => { | |
document.addEventListener('mousemove', this.handleDrag) | |
document.addEventListener('mouseup', this.handleEnd) | |
} | |
handleDrag = (e) => { | |
this.handleNoop(e) | |
const { onChange } = this.props | |
if (!onChange) return | |
const value = this.position(e) | |
onChange && onChange(value) | |
} | |
handleEnd = () => { | |
document.removeEventListener('mousemove', this.handleDrag) | |
document.removeEventListener('mouseup', this.handleEnd) | |
} | |
position = (e) => { | |
const { grab } = this.state | |
const { | |
direction | |
} = this.props | |
const clientCoordinateStyle = `clientX` | |
const coordinate = !e.touches | |
? e[clientCoordinateStyle] | |
: e.touches[0][clientCoordinateStyle] | |
const pos = coordinate - direction - grab | |
const value = this.getValueFromPosition(pos) | |
return value | |
} | |
getValueFromPosition = (pos) => { | |
let value = null | |
const { limit } = this.state | |
const { min, max, step } = this.props | |
const percentage = (clamp(pos, 0, limit) / (limit || 1)) | |
const baseVal = step * Math.round(percentage * (max - min) / step) | |
value = baseVal + min | |
if (value >= max) value = max | |
if (value <= min) value = min | |
return value | |
} | |
render () { | |
let { | |
value, | |
handleNoop, getPositionFromValue | |
} = this.props | |
return ( | |
<div | |
ref={(sh) => { this.handle = sh }} | |
className='rangeslider__handle' | |
onMouseDown={this.handleStart} | |
onTouchEnd={handleNoop} | |
onTouchMove={this.handleDrag} | |
style={{ | |
left: getPositionFromValue(value) + 'px' | |
}} | |
/> | |
) | |
} | |
} | |
export default Handle | |
function clamp (value, min, max) { | |
return Math.min(Math.max(value, min), max) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.rangeslider { | |
margin: 20px 0; | |
position: relative; | |
background: #e6e6e6; | |
transition: all 0.5s ease; | |
} | |
.rangeslider .rangeslider__fill, | |
.rangeslider .rangeslider__handle { | |
position: absolute; | |
} | |
.rangeslider, | |
.rangeslider .rangeslider__fill { | |
display: block; | |
box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3); | |
} | |
.rangeslider .rangeslider__handle { | |
background: #fff; | |
border: 1px solid #ccc; | |
cursor: pointer; | |
display: inline-block; | |
position: absolute; | |
} | |
.rangeslider .rangeslider__handle:active { | |
background: #999; | |
} | |
/** | |
* Rangeslider - Horizontal slider | |
*/ | |
.rangeslider-horizontal { | |
height: 20px; | |
border-radius: 10px; | |
} | |
.rangeslider-horizontal .rangeslider__fill { | |
height: 100%; | |
background: #319ed6; | |
border-radius: 10px; | |
top: 0; | |
} | |
.rangeslider-horizontal .rangeslider__handle { | |
width: 40px; | |
height: 40px; | |
border-radius: 40px; | |
top: -10px; | |
} | |
@media screen and (min-width: 1200px) { | |
#mount { | |
width: 600px; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
require("./Slider.css"); | |
import React, { PropTypes, Component } from 'react' | |
import Fill from './Fill' | |
import Handle from './Handle' | |
class Slider extends Component { | |
static defaultProps = { | |
min: 0, | |
max: 20, | |
step: 1, | |
value: 0, | |
minRange: 5 | |
} | |
constructor(props, context){ | |
super(props, context) | |
this.state = { | |
limit: 0 | |
} | |
} | |
componentDidMount () { | |
window.addEventListener('resize', this.handleUpdate) | |
this.handleUpdate() | |
} | |
componentWillUnmount () { | |
window.removeEventListener('resize', this.handleUpdate) | |
} | |
updateStart = (start) => { | |
let { | |
onChange, end, minRange | |
} = this.props | |
if(end - start < minRange){ | |
end = start + minRange | |
} | |
onChange(start, end) | |
} | |
updateEnd = (end) => { | |
let { | |
onChange, start, minRange | |
} = this.props | |
if(end - start < minRange){ | |
start = end - minRange | |
} | |
onChange(start, end) | |
} | |
handleUpdate = () => { | |
const sliderPos = this.slider[`offsetWidth`] | |
const handlePos = 40 | |
const node = this.slider | |
const direction = node.getBoundingClientRect()['left'] | |
console.log('base dir', direction) | |
this.setState({ | |
limit: sliderPos - handlePos, | |
grab: handlePos / 2, | |
sliderPos, | |
direction | |
}) | |
} | |
handleNoop = (e) => { | |
e.stopPropagation() | |
e.preventDefault() | |
} | |
getPositionFromValue = (value) => { | |
const { limit } = this.state | |
const { min, max } = this.props | |
const diffMaxMin = max - min | |
const diffValMin = value - min | |
const percentage = diffValMin / diffMaxMin | |
const pos = Math.round(percentage * limit) | |
return pos | |
} | |
render(){ | |
let { | |
min, max, | |
start, end, minRange | |
} = this.props | |
let { | |
sliderPos, direction | |
} = this.state | |
return <div | |
className={'rangeslider' + ' ' + `rangeslider-horizontal`} | |
ref={(s) => { this.slider = s }} | |
onTouchEnd={this.handleNoop} | |
> | |
<Fill | |
start={start} | |
end={end} | |
getPositionFromValue={this.getPositionFromValue} | |
/> | |
<Handle | |
value={start} | |
onChange={this.updateStart} | |
min={min} | |
max={max - minRange} | |
handleNoop={this.handleNoop} | |
sliderPos={sliderPos} | |
direction={direction} | |
getPositionFromValue={this.getPositionFromValue} | |
/> | |
<Handle | |
value={end} | |
onChange={this.updateEnd} | |
min={min + minRange} | |
max={max} | |
handleNoop={this.handleNoop} | |
sliderPos={sliderPos} | |
direction={direction} | |
getPositionFromValue={this.getPositionFromValue} | |
/> | |
</div> | |
} | |
} | |
export default Slider |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react' | |
import Slider from './Slider' | |
class Usage extends Component { | |
constructor (props, context) { | |
super(props, context) | |
this.state = { | |
start: 0, | |
end: 20 | |
} | |
} | |
handleChange = (start, end) => { | |
console.log('start',start, 'end', end) | |
this.setState({ | |
start, end | |
}) | |
} | |
render () { | |
const { start, end } = this.state | |
return ( | |
<div className='horizontal-slider'> | |
<h4>Basic Slider</h4> | |
<Slider | |
min={0} | |
max={20} | |
start={start} | |
end={end} | |
minRange={5} | |
onChange={this.handleChange} | |
/> | |
<div className='value'>Start: {start} End: {end}</div> | |
<hr /> | |
</div> | |
) | |
} | |
} | |
export default Usage |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment