import React, { ReactNode, forwardRef, CSSProperties } from 'react'; export interface HeadingProps { children: ReactNode; level?: 1 | 2 | 3 | 4 | 5 | 6; weight?: 'normal' | 'medium' | 'semibold' | 'bold'; align?: 'left' | 'center' | 'right'; uppercase?: boolean; intent?: 'primary' | 'telemetry' | 'warning' | 'critical' | 'default'; truncate?: boolean; icon?: ReactNode; id?: string; /** @deprecated Use semantic props instead. */ className?: string; /** @deprecated Use semantic props instead. */ style?: CSSProperties; /** @deprecated Use semantic props instead. */ mb?: number | string; /** @deprecated Use semantic props instead. */ marginBottom?: number | string; /** @deprecated Use semantic props instead. */ mt?: number | string; /** @deprecated Use semantic props instead. */ marginTop?: number | string; /** @deprecated Use semantic props instead. */ color?: string; /** @deprecated Use semantic props instead. */ fontSize?: string | { base: string; sm?: string; md: string; lg?: string; xl?: string }; /** @deprecated Use semantic props instead. */ letterSpacing?: string; /** @deprecated Use semantic props instead. */ size?: string; /** @deprecated Use semantic props instead. */ groupHoverColor?: string; /** @deprecated Use semantic props instead. */ lineHeight?: string | number; /** @deprecated Use semantic props instead. */ transition?: boolean; 'data-testid'?: string; } /** * Heading - Redesigned for "Modern Precision" theme. * Enforces semantic props. */ export const Heading = forwardRef(({ children, level = 1, weight = 'bold', align = 'left', uppercase = false, intent = 'default', truncate, icon, id, className, style: styleProp, mb, marginBottom, mt, marginTop, color, fontSize, letterSpacing, size, groupHoverColor, lineHeight, transition, 'data-testid': dataTestId, }, ref) => { const Tag = `h${level}` as const; const weightClasses = { normal: 'font-normal', medium: 'font-medium', semibold: 'font-semibold', bold: 'font-bold' }; const sizeClasses = { 1: 'text-4xl md:text-5xl tracking-tighter leading-none', 2: 'text-3xl md:text-4xl tracking-tight leading-tight', 3: 'text-2xl md:text-3xl tracking-tight leading-snug', 4: 'text-xl md:text-2xl tracking-normal leading-normal', 5: 'text-lg md:text-xl tracking-normal leading-normal', 6: 'text-base md:text-lg tracking-wide leading-normal' }; const intentClasses = { default: 'text-[var(--ui-color-text-high)]', primary: 'text-[var(--ui-color-intent-primary)]', telemetry: 'text-[var(--ui-color-intent-telemetry)]', warning: 'text-[var(--ui-color-intent-warning)]', critical: 'text-[var(--ui-color-intent-critical)]', }; const getResponsiveFontSize = (fs: HeadingProps['fontSize']) => { if (!fs || typeof fs === 'string') return ''; const classes = []; if (fs.base) classes.push(`text-${fs.base}`); if (fs.sm) classes.push(`sm:text-${fs.sm}`); if (fs.md) classes.push(`md:text-${fs.md}`); if (fs.lg) classes.push(`lg:text-${fs.lg}`); if (fs.xl) classes.push(`xl:text-${fs.xl}`); return classes.join(' '); }; const classes = [ intentClasses[intent as keyof typeof intentClasses] || intentClasses.default, weightClasses[weight], fontSize ? getResponsiveFontSize(fontSize) : sizeClasses[level], align === 'center' ? 'text-center' : (align === 'right' ? 'text-right' : 'text-left'), uppercase ? 'uppercase tracking-widest' : '', truncate ? 'truncate' : '', transition ? 'transition-all duration-200' : '', color?.startsWith('text-') ? color : '', className, ].join(' '); const combinedStyle: React.CSSProperties = { ...(mb !== undefined ? { marginBottom: typeof mb === 'number' ? `${mb * 0.25}rem` : mb } : {}), ...(marginBottom !== undefined ? { marginBottom: typeof marginBottom === 'number' ? `${marginBottom * 0.25}rem` : marginBottom } : {}), ...(mt !== undefined ? { marginTop: typeof mt === 'number' ? `${mt * 0.25}rem` : mt } : {}), ...(marginTop !== undefined ? { marginTop: typeof marginTop === 'number' ? `${marginTop * 0.25}rem` : marginTop } : {}), ...(color && !color.startsWith('text-') ? { color } : {}), ...(letterSpacing ? { letterSpacing } : {}), ...(typeof fontSize === 'string' ? { fontSize } : {}), ...(lineHeight ? { lineHeight } : {}), ...(styleProp || {}), }; return ( 0 ? combinedStyle : undefined} id={id}>
{icon} {children}
); }); Heading.displayName = 'Heading';