import * as React from 'react' import { Info, CheckCircle, AlertCircle, AlertTriangle, X } from 'lucide-react' import type { SAILShape, SAILMarginSize, SAILAlign } from '../../types/sail' import type { ButtonWidgetProps } from '../Button/ButtonWidget' import { ButtonWidget } from '../Button/ButtonWidget' import { ButtonArrayLayout } from '../Button/ButtonArrayLayout' import { mergeClasses } from '../../utils/classNames' import { marginAboveMap, marginBelowMap, shapeMap } from '../../utils/sailMaps' export type BackgroundColor = "INFO" | "SUCCESS" | "WARN" | "ERROR" | string export type HighlightColor = "INFO" | "POSITIVE" | "WARN" | "NEGATIVE" | string export type AnnounceBehavior = "DISPLAY_ONLY" | "DISPLAY_AND_ANNOUNCE" | "ANNOUNCE_ONLY" export interface MessageBannerProps { /** Text to display on the first line inside the banner */ primaryText?: string /** Text to display beneath the primary text inside the banner */ secondaryText?: string /** Background color - semantic values or hex color (with optional transparency) */ backgroundColor?: BackgroundColor /** Color of the decorative bar and icon - semantic values or hex color */ highlightColor?: HighlightColor /** Icon to display before the primary text (decorative only) */ icon?: "info" | "success" | "warning" | "error" /** Whether to show the decorative bar */ showDecorativeBar?: boolean /** Banner shape */ shape?: SAILShape /** Space above the banner */ marginAbove?: SAILMarginSize /** Space below the banner */ marginBelow?: SAILMarginSize /** Whether the component is evaluated */ showWhen?: boolean /** Screen reader behavior for announcing banner text */ announceBehavior?: AnnounceBehavior /** Additional text for screen readers only */ accessibilityText?: string /** Optional buttons to display to the right of the content */ buttons?: ButtonWidgetProps[] /** Alignment of the optional buttons */ buttonsAlign?: SAILAlign /** Whether to show a close button in the upper right */ showCloseButton?: boolean /** Callback when the close button is clicked */ onClose?: () => void /** Additional Tailwind classes for prototype-specific styling (not part of SAIL API) */ className?: string } export const MessageBanner: React.FC = ({ primaryText, secondaryText, backgroundColor = "INFO", highlightColor = "INFO", icon, showDecorativeBar = true, shape = "SQUARED", marginAbove = "NONE", marginBelow = "STANDARD", showWhen = true, announceBehavior = "DISPLAY_ONLY", accessibilityText, buttons, buttonsAlign = "END", showCloseButton = false, onClose, className: classNameProp }) => { // Visibility control if (!showWhen) return null // Don't render visually if announce-only const isVisuallyHidden = announceBehavior === "ANNOUNCE_ONLY" // Semantic color mappings with dark semantic text const backgroundColorMap: Record = { INFO: { bg: 'bg-sky-50', text: 'text-sky-900' }, SUCCESS: { bg: 'bg-green-50', text: 'text-green-900' }, WARN: { bg: 'bg-yellow-50', text: 'text-yellow-900' }, ERROR: { bg: 'bg-red-50', text: 'text-red-900' } } const highlightColorMap: Record = { INFO: 'bg-sky-500', POSITIVE: 'bg-green-500', WARN: 'bg-yellow-500', NEGATIVE: 'bg-red-500' } // Icon mapping const iconMap = { info: Info, success: CheckCircle, warning: AlertTriangle, error: AlertCircle } const IconComponent = icon ? iconMap[icon] : null const isSemanticBg = backgroundColor in backgroundColorMap const bgColors = isSemanticBg ? backgroundColorMap[backgroundColor] : { bg: '', text: 'text-gray-900' } // Determine highlight bar color const isSemanticHighlight = highlightColor in highlightColorMap const highlightBarColor = isSemanticHighlight ? highlightColorMap[highlightColor] : '' // Build CSS classes const sailClasses = [ 'relative', 'flex', 'items-start', 'p-4', shapeMap[shape], marginAboveMap[marginAbove], marginBelowMap[marginBelow], isSemanticBg ? bgColors.bg : '', isSemanticBg ? bgColors.text : 'text-gray-900', isVisuallyHidden && 'sr-only' ].filter(Boolean).join(' ') const containerClasses = mergeClasses(sailClasses, classNameProp) // Inline styles for custom colors const containerStyle: React.CSSProperties = {} if (!isSemanticBg && backgroundColor) { containerStyle.backgroundColor = backgroundColor } const highlightStyle: React.CSSProperties = {} if (!isSemanticHighlight && highlightColor) { highlightStyle.backgroundColor = highlightColor } // ARIA attributes for screen reader behavior const ariaAttributes: Record = {} if (announceBehavior === "DISPLAY_AND_ANNOUNCE" || announceBehavior === "ANNOUNCE_ONLY") { ariaAttributes.role = "status" ariaAttributes["aria-live"] = "polite" } return (
{/* Decorative bar */} {showDecorativeBar && ( ) }