@chayns-components/core
Version:
A set of beautiful React components for developing your own applications with chayns.
319 lines (317 loc) ⢠10.2 kB
JavaScript
import { setRefreshScrollEnabled } from 'chayns-api';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTheme } from 'styled-components';
import { useElementSize } from '../../hooks/element';
import { calculateGradientOffset, calculatePopupPosition, fillSlider, getThumbMaxWidth } from '../../utils/slider';
import { StyledSlider, StyledSliderInput, StyledSliderThumb, StyledSliderThumbLabel } from './Slider.styles';
const Slider = _ref => {
let {
maxValue,
minValue,
value,
onSelect,
onChange,
interval,
isDisabled,
thumbLabelFormatter,
shouldShowThumbLabel = false,
steps = 1
} = _ref;
const [fromValue, setFromValue] = useState(0);
const [toValue, setToValue] = useState(maxValue);
const [thumbWidth, setThumbWidth] = useState(20);
const [isBigSlider, setIsBigSlider] = useState(false);
const fromSliderRef = useRef(null);
const toSliderRef = useRef(null);
const fromSliderThumbRef = useRef(null);
const toSliderThumbRef = useRef(null);
const fromSliderThumbContentRef = useRef(null);
const toSliderThumbContentRef = useRef(null);
const sliderWrapperRef = useRef(null);
const sliderWrapperSize = useElementSize(sliderWrapperRef);
const theme = useTheme();
useEffect(() => {
if (shouldShowThumbLabel) {
setThumbWidth(getThumbMaxWidth({
maxNumber: maxValue,
thumbLabelFormatter
}));
}
}, [maxValue, shouldShowThumbLabel, thumbLabelFormatter]);
/**
* This function sets the value
*/
useEffect(() => {
if (typeof value !== 'number') {
return;
}
if (value >= minValue && value <= maxValue) {
setFromValue(value);
}
}, [maxValue, minValue, value]);
useEffect(() => {
if (fromValue > toValue) {
setFromValue(toValue);
}
if (toValue < fromValue) {
setToValue(fromValue);
}
}, [fromValue, toValue]);
const handleMouseUp = useCallback(() => {
if (isDisabled) {
return;
}
const from = Number(fromSliderRef.current?.value);
const to = Number(toSliderRef.current?.value);
if (typeof onSelect === 'function') {
onSelect(interval ? undefined : from, interval ? {
maxValue: to,
minValue: from
} : undefined);
}
}, [interval, isDisabled, onSelect]);
const handleControlFromSlider = useCallback(event => {
if (!fromSliderRef.current || !toSliderRef.current) {
return;
}
let newValue = Number(event.target.value);
if (newValue > maxValue || newValue > maxValue - maxValue % steps) {
newValue = maxValue;
} else if (newValue < minValue) {
newValue = minValue;
} else {
newValue = Math.round(newValue / steps) * steps;
}
setFromValue(newValue);
const to = Number(toSliderRef.current.value);
if (typeof onChange === 'function') {
onChange(undefined, {
maxValue: to,
minValue: newValue
});
}
fillSlider({
toSlider: toSliderRef.current,
fromSlider: fromSliderRef.current,
fromValue: newValue,
theme
});
if (newValue > to) {
fromSliderRef.current.value = String(to);
} else {
fromSliderRef.current.value = String(newValue);
}
}, [maxValue, minValue, onChange, steps, theme]);
const handleControlToSlider = useCallback(event => {
if (isDisabled) {
return;
}
if (!fromSliderRef.current || !toSliderRef.current) {
return;
}
let newValue = Number(event.target.value);
if (newValue > maxValue || newValue > maxValue - maxValue % steps) {
newValue = maxValue;
} else if (newValue < minValue) {
newValue = minValue;
} else {
newValue = Math.round(newValue / steps) * steps;
}
setToValue(newValue);
const from = Number(fromSliderRef.current.value);
if (typeof onChange === 'function') {
onChange(undefined, {
maxValue: newValue,
minValue: from
});
}
fillSlider({
toSlider: toSliderRef.current,
fromSlider: fromSliderRef.current,
toValue: newValue,
theme
});
if (from <= newValue) {
toSliderRef.current.value = String(newValue);
} else {
toSliderRef.current.value = String(from);
}
}, [isDisabled, maxValue, minValue, onChange, steps, theme]);
useEffect(() => {
if (!fromSliderRef.current || !toSliderRef.current || !interval) {
return;
}
setFromValue(interval.minValue);
setToValue(interval.maxValue);
fromSliderRef.current.value = String(interval.minValue);
toSliderRef.current.value = String(interval.maxValue);
fillSlider({
fromSlider: fromSliderRef.current,
toSlider: toSliderRef.current,
theme
});
// Note: interval can“t be in the deps because of rerender
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [theme]);
/**
* This function updates the value
*/
const handleInputChange = useCallback(event => {
if (isDisabled) {
return;
}
let newValue = Number(event.target.value);
if (newValue > maxValue || newValue > maxValue - maxValue % steps) {
newValue = maxValue;
} else if (newValue < minValue) {
newValue = minValue;
} else {
newValue = Math.round(newValue / steps) * steps;
}
if (interval) {
handleControlFromSlider(event);
return;
}
setFromValue(newValue);
if (onChange) {
onChange(newValue);
}
}, [handleControlFromSlider, interval, isDisabled, maxValue, minValue, onChange, steps]);
const fromSliderThumbPosition = useMemo(() => {
if (fromSliderRef.current && fromSliderThumbRef.current && sliderWrapperSize) {
return calculateGradientOffset({
max: maxValue,
min: minValue,
value: fromValue,
thumbWidth: 20,
containerWidth: fromSliderRef.current.offsetWidth
});
}
return 0;
}, [fromValue, maxValue, minValue, sliderWrapperSize]);
const toSliderThumbPosition = useMemo(() => {
if (toSliderRef.current && toSliderThumbRef.current && sliderWrapperSize) {
return calculateGradientOffset({
max: maxValue,
min: minValue,
value: toValue,
thumbWidth: 20,
containerWidth: toSliderRef.current.offsetWidth
});
}
return 0;
}, [toValue, minValue, maxValue, sliderWrapperSize]);
const toSliderThumbContentPosition = useMemo(() => calculatePopupPosition({
min: minValue,
max: maxValue,
sliderValue: toValue,
popupWidth: thumbWidth
}), [maxValue, minValue, thumbWidth, toValue]);
const fromSliderThumbContentPosition = useMemo(() => calculatePopupPosition({
min: minValue,
max: maxValue,
sliderValue: fromValue,
popupWidth: thumbWidth
}), [fromValue, maxValue, minValue, thumbWidth]);
const handleTouchStart = useCallback(() => {
if (isDisabled) {
return;
}
void setRefreshScrollEnabled(false);
if (shouldShowThumbLabel) {
setIsBigSlider(true);
}
}, [isDisabled, shouldShowThumbLabel]);
const handleTouchEnd = useCallback(() => {
if (isDisabled) {
return;
}
void setRefreshScrollEnabled(true);
const from = Number(fromSliderRef.current?.value);
const to = Number(toSliderRef.current?.value);
if (typeof onSelect === 'function') {
onSelect(interval ? undefined : from, interval ? {
maxValue: to,
minValue: from
} : undefined);
}
if (shouldShowThumbLabel) {
setIsBigSlider(false);
}
}, [interval, isDisabled, onSelect, shouldShowThumbLabel]);
return useMemo(() => /*#__PURE__*/React.createElement(StyledSlider, {
ref: sliderWrapperRef,
$isDisabled: isDisabled
}, /*#__PURE__*/React.createElement(StyledSliderInput, {
animate: {
height: isBigSlider ? 30 : 10
},
initial: {
height: 10
},
exit: {
height: 10
},
$thumbWidth: 40,
ref: fromSliderRef,
$isInterval: !!interval,
type: "range",
value: fromValue,
step: 0.01,
max: maxValue,
min: minValue,
onTouchStart: handleTouchStart,
onTouchEnd: handleTouchEnd,
onChange: handleInputChange,
onMouseUp: handleMouseUp,
$max: maxValue,
$min: minValue,
$value: fromValue
}), /*#__PURE__*/React.createElement(StyledSliderThumb, {
ref: fromSliderThumbRef,
$position: fromSliderThumbPosition,
$isBigSlider: isBigSlider
}, shouldShowThumbLabel && /*#__PURE__*/React.createElement(StyledSliderThumbLabel, {
$width: thumbWidth,
$isBigSlider: isBigSlider,
$position: fromSliderThumbContentPosition,
ref: fromSliderThumbContentRef
}, typeof thumbLabelFormatter === 'function' ? thumbLabelFormatter(fromValue) : fromValue)), interval && /*#__PURE__*/React.createElement(StyledSliderThumb, {
ref: toSliderThumbRef,
$position: toSliderThumbPosition,
$isBigSlider: isBigSlider
}, shouldShowThumbLabel && /*#__PURE__*/React.createElement(StyledSliderThumbLabel, {
$width: thumbWidth,
$isBigSlider: isBigSlider,
$position: toSliderThumbContentPosition,
ref: toSliderThumbContentRef
}, typeof thumbLabelFormatter === 'function' ? thumbLabelFormatter(toValue) : toValue)), interval && /*#__PURE__*/React.createElement(StyledSliderInput, {
animate: {
height: isBigSlider ? 30 : 10
},
initial: {
height: 10
},
exit: {
height: 10
},
$thumbWidth: 40,
$max: maxValue,
$min: minValue,
$value: toValue,
ref: toSliderRef,
$isInterval: !!interval,
type: "range",
value: toValue,
step: 0.01,
max: maxValue,
min: minValue,
onTouchStart: handleTouchStart,
onTouchEnd: handleTouchEnd,
onChange: handleControlToSlider,
onMouseUp: handleMouseUp
})), [isDisabled, isBigSlider, interval, fromValue, maxValue, minValue, handleTouchStart, handleTouchEnd, handleInputChange, handleMouseUp, fromSliderThumbPosition, shouldShowThumbLabel, thumbWidth, fromSliderThumbContentPosition, thumbLabelFormatter, toSliderThumbPosition, toSliderThumbContentPosition, toValue, handleControlToSlider]);
};
Slider.displayName = 'Slider';
export default Slider;
//# sourceMappingURL=Slider.js.map