feat(viz): two pointers narrative

This commit is contained in:
2025-08-24 15:30:46 +01:00
parent f33dddfb4c
commit 6fc2133238
17 changed files with 2191 additions and 2 deletions

View File

@@ -0,0 +1,65 @@
"use client";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
import type { PointerState } from "@/lib/visualizations/types";
interface PointerProps {
pointer: PointerState;
elementWidth: number;
gap: number;
value?: number;
className?: string;
}
const COLOR_CLASSES = {
left: "text-blue-500 fill-blue-500",
right: "text-orange-500 fill-orange-500",
mid: "text-purple-500 fill-purple-500",
default: "text-blue-500 fill-blue-500",
} as const;
export function Pointer({
pointer,
elementWidth,
gap,
value,
className,
}: PointerProps) {
// Calculate center of element: index * (width + gap) + width / 2
const offset = pointer.index * (elementWidth + gap) + elementWidth / 2;
return (
<motion.div
initial={false}
animate={{ left: offset }}
transition={{
type: "spring",
stiffness: 300,
damping: 30,
}}
className={cn("absolute top-0 h-full", className)}
>
{/* Label - centered above the point */}
<span
className={cn(
"absolute left-0 top-0 -translate-x-1/2 whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-medium",
COLOR_CLASSES[pointer.color]
)}
>
{pointer.name}
{pointer.showValue && value !== undefined && (
<span className="ml-1 font-mono">= {value}</span>
)}
</span>
{/* Arrow - centered at position */}
<svg
viewBox="0 0 24 24"
className={cn("absolute left-0 bottom-0 -translate-x-1/2 h-4 w-4", COLOR_CLASSES[pointer.color])}
aria-hidden="true"
>
<path d="M12 22L4 14h6V2h4v12h6L12 22z" />
</svg>
</motion.div>
);
}