- react-router-dom 도입, /design 경로에 디자인 토큰/컴포넌트 카탈로그 페이지 추가 - SCAT 지도에서 하드코딩된 제주 해안선 좌표 제거, 인접 구간 기반 동적 방향 계산으로 전환 - @/ path alias 추가, SVG 아이콘 에셋 추가
490 lines
18 KiB
TypeScript
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" 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;
|