/** * InfoBanner — 테스트 페이지 상단 안내 배너 (Phase B) * Stitch 샘플의 정보 배너 스타일: 아이콘 + 텍스트 + 닫기 버튼 * AdBanner와 구분되는 정보 전달용 배너 */ import styled from 'styled-components'; import MatIcon from './MatIcon'; export type InfoBannerType = 'info' | 'success' | 'warning' | 'error'; export type InfoBannerSize = 'sm' | 'md' | 'lg'; interface InfoBannerProps { /** 배너 타입 (색상 테마 결정) */ type?: InfoBannerType; /** 표시할 텍스트 또는 노드 */ children: React.ReactNode; /** 크기 (기본: md) */ size?: InfoBannerSize; /** 아이콘 표시 여부 (기본: true) */ showIcon?: boolean; /** 닫기 버튼 표시 여부 (기본: false) */ dismissible?: boolean; /** 닫기 클릭 핸들러 */ onDismiss?: () => void; /** 전체 너비 사용 (기본: true) */ fullWidth?: boolean; /** 추가 클래스명 */ className?: string; } const TYPE_STYLES = { info: { bg: 'var(--md3-primary-container)', color: 'var(--md3-on-primary-container)', border: '1px solid var(--md3-primary)', icon: 'info', }, success: { bg: 'var(--md3-tertiary-container)', color: 'var(--md3-on-tertiary-container)', border: '1px solid var(--md3-tertiary)', icon: 'check_circle', }, warning: { bg: '#fff3cd', color: '#856404', border: '1px solid #ffc107', icon: 'warning', }, error: { bg: 'var(--md3-error-container)', color: 'var(--md3-on-error-container)', border: '1px solid var(--md3-error)', icon: 'error', }, }; const SIZE_STYLES = { sm: { px: '10px', py: '8px', fontSize: '12px', lineHeight: '1.4', gap: '8px', iconSize: 16 }, md: { px: '12px', py: '10px', fontSize: '13px', lineHeight: '1.5', gap: '10px', iconSize: 18 }, lg: { px: '16px', py: '12px', fontSize: '14px', lineHeight: '1.5', gap: '12px', iconSize: 20 }, }; const StyledBanner = styled.div<{ $type: InfoBannerType; $size: InfoBannerSize; $fullWidth: boolean; }>` display: flex; align-items: flex-start; justify-content: space-between; gap: ${(p) => SIZE_STYLES[p.$size].gap}; padding: ${(p) => SIZE_STYLES[p.$size].py} ${(p) => SIZE_STYLES[p.$size].px}; border-radius: 14px; border: ${(p) => TYPE_STYLES[p.$type].border}; background: ${(p) => TYPE_STYLES[p.$type].bg}; color: ${(p) => TYPE_STYLES[p.$type].color}; font-family: 'Inter', 'Pretendard', system-ui, sans-serif; font-size: ${(p) => SIZE_STYLES[p.$size].fontSize}; line-height: ${(p) => SIZE_STYLES[p.$size].lineHeight}; letter-spacing: 0.01em; width: ${(p) => (p.$fullWidth ? '100%' : 'auto')}; box-sizing: border-box; animation: slideDown 0.25s ease-out; overflow: hidden; @keyframes slideDown { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } } `; const Content = styled.div` display: flex; align-items: flex-start; gap: inherit; flex: 1; min-width: 0; `; const Text = styled.div` flex: 1; word-break: keep-all; overflow-wrap: break-word; `; const DismissButton = styled.button` display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background: transparent; color: inherit; opacity: 0.6; transition: opacity 0.15s, background-color 0.15s; flex-shrink: 0; margin-left: auto; &:hover:not(:disabled) { opacity: 1; background: rgba(0, 0, 0, 0.06); } &:active:not(:disabled) { background: rgba(0, 0, 0, 0.1); } &:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--md3-primary); } @media (hover: none) and (pointer: coarse) { &:hover:not(:disabled) { opacity: 0.6; background: transparent; } &:active:not(:disabled) { opacity: 1; background: rgba(0, 0, 0, 0.06); } } `; export default function InfoBanner({ type = 'info', children, size = 'md', showIcon = true, dismissible = false, onDismiss, fullWidth = true, className, }: InfoBannerProps) { const iconName = TYPE_STYLES[type].icon; const iconSize = SIZE_STYLES[size].iconSize; return ( {showIcon && } {children} {dismissible && onDismiss && ( )} ); }