61 lines
1.7 KiB
TypeScript
61 lines
1.7 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useMemo } from 'react';
|
||
|
|
import { type ConversionResult } from '@/lib/units';
|
||
|
|
import { formatNumber, cn } from '@/lib/utils';
|
||
|
|
|
||
|
|
interface VisualComparisonProps {
|
||
|
|
conversions: ConversionResult[];
|
||
|
|
color: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function VisualComparison({
|
||
|
|
conversions,
|
||
|
|
color,
|
||
|
|
}: VisualComparisonProps) {
|
||
|
|
// Calculate percentages for visual bars
|
||
|
|
const withPercentages = useMemo(() => {
|
||
|
|
if (conversions.length === 0) return [];
|
||
|
|
|
||
|
|
const maxValue = Math.max(...conversions.map(c => Math.abs(c.value)));
|
||
|
|
if (maxValue === 0) return conversions.map(c => ({ ...c, percentage: 0 }));
|
||
|
|
|
||
|
|
return conversions.map(c => ({
|
||
|
|
...c,
|
||
|
|
percentage: (Math.abs(c.value) / maxValue) * 100,
|
||
|
|
}));
|
||
|
|
}, [conversions]);
|
||
|
|
|
||
|
|
if (conversions.length === 0) {
|
||
|
|
return (
|
||
|
|
<div className="text-center py-8 text-muted-foreground">
|
||
|
|
Enter a value to see conversions
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-3">
|
||
|
|
{withPercentages.map(item => (
|
||
|
|
<div key={item.unit} className="space-y-1">
|
||
|
|
<div className="flex items-center justify-between text-sm">
|
||
|
|
<span className="font-medium">{item.unitInfo.plural}</span>
|
||
|
|
<span className="tabular-nums">
|
||
|
|
{formatNumber(item.value)} {item.unit}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||
|
|
<div
|
||
|
|
className="h-full rounded-full transition-all duration-500 ease-out"
|
||
|
|
style={{
|
||
|
|
width: `${item.percentage}%`,
|
||
|
|
backgroundColor: `var(--color-${color})`,
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|