Files
invyone/frontend/types/component.ts
T
gbpark 1aa48cc0bb INVYONE 화면 디자이너 개편 + 컴포넌트 86→8 통합
## 디자인 개편
- IDE 톤 CSS 오버라이드 (builder-ide.css)
- 컴팩트화 (폰트/간격/패딩 축소)
- INVYONE STUDIO 로고 추가 (SlimToolbar)
- 좌측 수평 탭 → 수직 아코디언 (details/summary)
- 우측 속성 패널 신설 (V2PropertiesPanel 완전 이주)
- 다크모드 지원 (7개 통합 컴포넌트 inline hex → CSS 변수)

## 기반 시스템
- ScreenDefinition.fields/connections 타입 확장
- ComponentDefinition.dataPorts 타입 확장
- FieldConfig adapters (fieldsToColumns/Search/Form)
- DataPortBus + setupConnections runtime
- FieldsPanel (화면 수준 필드 관리 패널)

## 컴포넌트 통합 (Phase A~C)
- divider (3→1): 가로/세로 + 텍스트 구분선
- title (2→1): h1~h6/body/caption variant
- button (3→1): 6 variant × 13 actionType
- search (3→1): inline/stacked 검색 필터
- input (20+→1): FieldConfig.type 10종 내부 분기
- stats (6→1): card/chip/bigNumber 3종 스타일
- table (9→1): table/split/grouped/pivot/card 5종 displayMode
- container (11→1): tabs/section/accordion/repeater/conditional 5종

## 버그 수정 (기존 VEX 코드)
- 드래그 드롭 불가 (defaultSize camelCase 불일치)
- 설정 변경 미반영 (componentConfig vs component_config)
- ConfigPanel 미인식 (config_panel vs configPanel)
- v2- 자동 매핑 함정 (INVYONE_UNIFIED_IDS 화이트리스트)
- LayerManagerPanel 무한 API 호출 (useEffect deps)
- Button size 이름 충돌 (visual size 객체 vs config string)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 20:37:23 +09:00

396 lines
12 KiB
TypeScript

/**
* 컴포넌트 시스템 타입 정의
* 레이아웃 시스템과 동일한 패턴으로 설계된 새로운 컴포넌트 관리 시스템
*/
import React from "react";
import type { WebType, AutoGenerationConfig } from "./screen";
import type { DataPort } from "./invyone-component";
/**
* 컴포넌트 카테고리 열거형
* 기존의 하드코딩된 카테고리를 타입 안전한 enum으로 대체
*/
export enum ComponentCategory {
UI = "ui", // 기본 UI 컴포넌트 (버튼, 입력 등)
INPUT = "input", // 입력 컴포넌트 (텍스트, 셀렉트, 체크박스 등)
DISPLAY = "display", // 표시 컴포넌트 (라벨, 이미지, 아이콘 등)
ACTION = "action", // 액션 컴포넌트 (버튼, 링크 등)
LAYOUT = "layout", // 레이아웃 컴포넌트 (컨테이너, 그룹 등)
DATA = "data", // 데이터 컴포넌트 (테이블, 리스트 등)
CHART = "chart", // 차트 컴포넌트 (그래프, 차트 등)
FORM = "form", // 폼 컴포넌트 (폼 그룹, 필드셋 등)
MEDIA = "media", // 미디어 컴포넌트 (이미지, 비디오 등)
NAVIGATION = "navigation", // 네비게이션 컴포넌트 (메뉴, 탭 등)
FEEDBACK = "feedback", // 피드백 컴포넌트 (알림, 토스트 등)
UTILITY = "utility", // 유틸리티 컴포넌트 (로딩, 스피너 등)
CONTAINER = "container", // 컨테이너 컴포넌트 (패널, 카드 등)
SYSTEM = "system", // 시스템 컴포넌트 (에러 바운더리 등)
ADMIN = "admin", // 관리자 전용 컴포넌트
CUSTOM = "custom", // 커스텀 컴포넌트
V2 = "v2", // 통합 컴포넌트 (새로운 V2 컴포넌트 시스템)
}
/**
* 컴포넌트 설정 인터페이스
* 각 컴포넌트의 고유한 설정값들을 타입 안전하게 관리
*/
export interface ComponentConfig {
[key: string]: any;
}
/**
* 컴포넌트 크기 정의
*/
export interface ComponentSize {
width: number;
height: number;
}
// screen.ts에서 자동생성 관련 타입들을 import
export type { AutoGenerationType, AutoGenerationConfig } from "./screen";
/**
* 컴포넌트 렌더러 Props
* 모든 컴포넌트 렌더러가 받는 공통 Props
*/
export interface ComponentRendererProps {
component: any; // ComponentData from screen.ts
isDesignMode?: boolean;
isSelected?: boolean;
isInteractive?: boolean;
onClick?: () => void;
onDragStart?: (e: React.DragEvent) => void;
onDragEnd?: (e: React.DragEvent) => void;
onUpdate?: (updates: Partial<any>) => void;
className?: string;
style?: React.CSSProperties;
form_data?: Record<string, any>;
onFormDataChange?: (fieldName: string, value: any) => void;
// 새로운 기능들
auto_generation?: AutoGenerationConfig; // 자동생성 설정
hidden?: boolean; // 숨김 기능 (편집기에서는 연하게, 실제 화면에서는 숨김)
// 설정 변경 핸들러
onConfigChange?: (config: any) => void;
[key: string]: any;
}
/**
* 컴포넌트 렌더러 클래스 인터페이스
*/
export interface ComponentRendererClass {
new (props: ComponentRendererProps): {
render(): React.ReactElement;
};
componentDefinition: ComponentDefinition;
registerSelf(): void;
}
/**
* 컴포넌트 정의 인터페이스
* 각 컴포넌트의 메타데이터와 설정을 담는 핵심 인터페이스
*/
export interface ComponentDefinition {
// 기본 정보
id: string; // 고유 ID (kebab-case)
name: string; // 한글 표시명
name_eng?: string; // 영문명
description: string; // 설명
// 분류 및 타입
category: ComponentCategory; // 카테고리 (enum)
web_type: WebType; // 웹 타입 (기존 WebType 재사용)
// React 컴포넌트
component: React.ComponentType<any>; // 실제 React 컴포넌트
renderer?: ComponentRendererClass; // 렌더러 클래스 (선택사항)
// 설정
default_config: ComponentConfig; // 기본 설정값
default_size: ComponentSize; // 기본 크기
config_panel?: React.ComponentType<any>; // 설정 패널 컴포넌트
// UI 요소
icon?: React.ComponentType | string; // 아이콘 (컴포넌트 또는 문자열)
preview_image?: string; // 미리보기 이미지 URL
// 메타데이터
tags?: string[]; // 검색용 태그
version?: string; // 버전
author?: string; // 작성자
documentation?: string; // 문서 URL
// 검증 및 제약
validation?: ComponentValidation; // 유효성 검사 규칙
dependencies?: string[]; // 의존성 컴포넌트 ID
// 표시 설정
hidden?: boolean; // 팔레트에서 숨김 여부
// 생성 정보
created_at?: Date; // 생성일
updated_at?: Date; // 수정일
// ─── INVYONE 확장 (Phase 1+) ───
/** 컴포넌트 간 통신용 데이터 포트. 빌더에서 시각적 연결 시 사용. */
dataPorts?: {
inputs?: DataPort[];
outputs?: DataPort[];
};
}
/**
* 컴포넌트 유효성 검사 규칙
*/
export interface ComponentValidation {
required?: string[]; // 필수 속성들
min_size?: ComponentSize; // 최소 크기
max_size?: ComponentSize; // 최대 크기
allowed_parents?: string[]; // 허용되는 부모 컴포넌트 타입
allowed_children?: string[]; // 허용되는 자식 컴포넌트 타입
max_length?: { [field: string]: number }; // 필드별 최대 길이
pattern?: { [field: string]: RegExp }; // 필드별 정규식 패턴
custom?: (component: any) => string | null; // 커스텀 검증 함수
}
/**
* 컴포넌트 레지스트리 이벤트
*/
export interface ComponentRegistryEvent {
type: "component_registered" | "component_unregistered" | "component_updated";
data: ComponentDefinition;
timestamp: Date;
}
/**
* 컴포넌트 검색 옵션
*/
export interface ComponentSearchOptions {
query?: string; // 검색어
category?: ComponentCategory; // 카테고리 필터
web_type?: WebType; // 웹타입 필터
tags?: string[]; // 태그 필터
author?: string; // 작성자 필터
limit?: number; // 결과 제한
offset?: number; // 시작 위치
}
/**
* 컴포넌트 통계 정보
*/
export interface ComponentStats {
total: number; // 전체 컴포넌트 수
by_category: Array<{
// 카테고리별 통계
category: ComponentCategory;
count: number;
}>;
by_web_type: Array<{
// 웹타입별 통계
web_type: WebType;
count: number;
}>;
by_author: Array<{
// 작성자별 통계
author: string;
count: number;
}>;
recently_added: ComponentDefinition[]; // 최근 추가된 컴포넌트
}
/**
* 컴포넌트 자동 발견 옵션
*/
export interface ComponentAutoDiscoveryOptions {
pattern?: string; // 파일 패턴
base_dir?: string; // 기본 디렉토리
verbose?: boolean; // 상세 로그
continue_on_error?: boolean; // 오류 시 계속 진행
timeout?: number; // 타임아웃 (ms)
}
/**
* 컴포넌트 자동 발견 결과
*/
export interface ComponentDiscoveryResult {
success: boolean; // 성공 여부
components_found: number; // 발견된 컴포넌트 수
components_loaded: number; // 로드된 컴포넌트 수
errors: Error[]; // 발생한 오류들
duration: number; // 소요 시간 (ms)
details: ComponentModuleInfo[]; // 상세 정보
}
/**
* 컴포넌트 모듈 정보
*/
export interface ComponentModuleInfo {
path: string; // 파일 경로
id: string; // 컴포넌트 ID
name: string; // 컴포넌트 이름
loaded: boolean; // 로드 여부
error?: Error; // 오류 (있는 경우)
timestamp: number; // 타임스탬프
}
/**
* 컴포넌트 생성 옵션 (CLI용)
*/
export interface CreateComponentOptions {
id: string; // 컴포넌트 ID
name: string; // 컴포넌트 이름
name_eng?: string; // 영문명
description: string; // 설명
category: ComponentCategory; // 카테고리
web_type: WebType; // 웹타입
default_size: ComponentSize; // 기본 크기
author?: string; // 작성자
tags?: string[]; // 태그
icon?: string; // 아이콘
template?: string; // 템플릿 이름
}
/**
* 컴포넌트 마이그레이션 옵션
*/
export interface ComponentMigrationOptions {
component_code: string; // 기존 컴포넌트 코드
new_id: string; // 새 컴포넌트 ID
preserve_config?: boolean; // 설정 보존 여부
update_references?: boolean; // 참조 업데이트 여부
dry_run?: boolean; // 테스트 실행 여부
}
/**
* 컴포넌트 정의 생성 헬퍼 함수의 옵션
*/
export interface CreateComponentDefinitionOptions {
id: string;
name: string;
name_eng?: string;
description: string;
category: ComponentCategory;
web_type: WebType;
component: React.ComponentType<any>;
renderer?: ComponentRendererClass;
default_config?: ComponentConfig;
default_size: ComponentSize;
config_panel?: React.ComponentType<any>;
icon?: React.ComponentType | string;
preview_image?: string;
tags?: string[];
version?: string;
author?: string;
documentation?: string;
validation?: ComponentValidation;
dependencies?: string[];
hidden?: boolean; // 팔레트에서 숨김 여부
// ─── INVYONE 확장 ───
/** 컴포넌트 간 통신용 데이터 포트 선언 (Phase 1+) */
dataPorts?: {
inputs?: DataPort[];
outputs?: DataPort[];
};
}
/**
* 유틸리티 타입들
*/
export type ComponentId = string;
export type ComponentName = string;
export type ComponentCategoryKey = keyof typeof ComponentCategory;
/**
* 기본 상수들
*/
export const DEFAULT_COMPONENT_SIZE: ComponentSize = {
width: 200,
height: 36,
};
export const COMPONENT_CATEGORIES_INFO = {
[ComponentCategory.UI]: {
name: "UI",
description: "기본 UI 컴포넌트",
color: "#3b82f6",
},
[ComponentCategory.INPUT]: {
name: "입력",
description: "사용자 입력을 받는 컴포넌트",
color: "#10b981",
},
[ComponentCategory.DISPLAY]: {
name: "표시",
description: "정보를 표시하는 컴포넌트",
color: "#f59e0b",
},
[ComponentCategory.ACTION]: {
name: "액션",
description: "사용자 동작을 처리하는 컴포넌트",
color: "#ef4444",
},
[ComponentCategory.LAYOUT]: {
name: "레이아웃",
description: "화면 구조를 제공하는 컴포넌트",
color: "#8b5cf6",
},
[ComponentCategory.CHART]: {
name: "차트",
description: "데이터 시각화 컴포넌트",
color: "#212121",
},
[ComponentCategory.FORM]: {
name: "폼",
description: "폼 관련 컴포넌트",
color: "#84cc16",
},
[ComponentCategory.MEDIA]: {
name: "미디어",
description: "이미지, 비디오 등 미디어 컴포넌트",
color: "#f97316",
},
[ComponentCategory.NAVIGATION]: {
name: "네비게이션",
description: "화면 이동을 도와주는 컴포넌트",
color: "#6366f1",
},
[ComponentCategory.FEEDBACK]: {
name: "피드백",
description: "사용자 피드백을 제공하는 컴포넌트",
color: "#ec4899",
},
[ComponentCategory.UTILITY]: {
name: "유틸리티",
description: "보조 기능 컴포넌트",
color: "#6b7280",
},
[ComponentCategory.CONTAINER]: {
name: "컨테이너",
description: "다른 컴포넌트를 담는 컨테이너",
color: "#212121",
},
[ComponentCategory.SYSTEM]: {
name: "시스템",
description: "시스템 관련 컴포넌트",
color: "#1f2937",
},
[ComponentCategory.ADMIN]: {
name: "관리",
description: "관리자 전용 컴포넌트",
color: "#7c2d12",
},
[ComponentCategory.CUSTOM]: {
name: "커스텀",
description: "사용자 정의 컴포넌트",
color: "#059669",
},
[ComponentCategory.V2]: {
name: "통합",
description: "새로운 통합 컴포넌트 시스템",
color: "#2563eb",
},
} as const;