// Social Button // Purpose: Defines the Social Button module and the local helpers/components used in this file. // Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit. import * as FaIcons from 'react-icons/fa'; import { FaLink } from 'react-icons/fa'; import { useSessionSelector } from '../../context/SessionContext.jsx'; import { getSocialById } from '../../lib/socials.js'; import { trackAnalyticsEvent } from '../../analytics/index.js'; function sanitizeCssColor(value) { if (typeof value !== 'string') return null; const trimmed = value.trim(); if (!trimmed) return null; if (/^#[0-9a-fA-F]{3,8}$/.test(trimmed)) return trimmed; if (/^(rgb|rgba|hsl|hsla)\([^)]+\)$/.test(trimmed)) return trimmed; return null; } function resolveIcon(iconName) { if (typeof iconName !== 'string' || !iconName.trim()) return FaLink; const icon = FaIcons[iconName.trim()]; return typeof icon === 'function' ? icon : FaLink; } export default function SocialButton({ id = null, label, url, icon, color, layout = 'stacked', className = '' }) { const socialFromId = useSessionSelector((state) => (id ? getSocialById(state, id) : null)); const resolvedUrl = url || socialFromId?.url || null; if (!resolvedUrl) return null; const resolvedIcon = icon || socialFromId?.icon || null; const resolvedColor = color || socialFromId?.color || null; const Icon = resolveIcon(resolvedIcon); const bgColor = sanitizeCssColor(resolvedColor); const text = label || socialFromId?.label || 'Link'; const isInline = layout === 'inline'; return ( { trackAnalyticsEvent('social_link_clicked', { destination: id || 'custom', label: text, layout, }); }} className={`grid h-full min-h-0 w-full place-items-center rounded-md bg-slate-700 px-0.5 ${isInline ? 'py-0.5' : 'pb-3'} text-center text-sm font-medium text-white transition hover:opacity-90 ${className}`} style={bgColor ? { backgroundColor: bgColor } : undefined} > {isInline ? ( {text} ) : ( {/* */} {text} )} ); }