refactor: Move page header outside content container using context

Problem:
- Page header inside scrollable content container
- Complex sticky positioning logic
- Different behavior in different modes

Better Architecture:
Move page header to same level as submenu, outside scroll container

Structure:
<main flex flex-col>
  <SubmenuBar sticky>           ← Sticky outside scroll
  <PageHeader sticky>            ← Sticky outside scroll 
  <div overflow-auto>            ← Only content scrolls
    <AppRoutes />

Implementation:
1. PageHeaderContext - Global state for page header
   - title: string
   - action: ReactNode (e.g., Save button)
   - setPageHeader() / clearPageHeader()

2. PageHeader Component - Renders at app level
   - Positioned after submenu
   - Sticky top-[49px] (below submenu)
   - Boxed layout (max-w-5xl, centered)
   - Consumes context

3. SettingsLayout - Sets header via context
   - useEffect to set/clear header
   - No inline sticky header
   - Cleaner component

Benefits:
 Page header outside scroll container
 Sticky works consistently (no mode detection)
 Submenu layout preserved (justify-start)
 Page header uses page layout (boxed, centered)
 Separation of concerns
 Reusable for any page that needs sticky header

Layout Hierarchy:
┌─────────────────────────────────────┐
│ <main flex flex-col>                │
│   ┌─────────────────────────────┐   │
│   │ SubmenuBar (sticky)         │   │ ← justify-start
│   ├─────────────────────────────┤   │
│   │ PageHeader (sticky)         │   │ ← max-w-5xl centered
│   ├─────────────────────────────┤   │
│   │ <div overflow-auto>         │   │
│   │   Content (scrolls)         │   │
│   └─────────────────────────────┘   │
└─────────────────────────────────────┘

Files Created:
- PageHeaderContext.tsx: Context provider
- PageHeader.tsx: Header component

Files Modified:
- App.tsx: Added PageHeader after submenu in all layouts
- SettingsLayout.tsx: Use context instead of inline header

Result:
 Clean architecture
 Consistent sticky behavior
 No mode-specific logic
 Reusable pattern
This commit is contained in:
dwindown
2025-11-06 15:34:00 +07:00
parent 99748ca202
commit 2ec76c7dec
4 changed files with 98 additions and 28 deletions

View File

@@ -29,6 +29,8 @@ import { useCommandStore } from "@/lib/useCommandStore";
import SubmenuBar from './components/nav/SubmenuBar';
import DashboardSubmenuBar from './components/nav/DashboardSubmenuBar';
import { DashboardProvider } from '@/contexts/DashboardContext';
import { PageHeaderProvider } from '@/contexts/PageHeaderContext';
import { PageHeader } from '@/components/PageHeader';
import { useActiveSection } from '@/hooks/useActiveSection';
import { NAV_TREE_VERSION } from '@/nav/tree';
import { __ } from '@/lib/i18n';
@@ -420,6 +422,7 @@ function Shell() {
) : (
<SubmenuBar items={main.children} fullscreen={true} />
)}
<PageHeader />
<div className="flex-1 overflow-auto p-4">
<AppRoutes />
</div>
@@ -434,6 +437,7 @@ function Shell() {
<SubmenuBar items={main.children} fullscreen={true} />
)}
<main className="flex-1 flex flex-col min-h-0">
<PageHeader />
<div className="flex-1 overflow-auto p-4">
<AppRoutes />
</div>
@@ -448,6 +452,7 @@ function Shell() {
) : (
<SubmenuBar items={main.children} fullscreen={false} />
)}
<PageHeader />
<main className="flex-1 flex flex-col min-h-0">
<div className="flex-1 overflow-auto p-4">
<AppRoutes />
@@ -505,9 +510,11 @@ function AuthWrapper() {
}
return (
<DashboardProvider>
<Shell />
</DashboardProvider>
<PageHeaderProvider>
<DashboardProvider>
<Shell />
</DashboardProvider>
</PageHeaderProvider>
);
}