{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-usage-card",
  "type": "registry:ui",
  "title": "Animated Usage Card",
  "description": "An animated version of usage-card with smooth spring animations powered by motion.",
  "files": [
    {
      "path": "src/registry/animated/animated-usage-card.tsx",
      "content": "\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { ChevronDown } from \"lucide-react\";\nimport { motion, useMotionValue, useSpring, useTransform } from \"motion/react\";\nimport * as React from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\nconst animatedUsageCardVariants = cva(\n  \"relative min-w-[280px] rounded-2xl border bg-card text-card-foreground\",\n  {\n    variants: {\n      variant: {\n        default: \"border-border\",\n        elevated: \"border-border shadow-lg\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n);\n\ninterface AnimatedUsageCardProps\n  extends VariantProps<typeof animatedUsageCardVariants> {\n  className?: string;\n  children?: React.ReactNode;\n}\n\nconst AnimatedUsageCard = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardProps\n>(({ className, variant, children }, ref) => (\n  <motion.div\n    ref={ref}\n    initial={{ opacity: 0, y: 20, scale: 0.95 }}\n    animate={{ opacity: 1, y: 0, scale: 1 }}\n    transition={{\n      type: \"spring\",\n      bounce: 0.3,\n      duration: 0.6,\n    }}\n    className={cn(animatedUsageCardVariants({ variant, className }))}\n  >\n    {children}\n  </motion.div>\n));\nAnimatedUsageCard.displayName = \"AnimatedUsageCard\";\n\ninterface AnimatedUsageCardHeaderProps\n  extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst AnimatedUsageCardHeader = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardHeaderProps\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\n      \"flex items-center justify-between gap-4 p-4 pb-0\",\n      className,\n    )}\n    {...props}\n  />\n));\nAnimatedUsageCardHeader.displayName = \"AnimatedUsageCardHeader\";\n\ninterface AnimatedUsageCardPeriodProps\n  extends React.HTMLAttributes<HTMLSpanElement> {\n  daysRemaining?: number;\n}\n\nconst AnimatedUsageCardPeriod = React.forwardRef<\n  HTMLSpanElement,\n  AnimatedUsageCardPeriodProps\n>(({ className, daysRemaining, children, ...props }, ref) => (\n  <span\n    ref={ref}\n    className={cn(\"text-sm font-medium text-foreground\", className)}\n    {...props}\n  >\n    {children ??\n      (daysRemaining !== undefined &&\n        `${daysRemaining} days remaining in cycle`)}\n  </span>\n));\nAnimatedUsageCardPeriod.displayName = \"AnimatedUsageCardPeriod\";\n\ninterface AnimatedUsageCardActionProps\n  extends React.ComponentPropsWithoutRef<typeof Button> {}\n\nconst AnimatedUsageCardAction = React.forwardRef<\n  HTMLButtonElement,\n  AnimatedUsageCardActionProps\n>(({ className, ...props }, ref) => (\n  <Button\n    ref={ref}\n    variant=\"outline\"\n    size=\"sm\"\n    className={cn(\"rounded-full\", className)}\n    {...props}\n  />\n));\nAnimatedUsageCardAction.displayName = \"AnimatedUsageCardAction\";\n\ninterface AnimatedUsageCardSummaryProps\n  extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst AnimatedUsageCardSummary = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardSummaryProps\n>(({ className, ...props }, ref) => (\n  <div ref={ref} className={cn(\"p-4 space-y-3\", className)} {...props} />\n));\nAnimatedUsageCardSummary.displayName = \"AnimatedUsageCardSummary\";\n\ninterface AnimatedUsageCardLabelsProps\n  extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst AnimatedUsageCardLabels = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardLabelsProps\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex items-start justify-between gap-4\", className)}\n    {...props}\n  />\n));\nAnimatedUsageCardLabels.displayName = \"AnimatedUsageCardLabels\";\n\ninterface AnimatedUsageCardLabelProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  label: string;\n  amount: number;\n  limit?: number;\n  currency?: string;\n  align?: \"left\" | \"right\";\n}\n\nconst AnimatedUsageCardLabel = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardLabelProps\n>(\n  (\n    {\n      className,\n      label,\n      amount,\n      limit,\n      currency = \"$\",\n      align = \"left\",\n      ...props\n    },\n    ref,\n  ) => (\n    <div\n      ref={ref}\n      className={cn(\n        \"flex flex-col gap-0.5\",\n        align === \"right\" && \"items-end\",\n        className,\n      )}\n      {...props}\n    >\n      <span className=\"text-xs text-muted-foreground\">{label}</span>\n      <span className=\"text-sm font-medium tabular-nums\">\n        {currency}\n        {amount.toLocaleString(\"en-US\", {\n          minimumFractionDigits: 2,\n          maximumFractionDigits: 2,\n        })}\n        {limit !== undefined && (\n          <span className=\"text-muted-foreground\">\n            {\" / \"}\n            {currency}\n            {limit.toLocaleString(\"en-US\", {\n              minimumFractionDigits: 0,\n              maximumFractionDigits: 0,\n            })}\n          </span>\n        )}\n      </span>\n    </div>\n  ),\n);\nAnimatedUsageCardLabel.displayName = \"AnimatedUsageCardLabel\";\n\ninterface AnimatedUsageCardProgressProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  value: number;\n  max?: number;\n  showOverage?: boolean;\n}\n\nconst AnimatedUsageCardProgress = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardProgressProps\n>(({ className, value, max = 100, showOverage = false, ...props }, ref) => {\n  const percentage = Math.min((value / max) * 100, 100);\n  const isOverage = value > max;\n\n  const motionValue = useMotionValue(0);\n  const springValue = useSpring(motionValue, {\n    stiffness: 120,\n    damping: 14,\n    mass: 0.8,\n  });\n  const width = useTransform(springValue, (v) => `${v}%`);\n\n  React.useEffect(() => {\n    motionValue.set(percentage);\n  }, [percentage, motionValue]);\n\n  return (\n    <div\n      ref={ref}\n      className={cn(\n        \"relative h-2 w-full overflow-hidden rounded-full bg-muted\",\n        className,\n      )}\n      {...props}\n    >\n      <motion.div\n        className={cn(\n          \"h-full rounded-full\",\n          isOverage ? \"bg-destructive\" : \"bg-primary\",\n        )}\n        style={{ width }}\n      />\n    </div>\n  );\n});\nAnimatedUsageCardProgress.displayName = \"AnimatedUsageCardProgress\";\n\ninterface AnimatedUsageCardListContextValue {\n  expanded: boolean;\n  setExpanded: (expanded: boolean) => void;\n  itemCount: number;\n  collapsedHeight: number;\n  expandedHeight: number;\n}\n\nconst AnimatedUsageCardListContext =\n  React.createContext<AnimatedUsageCardListContextValue | null>(null);\n\nfunction useAnimatedUsageCardList() {\n  const context = React.useContext(AnimatedUsageCardListContext);\n  if (!context) {\n    throw new Error(\n      \"useAnimatedUsageCardList must be used within AnimatedUsageCardList\",\n    );\n  }\n  return context;\n}\n\nconst ITEM_HEIGHT = 44;\n\ninterface AnimatedUsageCardListProps {\n  className?: string;\n  children?: React.ReactNode;\n  defaultExpanded?: boolean;\n  /** Number of items to show when collapsed (can be decimal like 1.5 to show partial item) */\n  visibleItems?: number;\n  /** Enable collapse/expand functionality */\n  collapsible?: boolean;\n  /** Show dividers between items */\n  dividers?: boolean;\n}\n\nconst AnimatedUsageCardList = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardListProps\n>(\n  (\n    {\n      className,\n      defaultExpanded = false,\n      visibleItems = 1.5,\n      collapsible = false,\n      dividers = true,\n      children,\n    },\n    ref,\n  ) => {\n    const [expanded, setExpanded] = React.useState(defaultExpanded);\n    const childrenArray = React.Children.toArray(children).filter((child) =>\n      React.isValidElement(child),\n    );\n    const itemCount = childrenArray.length;\n\n    const bounceTransition = {\n      type: \"spring\" as const,\n      stiffness: 400,\n      damping: 25,\n      mass: 1,\n    };\n\n    const collapsedHeight = visibleItems * ITEM_HEIGHT;\n    const expandedHeight = itemCount * ITEM_HEIGHT;\n\n    return (\n      <AnimatedUsageCardListContext.Provider\n        value={{\n          expanded,\n          setExpanded,\n          itemCount,\n          collapsedHeight,\n          expandedHeight,\n        }}\n      >\n        <motion.div\n          ref={ref}\n          className={cn(\"relative\", collapsible && \"pb-2\", className)}\n          layout\n          transition={bounceTransition}\n        >\n          <motion.div\n            className={cn(\n              \"overflow-hidden\",\n              dividers && \"divide-y divide-border\",\n            )}\n            initial={false}\n            animate={{\n              height:\n                collapsible && !expanded ? collapsedHeight : expandedHeight,\n            }}\n            transition={bounceTransition}\n          >\n            {childrenArray}\n          </motion.div>\n\n          {collapsible && (\n            <motion.div\n              className=\"pointer-events-none absolute inset-x-0 bottom-2 bg-linear-to-t from-card via-card/60 to-transparent\"\n              style={{ height: `${collapsedHeight * 0.6}px` }}\n              initial={false}\n              animate={{\n                opacity: expanded ? 0 : 1,\n              }}\n              transition={{\n                duration: 0.25,\n              }}\n            />\n          )}\n\n          {collapsible && itemCount > 1 && (\n            <motion.button\n              type=\"button\"\n              onClick={() => setExpanded(!expanded)}\n              className=\"absolute left-1/2 bottom-0 -translate-x-1/2 translate-y-1/2 z-10 flex h-7 w-7 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm hover:bg-muted hover:text-foreground transition-colors\"\n              aria-label={expanded ? \"Collapse\" : \"Expand\"}\n              whileHover={{ scale: 1.15 }}\n              whileTap={{ scale: 0.9 }}\n              transition={{\n                type: \"spring\",\n                stiffness: 500,\n                damping: 20,\n              }}\n              layout\n            >\n              <motion.div\n                initial={false}\n                animate={{ rotate: expanded ? 180 : 0 }}\n                transition={{\n                  type: \"spring\",\n                  stiffness: 400,\n                  damping: 20,\n                }}\n              >\n                <ChevronDown className=\"h-4 w-4\" />\n              </motion.div>\n            </motion.button>\n          )}\n        </motion.div>\n      </AnimatedUsageCardListContext.Provider>\n    );\n  },\n);\nAnimatedUsageCardList.displayName = \"AnimatedUsageCardList\";\n\ninterface AnimatedUsageCardItemProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  highlighted?: boolean;\n}\n\nconst AnimatedUsageCardItem = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardItemProps\n>(({ className, highlighted = false, children, ...props }, ref) => {\n  return (\n    <div\n      ref={ref}\n      className={cn(\n        \"flex items-center justify-between gap-4 px-4 py-3\",\n        highlighted && \"bg-muted/50\",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n});\nAnimatedUsageCardItem.displayName = \"AnimatedUsageCardItem\";\n\ninterface AnimatedUsageCardItemLabelProps\n  extends React.HTMLAttributes<HTMLSpanElement> {}\n\nconst AnimatedUsageCardItemLabel = React.forwardRef<\n  HTMLSpanElement,\n  AnimatedUsageCardItemLabelProps\n>(({ className, ...props }, ref) => (\n  <span\n    ref={ref}\n    className={cn(\"text-sm text-foreground truncate\", className)}\n    {...props}\n  />\n));\nAnimatedUsageCardItemLabel.displayName = \"AnimatedUsageCardItemLabel\";\n\ninterface AnimatedUsageCardItemValueProps\n  extends React.HTMLAttributes<HTMLSpanElement> {\n  amount?: number;\n  currency?: string;\n  unit?: string;\n}\n\nconst AnimatedUsageCardItemValue = React.forwardRef<\n  HTMLSpanElement,\n  AnimatedUsageCardItemValueProps\n>(({ className, amount, currency = \"$\", unit, children, ...props }, ref) => (\n  <span\n    ref={ref}\n    className={cn(\n      \"text-sm font-medium tabular-nums text-muted-foreground shrink-0\",\n      className,\n    )}\n    {...props}\n  >\n    {children ??\n      (amount !== undefined ? (\n        <>\n          {currency}\n          {amount.toLocaleString(\"en-US\", {\n            minimumFractionDigits: 2,\n            maximumFractionDigits: 2,\n          })}\n          {unit && <span className=\"text-xs ml-0.5\">{unit}</span>}\n        </>\n      ) : null)}\n  </span>\n));\nAnimatedUsageCardItemValue.displayName = \"AnimatedUsageCardItemValue\";\n\ninterface AnimatedUsageCardMeterProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  used: number;\n  limit: number;\n  label: string;\n  unit?: string;\n  showPercentage?: boolean;\n}\n\nconst AnimatedUsageCardMeter = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardMeterProps\n>(\n  (\n    { className, used, limit, label, unit, showPercentage = false, ...props },\n    ref,\n  ) => {\n    const percentage = Math.min((used / limit) * 100, 100);\n    const isWarning = percentage >= 80;\n    const isCritical = percentage >= 95;\n\n    const motionValue = useMotionValue(0);\n    const springValue = useSpring(motionValue, {\n      stiffness: 120,\n      damping: 12,\n      mass: 0.8,\n    });\n    const width = useTransform(springValue, (v) => `${v}%`);\n\n    React.useEffect(() => {\n      motionValue.set(percentage);\n    }, [percentage, motionValue]);\n\n    return (\n      <div\n        ref={ref}\n        className={cn(\"px-4 py-3 space-y-2\", className)}\n        {...props}\n      >\n        <div className=\"flex items-center justify-between gap-2\">\n          <span className=\"text-sm text-foreground\">{label}</span>\n          <span className=\"text-sm tabular-nums text-muted-foreground\">\n            <AnimatedNumber value={used} />\n            {unit && ` ${unit}`}\n            <span className=\"text-muted-foreground/60\">\n              {\" \"}\n              / {limit.toLocaleString()}\n              {unit && ` ${unit}`}\n            </span>\n            {showPercentage && (\n              <span\n                className={cn(\n                  \"ml-2\",\n                  isCritical\n                    ? \"text-destructive\"\n                    : isWarning\n                      ? \"text-amber-500\"\n                      : \"text-muted-foreground\",\n                )}\n              >\n                (<AnimatedNumber value={percentage} decimals={0} />\n                %)\n              </span>\n            )}\n          </span>\n        </div>\n        <div className=\"relative h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n          <motion.div\n            className={cn(\n              \"h-full rounded-full\",\n              isCritical\n                ? \"bg-destructive\"\n                : isWarning\n                  ? \"bg-amber-500\"\n                  : \"bg-primary\",\n            )}\n            style={{ width }}\n          />\n        </div>\n      </div>\n    );\n  },\n);\nAnimatedUsageCardMeter.displayName = \"AnimatedUsageCardMeter\";\n\ninterface AnimatedNumberProps {\n  value: number;\n  decimals?: number;\n}\n\nfunction AnimatedNumber({ value, decimals = 0 }: AnimatedNumberProps) {\n  const motionValue = useMotionValue(0);\n  const springValue = useSpring(motionValue, {\n    stiffness: 150,\n    damping: 15,\n    mass: 0.5,\n  });\n  const display = useTransform(springValue, (v) =>\n    decimals > 0 ? v.toFixed(decimals) : Math.round(v).toLocaleString(),\n  );\n\n  React.useEffect(() => {\n    motionValue.set(value);\n  }, [value, motionValue]);\n\n  return <motion.span>{display}</motion.span>;\n}\n\ninterface AnimatedUsageCardTotalProps {\n  className?: string;\n  label?: string;\n  amount: number;\n  currency?: string;\n}\n\nconst AnimatedUsageCardTotal = React.forwardRef<\n  HTMLDivElement,\n  AnimatedUsageCardTotalProps\n>(({ className, label = \"Total\", amount, currency = \"$\" }, ref) => {\n  const motionValue = useMotionValue(0);\n  const springValue = useSpring(motionValue, {\n    stiffness: 150,\n    damping: 15,\n    mass: 0.5,\n  });\n  const display = useTransform(springValue, (v) =>\n    v.toLocaleString(\"en-US\", {\n      minimumFractionDigits: 2,\n      maximumFractionDigits: 2,\n    }),\n  );\n\n  React.useEffect(() => {\n    motionValue.set(amount);\n  }, [amount, motionValue]);\n\n  return (\n    <motion.div\n      ref={ref}\n      className={cn(\n        \"flex items-center justify-between gap-4 px-4 py-3 bg-muted/30 border-t border-border\",\n        className,\n      )}\n      initial={{ opacity: 0, y: 10 }}\n      animate={{ opacity: 1, y: 0 }}\n      transition={{\n        type: \"spring\",\n        bounce: 0.4,\n        duration: 0.5,\n        delay: 0.1,\n      }}\n    >\n      <span className=\"text-sm font-medium text-foreground\">{label}</span>\n      <span className=\"text-sm font-semibold tabular-nums\">\n        {currency}\n        <motion.span>{display}</motion.span>\n      </span>\n    </motion.div>\n  );\n});\nAnimatedUsageCardTotal.displayName = \"AnimatedUsageCardTotal\";\n\nexport {\n  AnimatedUsageCard,\n  AnimatedUsageCardHeader,\n  AnimatedUsageCardPeriod,\n  AnimatedUsageCardAction,\n  AnimatedUsageCardSummary,\n  AnimatedUsageCardLabels,\n  AnimatedUsageCardLabel,\n  AnimatedUsageCardProgress,\n  AnimatedUsageCardList,\n  AnimatedUsageCardItem,\n  AnimatedUsageCardItemLabel,\n  AnimatedUsageCardItemValue,\n  AnimatedUsageCardMeter,\n  AnimatedUsageCardTotal,\n  animatedUsageCardVariants,\n  useAnimatedUsageCardList,\n};\n\nexport type {\n  AnimatedUsageCardProps,\n  AnimatedUsageCardHeaderProps,\n  AnimatedUsageCardPeriodProps,\n  AnimatedUsageCardActionProps,\n  AnimatedUsageCardSummaryProps,\n  AnimatedUsageCardLabelsProps,\n  AnimatedUsageCardLabelProps,\n  AnimatedUsageCardProgressProps,\n  AnimatedUsageCardListProps,\n  AnimatedUsageCardItemProps,\n  AnimatedUsageCardItemLabelProps,\n  AnimatedUsageCardItemValueProps,\n  AnimatedUsageCardMeterProps,\n  AnimatedUsageCardTotalProps,\n  AnimatedUsageCardListContextValue,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/animated-usage-card.tsx"
    },
    {
      "path": "src/registry/lib/utils.ts",
      "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n",
      "type": "registry:lib",
      "target": "lib/utils.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lucide-react",
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [
    "button"
  ]
}