react-ranger
Hooks for building range and multi-range sliders in React.
Quick Features
- Headless!
- Single or Multiple Handles
- Handle Devider Items
- Custom Steps or Step-Size
- Custom Ticks
Examples
- Basic - CodeSandbox - Source
- Multi-Range - CodeSandbox - Source
- Custom Steps - CodeSandbox - Source
- Custom Styles - CodeSandbox - Source
- Update On Drag - CodeSandbox - Source
Installation
$ npm i --save react-ranger
# or
$ yarn add react-ranger
Sample Usage
The following is a very basic example of a single range input that looks similar to Chrome's default appearance.
import ReactRanger from 'react-ranger'
function App() {
const [values, setValues] = React.useState([10])
const { getTrackProps, handles } = useRanger({
values,
onChange: setValues,
min: 0,
max: 100,
stepSize: 5,
})
return (
<>
<div
{...getTrackProps({
style: {
height: '4px',
background: '#ddd',
boxShadow: 'inset 0 1px 2px rgba(0,0,0,.6)',
borderRadius: '2px',
},
})}
>
{handles.map(({ getHandleProps }) => (
<div
{...getHandleProps({
style: {
width: '12px',
height: '12px',
borderRadius: '100%',
background: 'linear-gradient(to bottom, #eee 45%, #ddd 55%)',
border: 'solid 1px #888',
},
})}
/>
))}
</div>
</>
)
}
Options
value: Array<number>
- The current value (or values) for the range- Required
min: number
- The minimum limit for the range- Required
max: number
- The maximum limit for the range- Required
stepSize: number
- The distance between selectable steps- Required
steps: arrayOf(number)
- An array of custom steps to use. This will overridestepSize
,tickSize: number
ticks: arrayOf(number): Default: 10
- An array of custom ticks to use. This will overridetickSize
,onChange: Function(newValue)
- A function that is called when the handle is releasedonDrag: Function(newValue)
- A function that is called when a handled is draggedinterpolator: { getPercentageForValue: Function(value) => decimal, getValueForClientX: Function(x) => value}
- The Interpolator to use
- Defualts to the bundled linear-scale interpolator
See the Interpolation section for more info
Returns
useRanger
returns an object
with the following properties:
getTrackProps(userProps): func
- A function that takes optional props and returns the combined necessary props for the track component.ticks: array
- Ticks to be rendered. Eachtick
has the following props:value: number
- The tick number to be displayedgetTickProps(userProps): func
- A function that take optional props and returns the combined necessary props for the tick component.
segments: array
- Segments to be rendered. Eachsegment
has the following props:value: number
- The segments ending valuegetSegmentProps(userProps): func
- A function that take optional props and returns the combined necessary props for the segment component.
handles: array
- Handles to be rendered. Eachhandle
has the following props:value: number
- The current value for the handleactive: boolean
- Denotes if the handle is currently being dragged.getHandleProps(userProps): func
- A function that take optional props and returns the combined necessary props for the handle component.
activeHandleIndex: oneOfType([null, number])
- The zero-based index of the handle that is currently being dragged, ornull
if no handle is being dragged.
Interpolation
By default, react-ranger
uses linear interpolation between data points, but allows you to easily customize it to use your own interpolation functions by passing an object that implements the following interface:
const interpolator = {
// Takes the value & range and returns a percentage [0, 100] where the value sits from left to right
getPercentageForValue: (val: number, min: number, max: number): number
// Takes the clientX (offset from the left edge of the ranger) along with the dimensions
// and range settings and transforms a pixel coordinate back into a value
getValueForClientX: (clientX: number, trackDims: object, min: number, max: number): number
}
Here is an exmaple of building and using a logarithmic interpolator!
import { useRanger } from 'react-ranger'
const logInterpolator = {
getPercentageForValue: (val, min, max) => {
const minSign = Math.sign(min)
const maxSign = Math.sign(max)
if (minSign !== maxSign) {
throw new Error(
'Error: logarithmic interpolation does not support ranges that cross 0.'
)
}
let percent =
(100 / (Math.log10(Math.abs(max)) - Math.log10(Math.abs(min)))) *
(Math.log10(Math.abs(val)) - Math.log10(Math.abs(min)))
if (minSign < 0) {
// negative range, means we need to invert our percent because of the Math.abs above
return 100 - percent
}
return percent
},
getValueForClientX: (clientX, trackDims, min, max) => {
const { left, width } = trackDims
let value = clientX - left
value *= Math.log10(max) - Math.log10(min)
value /= width
value = Math.pow(10, Math.log10(min) + value)
return value
},
}
useRanger({
interpolator: logInterpolator,
})