UNPKG

@chayns-components/core

Version:

A set of beautiful React components for developing your own applications with chayns.

319 lines (317 loc) • 10.2 kB
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