{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "momentum-chart",
  "title": "Momentum Chart",
  "description": "Displays a metric’s value, change, and trend to highlight momentum over time.",
  "dependencies": [
    "recharts",
    "motion",
    "@hugeicons/react",
    "@hugeicons/core-free-icons"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/ui/momentum-chart.tsx",
      "content": "\"use client\";\r\nimport { HugeiconsIcon } from \"@hugeicons/react\";\r\nimport { DotPattern } from \"./dot-pattern\";\r\nimport { ArrowUpRight01Icon } from \"@hugeicons/core-free-icons\";\r\nimport { Area, AreaChart, ResponsiveContainer, XAxis } from \"recharts\";\r\nimport { useSpring, motion, useTransform } from \"motion/react\";\r\nimport { useEffect } from \"react\";\r\n\r\nconst momentumData = [\r\n  { day: \"Mon\", value: 90000 },\r\n  { day: \"Tue\", value: 140000 },\r\n  { day: \"Wed\", value: 175000 },\r\n  { day: \"Thu\", value: 150000 },\r\n  { day: \"Fri\", value: 110000 },\r\n  { day: \"Sat\", value: 200000 },\r\n  { day: \"Sun\", value: 264802.43 },\r\n];\r\n\r\nexport const MomentumChart = () => {\r\n  const lastPrice = momentumData[momentumData.length - 1];\r\n  const priceSpring = useSpring(lastPrice.value, {\r\n    damping: 20,\r\n    stiffness: 300,\r\n  });\r\n\r\n  const basePrice = momentumData[0].value;\r\n  const delta = useTransform(priceSpring, (v) => v - basePrice);\r\n  const percentage = useTransform(priceSpring, (v) => {\r\n    const change = v - basePrice;\r\n    return (change / basePrice) * 100;\r\n  });\r\n\r\n  const formattedPrice = useTransform(priceSpring, (v) =>\r\n    v.toLocaleString(\"en-US\", {\r\n      minimumFractionDigits: 2,\r\n      maximumFractionDigits: 2,\r\n    }),\r\n  );\r\n  const formattedDelta = useTransform(delta, (v) => {\r\n    const sign = v >= 0 ? \"+\" : \"-\";\r\n    return `${sign} $${Math.abs(v).toLocaleString(\"en-US\", {\r\n      minimumFractionDigits: 2,\r\n      maximumFractionDigits: 2,\r\n    })}`;\r\n  });\r\n\r\n  const formattedPercentage = useTransform(percentage, (v) => {\r\n    const sign = v >= 0 ? \"+\" : \"-\";\r\n    return `(${sign}${Math.abs(v).toFixed(2)}%)`;\r\n  });\r\n\r\n  const isPositive = useTransform(delta, (v) => v >= 0);\r\n  const deltaColor = useTransform(isPositive, (v) =>\r\n    v ? \"#60a5fa\" : \"#f87171\",\r\n  );\r\n\r\n  const previousWeekClose = momentumData[0].value;\r\n  const weekComparison = useTransform(priceSpring, (v) => {\r\n    const change = v - previousWeekClose;\r\n    return (change / previousWeekClose) * 100;\r\n  });\r\n  const weekSentence = useTransform(weekComparison, (v) => {\r\n    const sign = v >= 0 ? \"+\" : \"-\";\r\n    const word = v >= 0 ? \"More\" : \"Less\";\r\n\r\n    return `${sign}${Math.abs(v).toFixed(2)}% ${word} than last week`;\r\n  });\r\n\r\n  useEffect(() => {\r\n    priceSpring.set(lastPrice.value);\r\n  }, [lastPrice.value]);\r\n\r\n  return (\r\n    <div className=\"w-80 h-80 md:w-120 rounded-4xl overflow-hidden bg-linear-to-l from-neutral-900 via-neutral-800 to-neutral-900 relative md:p-8 p-6 text-white\">\r\n      <DotPattern className=\"text-white/8 [mask-image:radial-gradient(400px_circle_at_center,white,transparent)]\" />\r\n      <div className=\"flex flex-col gap-5\">\r\n        <div className=\"text-2xl flex gap-1\">\r\n          <span className=\"text-neutral-500 font-semibold\">$</span>\r\n          <motion.span className=\"font-light tracking-wide\">\r\n            {formattedPrice}\r\n          </motion.span>\r\n        </div>\r\n        <div className=\"flex flex-col gap-1.5\">\r\n          <div className=\"flex gap-2 items-center\">\r\n            <HugeiconsIcon\r\n              icon={ArrowUpRight01Icon}\r\n              className=\"p-1 rounded-full bg-blue-500 text-white size-6\"\r\n            />\r\n            <motion.span\r\n              style={{ color: deltaColor }}\r\n              className=\"font-light text-lg tracking-wide\"\r\n            >\r\n              {formattedDelta}\r\n            </motion.span>\r\n            <motion.span className=\"text-sm\">{formattedPercentage}</motion.span>\r\n          </div>\r\n          <div>\r\n            <motion.span className=\"text-neutral-300 text-xs tracking-wider opacity-80\">\r\n              {weekSentence}\r\n            </motion.span>\r\n          </div>\r\n        </div>\r\n        <div className=\"w-full h-30\">\r\n          <ResponsiveContainer width=\"100%\" height=\"100%\">\r\n            <AreaChart\r\n              data={momentumData}\r\n              margin={{ left: 20, right: 20 }}\r\n              onMouseMove={(state) => {\r\n                const rawIndex = state?.activeTooltipIndex;\r\n                if (rawIndex === null) return;\r\n\r\n                const index = Number(rawIndex);\r\n                const point = momentumData[index];\r\n                if (!point) return;\r\n                priceSpring.set(point.value);\r\n              }}\r\n              onMouseLeave={() => {\r\n                priceSpring.set(lastPrice.value);\r\n              }}\r\n            >\r\n              <Area\r\n                type=\"bump\"\r\n                dataKey=\"value\"\r\n                stroke=\"#3b82f6\"\r\n                strokeWidth={2.5}\r\n                fill=\"transparent\"\r\n                filter=\"url(#line-glow)\"\r\n                activeDot={{ r: 5, fill: \"#F7F8F0\", stroke: \"0\" }}\r\n              />\r\n              <XAxis\r\n                dataKey=\"day\"\r\n                tickLine={false}\r\n                axisLine={false}\r\n                tickMargin={8}\r\n                tick={{\r\n                  fontSize: 11,\r\n                  fill: \"#F7F8F0\",\r\n                  letterSpacing: \"0.08em\",\r\n                }}\r\n                tickFormatter={(value) => value.toUpperCase()}\r\n              />\r\n\r\n              <defs>\r\n                <filter\r\n                  id=\"line-glow\"\r\n                  x=\"-20%\"\r\n                  y=\"-20%\"\r\n                  width=\"140%\"\r\n                  height=\"140%\"\r\n                >\r\n                  <feGaussianBlur stdDeviation=\"10\" result=\"blur\" />\r\n                  <feComposite in=\"SourceGraphic\" in2=\"blur\" operator=\"over\" />\r\n                </filter>\r\n              </defs>\r\n            </AreaChart>\r\n          </ResponsiveContainer>\r\n        </div>\r\n      </div>\r\n    </div>\r\n  );\r\n};\r\n",
      "type": "registry:component"
    },
    {
      "path": "registry/default/ui/dot-pattern.tsx",
      "content": "\"use client\"\r\n\r\nimport React from \"react\"\r\nimport { cn } from \"@/lib/utils\"\r\n\r\ninterface DotPatternProps extends React.SVGProps<SVGSVGElement> {\r\n  size?: number      // space between dots\r\n  radius?: number    // dot size\r\n  className?: string\r\n}\r\n\r\nexport function DotPattern({\r\n  size = 16,\r\n  radius = 1,\r\n  className,\r\n  ...props\r\n}: DotPatternProps) {\r\n  return (\r\n    <svg\r\n      aria-hidden=\"true\"\r\n      className={cn(\r\n        \"pointer-events-none absolute inset-2 h-full w-full\",\r\n        className\r\n      )}\r\n      {...props}\r\n    >\r\n      <defs>\r\n        <pattern\r\n          id=\"dot-pattern\"\r\n          x=\"0\"\r\n          y=\"0\"\r\n          width={size}\r\n          height={size}\r\n          patternUnits=\"userSpaceOnUse\"\r\n        >\r\n          <circle\r\n            cx={radius}\r\n            cy={radius}\r\n            r={radius}\r\n            fill=\"currentColor\"\r\n          />\r\n        </pattern>\r\n      </defs>\r\n\r\n      <rect width=\"100%\" height=\"100%\" fill=\"url(#dot-pattern)\" />\r\n    </svg>\r\n  )\r\n}\r\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}