ExplainVisualizer.tsx 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. import { useMemo } from 'react'
  2. import { ExplainHeader } from './ExplainVisualizer.Header'
  3. import { ExplainNodeRow } from './ExplainVisualizer.NodeRow'
  4. import { calculateMaxDuration, calculateSummary, createNodeTree } from './ExplainVisualizer.parser'
  5. import type { QueryPlanRow } from './ExplainVisualizer.types'
  6. export interface ExplainVisualizerProps {
  7. rows: readonly QueryPlanRow[]
  8. onShowRaw?: () => void
  9. id?: string
  10. }
  11. export function ExplainVisualizer({ rows, onShowRaw, id }: ExplainVisualizerProps) {
  12. const parsedTree = useMemo(() => createNodeTree(rows), [rows])
  13. const maxDuration = useMemo(() => calculateMaxDuration(parsedTree), [parsedTree])
  14. const summary = useMemo(() => calculateSummary(parsedTree), [parsedTree])
  15. if (parsedTree.length === 0) {
  16. return (
  17. <div className="bg-studio">
  18. <p className="m-0 border-0 px-4 py-3 font-mono text-sm text-foreground-light">
  19. No execution plan data available
  20. </p>
  21. </div>
  22. )
  23. }
  24. return (
  25. <div className="bg-studio h-full flex flex-col min-h-0">
  26. {onShowRaw && (
  27. <ExplainHeader
  28. mode="visual"
  29. onToggleMode={onShowRaw}
  30. summary={summary}
  31. id={id}
  32. rows={rows}
  33. />
  34. )}
  35. {/* Plan nodes */}
  36. <div className="flex-1 overflow-auto min-h-0">
  37. <div className="flex flex-col min-w-max pb-1 divide-y divide-border-muted">
  38. {parsedTree.map((node, idx) => (
  39. <ExplainNodeRow key={idx} node={node} depth={0} maxDuration={maxDuration} />
  40. ))}
  41. </div>
  42. </div>
  43. </div>
  44. )
  45. }