Files
invyone/frontend/types/v2-components.ts
T

405 lines
10 KiB
TypeScript

/**
* V2 컴포넌트 타입 정의 (잔존 7종)
*
* - V2Text
* - V2List
* - V2Layout
* - V2Group
* - V2Biz
* - V2Hierarchy
*
* 옛 입력/선택 두 종은 Phase D.3 (2026-05-12) 에서 폐기 — canonical `input`
* (InputComponent + InvFieldConfigPanel) 로 흡수됨.
* V2Date 도 폐기됨 — InvField triple type=date 의 4 format(date/datetime/time/range) 으로 통일.
* 런타임은 InputComponent + lib/registry/components/input/pickers.tsx.
*/
import { Position, Size, CommonStyle, ValidationRule } from "./v2-core";
// ===== 공통 타입 =====
/**
* V2 컴포넌트 타입
*/
export type V2ComponentType =
| "V2Text"
| "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;
source_table: string;
filter_column: string;
user_field: "companyCode" | "userId" | "deptCode";
display_column: string;
}
/**
* 연쇄 관계 설정
*/
export interface CascadingConfig {
parent_field: string;
filter_column: string;
clear_on_change?: boolean;
}
/**
* 상호 배제 설정
*/
export interface MutualExclusionConfig {
enabled: boolean;
target_field: string;
type: "exclusive" | "inclusive";
}
/**
* 공통 V2 컴포넌트 속성
*/
export interface V2BaseProps {
id: string;
label?: string;
required?: boolean;
readonly?: boolean;
disabled?: boolean;
// 데이터 바인딩
table_name?: string;
column_name?: string;
// 위치 및 크기
position?: Position;
size?: Size;
// 스타일
style?: CommonStyle;
// 조건부 및 자동화
conditional?: ConditionalConfig;
auto_fill?: AutoFillConfig;
// 유효성 검사
validation?: ValidationRule[];
// 디자인 모드 (클릭 방지)
isDesignMode?: boolean;
}
// 옛 입력/선택 타입 정의는 Phase D.3 (2026-05-12) 에서 제거됨.
// 입력 옵션은 canonical InputConfig (lib/registry/components/input/types.ts) 와
// FieldConfig (types/invyone-component.ts) 로 일원화. 옵션 필터는
// OptionFilter (lib/registry/components/input/use-option-loader.ts) 로 이전됨.
// ===== V2Text =====
export type V2TextMode = "simple" | "rich" | "code" | "markdown";
export interface V2TextConfig {
mode: V2TextMode;
rows?: number;
max_length?: number;
placeholder?: string;
resize?: "none" | "vertical" | "horizontal" | "both";
}
export interface V2TextProps extends V2BaseProps {
v2Type: "V2Text";
config: V2TextConfig;
value?: string;
onChange?: (value: string) => void;
}
// V2Media 타입 정의는 Phase D.5 (2026-05-12) 에서 제거됨 — canonical input 의 file 분기로 흡수.
// ===== 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 {
title_column?: string;
subtitle_column?: string;
description_column?: string;
image_column?: string;
cards_per_row?: number;
card_spacing?: number;
show_actions?: boolean;
}
export interface V2ListConfig {
view_mode: V2ListViewMode;
editable?: boolean;
searchable?: boolean;
pageable?: boolean;
page_size?: number;
sortable?: boolean;
pagination?: boolean;
source?: "static" | "db" | "api"; // 데이터 소스 타입
columns?: ListColumn[];
modal?: boolean;
card_config?: V2ListCardConfig;
// 데이터 소스
data_source?: {
table?: string;
api?: string;
filters?: Array<{ column: string; operator: string; value: unknown }>;
};
}
export interface V2ListProps extends V2BaseProps {
v2Type: "V2List";
config: V2ListConfig;
data?: Record<string, unknown>[];
selected_rows?: Record<string, unknown>[];
onRowSelect?: (rows: Record<string, unknown>[]) => void;
onRowClick?: (row: Record<string, unknown>) => void;
}
// ===== V2Layout =====
export type V2LayoutType = "grid" | "split" | "flex" | "divider" | "screen-embed";
export interface V2LayoutConfig {
type: V2LayoutType;
columns?: number; // 12컬럼 시스템에서 실제 표시할 컬럼 수 (1-12)
gap?: string;
split_ratio?: number[];
direction?: "horizontal" | "vertical";
use_12_column?: boolean; // 12컬럼 시스템 사용 여부 (기본 true)
// screen-embed 전용
screen_id?: 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;
default_expanded?: boolean;
default_open?: boolean; // default_expanded 별칭
// 탭 전용
tabs?: TabItem[];
active_tab?: string;
// 모달 전용
modal_size?: "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<string, unknown>;
// 플로우 전용
flow_config?: {
flow_id?: number;
show_progress?: 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;
parent_id?: string;
label: string;
children?: HierarchyNode[];
data?: Record<string, unknown>;
}
export interface V2HierarchyConfig {
type: V2HierarchyType;
view_mode: V2HierarchyViewMode;
source?: string; // 계층 그룹 코드
editable?: boolean;
draggable?: boolean;
show_qty?: boolean; // BOM 전용
max_level?: number;
// 데이터 소스
data_source?: {
table?: string;
id_column?: string;
parent_column?: string;
label_column?: string;
};
}
export interface V2HierarchyProps extends V2BaseProps {
v2Type: "V2Hierarchy";
config: V2HierarchyConfig;
data?: HierarchyNode[];
selected_node?: HierarchyNode;
onNodeSelect?: (node: HierarchyNode) => void;
onNodeMove?: (nodeId: string, newParentId: string) => void;
}
// ===== 통합 Props 유니온 타입 =====
export type V2ComponentProps =
| V2TextProps
| V2ListProps
| V2LayoutProps
| V2GroupProps
| V2BizProps
| V2HierarchyProps;
// ===== 타입 가드 =====
export function isV2Text(props: V2ComponentProps): props is V2TextProps {
return props.v2Type === "V2Text";
}
// isV2Media 는 Phase D.5 에서 제거됨 (canonical input 의 file 분기로 흡수)
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<string, JSONSchemaProperty>;
required?: string[];
}
export interface V2ConfigSchema {
type: "object";
properties: Record<string, JSONSchemaProperty>;
required?: string[];
}
// ===== 레거시 컴포넌트 → V2 컴포넌트 매핑 =====
export const LEGACY_TO_V2_MAP: Record<string, V2ComponentType> = {
// Input / Select 계열은 canonical `input` 으로 흡수됨 (Phase D.3, Phase E) — 이 매핑에서 제거.
// Text 계열 — textarea-basic 은 Phase E 에서 canonical input 으로 흡수, 매핑 제거.
// Media 계열 — Phase D.5 에서 canonical input 으로 흡수, 매핑 제거.
// 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 계열
// numbering-rule canvas 컴포넌트는 2026-05-11 폐기 — 매핑 제거.
"category-manager": "V2Biz",
"flow-widget": "V2Biz",
// Button (Input 모드) — canonical `input` 으로 흡수됨 (Phase D.3), 매핑 제거
};
// ===== 조건부 레이어 시스템 =====
/**
* 레이어 조건 설정
* 특정 필드값에 따라 레이어 활성화 여부를 결정
*/
export interface LayerCondition {
field: string; // 트리거 필드 (column_name 또는 탭ID)
operator: "=" | "!=" | "in" | "notIn" | "isEmpty" | "isNotEmpty";
value: string | string[]; // 비교값
}
/**
* 레이어 설정
* 특정 조건이 충족될 때 표시되는 컴포넌트들의 그룹
*/
export interface LayerConfig {
layer_id: string; // 고유 ID
layer_name: string; // 표시명 (설정용)
conditions: LayerCondition[]; // 조건 목록
condition_logic?: "AND" | "OR"; // 조건 조합 방식 (기본: AND)
target_components: string[]; // 표시할 컴포넌트 ID 목록
always_visible?: boolean; // 항상 표시 (조건 무시)
}