215 lines
6.7 KiB
TypeScript
215 lines
6.7 KiB
TypeScript
// ComponentsOverview.tsx — Components 탭 Overview 카드 그리드
|
|
|
|
import type { DesignTheme } from './designTheme';
|
|
|
|
// ---------- 타입 ----------
|
|
|
|
interface OverviewCard {
|
|
id: string;
|
|
label: string;
|
|
thumbnail: (isDark: boolean) => React.ReactNode;
|
|
}
|
|
|
|
// ---------- 썸네일 구현 ----------
|
|
|
|
const ButtonsThumbnail = ({ isDark }: { isDark: boolean }) => {
|
|
const accent = isDark ? '#4cd7f6' : '#06b6d4';
|
|
const secondaryBg = isDark ? 'rgba(255,255,255,0.07)' : '#e2e8f0';
|
|
const secondaryText = isDark ? 'rgba(223,226,243,0.85)' : '#475569';
|
|
const outlineBorder = isDark ? 'rgba(76,215,246,0.40)' : 'rgba(6,182,212,0.50)';
|
|
|
|
const buttons = [
|
|
{ label: 'Primary', bg: accent, border: accent, color: isDark ? '#0a0e1a' : '#ffffff' },
|
|
{ label: 'Secondary', bg: secondaryBg, border: 'transparent', color: secondaryText },
|
|
{ label: 'Outline', bg: 'transparent', border: outlineBorder, color: accent },
|
|
];
|
|
|
|
return (
|
|
<div className="w-full h-full flex flex-col items-center justify-center gap-3 px-8">
|
|
{buttons.map(({ label, bg, border, color }) => (
|
|
<div
|
|
key={label}
|
|
className="w-full rounded flex items-center justify-center"
|
|
style={{
|
|
height: '32px',
|
|
backgroundColor: bg,
|
|
border: `1.5px solid ${border}`,
|
|
color,
|
|
fontSize: '12px',
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
{label}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const TextInputsThumbnail = ({ isDark }: { isDark: boolean }) => {
|
|
const labelColor = isDark ? 'rgba(194,198,214,0.80)' : '#64748b';
|
|
const inputBg = isDark ? 'rgba(255,255,255,0.04)' : '#ffffff';
|
|
const inputBorder = isDark ? 'rgba(255,255,255,0.12)' : '#cbd5e1';
|
|
const placeholderColor = isDark ? 'rgba(140,144,159,0.60)' : '#94a3b8';
|
|
const accentBorder = isDark ? '#4cd7f6' : '#06b6d4';
|
|
|
|
return (
|
|
<div className="w-full h-full flex flex-col items-center justify-center gap-3 px-8">
|
|
{/* 라벨 + 기본 입력 */}
|
|
<div className="w-full flex flex-col gap-1.5">
|
|
<div
|
|
className="rounded"
|
|
style={{ height: '9px', width: '48px', backgroundColor: labelColor, opacity: 0.6 }}
|
|
/>
|
|
<div
|
|
className="w-full rounded flex items-center px-3"
|
|
style={{
|
|
height: '30px',
|
|
backgroundColor: inputBg,
|
|
border: `1.5px solid ${inputBorder}`,
|
|
}}
|
|
>
|
|
<div
|
|
className="rounded"
|
|
style={{
|
|
height: '8px',
|
|
width: '70px',
|
|
backgroundColor: placeholderColor,
|
|
opacity: 0.5,
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{/* 포커스 상태 입력 */}
|
|
<div className="w-full flex flex-col gap-1.5">
|
|
<div
|
|
className="rounded"
|
|
style={{ height: '9px', width: '56px', backgroundColor: labelColor, opacity: 0.6 }}
|
|
/>
|
|
<div
|
|
className="w-full rounded flex items-center px-3"
|
|
style={{
|
|
height: '30px',
|
|
backgroundColor: inputBg,
|
|
border: `1.5px solid ${accentBorder}`,
|
|
boxShadow: isDark
|
|
? `0 0 0 2px rgba(76,215,246,0.15)`
|
|
: `0 0 0 2px rgba(6,182,212,0.12)`,
|
|
}}
|
|
>
|
|
<div
|
|
className="rounded"
|
|
style={{
|
|
height: '8px',
|
|
width: '90px',
|
|
backgroundColor: isDark ? 'rgba(223,226,243,0.50)' : '#475569',
|
|
opacity: 0.7,
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// ---------- 카드 정의 ----------
|
|
|
|
const OVERVIEW_CARDS: OverviewCard[] = [
|
|
{
|
|
id: 'buttons',
|
|
label: 'Buttons',
|
|
thumbnail: (isDark) => <ButtonsThumbnail isDark={isDark} />,
|
|
},
|
|
{
|
|
id: 'text-field',
|
|
label: 'Text Field',
|
|
thumbnail: (isDark) => <TextInputsThumbnail isDark={isDark} />,
|
|
},
|
|
];
|
|
|
|
// ---------- Props ----------
|
|
|
|
interface ComponentsOverviewProps {
|
|
theme: DesignTheme;
|
|
onNavigate: (id: string) => void;
|
|
}
|
|
|
|
// ---------- 컴포넌트 ----------
|
|
|
|
const ComponentsOverview = ({ theme, onNavigate }: ComponentsOverviewProps) => {
|
|
const t = theme;
|
|
const isDark = t.mode === 'dark';
|
|
|
|
const cardBg = isDark ? 'rgba(255,255,255,0.03)' : '#f5f5f5';
|
|
const cardBorder = isDark ? 'rgba(255,255,255,0.06)' : '#e5e5e5';
|
|
const thumbnailBorderBottom = isDark ? 'rgba(255,255,255,0.06)' : '#e0e0e0';
|
|
|
|
return (
|
|
<div className="pt-24 px-12 pb-16 max-w-5xl flex flex-col gap-12">
|
|
{/* ── 헤더 영역 ── */}
|
|
<div className="flex flex-col gap-3">
|
|
<span
|
|
className="font-mono text-xs font-semibold uppercase"
|
|
style={{ letterSpacing: '1.4px', color: t.textAccent }}
|
|
>
|
|
Components
|
|
</span>
|
|
<h1 className="font-sans text-4xl font-bold leading-tight" style={{ color: t.textPrimary }}>
|
|
Overview
|
|
</h1>
|
|
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
|
재사용 가능한 UI 컴포넌트 카탈로그입니다.
|
|
</p>
|
|
</div>
|
|
|
|
{/* ── 3열 카드 그리드 ── */}
|
|
<div className="grid gap-5" style={{ gridTemplateColumns: 'repeat(3, 1fr)' }}>
|
|
{OVERVIEW_CARDS.map((card) => (
|
|
<div
|
|
key={card.id}
|
|
className="rounded-lg border border-solid flex flex-col cursor-pointer transition-all duration-200"
|
|
style={{
|
|
backgroundColor: cardBg,
|
|
borderColor: cardBorder,
|
|
}}
|
|
onClick={() => onNavigate(card.id)}
|
|
onMouseEnter={(e) => {
|
|
const el = e.currentTarget;
|
|
el.style.transform = 'scale(1.025)';
|
|
el.style.boxShadow = isDark
|
|
? '0 8px 24px rgba(0,0,0,0.35)'
|
|
: '0 6px 18px rgba(0,0,0,0.10)';
|
|
el.style.borderColor = isDark ? 'rgba(76,215,246,0.22)' : 'rgba(6,182,212,0.28)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
const el = e.currentTarget;
|
|
el.style.transform = 'scale(1)';
|
|
el.style.boxShadow = 'none';
|
|
el.style.borderColor = cardBorder;
|
|
}}
|
|
>
|
|
{/* 썸네일 영역 */}
|
|
<div
|
|
className="h-48 rounded-t-lg overflow-hidden"
|
|
style={{
|
|
borderBottom: `1px solid ${thumbnailBorderBottom}`,
|
|
}}
|
|
>
|
|
{card.thumbnail(isDark)}
|
|
</div>
|
|
|
|
{/* 카드 라벨 */}
|
|
<div className="px-5 py-4">
|
|
<span className="font-sans text-sm font-semibold" style={{ color: t.textPrimary }}>
|
|
{card.label}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ComponentsOverview;
|