fix: position page below AppShell header, wait for container size

- Page uses top-16/sm:top-14 to start below AppShell header (z-50)
  instead of inset-0 which renders behind the header
- Sigma init polls with RAF until container has real dimensions
  instead of allowInvalidContainer which creates broken canvas
- Removed overflow-hidden from content area (not needed)
This commit is contained in:
Fringg
2026-03-19 09:04:55 +03:00
parent 6f58a0ce5d
commit fd9a47ecda
2 changed files with 18 additions and 11 deletions

View File

@@ -29,24 +29,24 @@ export function ReferralNetwork() {
const isPanelOpen = selectedNode !== null; const isPanelOpen = selectedNode !== null;
return ( return (
<div id="referral-network-container" className="fixed inset-0 z-40 flex flex-col bg-[#0a0a0f]"> <div
id="referral-network-container"
className="fixed inset-x-0 bottom-0 top-16 z-40 flex flex-col bg-[#0a0a0f] sm:top-14"
>
{/* Top bar */} {/* Top bar */}
<div className="relative z-20 shrink-0 border-b border-dark-700/50 bg-dark-900/90 backdrop-blur-md"> <div className="relative z-20 shrink-0 border-b border-dark-700/50 bg-dark-900/90 backdrop-blur-md">
{/* Row 1: Back + Title + Search + Filter */}
<div className="flex items-center gap-2 px-3 py-2 sm:gap-3 sm:px-4 sm:py-3"> <div className="flex items-center gap-2 px-3 py-2 sm:gap-3 sm:px-4 sm:py-3">
<AdminBackButton /> <AdminBackButton />
<h1 className="shrink-0 text-sm font-bold text-dark-100 sm:text-base"> <h1 className="shrink-0 text-sm font-bold text-dark-100 sm:text-base">
{t('admin.referralNetwork.title')} {t('admin.referralNetwork.title')}
</h1> </h1>
{/* Search — always visible */}
<NetworkSearch className="min-w-0 flex-1 sm:max-w-md" /> <NetworkSearch className="min-w-0 flex-1 sm:max-w-md" />
{/* Filter button */}
{networkData && <NetworkFilters data={networkData} />} {networkData && <NetworkFilters data={networkData} />}
</div> </div>
</div> </div>
{/* Main content area */} {/* Main content area */}
<div className="relative min-h-0 flex-1 overflow-hidden"> <div className="relative min-h-0 flex-1">
{/* Loading state */} {/* Loading state */}
{isLoading && ( {isLoading && (
<div className="absolute inset-0 flex items-center justify-center"> <div className="absolute inset-0 flex items-center justify-center">

View File

@@ -129,11 +129,16 @@ export function NetworkGraph({ data, className }: NetworkGraphProps) {
if (!containerRef.current || !data) return; if (!containerRef.current || !data) return;
const container = containerRef.current; const container = containerRef.current;
let rafId: number;
let cancelled = false;
// Defer initialization to next frame so the browser computes layout first. // Poll until container has real dimensions (browser needs a frame to compute layout)
// Without this, container.offsetHeight is 0 and Sigma throws. function tryInit() {
const rafId = requestAnimationFrame(() => { if (cancelled || !container.isConnected) return;
if (!container.isConnected) return; if (container.offsetWidth === 0 || container.offsetHeight === 0) {
rafId = requestAnimationFrame(tryInit);
return;
}
// Cleanup previous instance // Cleanup previous instance
killFA2(); killFA2();
@@ -150,7 +155,6 @@ export function NetworkGraph({ data, className }: NetworkGraphProps) {
hiddenNodesRef.current = computeHiddenNodes(graph, initialFilters); hiddenNodesRef.current = computeHiddenNodes(graph, initialFilters);
const sigma = new Sigma(graph, container, { const sigma = new Sigma(graph, container, {
allowInvalidContainer: true,
renderEdgeLabels: false, renderEdgeLabels: false,
labelDensity: 0.5, labelDensity: 0.5,
labelRenderedSizeThreshold: 6, labelRenderedSizeThreshold: 6,
@@ -297,9 +301,12 @@ export function NetworkGraph({ data, className }: NetworkGraphProps) {
containerRef.current.style.cursor = 'default'; containerRef.current.style.cursor = 'default';
} }
}); });
}); // end requestAnimationFrame }
rafId = requestAnimationFrame(tryInit);
return () => { return () => {
cancelled = true;
cancelAnimationFrame(rafId); cancelAnimationFrame(rafId);
killFA2(); killFA2();
if (sigmaRef.current) { if (sigmaRef.current) {