/** * V2 컴포넌트 타입 정의 * * 10개의 통합 컴포넌트 시스템을 위한 타입 정의 * - V2Input * - V2Select * - V2Date * - V2Text * - V2Media * - V2List * - V2Layout * - V2Group * - V2Biz * - V2Hierarchy */ import { Position, Size, CommonStyle, ValidationRule } from "./v2-core"; // ===== 공통 타입 ===== /** * V2 컴포넌트 타입 */ export type V2ComponentType = | "V2Input" | "V2Select" | "V2Date" | "V2Text" | "V2Media" | "V2List" | "V2Layout" | "V2Group" | "V2Biz" | "V2Hierarchy"; /** * 조건부 렌더링 설정 */ export interface ConditionalConfig { enabled: boolean; field: string; // 참조 필드 operator: "=" | "!=" | ">" | "<" | "in" | "notIn" | "isEmpty" | "isNotEmpty"; value: unknown; action: "show" | "hide" | "disable" | "enable"; } /** * 자동 입력 설정 */ export interface AutoFillConfig { enabled: boolean; sourceTable: string; filterColumn: string; userField: "companyCode" | "userId" | "deptCode"; displayColumn: string; } /** * 연쇄 관계 설정 */ export interface CascadingConfig { parentField: string; filterColumn: string; clearOnChange?: boolean; } /** * 상호 배제 설정 */ export interface MutualExclusionConfig { enabled: boolean; targetField: string; type: "exclusive" | "inclusive"; } /** * 공통 V2 컴포넌트 속성 */ export interface V2BaseProps { id: string; label?: string; required?: boolean; readonly?: boolean; disabled?: boolean; // 데이터 바인딩 tableName?: string; columnName?: string; // 위치 및 크기 position?: Position; size?: Size; // 스타일 style?: CommonStyle; // 조건부 및 자동화 conditional?: ConditionalConfig; autoFill?: AutoFillConfig; // 유효성 검사 validation?: ValidationRule[]; // 디자인 모드 (클릭 방지) isDesignMode?: boolean; } // ===== V2Input ===== export type V2InputType = "text" | "number" | "password" | "slider" | "color" | "button"; export type V2InputFormat = "none" | "email" | "tel" | "url" | "currency" | "biz_no"; export interface V2InputConfig { type: V2InputType; inputType?: V2InputType; // type 별칭 format?: V2InputFormat; mask?: string; placeholder?: string; // 숫자 전용 min?: number; max?: number; step?: number; // 버튼 전용 buttonText?: string; buttonVariant?: "default" | "destructive" | "outline" | "secondary" | "ghost"; onClick?: () => void; // 테이블명 (채번용) tableName?: string; } export interface V2InputProps extends V2BaseProps { v2Type: "V2Input"; config: V2InputConfig; value?: string | number; onChange?: (value: string | number) => void; } // ===== V2Select ===== export type V2SelectMode = "dropdown" | "combobox" | "radio" | "check" | "tag" | "tagbox" | "toggle" | "swap"; export type V2SelectSource = "static" | "code" | "db" | "api" | "entity" | "category"; export interface SelectOption { value: string; label: string; } export interface V2SelectConfig { mode: V2SelectMode; source: V2SelectSource | "distinct" | "select"; // distinct/select 추가 (테이블 컬럼에서 자동 로드) // 정적 옵션 (source: static) options?: SelectOption[]; // 코드 그룹 (source: code) codeGroup?: string; codeCategory?: string; // codeGroup 별칭 // DB 연결 (source: db) table?: string; valueColumn?: string; labelColumn?: string; filters?: Array<{ column: string; operator: string; value: unknown }>; // 엔티티 연결 (source: entity) entityTable?: string; entityValueField?: string; entityLabelField?: string; entityValueColumn?: string; // alias for entityValueField entityLabelColumn?: string; // alias for entityLabelField // API 연결 (source: api) apiEndpoint?: string; // 카테고리 연결 (source: category) - 레거시, code로 자동 변환됨 categoryTable?: string; categoryColumn?: string; // 공통 옵션 searchable?: boolean; multiple?: boolean; maxSelect?: number; allowClear?: boolean; // 연쇄 관계 cascading?: CascadingConfig; // 상호 배제 mutualExclusion?: MutualExclusionConfig; // 계층 코드 연쇄 선택 (source: code일 때 계층 구조 사용) hierarchical?: boolean; // 계층 구조 사용 여부 parentField?: string; // 부모 값을 참조할 필드 (다른 컴포넌트의 columnName) } export interface V2SelectProps extends V2BaseProps { v2Type: "V2Select"; config: V2SelectConfig; value?: string | string[]; onChange?: (value: string | string[]) => void; } // ===== V2Date ===== export type V2DateType = "date" | "time" | "datetime"; export interface V2DateConfig { type: V2DateType; format?: string; range?: boolean; minDate?: string; maxDate?: string; showToday?: boolean; } export interface V2DateProps extends V2BaseProps { v2Type: "V2Date"; config: V2DateConfig; value?: string | [string, string]; // 범위 선택 시 튜플 onChange?: (value: string | [string, string]) => void; } // ===== V2Text ===== export type V2TextMode = "simple" | "rich" | "code" | "markdown"; export interface V2TextConfig { mode: V2TextMode; rows?: number; maxLength?: number; placeholder?: string; resize?: "none" | "vertical" | "horizontal" | "both"; } export interface V2TextProps extends V2BaseProps { v2Type: "V2Text"; config: V2TextConfig; value?: string; onChange?: (value: string) => void; } // ===== V2Media ===== export type V2MediaType = "file" | "image" | "video" | "audio"; export interface V2MediaConfig { type: V2MediaType; multiple?: boolean; accept?: string; maxSize?: number; preview?: boolean; uploadEndpoint?: string; // 레거시 FileUpload 호환 설정 docType?: string; docTypeName?: string; showFileList?: boolean; dragDrop?: boolean; } export interface V2MediaProps extends V2BaseProps { v2Type?: "V2Media"; config?: V2MediaConfig; value?: string | string[]; // 파일 URL 또는 배열 onChange?: (value: string | string[]) => void; // 레거시 FileUpload 호환 props formData?: Record; columnName?: string; tableName?: string; // 부모 컴포넌트 시그니처: (fieldName, value) 형식 onFormDataChange?: (fieldName: string, value: any) => void; isDesignMode?: boolean; isInteractive?: boolean; onUpdate?: (updates: Partial) => void; } // ===== V2List ===== export type V2ListViewMode = "table" | "card" | "kanban" | "list"; export interface ListColumn { field: string; header: string; width?: number; sortable?: boolean; filterable?: boolean; editable?: boolean; format?: string; } export interface V2ListCardConfig { titleColumn?: string; subtitleColumn?: string; descriptionColumn?: string; imageColumn?: string; cardsPerRow?: number; cardSpacing?: number; showActions?: boolean; } export interface V2ListConfig { viewMode: V2ListViewMode; editable?: boolean; searchable?: boolean; pageable?: boolean; pageSize?: number; sortable?: boolean; pagination?: boolean; source?: "static" | "db" | "api"; // 데이터 소스 타입 columns?: ListColumn[]; modal?: boolean; cardConfig?: V2ListCardConfig; // 데이터 소스 dataSource?: { table?: string; api?: string; filters?: Array<{ column: string; operator: string; value: unknown }>; }; } export interface V2ListProps extends V2BaseProps { v2Type: "V2List"; config: V2ListConfig; data?: Record[]; selectedRows?: Record[]; onRowSelect?: (rows: Record[]) => void; onRowClick?: (row: Record) => void; } // ===== V2Layout ===== export type V2LayoutType = "grid" | "split" | "flex" | "divider" | "screen-embed"; export interface V2LayoutConfig { type: V2LayoutType; columns?: number; // 12컬럼 시스템에서 실제 표시할 컬럼 수 (1-12) gap?: string; splitRatio?: number[]; direction?: "horizontal" | "vertical"; use12Column?: boolean; // 12컬럼 시스템 사용 여부 (기본 true) // screen-embed 전용 screenId?: number; } export interface V2LayoutProps extends V2BaseProps { v2Type: "V2Layout"; config: V2LayoutConfig; children?: React.ReactNode; } // ===== V2Group ===== export type V2GroupType = "tabs" | "accordion" | "section" | "card-section" | "modal" | "form-modal"; export interface TabItem { id: string; title: string; content?: React.ReactNode; } export interface V2GroupConfig { type: V2GroupType; title?: string; collapsible?: boolean; defaultExpanded?: boolean; defaultOpen?: boolean; // defaultExpanded 별칭 // 탭 전용 tabs?: TabItem[]; activeTab?: string; // 모달 전용 modalSize?: "sm" | "md" | "lg" | "xl"; } export interface V2GroupProps extends V2BaseProps { v2Type: "V2Group"; config: V2GroupConfig; children?: React.ReactNode; open?: boolean; onOpenChange?: (open: boolean) => void; } // ===== V2Biz ===== export type V2BizType = "flow" | "rack" | "map" | "numbering" | "category" | "mapping" | "related-buttons"; export interface V2BizConfig { type: V2BizType; // 각 타입별 설정은 제네릭하게 처리 config?: Record; // 플로우 전용 flowConfig?: { flowId?: number; showProgress?: boolean; }; } export interface V2BizProps extends V2BaseProps { v2Type: "V2Biz"; config: V2BizConfig; } // ===== V2Hierarchy ===== export type V2HierarchyType = "tree" | "org" | "bom" | "cascading"; export type V2HierarchyViewMode = "tree" | "table" | "indent" | "dropdown"; export interface HierarchyNode { id: string; parentId?: string; label: string; children?: HierarchyNode[]; data?: Record; } export interface V2HierarchyConfig { type: V2HierarchyType; viewMode: V2HierarchyViewMode; source?: string; // 계층 그룹 코드 editable?: boolean; draggable?: boolean; showQty?: boolean; // BOM 전용 maxLevel?: number; // 데이터 소스 dataSource?: { table?: string; idColumn?: string; parentColumn?: string; labelColumn?: string; }; } export interface V2HierarchyProps extends V2BaseProps { v2Type: "V2Hierarchy"; config: V2HierarchyConfig; data?: HierarchyNode[]; selectedNode?: HierarchyNode; onNodeSelect?: (node: HierarchyNode) => void; onNodeMove?: (nodeId: string, newParentId: string) => void; } // ===== 통합 Props 유니온 타입 ===== export type V2ComponentProps = | V2InputProps | V2SelectProps | V2DateProps | V2TextProps | V2MediaProps | V2ListProps | V2LayoutProps | V2GroupProps | V2BizProps | V2HierarchyProps; // ===== 타입 가드 ===== export function isV2Input(props: V2ComponentProps): props is V2InputProps { return props.v2Type === "V2Input"; } export function isV2Select(props: V2ComponentProps): props is V2SelectProps { return props.v2Type === "V2Select"; } export function isV2Date(props: V2ComponentProps): props is V2DateProps { return props.v2Type === "V2Date"; } export function isV2Text(props: V2ComponentProps): props is V2TextProps { return props.v2Type === "V2Text"; } export function isV2Media(props: V2ComponentProps): props is V2MediaProps { return props.v2Type === "V2Media"; } export function isV2List(props: V2ComponentProps): props is V2ListProps { return props.v2Type === "V2List"; } export function isV2Layout(props: V2ComponentProps): props is V2LayoutProps { return props.v2Type === "V2Layout"; } export function isV2Group(props: V2ComponentProps): props is V2GroupProps { return props.v2Type === "V2Group"; } export function isV2Biz(props: V2ComponentProps): props is V2BizProps { return props.v2Type === "V2Biz"; } export function isV2Hierarchy(props: V2ComponentProps): props is V2HierarchyProps { return props.v2Type === "V2Hierarchy"; } // ===== JSON Schema 타입 ===== export interface JSONSchemaProperty { type: "string" | "number" | "boolean" | "array" | "object"; title?: string; description?: string; enum?: string[]; default?: unknown; items?: JSONSchemaProperty; properties?: Record; required?: string[]; } export interface V2ConfigSchema { type: "object"; properties: Record; required?: string[]; } // ===== 레거시 컴포넌트 → V2 컴포넌트 매핑 ===== export const LEGACY_TO_V2_MAP: Record = { // Input 계열 "text-input": "V2Input", "number-input": "V2Input", "password-input": "V2Input", // Select 계열 "select-basic": "V2Select", "radio-basic": "V2Select", "checkbox-basic": "V2Select", "entity-search-input": "V2Select", "autocomplete-search-input": "V2Select", // Date 계열 "date-input": "V2Date", // Text 계열 "textarea-basic": "V2Text", // Media 계열 "file-upload": "V2Media", "image-widget": "V2Media", // List 계열 "table-list": "V2List", "table-search-widget": "V2List", "modal-repeater-table": "V2List", "repeater-field-group": "V2List", "card-display": "V2List", // Layout 계열 "split-panel-layout": "V2Layout", "screen-split-panel": "V2Layout", // Group 계열 "tabs-widget": "V2Group", "section-paper": "V2Group", "section-card": "V2Group", "universal-form-modal": "V2Group", // Biz 계열 "category-manager": "V2Biz", "numbering-rule": "V2Biz", "flow-widget": "V2Biz", // Button (Input의 버튼 모드) "button-primary": "V2Input", }; // ===== 조건부 레이어 시스템 ===== /** * 레이어 조건 설정 * 특정 필드값에 따라 레이어 활성화 여부를 결정 */ export interface LayerCondition { field: string; // 트리거 필드 (columnName 또는 탭ID) operator: "=" | "!=" | "in" | "notIn" | "isEmpty" | "isNotEmpty"; value: string | string[]; // 비교값 } /** * 레이어 설정 * 특정 조건이 충족될 때 표시되는 컴포넌트들의 그룹 */ export interface LayerConfig { layerId: string; // 고유 ID layerName: string; // 표시명 (설정용) conditions: LayerCondition[]; // 조건 목록 conditionLogic?: "AND" | "OR"; // 조건 조합 방식 (기본: AND) targetComponents: string[]; // 표시할 컴포넌트 ID 목록 alwaysVisible?: boolean; // 항상 표시 (조건 무시) }