import React, { type ReactElement } from 'react'; import { cn } from '../../utils/cn'; import { type RadiusVariantKey } from '../../utils/variant-common'; import { Avatar, type AvatarProps } from './avatar'; export interface AvatarGroupProps extends React.ComponentPropsWithoutRef<'div'> { max?: number; total?: number; formatTotal?: (v: number) => React.ReactNode; size?: NonNullable; radius?: RadiusVariantKey; } const mappingSpacing: Record, number> = { xs: 4, sm: 8, md: 12, lg: 16, xl: 20, '2xl': 24, '3xl': 28, '4xl': 32, '5xl': 36, }; const AvatarGroup = React.forwardRef, AvatarGroupProps>((props, ref) => { const { className, size = 'md', radius = 'sm', children: childrenProp, formatTotal, total, max = 5, ...etc } = props; const children = React.Children.toArray(childrenProp).filter((child) => { return React.isValidElement(child); }) as unknown as ReactElement[]; let clampedMax = max < 2 ? 2 : max; const totalAvatars = total || children.length; if (totalAvatars === clampedMax) { clampedMax += 1; } clampedMax = Math.min(totalAvatars + 1, clampedMax); const maxAvatars = Math.min(children.length, clampedMax - 1); const extraAvatars = Math.max(totalAvatars - clampedMax, totalAvatars - maxAvatars, 0); const extraAvatarsElement = formatTotal ? formatTotal(extraAvatars) : `+${extraAvatars}`; const marginLeft = -mappingSpacing[size]; return (
{children .slice(0, maxAvatars) .reverse() .map((child, index) => { return React.cloneElement(child, { ...child.props, size, outlined: true, style: { marginLeft: index === 0 ? undefined : marginLeft, ...child.props.style, }, }); })} {extraAvatarsElement}
); }); AvatarGroup.displayName = 'AvatarGroup'; export { AvatarGroup };