fix: add immediate visual feedback for dragged bar length

The bar length now updates in real-time as you drag!

Problem: When dragging, all conversions recalculate proportionally, causing
relative percentages to stay the same (no visual change in bar length).

Solution: Track draggedPercentage state and display it directly on the bar
being dragged, bypassing the calculated percentage from conversions.

How it works:
- Added draggedPercentage state to track the current drag position
- Updated handleMouseMove/handleTouchMove to set draggedPercentage
- Use draggedPercentage for the dragging bar, calculated percentage for others
- Clear draggedPercentage on drag end (mouseup/touchend)
- Bar width and percentage label both use displayPercentage

Now when you drag a bar, you get instant visual feedback as the bar
resizes to follow your cursor, making the interaction feel responsive
and tactile.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-08 10:48:06 +01:00
parent bb1b4c5d29
commit 4911f0144f

View File

@@ -16,6 +16,7 @@ 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);
@@ -100,6 +101,9 @@ export default function VisualComparison({
let newPercentage = dragStartWidth.current + deltaPercentage;
newPercentage = Math.max(3, Math.min(100, newPercentage));
// Update the visual percentage immediately
setDraggedPercentage(newPercentage);
// Find the conversion result for this unit
const conversion = conversions.find(c => c.unit === draggingUnit);
if (!conversion) return;
@@ -117,6 +121,7 @@ export default function VisualComparison({
const handleMouseUp = useCallback(() => {
setDraggingUnit(null);
setDraggedPercentage(null);
activeBarRef.current = null;
}, []);
@@ -143,6 +148,9 @@ export default function VisualComparison({
let newPercentage = dragStartWidth.current + deltaPercentage;
newPercentage = Math.max(3, Math.min(100, newPercentage));
// Update the visual percentage immediately
setDraggedPercentage(newPercentage);
const conversion = conversions.find(c => c.unit === draggingUnit);
if (!conversion) return;
@@ -157,6 +165,7 @@ export default function VisualComparison({
const handleTouchEnd = useCallback(() => {
setDraggingUnit(null);
setDraggedPercentage(null);
activeBarRef.current = null;
}, []);
@@ -190,6 +199,8 @@ export default function VisualComparison({
{withPercentages.map(item => {
const isDragging = draggingUnit === item.unit;
const isDraggable = !!onValueChange;
// Use dragged percentage if this bar is being dragged, otherwise use calculated percentage
const displayPercentage = isDragging && draggedPercentage !== null ? draggedPercentage : item.percentage;
return (
<div key={item.unit} className="space-y-1.5">
@@ -230,14 +241,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>