{ "name": "avatar-group", "type": "registry:ui", "files": [ { "path": "ui/avatar-group.tsx", "type": "registry:ui", "content": "import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nconst avatarGroupVariants = cva(\"flex items-center\", {\n variants: {\n orientation: {\n horizontal: \"flex-row\",\n vertical: \"flex-col\",\n },\n dir: {\n ltr: \"\",\n rtl: \"\",\n },\n },\n compoundVariants: [\n {\n orientation: \"horizontal\",\n dir: \"ltr\",\n className: \"-space-x-1\",\n },\n {\n orientation: \"horizontal\",\n dir: \"rtl\",\n className: \"flex-row-reverse -space-x-1 space-x-reverse\",\n },\n {\n orientation: \"vertical\",\n dir: \"ltr\",\n className: \"-space-y-1\",\n },\n {\n orientation: \"vertical\",\n dir: \"rtl\",\n className: \"flex-col-reverse -space-y-1 space-y-reverse\",\n },\n ],\n defaultVariants: {\n orientation: \"horizontal\",\n dir: \"ltr\",\n },\n});\n\ninterface AvatarGroupProps\n extends Omit, \"dir\">,\n Omit, \"dir\">,\n useRender.ComponentProps<\"div\"> {\n dir?: \"ltr\" | \"rtl\";\n size?: number;\n max?: number;\n reverse?: boolean;\n renderOverflow?: (count: number) => React.ReactNode;\n}\n\nfunction AvatarGroup(props: AvatarGroupProps) {\n const {\n orientation = \"horizontal\",\n dir = \"ltr\",\n size = 40,\n max,\n reverse = false,\n renderOverflow,\n className,\n children,\n render,\n ...rootProps\n } = props;\n\n const childrenArray = React.Children.toArray(children).filter(\n React.isValidElement,\n );\n const itemCount = childrenArray.length;\n const shouldTruncate = max && itemCount > max;\n const visibleItems = shouldTruncate\n ? childrenArray.slice(0, max - 1)\n : childrenArray;\n const overflowCount = shouldTruncate ? itemCount - (max - 1) : 0;\n const totalRenderedItems = shouldTruncate ? max : itemCount;\n\n return useRender({\n defaultTagName: \"div\",\n props: mergeProps<\"div\">(\n {\n className: cn(avatarGroupVariants({ orientation, dir, className })),\n children: (\n <>\n {visibleItems.map((child, index) => (\n \n ))}\n {shouldTruncate && (\n \n +{overflowCount}\n \n )\n }\n index={visibleItems.length}\n itemCount={totalRenderedItems}\n orientation={orientation}\n dir={dir}\n size={size}\n reverse={reverse}\n />\n )}\n \n ),\n },\n rootProps,\n ),\n render,\n state: {\n slot: \"avatar-group\",\n orientation,\n },\n });\n}\n\ninterface AvatarGroupItemProps\n extends Omit, \"dir\">,\n VariantProps {\n child: React.ReactNode;\n index: number;\n itemCount: number;\n size: number;\n reverse: boolean;\n}\n\nfunction AvatarGroupItem(props: AvatarGroupItemProps) {\n const {\n child,\n index,\n size,\n orientation,\n dir = \"ltr\",\n reverse = false,\n itemCount,\n className,\n style,\n ...itemProps\n } = props;\n\n const maskStyle = React.useMemo(() => {\n let maskImage = \"\";\n\n let shouldMask = false;\n\n if (orientation === \"vertical\" && dir === \"rtl\" && reverse) {\n shouldMask = index !== itemCount - 1;\n } else {\n shouldMask = reverse ? index < itemCount - 1 : index > 0;\n }\n\n if (shouldMask) {\n const maskRadius = size / 2;\n const maskOffset = size / 4 + size / 10;\n\n if (orientation === \"vertical\") {\n if (dir === \"ltr\") {\n if (reverse) {\n maskImage = `radial-gradient(circle ${maskRadius}px at 50% ${size + maskOffset}px, transparent 99%, white 100%)`;\n } else {\n maskImage = `radial-gradient(circle ${maskRadius}px at 50% -${maskOffset}px, transparent 99%, white 100%)`;\n }\n } else {\n if (reverse) {\n maskImage = `radial-gradient(circle ${maskRadius}px at 50% -${maskOffset}px, transparent 99%, white 100%)`;\n } else {\n maskImage = `radial-gradient(circle ${maskRadius}px at 50% ${size + maskOffset}px, transparent 99%, white 100%)`;\n }\n }\n } else {\n if (dir === \"ltr\") {\n if (reverse) {\n maskImage = `radial-gradient(circle ${maskRadius}px at ${size + maskOffset}px 50%, transparent 99%, white 100%)`;\n } else {\n maskImage = `radial-gradient(circle ${maskRadius}px at -${maskOffset}px 50%, transparent 99%, white 100%)`;\n }\n } else {\n if (reverse) {\n maskImage = `radial-gradient(circle ${maskRadius}px at -${maskOffset}px 50%, transparent 99%, white 100%)`;\n } else {\n maskImage = `radial-gradient(circle ${maskRadius}px at ${size + maskOffset}px 50%, transparent 99%, white 100%)`;\n }\n }\n }\n }\n\n return {\n width: size,\n height: size,\n maskImage,\n };\n }, [size, index, orientation, dir, reverse, itemCount]);\n\n if (!React.isValidElement>(child)) {\n return child;\n }\n\n return React.cloneElement(child, {\n ...itemProps,\n className: cn(\n \"size-full shrink-0 overflow-hidden rounded-full [&_img]:size-full\",\n className,\n child.props.className,\n ),\n style: {\n ...maskStyle,\n ...style,\n ...child.props.style,\n },\n });\n}\n\nexport { AvatarGroup };\n", "target": "" } ], "dependencies": [ "@base-ui/react" ] }