{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar-group",
  "title": "Avatar Group",
  "description": "Overlapping avatars that fan apart on hover, with a +N overflow chip.",
  "dependencies": ["framer-motion"],
  "registryDependencies": ["@godui/godui-theme"],
  "files": [
    {
      "path": "packages/components/src/avatar-group/avatar-group.tsx",
      "content": "\"use client\";\n\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport * as React from \"react\";\n\nexport type Avatar = {\n  src?: string;\n  alt?: string;\n  /** Fallback initials shown when no `src` (or it fails to load). */\n  fallback?: string;\n  href?: string;\n};\n\nexport type AvatarGroupSize = \"sm\" | \"md\" | \"lg\";\n\nexport type AvatarGroupProps = React.HTMLAttributes<HTMLDivElement> & {\n  avatars: Avatar[];\n  /** Maximum avatars shown before collapsing into a `+N` chip. */\n  max?: number;\n  size?: AvatarGroupSize;\n  /** Spread the stack apart on hover. */\n  spreadOnHover?: boolean;\n};\n\nconst sizeClasses: Record<AvatarGroupSize, string> = {\n  sm: \"size-8 text-xs\",\n  md: \"size-10 text-sm\",\n  lg: \"size-12 text-base\",\n};\n\nconst overlap: Record<AvatarGroupSize, number> = {\n  sm: 10,\n  md: 12,\n  lg: 14,\n};\n\nfunction Initials({ avatar }: { avatar: Avatar }) {\n  const [errored, setErrored] = React.useState(false);\n  const label = avatar.fallback ?? avatar.alt?.slice(0, 2).toUpperCase() ?? \"?\";\n  if (avatar.src && !errored) {\n    return (\n      <img\n        src={avatar.src}\n        alt={avatar.alt ?? \"\"}\n        onError={() => setErrored(true)}\n        className=\"size-full object-cover\"\n      />\n    );\n  }\n  return (\n    <span className=\"flex size-full items-center justify-center bg-muted font-medium text-muted-foreground\">\n      {label}\n    </span>\n  );\n}\n\nconst AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(\n  (\n    {\n      avatars,\n      max = 4,\n      size = \"md\",\n      spreadOnHover = true,\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const reduceMotion = useReducedMotion();\n    const visible = avatars.slice(0, max);\n    const overflow = avatars.length - visible.length;\n    const margin = -overlap[size];\n\n    return (\n      <motion.div\n        ref={ref}\n        initial=\"rest\"\n        whileHover={spreadOnHover && !reduceMotion ? \"spread\" : undefined}\n        animate=\"rest\"\n        className={`group flex items-center ${className ?? \"\"}`}\n        {...(props as React.ComponentProps<typeof motion.div>)}\n      >\n        {visible.map((avatar, i) => {\n          const ringClasses =\n            \"ring-2 ring-background rounded-full overflow-hidden bg-background shadow-sm\";\n          const inner = (\n            // biome-ignore lint/correctness/useJsxKeyInIterable: keyed on the wrapper element returned below\n            <motion.div\n              variants={{\n                rest: { marginLeft: i === 0 ? 0 : margin, y: 0 },\n                spread: { marginLeft: i === 0 ? 0 : 4, y: -2 },\n              }}\n              transition={{ type: \"spring\", stiffness: 520, damping: 32 }}\n              whileHover={reduceMotion ? undefined : { y: -6, scale: 1.06 }}\n              style={{ zIndex: i }}\n              className={`relative ${sizeClasses[size]} ${ringClasses}`}\n            >\n              <Initials avatar={avatar} />\n            </motion.div>\n          );\n          return avatar.href ? (\n            <a\n              // biome-ignore lint/suspicious/noArrayIndexKey: stable avatar order\n              key={i}\n              href={avatar.href}\n              aria-label={avatar.alt}\n              className=\"inline-flex focus:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-full\"\n            >\n              {inner}\n            </a>\n          ) : (\n            // biome-ignore lint/suspicious/noArrayIndexKey: stable avatar order\n            <React.Fragment key={i}>{inner}</React.Fragment>\n          );\n        })}\n\n        {overflow > 0 && (\n          <motion.div\n            variants={{\n              rest: { marginLeft: margin },\n              spread: { marginLeft: 4 },\n            }}\n            transition={{ type: \"spring\", stiffness: 520, damping: 32 }}\n            style={{ zIndex: visible.length }}\n            className={`relative flex items-center justify-center rounded-full bg-muted font-medium text-muted-foreground ring-2 ring-background ${sizeClasses[size]}`}\n          >\n            +{overflow}\n          </motion.div>\n        )}\n      </motion.div>\n    );\n  },\n);\nAvatarGroup.displayName = \"AvatarGroup\";\n\nexport { AvatarGroup };\n",
      "type": "registry:ui",
      "target": "components/godui/avatar-group.tsx"
    }
  ],
  "type": "registry:ui"
}
