1560 lines
59 KiB
TypeScript
1560 lines
59 KiB
TypeScript
// TextFieldContent.tsx — WING-OPS Text Field 컴포넌트 상세 페이지 (다크/라이트 테마 지원)
|
||
|
||
import type { DesignTheme } from './designTheme';
|
||
|
||
// ---------- 타입 ----------
|
||
|
||
interface InputFieldStyle {
|
||
bg: string;
|
||
border: string;
|
||
textColor: string;
|
||
placeholderColor: string;
|
||
borderWidth?: string;
|
||
opacity?: string;
|
||
}
|
||
|
||
interface StateRow {
|
||
state: string;
|
||
badge: string;
|
||
placeholder: string;
|
||
hasCursor: boolean;
|
||
style: InputFieldStyle;
|
||
showClear?: boolean;
|
||
showSparkle?: boolean;
|
||
}
|
||
|
||
// ---------- 헬퍼 데이터 ----------
|
||
|
||
const getDarkStateRows = (): StateRow[] => [
|
||
{
|
||
state: 'Enabled',
|
||
badge: 'Enabled',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: { bg: '#1e293b', border: '#334155', textColor: '#e2e8f0', placeholderColor: '#64748b' },
|
||
},
|
||
{
|
||
state: 'Focused',
|
||
badge: 'Focused',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: true,
|
||
style: {
|
||
bg: '#1e293b',
|
||
border: '#e2e8f0',
|
||
textColor: '#e2e8f0',
|
||
placeholderColor: '#64748b',
|
||
borderWidth: '2px',
|
||
},
|
||
},
|
||
{
|
||
state: 'Error',
|
||
badge: 'Error',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: { bg: '#1e293b', border: '#ef4444', textColor: '#e2e8f0', placeholderColor: '#64748b' },
|
||
},
|
||
{
|
||
state: 'Error Focused',
|
||
badge: 'Error Focused',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: true,
|
||
style: {
|
||
bg: '#1e293b',
|
||
border: '#ef4444',
|
||
textColor: '#e2e8f0',
|
||
placeholderColor: '#64748b',
|
||
borderWidth: '2px',
|
||
},
|
||
},
|
||
{
|
||
state: 'Disabled',
|
||
badge: 'Disabled',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: {
|
||
bg: 'rgba(255,255,255,0.02)',
|
||
border: '#1e293b',
|
||
textColor: '#e2e8f0',
|
||
placeholderColor: '#64748b',
|
||
opacity: '0.4',
|
||
},
|
||
},
|
||
{
|
||
state: 'Read Only',
|
||
badge: 'Read Only',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: {
|
||
bg: 'rgba(255,255,255,0.02)',
|
||
border: '#334155',
|
||
textColor: '#e2e8f0',
|
||
placeholderColor: '#64748b',
|
||
},
|
||
},
|
||
{
|
||
state: 'AI Loading',
|
||
badge: 'AI Loading',
|
||
placeholder: '단서를 모아서 추리 중...',
|
||
hasCursor: false,
|
||
showSparkle: true,
|
||
style: {
|
||
bg: 'rgba(255,255,255,0.02)',
|
||
border: '#334155',
|
||
textColor: '#e2e8f0',
|
||
placeholderColor: '#64748b',
|
||
},
|
||
},
|
||
];
|
||
|
||
const getLightStateRows = (): StateRow[] => [
|
||
{
|
||
state: 'Enabled',
|
||
badge: 'Enabled',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: { bg: '#fff', border: '#d1d5db', textColor: '#1f2937', placeholderColor: '#9ca3af' },
|
||
},
|
||
{
|
||
state: 'Focused',
|
||
badge: 'Focused',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: true,
|
||
style: {
|
||
bg: '#fff',
|
||
border: '#1f2937',
|
||
textColor: '#1f2937',
|
||
placeholderColor: '#9ca3af',
|
||
borderWidth: '2px',
|
||
},
|
||
},
|
||
{
|
||
state: 'Error',
|
||
badge: 'Error',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: { bg: '#fff', border: '#ef4444', textColor: '#1f2937', placeholderColor: '#9ca3af' },
|
||
},
|
||
{
|
||
state: 'Error Focused',
|
||
badge: 'Error Focused',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: true,
|
||
style: {
|
||
bg: '#fff',
|
||
border: '#ef4444',
|
||
textColor: '#1f2937',
|
||
placeholderColor: '#9ca3af',
|
||
borderWidth: '2px',
|
||
},
|
||
},
|
||
{
|
||
state: 'Disabled',
|
||
badge: 'Disabled',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: {
|
||
bg: '#f9fafb',
|
||
border: '#e5e7eb',
|
||
textColor: '#1f2937',
|
||
placeholderColor: '#9ca3af',
|
||
opacity: '0.4',
|
||
},
|
||
},
|
||
{
|
||
state: 'Read Only',
|
||
badge: 'Read Only',
|
||
placeholder: '플레이스홀더',
|
||
hasCursor: false,
|
||
style: { bg: '#f9fafb', border: '#d1d5db', textColor: '#1f2937', placeholderColor: '#9ca3af' },
|
||
},
|
||
{
|
||
state: 'AI Loading',
|
||
badge: 'AI Loading',
|
||
placeholder: '단서를 모아서 추리 중...',
|
||
hasCursor: false,
|
||
showSparkle: true,
|
||
style: { bg: '#f9fafb', border: '#d1d5db', textColor: '#1f2937', placeholderColor: '#9ca3af' },
|
||
},
|
||
];
|
||
|
||
// ---------- Props ----------
|
||
|
||
interface TextFieldContentProps {
|
||
theme: DesignTheme;
|
||
}
|
||
|
||
// ---------- 컴포넌트 ----------
|
||
|
||
export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||
const t = theme;
|
||
const isDark = t.mode === 'dark';
|
||
|
||
const sectionCardBg = isDark ? 'rgba(255,255,255,0.03)' : '#f5f5f5';
|
||
const dividerColor = isDark ? 'rgba(255,255,255,0.08)' : '#e5e7eb';
|
||
const annotationColor = '#8b5cf6';
|
||
|
||
// 입력 필드 공통 스타일 (Anatomy, Guideline용)
|
||
const fieldBg = isDark ? '#1e293b' : '#fff';
|
||
const fieldBorder = isDark ? '#334155' : '#d1d5db';
|
||
const fieldText = isDark ? '#e2e8f0' : '#1f2937';
|
||
const fieldPlaceholder = isDark ? '#64748b' : '#9ca3af';
|
||
|
||
const stateRows = isDark ? getDarkStateRows() : getLightStateRows();
|
||
|
||
return (
|
||
<div className="p-12" style={{ color: t.textPrimary }}>
|
||
<div style={{ maxWidth: '64rem' }}>
|
||
{/* ── 섹션 1: 헤더 ── */}
|
||
<div className="pb-10 mb-12 border-b border-solid" style={{ borderColor: dividerColor }}>
|
||
<p
|
||
className="font-mono text-body-2 uppercase tracking-widest mb-3"
|
||
style={{ color: t.textAccent }}
|
||
>
|
||
Components
|
||
</p>
|
||
<h1 className="text-4xl font-bold mb-4" style={{ color: t.textPrimary }}>
|
||
Text Field
|
||
</h1>
|
||
<p className="text-lg" style={{ color: t.textSecondary }}>
|
||
사용자로부터 텍스트 데이터를 입력받는 기본 입력 컴포넌트입니다.
|
||
</p>
|
||
</div>
|
||
|
||
{/* ── Input Field 소제목 ── */}
|
||
<div className="pb-6 mb-10 border-b border-solid" style={{ borderColor: dividerColor }}>
|
||
<h2 className="text-2xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||
Input Field
|
||
</h2>
|
||
<p className="text-body-2" style={{ color: t.textSecondary }}>
|
||
단일 행 텍스트를 입력받는 필드입니다.
|
||
</p>
|
||
</div>
|
||
|
||
{/* ── 섹션 2: Anatomy ── */}
|
||
<div className="mb-16">
|
||
<h2 className="text-2xl font-bold mb-8" style={{ color: t.textPrimary }}>
|
||
Anatomy
|
||
</h2>
|
||
|
||
{/* Anatomy 카드 */}
|
||
<div className="rounded-xl p-10 mb-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex flex-col items-center gap-12">
|
||
{/* 입력 필드 구조 분해도 */}
|
||
<div className="relative flex flex-col items-center" style={{ width: '480px' }}>
|
||
{/* 상단 주석 라벨: Prefix, Input, Suffix */}
|
||
<div className="flex items-end justify-between w-full mb-1 px-2">
|
||
{/* Prefix label */}
|
||
<div className="flex flex-col items-center gap-0.5" style={{ width: '70px' }}>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Prefix
|
||
</span>
|
||
<span className="font-mono" style={{ fontSize: '9px', color: annotationColor }}>
|
||
(Optional)
|
||
</span>
|
||
{/* 아래 화살표 선 */}
|
||
<div
|
||
style={{ width: '1px', height: '10px', backgroundColor: annotationColor }}
|
||
/>
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderTop: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Input label */}
|
||
<div className="flex flex-col items-center gap-0.5">
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Input
|
||
</span>
|
||
{/* 아래 화살표 선 */}
|
||
<div
|
||
style={{ width: '1px', height: '18px', backgroundColor: annotationColor }}
|
||
/>
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderTop: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Suffix label */}
|
||
<div className="flex flex-col items-center gap-0.5" style={{ width: '70px' }}>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Suffix
|
||
</span>
|
||
<span className="font-mono" style={{ fontSize: '9px', color: annotationColor }}>
|
||
(Optional)
|
||
</span>
|
||
{/* 아래 화살표 선 */}
|
||
<div
|
||
style={{ width: '1px', height: '10px', backgroundColor: annotationColor }}
|
||
/>
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderTop: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 실제 입력 필드 */}
|
||
<div
|
||
className="relative w-full flex items-center rounded-md px-3"
|
||
style={{
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
}}
|
||
>
|
||
{/* Prefix 아이콘 영역 */}
|
||
<div
|
||
className="flex items-center justify-center mr-2 shrink-0"
|
||
style={{
|
||
width: '20px',
|
||
height: '20px',
|
||
borderRadius: '4px',
|
||
border: `1px dashed ${annotationColor}`,
|
||
}}
|
||
>
|
||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||
<circle cx="5" cy="5" r="3.5" stroke={fieldPlaceholder} strokeWidth="1.5" />
|
||
<line
|
||
x1="7.5"
|
||
y1="7.5"
|
||
x2="10.5"
|
||
y2="10.5"
|
||
stroke={fieldPlaceholder}
|
||
strokeWidth="1.5"
|
||
strokeLinecap="round"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
|
||
{/* 입력 텍스트 영역 */}
|
||
<div className="flex-1" style={{ fontSize: '14px', color: fieldText }}>
|
||
입력값 텍스트
|
||
</div>
|
||
|
||
{/* Clear 버튼 */}
|
||
<div
|
||
className="flex items-center justify-center shrink-0 ml-2"
|
||
style={{
|
||
width: '18px',
|
||
height: '18px',
|
||
borderRadius: '50%',
|
||
backgroundColor: fieldPlaceholder,
|
||
fontSize: '11px',
|
||
color: fieldBg,
|
||
fontWeight: 700,
|
||
lineHeight: 1,
|
||
cursor: 'default',
|
||
}}
|
||
>
|
||
×
|
||
</div>
|
||
|
||
{/* Suffix 텍스트 */}
|
||
<span
|
||
className="shrink-0 ml-2 font-mono text-body-2"
|
||
style={{ color: fieldPlaceholder }}
|
||
>
|
||
원
|
||
</span>
|
||
|
||
{/* Container 점선 테두리 */}
|
||
<span
|
||
className="absolute inset-0 rounded-md pointer-events-none"
|
||
style={{
|
||
border: `1.5px dashed ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Container 왼쪽 주석 */}
|
||
<div
|
||
className="absolute flex items-center gap-1"
|
||
style={{
|
||
left: '-120px',
|
||
top: '50%',
|
||
transform: 'translateY(-50%)',
|
||
}}
|
||
>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Container
|
||
</span>
|
||
{/* 화살표 선 */}
|
||
<div style={{ width: '30px', height: '1px', backgroundColor: annotationColor }} />
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderTop: '4px solid transparent',
|
||
borderBottom: '4px solid transparent',
|
||
borderLeft: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* 하단 주석 라벨: Clear Button */}
|
||
<div className="flex justify-end w-full mt-1 pr-16">
|
||
<div className="flex flex-col items-center gap-0.5">
|
||
{/* 위 화살표 선 */}
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderBottom: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
<div
|
||
style={{ width: '1px', height: '10px', backgroundColor: annotationColor }}
|
||
/>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Clear Button
|
||
</span>
|
||
<span className="font-mono" style={{ fontSize: '9px', color: annotationColor }}>
|
||
(Optional)
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── 섹션 3: Guideline ── */}
|
||
<div className="mb-16">
|
||
<h2 className="text-2xl font-bold mb-10" style={{ color: t.textPrimary }}>
|
||
Guideline
|
||
</h2>
|
||
|
||
{/* 3-1. Container */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
1
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Container
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
입력 필드의 외곽 영역입니다. 테두리, 곡률, 내부 여백을 정의합니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex items-center gap-8">
|
||
{/* 컨테이너 박스 + 치수선 */}
|
||
<div
|
||
className="relative"
|
||
style={{ marginLeft: '20px', marginTop: '20px', marginBottom: '24px' }}
|
||
>
|
||
{/* 높이 치수선 (오른쪽) */}
|
||
<div
|
||
className="absolute flex flex-col items-center"
|
||
style={{ right: '-32px', top: 0, bottom: 0, justifyContent: 'center' }}
|
||
>
|
||
<div style={{ width: '1px', flex: 1, backgroundColor: '#ef4444' }} />
|
||
<span
|
||
className="font-mono"
|
||
style={{
|
||
fontSize: '9px',
|
||
color: '#ef4444',
|
||
writingMode: 'vertical-rl',
|
||
padding: '2px 0',
|
||
}}
|
||
>
|
||
44px
|
||
</span>
|
||
<div style={{ width: '1px', flex: 1, backgroundColor: '#ef4444' }} />
|
||
</div>
|
||
|
||
{/* padding 치수선 (상단 왼쪽) */}
|
||
<span
|
||
className="absolute font-mono"
|
||
style={{ fontSize: '9px', color: '#ef4444', top: '-16px', left: '0' }}
|
||
>
|
||
px: 12px
|
||
</span>
|
||
|
||
{/* 빈 컨테이너 박스 */}
|
||
<div
|
||
className="rounded-md"
|
||
style={{
|
||
width: '280px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<p className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||
height: 44px (Medium)
|
||
</p>
|
||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||
padding: 12px (좌우)
|
||
</p>
|
||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||
border-radius: 6px
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 3-2. Placeholder */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
2
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Placeholder
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
값이 입력되지 않았을 때 표시되는 안내 텍스트입니다. 입력 시 사라집니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-6">
|
||
{/* 플레이스홀더 있는 필드 */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
플레이스홀더 있음
|
||
</span>
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
}}
|
||
>
|
||
검색어를 입력하세요
|
||
</div>
|
||
</div>
|
||
|
||
{/* 빈 필드 (플레이스홀더 없음) */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
플레이스홀더 없음
|
||
</span>
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 3-3. Label */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
3
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Label
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
입력 필드의 용도를 설명하는 텍스트입니다. 필수 항목은 * 표시로 구분합니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-8">
|
||
{/* 일반 라벨 */}
|
||
<div className="flex flex-col gap-1">
|
||
<span
|
||
className="text-body-2 font-semibold mb-1.5"
|
||
style={{ color: t.textPrimary }}
|
||
>
|
||
이름
|
||
</span>
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '200px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
}}
|
||
>
|
||
홍길동
|
||
</div>
|
||
</div>
|
||
|
||
{/* 필수 라벨 */}
|
||
<div className="flex flex-col gap-1">
|
||
<span
|
||
className="text-body-2 font-semibold mb-1.5"
|
||
style={{ color: t.textPrimary }}
|
||
>
|
||
이메일 <span style={{ color: '#ef4444' }}>*</span>
|
||
</span>
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '200px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
}}
|
||
>
|
||
이메일을 입력하세요
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 3-4. Input Text */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
4
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Input Text
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
사용자가 실제로 입력한 텍스트입니다. 플레이스홀더보다 진한 색상으로 표시됩니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex flex-col gap-4">
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '280px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldText,
|
||
}}
|
||
>
|
||
홍길동
|
||
</div>
|
||
<div
|
||
className="flex gap-6 text-caption font-mono"
|
||
style={{ color: t.textSecondary }}
|
||
>
|
||
<span>font-size: 14px</span>
|
||
<span>color: textPrimary</span>
|
||
<span>font-weight: 400</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 3-5. Clear Icon */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
5
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Clear Icon
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
입력값이 있을 때 표시되는 초기화 버튼입니다. 클릭 시 입력값을 삭제합니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-6">
|
||
{/* 텍스트 입력 + Clear 아이콘 표시 */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
입력값 있음 (Clear 표시)
|
||
</span>
|
||
<div
|
||
className="flex items-center rounded-md px-3 gap-2"
|
||
style={{
|
||
width: '240px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldText,
|
||
}}
|
||
>
|
||
<span className="flex-1">홍길동</span>
|
||
{/* Clear 버튼 */}
|
||
<div
|
||
className="flex items-center justify-center shrink-0"
|
||
style={{
|
||
width: '18px',
|
||
height: '18px',
|
||
borderRadius: '50%',
|
||
backgroundColor: fieldPlaceholder,
|
||
fontSize: '11px',
|
||
color: fieldBg,
|
||
fontWeight: 700,
|
||
lineHeight: 1,
|
||
cursor: 'default',
|
||
}}
|
||
>
|
||
×
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 빈 상태 (Clear 미표시) */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
입력값 없음 (Clear 미표시)
|
||
</span>
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
}}
|
||
>
|
||
플레이스홀더
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 3-6. Helper Text */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
6
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Helper Text
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
입력 필드 하단에 표시되는 보조 텍스트입니다. 안내 또는 에러 메시지로 사용됩니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-8">
|
||
{/* 기본 도움말 */}
|
||
<div className="flex flex-col gap-1.5">
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
}}
|
||
>
|
||
비밀번호
|
||
</div>
|
||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||
영문, 숫자 포함 8자 이상
|
||
</span>
|
||
</div>
|
||
|
||
{/* 에러 메시지 */}
|
||
<div className="flex flex-col gap-1.5">
|
||
<div
|
||
className="flex items-center rounded-md px-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '44px',
|
||
backgroundColor: fieldBg,
|
||
border: '1px solid #ef4444',
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
}}
|
||
>
|
||
비밀번호
|
||
</div>
|
||
<span className="text-caption" style={{ color: '#ef4444' }}>
|
||
필수 입력 항목입니다.
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── 섹션 4: State (Input Field) ── */}
|
||
<div className="pt-12 border-t border-solid" style={{ borderColor: dividerColor }}>
|
||
<h2 className="text-2xl font-bold mb-8" style={{ color: t.textPrimary }}>
|
||
State
|
||
</h2>
|
||
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex flex-col gap-5">
|
||
{stateRows.map((row) => (
|
||
<div key={row.state} className="flex items-center gap-8">
|
||
{/* 왼쪽: State 라벨 + 뱃지 */}
|
||
<div className="flex items-center gap-2 shrink-0" style={{ width: '200px' }}>
|
||
<span className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||
State
|
||
</span>
|
||
<span
|
||
className="rounded-md px-2 font-semibold"
|
||
style={{
|
||
fontSize: '11px',
|
||
paddingTop: '2px',
|
||
paddingBottom: '2px',
|
||
backgroundColor: '#3b82f6',
|
||
color: '#fff',
|
||
}}
|
||
>
|
||
{row.badge}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 오른쪽: 입력 필드 */}
|
||
<div
|
||
className="flex items-center rounded-md px-3 gap-2"
|
||
style={{
|
||
width: '320px',
|
||
height: '44px',
|
||
backgroundColor: row.style.bg,
|
||
border: `${row.style.borderWidth ?? '1px'} solid ${row.style.border}`,
|
||
opacity: row.style.opacity ?? '1',
|
||
fontSize: '14px',
|
||
}}
|
||
>
|
||
{row.showSparkle && <span style={{ fontSize: '16px' }}>✨</span>}
|
||
<span className="flex-1" style={{ color: row.style.placeholderColor }}>
|
||
{row.placeholder}
|
||
{row.hasCursor && (
|
||
<span
|
||
style={{
|
||
color: row.style.textColor,
|
||
fontWeight: 400,
|
||
marginLeft: '1px',
|
||
}}
|
||
>
|
||
|
|
||
</span>
|
||
)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ════════════════════════════════════════════════════
|
||
Text Area 단락
|
||
════════════════════════════════════════════════════ */}
|
||
|
||
{/* ── Text Area 소제목 ── */}
|
||
<div
|
||
className="pt-12 pb-6 mt-16 mb-10 border-t border-b border-solid"
|
||
style={{ borderColor: dividerColor }}
|
||
>
|
||
<h2 className="text-2xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||
Text Area
|
||
</h2>
|
||
<p className="text-body-2" style={{ color: t.textSecondary }}>
|
||
여러 줄의 텍스트를 입력받는 필드입니다.
|
||
</p>
|
||
</div>
|
||
|
||
{/* ── Text Area Anatomy ── */}
|
||
<div className="mb-16">
|
||
<h2 className="text-2xl font-bold mb-8" style={{ color: t.textPrimary }}>
|
||
Anatomy
|
||
</h2>
|
||
|
||
<div className="rounded-xl p-10 mb-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex flex-col items-center gap-12">
|
||
{/* TextArea 구조 분해도 */}
|
||
<div className="relative flex flex-col items-center" style={{ width: '480px' }}>
|
||
{/* 상단 주석 라벨: Input Area, Character Counter */}
|
||
<div className="flex items-end justify-between w-full mb-1 px-2">
|
||
{/* Input Area label */}
|
||
<div className="flex flex-col items-center gap-0.5" style={{ width: '80px' }}>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Input Area
|
||
</span>
|
||
<div
|
||
style={{ width: '1px', height: '18px', backgroundColor: annotationColor }}
|
||
/>
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderTop: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Placeholder label */}
|
||
<div className="flex flex-col items-center gap-0.5">
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Placeholder
|
||
</span>
|
||
<div
|
||
style={{ width: '1px', height: '18px', backgroundColor: annotationColor }}
|
||
/>
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderTop: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Character Counter label */}
|
||
<div className="flex flex-col items-center gap-0.5" style={{ width: '110px' }}>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Character Counter
|
||
</span>
|
||
<span className="font-mono" style={{ fontSize: '9px', color: annotationColor }}>
|
||
(Optional)
|
||
</span>
|
||
<div
|
||
style={{ width: '1px', height: '10px', backgroundColor: annotationColor }}
|
||
/>
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderTop: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 실제 TextArea */}
|
||
<div
|
||
className="relative w-full rounded-md p-3"
|
||
style={{
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
}}
|
||
>
|
||
{/* 플레이스홀더 텍스트 */}
|
||
<div
|
||
style={{
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
내용을 입력하세요
|
||
</div>
|
||
|
||
{/* 우하단: 문자 수 카운터 + resize 핸들 */}
|
||
<div className="absolute bottom-2 right-2 flex items-center gap-2">
|
||
<span
|
||
className="font-mono"
|
||
style={{ fontSize: '11px', color: fieldPlaceholder }}
|
||
>
|
||
0/500
|
||
</span>
|
||
{/* Resize 핸들 (대각선 줄무늬) */}
|
||
<div
|
||
style={{
|
||
width: '12px',
|
||
height: '12px',
|
||
backgroundImage: `repeating-linear-gradient(
|
||
135deg,
|
||
${fieldPlaceholder} 0px,
|
||
${fieldPlaceholder} 1px,
|
||
transparent 1px,
|
||
transparent 4px
|
||
)`,
|
||
cursor: 'nwse-resize',
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Container 점선 테두리 */}
|
||
<span
|
||
className="absolute inset-0 rounded-md pointer-events-none"
|
||
style={{
|
||
border: `1.5px dashed ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Container 왼쪽 주석 */}
|
||
<div
|
||
className="absolute flex items-center gap-1"
|
||
style={{
|
||
left: '-120px',
|
||
top: '50%',
|
||
transform: 'translateY(calc(-50% + 20px))',
|
||
}}
|
||
>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Container
|
||
</span>
|
||
<div style={{ width: '30px', height: '1px', backgroundColor: annotationColor }} />
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderTop: '4px solid transparent',
|
||
borderBottom: '4px solid transparent',
|
||
borderLeft: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* 하단 주석 라벨: Resize Handle */}
|
||
<div className="flex justify-end w-full mt-1 pr-1">
|
||
<div className="flex flex-col items-center gap-0.5">
|
||
<div
|
||
style={{
|
||
width: 0,
|
||
height: 0,
|
||
borderLeft: '4px solid transparent',
|
||
borderRight: '4px solid transparent',
|
||
borderBottom: `5px solid ${annotationColor}`,
|
||
}}
|
||
/>
|
||
<div
|
||
style={{ width: '1px', height: '10px', backgroundColor: annotationColor }}
|
||
/>
|
||
<span
|
||
className="font-mono text-caption font-semibold"
|
||
style={{ color: annotationColor }}
|
||
>
|
||
Resize Handle
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── Text Area Guideline ── */}
|
||
<div className="mb-16">
|
||
<h2 className="text-2xl font-bold mb-10" style={{ color: t.textPrimary }}>
|
||
Guideline
|
||
</h2>
|
||
|
||
{/* TA-1. Container */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
1
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Container
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
텍스트 영역의 외곽 컨테이너입니다. 기본 높이 112px이며 사용자가 리사이즈할 수
|
||
있습니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex items-start gap-8">
|
||
<div
|
||
className="relative"
|
||
style={{ marginLeft: '20px', marginTop: '20px', marginBottom: '24px' }}
|
||
>
|
||
{/* 높이 치수선 (오른쪽) */}
|
||
<div
|
||
className="absolute flex flex-col items-center"
|
||
style={{ right: '-36px', top: 0, bottom: 0, justifyContent: 'center' }}
|
||
>
|
||
<div style={{ width: '1px', flex: 1, backgroundColor: '#ef4444' }} />
|
||
<span
|
||
className="font-mono"
|
||
style={{
|
||
fontSize: '9px',
|
||
color: '#ef4444',
|
||
writingMode: 'vertical-rl',
|
||
padding: '2px 0',
|
||
}}
|
||
>
|
||
112px
|
||
</span>
|
||
<div style={{ width: '1px', flex: 1, backgroundColor: '#ef4444' }} />
|
||
</div>
|
||
|
||
{/* padding 치수선 (상단 왼쪽) */}
|
||
<span
|
||
className="absolute font-mono"
|
||
style={{ fontSize: '9px', color: '#ef4444', top: '-16px', left: '0' }}
|
||
>
|
||
p: 12px
|
||
</span>
|
||
|
||
{/* 빈 컨테이너 박스 */}
|
||
<div
|
||
className="rounded-md"
|
||
style={{
|
||
width: '280px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
<div className="pt-2">
|
||
<p className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||
height: 112px (default)
|
||
</p>
|
||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||
padding: 12px
|
||
</p>
|
||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||
border-radius: 6px
|
||
</p>
|
||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||
resize: vertical
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* TA-2. Placeholder */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
2
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Placeholder
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
값이 입력되지 않았을 때 표시되는 안내 텍스트입니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-6">
|
||
{/* 플레이스홀더 있는 TextArea */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
플레이스홀더 있음
|
||
</span>
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
내용을 입력하세요
|
||
</div>
|
||
</div>
|
||
|
||
{/* 빈 TextArea */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
플레이스홀더 없음
|
||
</span>
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* TA-3. Label */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
3
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Label
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
텍스트 영역의 용도를 설명하는 라벨입니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-8">
|
||
{/* 기본 라벨 */}
|
||
<div className="flex flex-col gap-1">
|
||
<span
|
||
className="text-body-2 font-semibold mb-1.5"
|
||
style={{ color: t.textPrimary }}
|
||
>
|
||
내용
|
||
</span>
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '200px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
내용을 입력하세요
|
||
</div>
|
||
</div>
|
||
|
||
{/* 필수(*) 라벨 */}
|
||
<div className="flex flex-col gap-1">
|
||
<span
|
||
className="text-body-2 font-semibold mb-1.5"
|
||
style={{ color: t.textPrimary }}
|
||
>
|
||
비고 <span style={{ color: '#ef4444' }}>*</span>
|
||
</span>
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '200px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
필수 항목입니다
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* TA-4. Input Text */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
4
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Input Text
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
사용자가 입력한 여러 줄의 텍스트입니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex flex-col gap-4">
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '320px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldText,
|
||
lineHeight: '1.6',
|
||
whiteSpace: 'pre-line',
|
||
}}
|
||
>
|
||
{'오늘 점검 내용을 기록합니다.\n상세 내용은 아래와 같습니다.'}
|
||
</div>
|
||
<div
|
||
className="flex gap-6 text-caption font-mono"
|
||
style={{ color: t.textSecondary }}
|
||
>
|
||
<span>font-size: 14px</span>
|
||
<span>color: textPrimary</span>
|
||
<span>line-height: 1.6</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* TA-5. Clear Icon */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
5
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Clear Icon
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
입력값 초기화 버튼입니다. 텍스트 영역 우상단에 표시됩니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-6">
|
||
{/* 텍스트 있는 상태 (Clear 표시) */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
입력값 있음 (Clear 표시)
|
||
</span>
|
||
<div
|
||
className="relative rounded-md p-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldText,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
입력된 내용이 있습니다.
|
||
{/* Clear 버튼 우상단 */}
|
||
<div
|
||
className="absolute top-2 right-2 flex items-center justify-center"
|
||
style={{
|
||
width: '18px',
|
||
height: '18px',
|
||
borderRadius: '50%',
|
||
backgroundColor: fieldPlaceholder,
|
||
fontSize: '11px',
|
||
color: fieldBg,
|
||
fontWeight: 700,
|
||
lineHeight: 1,
|
||
cursor: 'default',
|
||
}}
|
||
>
|
||
×
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 빈 상태 (Clear 미표시) */}
|
||
<div className="flex flex-col gap-2">
|
||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||
입력값 없음 (Clear 미표시)
|
||
</span>
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
내용을 입력하세요
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* TA-6. Helper Text */}
|
||
<div className="mb-12">
|
||
<div className="flex items-baseline gap-3 mb-2">
|
||
<span className="font-mono text-lg font-bold" style={{ color: annotationColor }}>
|
||
6
|
||
</span>
|
||
<h3 className="text-xl font-bold" style={{ color: t.textPrimary }}>
|
||
Helper Text
|
||
</h3>
|
||
</div>
|
||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||
텍스트 영역 하단의 도움말 또는 에러 메시지입니다.
|
||
</p>
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex gap-8">
|
||
{/* 기본 도움말 + 문자 수 카운터 */}
|
||
<div className="flex flex-col gap-1.5">
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: `1px solid ${fieldBorder}`,
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
내용을 입력하세요
|
||
</div>
|
||
<div className="flex items-center justify-between" style={{ width: '240px' }}>
|
||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||
상세 내용을 입력해 주세요
|
||
</span>
|
||
<span className="text-caption font-mono" style={{ color: t.textMuted }}>
|
||
0/500
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 에러 메시지 */}
|
||
<div className="flex flex-col gap-1.5">
|
||
<div
|
||
className="rounded-md p-3"
|
||
style={{
|
||
width: '240px',
|
||
height: '112px',
|
||
backgroundColor: fieldBg,
|
||
border: '1px solid #ef4444',
|
||
fontSize: '14px',
|
||
color: fieldPlaceholder,
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
내용을 입력하세요
|
||
</div>
|
||
<span className="text-caption" style={{ color: '#ef4444' }}>
|
||
필수 입력 항목입니다.
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── Text Area State ── */}
|
||
<div className="pt-12 border-t border-solid" style={{ borderColor: dividerColor }}>
|
||
<h2 className="text-2xl font-bold mb-8" style={{ color: t.textPrimary }}>
|
||
State
|
||
</h2>
|
||
|
||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||
<div className="flex flex-col gap-5">
|
||
{stateRows.map((row) => (
|
||
<div key={`ta-${row.state}`} className="flex items-start gap-8">
|
||
{/* 왼쪽: State 라벨 + 뱃지 */}
|
||
<div className="flex items-center gap-2 shrink-0 pt-3" style={{ width: '200px' }}>
|
||
<span className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||
State
|
||
</span>
|
||
<span
|
||
className="rounded-md px-2 font-semibold"
|
||
style={{
|
||
fontSize: '11px',
|
||
paddingTop: '2px',
|
||
paddingBottom: '2px',
|
||
backgroundColor: '#3b82f6',
|
||
color: '#fff',
|
||
}}
|
||
>
|
||
{row.badge}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 오른쪽: TextArea */}
|
||
<div
|
||
className="relative rounded-md p-3"
|
||
style={{
|
||
width: '320px',
|
||
height: '112px',
|
||
backgroundColor: row.style.bg,
|
||
border: `${row.style.borderWidth ?? '1px'} solid ${row.style.border}`,
|
||
opacity: row.style.opacity ?? '1',
|
||
fontSize: '14px',
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
<div className="flex items-start gap-2">
|
||
{row.showSparkle && <span style={{ fontSize: '16px' }}>✨</span>}
|
||
<span style={{ color: row.style.placeholderColor }}>
|
||
{row.placeholder}
|
||
{row.hasCursor && (
|
||
<span
|
||
style={{
|
||
color: row.style.textColor,
|
||
fontWeight: 400,
|
||
marginLeft: '1px',
|
||
}}
|
||
>
|
||
|
|
||
</span>
|
||
)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default TextFieldContent;
|