ERP-node/frontend/components/layout/TabContent.tsx

95 lines
3.2 KiB
TypeScript

"use client";
import React, { Suspense, useRef } from "react";
import { Loader2, Inbox } from "lucide-react";
import { useTab, TabItem } from "@/contexts/TabContext";
import { ScreenViewPageEmbeddable } from "@/app/(main)/screens/[screenId]/page";
function TabLoadingFallback() {
return (
<div className="flex h-full min-h-[400px] w-full items-center justify-center">
<div className="rounded-xl border border-slate-200 bg-white p-8 text-center shadow-lg">
<Loader2 className="text-primary mx-auto h-10 w-10 animate-spin" />
<p className="mt-4 font-medium text-slate-900"> ...</p>
</div>
</div>
);
}
function EmptyTabState() {
return (
<div className="flex h-full w-full items-center justify-center bg-white">
<div className="flex flex-col items-center py-12 text-center">
<div className="mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-slate-100">
<Inbox className="h-8 w-8 text-slate-400" />
</div>
<h3 className="mb-2 text-lg font-semibold text-slate-700"> </h3>
<p className="max-w-sm text-sm text-slate-500">
.
</p>
</div>
</div>
);
}
export function TabContent() {
const { tabs, activeTabId, refreshKeys } = useTab();
// 탭 순서 변경(드래그 리오더링) 시 DOM 재배치를 방지하기 위해
// 탭이 추가된 순서(id 기준)로 고정 렌더링
const stableOrderRef = useRef<TabItem[]>([]);
// F5 이후 활성 탭만 마운트하고, 비활성 탭은 클릭 시 마운트 (Lazy Mount)
const mountedTabIdsRef = useRef<Set<string>>(
new Set(activeTabId ? [activeTabId] : [])
);
// 활성 탭을 마운트 목록에 추가
if (activeTabId && !mountedTabIdsRef.current.has(activeTabId)) {
mountedTabIdsRef.current.add(activeTabId);
}
// 새 탭 추가 시 stableOrder에 append, 닫힌 탭은 제거
const currentIds = new Set(tabs.map(t => t.id));
const stableIds = new Set(stableOrderRef.current.map(t => t.id));
// 닫힌 탭 제거
stableOrderRef.current = stableOrderRef.current.filter(t => currentIds.has(t.id));
// 닫힌 탭을 마운트 목록에서도 제거
for (const id of mountedTabIdsRef.current) {
if (!currentIds.has(id)) mountedTabIdsRef.current.delete(id);
}
// 새로 추가된 탭 append
for (const tab of tabs) {
if (!stableIds.has(tab.id)) {
stableOrderRef.current.push(tab);
}
}
const stableTabs = stableOrderRef.current;
if (stableTabs.length === 0) {
return <EmptyTabState />;
}
return (
<>
{stableTabs.map((tab) => (
<div
key={`${tab.id}-${refreshKeys[tab.id] || 0}`}
className="h-full w-full"
style={{ display: tab.id === activeTabId ? "block" : "none" }}
>
{mountedTabIdsRef.current.has(tab.id) ? (
<Suspense fallback={<TabLoadingFallback />}>
<ScreenViewPageEmbeddable
screenId={tab.screenId}
menuObjid={tab.menuObjid}
/>
</Suspense>
) : null}
</div>
))}
</>
);
}