{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "files": [
    {
      "content": "export * from \"./avatar\";\nexport * as Avatar from \"./namespace\";\nexport * from \"./styles\";\n",
      "path": "src/registry/ui/avatar/index.ts",
      "type": "registry:ui"
    },
    {
      "content": "\"use client\";\n\nimport { createContext, use, useLayoutEffect, useState } from \"react\";\nimport { AvatarStyles } from \"./styles\";\n\ntype Status = \"idle\" | \"loading\" | \"success\" | \"error\";\nconst AvatarContext = createContext<{\n  status: Status;\n  onStatusChange: (status: Status) => void;\n} | null>(null);\n\nfunction useAvatarContext() {\n  const context = use(AvatarContext);\n  if (!context) {\n    throw new Error(\"Avatar components must be rendered within the AvatarRoot\");\n  }\n  return context;\n}\n\nexport interface AvatarRootProps extends React.ComponentProps<\"span\"> {}\n\nexport function AvatarRoot({ className, ...props }: AvatarRootProps) {\n  const [status, setStatus] = useState<Status>(\"idle\");\n\n  return (\n    <AvatarContext value={{ onStatusChange: setStatus, status }}>\n      <span\n        className={AvatarStyles.Root({ className })}\n        data-slot=\"avatar-root\"\n        data-status={status}\n        {...props}\n      />\n    </AvatarContext>\n  );\n}\n\nfunction useImageLoadingStatus(src?: string) {\n  const [status, setStatus] = useState<Status>(\"idle\");\n\n  useLayoutEffect(() => {\n    if (!src) {\n      setStatus(\"error\");\n      return;\n    }\n    let isMounted = true;\n    const image = new window.Image();\n    const updateStatus = (status: Status) => () => {\n      if (!isMounted) {\n        return;\n      }\n      setStatus(status);\n    };\n    setStatus(\"loading\");\n    image.onload = updateStatus(\"success\");\n    image.onerror = updateStatus(\"error\");\n    image.src = src;\n    return () => {\n      isMounted = false;\n    };\n  }, [src]);\n\n  return status;\n}\n\nexport interface AvatarImageProps extends React.ComponentProps<\"img\"> {\n  src?: string;\n}\n\nexport function AvatarImage({\n  className,\n  src,\n  width,\n  height,\n  ...props\n}: AvatarImageProps) {\n  const context = useAvatarContext();\n  const status = useImageLoadingStatus(src);\n\n  useLayoutEffect(() => {\n    if (status !== \"idle\") {\n      context.onStatusChange(status);\n    }\n  }, [status, context.onStatusChange]);\n\n  if (status !== \"success\") {\n    return null;\n  }\n\n  return (\n    <img\n      {...props}\n      alt={props.alt}\n      className={AvatarStyles.Image({ className })}\n      data-slot=\"avatar-image\"\n      height={height}\n      src={src}\n      width={width}\n    />\n  );\n}\n\nexport interface AvatarFallbackProps extends React.ComponentProps<\"span\"> {}\n\nexport function AvatarFallback({ className, ...props }: AvatarFallbackProps) {\n  const { status } = useAvatarContext();\n\n  return (\n    <span\n      {...props}\n      aria-hidden={status !== \"error\"}\n      className={AvatarStyles.Fallback({ className })}\n      data-slot=\"avatar-fallback\"\n    />\n  );\n}\n\nexport interface AvatarPlaceholderProps extends React.ComponentProps<\"span\"> {}\n\nexport function AvatarPlaceholder({\n  className,\n  ...props\n}: AvatarPlaceholderProps) {\n  const { status } = useAvatarContext();\n\n  if (status !== \"idle\" && status !== \"loading\") {\n    return null;\n  }\n\n  return (\n    <span\n      {...props}\n      className={AvatarStyles.Placeholder({ className })}\n      data-slot=\"avatar-placeholder\"\n    />\n  );\n}\n",
      "path": "src/registry/ui/avatar/avatar.tsx",
      "type": "registry:ui"
    },
    {
      "content": "import { cva } from \"~/registry/lib/cva\";\n\nexport const AvatarStyles = {\n  Fallback: cva({\n    base: [\n      \"col-start-1 row-start-1 size-full select-none place-content-center rounded-full bg-muted\",\n      \"grid group-data-[status=success]:hidden\",\n    ],\n  }),\n  Image: cva({\n    base: [\"aspect-square size-full\"],\n  }),\n  Placeholder: cva({\n    base: [\n      \"col-start-1 row-start-1 grid size-full animate-pulse place-content-center bg-muted\",\n    ],\n  }),\n  Root: cva({\n    base: [\"group relative grid size-8 shrink-0 overflow-hidden rounded-full\"],\n  }),\n};\n",
      "path": "src/registry/ui/avatar/styles.ts",
      "type": "registry:ui"
    },
    {
      "content": "export {\n  AvatarFallback as Fallback,\n  AvatarImage as Image,\n  AvatarPlaceholder as Placeholder,\n  AvatarRoot as Root,\n} from \"./avatar\";\n",
      "path": "src/registry/ui/avatar/namespace.ts",
      "type": "registry:ui"
    }
  ],
  "name": "avatar",
  "registryDependencies": [
    "@kanpeki/cva",
    "@kanpeki/custom-plugin"
  ],
  "type": "registry:ui"
}