Karim Krklec
'first'
dcce181
Raw
History Blame Contribute Delete
9.1 kB
import { useState } from 'react'
export function Icon({ name, size = 18, color, strokeWidth = 1.5, style }) {
const paths = {
shield: <><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></>,
code: <><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></>,
upload: <><path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"/><path d="M12 12v9"/><path d="m8 17 4-5 4 5"/></>,
play: <polygon points="5 3 19 12 5 21 5 3"/>,
download: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></>,
search: <><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></>,
alert: <><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><line x1="12" y1="9" x2="12" y2="13"/><circle cx="12" cy="17" r=".5"/></>,
check: <><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></>,
filter: <><line x1="4" y1="6" x2="20" y2="6"/><line x1="6" y1="12" x2="18" y2="12"/><line x1="9" y1="18" x2="15" y2="18"/></>,
chevron: <polyline points="6 9 12 15 18 9"/>,
chevronRight: <polyline points="9 18 15 12 9 6"/>,
chevronLeft: <polyline points="15 18 9 12 15 6"/>,
file: <><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></>,
fileCode: <><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="m10 13-2 2 2 2"/><path d="m14 17 2-2-2-2"/></>,
users: <><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></>,
chart: <><path d="M3 3v18h18"/><rect x="7" y="13" width="3" height="6"/><rect x="12" y="9" width="3" height="10"/><rect x="17" y="5" width="3" height="14"/></>,
xCircle: <><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></>,
external: <><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></>,
eye: <><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></>,
sort: <><path d="m21 16-4 4-4-4"/><path d="M17 20V4"/><path d="m3 8 4-4 4 4"/><path d="M7 4v16"/></>,
arrowLeft: <><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></>,
sparkles: <><path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3L12 3z"/></>,
x: <><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></>,
}
return (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none"
stroke={color || 'currentColor'} strokeWidth={strokeWidth}
strokeLinecap="round" strokeLinejoin="round" style={style}>
{paths[name] || null}
</svg>
)
}
export function Button({ variant = 'primary', size = 'md', icon, iconRight, children, onClick, style, disabled }) {
const [hover, setHover] = useState(false)
const [pressed, setPressed] = useState(false)
const base = {
display: 'inline-flex', alignItems: 'center', gap: 8,
height: size === 'sm' ? 32 : size === 'lg' ? 48 : 40,
padding: size === 'sm' ? '0 12px' : size === 'lg' ? '0 24px' : '0 18px',
borderRadius: 8, fontFamily: 'Inter, sans-serif',
fontSize: size === 'sm' ? 13 : size === 'lg' ? 15 : 14,
fontWeight: 600, cursor: disabled ? 'not-allowed' : 'pointer',
border: '1px solid transparent',
transition: 'all .12s cubic-bezier(0.22,1,0.36,1)',
opacity: disabled ? 0.4 : 1,
transform: pressed ? 'scale(0.98)' : 'scale(1)',
whiteSpace: 'nowrap',
}
const variants = {
primary: {
background: hover ? '#22E0FF' : '#00D4FF', color: '#0A0F1C',
boxShadow: pressed ? 'none' : hover ? '0 0 32px rgba(0,212,255,0.4)' : '0 0 20px rgba(0,212,255,0.2)',
},
secondary: {
background: 'transparent', color: hover ? '#F8FAFC' : '#CBD5E1',
borderColor: hover ? 'rgba(148,163,184,0.30)' : 'rgba(148,163,184,0.12)',
},
ghost: {
background: hover ? 'rgba(148,163,184,0.06)' : 'transparent',
color: hover ? '#F8FAFC' : '#94A3B8', border: 'none',
},
danger: {
background: hover ? 'rgba(239,68,68,0.20)' : 'rgba(239,68,68,0.12)',
color: '#EF4444', borderColor: 'rgba(239,68,68,0.35)',
},
}
return (
<button style={{ ...base, ...variants[variant], ...style }}
onMouseEnter={() => setHover(true)} onMouseLeave={() => { setHover(false); setPressed(false) }}
onMouseDown={() => setPressed(true)} onMouseUp={() => setPressed(false)}
onClick={disabled ? undefined : onClick} disabled={disabled}>
{icon && <Icon name={icon} size={16}/>}
{children}
{iconRight && <Icon name={iconRight} size={16}/>}
</button>
)
}
export function Badge({ tone = 'cyan', children, dot = true, size = 'md' }) {
const tones = {
cyan: { bg: 'rgba(0,212,255,0.12)', bd: 'rgba(0,212,255,0.30)', fg: '#00D4FF' },
red: { bg: 'rgba(239,68,68,0.14)', bd: 'rgba(239,68,68,0.35)', fg: '#EF4444' },
amber: { bg: 'rgba(245,158,11,0.14)', bd: 'rgba(245,158,11,0.35)', fg: '#F59E0B' },
green: { bg: 'rgba(16,185,129,0.14)', bd: 'rgba(16,185,129,0.35)', fg: '#10B981' },
slate: { bg: 'rgba(148,163,184,0.10)',bd: 'rgba(148,163,184,0.25)',fg: '#94A3B8' },
}
const t = tones[tone]
return (
<span style={{
display: 'inline-flex', alignItems: 'center', gap: 6,
padding: size === 'sm' ? '3px 8px' : '5px 11px',
borderRadius: 999, fontSize: size === 'sm' ? 10 : 11, fontWeight: 600,
letterSpacing: '0.04em', border: `1px solid ${t.bd}`,
background: t.bg, color: t.fg, lineHeight: 1, whiteSpace: 'nowrap',
}}>
{dot && <span style={{ width: 5, height: 5, borderRadius: '50%', background: t.fg }}/>}
{children}
</span>
)
}
export function LangBadge({ lang }) {
const colors = { Python: '#3776AB', JavaScript: '#F7DF1E', Java: '#F89820', 'C++': '#00599C', Go: '#00ADD8', Rust: '#CE422B', Ruby: '#CC342D', TypeScript: '#3178C6' }
return (
<span style={{
display: 'inline-flex', alignItems: 'center', gap: 6,
padding: '4px 10px', borderRadius: 6,
fontFamily: 'JetBrains Mono, monospace', fontSize: 11, fontWeight: 500,
background: '#1F2937', border: '1px solid rgba(148,163,184,0.12)',
color: '#CBD5E1', lineHeight: 1,
}}>
<span style={{ width: 8, height: 8, borderRadius: 2, background: colors[lang] || '#94A3B8' }}/>
{lang || 'Unknown'}
</span>
)
}
export function Card({ children, style, glow, hoverable, onClick }) {
const [hover, setHover] = useState(false)
return (
<div onClick={onClick}
onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
style={{
background: 'rgba(17,24,39,0.6)',
border: `1px solid ${hover && hoverable ? 'rgba(148,163,184,0.24)' : 'rgba(148,163,184,0.12)'}`,
borderRadius: 12, padding: 20,
backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)',
boxShadow: glow
? 'inset 0 1px 0 rgba(255,255,255,0.03), 0 0 24px rgba(0,212,255,0.15)'
: 'inset 0 1px 0 rgba(255,255,255,0.03)',
cursor: onClick ? 'pointer' : 'default',
transition: 'border-color .12s cubic-bezier(0.22,1,0.36,1)',
...style,
}}>
{children}
</div>
)
}
export function Input({ icon, placeholder, value, onChange, style, type = 'text' }) {
const [focus, setFocus] = useState(false)
return (
<div style={{
display: 'flex', alignItems: 'center', gap: 8,
height: 40, padding: '0 14px',
background: '#1F2937',
border: `1px solid ${focus ? 'rgba(0,212,255,0.40)' : 'rgba(148,163,184,0.12)'}`,
borderRadius: 8,
boxShadow: focus ? '0 0 0 3px rgba(0,212,255,0.10)' : 'none',
transition: 'all .12s cubic-bezier(0.22,1,0.36,1)',
...style,
}}>
{icon && <Icon name={icon} size={16} color="#94A3B8"/>}
<input type={type} value={value} placeholder={placeholder}
onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
onChange={e => onChange && onChange(e.target.value)}
style={{
flex: 1, background: 'transparent', border: 'none', outline: 'none',
color: '#F8FAFC', fontFamily: 'Inter, sans-serif', fontSize: 14,
}}/>
</div>
)
}
export function Label({ children, style }) {
return (
<div style={{
fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.04em',
fontWeight: 500, color: '#94A3B8', ...style,
}}>{children}</div>
)
}
export function Spinner({ size = 32 }) {
return (
<div style={{
width: size, height: size, borderRadius: '50%',
border: '2px solid rgba(0,212,255,0.20)',
borderTopColor: '#00D4FF',
animation: 'spin 0.8s linear infinite',
}}/>
)
}