{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "defi-ticker",
  "type": "registry:ui",
  "description": "Ticker component for displaying real-time data",
  "files": [
    {
      "path": "packages/defi/ticker.tsx",
      "content": "'use client';\n\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { createContext, memo, useContext, useMemo } from 'react';\nimport { Avatar, AvatarFallback, AvatarImage } from '@repo/shadcn-ui/components/ui/avatar';\nimport { cn } from '@repo/shadcn-ui/lib/utils';\n\ntype DeFiTickerContextValue = {\n    formatter: Intl.NumberFormat;\n};\n\nconst DEFAULT_CURRENCY = 'USD';\nconst DEFAULT_LOCALE = 'en-US';\n\nconst defaultFormatter = new Intl.NumberFormat(DEFAULT_LOCALE, {\n    style: 'currency',\n    currency: DEFAULT_CURRENCY,\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n});\n\nconst DeFiTickerContext = createContext<DeFiTickerContextValue>({\n    formatter: defaultFormatter,\n});\n\nexport const useDeFiTickerContext = () => useContext(DeFiTickerContext);\n\nexport type DeFiTickerProps = HTMLAttributes<HTMLButtonElement> & {\n    currency?: string;\n    locale?: string;\n};\n\nexport const DeFiTicker = memo(\n    ({\n        children,\n        className,\n        currency = DEFAULT_CURRENCY,\n        locale = DEFAULT_LOCALE,\n        ...props\n    }: DeFiTickerProps & { children: ReactNode }) => {\n        const formatter = useMemo(() => {\n            try {\n                return new Intl.NumberFormat(locale, {\n                    style: 'currency',\n                    currency: currency.toUpperCase(),\n                    minimumFractionDigits: 2,\n                    maximumFractionDigits: 2,\n                });\n            } catch {\n                return defaultFormatter;\n            }\n        }, [currency, locale]);\n\n        return (\n            <DeFiTickerContext.Provider value={{ formatter }}>\n                <button\n                    className={cn(\n                        'inline-flex items-center gap-1.5 whitespace-nowrap align-middle',\n                        className\n                    )}\n                    type=\"button\"\n                    {...props}\n                >\n                    {children}\n                </button>\n            </DeFiTickerContext.Provider>\n        );\n    }\n);\nDeFiTicker.displayName = 'DeFiTicker';\n\nexport type DeFiTickerIconProps = HTMLAttributes<HTMLImageElement> & {\n    src: string;\n    symbol: string;\n};\n\nexport const DeFiTickerIcon = memo(\n    ({ src, symbol, className, ...props }: DeFiTickerIconProps) => {\n        if (!src) {\n            return null;\n        }\n        return (\n            <Avatar\n                className={cn('size-7 border border-border bg-muted p-1', className)}\n            >\n                <AvatarImage src={src} {...props} />\n                <AvatarFallback className=\"font-semibold text-muted-foreground text-sm\">\n                    {symbol.slice(0, 2).toUpperCase()}\n                </AvatarFallback>\n            </Avatar>\n        );\n    }\n);\nDeFiTickerIcon.displayName = 'DeFiTickerIcon';\n\nexport type DeFiTickerSymbolProps = HTMLAttributes<HTMLSpanElement> & {\n    symbol: string;\n};\n\nexport const DeFiTickerSymbol = memo(\n    ({ symbol, className, ...props }: DeFiTickerSymbolProps) => (\n        <span className={cn('font-medium', className)} {...props}>\n            {symbol.toUpperCase()}\n        </span>\n    )\n);\nDeFiTickerSymbol.displayName = 'DeFiTickerSymbol';\n\nexport type DeFiTickerPriceProps = HTMLAttributes<HTMLSpanElement> & {\n    price: number;\n};\n\nexport const DeFiTickerPrice = memo(\n    ({ price, className, ...props }: DeFiTickerPriceProps) => {\n        const context = useDeFiTickerContext();\n\n        const formattedPrice = useMemo(\n            () => context.formatter.format(price),\n            [price, context]\n        );\n\n        return (\n            <span className={cn('text-muted-foreground', className)} {...props}>\n                {formattedPrice}\n            </span>\n        );\n    }\n);\nDeFiTickerPrice.displayName = 'DeFiTickerPrice';\n\nexport type DeFiTickerPriceChangeProps = HTMLAttributes<HTMLSpanElement> & {\n    change: number;\n    isPercent?: boolean;\n};\n\nexport const DeFiTickerPriceChange = memo(\n    ({ change, isPercent, className, ...props }: DeFiTickerPriceChangeProps) => {\n        const isPositiveChange = useMemo(() => change >= 0, [change]);\n        const context = useDeFiTickerContext();\n\n        const changeFormatted = useMemo(() => {\n            if (isPercent) {\n                return `${change.toFixed(2)}%`;\n            }\n            return context.formatter.format(change);\n        }, [change, isPercent, context]);\n\n        return (\n            <span\n                className={cn(\n                    'flex items-center gap-0.5',\n                    isPositiveChange\n                        ? 'text-green-600 dark:text-green-500'\n                        : 'text-red-600 dark:text-red-500',\n                    className\n                )}\n                {...props}\n            >\n                <svg\n                    aria-labelledby=\"ticker-change-icon-title\"\n                    className={isPositiveChange ? '' : 'rotate-180'}\n                    fill=\"currentColor\"\n                    height=\"12\"\n                    role=\"img\"\n                    viewBox=\"0 0 24 24\"\n                    width=\"12\"\n                    xmlns=\"http://www.w3.org/2000/svg\"\n                >\n                    <title id=\"ticker-change-icon-title\">\n                        {isPositiveChange ? 'Up icon' : 'Down icon'}\n                    </title>\n                    <path d=\"M24 22h-24l12-20z\" />\n                </svg>\n                {changeFormatted}\n            </span>\n        );\n    }\n);\nDeFiTickerPriceChange.displayName = 'DeFiTickerPriceChange';\n",
      "type": "registry:component"
    }
  ]
}