fix: Separate mobile/desktop sidebar components

- Mobile: fixed overlay sidebar with proper z-index
- Desktop: sticky sidebar with correct top offset
- Extracted SidebarContent component to avoid duplication
- Matches App.tsx submenu bar positioning logic
This commit is contained in:
Dwindi Ramadhana
2026-01-04 12:33:46 +07:00
parent 3a8c436839
commit 54a1ec1c88

View File

@@ -27,14 +27,15 @@ export default function Help() {
const currentSlug = searchParams.get('doc') || 'getting-started'; const currentSlug = searchParams.get('doc') || 'getting-started';
// Calculate sticky top position based on mode // Calculate sticky top position based on mode
// Standalone/fullscreen mode: top-16 (below 64px header) // This matches the submenu bar logic in App.tsx:
// WP Admin mode: top-[calc(7rem+32px)] (header + topnav + wp-admin bar) // - Standalone/fullscreen: top-0 (header already handles offset)
const sidebarTopClass = isStandalone // - WP Admin: top-[calc(7rem+32px)] = 144px (header 64px + topnav 48px + wp-admin bar 32px)
? 'top-16' const sidebarStickyTop = isStandalone
? 'top-0'
: 'top-[calc(7rem+32px)]'; : 'top-[calc(7rem+32px)]';
// Height calculation: full height minus header // Height calculation matches App.tsx Sidebar pattern
const sidebarHeightClass = isStandalone const sidebarHeight = isStandalone
? 'h-[calc(100vh-64px)]' ? 'h-[calc(100vh-64px)]'
: 'h-[calc(100vh-7rem-32px)]'; : 'h-[calc(100vh-7rem-32px)]';
@@ -53,7 +54,6 @@ export default function Help() {
if (data.success) { if (data.success) {
setSections(data.sections); setSections(data.sections);
// Expand all sections by default
const expanded: Record<string, boolean> = {}; const expanded: Record<string, boolean> = {};
data.sections.forEach((section: DocSection) => { data.sections.forEach((section: DocSection) => {
expanded[section.key] = true; expanded[section.key] = true;
@@ -85,8 +85,8 @@ export default function Help() {
const isActive = (slug: string) => slug === currentSlug; const isActive = (slug: string) => slug === currentSlug;
return ( return (
<div className="flex min-h-0"> <div className="flex">
{/* Mobile menu button */} {/* Mobile menu button - only show on small screens */}
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
@@ -96,20 +96,80 @@ export default function Help() {
{sidebarOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />} {sidebarOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</Button> </Button>
{/* Sidebar - sticky on desktop, fixed on mobile */} {/* Backdrop for mobile sidebar */}
{sidebarOpen && (
<div
className="fixed inset-0 bg-black/50 z-30 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
{/* Mobile sidebar - fixed overlay */}
<aside <aside
className={cn( className={cn(
"w-72 border-r bg-muted/30 flex-shrink-0 overflow-y-auto", "lg:hidden fixed left-0 z-40 w-72 bg-background border-r overflow-y-auto",
// Mobile: fixed position covering from below header sidebarStickyTop,
"fixed left-0 z-40", sidebarHeight,
sidebarTopClass, sidebarOpen ? "block" : "hidden"
sidebarHeightClass,
// Desktop: sticky instead of fixed
"lg:sticky",
sidebarOpen ? "block" : "hidden lg:block"
)} )}
> >
<div className="p-4 border-b bg-muted/30"> <SidebarContent
loading={loading}
sections={sections}
expandedSections={expandedSections}
toggleSection={toggleSection}
selectDoc={selectDoc}
isActive={isActive}
/>
</aside>
{/* Desktop sidebar - sticky */}
<aside
className={cn(
"hidden lg:block w-72 flex-shrink-0 border-r bg-muted/30 overflow-y-auto sticky",
sidebarStickyTop,
sidebarHeight
)}
>
<SidebarContent
loading={loading}
sections={sections}
expandedSections={expandedSections}
toggleSection={toggleSection}
selectDoc={selectDoc}
isActive={isActive}
/>
</aside>
{/* Main content - uses page scroll */}
<main className="flex-1 min-w-0">
<div className="max-w-4xl mx-auto py-6 px-6 lg:px-10">
<DocContent slug={currentSlug} />
</div>
</main>
</div>
);
}
// Extracted sidebar content to avoid duplication
function SidebarContent({
loading,
sections,
expandedSections,
toggleSection,
selectDoc,
isActive,
}: {
loading: boolean;
sections: DocSection[];
expandedSections: Record<string, boolean>;
toggleSection: (key: string) => void;
selectDoc: (slug: string) => void;
isActive: (slug: string) => boolean;
}) {
return (
<>
<div className="p-4 border-b bg-muted/30 sticky top-0">
<h2 className="text-lg font-semibold flex items-center gap-2"> <h2 className="text-lg font-semibold flex items-center gap-2">
<Book className="w-5 h-5" /> <Book className="w-5 h-5" />
Documentation Documentation
@@ -161,22 +221,6 @@ export default function Help() {
)) ))
)} )}
</nav> </nav>
</aside> </>
{/* Backdrop for mobile */}
{sidebarOpen && (
<div
className="fixed inset-0 bg-black/50 z-30 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
{/* Main content - uses page scroll, not internal overflow */}
<main className="flex-1 min-w-0">
<div className="max-w-4xl mx-auto py-6 px-6 lg:px-10">
<DocContent slug={currentSlug} />
</div>
</main>
</div>
); );
} }