import classNames from 'classnames' import React from 'react' import CardCta from '../LinkAttachment/components/_shared/CardCta' import type { LinkAttachmentVariant } from '../LinkAttachment/components/_shared/CardShell' import type { RichCardActionButton } from './types' export interface RichCardBodyProps { /** Surface the action buttons paint against (from `useChinPalette().surface`). */ ctaSurface: LinkAttachmentVariant /** Ink for the title + subtitle, from `useChinPalette` — fixed per surface. */ chinTextColor: string title?: string subtitle?: string subtitleLines?: number appIcon?: React.ReactNode actions?: RichCardActionButton[] } // body/small from Figma. Title is medium; subtitle is regular. No // `text-wrap:balance` — it evened two-line titles by breaking mid-phrase. const TITLE_CLASS = 'line-clamp-2 text-[14px] font-medium leading-5 tracking-[0.02em]' const SUBTITLE_CLASS = 'text-[14px] leading-5 tracking-[0.02em]' /** `
` ignores vertical margin; the spacer is the 6px Figma gap. */ const LineBreak: React.FC = () => ( <>
) const withLineBreaks = (text: string) => text.split(/\r?\n/).map((line, index, lines) => ( {line} {index < lines.length - 1 ? : null} )) /** * The action footer. `layout` (chosen by the first action's `variant` in * {@link RichCardBody}) picks the presentation: `stacked` (`primary`) → * full-width buttons stacked below the copy; `row` (`secondary`) → content-width * buttons pinned to the right of the chin row (`shrink-0`, so they never * compress the title/subtitle). Each button still paints in its own emphasis. * Returns `null` when there are no actions. */ const RichCardFooter: React.FC<{ surface: LinkAttachmentVariant actions?: RichCardActionButton[] layout: 'stacked' | 'row' }> = ({ surface, actions, layout }) => { if (actions == null || actions.length === 0) return null const stacked = layout === 'stacked' return (
{actions.map((action, index) => ( ))}
) } /** * The rich card's "chin": title, subtitle (prose, multi-line by default), and * the action footer. Padding matches `CardBody` (16px x, 12px y) so a rich card * lines up with the link cards around it. */ const RichCardBody: React.FC = ({ ctaSurface, chinTextColor, title, subtitle, subtitleLines = 3, appIcon, actions, }) => { const hasTitle = title != null && title.trim() !== '' const hasSubtitle = subtitle != null && subtitle.trim() !== '' const hasActions = actions != null && actions.length > 0 if (!hasTitle && !hasSubtitle && !hasActions) return null const chinTextStyle: React.CSSProperties = { color: chinTextColor } const subtitleStyle: React.CSSProperties = subtitleLines > 1 ? { ...chinTextStyle, display: '-webkit-box', WebkitBoxOrient: 'vertical', WebkitLineClamp: subtitleLines, overflow: 'hidden', } : chinTextStyle // A secondary primary-action lays out as a row (copy left, action right); // primary stacks the button full-width below the copy. const inline = hasActions && (actions?.[0]?.variant ?? 'primary') === 'secondary' const textBlock = (
{hasTitle && (
{appIcon ? {appIcon} : null}

{title}

)} {hasSubtitle && (

{subtitleLines > 1 ? withLineBreaks(subtitle ?? '') : subtitle}

)}
) return (
{inline ? (
{textBlock}
) : ( <> {textBlock} )}
) } export default RichCardBody