// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file AvatarGroupOverflow.tsx * @input Uses React, StyleX, AvatarGroupContext, i18n (useTranslator) * @output Exports AvatarGroupOverflow for overflow indicator * @position Slot component used inside AvatarGroup * * SYNC: When modified, update these files to stay in sync: * - /packages/core/src/AvatarGroup/AvatarGroup.doc.mjs * - /packages/core/src/AvatarGroup/index.ts * - /apps/storybook/stories/AvatarGroupOverflow.stories.tsx * - /packages/cli/assets/templates/blocks/components/AvatarGroup/ (showcase blocks) */ import React, { type ReactNode } from 'react'; import * as stylex from "../style-runtime.js"; import "../theme/tokens.stylex.js"; import { colorVars, typographyVars, typeScaleVars, fontWeightVars, spacingVars } from "../theme/tokens.stylex.js"; import { shapeStyles } from "../Avatar/Avatar.js"; import { mergeProps } from "../utils/index.js"; import { resolveSize } from "../Avatar/index.js"; import { useAvatarGroup } from "./AvatarGroupContext.js"; import type { BaseProps } from "../BaseProps.js"; import { themeProps } from "../utils/themeProps.js"; import { focusOutlineProps } from "../utils/focusOutline.stylex.js"; import { interactionOverlayStyles } from "../utils/interactionOverlay.stylex.js"; import { useTranslator } from "../i18n/index.js"; const BORDER_WIDTH = 2; const OVERFLOW_FONT_RATIO = 0.35; export interface AvatarGroupOverflowProps extends Omit, 'onClick'> { ref?: React.Ref; /** * The overflow count to display. */ count: number; /** * Callback fired when the overflow indicator is clicked. * When provided, the indicator renders as a focusable button. */ onClick?: () => void; /** * Custom content to render instead of the default "+N" label. */ children?: ReactNode; } const styles = { base: { kVAEAm: "x1n2onr6", k1xSpc: "x3nfvp2", kGNEyG: "x6s0dn4", kjj79g: "xl56j7k", kaIpWk: "xbc78cj", kWkggS: "x10xzikg", kMwMTN: "xv1l7n4", kMv6JI: "x9ynric", k63SB2: "x1e4wzip", kfSwDN: "x87ps6o", kMzoRj: "xdh2fpr", ksu8eU: "x1y0btm7", kVAM5u: "x1touxvs", kB7OPa: "x9f619", kg3NbH: "xf314gf", kKwaWg: "x14bno8m", $$css: true as const }, button: { kkrTdU: "x1ypdohk x16khyan", k8WAf4: "xt970qd", $$css: true as const }, overlap: { keTefX: "x1sik18w", $$css: true as const } }; const _temp = { k7Eaqz: "xkj4a21", kZKoxP: "x16ye13r", "$$css": true }; const _temp2 = { kGuDYH: "xdmh292", "$$css": true }; const _temp3 = { "--_avatar-group-overlap": "xlz5hwt", "$$css": true }; const dynamicStyles = { size: (s: number) => [_temp, { "--x-minWidth": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(s + BORDER_WIDTH * 2), "--x-height": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(s + BORDER_WIDTH * 2) }], fontSize: (s: number) => [_temp2, { "--x-fontSize": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(`max(${typeScaleVars['--text-supporting-size']}, ${s * OVERFLOW_FONT_RATIO}px)`) }], overlap: (offset: number) => [_temp3, { "--x---_avatar-group-overlap": `${offset}px` != null ? `${offset}px` : undefined }] }; /** * Overflow indicator for AvatarGroup. Shows a "+N" count and * optionally handles clicks. * * @example * ``` * * {users.slice(0, 3).map(u => ( * * ))} * * * ``` */ export function AvatarGroupOverflow({ ref, count, onClick, children, xstyle, className, style, ...rest }: AvatarGroupOverflowProps): ReactNode { const t = useTranslator(); const group = useAvatarGroup(); const size = group?.size ?? 'md'; const shape = group?.shape ?? 'circle'; const numericSize = group?.numericSize ?? resolveSize('md'); const overlap = group?.overlap ?? 0; // count is a plain number, and the documented shape for it is // `total - visibleCount`, which goes negative whenever the list is shorter // than the slice. Clamping keeps that from rendering "+-3". const safeCount = Math.max(0, count); const label = t('@astryx.avatarGroup.overflow', { count: safeCount }); const content = children ?? `+${safeCount}`; if (onClick) { return ; } return {content} ; } AvatarGroupOverflow.displayName = 'AvatarGroupOverflow';