UNPKG

@fseehawer/react-circular-slider

Version:

A customizable circular slider with no dependencies.

905 lines (896 loc) 32.3 kB
"use client"; var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __pow = Math.pow; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; // src/CircularSlider/index.tsx import React4, { useCallback, useEffect as useEffect3, useReducer, useRef as useRef3, forwardRef, useImperativeHandle } from "react"; // src/redux/reducer.ts var reducer = (state, action) => { switch (action.type) { case "init": case "setKnobPosition": case "onMouseDown": case "onMouseUp": case "setInitialKnobPosition": case "updateDimensions": return __spreadValues(__spreadValues({}, state), action.payload); default: throw new Error("Unknown action type"); } }; var reducer_default = reducer; // src/hooks/useEventListener.ts import { useEffect, useRef } from "react"; function useEventListener(eventName, handler) { const savedHandler = useRef(void 0); useEffect(() => { savedHandler.current = handler; }, [handler]); useEffect(() => { if (typeof window !== "undefined" && window.addEventListener) { const eventListener = (event) => { if (savedHandler.current) savedHandler.current(event); }; window.addEventListener(eventName, eventListener, { passive: false }); return () => { window.removeEventListener(eventName, eventListener); }; } }, [eventName]); } var useEventListener_default = useEventListener; // src/hooks/useIsServer.ts import { useEffect as useEffect2, useState } from "react"; var useIsServer = () => { const [isServer, setIsServer] = useState(true); useEffect2(() => { setIsServer(false); }, []); return isServer; }; var useIsServer_default = useIsServer; // src/Knob/index.tsx import React from "react"; var Knob = ({ isDragging, knobPosition, knobColor, knobSize, hideKnob = false, hideKnobRing = false, knobDraggable = true, trackSize, children, onMouseDown }) => { const styles = { knob: { position: "absolute", left: `-${knobSize / 2 - trackSize / 2}px`, top: `-${knobSize / 2 - trackSize / 2}px`, cursor: knobDraggable ? "grab" : "auto", zIndex: 3 }, dragging: { cursor: "grabbing" }, pause: { animationPlayState: "paused" }, animation: { transformOrigin: "50% 50%", animationTimingFunction: "ease-out", animationDuration: "1500ms", animationIterationCount: "infinite", animationName: "pulse" }, hide: { opacity: 0 } }; return /* @__PURE__ */ React.createElement( "div", { style: __spreadValues(__spreadValues(__spreadValues({ transform: `translate(${knobPosition.x}px, ${knobPosition.y}px)` }, styles.knob), isDragging ? styles.dragging : {}), hideKnob ? styles.hide : {}), onMouseDown, onTouchStart: onMouseDown }, /* @__PURE__ */ React.createElement("svg", { width: knobSize, height: knobSize, viewBox: `0 0 ${knobSize} ${knobSize}` }, !hideKnobRing && /* @__PURE__ */ React.createElement( "circle", { style: __spreadValues(__spreadValues({}, styles.animation), isDragging ? styles.pause : {}), fill: knobColor, fillOpacity: 0.2, stroke: "none", cx: knobSize / 2, cy: knobSize / 2, r: knobSize / 2 } ), /* @__PURE__ */ React.createElement( "circle", { fill: knobColor, stroke: "none", cx: knobSize / 2, cy: knobSize / 2, r: knobSize * 2 / 3 / 2 } ), children != null ? children : /* @__PURE__ */ React.createElement("svg", { width: knobSize, height: knobSize, viewBox: "0 0 36 36" }, /* @__PURE__ */ React.createElement("rect", { fill: "#FFFFFF", x: "14", y: "14", width: "8", height: "1" }), /* @__PURE__ */ React.createElement("rect", { fill: "#FFFFFF", x: "14", y: "17", width: "8", height: "1" }), /* @__PURE__ */ React.createElement("rect", { fill: "#FFFFFF", x: "14", y: "20", width: "8", height: "1" }))) ); }; var Knob_default = Knob; // src/Labels/index.tsx import React2 from "react"; var Labels = ({ label, value, labelColor = "#000", labelBottom = false, labelFontSize = "1rem", valueFontSize = "3rem", appendToValue = "", prependToValue = "", verticalOffset = "1.5rem", hideLabelValue = false }) => { const styles = { labels: { position: "absolute", top: "0", left: "0", width: "100%", height: "100%", display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", color: labelColor, userSelect: "none", zIndex: 1 }, codeTop: { verticalAlign: "text-top" }, value: { fontSize: valueFontSize, position: "relative" }, bottomMargin: { marginBottom: `calc(${verticalOffset})` }, appended: { position: "absolute", right: "0", top: "0", transform: "translate(100%, 0)" }, prepended: { position: "absolute", left: "0", top: "0", transform: "translate(-100%, 0)" }, hide: { display: "none" } }; return /* @__PURE__ */ React2.createElement("div", { style: __spreadValues(__spreadValues({}, styles.labels), hideLabelValue ? styles.hide : {}) }, !labelBottom && /* @__PURE__ */ React2.createElement("div", { style: { fontSize: labelFontSize } }, label), /* @__PURE__ */ React2.createElement("div", { style: __spreadValues(__spreadValues({}, styles.value), !labelBottom ? styles.bottomMargin : {}) }, /* @__PURE__ */ React2.createElement("code", { style: styles.codeTop }, /* @__PURE__ */ React2.createElement("span", { style: styles.prepended }, prependToValue), value, /* @__PURE__ */ React2.createElement("span", { style: styles.appended }, appendToValue))), labelBottom && /* @__PURE__ */ React2.createElement("div", { style: { fontSize: labelFontSize, marginTop: "-0.5rem" } }, label)); }; var Labels_default = Labels; // src/Svg/index.tsx import React3, { useRef as useRef2, useMemo } from "react"; var Svg = ({ width, label, direction, strokeDasharray, strokeDashoffset, progressColorFrom, progressColorTo, progressGradient, progressLineCap = "round", progressSize, trackColor, trackGradient, trackSize, radiansOffset, svgFullPath, onMouseDown, isDragging, arcStart, arcEnd }) => { const circleRef = useRef2(null); const halfTrack = trackSize / 2; const radius = width / 2 - halfTrack; const isArcMode = typeof arcStart === "number" && typeof arcEnd === "number"; const styles = { svg: { position: "relative", zIndex: 2, userSelect: isDragging ? "none" : "auto" }, path: { transform: isArcMode ? `${direction === -1 ? "scale(-1, 1)" : "scale(1, 1)"}` : `rotate(${radiansOffset}rad) ${direction === -1 ? "scale(-1, 1)" : "scale(1, 1)"}`, transformOrigin: "center center", // No transition to make movement instant transition: "none" } }; const validatedLineCap = progressLineCap === "round" || progressLineCap === "butt" ? progressLineCap : "round"; let trackPath = ""; let progressPath = ""; if (isArcMode) { const startAngle = (arcStart - 90) * Math.PI / 180; const endAngle = (arcEnd - 90) * Math.PI / 180; const startX = width / 2 + radius * Math.cos(startAngle); const startY = width / 2 + radius * Math.sin(startAngle); const endX = width / 2 + radius * Math.cos(endAngle); const endY = width / 2 + radius * Math.sin(endAngle); const arcSpan = (arcEnd - arcStart + 360) % 360; const largeArc = arcSpan > 180 ? 1 : 0; trackPath = `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArc} 1 ${endX} ${endY}`; progressPath = trackPath; } else { trackPath = ` M ${width / 2}, ${width / 2} m 0, -${radius} a ${radius},${radius} 0 0,1 0,${radius * 2} a -${radius},-${radius} 0 0,1 0,-${radius * 2} `; progressPath = trackPath; } const handleClick = (event) => { var _a; if (!onMouseDown) return; const bounds = (_a = circleRef.current) == null ? void 0 : _a.getBoundingClientRect(); if (!bounds) return; const clientX = "touches" in event ? event.touches[0].clientX : event.clientX; const clientY = "touches" in event ? event.touches[0].clientY : event.clientY; const centerX = bounds.left + bounds.width / 2; const centerY = bounds.top + bounds.height / 2; const distance = Math.sqrt(__pow(clientX - centerX, 2) + __pow(clientY - centerY, 2)); const threshold = bounds.width / (isDragging ? 4 : 2) - trackSize; if (distance < threshold) return; onMouseDown(event); }; const gradientIdRef = useRef2(`radial-${label}-${Math.random().toString(36).substr(2, 9)}`); const gradientId = gradientIdRef.current; const trackGradientId = `track-${gradientId}`; const progressGradientId = `progress-${gradientId}`; const createColorStops = (colors) => { return colors.map((color, index) => { let stopProps; if (typeof color === "string") { stopProps = { stopColor: color }; } else { stopProps = color; } let { offset, stopColor, stopOpacity } = stopProps; if (!offset) { if (index === 0) { offset = "0%"; } else if (index === colors.length - 1) { offset = "100%"; } else { offset = `${100 / (colors.length - 1) * index}%`; } } return /* @__PURE__ */ React3.createElement( "stop", { key: index, offset, stopColor, stopOpacity: stopOpacity != null ? stopOpacity : 1 } ); }); }; const gradientDefs = useMemo(() => { const defs = []; if (trackGradient && trackGradient.length > 0) { defs.push( /* @__PURE__ */ React3.createElement("linearGradient", { key: "track", id: trackGradientId, x1: "100%", x2: "0%" }, createColorStops(trackGradient)) ); } if (progressGradient && progressGradient.length > 0) { defs.push( /* @__PURE__ */ React3.createElement("linearGradient", { key: "progress", id: progressGradientId, x1: "100%", x2: "0%" }, createColorStops(progressGradient)) ); } else { defs.push( /* @__PURE__ */ React3.createElement("linearGradient", { key: "progress-default", id: gradientId, x1: "100%", x2: "0%" }, /* @__PURE__ */ React3.createElement("stop", { offset: "0%", stopColor: progressColorFrom }), /* @__PURE__ */ React3.createElement("stop", { offset: "100%", stopColor: progressColorTo })) ); } return defs; }, [trackGradient, progressGradient, trackGradientId, progressGradientId, gradientId, progressColorFrom, progressColorTo]); const actualTrackStroke = trackGradient && trackGradient.length > 0 ? `url(#${trackGradientId})` : trackColor; const actualProgressStroke = progressGradient && progressGradient.length > 0 ? `url(#${progressGradientId})` : `url(#${gradientId})`; return /* @__PURE__ */ React3.createElement( "svg", { width: `${width}px`, height: `${width}px`, viewBox: `0 0 ${width} ${width}`, overflow: "visible", style: styles.svg, onMouseDown: handleClick, onTouchStart: handleClick }, /* @__PURE__ */ React3.createElement("defs", null, gradientDefs), isArcMode ? /* @__PURE__ */ React3.createElement( "path", { ref: circleRef, strokeWidth: trackSize, fill: "none", stroke: actualTrackStroke, strokeLinecap: validatedLineCap, d: trackPath } ) : /* @__PURE__ */ React3.createElement( "circle", { ref: circleRef, strokeWidth: trackSize, fill: "none", stroke: actualTrackStroke, cx: width / 2, cy: width / 2, r: radius } ), /* @__PURE__ */ React3.createElement( "path", { style: styles.path, ref: svgFullPath, strokeDasharray: strokeDasharray || 0, strokeDashoffset: strokeDashoffset || 0, strokeWidth: progressSize, strokeLinecap: validatedLineCap, fill: "none", stroke: actualProgressStroke, d: progressPath } ) ); }; var Svg_default = Svg; // src/CircularSlider/index.tsx var spreadDegrees = 360; var knobOffsetConsts = { top: Math.PI / 2, right: 0, bottom: -Math.PI / 2, left: -Math.PI }; var getSliderRotation = (value) => value < 0 ? -1 : 1; var getRadians = (degrees) => degrees * Math.PI / 180; var generateRange = (min, max) => Array.from({ length: max - min + 1 }, (_, i) => i + min); var getKnobOffsetAmount = (knobPosition) => { if (typeof knobPosition === "string" && knobPosition in knobOffsetConsts) { return knobOffsetConsts[knobPosition]; } const parsed = typeof knobPosition === "number" ? knobPosition : parseFloat(knobPosition); return getRadians(parsed); }; var CircularSlider = forwardRef((props, ref) => { const { label = "ANGLE", width = 280, direction = 1, min = 0, max = 360, initialValue = 0, value = null, knobColor = "#4e63ea", knobSize = 36, knobPosition = "top", labelColor = "#272b77", labelBottom = false, labelFontSize = "1rem", valueFontSize = "3rem", appendToValue = "", prependToValue = "", verticalOffset = "1.5rem", hideLabelValue = false, hideKnob = false, hideKnobRing = false, knobDraggable = true, progressColorFrom = "#80C3F3", progressColorTo = "#4990E2", progressGradient, useMouseAdditionalToTouch = false, progressSize = 8, trackColor = "#DDDEFB", trackGradient, trackSize = 8, trackDraggable = false, data = [], dataIndex = 0, progressLineCap = "round", renderLabelValue = null, children, onChange = () => { }, isDragging = () => { }, limitDragRange = false, arcStart, arcEnd } = props; const resizeObserverRef = useRef3(null); const draggingRef = useRef3(false); const currentPositionRef = useRef3(null); const lastDataIndexRef = useRef3(dataIndex); const lastDirectionRef = useRef3(direction); const clickInProgressRef = useRef3(false); const disableEffectsRef = useRef3(false); const preventPositionResetRef = useRef3(false); const dataArray = data.length > 0 ? [...data] : [...generateRange(min, max)]; const initialState = { mounted: false, isDragging: false, width, radius: width / 2, knobOffset: getKnobOffsetAmount(knobPosition), label: initialValue, data: dataArray, radians: 0, offset: 0, knob: { x: 0, y: 0 }, dashFullArray: 0, dashFullOffset: 0 }; const isServer = useIsServer_default(); const [state, dispatch] = useReducer(reducer_default, initialState); const circularSlider = useRef3(null); const svgFullPath = useRef3(null); const touchSupported = !isServer && "ontouchstart" in window; const useMouse = !touchSupported || touchSupported && useMouseAdditionalToTouch; const valueFromParentRef = useRef3(void 0); const initCompletedRef = useRef3(false); const isMountedRef = useRef3(false); const positionSetByDragRef = useRef3(false); const setKnobPosition = useCallback((radians, fromDrag = false) => { if (disableEffectsRef.current && !fromDrag) return; if (fromDrag) { positionSetByDragRef.current = true; setTimeout(() => { positionSetByDragRef.current = false; }, 300); } const radius = state.radius - trackSize / 2; const offsetRadians = radians + getKnobOffsetAmount(knobPosition); let degrees = (offsetRadians > 0 ? offsetRadians : 2 * Math.PI + offsetRadians) * spreadDegrees / (2 * Math.PI); degrees = getSliderRotation(direction) === -1 ? spreadDegrees - degrees : degrees; if (typeof arcStart === "number" && typeof arcEnd === "number") { const arcSpan = arcEnd - arcStart; const normalizedArcStart = (arcStart + 360) % 360; const normalizedArcEnd = (arcEnd + 360) % 360; const normalizedDegrees2 = (degrees + 360) % 360; const arcCrossesBoundary = normalizedArcEnd < normalizedArcStart; let constrainedDegrees = normalizedDegrees2; if (!arcCrossesBoundary) { if (normalizedDegrees2 < normalizedArcStart) { constrainedDegrees = normalizedArcStart; } else if (normalizedDegrees2 > normalizedArcEnd) { constrainedDegrees = normalizedArcEnd; } } else { if (normalizedDegrees2 > normalizedArcEnd && normalizedDegrees2 < normalizedArcStart) { const distToStart = Math.min(Math.abs(normalizedDegrees2 - normalizedArcStart), 360 - Math.abs(normalizedDegrees2 - normalizedArcStart)); const distToEnd = Math.min(Math.abs(normalizedDegrees2 - normalizedArcEnd), 360 - Math.abs(normalizedDegrees2 - normalizedArcEnd)); constrainedDegrees = distToStart <= distToEnd ? normalizedArcStart : normalizedArcEnd; } } degrees = constrainedDegrees; const constrainedRadians = getRadians(degrees) - getKnobOffsetAmount(knobPosition); radians = constrainedRadians; } const dataArrayLength = state.data.length; const normalizedDegrees = (degrees + 360) % 360; let dashOffsetValue; let dataPointIndex; if (typeof arcStart === "number" && typeof arcEnd === "number") { const arcSpan = (arcEnd - arcStart + 360) % 360; const normArcStart = (arcStart + 360) % 360; const normArcEnd = (arcEnd + 360) % 360; const arcCrosses = normArcEnd < normArcStart; let degreesInArc; if (arcCrosses) { degreesInArc = normalizedDegrees >= normArcStart ? normalizedDegrees - normArcStart : 360 - normArcStart + normalizedDegrees; } else { degreesInArc = normalizedDegrees - normArcStart; } const arcProgress = Math.max(0, Math.min(1, degreesInArc / arcSpan)); dataPointIndex = Math.round(arcProgress * (dataArrayLength - 1)); dashOffsetValue = state.dashFullArray - arcProgress * state.dashFullArray; } else { const dashOffset = degrees / spreadDegrees * state.dashFullArray; dashOffsetValue = state.dashFullArray - dashOffset; dataPointIndex = Math.round(normalizedDegrees / 360 * (dataArrayLength - 1)); } const safeIndex = Math.min(Math.max(0, dataPointIndex), dataArrayLength - 1); const labelValue = state.data[safeIndex]; const knobXY = { x: radius * Math.cos(radians) + radius, y: radius * Math.sin(radians) + radius }; const isZeroValue = !fromDrag && dataIndex === 0 || !fromDrag && safeIndex === 0 && dataArrayLength > 1 || typeof labelValue === "number" && labelValue === 0 && !fromDrag || typeof labelValue === "string" && labelValue === "0" && !fromDrag; const finalDashOffset = isZeroValue ? state.dashFullArray : dashOffsetValue; currentPositionRef.current = { radians, label: labelValue, knob: knobXY, dashOffset: finalDashOffset }; if (labelValue !== state.label && initCompletedRef.current) { onChange(labelValue); } dispatch({ type: "setKnobPosition", payload: { dashFullOffset: finalDashOffset, label: labelValue, knob: knobXY } }); }, [state, trackSize, knobPosition, direction, onChange, dataIndex, arcStart, arcEnd]); const positionForDataIndex = useCallback(() => { if (!state.mounted || state.data.length === 0) return; if (positionSetByDragRef.current) return; if (preventPositionResetRef.current) return; const dataIndexSafe = Math.min(Math.max(0, dataIndex), state.data.length - 1); let radians; if (typeof arcStart === "number" && typeof arcEnd === "number") { const arcSpan = (arcEnd - arcStart + 360) % 360; const progress = dataIndexSafe / Math.max(state.data.length - 1, 1); const targetDegrees = (arcStart + progress * arcSpan + 360) % 360; radians = getRadians(targetDegrees) - state.knobOffset; } else { const degrees = dataIndexSafe / (state.data.length - 1) * 360 * getSliderRotation(direction); radians = getRadians(degrees) - state.knobOffset; } setKnobPosition(radians); lastDataIndexRef.current = dataIndex; }, [dataIndex, state.mounted, state.data.length, state.knobOffset, direction, setKnobPosition, arcStart, arcEnd]); const onMouseDown = (event) => { if (clickInProgressRef.current) { event.preventDefault(); event.stopPropagation(); return; } draggingRef.current = true; disableEffectsRef.current = true; clickInProgressRef.current = true; preventPositionResetRef.current = true; isDragging(true); dispatch({ type: "onMouseDown", payload: { isDragging: true } }); setTimeout(() => { clickInProgressRef.current = false; }, 100); }; const onMouseUp = () => { if (state.isDragging) { isDragging(false); draggingRef.current = false; dispatch({ type: "onMouseUp", payload: { isDragging: false } }); setTimeout(() => { if (isMountedRef.current) { disableEffectsRef.current = false; setTimeout(() => { if (isMountedRef.current) { preventPositionResetRef.current = false; } }, 250); } }, 50); } }; const onKeyDown = useCallback((event) => { if (!knobDraggable && !trackDraggable) return; const step = state.data.length > 1 ? 1 : (max - min) / 100; let newIndex = dataIndex; switch (event.key) { case "ArrowRight": case "ArrowUp": event.preventDefault(); newIndex = Math.min(dataIndex + step, state.data.length - 1); break; case "ArrowLeft": case "ArrowDown": event.preventDefault(); newIndex = Math.max(dataIndex - step, 0); break; case "Home": event.preventDefault(); newIndex = 0; break; case "End": event.preventDefault(); newIndex = state.data.length - 1; break; case "Enter": case " ": event.preventDefault(); return; default: return; } if (newIndex !== dataIndex) { const degrees = newIndex / (state.data.length - 1) * 360 * getSliderRotation(direction); const radians = getRadians(degrees) - state.knobOffset; setKnobPosition(radians); } }, [knobDraggable, trackDraggable, state.data.length, dataIndex, max, min, direction, state.knobOffset, setKnobPosition]); const onMouseMove = useCallback((event) => { if (!state.isDragging || !knobDraggable && !trackDraggable || event.type === "mousemove" && !useMouse) return; event.preventDefault(); const touch = event.type === "touchmove" ? event.changedTouches[0] : null; const getOffset = (ref2) => { var _a, _b, _c, _d; const element = ref2.current; if (!element) { return { top: 0, left: 0 }; } const rect = element.getBoundingClientRect(); const scrollLeft = (_b = (_a = window.pageXOffset) != null ? _a : document.documentElement.scrollLeft) != null ? _b : 0; const scrollTop = (_d = (_c = window.pageYOffset) != null ? _c : document.documentElement.scrollTop) != null ? _d : 0; return { top: rect.top + scrollTop, left: rect.left + scrollLeft }; }; const offset = getOffset(circularSlider); const pageX = touch ? touch.pageX : event.pageX; const pageY = touch ? touch.pageY : event.pageY; const mouseX = pageX - (offset.left + state.radius); const mouseY = pageY - (offset.top + state.radius); let radians = Math.atan2(mouseY, mouseX); if (limitDragRange) { let normalizedRadians = radians; while (normalizedRadians < 0) normalizedRadians += 2 * Math.PI; while (normalizedRadians >= 2 * Math.PI) normalizedRadians -= 2 * Math.PI; const degrees = normalizedRadians * 180 / Math.PI; const adjustedDegrees = getSliderRotation(direction) === -1 ? 360 - degrees : degrees; const normalizedDegrees = (adjustedDegrees + 360) % 360; const dataArrayLength = state.data.length; const targetIndex = Math.round(normalizedDegrees / 360 * (dataArrayLength - 1)); const clampedIndex = Math.min(Math.max(0, targetIndex), dataArrayLength - 1); const clampedDegrees = clampedIndex / (dataArrayLength - 1) * 360; const adjustedClampedDegrees = getSliderRotation(direction) === -1 ? 360 - clampedDegrees : clampedDegrees; radians = adjustedClampedDegrees * Math.PI / 180 - getKnobOffsetAmount(knobPosition); } setKnobPosition(radians, true); }, [state.isDragging, state.radius, knobDraggable, trackDraggable, useMouse, setKnobPosition, limitDragRange, state.data.length, direction, knobPosition]); const refresh = useCallback(() => { var _a; if (!circularSlider.current || !svgFullPath.current || !isMountedRef.current) return; if (draggingRef.current) return; const newWidth = width; const newRadius = newWidth / 2; dispatch({ type: "updateDimensions", payload: { width: newWidth, radius: newRadius, dashFullArray: ((_a = svgFullPath.current) == null ? void 0 : _a.getTotalLength()) || 0 } }); if (currentPositionRef.current) { setTimeout(() => { var _a2, _b; if (!draggingRef.current && currentPositionRef.current && isMountedRef.current) { disableEffectsRef.current = true; setKnobPosition((_b = (_a2 = currentPositionRef.current) == null ? void 0 : _a2.radians) != null ? _b : 0); setTimeout(() => { if (isMountedRef.current) { disableEffectsRef.current = false; } }, 50); } }, 10); } }, [width, setKnobPosition]); useImperativeHandle(ref, () => ({ refresh })); useEffect3(() => { var _a, _b, _c; isMountedRef.current = true; disableEffectsRef.current = true; dispatch({ type: "init", payload: { mounted: true, dashFullArray: (_c = (_b = (_a = svgFullPath.current) == null ? void 0 : _a.getTotalLength) == null ? void 0 : _b.call(_a)) != null ? _c : 0 } }); setTimeout(() => { if (isMountedRef.current) { disableEffectsRef.current = false; } }, 100); return () => { isMountedRef.current = false; }; }, []); useEffect3(() => { if (!state.mounted || state.dashFullArray === 0 || disableEffectsRef.current || initCompletedRef.current) return; dispatch({ type: "setInitialKnobPosition", payload: { radians: Math.PI / 2 - state.knobOffset, offset: 0 } }); positionForDataIndex(); initCompletedRef.current = true; }, [state.mounted, state.dashFullArray, state.knobOffset, positionForDataIndex]); useEffect3(() => { if (!state.mounted || !initCompletedRef.current || disableEffectsRef.current || draggingRef.current) return; if (dataIndex !== lastDataIndexRef.current) { positionForDataIndex(); } }, [dataIndex, positionForDataIndex, state.mounted]); useEffect3(() => { if (!state.mounted || !initCompletedRef.current || disableEffectsRef.current || draggingRef.current) return; if (direction !== lastDirectionRef.current) { lastDirectionRef.current = direction; positionForDataIndex(); } }, [direction, positionForDataIndex, state.mounted]); useEffect3(() => { if (!state.mounted || disableEffectsRef.current || draggingRef.current || preventPositionResetRef.current) return; if (typeof value === "number" && value !== valueFromParentRef.current) { valueFromParentRef.current = value; const radians = getRadians(value); const offsetRadians = -state.knobOffset + radians * getSliderRotation(direction); setTimeout(() => { if (!draggingRef.current && isMountedRef.current && !preventPositionResetRef.current) { setKnobPosition(offsetRadians); } }, 0); } }, [direction, state.knobOffset, value, state.mounted, setKnobPosition]); useEffect3(() => { if (typeof ResizeObserver === "undefined") return; const observeResize = () => { var _a; if (circularSlider.current) { resizeObserverRef.current = new ResizeObserver(() => { if (!draggingRef.current && isMountedRef.current) { refresh(); } }); (_a = resizeObserverRef.current) == null ? void 0 : _a.observe(circularSlider.current); } }; observeResize(); return () => { var _a; if (resizeObserverRef.current) { (_a = resizeObserverRef.current) == null ? void 0 : _a.disconnect(); } }; }, [refresh]); useEffect3(() => { const handleResize = () => { if (!draggingRef.current && isMountedRef.current) { refresh(); } }; window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; }, [refresh]); useEventListener_default("touchend", onMouseUp); useEventListener_default("mouseup", onMouseUp); useEventListener_default("touchmove", onMouseMove); useEventListener_default("mousemove", onMouseMove); useEventListener_default("keydown", onKeyDown); const sanitizedLabel = label.replace(/[^a-zA-Z0-9-_]/g, "_"); const sliderStyle = { position: "relative", display: "inline-block", width: "max-content", opacity: state.mounted ? 1 : 0, transition: "opacity 1s ease-in" }; const displayValue = typeof valueFromParentRef.current !== "undefined" ? `${valueFromParentRef.current}` : `${state.label}`; return /* @__PURE__ */ React4.createElement( "div", { style: sliderStyle, ref: circularSlider, role: "slider", "aria-label": `${label}: ${displayValue}`, "aria-valuemin": data.length > 0 ? 0 : min, "aria-valuemax": data.length > 0 ? data.length - 1 : max, "aria-valuenow": data.length > 0 ? dataIndex : parseFloat(displayValue), "aria-valuetext": `${prependToValue}${displayValue}${appendToValue}`, tabIndex: 0 }, /* @__PURE__ */ React4.createElement( Svg_default, { width, label: sanitizedLabel, direction, strokeDasharray: state.dashFullArray, strokeDashoffset: state.dashFullOffset, svgFullPath, progressSize, progressColorFrom, progressColorTo, progressGradient, progressLineCap, trackColor, trackGradient, trackSize, radiansOffset: state.radians, onMouseDown: trackDraggable ? onMouseDown : void 0, isDragging: state.isDragging, arcStart, arcEnd } ), /* @__PURE__ */ React4.createElement( Knob_default, { isDragging: state.isDragging, knobPosition: state.knob, knobSize, knobColor, trackSize, hideKnob, hideKnobRing, knobDraggable, onMouseDown }, children ), renderLabelValue != null ? renderLabelValue : /* @__PURE__ */ React4.createElement( Labels_default, { label, labelColor, labelBottom, labelFontSize, verticalOffset, valueFontSize, appendToValue, prependToValue, hideLabelValue, value: displayValue } ) ); }); var CircularSlider_default = CircularSlider; // src/index.ts var index_default = CircularSlider_default; export { index_default as default }; //# sourceMappingURL=index.mjs.map