Files
invyone/frontend/types/component-events.ts
T
DDD1542 e44ba2953a refactor: v2-table-grouped/v2-pivot-grid/v2-card-display 일괄 폐기 (T5)
T3 (본체) + T4 (ConfigPanel) 흡수 완료에 따라 옛 별도 컴포넌트 폐기.
v2-split-panel-layout 은 별도 컨테이너로 유지 (Codex 권고).

폴더 5개 삭제
- v2-table-grouped/ (537 + ConfigPanel)
- v2-pivot-grid/ (1963 + utils + components + hooks + ConfigPanel)
- v2-card-display/ (1314 + ConfigPanel)
- pivot-grid/ (legacy)
- card-display/ (legacy)

ConfigPanel 3개 삭제
- V2TableGroupedConfigPanel.tsx
- V2PivotGridConfigPanel.tsx
- V2CardDisplayConfigPanel.tsx

레지스트리 / alias / hidden 정리
- lib/registry/components/index.ts: 5개 import 라인 제거 (renderer 자동 등록 폐기)
- ComponentsPanel.tsx: hidden 목록의 v2-card-display ("→ stats" 잘못된 매핑) /
  v2-table-grouped / v2-pivot-grid / pivot-grid / card-display 모두 제거
- DynamicComponentRenderer.tsx LEGACY_TO_UNIFIED: v2-card-display "→stats"
  (잘못) / v2-table-grouped / v2-pivot-grid / card-display alias 제거
- getComponentConfigPanel.tsx: 4개 dynamic import + alias 제거
- templateMigrate.ts: 3 매핑 제거
- componentConfig.ts: v2PivotGridOverridesSchema, v2CardDisplayOverridesSchema
  + 등록 + defaults 제거

이벤트 dead code
- types/component-events.ts: RefreshCardDisplayDetail / REFRESH_CARD_DISPLAY
  이벤트 (사용처 0건 — v2-card-display 전용) 제거

ScreenDesigner
- 4058 의 isCardDisplay 분기 (66.67% 그리드 특수 처리) 제거
- 4098 의 gridColumnsRatioMap 의 "card-display" 항목 제거

검증
- npx tsc --noEmit 우리 작업 파일 새 에러 0
- v2-table-grouped/v2-pivot-grid/v2-card-display/RefreshCardDisplay 잔존
  grep — 의도적 주석 5곳만 (폐기 흔적 설명)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:38:05 +09:00

231 lines
6.2 KiB
TypeScript

/**
* V2 컴포넌트 간 통신 이벤트 타입 정의
*
* 모든 V2 컴포넌트는 이 파일에 정의된 이벤트 타입을 사용해야 합니다.
* 이벤트 발행/구독 시 타입 안전성을 보장합니다.
*/
// ============================================================
// 이벤트 상세 데이터 타입 (event.detail)
// ============================================================
/**
* 테이블 리스트 데이터 변경 이벤트
* 발행: v2-table-list
* 구독: v2-aggregation-widget, v2-repeat-container
*/
export interface TableListDataChangeDetail {
component_id: string;
table_name: string;
data: any[];
selected_rows: string[] | number[];
}
/**
* 리피터 데이터 변경 이벤트
* 발행: v2-unified-repeater
* 구독: v2-aggregation-widget, v2-repeat-container
*/
export interface RepeaterDataChangeDetail {
component_id: string;
table_name: string;
data: any[];
selected_data?: any[];
}
/**
* 폼 저장 전 이벤트
* 발행: buttonActions, UnifiedFormContext
* 구독: v2-unified-repeater, simple-repeater-table, modal-repeater-table 등
*/
export interface BeforeFormSaveDetail {
form_data: Record<string, any>;
skip_default_save?: boolean;
}
/**
* 폼 저장 후 이벤트
* 발행: UnifiedFormContext
* 구독: 저장 결과 처리 컴포넌트들
*/
export interface AfterFormSaveDetail {
success: boolean;
data?: any;
error?: string;
}
/**
* 리피터 저장 이벤트 (마스터-디테일 FK 연결용)
* 발행: InteractiveScreenViewerDynamic
* 구독: v2-unified-repeater
*/
export interface RepeaterSaveDetail {
parent_id?: string | number;
master_record_id: string | number;
main_form_data: Record<string, any>;
table_name: string;
}
/**
* 테이블 새로고침 이벤트
* 발행: v2-button-primary, buttonActions
* 구독: v2-table-list, v2-split-panel-layout
*/
export interface RefreshTableDetail {
table_name?: string;
component_id?: string;
}
/**
* 컴포넌트 간 데이터 전달 이벤트
* 발행: buttonActions
* 구독: v2-unified-repeater
*/
export interface ComponentDataTransferDetail {
source_component_id: string;
target_component_id: string;
data: any[];
mode: "append" | "replace" | "merge";
mapping_rules?: Array<{
source_field: string;
target_field: string;
default_value?: any;
}>;
}
/**
* 분할 패널 간 데이터 전달 이벤트
* 발행: buttonActions
* 구독: v2-unified-repeater, repeater-field-group
*/
export interface SplitPanelDataTransferDetail {
source_position: "left" | "right";
target_position: "left" | "right";
data: any[];
mode: "append" | "replace" | "merge";
mapping_rules?: Array<{
source_field: string;
target_field: string;
default_value?: any;
}>;
}
/**
* 연관 데이터 버튼 선택 이벤트
* 발행: related-data-buttons
* 구독: v2-table-list
*/
export interface RelatedButtonSelectDetail {
target_table: string;
filter_column: string;
filter_value: any;
selected_data?: any;
}
/**
* 모달 제어 이벤트
*/
export interface EditModalDetail {
screen_id?: number;
record_id?: string | number;
data?: any;
}
// ============================================================
// 이벤트 이름 상수
// ============================================================
export const V2_EVENTS = {
// 데이터 변경 이벤트
TABLE_LIST_DATA_CHANGE: "tableListDataChange",
REPEATER_DATA_CHANGE: "repeaterDataChange",
// 폼 저장 이벤트
BEFORE_FORM_SAVE: "beforeFormSave",
AFTER_FORM_SAVE: "afterFormSave",
REPEATER_SAVE: "repeaterSave",
// UI 갱신 이벤트
REFRESH_TABLE: "refreshTable",
// 데이터 전달 이벤트
COMPONENT_DATA_TRANSFER: "componentDataTransfer",
SPLIT_PANEL_DATA_TRANSFER: "splitPanelDataTransfer",
// 모달 제어 이벤트
OPEN_EDIT_MODAL: "openEditModal",
CLOSE_EDIT_MODAL: "closeEditModal",
SAVE_SUCCESS_IN_MODAL: "saveSuccessInModal",
// 연관 데이터 버튼 이벤트
RELATED_BUTTON_SELECT: "related-button-select",
RELATED_BUTTON_REGISTER: "related-button-register",
RELATED_BUTTON_UNREGISTER: "related-button-unregister",
} as const;
// ============================================================
// Window EventMap 확장 (타입 안전한 이벤트 리스너)
// ============================================================
declare global {
interface WindowEventMap {
// 데이터 변경 이벤트
[V2_EVENTS.TABLE_LIST_DATA_CHANGE]: CustomEvent<TableListDataChangeDetail>;
[V2_EVENTS.REPEATER_DATA_CHANGE]: CustomEvent<RepeaterDataChangeDetail>;
// 폼 저장 이벤트
[V2_EVENTS.BEFORE_FORM_SAVE]: CustomEvent<BeforeFormSaveDetail>;
[V2_EVENTS.AFTER_FORM_SAVE]: CustomEvent<AfterFormSaveDetail>;
[V2_EVENTS.REPEATER_SAVE]: CustomEvent<RepeaterSaveDetail>;
// UI 갱신 이벤트
[V2_EVENTS.REFRESH_TABLE]: CustomEvent<RefreshTableDetail>;
// 데이터 전달 이벤트
[V2_EVENTS.COMPONENT_DATA_TRANSFER]: CustomEvent<ComponentDataTransferDetail>;
[V2_EVENTS.SPLIT_PANEL_DATA_TRANSFER]: CustomEvent<SplitPanelDataTransferDetail>;
// 연관 데이터 버튼 이벤트
[V2_EVENTS.RELATED_BUTTON_SELECT]: CustomEvent<RelatedButtonSelectDetail>;
}
}
// ============================================================
// 유틸리티 함수
// ============================================================
/**
* 타입 안전한 이벤트 발행 함수
*/
export function dispatchV2Event<K extends keyof WindowEventMap>(
eventName: K,
detail: WindowEventMap[K] extends CustomEvent<infer D> ? D : never
): void {
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent(eventName, { detail }));
}
}
/**
* 타입 안전한 이벤트 구독 함수
*/
export function subscribeV2Event<K extends keyof WindowEventMap>(
eventName: K,
handler: (event: WindowEventMap[K]) => void
): () => void {
if (typeof window === "undefined") {
return () => {};
}
window.addEventListener(eventName, handler as EventListener);
return () => {
window.removeEventListener(eventName, handler as EventListener);
};
}
// ============================================================
// 내보내기
// ============================================================
export type V2EventName = typeof V2_EVENTS[keyof typeof V2_EVENTS];