{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar",
  "title": "Avatar",
  "description": "An image element with a fallback for representing the user.",
  "registryDependencies": [],
  "files": [
    {
      "path": "ui/avatar.tsx",
      "content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n  Children,\n  cloneElement,\n  createContext,\n  isValidElement,\n  useCallback,\n  useContext,\n  useMemo,\n  useState,\n} from \"react\";\nimport { Image, type ImageProps, Text, View } from \"react-native\";\nimport { withUniwind } from \"uniwind\";\nimport { cn } from \"@/lib/utils\";\n\n// Types\ntype AvatarSize = NonNullable<VariantProps<typeof avatarVariants>[\"size\"]>;\ntype AvatarImageStatus = \"idle\" | \"loading\" | \"loaded\" | \"error\";\n\ntype AvatarImageKeyStatus = {\n  status: AvatarImageStatus;\n  srcKey: string;\n};\n\ntype AvatarContextValue = {\n  size: AvatarSize;\n  imageKeyStatus: AvatarImageKeyStatus;\n  setImageKeyStatus: (status: AvatarImageKeyStatus) => void;\n};\n\ntype AvatarProps = React.ComponentProps<typeof View> &\n  VariantProps<typeof avatarVariants> & {\n    children?: React.ReactNode;\n  };\n\ntype AvatarImageProps = ImageProps;\n\ntype AvatarFallbackProps = React.ComponentProps<typeof View> & {\n  children?: React.ReactNode;\n};\n\ntype AvatarBadgeProps = React.ComponentProps<typeof View> & {\n  children?: React.ReactNode;\n};\n\ntype ClassNameElement = React.ReactElement<{ className?: string }>;\n\n// Context\nconst AvatarContext = createContext<AvatarContextValue | null>(null);\n\nconst useAvatarContext = () => {\n  const context = useContext(AvatarContext);\n  if (!context) {\n    throw new Error(\"Avatar components must be used within an Avatar\");\n  }\n  return context;\n};\n\nconst StyledImage = withUniwind(Image);\n\n// Components\nexport const Avatar = ({\n  children,\n  className,\n  size = \"default\",\n  ...props\n}: AvatarProps) => {\n  const [imageKeyStatus, _setImageKeyStatus] = useState<AvatarImageKeyStatus>({\n    srcKey: \"\",\n    status: \"idle\",\n  });\n\n  const setImageKeyStatus = useCallback((payload: AvatarImageKeyStatus) => {\n    _setImageKeyStatus((prev) => {\n      const isSameKey = payload.srcKey === prev.srcKey;\n\n      if (prev.status === \"loaded\" && isSameKey) {\n        return prev;\n      }\n\n      return payload;\n    });\n  }, []);\n\n  const ctx = useMemo(\n    () => ({\n      imageKeyStatus,\n      setImageKeyStatus,\n      size: size ?? \"default\",\n    }),\n    [imageKeyStatus, size, setImageKeyStatus]\n  );\n\n  return (\n    <AvatarContext.Provider value={ctx}>\n      <View\n        className={cn(avatarVariants({ className, size }))}\n        data-slot=\"avatar\"\n        {...props}\n      >\n        {children}\n      </View>\n    </AvatarContext.Provider>\n  );\n};\n\nexport const AvatarImage = ({\n  alt,\n  className,\n  onError,\n  onLoad,\n  onLoadStart,\n  source,\n  src,\n  ...props\n}: AvatarImageProps) => {\n  const { setImageKeyStatus } = useAvatarContext();\n\n  const resolvedSource = source ?? (src ? { uri: src } : undefined);\n  const resolvedSourceKey = JSON.stringify(resolvedSource);\n\n  if (!resolvedSource) {\n    return null;\n  }\n\n  return (\n    <StyledImage\n      accessibilityLabel={alt}\n      className={cn(\"aspect-square size-full rounded-full\", className)}\n      data-slot=\"avatar-image\"\n      onError={(event) => {\n        setImageKeyStatus({ srcKey: resolvedSourceKey, status: \"error\" });\n        onError?.(event);\n      }}\n      onLoad={(event) => {\n        setImageKeyStatus({ srcKey: resolvedSourceKey, status: \"loaded\" });\n        onLoad?.(event);\n      }}\n      onLoadStart={() => {\n        setImageKeyStatus({ srcKey: resolvedSourceKey, status: \"loading\" });\n        onLoadStart?.();\n      }}\n      source={resolvedSource}\n      {...props}\n    />\n  );\n};\n\nexport const AvatarFallback = ({\n  children,\n  className,\n  ...props\n}: AvatarFallbackProps) => {\n  const { imageKeyStatus, size } = useAvatarContext();\n\n  if (imageKeyStatus.status === \"loaded\") {\n    return null;\n  }\n\n  return (\n    <View\n      className={cn(\n        \"absolute inset-0 flex size-full items-center justify-center rounded-full bg-muted\",\n        className\n      )}\n      data-slot=\"avatar-fallback\"\n      {...props}\n    >\n      {Children.map(children, (child) => {\n        if (typeof child === \"string\") {\n          return (\n            <Text\n              className={cn(\n                \"font-medium text-muted-foreground\",\n                size === \"sm\" ? \"text-xs\" : \"text-sm\"\n              )}\n            >\n              {child}\n            </Text>\n          );\n        }\n\n        return child;\n      })}\n    </View>\n  );\n};\n\nexport const AvatarBadge = ({\n  children,\n  className,\n  ...props\n}: AvatarBadgeProps) => {\n  const { size } = useAvatarContext();\n\n  return (\n    <View\n      className={cn(avatarBadgeVariants({ className, size }))}\n      data-slot=\"avatar-badge\"\n      {...props}\n    >\n      {Children.map(children, (child) => {\n        if (!isValidElement(child)) {\n          return child;\n        }\n\n        const element = child as ClassNameElement;\n\n        return cloneElement(element, {\n          className: cn(\n            size === \"sm\" ? \"size-1.5\" : \"size-2\",\n            \"text-primary-foreground\",\n            element.props.className\n          ),\n        });\n      })}\n    </View>\n  );\n};\n\nexport const AvatarGroup = ({\n  children,\n  className,\n  ...props\n}: React.ComponentProps<typeof View>) => {\n  return (\n    <View\n      className={cn(\"flex flex-row items-center\", className)}\n      data-slot=\"avatar-group\"\n      {...props}\n    >\n      {Children.map(children, (child, index) => {\n        if (!isValidElement(child)) {\n          return child;\n        }\n\n        const element = child as ClassNameElement;\n        const overlapClassName = index > 0 ? \"-ml-2\" : undefined;\n\n        return cloneElement(element, {\n          className: cn(\n            overlapClassName,\n            \"border-2 border-background\",\n            element.props.className\n          ),\n        });\n      })}\n    </View>\n  );\n};\n\nexport const AvatarGroupCount = ({\n  children,\n  className,\n  ...props\n}: React.ComponentProps<typeof View> & {\n  children?: React.ReactNode;\n}) => {\n  return (\n    <View\n      className={cn(\n        \"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted\",\n        className\n      )}\n      data-slot=\"avatar-group-count\"\n      {...props}\n    >\n      {Children.map(children, (child) => {\n        if (typeof child === \"string\") {\n          return (\n            <Text className=\"font-medium text-muted-foreground text-sm\">\n              {child}\n            </Text>\n          );\n        }\n\n        return child;\n      })}\n    </View>\n  );\n};\n\n// Styles\nconst avatarVariants = cva(\n  \"relative flex shrink-0 items-center justify-center rounded-full border border-border\",\n  {\n    defaultVariants: {\n      size: \"default\",\n    },\n    variants: {\n      size: {\n        default: \"size-8\",\n        lg: \"size-10\",\n        sm: \"size-6\",\n      },\n    },\n  }\n);\n\nconst avatarBadgeVariants = cva(\n  \"absolute z-10 flex items-center justify-center rounded-full border-2 border-background bg-primary\",\n  {\n    defaultVariants: {\n      size: \"default\",\n    },\n    variants: {\n      size: {\n        default: \"right-0 bottom-0 size-2.5\",\n        lg: \"right-0 bottom-0 size-3\",\n        sm: \"right-0 bottom-0 size-2\",\n      },\n    },\n  }\n);\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
