import { forwardRef, useMemo } from 'react' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import Block from '../Primitives/Block/Block' import Text from '../Text/Text' import { filterBlockedProps } from '../../utils/prop-helpers' import { BadgeColor, BadgeProps, BadgeSize } from './Badge.types' import { BadgeTokensType } from './badge.tokens' import { formatCount, getAccessibleLabel, getPositionStyles, } from './badge.utils' const BadgeContent = forwardRef< HTMLSpanElement, Omit >( ( { count, maxCount = 99, size = BadgeSize.MD, color = BadgeColor.ALERT, text, showBadge = true, showZero = false, ...rest }, ref ) => { const tokens = useResponsiveTokens('BADGE') const filteredRest = filterBlockedProps(rest) // Hide badge when count is 0 and showZero is false const effectiveShowBadge = useMemo(() => { if (!showBadge) return false if (count === 0 && !showZero) return false return true }, [showBadge, count, showZero]) const displayText = useMemo(() => { if (text) return text if (count !== undefined) return formatCount(count, maxCount) return '' }, [count, maxCount, text]) // Determine if this should be a dot (no content) or pill (has content) const hasContent = displayText !== '' if (!effectiveShowBadge) return null const ariaLabel = getAccessibleLabel( count, text, maxCount, effectiveShowBadge ) if (!hasContent) { return ( } as="span" role="status" aria-label={ariaLabel} data-badge="dot" display="inline-block" width={tokens.dot.width[size]} height={tokens.dot.height[size]} backgroundColor={tokens.backgroundColor[color]} borderRadius={tokens.dot.borderRadius} {...filteredRest} /> ) } return ( } as="span" role="status" aria-label={ariaLabel} data-badge="pill" data-content={displayText} display="inline-flex" alignItems="center" justifyContent="center" minWidth={tokens.pill.minWidth[size]} height={tokens.pill.height[size]} paddingLeft={tokens.pill.paddingLeft[size]} paddingRight={tokens.pill.paddingRight[size]} backgroundColor={tokens.backgroundColor[color]} borderRadius={tokens.pill.borderRadius[size]} {...filteredRest} > {displayText} ) } ) BadgeContent.displayName = 'BadgeContent' const Badge = forwardRef((props, ref) => { const tokens = useResponsiveTokens('BADGE') // If no children, render standalone badge if (props.children === undefined) { const { size = BadgeSize.MD, ...rest } = props const filteredRest = filterBlockedProps(rest) return } // If children provided, wrap with relative positioning const { children, size = BadgeSize.MD, position = 'top-right', offset, isCircular = false, ...rest } = props const filteredRest = filterBlockedProps(rest) // Determine if this should be a dot (no content) or pill (has content) for positioning const hasContent = filteredRest.count !== undefined || (filteredRest.text !== undefined && filteredRest.text !== '') const positionStyles = getPositionStyles( position, size, tokens, offset, hasContent, isCircular ) return ( {children} ) }) Badge.displayName = 'Badge' export default Badge