wing-ops/frontend/src/pages/design/DesignContent.tsx
leedano d0491c3f0f feat(design): Stitch MCP 기반 디자인 시스템 카탈로그 + SCAT 하드코딩 해안선 제거
- react-router-dom 도입, /design 경로에 디자인 토큰/컴포넌트 카탈로그 페이지 추가
- SCAT 지도에서 하드코딩된 제주 해안선 좌표 제거, 인접 구간 기반 동적 방향 계산으로 전환
- @/ path alias 추가, SVG 아이콘 에셋 추가
2026-03-24 16:36:50 +09:00

490 lines
18 KiB
TypeScript

// DesignContent.tsx — 디자인 토큰 탭 콘텐츠 (다크/라이트 테마 지원)
import type { DesignTheme } from './designTheme';
// ---------- 06 타이포그래피 스케일 데이터 ----------
interface TypoRow {
size: string;
sampleNode: (theme: DesignTheme) => React.ReactNode;
properties: string;
isData?: boolean;
}
// ---------- 공통 섹션 타이틀 ----------
interface SectionTitleProps {
num: string;
title: string;
sub?: string;
rightNode?: React.ReactNode;
theme: DesignTheme;
}
const SectionTitle = ({ num, title, sub, rightNode, theme }: SectionTitleProps) => (
<div className="flex flex-col gap-1">
<div className="flex flex-row items-center justify-between">
<p
className="font-sans text-lg leading-7 font-bold"
style={{ letterSpacing: '0.45px', color: theme.sectionTitle }}
>
{num} {title}
</p>
{rightNode}
</div>
{sub && (
<p
className="font-mono text-[10px] leading-[15px] uppercase"
style={{ letterSpacing: theme.sectionSubSpacing, color: theme.sectionSub }}
>
{sub}
</p>
)}
</div>
);
// ---------- 타이포그래피 행 ----------
const TYPO_ROWS: TypoRow[] = [
{
size: '9px / Meta',
sampleNode: (t) => (
<span className="font-korean text-[9px]" style={{ color: t.typoSampleText }}> Meta info</span>
),
properties: 'Regular / 400',
},
{
size: '10px / Table',
sampleNode: (t) => (
<span className="font-korean text-[10px]" style={{ color: t.typoSampleText }}> Table data</span>
),
properties: 'Medium / 500',
},
{
size: '11px / Action',
sampleNode: (t) => (
<span
className="inline-flex items-center rounded-md border border-solid py-2 px-4"
style={{ backgroundColor: t.typoActionBg, borderColor: t.typoActionBorder }}
>
<span className="font-korean text-[11px] font-medium" style={{ color: t.typoActionText }}>
/ Input/Button text
</span>
</span>
),
properties: 'Medium / 500',
},
{
size: '13px / Header',
sampleNode: (t) => (
<span className="font-korean text-[13px] font-bold" style={{ color: t.textPrimary }}>
Section Header
</span>
),
properties: 'Bold / 700',
},
{
size: '15px / Title',
sampleNode: (t) => (
<span className="font-korean text-[15px] font-bold" style={{ color: t.textPrimary }}>
Panel Title
</span>
),
properties: 'ExtraBold / 800',
},
{
size: 'Data / Mono',
sampleNode: (t) => (
<div className="flex flex-col gap-1">
<span className="font-mono text-[11px]" style={{ color: t.typoDataText }}>1,234.56 km²</span>
<span className="font-mono text-[11px]" style={{ color: t.typoCoordText }}>35° 06' 12&quot; N</span>
</div>
),
properties: 'JetBrains Mono / 11px',
isData: true,
},
];
// ---------- 컴포넌트 ----------
export interface DesignContentProps {
theme: DesignTheme;
}
export const DesignContent = ({ theme }: DesignContentProps) => {
const t = theme;
const isDark = t.mode === 'dark';
return (
<div className="pt-24 px-8 pb-16 flex flex-col gap-12 items-start justify-start max-w-[1440px]">
{/* ── 헤더 섹션 ── */}
<div
className="w-full border-b border-solid pb-8 flex flex-row items-end justify-between"
style={{ borderColor: isDark ? 'rgba(66,71,84,0.20)' : '#e2e8f0' }}
>
<div className="flex flex-col gap-2">
<h1
className="font-sans text-4xl leading-10 font-bold"
style={{ letterSpacing: '-0.9px', color: t.textPrimary }}
>
디자인 토큰
</h1>
<p className="font-korean text-base leading-6 font-light" style={{ color: t.textSecondary }}>
Comprehensive design token reference for the WING-OPS operational interface.
</p>
</div>
{/* 상태 뱃지 */}
<div
className="rounded border border-solid p-2 flex flex-row gap-3 items-center shrink-0"
style={{
backgroundColor: t.systemActiveBg,
borderColor: t.systemActiveBorder,
boxShadow: isDark ? 'none' : t.systemActiveShadow,
}}
>
<span
className="rounded-xl w-3 h-3 shrink-0"
style={{ backgroundColor: t.textAccent, boxShadow: t.systemActiveShadow }}
/>
<span
className="font-mono text-xs leading-4 uppercase"
style={{ letterSpacing: '1.2px', color: t.textAccent }}
>
System Active
</span>
</div>
</div>
{/* ── 2컬럼 그리드 ── */}
<div
className="w-full grid gap-10"
style={{ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))' }}
>
{/* ── 행 1 좌측: 01 배경색 ── */}
<div className="flex flex-col gap-4">
<SectionTitle num="01" title="배경색" sub="Surface Hierarchy" theme={t} />
<div className="flex flex-col gap-4">
{t.bgTokens.map((item) => (
<div
key={item.token}
className="rounded-[10px] border border-solid p-6 flex flex-row gap-4 items-center"
style={{
backgroundColor: isDark ? item.bg : t.cardBg,
borderColor: item.isHover ? t.cardBorderHover : t.cardBorder,
boxShadow: t.cardShadow,
}}
>
{/* 색상 스와치 */}
<div
className="w-24 h-24 rounded-sm border border-solid shrink-0"
style={{
backgroundColor: item.bg,
borderColor: item.isHover ? t.swatchBorderHover : t.swatchBorder,
boxShadow: 'inset 0px 2px 4px 1px rgba(0, 0, 0, 0.05)',
}}
/>
{/* 정보 */}
<div className="flex flex-col gap-1">
<span className="font-mono text-xs leading-4" style={{ color: t.textAccent }}>{item.token}</span>
<span className="font-korean text-sm leading-5 font-bold" style={{ color: t.textPrimary }}>
{item.hex}
</span>
<span className="font-korean text-[11px] leading-[16.5px]" style={{ color: t.textSecondary }}>
{item.desc}
</span>
</div>
</div>
))}
</div>
</div>
{/* ── 행 1 우측: 02 테두리 색상 + 03 텍스트 색상 ── */}
<div className="flex flex-col gap-8">
{/* 02 테두리 색상 */}
<div className="flex flex-col gap-4">
<SectionTitle num="02" title="테두리 색상" theme={t} />
<div className="grid grid-cols-2 gap-4">
{t.borderTokens.map((item) => (
<div
key={item.token}
className="rounded-md border border-solid p-4 flex flex-col gap-2"
style={{
backgroundColor: t.borderCardBg,
borderColor: item.border,
boxShadow: t.borderCardShadow,
}}
>
<span className="font-mono text-[10px]" style={{ color: t.textAccent }}>{item.token}</span>
<span className="font-korean text-sm font-bold pb-2" style={{ color: t.textPrimary }}>
{item.hex}
</span>
<div className="h-1 self-stretch rounded-sm" style={{ backgroundColor: item.barBg }} />
</div>
))}
</div>
</div>
{/* 03 텍스트 색상 */}
<div className="flex flex-col gap-4">
<SectionTitle num="03" title="텍스트 색상" theme={t} />
<div
className="rounded-lg border border-solid p-8 flex flex-col gap-6"
style={{
backgroundColor: t.textSectionBg,
borderColor: t.textSectionBorder,
}}
>
{t.textTokens.map((item) => (
<div key={item.token} className="flex flex-col gap-[3px]">
<span className="font-mono text-[10px]" style={{ color: t.textAccent }}>{item.token}</span>
<span className={item.sampleClass}>{item.sampleText}</span>
<span className="font-korean text-xs" style={{ color: item.descColor }}>
{item.desc}
</span>
</div>
))}
</div>
</div>
</div>
{/* ── 행 2 좌측: 04 강조 색상 ── */}
<div className="flex flex-col gap-4">
<SectionTitle num="04" title="강조 색상" theme={t} />
<div className="flex flex-col gap-4">
{t.accentTokens.map((item) => (
<div
key={item.token}
className="rounded-lg border border-solid p-4 flex flex-row items-center justify-between"
style={{
backgroundColor: t.cardBg,
borderColor: t.cardBorder,
boxShadow: t.cardShadow,
}}
>
{/* 좌측: 색상 원 + 이름/토큰 */}
<div className="flex flex-row items-center gap-4">
<div
className={`w-12 h-12 ${t.badgeRadius} shrink-0`}
style={{
backgroundColor: item.color,
boxShadow: item.glow,
}}
/>
<div className="flex flex-col gap-1">
<span className="font-korean text-sm font-bold" style={{ color: t.textPrimary }}>{item.name}</span>
<span className="font-mono text-[10px]" style={{ color: t.textMuted }}>
{item.token} / {item.color}
</span>
</div>
</div>
{/* 우측: 뱃지 */}
<div
className={`${t.badgeRadius} border border-solid py-1 px-3`}
style={{
backgroundColor: item.badgeBg,
borderColor: item.badgeBorder,
}}
>
<span
className="font-korean text-[11px] font-medium"
style={{ color: item.badgeText }}
>
{item.badge}
</span>
</div>
</div>
))}
</div>
</div>
{/* ── 행 2 우측: 05 상태 표시기 ── */}
<div className="flex flex-col gap-4">
<SectionTitle num="05" title="상태 표시기" theme={t} />
<div className="grid grid-cols-2 gap-4">
{t.statusTokens.map((item) => (
<div
key={item.hex}
className={`${t.badgeRadius} border border-solid p-4 flex flex-row gap-3 items-center`}
style={{
backgroundColor: item.bg,
borderColor: item.border,
}}
>
<span
className={`w-2.5 h-2.5 ${t.badgeRadius} shrink-0`}
style={{
backgroundColor: item.color,
...(item.glow ? { boxShadow: item.glow } : {}),
}}
/>
<span
className="font-korean text-[13px] font-bold flex-1"
style={{ color: item.color }}
>
{item.label}
</span>
<span className="font-mono text-[10px] opacity-40" style={{ color: item.color }}>
{item.hex}
</span>
</div>
))}
</div>
</div>
{/* ── 행 3: 06 타이포그래피 스케일 (전체 열 span) ── */}
<div className="col-span-2 flex flex-col gap-4">
<SectionTitle
num="06"
title="타이포그래피 스케일"
theme={t}
rightNode={
<div className="flex flex-row gap-2 items-center">
<span
className="rounded-sm py-0.5 px-2 font-korean text-[10px] font-bold"
style={{ backgroundColor: t.fontBadgePrimaryBg, color: t.fontBadgePrimaryText }}
>
Noto Sans KR
</span>
<span
className="rounded-sm border border-solid py-0.5 px-2 font-korean text-[10px] font-bold"
style={{ borderColor: t.fontBadgeSecondaryBorder, color: t.fontBadgeSecondaryText }}
>
JetBrains Mono
</span>
</div>
}
/>
<div
className="rounded-lg border border-solid overflow-hidden w-full"
style={{ backgroundColor: t.tableContainerBg, borderColor: t.cardBorder }}
>
{/* 헤더 행 */}
<div className="flex flex-row items-start" style={{ backgroundColor: t.tableHeaderBg }}>
{(['Size / Role', 'Sample String', 'Properties'] as const).map((col, i) => (
<div
key={col}
className="flex-1 py-4 px-8 border-b border-solid"
style={{ textAlign: i === 2 ? 'right' : 'left', borderColor: t.tableRowBorder }}
>
<span
className="font-mono text-[10px] font-medium uppercase"
style={{ letterSpacing: '1px', color: t.textMuted }}
>
{col}
</span>
</div>
))}
</div>
{/* 데이터 행 */}
{TYPO_ROWS.map((row) => (
<div
key={row.size}
className="flex flex-row items-center border-t border-solid"
style={{
borderColor: t.tableRowBorder,
backgroundColor: row.isData ? t.tableDataRowBg : undefined,
}}
>
{/* Size */}
<div className="flex-1 py-4 px-8">
<span className="font-mono text-[10px]" style={{ color: t.typoSizeText }}>{row.size}</span>
</div>
{/* Sample */}
<div className="flex-1 py-4 px-8">{row.sampleNode(t)}</div>
{/* Properties */}
<div className="flex-1 py-4 px-8 text-right" style={{ opacity: 0.5 }}>
<span className="font-mono text-[10px]" style={{ color: t.typoPropertiesText }}>{row.properties}</span>
</div>
</div>
))}
</div>
</div>
{/* ── 행 4: 07 테두리 곡률 (전체 열 span) ── */}
<div className="col-span-2 flex flex-col gap-4">
<SectionTitle num="07" title="테두리 곡률" theme={t} />
<div className="grid grid-cols-2 gap-8">
{/* radius-sm */}
<div className="flex flex-col gap-2">
<span className="font-mono text-xs leading-4" style={{ color: t.textMuted }}>{t.radiusSmLabel}</span>
<div
className="rounded-md border border-solid p-6 h-32 flex flex-col justify-end"
style={{
backgroundColor: t.radiusCardBg,
borderColor: t.radiusCardBorder,
boxShadow: t.radiusCardShadow,
}}
>
<span
className="font-korean text-[10px] font-bold uppercase"
style={{ letterSpacing: '1px', color: t.textAccent }}
>
Small Elements
</span>
<p className="font-korean text-xs leading-[19.5px] mt-1" style={{ color: t.radiusDescText }}>
Applied to tactical buttons, search inputs, and micro-cards for a precise, sharp industrial feel.
</p>
</div>
</div>
{/* radius-md */}
<div className="flex flex-col gap-2">
<span className="font-mono text-xs leading-4" style={{ color: t.textMuted }}>{t.radiusMdLabel}</span>
<div
className="rounded-[10px] border border-solid p-6 h-32 flex flex-col justify-end"
style={{
backgroundColor: t.radiusCardBg,
borderColor: t.radiusCardBorder,
boxShadow: t.radiusCardShadow,
}}
>
<span
className="font-korean text-[10px] font-bold uppercase"
style={{ letterSpacing: '1px', color: t.textAccent }}
>
Structural Panels
</span>
<p className="font-korean text-xs leading-[19.5px] mt-1" style={{ color: t.radiusDescText }}>
Applied to telemetry cards, floating modals, and primary operational panels to soften high-density data.
</p>
</div>
</div>
</div>
</div>
</div>
{/* ── 푸터 ── */}
<div
className="w-full border-t border-solid pt-12 flex flex-row items-center justify-between"
style={{ borderColor: t.footerBorder }}
>
{/* 좌측 */}
<div className="flex flex-row gap-8">
{['Precision Engineering', 'Safety Compliant', 'Optimized v8.42'].map((label) => (
<span
key={label}
className="font-mono text-[10px] uppercase"
style={{ letterSpacing: '1px', color: t.footerText }}
>
{label}
</span>
))}
</div>
{/* 우측 */}
<div className="flex flex-row gap-2 items-center">
<span
className="font-mono text-[10px] uppercase"
style={{ letterSpacing: '1px', color: t.footerText }}
>
Generated for Terminal:
</span>
<span
className="font-mono text-[10px] font-medium uppercase"
style={{ letterSpacing: '1px', color: t.footerAccent }}
>
1440x900_PR_MKT
</span>
</div>
</div>
</div>
);
};
export default DesignContent;