{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "price",
  "title": "price",
  "description": "Flexible price component with customizable features and options to display the amount and currency.",
  "files": [
    {
      "path": "registry/ecommerce/price/index.tsx",
      "content": "'use client';\nimport { cn } from '@/lib/utils';\nimport React from 'react';\n\nexport const currencyMinorUnits: Record<string, number> = {\n  USD: 2,\n  EUR: 2,\n  GBP: 2,\n  JPY: 0,\n};\n\ninterface PriceContextValue {\n  amount: number | null;\n  compareAt?: number | null;\n  savingsPercent?: number | null;\n  currency?: string;\n  locale?: string;\n}\nconst PriceContext = React.createContext<PriceContextValue | undefined>(\n  undefined,\n);\nfunction usePriceContext() {\n  const context = React.useContext(PriceContext);\n  if (context === undefined) {\n    throw new Error('usePriceContext must be used within a PriceProvider');\n  }\n  return context;\n}\n/**\n * Returns the divider to convert minor units to major units.\n * e.g. USD -> 100, JPY -> 1\n */\nexport function minorUnitDivider(currency?: string): number {\n  const code = (currency ?? 'USD').toUpperCase();\n  const digits = currencyMinorUnits[code] ?? 2;\n  return Math.pow(10, digits);\n}\n\n/**\n * Memoized Intl.NumberFormat factory keyed by locale|currency|digits.\n */\nconst nfCache = new Map<string, Intl.NumberFormat>();\nfunction getNumberFormatter(\n  locale: string,\n  currency: string,\n  fractionDigits: number,\n) {\n  const key = `${locale}|${currency}|${fractionDigits}`;\n  const cached = nfCache.get(key);\n  if (cached) return cached;\n  const nf = new Intl.NumberFormat(locale, {\n    style: 'currency',\n    currency,\n    minimumFractionDigits: fractionDigits,\n    maximumFractionDigits: fractionDigits,\n    currencyDisplay: 'symbol',\n  });\n  nfCache.set(key, nf);\n  return nf;\n}\n\n/**\n * Format an integer minor-unit amount (e.g. cents) to a localized currency string.\n * - amountMinor: integer (may be negative)\n * - currency: ISO code, defaults to USD\n * - locale: BCP47 locale, defaults to en-US\n */\nexport function formatCurrencyFromMinor(\n  amountMinor: number,\n  currency = 'USD',\n  locale = 'en-US',\n): string {\n  if (!Number.isInteger(amountMinor)) {\n    console.warn('amountMinor should be an integer');\n    amountMinor = Math.round(amountMinor);\n  }\n  const digits = currencyMinorUnits[currency.toUpperCase()] ?? 2;\n  const divider = minorUnitDivider(currency);\n  // Use exact division (no floating rounding) then delegate to Intl for display.\n  const amountMajor = amountMinor / divider;\n  const nf = getNumberFormatter(locale, currency, digits);\n  return nf.format(amountMajor);\n}\n\n// components/Price.tsx\n\ninterface PriceProps extends React.ComponentProps<'div'> {\n  amount: number | null; // minor units\n  currency?: 'USD' | 'EUR' | 'GBP' | 'JPY';\n  locale?: string;\n  compareAt?: number | null; // minor units\n  range?: { min: number | null; max: number | null } | null;\n}\nconst format = (amt: number | null, currency?: string, locale?: string) => {\n  if (amt === null) return '';\n  return formatCurrencyFromMinor(amt, currency ?? 'USD', locale ?? 'en-US');\n};\n\nexport function Price({\n  amount,\n  currency = 'USD',\n  locale = 'en-US',\n  compareAt = null,\n  range = null,\n  className,\n  ...props\n}: PriceProps) {\n  // Range handling\n  if (range) {\n    if (range.min === null || range.max === null) return null;\n    if (range.min === range.max) {\n      return (\n        <span {...props}>\n          <span className=\"sr-only\">Price: </span>\n          {format(range.min)}\n        </span>\n      );\n    }\n    return (\n      <span {...props}>\n        <span className=\"sr-only\">Price range: </span>\n        {format(range.min)}&nbsp;–&nbsp;{format(range.max)}\n      </span>\n    );\n  }\n\n  // Unavailable / Free\n  if (amount == null) {\n    return (\n      <span aria-hidden={false} {...props}>\n        <span className=\"sr-only\">Price not available</span>\n      </span>\n    );\n  }\n  if (amount === 0) {\n    return (\n      <span {...props}>\n        <span className=\"sr-only\">Free</span>\n        Free\n      </span>\n    );\n  }\n\n  const isOnSale = compareAt != null && compareAt > amount;\n  let savingsPercent: number | null = null;\n  if (isOnSale) {\n    // integer math to avoid float errors:\n    savingsPercent = Math.round(((compareAt - amount) * 100) / compareAt);\n  }\n  return (\n    <PriceContext.Provider\n      value={{ amount, compareAt, savingsPercent, currency, locale }}\n    >\n      <span\n        className={cn('inline-flex items-baseline', className)}\n        {...props}\n      />\n    </PriceContext.Provider>\n  );\n}\n\nexport function PriceCurrent({ ...props }: React.ComponentProps<'span'>) {\n  const { amount, currency, locale } = usePriceContext();\n  return (\n    <span {...props}>\n      <span className=\"sr-only\">Price: </span>\n      {format(amount, currency, locale)}\n    </span>\n  );\n}\n\nexport function PriceCompareAt({ ...props }: React.ComponentProps<'span'>) {\n  const { compareAt, currency, locale } = usePriceContext();\n  return (\n    <span {...props}>\n      <span className=\"sr-only\">Old Price: </span>\n      {format(compareAt!, currency, locale)}\n    </span>\n  );\n}\n\nexport function PriceSavings({\n  children,\n  ...props\n}: React.ComponentProps<'span'>) {\n  const { savingsPercent } = usePriceContext();\n  return (\n    <span aria-hidden=\"true\" {...props}>\n      <span className=\"sr-only\">Save: </span>\n      {children} {savingsPercent}%\n    </span>\n  );\n}\n",
      "type": "registry:block",
      "target": "components/systaliko-ui/price.tsx"
    }
  ],
  "type": "registry:block"
}