import { forwardRef, useId, useMemo } from 'react' import styled, { css } from 'styled-components' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import { filterBlockedProps } from '../../utils/prop-helpers' import Block from '../Primitives/Block/Block' import type { CardV2TokensType } from './cardV2.tokens' import { CardV2Context } from './CardV2Context' import { CardV2Actions, CardV2Body, CardV2Content, CardV2Footer, CardV2Header, CardV2Media, CardV2Meta, } from './CardV2Components' import CardV2Skeleton from './CardV2Skeleton' import { CardV2ActionPlacement, CardV2Orientation, CardV2Padding, CardV2Variant, type CardV2Props, } from './cardV2.types' import { getCardV2Padding, isTextPrimitive, toCardV2ActionArray, } from './CardV2.utils' const StyledCard = styled(Block)<{ $tokens: CardV2TokensType $interactive: boolean $selected: boolean }>` transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; ${({ $tokens, $interactive }) => $interactive && css` cursor: pointer; &:hover { border: ${$tokens.state.hover.border}; box-shadow: ${$tokens.state.hover.boxShadow}; } `} ${({ $tokens, $selected }) => $selected && css` border: ${$tokens.state.selected.border}; box-shadow: ${$tokens.state.selected.boxShadow}; `} &:focus-visible { outline: ${({ $tokens }) => $tokens.state.focus.outline}; outline-offset: ${({ $tokens }) => $tokens.state.focus.outlineOffset}; } ` const CardV2Root = forwardRef( ( { variant = CardV2Variant.OUTLINED, orientation = CardV2Orientation.VERTICAL, padding, title, truncateTitle, subtitle, description, eyebrow, media, mediaWidth, mediaHeight, mediaMinHeight, leadingSlot, trailingSlot, footer, actions, actionPlacement = CardV2ActionPlacement.BODY, centered = false, interactive = false, selected = false, skeleton, width, minWidth, maxWidth, height, minHeight, maxHeight, scrollable, children, role, tabIndex, ...props }, ref ) => { const tokens = useResponsiveTokens('CARDV2') const filteredProps = filterBlockedProps(props) const { 'aria-label': providedAriaLabel, ...filteredPropsWithoutAriaLabel } = filteredProps const actionItems = toCardV2ActionArray(actions) const isHorizontal = orientation === CardV2Orientation.HORIZONTAL const resolvedPadding = padding ?? (variant === CardV2Variant.GHOST ? CardV2Padding.NONE : CardV2Padding.COMFORTABLE) const resolvedRole = role ?? (interactive ? 'button' : 'region') const supportsAriaSelected = [ 'gridcell', 'option', 'row', 'tab', 'treeitem', ].includes(resolvedRole) const hasBodyActions = actionPlacement === CardV2ActionPlacement.BODY && actionItems.length > 0 const hasFooterActions = actionPlacement === CardV2ActionPlacement.FOOTER && actionItems.length > 0 const hasHeaderContent = Boolean( leadingSlot || eyebrow || title || subtitle || trailingSlot ) const hasBodyContent = Boolean( description || children || hasBodyActions ) const hasFooterContent = Boolean(footer || hasFooterActions) const hasContentColumn = Boolean( hasHeaderContent || hasBodyContent || hasFooterContent ) const shouldShowFooterDivider = Boolean( hasFooterActions && (media || hasHeaderContent || hasBodyContent) ) const resolvedScrollable = scrollable ?? Boolean(maxHeight) const hasPropComposition = Boolean( title || subtitle || description || eyebrow || media || leadingSlot || trailingSlot || footer || actionItems.length > 0 ) const baseId = useId() const ids = { eyebrow: `${baseId}-card-eyebrow`, title: `${baseId}-card-title`, subtitle: `${baseId}-card-subtitle`, description: `${baseId}-card-description`, } const ariaLabel = useMemo(() => { const parts = [title, subtitle, description].filter(isTextPrimitive) return parts.length > 0 ? parts.join(' - ') : undefined }, [title, subtitle, description]) return ( {skeleton?.show ? ( ) : children && !hasPropComposition ? ( {children} ) : ( {media} {hasContentColumn && ( {children} {footer} )} )} ) } ) CardV2Root.displayName = 'CardV2' const CardV2: typeof CardV2Root & { Header: typeof CardV2Header Meta: typeof CardV2Meta Media: typeof CardV2Media Body: typeof CardV2Body Content: typeof CardV2Content Actions: typeof CardV2Actions Footer: typeof CardV2Footer Skeleton: typeof CardV2Skeleton } = Object.assign(CardV2Root, { Header: CardV2Header, Meta: CardV2Meta, Media: CardV2Media, Body: CardV2Body, Content: CardV2Content, Actions: CardV2Actions, Footer: CardV2Footer, Skeleton: CardV2Skeleton, }) export default CardV2