Are you tired of using boring, ordinary range sliders to filter through your favorite ski resorts? Well, look no further because we have the solution for you: the Double Range Slider Component with Rheostat Graph.
With this state-of-the-art slider, not only can you filter through your resorts by score, but you can also visually track your selected range with a fancy Rheostat graph. And if that wasn't enough, we've even added a little bit of magic to make sure the sliders always have a minimum distance of 8 between them. No more pesky overlapping sliders!
First, let's start by importing all the necessary libraries and components:
import React, { useState } from 'react';
import { CFormRange, CFormInput, CFormLabel, CCol, CRow } from '@coreui/react';
import PropTypes from 'prop-types';
import RangeRheostatGraph from '../Rheostat/RangeRheostatGraph';
Next, we'll use the useState hook to set the initial values for the lower and upper range sliders and their corresponding input fields:
const [lowerValue, setLowerValue] = useState(50);
const [upperValue, setUpperValue] = useState(500);
const [lowerFieldValue, setLowerFieldValue] = useState(50);
const [upperFieldValue, setUpperFieldValue] = useState(500);
Then, we have some clever math to calculate the width and position of the range between the sliders:
const widthSpanPercent = 100 / (sliderMax - sliderMin);
const leftPosition = (lowerValue - sliderMin) * widthSpanPercent;
const rightPosition = (upperValue - sliderMin) * widthSpanPercent;
const rangeWidth = (rightPosition + sliderMin) - (leftPosition + sliderMin);
Now comes the fun part: the event handlers! We have two functions to handle the change of each slider and make sure the other slider adjusts accordingly:
function setLowerSlider(targetUpperValue) {
if ((lowerValue > targetUpperValue - 8) || (targetUpperValue < lowerValue + 8)) {
if (targetUpperValue >= sliderMax) {
setLowerValue(parseInt(sliderMax, 10) - 8);
setLowerFieldValue(parseInt(sliderMax, 10) - 8);
}
setLowerValue(Math.max(targetUpperValue - 8, sliderMin));
setLowerFieldValue(Math.max(targetUpperValue - 8, sliderMin));
if (targetUpperValue <= sliderMin + 8) {
setUpperValue(sliderMin + 8);
setUpperFieldValue(sliderMin + 8);
}
}
}
function setUpperSlider(targetLowerValue) {
if ((targetLowerValue > upperValue - 8) || (upperValue < targetLowerValue + 8)) {
if (targetLowerValue < sliderMax - 8) {
setUpperValue(targetLowerValue + 8);
setUpperFieldValue(targetLowerValue + 8);
}
setUpperValue(Math.min(targetLowerValue + 8, sliderMax));
setUpperFieldValue(Math.min(targetLowerValue + 8, sliderMax));
if (targetLowerValue >= sliderMax - 8) {
setLowerValue(sliderMax - 8);
setLowerFieldValue(sliderMax - 8);
}
}
}
We also have a function to limit the input values to within the specified range:
function limitInput(target) {
if (target > sliderMax) {
return parseInt(sliderMax, 10);
} if (target < sliderMin) {
return parseInt(sliderMin, 10);
}
return parseInt(target, 10);
}
Now, let's create the event handlers for when the user changes the values of the range sliders:
function handleLowerRangeSliderChange(target) {
setLowerValue(limitInput(target));
setLowerFieldValue(limitInput(target));
setUpperSlider(parseInt(target, 10));
}
function handleRangeSliderUpperChange(target) {
setUpperValue(limitInput(target));
setUpperFieldValue(limitInput(target));
setLowerSlider(parseInt(target, 10));
}
Finally, we can render the Double Range Slider Component with Rheostat Graph and pass in the necessary props:
return (
<div className={`double-range-slider ${className}`}>
{useGraph
&& (
<RangeRheostatGraph
scoreType={scoreType}
resortList={resortList}
leftPosition={leftPosition}
rightPosition={rightPosition}
sliderMin={sliderMin}
sliderMax={sliderMax}
upperValue={upperValue}
lowerValue={lowerValue}
graphTickerQuantity={tickerQuantity}
/>
)}
<div className="range-slider-wrap">
<div className="range-slider-range" style={{ left: `${leftPosition.toString()}%`, width: `${rangeWidth.toString()}%` }} />
</div>
<CFormRange
id="lower"
className="range-slider range-slider-lower"
steps={1}
min={sliderMin}
max={sliderMax}
onChange={(e) => handleLowerRangeSliderChange(e.target.value)}
value={lowerValue}
/>
<CFormRange
id="upper"
className="range-slider range-slider-upper"
steps={1}
min={sliderMin}
max={sliderMax}
onChange={(e) => handleRangeSliderUpperChange(e.target.value)}
value={upperValue}
/>
<CRow className="mt-5 d-flex justify-content-between p-3">
<CCol xl={5} lg={5} xs={5}>
<div className="input-wrap">
<CFormLabel htmlFor="lowerInput" className="w-100 label-inside-input resort-card__small-label">
Min
</CFormLabel>
<CFormInput
className="lower-input label-inside-input-padding"
type="number"
id="lowerInput"
label="Min"
placeholder="0"
min={sliderMin}
max={sliderMax}
onBlur={
(e) => handleLowerRangeSliderChange(e.target.value)
}
onChange={(e) => setLowerFieldValue(parseInt(e.target.value, 10))}
value={lowerFieldValue}
/>
</div>
</CCol>
<CCol xl={2} lg={2} xs={2} className="d-flex justify-content-center align-items-center">
-
</CCol>
<CCol xl={5} lg={5} xs={5}>
<div className="input-wrap">
<CFormLabel htmlFor="upperInput" className="w-100 label-inside-input resort-card__small-label">
Max
</CFormLabel>
<CFormInput
className="upper-input label-inside-input-padding"
type="number"
id="upperInput"
label="Max"
placeholder="0"
min={sliderMin}
max={sliderMax}
onBlur={
(e) => handleRangeSliderUpperChange(e.target.value)
}
onChange={(e) => setUpperFieldValue(parseInt(e.target.value, 10))}
value={upperFieldValue}
/>
</div>
</CCol>
</CRow>
</div>
);
The Rheostat graph is an optional feature of the Double Range Slider Component that allows you to visually track the selected range of values. Here's how to build it:
First, let's import all the necessary libraries and components:
import React from 'react';
import PropTypes from 'prop-types';
import { CSvg, CRect, CText } from '@coreui/react';
Here are the props that can be passed to the Double Range Slider Component with Rheostat Graph:
Then, we'll need to calculate the positions of the tick marks on the Rheostat graph:
resortList.forEach((resort) => {
let avg = 0;
resort.ratings.filter((rating) => rating.title === scoreType).forEach((score) => {
avg += score.value;
});
avg /= resort.ratings.filter((rating) => rating.title === scoreType).length;
resortAverages.push(avg);
});
// Get maxResortInSpan:
// Finds the most amount of resorts in a single ticker, so we can utilize full height.
tickerQuantity.forEach((tick) => {
const spanLow = spanPercent * tick;
const spanHigh = spanPercent * tick + spanPercent;
if (resortAverages.some((el) => el >= spanLow && el < spanHigh)) {
let resortQty = 0;
resortAverages.forEach((avg) => {
if (avg >= spanLow && avg <= spanHigh) {
resortQty += 1;
}
if (maxResortInSpan < resortQty) {
setMaxResortInSpan(resortQty);
}
});
}
});
// Set initial tickers
tickerQuantity.forEach((tick) => {
const spanLow = spanPercent * tick;
const spanHigh = spanPercent * tick + spanPercent;
let resortQty = 0;
if (resortAverages.some((el) => el >= spanLow && el < spanHigh)) {
resortQty = 0;
resortAverages.forEach((avg) => {
if (avg >= spanLow && avg <= spanHigh) {
resortQty += 1;
}
});
}
height = (100 / maxResortInSpan) * resortQty;
tickers.push({
key: tick,
height: height > 1 ? height : 5,
backgroundColor: 'light',
});
});
// Change color of graph ticks based on the sliders input.
if (upperValue < sliderMax) {
const tickersArray = tickers;
tickers.forEach((tick) => {
const tickObj = tick;
if ((tick.key * spanPercent) > rightPosition) {
tickObj.backgroundColor = 'dark';
} else {
tickObj.backgroundColor = 'light';
}
if (tick.key !== tickObj.key) {
tickersArray.push(tickObj);
}
});
tickers = tickersArray;
}
if (lowerValue > sliderMin) {
const tickersArray = tickers;
tickers.forEach((tick) => {
const tickObj = tick;
if ((tick.key * spanPercent) < leftPosition) {
tickObj.backgroundColor = 'dark';
} else if ((tick.key * spanPercent) < rightPosition) {
tickObj.backgroundColor = 'light';
}
if (tick.key !== tickObj.key) {
tickersArray.push(tickObj);
}
});
tickers = tickersArray;
}
Now, let's finish rendering the rheostat component
return (
<div className="range-rheostat-graph">
<div className="range-rheostat-graph__ticker-wrap d-flex justify-content-around">
{tickers && tickers.map((ticker) => (
<RheostatTicker
key={ticker.key}
value={ticker.height}
backgroundColor={ticker.backgroundColor}
/>
))}
</div>
</div>
);
That's it! You now know how to build a Double Range Slider Component with Rheostat Graph. This interactive and visually appealing component allows you to easily filter through a list of anything by your type and track your selected range with a fancy Rheostat graph. With just a few lines of code, you can add this impressive feature to your own projects and wow your users. Happy coding!