fix: make bar length actually update during drag with visual feedback
Fixed the draggable bars to show immediate visual feedback! Problem: Bars didn't resize during drag because: - Switching source unit kept relative proportions the same (log scale) - Bar width was using item.percentage which didn't update visually - No direct visual feedback for the dragged bar Solution: Use draggedPercentage state for immediate visual updates - Added draggedPercentage state to track visual position during drag - Save baseConversionsRef when drag starts (preserves original scale) - Calculate new value from percentage using BASE scale (not updated scale) - Use displayPercentage = isDragging ? draggedPercentage : item.percentage - Bar width and percentage label both use displayPercentage How it works now: 1. Mouse/touch down: save base conversions and current percentage 2. Mouse/touch move: calculate new percentage from drag delta 3. Set draggedPercentage state immediately (visual update!) 4. Calculate value from percentage using BASE scale 5. Call onValueChange to update conversions 6. Dragged bar shows draggedPercentage, others show calculated percentage 7. On release: clear draggedPercentage, bars settle to calculated positions Result: The dragged bar now visually follows your cursor in real-time! 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -16,10 +16,12 @@ export default function VisualComparison({
|
||||
onValueChange,
|
||||
}: VisualComparisonProps) {
|
||||
const [draggingUnit, setDraggingUnit] = useState<string | null>(null);
|
||||
const [draggedPercentage, setDraggedPercentage] = useState<number | null>(null);
|
||||
const dragStartX = useRef<number>(0);
|
||||
const dragStartWidth = useRef<number>(0);
|
||||
const activeBarRef = useRef<HTMLDivElement | null>(null);
|
||||
const lastUpdateTime = useRef<number>(0);
|
||||
const baseConversionsRef = useRef<ConversionResult[]>([]);
|
||||
// Calculate percentages for visual bars using logarithmic scale
|
||||
const withPercentages = useMemo(() => {
|
||||
if (conversions.length === 0) return [];
|
||||
@@ -86,10 +88,13 @@ export default function VisualComparison({
|
||||
|
||||
e.preventDefault();
|
||||
setDraggingUnit(unit);
|
||||
setDraggedPercentage(currentPercentage);
|
||||
dragStartX.current = e.clientX;
|
||||
dragStartWidth.current = currentPercentage;
|
||||
activeBarRef.current = barElement;
|
||||
}, [onValueChange]);
|
||||
// Save the current conversions as reference
|
||||
baseConversionsRef.current = [...conversions];
|
||||
}, [onValueChange, conversions]);
|
||||
|
||||
const handleMouseMove = useCallback((e: MouseEvent) => {
|
||||
if (!draggingUnit || !activeBarRef.current || !onValueChange) return;
|
||||
@@ -106,12 +111,14 @@ export default function VisualComparison({
|
||||
let newPercentage = dragStartWidth.current + deltaPercentage;
|
||||
newPercentage = Math.max(3, Math.min(100, newPercentage));
|
||||
|
||||
// Find the conversion result for this unit
|
||||
const conversion = conversions.find(c => c.unit === draggingUnit);
|
||||
if (!conversion) return;
|
||||
// Update visual percentage immediately
|
||||
setDraggedPercentage(newPercentage);
|
||||
|
||||
// Calculate min/max values for the scale
|
||||
const values = conversions.map(c => Math.abs(c.value));
|
||||
// Use the base conversions (from when drag started) for scale calculation
|
||||
const baseConversions = baseConversionsRef.current.length > 0 ? baseConversionsRef.current : conversions;
|
||||
|
||||
// Calculate min/max values for the scale from BASE conversions
|
||||
const values = baseConversions.map(c => Math.abs(c.value));
|
||||
const maxValue = Math.max(...values);
|
||||
const minValue = Math.min(...values.filter(v => v > 0));
|
||||
|
||||
@@ -130,7 +137,9 @@ export default function VisualComparison({
|
||||
}
|
||||
}
|
||||
setDraggingUnit(null);
|
||||
setDraggedPercentage(null);
|
||||
activeBarRef.current = null;
|
||||
baseConversionsRef.current = [];
|
||||
}, [draggingUnit, conversions, onValueChange]);
|
||||
|
||||
// Touch drag handlers
|
||||
@@ -139,10 +148,13 @@ export default function VisualComparison({
|
||||
|
||||
const touch = e.touches[0];
|
||||
setDraggingUnit(unit);
|
||||
setDraggedPercentage(currentPercentage);
|
||||
dragStartX.current = touch.clientX;
|
||||
dragStartWidth.current = currentPercentage;
|
||||
activeBarRef.current = barElement;
|
||||
}, [onValueChange]);
|
||||
// Save the current conversions as reference
|
||||
baseConversionsRef.current = [...conversions];
|
||||
}, [onValueChange, conversions]);
|
||||
|
||||
const handleTouchMove = useCallback((e: TouchEvent) => {
|
||||
if (!draggingUnit || !activeBarRef.current || !onValueChange) return;
|
||||
@@ -161,10 +173,13 @@ export default function VisualComparison({
|
||||
let newPercentage = dragStartWidth.current + deltaPercentage;
|
||||
newPercentage = Math.max(3, Math.min(100, newPercentage));
|
||||
|
||||
const conversion = conversions.find(c => c.unit === draggingUnit);
|
||||
if (!conversion) return;
|
||||
// Update visual percentage immediately
|
||||
setDraggedPercentage(newPercentage);
|
||||
|
||||
const values = conversions.map(c => Math.abs(c.value));
|
||||
// Use the base conversions (from when drag started) for scale calculation
|
||||
const baseConversions = baseConversionsRef.current.length > 0 ? baseConversionsRef.current : conversions;
|
||||
|
||||
const values = baseConversions.map(c => Math.abs(c.value));
|
||||
const maxValue = Math.max(...values);
|
||||
const minValue = Math.min(...values.filter(v => v > 0));
|
||||
|
||||
@@ -182,7 +197,9 @@ export default function VisualComparison({
|
||||
}
|
||||
}
|
||||
setDraggingUnit(null);
|
||||
setDraggedPercentage(null);
|
||||
activeBarRef.current = null;
|
||||
baseConversionsRef.current = [];
|
||||
}, [draggingUnit, conversions, onValueChange]);
|
||||
|
||||
// Add/remove global event listeners for drag
|
||||
@@ -215,6 +232,8 @@ export default function VisualComparison({
|
||||
{withPercentages.map(item => {
|
||||
const isDragging = draggingUnit === item.unit;
|
||||
const isDraggable = !!onValueChange;
|
||||
// Use draggedPercentage if this bar is being dragged
|
||||
const displayPercentage = isDragging && draggedPercentage !== null ? draggedPercentage : item.percentage;
|
||||
|
||||
return (
|
||||
<div key={item.unit} className="space-y-1.5">
|
||||
@@ -255,14 +274,14 @@ export default function VisualComparison({
|
||||
draggingUnit ? "transition-none" : "transition-all duration-500 ease-out"
|
||||
)}
|
||||
style={{
|
||||
width: `${item.percentage}%`,
|
||||
width: `${displayPercentage}%`,
|
||||
backgroundColor: color,
|
||||
}}
|
||||
/>
|
||||
{/* Percentage label overlay */}
|
||||
<div className="absolute inset-0 flex items-center px-3 text-xs font-bold pointer-events-none">
|
||||
<span className="text-foreground drop-shadow-sm">
|
||||
{Math.round(item.percentage)}%
|
||||
{Math.round(displayPercentage)}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user