feat(pop-search): 모달 뷰 전면 개선 - 아이콘 뷰, 가나다/ABC 필터 탭, 컬럼 라벨
모달 타입 통합 (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>
This commit is contained in:
@@ -46,6 +46,7 @@ interface PopViewerWithModalsProps {
|
||||
/** 열린 모달 상태 */
|
||||
interface OpenModal {
|
||||
definition: PopModalDefinition;
|
||||
returnTo?: string;
|
||||
}
|
||||
|
||||
// ========================================
|
||||
@@ -61,7 +62,7 @@ export default function PopViewerWithModals({
|
||||
overridePadding,
|
||||
}: PopViewerWithModalsProps) {
|
||||
const [modalStack, setModalStack] = useState<OpenModal[]>([]);
|
||||
const { subscribe } = usePopEvent(screenId);
|
||||
const { subscribe, publish } = usePopEvent(screenId);
|
||||
|
||||
// 연결 해석기: layout에 정의된 connections를 이벤트 라우팅으로 변환
|
||||
useConnectionResolver({
|
||||
@@ -69,34 +70,51 @@ export default function PopViewerWithModals({
|
||||
connections: layout.dataFlow?.connections || [],
|
||||
});
|
||||
|
||||
// 모달 열기 이벤트 구독
|
||||
// 모달 열기/닫기 이벤트 구독
|
||||
useEffect(() => {
|
||||
const unsubOpen = subscribe("__pop_modal_open__", (payload: unknown) => {
|
||||
const data = payload as {
|
||||
modalId?: string;
|
||||
title?: string;
|
||||
mode?: string;
|
||||
returnTo?: string;
|
||||
};
|
||||
|
||||
// fullscreen 모달: layout.modals에서 정의 찾기
|
||||
if (data?.modalId) {
|
||||
const modalDef = layout.modals?.find(m => m.id === data.modalId);
|
||||
if (modalDef) {
|
||||
setModalStack(prev => [...prev, { definition: modalDef }]);
|
||||
setModalStack(prev => [...prev, {
|
||||
definition: modalDef,
|
||||
returnTo: data.returnTo,
|
||||
}]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const unsubClose = subscribe("__pop_modal_close__", () => {
|
||||
// 가장 최근 모달 닫기
|
||||
setModalStack(prev => prev.slice(0, -1));
|
||||
const unsubClose = subscribe("__pop_modal_close__", (payload: unknown) => {
|
||||
const data = payload as { selectedRow?: Record<string, unknown> } | undefined;
|
||||
|
||||
setModalStack(prev => {
|
||||
if (prev.length === 0) return prev;
|
||||
const topModal = prev[prev.length - 1];
|
||||
|
||||
// 결과 데이터가 있고, 반환 대상이 지정된 경우 결과 이벤트 발행
|
||||
if (data?.selectedRow && topModal.returnTo) {
|
||||
publish("__pop_modal_result__", {
|
||||
selectedRow: data.selectedRow,
|
||||
returnTo: topModal.returnTo,
|
||||
});
|
||||
}
|
||||
|
||||
return prev.slice(0, -1);
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubOpen();
|
||||
unsubClose();
|
||||
};
|
||||
}, [subscribe, layout.modals]);
|
||||
}, [subscribe, publish, layout.modals]);
|
||||
|
||||
// 최상위 모달만 닫기 (X 버튼, overlay 클릭, ESC)
|
||||
const handleCloseTopModal = useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user