"use client"; import { useMemo } from "react"; import type { ArrayState, ElementState } from "@/types/visualization"; interface ArrayVisualizerProps { data: ArrayState; name?: string; } const CELL_WIDTH = 56; const CELL_HEIGHT = 56; const CELL_GAP = 4; const INDEX_AREA_HEIGHT = 20; const POINTER_HEIGHT = 24; const ANNOTATION_HEIGHT = 20; const SVG_PADDING = 16; function getStateColor(state: ElementState): string { switch (state) { case "active": return "var(--viz-active)"; case "comparing": return "var(--viz-comparing)"; case "found": return "var(--viz-found)"; case "visited": return "var(--viz-visited)"; case "swapping": return "var(--viz-swapping)"; default: return "var(--viz-default)"; } } function getTextColor(state: ElementState): string { switch (state) { case "active": case "found": case "swapping": return "white"; case "comparing": return "var(--foreground)"; case "visited": return "var(--muted-foreground)"; default: return "var(--foreground)"; } } export function ArrayVisualizer({ data, name }: ArrayVisualizerProps) { const { values, pointers = {} } = data; const dimensions = useMemo(() => { const hasPointers = Object.keys(pointers).length > 0; const hasAnnotations = values.some( (v) => v.annotations && v.annotations.length > 0 ); const contentWidth = values.length * CELL_WIDTH + (values.length - 1) * CELL_GAP; const width = contentWidth + SVG_PADDING * 2; let height = CELL_HEIGHT + INDEX_AREA_HEIGHT + SVG_PADDING * 2; if (hasPointers) height += POINTER_HEIGHT + 16; if (hasAnnotations) height += ANNOTATION_HEIGHT; return { width, height, contentWidth, hasPointers, hasAnnotations, startX: SVG_PADDING, startY: hasAnnotations ? SVG_PADDING + ANNOTATION_HEIGHT : SVG_PADDING + 8, }; }, [values, pointers]); // Group pointers by position for stacking const pointersByPosition = useMemo(() => { const grouped: Record = {}; for (const [label, index] of Object.entries(pointers)) { if (!grouped[index]) { grouped[index] = []; } grouped[index].push(label); } return grouped; }, [pointers]); return (
{name && ( {name} )}
{/* Array cells */} {values.map((element, index) => { const x = dimensions.startX + index * (CELL_WIDTH + CELL_GAP); const y = dimensions.startY; return ( {/* Cell background */} {/* Cell border */} {/* Value */} {String(element.value)} {/* Annotations (above cell) */} {element.annotations && element.annotations.length > 0 && ( {element.annotations.join(", ")} )} ); })} {/* Pointers (below cells, before indices) */} {dimensions.hasPointers && ( {Object.entries(pointersByPosition).map(([posStr, labels]) => { const position = parseInt(posStr, 10); if (position < 0 || position >= values.length) return null; const x = dimensions.startX + position * (CELL_WIDTH + CELL_GAP) + CELL_WIDTH / 2; const y = dimensions.startY + CELL_HEIGHT + INDEX_AREA_HEIGHT + POINTER_HEIGHT; return ( {/* Arrow */} {/* Label(s) */} {labels.join(", ")} ); })} )} {/* Index labels (rendered last to appear on top) */} {values.map((_, index) => { const x = dimensions.startX + index * (CELL_WIDTH + CELL_GAP); const y = dimensions.startY; return ( {index} ); })} {/* Arrow marker definition */}
); }