nudge-components-library
Version:
A library of nudge UI components
99 lines (96 loc) • 6.1 kB
JavaScript
import { jsxs, jsx } from 'react/jsx-runtime';
import React, { useContext, useRef, useState, useLayoutEffect } from 'react';
import { ThemeContext } from '../../theme/ThemeContext.js';
import '../../styles/tokens.css.js';
import '../../styles/globals.css.js';
import styles from './Slider.module.css.js';
function Slider({ sliderLabel, min = 0, max = 100, value, defaultValue, step = 1, showValueTooltip = true, onChange, disabled = false, nudgeText, id, ariaLabel, onFocus, onBlur, onCommit, nudgeVisible = true, nudgePosition = "bottom", renderNudge, renderValueTooltip, tooltipContainerStyle, alwaysShowTooltip, }) {
const theme = useContext(ThemeContext);
const sliderRef = useRef(null);
const containerRef = useRef(null);
const [sliderValue, setSliderValue] = useState(defaultValue ?? min);
const [thumbCenter, setThumbCenter] = useState(0);
const currentValue = value ?? sliderValue;
const percentage = ((currentValue - min) / (max - min)) * 100;
const filledPercentage = Math.min(percentage, 100);
useLayoutEffect(() => {
if (sliderRef.current) {
const sliderWidth = sliderRef.current.offsetWidth;
const thumbWidthStr = theme.slider.thumb.width
? theme.slider.thumb.width.toString()
: "20";
const thumbWidth = parseInt(thumbWidthStr, 7);
const pos = ((sliderWidth - thumbWidth) * (currentValue - min)) / (max - min) +
thumbWidth / 2;
setThumbCenter(pos);
}
}, [currentValue, min, max, theme.slider.thumb.width]);
React.useEffect(() => {
if (value !== undefined) {
setSliderValue(value);
}
}, [value]);
React.useEffect(() => {
if (defaultValue !== undefined) {
const clampedValue = Math.min(Math.max(defaultValue, min), max);
setSliderValue(clampedValue);
}
}, [defaultValue, min, max]);
const handleChange = (event) => {
const newValue = Number(event.target.value);
if (value === undefined) {
setSliderValue(newValue);
}
onChange?.(newValue);
};
const handleBlur = (event) => {
onBlur?.(event);
onCommit?.(Number(event.target.value));
};
const handleMouseUp = (event) => {
onCommit?.(Number(event.currentTarget.value));
};
const handleTouchStart = (e) => {
if (onFocus) {
onFocus(e);
}
};
React.useEffect(() => {
const handleTouchOutside = (event) => {
if (containerRef.current &&
!containerRef.current.contains(event.target)) {
if (onBlur) {
const dummyEvent = {
target: containerRef.current,
};
onBlur(dummyEvent);
}
onCommit?.(currentValue);
}
};
document.addEventListener("touchstart", handleTouchOutside);
return () => document.removeEventListener("touchstart", handleTouchOutside);
}, [onBlur, onCommit, currentValue]);
const nudgeId = id ? `${id}-nudge` : undefined;
const sliderInputElement = (jsxs("div", { style: theme.slider.container, className: styles.sliderContainer, children: [jsx("input", { ref: sliderRef, id: id, type: "range", min: min, max: max, step: step, value: currentValue, onChange: handleChange, onFocus: onFocus, onBlur: handleBlur, onMouseUp: handleMouseUp, onTouchStart: handleTouchStart, "aria-label": ariaLabel ?? sliderLabel, "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": currentValue, "aria-describedby": nudgeVisible ? nudgeId : undefined, style: {
...theme.slider.input,
background: `linear-gradient(to right, ${theme.slider.input.filledColor} 0%, ${theme.slider.input.filledColor} ${filledPercentage}%, ${theme.slider.input.emptyColor} ${filledPercentage}%, ${theme.slider.input.emptyColor} 100%)`,
}, disabled: disabled, className: styles.inputSlider }), jsx("div", { className: styles.customThumb, style: {
...theme.slider.thumb,
left: thumbCenter,
} }), showValueTooltip && (jsx("div", { className: `${styles.sliderTooltip} ${alwaysShowTooltip ? styles.alwaysVisible : ""}`, style: {
left: thumbCenter,
...theme.slider.tooltip,
...tooltipContainerStyle,
"--tooltip-triangle-color": theme.slider.tooltip.triangleColor,
"--tooltip-triangle-width": theme.slider.tooltip.triangleWidth,
}, children: renderValueTooltip ? renderValueTooltip(currentValue) : currentValue }))] }));
const nudgeElement = renderNudge && nudgeVisible ? (jsx("div", { id: nudgeId, style: theme.slider.nudgeText, children: renderNudge(currentValue) })) : nudgeVisible && nudgeText ? (jsx("div", { id: nudgeId, style: theme.slider.nudgeText, children: nudgeText })) : null;
return (jsxs("div", { style: {
...theme.slider.wrapper,
...(disabled ? theme.slider.disabled : {}),
}, className: styles.wrapper, ref: containerRef, children: [sliderLabel && (jsx("div", { style: theme.slider.sliderLabel, children: jsx("label", { htmlFor: id, children: sliderLabel }) })), nudgeVisible && nudgePosition === "top" && (jsx("div", { style: theme.slider.nudgeTop, children: nudgeElement })), nudgeVisible &&
(nudgePosition === "left" || nudgePosition === "right") ? (jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [nudgePosition === "left" && (jsx("div", { style: theme.slider.nudgeLeft, children: nudgeElement })), sliderInputElement, nudgePosition === "right" && (jsx("div", { style: theme.slider.nudgeRight, children: nudgeElement }))] })) : (sliderInputElement), nudgeVisible && nudgePosition === "bottom" && (jsx("div", { style: theme.slider.nudgeBottom, children: nudgeElement }))] }));
}
export { Slider, Slider as default };
//# sourceMappingURL=Slider.js.map