"use client"; import Image from "next/image"; import { useTranslation } from "../../../contexts/MessagesContext"; import MultiSelect from "../../controls/MultiSelect"; import InlineTextButton from "../../buttons/InlineTextButton"; import NavigationLink from "../../navigation/Link"; import Tag from "../../utility/Tag"; import type { RuleBottomLink, RuleViewProps } from "./Rule.types"; export function RuleView({ title, description, onDescriptionClick, descriptionEmptyHint, descriptionEditAriaLabel, onTitleClick, titleEditAriaLabel, icon, backgroundColor, className, onClick, onKeyDown, expanded, size, categories, logoUrl, logoAlt, communityInitials, hideCategoryAddButton = false, hasBottomLinks = false, bottomStatusLabel, bottomLinks, recommended = false, templateGridFigmaShell = false, fluidWidth = false, }: RuleViewProps) { const t = useTranslation("ruleCard"); const ariaLabel = t("ariaLabel")?.replace("{title}", title) || title; const interactiveCard = !hasBottomLinks; // Size-based styling const isLarge = size === "L"; const isMedium = size === "M"; const isSmall = size === "S"; const isExtraSmall = size === "XS"; // Card dimensions - use CSS classes from className if provided, otherwise use size-based logic // Check if className already has padding/gap classes const hasResponsivePadding = className?.includes("p-[") || className?.includes("px-[") || className?.includes("py-[") || className?.includes("pt-[") || className?.includes("pb-["); const hasResponsiveGap = className?.includes("gap-["); // Expanded + size: uniform padding on all sides (overrides conflicting utilities from `className`). const cardPadding = expanded && isLarge ? "!p-[24px]" : expanded && isMedium ? "!p-[16px]" : hasResponsivePadding ? "" : isLarge || isSmall ? "p-[24px]" : isMedium ? "p-[16px]" : "pb-[24px] pt-[12px] px-[12px]"; // XS: asymmetric padding const cardGap = expanded ? "gap-[16px]" : hasResponsiveGap ? "" // If className has responsive gap, don't add size-based gap : isLarge ? "gap-[10px]" : isMedium ? "gap-[12px]" : "gap-[18px]"; // XS and S: 18px gap const cardWidth = fluidWidth && expanded ? "" : expanded ? isLarge ? "w-[568px]" : isMedium ? "w-[398px]" : "" : ""; // Logo/Icon dimensions (inner circle) after Figma header `pl-1 pr-2 py-2` in icon cell // (Card / Rule — e.g. `22143:900771` / `19706:12110`); outer column width holds padding + this. const logoSize = 103; // `next/image` prop; actual box comes from `logoContainerClass` const logoContainerClass = templateGridFigmaShell ? ` max-[639px]:size-[56px] min-[640px]:max-[1023px]:size-[64px] min-[1024px]:size-[88px] ` : ` max-[639px]:size-[56px] min-[640px]:max-[1023px]:size-[64px] min-[1024px]:max-[1439px]:size-[56px] min-[1440px]:size-[88px] `; // Title typography - use CSS responsive classes const showRecommendedTag = recommended && !expanded; const titleClass = templateGridFigmaShell ? ` max-[639px]:font-inter max-[639px]:font-bold max-[639px]:text-[20px] max-[639px]:leading-[28px] min-[640px]:max-[1023px]:font-bricolage-grotesque min-[640px]:max-[1023px]:font-bold min-[640px]:max-[1023px]:text-[28px] min-[640px]:max-[1023px]:leading-[36px] min-[1024px]:max-[1439px]:font-bricolage-grotesque min-[1024px]:max-[1439px]:font-extrabold min-[1024px]:max-[1439px]:text-[36px] min-[1024px]:max-[1439px]:leading-[44px] min-[1440px]:font-bricolage-grotesque min-[1440px]:font-extrabold min-[1440px]:text-[36px] min-[1440px]:leading-[44px] ` : ` max-[639px]:font-inter max-[639px]:font-bold max-[639px]:text-[20px] max-[639px]:leading-[28px] min-[640px]:max-[1023px]:font-bricolage-grotesque min-[640px]:max-[1023px]:font-bold min-[640px]:max-[1023px]:text-[28px] min-[640px]:max-[1023px]:leading-[36px] min-[1024px]:max-[1439px]:font-bricolage-grotesque min-[1024px]:max-[1439px]:font-bold min-[1024px]:max-[1439px]:text-[24px] min-[1024px]:max-[1439px]:leading-[32px] min-[1440px]:font-bricolage-grotesque min-[1440px]:font-extrabold min-[1440px]:text-[36px] min-[1440px]:leading-[44px] `; const descriptionClass = isLarge ? "font-inter font-medium text-[18px] leading-[24px]" : isMedium ? templateGridFigmaShell ? "font-inter font-medium text-[14px] leading-[16px] min-[1024px]:max-[1439px]:text-[18px] min-[1024px]:max-[1439px]:leading-[24px]" : "font-inter font-medium text-[14px] leading-[16px]" : isSmall ? "font-inter font-medium text-[14px] leading-[16px]" // S: 14px, medium, Inter : "font-inter font-medium text-[12px] leading-[14px]"; // XS: 12px, medium, Inter const headerIconCellClass = templateGridFigmaShell ? ` flex shrink-0 items-center justify-center pl-[4px] pr-[8px] py-[8px] max-[639px]:w-[72px] min-[640px]:max-[1023px]:w-[80px] min-[1024px]:max-[1439px]:w-[130px] min-[1440px]:w-[119px] ` : ` flex shrink-0 items-center justify-center pl-[4px] pr-[8px] py-[8px] max-[639px]:w-[72px] min-[640px]:max-[1023px]:w-[80px] min-[1024px]:w-[119px] `; const titleColumnMinHClass = templateGridFigmaShell ? "min-h-[72px] min-[640px]:min-h-[80px] min-[1024px]:max-[1439px]:min-h-[136px] min-[1440px]:min-h-[136px]" : "min-h-[72px] min-[640px]:min-h-[80px] min-[1024px]:min-h-[88px] min-[1440px]:min-h-[136px]"; // Render logo/icon const renderLogo = () => { if (logoUrl) { // Check if it's a localhost URL or external URL that needs regular img tag const isLocalhost = logoUrl.startsWith("http://localhost") || logoUrl.startsWith("https://localhost"); const containerClass = `${logoContainerClass} relative rounded-full overflow-hidden mix-blend-luminosity`; if (isLocalhost) { return (
{/* eslint-disable-next-line @next/next/no-img-element */} {logoAlt
); } return (
{logoAlt
); } if (icon) { return (
{icon}
); } if (communityInitials) { const initialsSize = ` max-[639px]:text-[16px] min-[640px]:max-[1023px]:text-[20px] min-[1024px]:text-[36px] `; return (
{communityInitials}
); } return null; }; // Border radius - use CSS classes if provided via className, otherwise use size-based logic const borderRadiusClass = className?.includes("rounded-") ? "" // If className already has border radius, don't add size-based one : isExtraSmall ? "rounded-[var(--measures-radius-200,8px)]" : isSmall ? "rounded-[var(--measures-radius-300,12px)]" : "rounded-[var(--radius-measures-radius-small)]"; function renderBottomLink(link: RuleBottomLink) { const shared = { variant: "paragraph" as const, type: "primary" as const, theme: "light" as const, className: "shrink-0", children: link.label, }; if (link.href) { return ( e.stopPropagation()} /> ); } return ( { e.stopPropagation(); link.onClick?.(); }} /> ); } return (
{/* Figma: Header = `border-b` row, `gap-px`, icon `pl-1 pr-2 py-2` + `border-l` on title. */}
{renderLogo() && (
{renderLogo()}
)} {title && (
{/* Inner container for header text with padding */}
{showRecommendedTag ? ( {t("recommendedLabel")} ) : null} {onTitleClick ? ( { e.stopPropagation(); onTitleClick(); }} > {title} ) : (

{title}

)}
)}
{hasBottomLinks ? (
{description ? (

{description}

) : null} {bottomLinks && bottomLinks.length > 0 ? (
{bottomStatusLabel ? ( {bottomStatusLabel} ) : null} {/** * Figma `22143:900539` / `21867:46099`: one row — status (optional) + all links in * a single `flex-nowrap` group (`space/800` = 32px between links on large). * If the row is too narrow, scroll horizontally; links never wrap. */}
{bottomLinks.map((link) => renderBottomLink(link))}
) : null}
) : expanded ? ( <> {(description || (onDescriptionClick && typeof descriptionEmptyHint === "string")) && (
0 ? "border-b border-solid border-[var(--color-content-invert-primary)] pb-[16px]" : "" } ${expanded && (isLarge || isMedium) ? "px-0" : "px-[12px]"}`} > {onDescriptionClick ? ( { e.stopPropagation(); onDescriptionClick(); }} > {description ?? descriptionEmptyHint ?? ""} ) : ( description && (

{description}

) )}
)} {/* Categories Section - Using MultiSelect */} {categories && categories.length > 0 && (
{categories.map((category, categoryIndex) => ( { category.onChipClick?.(category.name, chipId); }} onAddClick={() => { category.onAddClick?.(category.name); }} onCustomChipConfirm={(chipId, value) => { category.onCustomChipConfirm?.( category.name, chipId, value, ); }} onCustomChipClose={(chipId) => { category.onCustomChipClose?.(category.name, chipId); }} addButton={ !hideCategoryAddButton && category.addButton !== false } addButtonText="" // Empty text for icon-only circular button className="w-full" /> ))}
)} ) : ( /* Collapsed State: Description */ description && (

{description}

) )}
); }