모달 타입 통합 (modal-table/card/icon-grid -> modal 1종): - normalizeInputType()으로 레거시 저장값 호환 - 캔버스 모달 모드 완전 제거 (ModalMode, modalCanvasId, returnEvent) - SearchInputType 9종으로 정리 모달 뷰 실제 구현: - TableView / IconView 분리 렌더링 (displayStyle 반영) - 아이콘 뷰: 이름 첫 글자 컬러 카드 + 초성 그룹 헤더 - getIconColor() 결정적 해시 색상 (16색 팔레트) 가나다/ABC 필터 탭: - ModalFilterTab 타입 + getGroupKey() 한글 초성 추출 - 쌍자음 합침 (ㄲ->ㄱ, ㄸ->ㄷ 등) - 모달 상단 토글 버튼으로 초성/알파벳 섹션 그룹화 디자이너 설정 개선: - 컬럼 헤더 라벨 커스터마이징 (columnLabels) - 필터 탭 활성화 체크박스 (가나다/ABC) - card 스타일 제거, 정렬 옵션 제거 - 검색 방식 (포함/시작/같음) 유지 시나리오 A 모달 선택 필터링: - ConnectionEditor 필터 컬럼에 DB 전체 컬럼 표시 - pop-string-list 복수 필터 AND 지원 - useConnectionResolver 페이로드 구조 정규화 Co-authored-by: Cursor <cursoragent@cursor.com>
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
/**
|
|
* useConnectionResolver - 런타임 컴포넌트 연결 해석기
|
|
*
|
|
* PopViewerWithModals에서 사용.
|
|
* layout.dataFlow.connections를 읽고, 소스 컴포넌트의 __comp_output__ 이벤트를
|
|
* 타겟 컴포넌트의 __comp_input__ 이벤트로 자동 변환/중계한다.
|
|
*
|
|
* 이벤트 규칙:
|
|
* 소스: __comp_output__${sourceComponentId}__${outputKey}
|
|
* 타겟: __comp_input__${targetComponentId}__${inputKey}
|
|
*/
|
|
|
|
import { useEffect, useRef } from "react";
|
|
import { usePopEvent } from "./usePopEvent";
|
|
import type { PopDataConnection } from "@/components/pop/designer/types/pop-layout";
|
|
|
|
interface UseConnectionResolverOptions {
|
|
screenId: string;
|
|
connections: PopDataConnection[];
|
|
}
|
|
|
|
export function useConnectionResolver({
|
|
screenId,
|
|
connections,
|
|
}: UseConnectionResolverOptions): void {
|
|
const { publish, subscribe } = usePopEvent(screenId);
|
|
|
|
// 연결 목록을 ref로 저장하여 콜백 안정성 확보
|
|
const connectionsRef = useRef(connections);
|
|
connectionsRef.current = connections;
|
|
|
|
useEffect(() => {
|
|
if (!connections || connections.length === 0) return;
|
|
|
|
const unsubscribers: (() => void)[] = [];
|
|
|
|
// 소스별로 그룹핑하여 구독 생성
|
|
const sourceGroups = new Map<string, PopDataConnection[]>();
|
|
for (const conn of connections) {
|
|
const sourceEvent = `__comp_output__${conn.sourceComponent}__${conn.sourceOutput || conn.sourceField}`;
|
|
const existing = sourceGroups.get(sourceEvent) || [];
|
|
existing.push(conn);
|
|
sourceGroups.set(sourceEvent, existing);
|
|
}
|
|
|
|
for (const [sourceEvent, conns] of sourceGroups) {
|
|
const unsub = subscribe(sourceEvent, (payload: unknown) => {
|
|
for (const conn of conns) {
|
|
const targetEvent = `__comp_input__${conn.targetComponent}__${conn.targetInput || conn.targetField}`;
|
|
|
|
// 항상 통일된 구조로 감싸서 전달: { value, filterConfig?, _connectionId }
|
|
const enrichedPayload = {
|
|
value: payload,
|
|
filterConfig: conn.filterConfig,
|
|
_connectionId: conn.id,
|
|
};
|
|
|
|
publish(targetEvent, enrichedPayload);
|
|
}
|
|
});
|
|
unsubscribers.push(unsub);
|
|
}
|
|
|
|
return () => {
|
|
for (const unsub of unsubscribers) {
|
|
unsub();
|
|
}
|
|
};
|
|
}, [screenId, connections, subscribe, publish]);
|
|
}
|