feat: Implement layer activation and data transfer enhancements
- Added support for force-activated layer IDs in ScreenViewPage, allowing layers to be activated based on data events. - Introduced ScreenContextProvider in ScreenModal and EditModal to manage screen-specific data and context. - Enhanced V2Repeater to register as a DataReceiver, enabling automatic data handling and integration with ScreenContext. - Improved ButtonPrimaryComponent to support automatic target component discovery and layer activation for data transfers. - Updated various components to streamline data handling and improve user experience during data transfers and layer management.
This commit is contained in:
@@ -6,17 +6,28 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useCallback, useRef, useState } from "react";
|
||||
import type { DataProvidable, DataReceivable } from "@/types/data-transfer";
|
||||
import type { DataProvidable, DataReceivable, DataReceiverConfig } from "@/types/data-transfer";
|
||||
import { logger } from "@/lib/utils/logger";
|
||||
import type { SplitPanelPosition } from "@/contexts/SplitPanelContext";
|
||||
|
||||
/**
|
||||
* 대기 중인 데이터 전달 항목
|
||||
* 타겟 컴포넌트가 아직 마운트되지 않은 경우 (조건부 레이어 등) 버퍼에 저장
|
||||
*/
|
||||
export interface PendingTransfer {
|
||||
targetComponentId: string;
|
||||
data: any[];
|
||||
config: DataReceiverConfig;
|
||||
timestamp: number;
|
||||
targetLayerId?: string;
|
||||
}
|
||||
|
||||
interface ScreenContextValue {
|
||||
screenId?: number;
|
||||
tableName?: string;
|
||||
menuObjid?: number; // 메뉴 OBJID (카테고리 값 조회 시 필요)
|
||||
splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치 (left/right)
|
||||
menuObjid?: number;
|
||||
splitPanelPosition?: SplitPanelPosition;
|
||||
|
||||
// 🆕 폼 데이터 (RepeaterFieldGroup 등 컴포넌트 데이터 저장)
|
||||
formData: Record<string, any>;
|
||||
updateFormData: (fieldName: string, value: any) => void;
|
||||
|
||||
@@ -33,6 +44,11 @@ interface ScreenContextValue {
|
||||
// 모든 컴포넌트 조회
|
||||
getAllDataProviders: () => Map<string, DataProvidable>;
|
||||
getAllDataReceivers: () => Map<string, DataReceivable>;
|
||||
|
||||
// 대기 중인 데이터 전달 (레이어 내부 컴포넌트 미마운트 대응)
|
||||
addPendingTransfer: (transfer: PendingTransfer) => void;
|
||||
getPendingTransfer: (componentId: string) => PendingTransfer | undefined;
|
||||
clearPendingTransfer: (componentId: string) => void;
|
||||
}
|
||||
|
||||
const ScreenContext = createContext<ScreenContextValue | null>(null);
|
||||
@@ -57,11 +73,10 @@ export function ScreenContextProvider({
|
||||
}: ScreenContextProviderProps) {
|
||||
const dataProvidersRef = useRef<Map<string, DataProvidable>>(new Map());
|
||||
const dataReceiversRef = useRef<Map<string, DataReceivable>>(new Map());
|
||||
const pendingTransfersRef = useRef<Map<string, PendingTransfer>>(new Map());
|
||||
|
||||
// 🆕 폼 데이터 상태 (RepeaterFieldGroup 등 컴포넌트 데이터 저장)
|
||||
const [formData, setFormData] = useState<Record<string, any>>({});
|
||||
|
||||
// 🆕 폼 데이터 업데이트 함수
|
||||
const updateFormData = useCallback((fieldName: string, value: any) => {
|
||||
setFormData((prev) => {
|
||||
const updated = { ...prev, [fieldName]: value };
|
||||
@@ -87,6 +102,25 @@ export function ScreenContextProvider({
|
||||
const registerDataReceiver = useCallback((componentId: string, receiver: DataReceivable) => {
|
||||
dataReceiversRef.current.set(componentId, receiver);
|
||||
logger.debug("데이터 수신자 등록", { componentId, componentType: receiver.componentType });
|
||||
|
||||
// 대기 중인 데이터 전달이 있으면 즉시 수신 처리
|
||||
const pending = pendingTransfersRef.current.get(componentId);
|
||||
if (pending) {
|
||||
logger.info("대기 중인 데이터 전달 자동 수신", {
|
||||
componentId,
|
||||
dataCount: pending.data.length,
|
||||
waitedMs: Date.now() - pending.timestamp,
|
||||
});
|
||||
receiver
|
||||
.receiveData(pending.data, pending.config)
|
||||
.then(() => {
|
||||
pendingTransfersRef.current.delete(componentId);
|
||||
logger.info("대기 데이터 전달 완료", { componentId });
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error("대기 데이터 전달 실패", { componentId, error: err });
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const unregisterDataReceiver = useCallback((componentId: string) => {
|
||||
@@ -110,7 +144,24 @@ export function ScreenContextProvider({
|
||||
return new Map(dataReceiversRef.current);
|
||||
}, []);
|
||||
|
||||
// 🆕 useMemo로 value 객체 메모이제이션 (무한 루프 방지)
|
||||
const addPendingTransfer = useCallback((transfer: PendingTransfer) => {
|
||||
pendingTransfersRef.current.set(transfer.targetComponentId, transfer);
|
||||
logger.info("데이터 전달 대기열 추가", {
|
||||
targetComponentId: transfer.targetComponentId,
|
||||
dataCount: transfer.data.length,
|
||||
targetLayerId: transfer.targetLayerId,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getPendingTransfer = useCallback((componentId: string) => {
|
||||
return pendingTransfersRef.current.get(componentId);
|
||||
}, []);
|
||||
|
||||
const clearPendingTransfer = useCallback((componentId: string) => {
|
||||
pendingTransfersRef.current.delete(componentId);
|
||||
logger.debug("대기 데이터 전달 클리어", { componentId });
|
||||
}, []);
|
||||
|
||||
const value = React.useMemo<ScreenContextValue>(
|
||||
() => ({
|
||||
screenId,
|
||||
@@ -127,6 +178,9 @@ export function ScreenContextProvider({
|
||||
getDataReceiver,
|
||||
getAllDataProviders,
|
||||
getAllDataReceivers,
|
||||
addPendingTransfer,
|
||||
getPendingTransfer,
|
||||
clearPendingTransfer,
|
||||
}),
|
||||
[
|
||||
screenId,
|
||||
@@ -143,6 +197,9 @@ export function ScreenContextProvider({
|
||||
getDataReceiver,
|
||||
getAllDataProviders,
|
||||
getAllDataReceivers,
|
||||
addPendingTransfer,
|
||||
getPendingTransfer,
|
||||
clearPendingTransfer,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user