Forked from WisaniShilumani/react-double-range-slider.jsx
Created
May 25, 2021 13:35
-
-
Save aberba/6a7b513e670ae6ca5bb14e775d1a12fa to your computer and use it in GitHub Desktop.
This file contains hidden or 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 { PRIMARY, PRIMARY_LIGHT, IRON_GREY } from '@styles/variables/colors' | |
import React, { useEffect, useRef, useState } from 'react' | |
import styled from 'styled-components' | |
const Slider = styled.div` | |
position: relative; | |
margin: 16px auto; | |
width: calc(100% - 12px); | |
height: 8px; | |
background: ${PRIMARY_LIGHT}; | |
border-radius: 5px; | |
cursor: pointer; | |
> div { | |
position: absolute; | |
top: 0; | |
width: 0; | |
height: 8px; | |
border-radius: 5px; | |
&:first-child { | |
z-index: 2; | |
background: ${PRIMARY_LIGHT}; | |
} | |
&:last-child { | |
z-index: 1; | |
background: ${PRIMARY}; | |
} | |
> div { | |
position: absolute; | |
right: -12px; | |
top: -8px; | |
width: 24px; | |
height: 24px; | |
border-radius: 50%; | |
background: ${PRIMARY}; | |
transition: all 0.3s; | |
&:hover { | |
box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.05); | |
} | |
&:active { | |
transform: scale(1.15, 1.15); | |
} | |
} | |
} | |
` | |
const SliderValues = styled.div` | |
display: flex; | |
justify-content: space-between; | |
width: 100%; | |
> * { | |
color: ${IRON_GREY}; | |
font-size: 14px; | |
} | |
` | |
const SliderWrapper = styled.div` | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
width: 100%; | |
padding: 16px 0; | |
box-sizing: border-box; | |
` | |
const roundToNearestHundredThousand = (number) => { | |
return Math.round(number / 100_000) * 100_000 | |
} | |
const DEFAULT_FORMAT_LABELS = (value) => `${value}` | |
const DoubleRangeSlider = ({ | |
min = 0, | |
max = 30_000_000, | |
onChange, | |
initialMinValue = 0, | |
initialMaxValue = 30_000_000, | |
formatLabels = DEFAULT_FORMAT_LABELS, | |
}) => { | |
const [initialize, setInitialize] = useState(false) | |
const [currentMin, setCurrentMin] = useState(initialMinValue) | |
const [currentMax, setCurrentMax] = useState(initialMaxValue) | |
const [sliderWidth, setSliderWidth] = useState(0) | |
const [offsetSliderWidth, setOffsetSliderWidth] = useState(0) | |
const [dragEnded, setDragEnded] = useState(false) | |
const minValueBetween = 500_000 | |
const minValue = useRef(null) | |
const maxValue = useRef(null) | |
const slider = useRef(null) | |
const minValueDrag = useRef(null) | |
const maxValueDrag = useRef(null) | |
useEffect(() => { | |
minValue.current.style.width = (currentMin * 100) / max + '%' | |
maxValue.current.style.width = (currentMax * 100) / max + '%' | |
const [{ left }] = slider.current.getClientRects() | |
setSliderWidth(slider.current.offsetWidth) | |
setOffsetSliderWidth(left) | |
if (!initialize) setTimeout(() => setInitialize(true), 1000) | |
}, [initialize]) | |
useEffect(() => { | |
if (dragEnded) onChange([currentMin, currentMax]) | |
}, [dragEnded]) | |
const onMouseMoveMin = (e) => { | |
const draggedWidth = (e.clientX || e.touches[0]?.pageX) - offsetSliderWidth | |
const draggedWidthInPercent = Math.max((draggedWidth * 100) / sliderWidth, 0) | |
const activeMin = Math.max(roundToNearestHundredThousand((max * draggedWidthInPercent) / 100), min) | |
if (activeMin <= currentMax - minValueBetween && minValue.current) { | |
minValue.current.style.width = draggedWidthInPercent + '%' | |
minValue.current.dataset.content = activeMin | |
setCurrentMin(activeMin) | |
} | |
} | |
const onMouseUpMin = () => { | |
document.removeEventListener('mouseup', onMouseUpMin) | |
document.removeEventListener('mousemove', onMouseMoveMin) | |
document.removeEventListener('touchend', onMouseUpMin) | |
document.removeEventListener('touchmove', onMouseMoveMin) | |
setDragEnded(true) | |
} | |
const changeMinValue = (e) => { | |
setDragEnded(false) | |
if (e.type !== 'touchstart') e.preventDefault() | |
document.addEventListener('mousemove', onMouseMoveMin) | |
document.addEventListener('mouseup', onMouseUpMin) | |
document.addEventListener('touchmove', onMouseMoveMin) | |
document.addEventListener('touchend', onMouseUpMin) | |
} | |
const changeMaxValue = (e) => { | |
setDragEnded(false) | |
if (e.type !== 'touchstart') e.preventDefault() | |
document.addEventListener('mousemove', onMouseMoveMax) | |
document.addEventListener('mouseup', onMouseUpMax) | |
document.addEventListener('touchmove', onMouseMoveMax) | |
document.addEventListener('touchend', onMouseUpMax) | |
} | |
const onMouseMoveMax = (e) => { | |
const draggedWidth = (e.clientX || e.touches[0]?.pageX) - offsetSliderWidth | |
const draggedWidthInPercent = Math.min((draggedWidth * 100) / sliderWidth, 100) | |
const activeMax = Math.min(roundToNearestHundredThousand((max * draggedWidthInPercent) / 100), max) | |
if (activeMax >= currentMin + minValueBetween && maxValue.current) { | |
maxValue.current.style.width = draggedWidthInPercent + '%' | |
maxValue.current.dataset.content = activeMax | |
setCurrentMax(activeMax) | |
} | |
} | |
const onMouseUpMax = () => { | |
document.removeEventListener('mouseup', onMouseUpMax) | |
document.removeEventListener('mousemove', onMouseMoveMax) | |
document.removeEventListener('touchend', onMouseUpMax) | |
document.removeEventListener('touchmove', onMouseMoveMax) | |
setDragEnded(true) | |
} | |
return ( | |
<SliderWrapper> | |
<SliderValues> | |
<span>{formatLabels(currentMin)}</span> | |
<span>{formatLabels(currentMax)}</span> | |
</SliderValues> | |
<Slider ref={slider}> | |
<div ref={minValue} data-content={currentMin}> | |
<div ref={minValueDrag} onMouseDown={changeMinValue} onTouchStart={changeMinValue}></div> | |
</div> | |
<div ref={maxValue} data-content={currentMax}> | |
<div ref={maxValueDrag} onMouseDown={changeMaxValue} onTouchStart={changeMaxValue}></div> | |
</div> | |
</Slider> | |
</SliderWrapper> | |
) | |
} | |
export default DoubleRangeSlider |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment