ERP-node/frontend/contexts/ScreenContext.tsx

134 lines
4.3 KiB
TypeScript

/**
* 화면 컨텍스트
* 같은 화면 내의 컴포넌트들이 서로 통신할 수 있도록 합니다.
*/
"use client";
import React, { createContext, useContext, useCallback, useRef } from "react";
import type { DataProvidable, DataReceivable } from "@/types/data-transfer";
import { logger } from "@/lib/utils/logger";
import type { SplitPanelPosition } from "@/contexts/SplitPanelContext";
interface ScreenContextValue {
screenId?: number;
tableName?: string;
splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치 (left/right)
// 컴포넌트 등록
registerDataProvider: (componentId: string, provider: DataProvidable) => void;
unregisterDataProvider: (componentId: string) => void;
registerDataReceiver: (componentId: string, receiver: DataReceivable) => void;
unregisterDataReceiver: (componentId: string) => void;
// 컴포넌트 조회
getDataProvider: (componentId: string) => DataProvidable | undefined;
getDataReceiver: (componentId: string) => DataReceivable | undefined;
// 모든 컴포넌트 조회
getAllDataProviders: () => Map<string, DataProvidable>;
getAllDataReceivers: () => Map<string, DataReceivable>;
}
const ScreenContext = createContext<ScreenContextValue | null>(null);
interface ScreenContextProviderProps {
screenId?: number;
tableName?: string;
splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치
children: React.ReactNode;
}
/**
* 화면 컨텍스트 프로바이더
*/
export function ScreenContextProvider({ screenId, tableName, splitPanelPosition, children }: ScreenContextProviderProps) {
const dataProvidersRef = useRef<Map<string, DataProvidable>>(new Map());
const dataReceiversRef = useRef<Map<string, DataReceivable>>(new Map());
const registerDataProvider = useCallback((componentId: string, provider: DataProvidable) => {
dataProvidersRef.current.set(componentId, provider);
logger.debug("데이터 제공자 등록", { componentId, componentType: provider.componentType });
}, []);
const unregisterDataProvider = useCallback((componentId: string) => {
dataProvidersRef.current.delete(componentId);
logger.debug("데이터 제공자 해제", { componentId });
}, []);
const registerDataReceiver = useCallback((componentId: string, receiver: DataReceivable) => {
dataReceiversRef.current.set(componentId, receiver);
logger.debug("데이터 수신자 등록", { componentId, componentType: receiver.componentType });
}, []);
const unregisterDataReceiver = useCallback((componentId: string) => {
dataReceiversRef.current.delete(componentId);
logger.debug("데이터 수신자 해제", { componentId });
}, []);
const getDataProvider = useCallback((componentId: string) => {
return dataProvidersRef.current.get(componentId);
}, []);
const getDataReceiver = useCallback((componentId: string) => {
return dataReceiversRef.current.get(componentId);
}, []);
const getAllDataProviders = useCallback(() => {
return new Map(dataProvidersRef.current);
}, []);
const getAllDataReceivers = useCallback(() => {
return new Map(dataReceiversRef.current);
}, []);
// 🆕 useMemo로 value 객체 메모이제이션 (무한 루프 방지)
const value = React.useMemo<ScreenContextValue>(() => ({
screenId,
tableName,
splitPanelPosition,
registerDataProvider,
unregisterDataProvider,
registerDataReceiver,
unregisterDataReceiver,
getDataProvider,
getDataReceiver,
getAllDataProviders,
getAllDataReceivers,
}), [
screenId,
tableName,
splitPanelPosition,
registerDataProvider,
unregisterDataProvider,
registerDataReceiver,
unregisterDataReceiver,
getDataProvider,
getDataReceiver,
getAllDataProviders,
getAllDataReceivers,
]);
return <ScreenContext.Provider value={value}>{children}</ScreenContext.Provider>;
}
/**
* 화면 컨텍스트 훅
*/
export function useScreenContext() {
const context = useContext(ScreenContext);
if (!context) {
throw new Error("useScreenContext는 ScreenContextProvider 내부에서만 사용할 수 있습니다.");
}
return context;
}
/**
* 화면 컨텍스트 훅 (선택적)
* 컨텍스트가 없어도 에러를 발생시키지 않습니다.
*/
export function useScreenContextOptional() {
return useContext(ScreenContext);
}