feat: new home web
This commit is contained in:
68
packages/web/src/components/LanguageToggle.tsx
Normal file
68
packages/web/src/components/LanguageToggle.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useI18n, type Locale } from '../lib/i18n';
|
||||
|
||||
const languages: { locale: Locale; flag: string; label: string }[] = [
|
||||
{ locale: 'en', flag: '🇺🇸', label: 'English' },
|
||||
{ locale: 'zh', flag: '🇨🇳', label: '简体中文' },
|
||||
];
|
||||
|
||||
export default function LanguageToggle() {
|
||||
const { locale, setLocale } = useI18n();
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const handler = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
|
||||
};
|
||||
document.addEventListener('mousedown', handler);
|
||||
return () => document.removeEventListener('mousedown', handler);
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className="relative">
|
||||
<button
|
||||
onClick={() => setOpen(!open)}
|
||||
className="flex items-center gap-1 px-2 py-1.5 rounded-lg text-xs font-medium transition-all duration-150 cursor-pointer hover:bg-bg-tertiary"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
aria-label="Switch language"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path d="M12 21a9 9 0 100-18 9 9 0 000 18z" />
|
||||
<path d="M3.6 9h16.8M3.6 15h16.8" />
|
||||
<path d="M12 3a15.3 15.3 0 014 9 15.3 15.3 0 01-4 9 15.3 15.3 0 01-4-9 15.3 15.3 0 014-9z" />
|
||||
</svg>
|
||||
<span className="tracking-wide">{locale === 'en' ? 'EN' : '中'}</span>
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div
|
||||
className="absolute top-full right-0 mt-1.5 min-w-[140px] rounded-xl py-1 z-50 animate-slide-down"
|
||||
style={{
|
||||
background: 'var(--bg-elevated)',
|
||||
border: '1px solid var(--border-default)',
|
||||
boxShadow: 'var(--shadow-lg)',
|
||||
}}
|
||||
>
|
||||
{languages.map(lang => (
|
||||
<button
|
||||
key={lang.locale}
|
||||
onClick={() => { setLocale(lang.locale); setOpen(false); }}
|
||||
className="flex items-center gap-2.5 w-full px-3 py-2 text-[13px] transition-colors cursor-pointer rounded-lg mx-0.5"
|
||||
style={{
|
||||
width: 'calc(100% - 4px)',
|
||||
color: locale === lang.locale ? 'var(--text-primary)' : 'var(--text-secondary)',
|
||||
fontWeight: locale === lang.locale ? 500 : 400,
|
||||
background: locale === lang.locale ? 'var(--bg-tertiary)' : 'transparent',
|
||||
}}
|
||||
>
|
||||
<span className="text-base leading-none">{lang.flag}</span>
|
||||
{lang.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user