import { useRef, useState, useCallback, useEffect } from "react"; import { RotateCw } from "lucide-react"; interface ThresholdBarProps { label: string; value: number; threshold: number; isCustom: boolean; showThreshold: boolean; thresholdLabel: string; tagLabel: string; hintLabel: string; onThresholdChange: (v: number) => void; onReset: () => void; formatValue: (v: number) => string; formatThreshold?: (threshold: number) => string; baseColor?: "emerald" | "cyan" | "purple"; } export function ThresholdBar({ label, value, threshold, isCustom, showThreshold, thresholdLabel, tagLabel, hintLabel, onThresholdChange, onReset, formatValue, formatThreshold, baseColor = "emerald" }: ThresholdBarProps) { const barRef = useRef(null); const [dragging, setDragging] = useState(false); const [hovering, setHovering] = useState(false); const calcPercent = useCallback((clientX: number) => { if (!barRef.current) return threshold; const rect = barRef.current.getBoundingClientRect(); const pct = Math.round(((clientX - rect.left) / rect.width) * 100); return Math.max(5, Math.min(100, pct)); }, [threshold]); useEffect(() => { if (!dragging) return; const onMove = (e: MouseEvent) => { onThresholdChange(calcPercent(e.clientX)); }; const onUp = () => { setDragging(false); }; window.addEventListener("mousemove", onMove); window.addEventListener("mouseup", onUp); return () => { window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseup", onUp); }; }, [dragging, calcPercent, onThresholdChange]); // Touch support useEffect(() => { if (!dragging) return; const onMove = (e: TouchEvent) => { if (e.touches[0]) onThresholdChange(calcPercent(e.touches[0].clientX)); }; const onEnd = () => { setDragging(false); }; window.addEventListener("touchmove", onMove); window.addEventListener("touchend", onEnd); return () => { window.removeEventListener("touchmove", onMove); window.removeEventListener("touchend", onEnd); }; }, [dragging, calcPercent, onThresholdChange]); const baseColorClass = baseColor === "purple" ? "bg-purple-500" : baseColor === "cyan" ? "bg-cyan-500" : "bg-emerald-500"; const barColor = showThreshold ? (value > threshold ? "bg-amber-500" : baseColorClass) : (value > 80 ? "bg-amber-500" : baseColorClass); const showTooltip = dragging || hovering; return (
{label} {formatValue(value)}
{ if (showThreshold && !dragging) onThresholdChange(calcPercent(e.clientX)); }} > {/* Clip wrapper — ensures the fill always respects the track's rounded shape, even at very low values (otherwise rounded-full on the inner fill creates a tiny pill that looks detached from the track edge). */}
{/* Threshold handle — only when notifications enabled */} {showThreshold && (
{ e.preventDefault(); setDragging(true); }} onTouchStart={(e) => { e.preventDefault(); setDragging(true); }} onMouseEnter={() => setHovering(true)} onMouseLeave={() => setHovering(false)} > {/* Invisible wider hit area */}
{/* Vertical line */}
{/* Drag handle diamond */}
{/* Tooltip */} {showTooltip && (
{threshold}% {formatThreshold && {formatThreshold(threshold)}}
)}
)}
{/* Label row — only when notifications enabled */} {showThreshold && (
{hintLabel}
{tagLabel} {isCustom && ( )} {threshold}%
)}
); }