feat(viz): heap pattern with kth largest
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { HeapNodeState } from '@/lib/visualizations/types';
|
||||
|
||||
interface HeapNodeProps {
|
||||
node: HeapNodeState;
|
||||
x: number;
|
||||
y: number;
|
||||
radius?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const STATE_CLASSES = {
|
||||
normal: 'fill-[var(--surface-variant)] stroke-[var(--border)]',
|
||||
comparing: 'fill-[var(--info)]/20 stroke-[var(--info)]',
|
||||
swapping: 'fill-[var(--warning)]/20 stroke-[var(--warning)]',
|
||||
settled: 'fill-[var(--success)]/20 stroke-[var(--success)]',
|
||||
highlighted: 'fill-[var(--primary)]/30 stroke-[var(--primary)]',
|
||||
removing: 'fill-[var(--destructive)]/20 stroke-[var(--destructive)] opacity-50',
|
||||
} as const;
|
||||
|
||||
const TEXT_CLASSES = {
|
||||
normal: 'fill-[var(--foreground)]',
|
||||
comparing: 'fill-[var(--info)]',
|
||||
swapping: 'fill-[var(--warning)]',
|
||||
settled: 'fill-[var(--success)]',
|
||||
highlighted: 'fill-[var(--primary)]',
|
||||
removing: 'fill-[var(--destructive)] opacity-50',
|
||||
} as const;
|
||||
|
||||
export function HeapNode({
|
||||
node,
|
||||
x,
|
||||
y,
|
||||
radius = 24,
|
||||
className,
|
||||
}: HeapNodeProps) {
|
||||
const isActive = node.state === 'comparing' || node.state === 'swapping' || node.state === 'highlighted';
|
||||
const isRemoving = node.state === 'removing';
|
||||
|
||||
return (
|
||||
<motion.g
|
||||
initial={false}
|
||||
animate={{
|
||||
scale: isActive ? 1.1 : isRemoving ? 0.8 : 1,
|
||||
opacity: isRemoving ? 0.5 : 1,
|
||||
}}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 300,
|
||||
damping: 30,
|
||||
}}
|
||||
style={{ transformOrigin: `${x}px ${y}px` }}
|
||||
className={cn(className)}
|
||||
>
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={radius}
|
||||
strokeWidth={2}
|
||||
className={cn(
|
||||
'transition-colors duration-200',
|
||||
STATE_CLASSES[node.state]
|
||||
)}
|
||||
initial={false}
|
||||
animate={{
|
||||
filter: isActive ? 'drop-shadow(0 0 8px var(--primary))' : 'none',
|
||||
}}
|
||||
transition={{ duration: 0.2 }}
|
||||
/>
|
||||
<text
|
||||
x={x}
|
||||
y={y}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="central"
|
||||
className={cn(
|
||||
'pointer-events-none select-none font-mono text-sm font-medium',
|
||||
TEXT_CLASSES[node.state]
|
||||
)}
|
||||
>
|
||||
{node.value}
|
||||
</text>
|
||||
</motion.g>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user