Skip to content

Instantly share code, notes, and snippets.

@minhhahl
Forked from misterfresh/Fill.js
Created December 2, 2016 07:05
Show Gist options
  • Save minhhahl/a6185c81aa7ecf31aaffcff927ae3375 to your computer and use it in GitHub Desktop.
Save minhhahl/a6185c81aa7ecf31aaffcff927ae3375 to your computer and use it in GitHub Desktop.
React Slider With Range
'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
'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)
}
.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;
}
}
'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
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