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 = '載入中' }) => (
);
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 (
);
};