// MOMO Pro - 共用 UI 元件 // ===== Badge ===== const Badge = ({ tone = 'secondary', children, dot = false, style }) => { const tones = { primary: { bg: 'var(--momo-primary-100)', text: 'var(--momo-primary-700)', dot: 'var(--momo-primary)' }, success: { bg: 'var(--momo-success-bg)', text: 'var(--momo-success-text)', dot: 'var(--momo-success)' }, danger: { bg: 'var(--momo-danger-bg)', text: 'var(--momo-danger-text)', dot: 'var(--momo-danger)' }, warning: { bg: 'var(--momo-warning-bg)', text: 'var(--momo-warning-text)', dot: 'var(--momo-warning)' }, info: { bg: 'var(--momo-info-bg)', text: 'var(--momo-info-text)', dot: 'var(--momo-info)' }, secondary: { bg: 'var(--momo-bg-muted)', text: 'var(--momo-text-secondary)', dot: 'var(--momo-text-tertiary)' }, }; const t = tones[tone] || tones.secondary; return ( {dot && } {children} ); }; // ===== Button ===== const Button = ({ variant = 'gradient', size = 'md', icon, iconRight, children, onClick, style, disabled, type = 'button' }) => { const sizes = { sm: { padding: '6px 12px', fontSize: 'var(--momo-font-size-xs)', height: 30, gap: 6 }, md: { padding: '8px 16px', fontSize: 'var(--momo-font-size-sm)', height: 38, gap: 8 }, lg: { padding: '10px 20px', fontSize: 'var(--momo-font-size-base)', height: 44, gap: 10 }, }; const variants = { gradient: { background: 'var(--momo-gradient-primary)', color: 'var(--momo-text-inverse)', boxShadow: 'var(--momo-shadow-sm)', }, solid: { background: 'var(--momo-primary)', color: 'var(--momo-text-inverse)', }, outline: { background: 'var(--momo-bg-surface)', color: 'var(--momo-primary)', border: '1.5px solid var(--momo-primary)', }, ghost: { background: 'transparent', color: 'var(--momo-text-secondary)', }, 'ghost-hover': { background: 'var(--momo-bg-subtle)', color: 'var(--momo-text-primary)', }, secondary: { background: 'var(--momo-bg-surface)', color: 'var(--momo-text-primary)', border: '1px solid var(--momo-border)', }, danger: { background: 'var(--momo-danger)', color: 'var(--momo-text-inverse)', }, }; const s = sizes[size]; const v = variants[variant] || variants.gradient; return ( ); }; // ===== Avatar ===== const Avatar = ({ name = '?', size = 32, gradient = false, style }) => { const initial = (name || '?').trim().charAt(0).toUpperCase(); // 從名字 hash 出穩定色相 let hash = 0; for (let i = 0; i < name.length; i++) hash = name.charCodeAt(i) + ((hash << 5) - hash); const hue = Math.abs(hash) % 360; return (
{initial}
); }; // ===== Card ===== const Card = ({ children, style, cardStyle = 'shadow', padding = true, hoverable = false }) => { const styles = { shadow: { boxShadow: 'var(--momo-shadow-md)', border: '1px solid transparent' }, flat: { boxShadow: 'none', border: '1px solid transparent', background: 'var(--momo-bg-subtle)' }, bordered:{ boxShadow: 'none', border: '1px solid var(--momo-border)' }, }; return (
{children}
); }; // ===== Input ===== const Input = ({ icon, value, onChange, placeholder, style, type = 'text', size = 'md' }) => { const heights = { sm: 32, md: 38, lg: 44 }; return (
{icon && }
); }; // ===== Checkbox ===== const Checkbox = ({ checked, onChange, indeterminate = false, style }) => { const ref = React.useRef(null); React.useEffect(() => { if (ref.current) ref.current.indeterminate = indeterminate; }, [indeterminate]); return ( ); }; // ===== Page Header ===== const PageHeader = ({ title, subtitle, actions, breadcrumbs }) => (
{breadcrumbs && (
{breadcrumbs.map((b, i) => ( {i > 0 && } {b} ))}
)}

{title}

{subtitle &&
{subtitle}
}
{actions &&
{actions}
}
); // ===== Tag(標籤統一元件 — 全站唯一入口) ===== // 用法:推薦 MOMO 領先 // tone: caramel | honey | rust | mahogany | earth | ink | muted | success // 不要再用任何寫死色碼做標籤! const Tag = ({ tone = 'earth', mono = false, dot = false, size = 'sm', children, style }) => { const sizes = { xs: { pad: '1px 6px', fs: 10, gap: 4 }, sm: { pad: '2px 8px', fs: 11, gap: 5 }, md: { pad: '3px 10px', fs: 12, gap: 6 }, }; const s = sizes[size] || sizes.sm; return ( {dot && } {children} ); }; Object.assign(window, { Badge, Button, Avatar, Card, Input, Checkbox, PageHeader, Tag });