UNPKG

@chayns-components/core

Version:

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

397 lines (391 loc) • 17.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _chaynsApi = require("chayns-api"); var _react = _interopRequireWildcard(require("react")); var _styledComponents = require("styled-components"); var _element = require("../../hooks/element"); var _useKeyboardFocusHighlighting = require("../../hooks/useKeyboardFocusHighlighting"); var _slider = require("../../utils/slider"); var _Slider = require("./Slider.styles"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const Slider = ({ interval, isDisabled, maxEnabledValue, maxValue, minEnabledValue, minValue, onChange, onSelect, shouldHighlightSteps = false, shouldEnableKeyboardHighlighting, shouldShowThumbLabel = false, step = 1, thumbLabelFormatter, value }) => { const [fromValue, setFromValue] = (0, _react.useState)(minEnabledValue ?? minValue); const [toValue, setToValue] = (0, _react.useState)(maxEnabledValue ?? maxValue); const [thumbWidth, setThumbWidth] = (0, _react.useState)(20); const [isBigSlider, setIsBigSlider] = (0, _react.useState)(false); const [isFromThumbFocused, setIsFromThumbFocused] = (0, _react.useState)(false); const [isToThumbFocused, setIsToThumbFocused] = (0, _react.useState)(false); const previousFromValueRef = (0, _react.useRef)(fromValue); const previousToValueRef = (0, _react.useRef)(toValue); const fromSliderRef = (0, _react.useRef)(null); const toSliderRef = (0, _react.useRef)(null); const fromSliderThumbRef = (0, _react.useRef)(null); const toSliderThumbRef = (0, _react.useRef)(null); const fromSliderThumbContentRef = (0, _react.useRef)(null); const toSliderThumbContentRef = (0, _react.useRef)(null); const sliderWrapperRef = (0, _react.useRef)(null); const sliderWrapperSize = (0, _element.useElementSize)(sliderWrapperRef); const theme = (0, _styledComponents.useTheme)(); const shouldShowKeyboardHighlighting = (0, _useKeyboardFocusHighlighting.useKeyboardFocusHighlighting)(shouldEnableKeyboardHighlighting && !isDisabled); const updateFromValue = (0, _react.useCallback)(nextValue => { previousFromValueRef.current = nextValue; setFromValue(nextValue); }, []); const updateToValue = (0, _react.useCallback)(nextValue => { previousToValueRef.current = nextValue; setToValue(nextValue); }, []); const normalizeIntervalValue = (0, _react.useCallback)(nextValue => { let newValue = Number(nextValue); if (newValue > maxValue || newValue > maxValue - maxValue % step) { newValue = maxValue; } else if (newValue < minValue) { newValue = minValue; } else { newValue = Math.round(newValue / step) * step; } return newValue; }, [maxValue, minValue, step]); const applyIntervalThumbChange = (0, _react.useCallback)((thumb, rawValue) => { if (!fromSliderRef.current || !toSliderRef.current) { return; } const newValue = normalizeIntervalValue(rawValue); const isFromThumb = thumb === 'from'; const previousValue = isFromThumb ? previousFromValueRef.current : previousToValueRef.current; const hasChanged = newValue !== previousValue; if (isFromThumb) { updateFromValue(newValue); } else { updateToValue(newValue); } const from = Number(fromSliderRef.current.value); const to = Number(toSliderRef.current.value); if (hasChanged && typeof onChange === 'function') { onChange(undefined, { minValue: isFromThumb ? newValue : from, maxValue: isFromThumb ? to : newValue }); } (0, _slider.fillSlider)({ toSlider: toSliderRef.current, fromSlider: fromSliderRef.current, ...(isFromThumb ? { fromValue: newValue } : { toValue: newValue }), theme }); if (isFromThumb) { fromSliderRef.current.value = String(newValue > to ? to : newValue); } else { toSliderRef.current.value = String(from <= newValue ? newValue : from); } }, [normalizeIntervalValue, onChange, theme, updateFromValue, updateToValue]); (0, _react.useEffect)(() => { if (shouldShowThumbLabel) { setThumbWidth((0, _slider.getThumbMaxWidth)({ maxNumber: maxValue, thumbLabelFormatter })); } }, [maxValue, shouldShowThumbLabel, thumbLabelFormatter]); /** * This function sets the value */ (0, _react.useEffect)(() => { if (typeof value === 'number' && value >= minValue && value <= maxValue && (typeof minEnabledValue !== 'number' || value >= minEnabledValue) && (typeof maxEnabledValue !== 'number' || value <= maxEnabledValue)) { updateFromValue(value); } }, [maxEnabledValue, maxValue, minEnabledValue, minValue, updateFromValue, value]); (0, _react.useEffect)(() => { if (fromValue > toValue) { updateFromValue(toValue); } if (toValue < fromValue) { updateToValue(fromValue); } }, [fromValue, toValue, updateFromValue, updateToValue]); const handleMouseUp = (0, _react.useCallback)(() => { var _fromSliderRef$curren, _toSliderRef$current; if (isDisabled) { return; } const from = Number((_fromSliderRef$curren = fromSliderRef.current) === null || _fromSliderRef$curren === void 0 ? void 0 : _fromSliderRef$curren.value); const to = Number((_toSliderRef$current = toSliderRef.current) === null || _toSliderRef$current === void 0 ? void 0 : _toSliderRef$current.value); if (typeof onSelect === 'function') { onSelect(interval ? undefined : from, interval ? { maxValue: to, minValue: from } : undefined); } }, [interval, isDisabled, onSelect]); const handleControlFromSlider = (0, _react.useCallback)(event => { applyIntervalThumbChange('from', Number(event.target.value)); }, [applyIntervalThumbChange]); const handleControlToSlider = (0, _react.useCallback)(event => { if (isDisabled) { return; } applyIntervalThumbChange('to', Number(event.target.value)); }, [applyIntervalThumbChange, isDisabled]); (0, _react.useEffect)(() => { if (!fromSliderRef.current || !toSliderRef.current || !interval) { return; } updateFromValue(interval.minValue); updateToValue(interval.maxValue); fromSliderRef.current.value = String(interval.minValue); toSliderRef.current.value = String(interval.maxValue); (0, _slider.fillSlider)({ fromSlider: fromSliderRef.current, toSlider: toSliderRef.current, theme }); // Note: An 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 = (0, _react.useCallback)(event => { if (isDisabled) { return; } // If interval mode is active, delegate to the "from" handler and return early if (interval) { handleControlFromSlider(event); return; } // Respect optionally enabled bounds in addition to absolute min/max const effectiveMin = typeof minEnabledValue === 'number' ? Math.max(minValue, minEnabledValue) : minValue; const effectiveMax = typeof maxEnabledValue === 'number' ? Math.min(maxValue, maxEnabledValue) : maxValue; let newValue = Number(event.target.value); // Clamp to effective range first if (Number.isNaN(newValue)) { newValue = effectiveMin; } if (newValue < effectiveMin) newValue = effectiveMin;else if (newValue > effectiveMax - effectiveMax % step) newValue = effectiveMax;else newValue = Math.round(newValue / step) * step; if (typeof onChange === 'function' && newValue !== previousFromValueRef.current) { onChange(newValue); } updateFromValue(newValue); }, [handleControlFromSlider, interval, isDisabled, maxEnabledValue, maxValue, minEnabledValue, minValue, onChange, step, updateFromValue]); const fromSliderThumbPosition = (0, _react.useMemo)(() => { var _fromSliderRef$curren2; if (typeof ((_fromSliderRef$curren2 = fromSliderRef.current) === null || _fromSliderRef$curren2 === void 0 ? void 0 : _fromSliderRef$curren2.offsetWidth) === 'number' && typeof (sliderWrapperSize === null || sliderWrapperSize === void 0 ? void 0 : sliderWrapperSize.width) === 'number') { return (0, _slider.calculateGradientOffset)({ maxValue, minValue, sliderWidth: fromSliderRef.current.offsetWidth, thumbWidth: 20, value: fromValue, wrapperWidth: sliderWrapperSize.width }); } return 0; }, [fromValue, maxValue, minValue, sliderWrapperSize === null || sliderWrapperSize === void 0 ? void 0 : sliderWrapperSize.width]); const toSliderThumbPosition = (0, _react.useMemo)(() => { var _toSliderRef$current2; if (typeof ((_toSliderRef$current2 = toSliderRef.current) === null || _toSliderRef$current2 === void 0 ? void 0 : _toSliderRef$current2.offsetWidth) === 'number' && typeof (sliderWrapperSize === null || sliderWrapperSize === void 0 ? void 0 : sliderWrapperSize.width) === 'number') { return (0, _slider.calculateGradientOffset)({ maxValue, minValue, sliderWidth: toSliderRef.current.offsetWidth, thumbWidth: 20, value: toValue, wrapperWidth: sliderWrapperSize.width }); } return 0; }, [maxValue, minValue, sliderWrapperSize === null || sliderWrapperSize === void 0 ? void 0 : sliderWrapperSize.width, toValue]); const toSliderThumbContentPosition = (0, _react.useMemo)(() => (0, _slider.calculatePopupPosition)({ min: minValue, max: maxValue, sliderValue: toValue, popupWidth: thumbWidth }), [maxValue, minValue, thumbWidth, toValue]); const fromSliderThumbContentPosition = (0, _react.useMemo)(() => (0, _slider.calculatePopupPosition)({ min: minValue, max: maxValue, sliderValue: fromValue, popupWidth: thumbWidth }), [fromValue, maxValue, minValue, thumbWidth]); const handleTouchStart = (0, _react.useCallback)(() => { if (isDisabled) { return; } void (0, _chaynsApi.setRefreshScrollEnabled)(false); if (shouldShowThumbLabel) { setIsBigSlider(true); } }, [isDisabled, shouldShowThumbLabel]); const handleTouchEnd = (0, _react.useCallback)(() => { var _fromSliderRef$curren3, _toSliderRef$current3; if (isDisabled) { return; } void (0, _chaynsApi.setRefreshScrollEnabled)(true); const from = Number((_fromSliderRef$curren3 = fromSliderRef.current) === null || _fromSliderRef$curren3 === void 0 ? void 0 : _fromSliderRef$curren3.value); const to = Number((_toSliderRef$current3 = toSliderRef.current) === null || _toSliderRef$current3 === void 0 ? void 0 : _toSliderRef$current3.value); if (typeof onSelect === 'function') { onSelect(interval ? undefined : from, interval ? { maxValue: to, minValue: from } : undefined); } if (shouldShowThumbLabel) { setIsBigSlider(false); } }, [interval, isDisabled, onSelect, shouldShowThumbLabel]); const highlightedStepElements = (0, _react.useMemo)(() => { var _fromSliderRef$curren4; const sliderWidth = ((_fromSliderRef$curren4 = fromSliderRef.current) === null || _fromSliderRef$curren4 === void 0 ? void 0 : _fromSliderRef$curren4.offsetWidth) ?? 0; const wrapperWidth = (sliderWrapperSize === null || sliderWrapperSize === void 0 ? void 0 : sliderWrapperSize.width) ?? 0; if (!shouldHighlightSteps || interval || sliderWidth === 0 || wrapperWidth === 0) { return null; } const elements = []; for (let i = minValue; i <= maxValue; i += step) { const isStepDisabled = typeof minEnabledValue === 'number' && i < minEnabledValue || typeof maxEnabledValue === 'number' && i > maxEnabledValue; const offset = (wrapperWidth - sliderWidth) / 2; const stepWidth = sliderWidth / (maxValue - minValue) * step; elements.push(/*#__PURE__*/_react.default.createElement(_Slider.StyledHighlightedStep, { key: `step--${i}`, $isDisabled: isStepDisabled, $isFilled: i < fromValue, $leftPosition: offset + stepWidth * i })); } return elements; }, [fromValue, interval, maxEnabledValue, maxValue, minEnabledValue, minValue, shouldHighlightSteps, sliderWrapperSize === null || sliderWrapperSize === void 0 ? void 0 : sliderWrapperSize.width, step]); const fromInputBackground = (0, _react.useMemo)(() => { if (interval) return undefined; const gradientPoints = []; const getPercentage = x => (x - minValue) / (maxValue - minValue) * 100; if (typeof minEnabledValue === 'number') { gradientPoints.push('rgb(215, 215, 215) 0%'); gradientPoints.push(`rgb(215, 215, 215) ${getPercentage(minEnabledValue)}%`); gradientPoints.push(`${theme['409'] ?? ''} ${getPercentage(minEnabledValue)}%`); } else { gradientPoints.push(`${theme['409'] ?? ''} 0%`); } gradientPoints.push(`${theme['409'] ?? ''} ${getPercentage(fromValue)}%`); gradientPoints.push(`${theme['403'] ?? ''} ${getPercentage(fromValue)}%`); if (typeof maxEnabledValue === 'number') { gradientPoints.push(`${theme['403'] ?? ''} ${getPercentage(maxEnabledValue)}%`); gradientPoints.push(`rgb(215, 215, 215) ${getPercentage(maxEnabledValue)}%`); gradientPoints.push('rgb(215, 215, 215) 100%'); } else { gradientPoints.push(`${theme['403'] ?? ''} 100%`); } return `linear-gradient(to right, ${gradientPoints.join(', ')})`; }, [fromValue, interval, maxEnabledValue, maxValue, minEnabledValue, minValue, theme]); return (0, _react.useMemo)(() => /*#__PURE__*/_react.default.createElement(_Slider.StyledSlider, { ref: sliderWrapperRef, $isDisabled: isDisabled }, highlightedStepElements, /*#__PURE__*/_react.default.createElement(_Slider.StyledSliderInput, { animate: { height: isBigSlider ? 30 : 10 }, initial: { height: 10 }, exit: { height: 10 }, $thumbWidth: 40, ref: fromSliderRef, $isInterval: !!interval, type: "range", value: fromValue, step: step, max: maxValue, min: minValue, onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, onChange: handleInputChange, onMouseUp: handleMouseUp, $background: fromInputBackground, $shouldShowKeyboardHighlighting: shouldShowKeyboardHighlighting, onFocus: () => { setIsFromThumbFocused(true); setIsToThumbFocused(false); }, onBlur: () => { setIsFromThumbFocused(false); } }), /*#__PURE__*/_react.default.createElement(_Slider.StyledSliderThumb, { ref: fromSliderThumbRef, $position: fromSliderThumbPosition, $isBigSlider: isBigSlider, $shouldShowFocusRing: shouldShowKeyboardHighlighting && isFromThumbFocused }, shouldShowThumbLabel && /*#__PURE__*/_react.default.createElement(_Slider.StyledSliderThumbLabel, { $width: thumbWidth, $isBigSlider: isBigSlider, $position: fromSliderThumbContentPosition, ref: fromSliderThumbContentRef }, typeof thumbLabelFormatter === 'function' ? thumbLabelFormatter(fromValue) : fromValue)), interval && /*#__PURE__*/_react.default.createElement(_Slider.StyledSliderThumb, { ref: toSliderThumbRef, $position: toSliderThumbPosition, $isBigSlider: isBigSlider, $shouldShowFocusRing: shouldShowKeyboardHighlighting && isToThumbFocused }, shouldShowThumbLabel && /*#__PURE__*/_react.default.createElement(_Slider.StyledSliderThumbLabel, { $width: thumbWidth, $isBigSlider: isBigSlider, $position: toSliderThumbContentPosition, ref: toSliderThumbContentRef }, typeof thumbLabelFormatter === 'function' ? thumbLabelFormatter(toValue) : toValue)), interval && /*#__PURE__*/_react.default.createElement(_Slider.StyledSliderInput, { animate: { height: isBigSlider ? 30 : 10 }, initial: { height: 10 }, exit: { height: 10 }, $thumbWidth: 40, ref: toSliderRef, $isInterval: !!interval, type: "range", value: toValue, step: step, max: maxValue, min: minValue, onTouchStart: handleTouchStart, onTouchEnd: handleTouchEnd, onChange: handleControlToSlider, onMouseUp: handleMouseUp, $shouldShowKeyboardHighlighting: shouldShowKeyboardHighlighting, onFocus: () => { setIsToThumbFocused(true); setIsFromThumbFocused(false); }, onBlur: () => { setIsToThumbFocused(false); } })), [fromInputBackground, fromSliderThumbContentPosition, fromSliderThumbPosition, fromValue, handleControlToSlider, handleInputChange, handleMouseUp, handleTouchEnd, handleTouchStart, highlightedStepElements, interval, isBigSlider, isDisabled, isFromThumbFocused, isToThumbFocused, maxValue, minValue, shouldShowThumbLabel, shouldShowKeyboardHighlighting, thumbLabelFormatter, thumbWidth, toSliderThumbContentPosition, toSliderThumbPosition, toValue]); }; Slider.displayName = 'Slider'; var _default = exports.default = Slider; //# sourceMappingURL=Slider.js.map