77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
'use client';
|
|
|
|
/**
|
|
* Lab Layout Component
|
|
*
|
|
* Main layout wrapper for the Lab section combining sidebar and content.
|
|
* Uses ThreeColumnLayout for consistent column structure across the app.
|
|
*
|
|
* Structure:
|
|
* - Left: LabSidebar (w-64, hidden lg:block, scrollable)
|
|
* - Center: Scrollable content area + fixed LabFooter at bottom
|
|
* - Right: Reserved for future use (TOC, preview panels, etc.)
|
|
*
|
|
* The center column uses a fixed height container with:
|
|
* - Scrollable content area (flex-1 overflow-y-auto)
|
|
* - Sticky footer always visible at bottom
|
|
*
|
|
* This layout is rendered inside PageProvider context which provides:
|
|
* - SubsectionNav at the top
|
|
* - ApiKeyWidget in the right slot
|
|
* - AnimatedBackground
|
|
*
|
|
* Scroll Detection:
|
|
* - Detects scroll in the main content area
|
|
* - After 50px threshold, collapses the main header via LabScrollContext
|
|
* - Height adjusts dynamically: 100vh-7rem (header visible) → 100vh-3rem (header hidden)
|
|
*/
|
|
|
|
import { ReactNode, useRef, useEffect, useCallback } from 'react';
|
|
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
|
|
import { LabSidebar } from './LabSidebar';
|
|
import { LabFooter } from './LabFooter';
|
|
import { useLabScroll } from '@/contexts/lab-scroll-context';
|
|
|
|
type LabLayoutProps = {
|
|
children: ReactNode;
|
|
};
|
|
|
|
const SCROLL_THRESHOLD = 50;
|
|
|
|
export const LabLayout = ({ children }: LabLayoutProps) => {
|
|
const { isScrolled, setIsScrolled } = useLabScroll();
|
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
|
|
const handleScroll = useCallback(() => {
|
|
if (!contentRef.current) return;
|
|
const scrollTop = contentRef.current.scrollTop;
|
|
setIsScrolled(scrollTop > SCROLL_THRESHOLD);
|
|
}, [setIsScrolled]);
|
|
|
|
useEffect(() => {
|
|
const contentElement = contentRef.current;
|
|
if (!contentElement) return;
|
|
|
|
contentElement.addEventListener('scroll', handleScroll);
|
|
return () => contentElement.removeEventListener('scroll', handleScroll);
|
|
}, [handleScroll]);
|
|
|
|
const containerHeight = isScrolled ? 'h-[calc(100vh-3rem)]' : 'h-[calc(100vh-7rem)]';
|
|
|
|
return (
|
|
<ThreeColumnLayout
|
|
left={
|
|
<div className={`border-r border-zinc-800 bg-zinc-950/50 backdrop-blur-sm ${containerHeight} overflow-y-auto transition-all duration-300`}>
|
|
<LabSidebar />
|
|
</div>
|
|
}
|
|
center={
|
|
<div className={`flex flex-col ${containerHeight} transition-all duration-300`}>
|
|
<div ref={contentRef} className="flex-1 overflow-y-auto min-h-0">{children}</div>
|
|
<LabFooter />
|
|
</div>
|
|
}
|
|
/>
|
|
);
|
|
};
|