152 lines
4.1 KiB
TypeScript
152 lines
4.1 KiB
TypeScript
import React, {
|
|
createContext,
|
|
useContext,
|
|
useState,
|
|
useCallback,
|
|
useMemo,
|
|
ReactNode,
|
|
} from "react";
|
|
import {
|
|
TableRegistration,
|
|
TableOptionsContextValue,
|
|
} from "@/types/table-options";
|
|
import { useActiveTab } from "./ActiveTabContext";
|
|
|
|
const TableOptionsContext = createContext<TableOptionsContextValue | undefined>(
|
|
undefined
|
|
);
|
|
|
|
export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
|
|
children,
|
|
}) => {
|
|
const [registeredTables, setRegisteredTables] = useState<
|
|
Map<string, TableRegistration>
|
|
>(new Map());
|
|
const [selectedTableId, setSelectedTableId] = useState<string | null>(null);
|
|
|
|
/**
|
|
* 테이블 등록
|
|
*/
|
|
const registerTable = useCallback((registration: TableRegistration) => {
|
|
setRegisteredTables((prev) => {
|
|
const newMap = new Map(prev);
|
|
newMap.set(registration.tableId, registration);
|
|
|
|
// 첫 번째 테이블이면 자동 선택
|
|
if (newMap.size === 1) {
|
|
setSelectedTableId(registration.tableId);
|
|
}
|
|
|
|
return newMap;
|
|
});
|
|
}, []);
|
|
|
|
/**
|
|
* 테이블 등록 해제
|
|
* 주의:
|
|
* 1. selectedTableId를 의존성으로 사용하면 무한 루프 발생 가능
|
|
* 2. 재등록 시에도 unregister가 호출되므로 selectedTableId를 변경하면 안됨
|
|
*/
|
|
const unregisterTable = useCallback(
|
|
(tableId: string) => {
|
|
setRegisteredTables((prev) => {
|
|
const newMap = new Map(prev);
|
|
newMap.delete(tableId);
|
|
return newMap;
|
|
});
|
|
|
|
// 🚫 selectedTableId를 변경하지 않음
|
|
// 이유: useEffect 재실행 시 cleanup → register 순서로 호출되는데,
|
|
// cleanup에서 selectedTableId를 null로 만들면 필터 설정이 초기화됨
|
|
// 다른 테이블이 선택되어야 하면 TableSearchWidget에서 자동 선택함
|
|
},
|
|
[] // 의존성 없음 - 무한 루프 방지
|
|
);
|
|
|
|
/**
|
|
* 특정 테이블 조회
|
|
*/
|
|
const getTable = useCallback(
|
|
(tableId: string) => {
|
|
return registeredTables.get(tableId);
|
|
},
|
|
[registeredTables]
|
|
);
|
|
|
|
/**
|
|
* 테이블 데이터 건수 업데이트
|
|
*/
|
|
const updateTableDataCount = useCallback((tableId: string, count: number) => {
|
|
setRegisteredTables((prev) => {
|
|
const table = prev.get(tableId);
|
|
if (table) {
|
|
// 기존 테이블 정보에 dataCount만 업데이트
|
|
const updatedTable = { ...table, dataCount: count };
|
|
const newMap = new Map(prev);
|
|
newMap.set(tableId, updatedTable);
|
|
return newMap;
|
|
}
|
|
return prev;
|
|
});
|
|
}, []);
|
|
|
|
// ActiveTab context 사용 (optional - 에러 방지)
|
|
const activeTabContext = useActiveTab();
|
|
|
|
/**
|
|
* 현재 활성 탭의 테이블만 반환
|
|
*/
|
|
const getActiveTabTables = useCallback(() => {
|
|
const allTables = Array.from(registeredTables.values());
|
|
const activeTabIds = activeTabContext.getAllActiveTabIds();
|
|
|
|
// 활성 탭이 없으면 탭에 속하지 않은 테이블만 반환
|
|
if (activeTabIds.length === 0) {
|
|
return allTables.filter(table => !table.parentTabId);
|
|
}
|
|
|
|
// 활성 탭에 속한 테이블 + 탭에 속하지 않은 테이블
|
|
return allTables.filter(table =>
|
|
!table.parentTabId || activeTabIds.includes(table.parentTabId)
|
|
);
|
|
}, [registeredTables, activeTabContext]);
|
|
|
|
/**
|
|
* 특정 탭의 테이블만 반환
|
|
*/
|
|
const getTablesForTab = useCallback((tabId: string) => {
|
|
const allTables = Array.from(registeredTables.values());
|
|
return allTables.filter(table => table.parentTabId === tabId);
|
|
}, [registeredTables]);
|
|
|
|
return (
|
|
<TableOptionsContext.Provider
|
|
value={{
|
|
registeredTables,
|
|
registerTable,
|
|
unregisterTable,
|
|
getTable,
|
|
updateTableDataCount,
|
|
selectedTableId,
|
|
setSelectedTableId,
|
|
getActiveTabTables,
|
|
getTablesForTab,
|
|
}}
|
|
>
|
|
{children}
|
|
</TableOptionsContext.Provider>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Context Hook
|
|
*/
|
|
export const useTableOptions = () => {
|
|
const context = useContext(TableOptionsContext);
|
|
if (!context) {
|
|
throw new Error("useTableOptions must be used within TableOptionsProvider");
|
|
}
|
|
return context;
|
|
};
|
|
|