{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stat-card-line-01",
  "type": "registry:block",
  "title": "Stat Card Line",
  "description": "Sessions stat card with line sparkline, overlaid NumberFlow, and trend badge",
  "dependencies": [
    "@visx/curve@4.0.1-alpha.0",
    "@number-flow/react",
    "@central-icons-react/all"
  ],
  "registryDependencies": [
    "@bklit/line-chart",
    "@bklit/chart-stat-flow",
    "card",
    "badge"
  ],
  "files": [
    {
      "path": "registry/blocks/stat-card-line-01/components/stat-card-line.tsx",
      "content": "\"use client\";\n\nimport { ChartStatFlow, Line, LineChart } from \"@/components/charts\";\nimport { curveBasis } from \"@visx/curve\";\nimport { useState } from \"react\";\nimport {\n  Card,\n  CardAction,\n  CardContent,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\";\nimport { sessionsSeries, sessionsStats } from \"../data/sessions-series\";\nimport {\n  StatCardChart,\n  statCardLabelClassName,\n  statCardValueClassName,\n} from \"./stat-card-chart\";\nimport {\n  formatStatCardWeekday,\n  StatCardHoverBridge,\n  type StatCardHoverState,\n} from \"./stat-card-hover-bridge\";\nimport { TrendBadge } from \"./trend-badge\";\n\nexport function StatCardLine() {\n  const [hover, setHover] = useState<StatCardHoverState>({\n    value: null,\n    label: null,\n    trend: null,\n  });\n  const average = Math.round(sessionsStats.average);\n  const displayValue = hover.value === null ? average : Math.round(hover.value);\n  const displayLabel = hover.label ?? \"Avg\";\n  const displayTrend = hover.trend ?? sessionsStats.trend;\n\n  return (\n    <Card className=\"w-full gap-0 py-0\">\n      <CardHeader className=\"px-4 py-3\">\n        <CardTitle>Active Sessions</CardTitle>\n        <CardAction>\n          <TrendBadge value={displayTrend} />\n        </CardAction>\n      </CardHeader>\n\n      <CardContent className=\"px-4 pt-2 pb-3\">\n        <StatCardChart size=\"md\">\n          <div className=\"pointer-events-none absolute right-4 bottom-4 z-10 flex flex-col items-end text-right\">\n            <ChartStatFlow\n              label={displayLabel}\n              labelClassName={statCardLabelClassName}\n              value={displayValue}\n              valueClassName={statCardValueClassName}\n            />\n          </div>\n\n          <LineChart\n            aspectRatio=\"2.5 / 1\"\n            className=\"w-full\"\n            data={sessionsSeries}\n            margin={{ top: 0, right: 0, bottom: 0, left: 0 }}\n          >\n            <StatCardHoverBridge\n              dataKey=\"value\"\n              formatLabel={formatStatCardWeekday}\n              onHoverChange={setHover}\n            />\n            <Line\n              curve={curveBasis}\n              dataKey=\"value\"\n              showHighlight\n              stroke=\"var(--chart-3)\"\n              strokeWidth={2.5}\n            />\n          </LineChart>\n        </StatCardChart>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/stat-card-line.tsx"
    },
    {
      "path": "registry/blocks/stat-card-line-01/components/stat-card-chart.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface StatCardHoverState {\n  value: number | null;\n  label: string | null;\n  trend: number | null;\n}\n\nexport const statCardValueClassName =\n  \"text-3xl font-semibold leading-none tracking-tight\";\n\nexport const statCardLabelClassName = \"mt-0 text-xs\";\n\nexport const statCardChartHeights = {\n  sm: \"[--stat-card-chart-h:96px]\",\n  md: \"[--stat-card-chart-h:190px]\",\n  lg: \"[--stat-card-chart-h:420px]\",\n} as const;\n\n/** Bleeds charts edge-to-edge inside stat card content padding. */\nexport function StatCardChart({\n  children,\n  className,\n  size = \"sm\",\n}: {\n  children: ReactNode;\n  className?: string;\n  size?: keyof typeof statCardChartHeights;\n}) {\n  return (\n    <div\n      className={cn(\n        \"relative -mx-4 -mb-3 overflow-hidden\",\n        \"[&_.relative.w-full]:aspect-auto! [&_.relative.w-full]:h-[var(--stat-card-chart-h)]!\",\n        statCardChartHeights[size],\n        className\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/stat-card-chart.tsx"
    },
    {
      "path": "registry/blocks/stat-card-line-01/components/stat-card-hover-bridge.tsx",
      "content": "\"use client\";\n\nimport { useChart } from \"@/components/charts\";\nimport { useEffect } from \"react\";\nimport type { StatCardHoverState } from \"./stat-card-chart\";\n\nexport type { StatCardHoverState } from \"./stat-card-chart\";\n\nexport function formatStatCardMonth(date: Date) {\n  return date.toLocaleDateString(\"en-US\", { month: \"short\" });\n}\n\nexport function formatStatCardWeekday(date: Date) {\n  return date.toLocaleDateString(\"en-US\", { weekday: \"long\" });\n}\n\nfunction parsePointDate(raw: unknown): Date | null {\n  if (raw instanceof Date) {\n    return raw;\n  }\n  if (typeof raw === \"string\") {\n    return new Date(raw);\n  }\n  return null;\n}\n\nfunction computePeriodTrend(\n  data: Record<string, unknown>[],\n  index: number,\n  dataKey: string\n): number | null {\n  if (index <= 0) {\n    return null;\n  }\n\n  const current = data[index]?.[dataKey];\n  const previous = data[index - 1]?.[dataKey];\n\n  if (\n    typeof current !== \"number\" ||\n    typeof previous !== \"number\" ||\n    previous === 0\n  ) {\n    return null;\n  }\n\n  return ((current - previous) / previous) * 100;\n}\n\n/** Syncs hovered chart values, labels, and trend into stat card UI. */\nexport function StatCardHoverBridge({\n  dataKey,\n  dateKey = \"date\",\n  formatLabel,\n  onHoverChange,\n}: {\n  dataKey: string;\n  dateKey?: string;\n  formatLabel: (date: Date) => string;\n  onHoverChange: (state: StatCardHoverState) => void;\n}) {\n  const { data, tooltipData } = useChart();\n\n  useEffect(() => {\n    if (!tooltipData?.point) {\n      onHoverChange({ value: null, label: null, trend: null });\n      return;\n    }\n\n    const raw = tooltipData.point[dataKey];\n    const value = typeof raw === \"number\" ? raw : null;\n    const date = parsePointDate(tooltipData.point[dateKey]);\n    const label = date ? formatLabel(date) : null;\n    const trend = computePeriodTrend(data, tooltipData.index, dataKey);\n\n    onHoverChange({ value, label, trend });\n  }, [data, dataKey, dateKey, formatLabel, onHoverChange, tooltipData]);\n\n  return null;\n}\n",
      "type": "registry:component",
      "target": "components/stat-card-hover-bridge.tsx"
    },
    {
      "path": "registry/blocks/stat-card-line-01/components/trend-badge.tsx",
      "content": "\"use client\";\n\nimport { CentralIcon } from \"@central-icons-react/all\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\n\nexport function TrendBadge({\n  value,\n  className,\n}: {\n  value: number;\n  className?: string;\n}) {\n  const positive = value >= 0;\n\n  return (\n    <Badge\n      className={cn(\n        positive &&\n          \"border-emerald-500/20 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\",\n        className\n      )}\n      variant={positive ? \"outline\" : \"destructive\"}\n    >\n      <CentralIcon\n        className=\"size-3\"\n        data-icon=\"inline-start\"\n        fill=\"outlined\"\n        join=\"round\"\n        name={positive ? \"IconArrowUp\" : \"IconArrowDown\"}\n        radius=\"0\"\n        stroke=\"1.5\"\n      />\n      {positive ? \"+\" : \"\"}\n      {value.toFixed(1)}%\n    </Badge>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/trend-badge.tsx"
    },
    {
      "path": "registry/blocks/stat-card-line-01/data/sessions-series.ts",
      "content": "export const sessionsSeries = [\n  { date: new Date(\"2024-06-03\"), value: 920 },\n  { date: new Date(\"2024-06-04\"), value: 1380 },\n  { date: new Date(\"2024-06-05\"), value: 1120 },\n  { date: new Date(\"2024-06-06\"), value: 1580 },\n  { date: new Date(\"2024-06-07\"), value: 1240 },\n  { date: new Date(\"2024-06-08\"), value: 1710 },\n  { date: new Date(\"2024-06-09\"), value: 1460 },\n];\n\nexport const sessionsStats = {\n  average:\n    sessionsSeries.reduce((sum, point) => sum + point.value, 0) /\n    sessionsSeries.length,\n  trend: 8.2,\n};\n",
      "type": "registry:lib",
      "target": "data/sessions-series.ts"
    },
    {
      "path": "registry/examples/stat-card-line-01-index.ts",
      "content": "/** biome-ignore-all lint/performance/noBarrelFile: v0 registry example barrel for shadcn install */\nexport { LineChart } from \"./line-chart\";\nexport { Line } from \"./line\";\nexport { ChartStatFlow } from \"./chart-stat-flow\";\nexport { useChart } from \"./chart-context\";\n",
      "type": "registry:lib",
      "target": "components/charts/index.ts"
    }
  ]
}