// 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 (
{/* ── 섹션 1: 헤더 ── */}

Components

Text Field

사용자로부터 텍스트 데이터를 입력받는 기본 입력 컴포넌트입니다.

{/* ── Input Field 소제목 ── */}

Input Field

단일 행 텍스트를 입력받는 필드입니다.

{/* ── 섹션 2: Anatomy ── */}

Anatomy

{/* Anatomy 카드 */}
{/* 입력 필드 구조 분해도 */}
{/* 상단 주석 라벨: Prefix, Input, Suffix */}
{/* Prefix label */}
Prefix (Optional) {/* 아래 화살표 선 */}
{/* Input label */}
Input {/* 아래 화살표 선 */}
{/* Suffix label */}
Suffix (Optional) {/* 아래 화살표 선 */}
{/* 실제 입력 필드 */}
{/* Prefix 아이콘 영역 */}
{/* 입력 텍스트 영역 */}
입력값 텍스트
{/* Clear 버튼 */}
×
{/* Suffix 텍스트 */} {/* Container 점선 테두리 */}
{/* Container 왼쪽 주석 */}
Container {/* 화살표 선 */}
{/* 하단 주석 라벨: Clear Button */}
{/* 위 화살표 선 */}
Clear Button (Optional)
{/* ── 섹션 3: Guideline ── */}

Guideline

{/* 3-1. Container */}
1

Container

입력 필드의 외곽 영역입니다. 테두리, 곡률, 내부 여백을 정의합니다.

{/* 컨테이너 박스 + 치수선 */}
{/* 높이 치수선 (오른쪽) */}
44px
{/* padding 치수선 (상단 왼쪽) */} px: 12px {/* 빈 컨테이너 박스 */}

height: 44px (Medium)

padding: 12px (좌우)

border-radius: 6px

{/* 3-2. Placeholder */}
2

Placeholder

값이 입력되지 않았을 때 표시되는 안내 텍스트입니다. 입력 시 사라집니다.

{/* 플레이스홀더 있는 필드 */}
플레이스홀더 있음
검색어를 입력하세요
{/* 빈 필드 (플레이스홀더 없음) */}
플레이스홀더 없음
{/* 3-3. Label */}
3

Label

입력 필드의 용도를 설명하는 텍스트입니다. 필수 항목은 * 표시로 구분합니다.

{/* 일반 라벨 */}
이름
홍길동
{/* 필수 라벨 */}
이메일 *
이메일을 입력하세요
{/* 3-4. Input Text */}
4

Input Text

사용자가 실제로 입력한 텍스트입니다. 플레이스홀더보다 진한 색상으로 표시됩니다.

홍길동
font-size: 14px color: textPrimary font-weight: 400
{/* 3-5. Clear Icon */}
5

Clear Icon

입력값이 있을 때 표시되는 초기화 버튼입니다. 클릭 시 입력값을 삭제합니다.

{/* 텍스트 입력 + Clear 아이콘 표시 */}
입력값 있음 (Clear 표시)
홍길동 {/* Clear 버튼 */}
×
{/* 빈 상태 (Clear 미표시) */}
입력값 없음 (Clear 미표시)
플레이스홀더
{/* 3-6. Helper Text */}
6

Helper Text

입력 필드 하단에 표시되는 보조 텍스트입니다. 안내 또는 에러 메시지로 사용됩니다.

{/* 기본 도움말 */}
비밀번호
영문, 숫자 포함 8자 이상
{/* 에러 메시지 */}
비밀번호
필수 입력 항목입니다.
{/* ── 섹션 4: State (Input Field) ── */}

State

{stateRows.map((row) => (
{/* 왼쪽: State 라벨 + 뱃지 */}
State {row.badge}
{/* 오른쪽: 입력 필드 */}
{row.showSparkle && } {row.placeholder} {row.hasCursor && ( | )}
))}
{/* ════════════════════════════════════════════════════ Text Area 단락 ════════════════════════════════════════════════════ */} {/* ── Text Area 소제목 ── */}

Text Area

여러 줄의 텍스트를 입력받는 필드입니다.

{/* ── Text Area Anatomy ── */}

Anatomy

{/* TextArea 구조 분해도 */}
{/* 상단 주석 라벨: Input Area, Character Counter */}
{/* Input Area label */}
Input Area
{/* Placeholder label */}
Placeholder
{/* Character Counter label */}
Character Counter (Optional)
{/* 실제 TextArea */}
{/* 플레이스홀더 텍스트 */}
내용을 입력하세요
{/* 우하단: 문자 수 카운터 + resize 핸들 */}
0/500 {/* Resize 핸들 (대각선 줄무늬) */}
{/* Container 점선 테두리 */}
{/* Container 왼쪽 주석 */}
Container
{/* 하단 주석 라벨: Resize Handle */}
Resize Handle
{/* ── Text Area Guideline ── */}

Guideline

{/* TA-1. Container */}
1

Container

텍스트 영역의 외곽 컨테이너입니다. 기본 높이 112px이며 사용자가 리사이즈할 수 있습니다.

{/* 높이 치수선 (오른쪽) */}
112px
{/* padding 치수선 (상단 왼쪽) */} p: 12px {/* 빈 컨테이너 박스 */}

height: 112px (default)

padding: 12px

border-radius: 6px

resize: vertical

{/* TA-2. Placeholder */}
2

Placeholder

값이 입력되지 않았을 때 표시되는 안내 텍스트입니다.

{/* 플레이스홀더 있는 TextArea */}
플레이스홀더 있음
내용을 입력하세요
{/* 빈 TextArea */}
플레이스홀더 없음
{/* TA-3. Label */}
3

Label

텍스트 영역의 용도를 설명하는 라벨입니다.

{/* 기본 라벨 */}
내용
내용을 입력하세요
{/* 필수(*) 라벨 */}
비고 *
필수 항목입니다
{/* TA-4. Input Text */}
4

Input Text

사용자가 입력한 여러 줄의 텍스트입니다.

{'오늘 점검 내용을 기록합니다.\n상세 내용은 아래와 같습니다.'}
font-size: 14px color: textPrimary line-height: 1.6
{/* TA-5. Clear Icon */}
5

Clear Icon

입력값 초기화 버튼입니다. 텍스트 영역 우상단에 표시됩니다.

{/* 텍스트 있는 상태 (Clear 표시) */}
입력값 있음 (Clear 표시)
입력된 내용이 있습니다. {/* Clear 버튼 우상단 */}
×
{/* 빈 상태 (Clear 미표시) */}
입력값 없음 (Clear 미표시)
내용을 입력하세요
{/* TA-6. Helper Text */}
6

Helper Text

텍스트 영역 하단의 도움말 또는 에러 메시지입니다.

{/* 기본 도움말 + 문자 수 카운터 */}
내용을 입력하세요
상세 내용을 입력해 주세요 0/500
{/* 에러 메시지 */}
내용을 입력하세요
필수 입력 항목입니다.
{/* ── Text Area State ── */}

State

{stateRows.map((row) => (
{/* 왼쪽: State 라벨 + 뱃지 */}
State {row.badge}
{/* 오른쪽: TextArea */}
{row.showSparkle && } {row.placeholder} {row.hasCursor && ( | )}
))}
); }; export default TextFieldContent;