{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "user-presence-avatar", "type": "registry:ui", "title": "User Presence Avatar", "description": "User Presence Avatar Component", "dependencies": [ "motion" ], "registryDependencies": [ "https://animate-ui.com/r/avatar-group" ], "files": [ { "path": "registry/ui-elements/user-presence-avatar/index.tsx", "content": "'use client';\n\nimport * as React from 'react';\nimport { motion, LayoutGroup } from 'motion/react';\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n} from '@/components/ui/avatar';\nimport {\n AvatarGroup,\n AvatarGroupTooltip,\n} from '@/components/animate-ui/components/avatar-group';\nimport { cn } from '@/lib/utils';\n\nconst USERS = [\n {\n id: 1,\n src: 'https://pbs.twimg.com/profile_images/1897311929028255744/otxpL-ke_400x400.jpg',\n fallback: 'AK',\n tooltip: 'Arhamkhnz',\n online: true,\n },\n {\n id: 2,\n src: 'https://pbs.twimg.com/profile_images/1909615404789506048/MTqvRsjo_400x400.jpg',\n fallback: 'SK',\n tooltip: 'Skyleen',\n online: true,\n },\n {\n id: 3,\n src: 'https://pbs.twimg.com/profile_images/1593304942210478080/TUYae5z7_400x400.jpg',\n fallback: 'CN',\n tooltip: 'Shadcn',\n online: true,\n },\n {\n id: 4,\n src: 'https://pbs.twimg.com/profile_images/1677042510839857154/Kq4tpySA_400x400.jpg',\n fallback: 'AW',\n tooltip: 'Adam Wathan',\n online: false,\n },\n {\n id: 5,\n src: 'https://pbs.twimg.com/profile_images/1783856060249595904/8TfcCN0r_400x400.jpg',\n fallback: 'GR',\n tooltip: 'Guillermo Rauch',\n online: false,\n },\n {\n id: 6,\n src: 'https://pbs.twimg.com/profile_images/1534700564810018816/anAuSfkp_400x400.jpg',\n fallback: 'JH',\n tooltip: 'Jhey',\n online: false,\n },\n];\n\nconst AVATAR_MOTION_TRANSITION = {\n type: 'spring',\n stiffness: 200,\n damping: 25,\n} as const;\n\nconst GROUP_CONTAINER_TRANSITION = {\n type: 'spring',\n stiffness: 150,\n damping: 20,\n} as const;\n\nfunction UserPresenceAvatar() {\n const [users, setUsers] = React.useState(USERS);\n const [togglingGroup, setTogglingGroup] = React.useState<\n 'online' | 'offline' | null\n >(null);\n\n const online = users.filter((u) => u.online);\n const offline = users.filter((u) => !u.online);\n\n const toggleStatus = (id: number) => {\n const user = users.find((u) => u.id === id);\n if (!user) return;\n\n setTogglingGroup(user.online ? 'online' : 'offline');\n setUsers((prev) => {\n const idx = prev.findIndex((u) => u.id === id);\n if (idx === -1) return prev;\n const updated = [...prev];\n const target = updated[idx];\n if (!target) return prev;\n updated.splice(idx, 1);\n updated.push({ ...target, online: !target.online });\n return updated;\n });\n // Reset group z-index after the animation duration (keep in sync with animation timing)\n setTimeout(() => setTogglingGroup(null), 500);\n };\n\n return (\n
\n \n {online.length > 0 && (\n \n u.id).join('_') + '-online'}\n translate=\"0\"\n className=\"h-12 -space-x-3\"\n tooltipProps={{ side: 'top', sideOffset: 14 }}\n >\n {online.map((user) => (\n toggleStatus(user.id)}\n animate={{\n filter: 'grayscale(0)',\n scale: 1,\n }}\n transition={AVATAR_MOTION_TRANSITION}\n title=\"Click to go offline\"\n initial={false}\n >\n \n \n {user.fallback}\n \n

{user.tooltip}

\n
\n
\n \n ))}\n \n \n )}\n\n {offline.length > 0 && (\n \n u.id).join('_') + '-offline'}\n translate=\"0\"\n className=\"h-12 -space-x-3\"\n tooltipProps={{ side: 'top', sideOffset: 14 }}\n >\n {offline.map((user) => (\n toggleStatus(user.id)}\n animate={{\n filter: 'grayscale(1)',\n scale: 1,\n }}\n transition={AVATAR_MOTION_TRANSITION}\n title=\"Click to go online\"\n initial={false}\n >\n \n \n {user.fallback}\n \n

{user.tooltip}

\n
\n
\n \n ))}\n \n \n )}\n
\n
\n );\n}\n\nexport { UserPresenceAvatar };\n", "type": "registry:ui", "target": "components/animate-ui/ui-elements/user-presence-avatar.tsx" } ] }