const { useState, useEffect, useCallback, useRef } = React; const Toast = ({ message, onClose }) => { useEffect(() => { const timer = setTimeout(onClose, 2500); return () => clearTimeout(timer); }, [onClose]); return (
+ {message}
); }; const AnnouncementModal = ({ announcement, onClose }) => { const overlayRef = useRef(null); const contentRef = useRef(null); useEffect(() => { const overlay = overlayRef.current; const content = contentRef.current; if (!overlay || !content) return; Motion.animate(overlay, { opacity: [0, 1] }, { duration: 0.15 }); Motion.animate(content, { opacity: [0, 1], transform: ['scale(0.95)', 'scale(1)'] }, { duration: 0.25, easing: [0.22, 1, 0.36, 1] } ); document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = ''; }; }, []); const handleClose = () => { localStorage.setItem('shop_announcement_read_version', announcement.version); onClose(); }; return (
e.stopPropagation()} style={{ background: '#fff', borderRadius: '1rem', padding: '1.5rem', maxWidth: '320px', width: '100%', textAlign: 'center', position: 'relative' }} >

最新公告

{announcement.content}

); }; const Loading = ({ message = '載入中' }) => (
{message}
); const MetalIcon = ({ type, size = 'md' }) => { const sizes = { sm: 24, md: 40, lg: 56 }; const s = sizes[size] || sizes.md; const colors = { gold: ['#d4a853', '#b8923f'], silver: ['#c9c9c9', '#a0a0a0'], platinum: ['#d8d4cf', '#b5b0a8'], palladium: ['#e0e0e0', '#c0c0c0'] }; const [c1, c2] = colors[type] || colors.gold; return ( ); };