This commit is contained in:
2026-05-14 15:07:34 +08:00
parent c2a398930d
commit 10d04c4083
179 changed files with 24073 additions and 1243 deletions

View File

@@ -0,0 +1,40 @@
import React from 'react';
import { useTheme } from '../../contexts/ThemeContext';
interface BadgeProps {
children: React.ReactNode;
color?: 'accent' | 'green' | 'orange' | 'red';
size?: 'sm' | 'md';
}
export const Badge: React.FC<BadgeProps> = ({
children,
color = 'accent',
size = 'sm',
}) => {
const { theme } = useTheme();
const colorStyles = {
accent: { bg: theme.gradientAccent, text: '#fff' },
green: { bg: theme.green, text: '#fff' },
orange: { bg: theme.orange, text: '#fff' },
red: { bg: '#ff4444', text: '#fff' },
};
const sizeStyles = {
sm: 'px-2 py-0.5 text-xs',
md: 'px-3 py-1 text-sm',
};
return (
<span
className={`${sizeStyles[size]} rounded font-mono font-medium`}
style={{
background: colorStyles[color].bg,
color: colorStyles[color].text,
}}
>
{children}
</span>
);
};

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { useTheme } from '../../contexts/ThemeContext';
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
className?: string;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
children,
onClick,
disabled = false,
className = '',
}) => {
const { theme } = useTheme();
const baseStyles = `
inline-flex items-center justify-center
font-semibold rounded-xl cursor-pointer
transition-all duration-300 ease
disabled:cursor-not-allowed disabled:opacity-50
`;
const sizeStyles = {
sm: 'px-3 py-1.5 text-xs',
md: 'px-5 py-3 text-sm',
lg: 'px-8 py-5 text-base',
};
const variantStyles = {
primary: `
bg-gradient-to-r from-t-accent to-t-accent-dark
text-white hover:shadow-t-accent hover:-translate-y-0.5
hover:from-[#f0208a] hover:to-[#d01070]
`,
secondary: `
bg-t-bg-hover border border-t-border
text-t-text2 hover:bg-t-bg-elevated
`,
};
return (
<button
onClick={onClick}
disabled={disabled}
className={`${baseStyles} ${sizeStyles[size]} ${variantStyles[variant]} ${className}`}
style={{
color: variant === 'primary' ? '#fff' : theme.text2,
}}
>
{children}
</button>
);
};

View File

@@ -0,0 +1,54 @@
import React from 'react';
import { useTheme } from '../../contexts/ThemeContext';
interface CardProps {
accent?: boolean;
highlight?: boolean;
padding?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
className?: string;
onClick?: () => void;
}
export const Card: React.FC<CardProps> = ({
accent = false,
highlight = false,
padding = 'md',
children,
className = '',
onClick,
}) => {
const { theme, isDark } = useTheme();
const paddingStyles = {
sm: 'p-4',
md: 'p-5',
lg: 'p-8',
};
return (
<div
onClick={onClick}
className={`
rounded-xl border transition-all duration-200 cursor-default
${paddingStyles[padding]}
${accent ? 'border-t-accent' : 'border-t-border'}
${highlight ? 'border-2 border-t-accent' : ''}
${!isDark ? 'shadow-t-card' : ''}
${onClick ? 'cursor-pointer hover:border-t-accent' : ''}
${className}
`}
style={{
backgroundColor: theme.bgCard,
}}
>
{accent && (
<div
className="absolute top-0 left-0 right-0 h-[3px] rounded-t-xl"
style={{ background: theme.gradientAccent }}
/>
)}
{children}
</div>
);
};

View File

@@ -0,0 +1,45 @@
import React from 'react';
import { useTheme } from '../../contexts/ThemeContext';
interface InputProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
onKeyDown?: (e: React.KeyboardEvent) => void;
className?: string;
type?: string;
}
export const Input: React.FC<InputProps> = ({
value,
onChange,
placeholder = '',
onKeyDown,
className = '',
type = 'text',
}) => {
const { theme } = useTheme();
return (
<input
type={type}
value={value}
onChange={(e) => onChange(e.target.value)}
onKeyDown={onKeyDown}
placeholder={placeholder}
className={`
w-full px-4 py-3 text-sm
bg-t-bg-card border border-t-border rounded-lg
text-t-text outline-none
focus:border-t-accent focus:ring-1 focus:ring-t-accent
placeholder:text-t-text3
${className}
`}
style={{
backgroundColor: theme.bgCard,
borderColor: theme.border,
color: theme.text,
}}
/>
);
};

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { useTheme } from '../../contexts/ThemeContext';
interface ProgressBarProps {
percent: number;
color?: 'accent' | 'green' | 'orange' | 'red';
showLabel?: boolean;
}
export const ProgressBar: React.FC<ProgressBarProps> = ({
percent,
color = 'accent',
showLabel = false,
}) => {
const { theme } = useTheme();
const colorStyles = {
accent: theme.gradientAccent,
green: `linear-gradient(90deg, ${theme.green}, #00ff88)`,
orange: `linear-gradient(90deg, ${theme.orange}, #ffaa00)`,
red: '#ff4444',
};
return (
<div className="flex items-center gap-3">
<div
className="h-2 rounded-full flex-1"
style={{ backgroundColor: theme.bgHover }}
>
<div
className="h-full rounded-full transition-all duration-300"
style={{
width: `${percent}%`,
background: colorStyles[color],
}}
/>
</div>
{showLabel && (
<span className="font-mono text-xs text-t-accent">{percent}%</span>
)}
</div>
);
};

View File

@@ -0,0 +1,52 @@
import React from 'react';
import { useTheme } from '../../contexts/ThemeContext';
interface ScoreBarProps {
score: number; // 0-100
label?: string;
accent?: boolean;
}
export const ScoreBar: React.FC<ScoreBarProps> = ({
score,
label,
accent = false,
}) => {
const { theme } = useTheme();
return (
<div
className="p-5 rounded-xl border"
style={{
backgroundColor: theme.bgCard,
borderColor: accent ? theme.accent : theme.border,
}}
>
{label && (
<div
className="font-mono text-xs mb-2"
style={{ color: theme.text3, letterSpacing: '1px' }}
>
{label}
</div>
)}
<div
className="font-mono text-3xl font-bold"
style={{ color: accent ? theme.accent : theme.text }}
>
{score}
</div>
<div className="flex gap-1 mt-2">
{[...Array(10)].map((_, i) => (
<div
key={i}
className="w-2 h-4 rounded-sm"
style={{
backgroundColor: i < score / 10 ? theme.accent : theme.bgHover,
}}
/>
))}
</div>
</div>
);
};

View File

@@ -0,0 +1,6 @@
export { Button } from './Button';
export { Card } from './Card';
export { Input } from './Input';
export { Badge } from './Badge';
export { ProgressBar } from './ProgressBar';
export { ScoreBar } from './ScoreBar';