@djangocfg/layouts 2.1.249 → 2.1.252
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/package.json +18 -18
- package/src/index.ts +4 -2
- package/src/layouts/AppLayout/AppLayout.tsx +70 -13
- package/src/layouts/AppLayout/index.ts +7 -1
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +22 -66
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +28 -11
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +159 -116
- package/src/layouts/PrivateLayout/components/index.ts +0 -2
- package/src/layouts/PublicLayout/PublicLayout.tsx +27 -42
- package/src/layouts/PublicLayout/components/PublicFooter/FooterMenuSections.tsx +13 -2
- package/src/layouts/PublicLayout/components/PublicFooter/FooterProjectInfo.tsx +7 -5
- package/src/layouts/PublicLayout/components/PublicFooter/PublicFooter.tsx +130 -109
- package/src/layouts/PublicLayout/components/PublicFooter/types.ts +9 -4
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +32 -8
- package/src/layouts/PublicLayout/components/PublicNavbar.tsx +74 -0
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +274 -113
- package/src/layouts/PublicLayout/components/index.ts +1 -0
- package/src/layouts/PublicLayout/context.tsx +0 -9
- package/src/layouts/PublicLayout/index.ts +8 -1
- package/src/layouts/_components/PrivateSidebarAccount.tsx +168 -0
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +0 -72
package/README.md
CHANGED
|
@@ -92,15 +92,17 @@ export default function RootLayout({ children }) {
|
|
|
92
92
|
<html lang="en" suppressHydrationWarning>
|
|
93
93
|
<body>
|
|
94
94
|
<AppLayout
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
95
|
+
layouts={{
|
|
96
|
+
public: { component: PublicLayout, enabledPath: ['/', '/legal', '/contact'] },
|
|
97
|
+
private: { component: PrivateLayout, enabledPath: ['/dashboard', '/profile'] },
|
|
98
|
+
admin: { component: AdminLayout, enabledPath: '/admin' },
|
|
99
|
+
noLayoutPaths: ['/private/terminal', '/embed'],
|
|
100
|
+
}}
|
|
101
|
+
baseApp={{
|
|
102
|
+
project: 'my-app',
|
|
103
|
+
theme: { defaultTheme: 'system' },
|
|
104
|
+
auth: { apiUrl: process.env.NEXT_PUBLIC_API_URL },
|
|
105
|
+
}}
|
|
104
106
|
>
|
|
105
107
|
{children}
|
|
106
108
|
</AppLayout>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.252",
|
|
4
4
|
"description": "Simple, straightforward layout components for Next.js - import and use with props",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
@@ -74,14 +74,14 @@
|
|
|
74
74
|
"check": "tsc --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"@djangocfg/api": "^2.1.
|
|
78
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
79
|
-
"@djangocfg/i18n": "^2.1.
|
|
80
|
-
"@djangocfg/monitor": "^2.1.
|
|
81
|
-
"@djangocfg/debuger": "^2.1.
|
|
82
|
-
"@djangocfg/ui-core": "^2.1.
|
|
83
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
84
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
77
|
+
"@djangocfg/api": "^2.1.252",
|
|
78
|
+
"@djangocfg/centrifugo": "^2.1.252",
|
|
79
|
+
"@djangocfg/i18n": "^2.1.252",
|
|
80
|
+
"@djangocfg/monitor": "^2.1.252",
|
|
81
|
+
"@djangocfg/debuger": "^2.1.252",
|
|
82
|
+
"@djangocfg/ui-core": "^2.1.252",
|
|
83
|
+
"@djangocfg/ui-nextjs": "^2.1.252",
|
|
84
|
+
"@djangocfg/ui-tools": "^2.1.252",
|
|
85
85
|
"@hookform/resolvers": "^5.2.2",
|
|
86
86
|
"consola": "^3.4.2",
|
|
87
87
|
"lucide-react": "^0.545.0",
|
|
@@ -109,15 +109,15 @@
|
|
|
109
109
|
"uuid": "^11.1.0"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
|
-
"@djangocfg/api": "^2.1.
|
|
113
|
-
"@djangocfg/i18n": "^2.1.
|
|
114
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
115
|
-
"@djangocfg/monitor": "^2.1.
|
|
116
|
-
"@djangocfg/debuger": "^2.1.
|
|
117
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
118
|
-
"@djangocfg/ui-core": "^2.1.
|
|
119
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
120
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
112
|
+
"@djangocfg/api": "^2.1.252",
|
|
113
|
+
"@djangocfg/i18n": "^2.1.252",
|
|
114
|
+
"@djangocfg/centrifugo": "^2.1.252",
|
|
115
|
+
"@djangocfg/monitor": "^2.1.252",
|
|
116
|
+
"@djangocfg/debuger": "^2.1.252",
|
|
117
|
+
"@djangocfg/typescript-config": "^2.1.252",
|
|
118
|
+
"@djangocfg/ui-core": "^2.1.252",
|
|
119
|
+
"@djangocfg/ui-nextjs": "^2.1.252",
|
|
120
|
+
"@djangocfg/ui-tools": "^2.1.252",
|
|
121
121
|
"@types/node": "^24.7.2",
|
|
122
122
|
"@types/react": "^19.1.0",
|
|
123
123
|
"@types/react-dom": "^19.1.0",
|
package/src/index.ts
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```tsx
|
|
9
|
-
* import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
|
|
9
|
+
* import { PublicLayout, PublicNavbar, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
|
|
10
10
|
*
|
|
11
11
|
* // Public page
|
|
12
|
-
* <PublicLayout
|
|
12
|
+
* <PublicLayout
|
|
13
|
+
* navbar={<PublicNavbar config={{ logo: "/logo.svg", siteName: "My App", navigation: navItems }} />}
|
|
14
|
+
* >
|
|
13
15
|
* {children}
|
|
14
16
|
* </PublicLayout>
|
|
15
17
|
*
|
|
@@ -88,9 +88,36 @@ interface LayoutConfig {
|
|
|
88
88
|
enabledPath?: string | string[];
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
export interface AppLayoutLayoutsConfig {
|
|
92
|
+
public?: LayoutConfig;
|
|
93
|
+
private?: LayoutConfig;
|
|
94
|
+
admin?: LayoutConfig;
|
|
95
|
+
noLayoutPaths?: string | string[];
|
|
96
|
+
authPath?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface AppLayoutBaseAppConfig {
|
|
100
|
+
project?: string;
|
|
101
|
+
theme?: ThemeConfig;
|
|
102
|
+
auth?: AuthConfig;
|
|
103
|
+
analytics?: AnalyticsConfig;
|
|
104
|
+
centrifugo?: CentrifugoConfig;
|
|
105
|
+
errorTracking?: ErrorTrackingConfig;
|
|
106
|
+
swr?: SWRConfigOptions;
|
|
107
|
+
errorBoundary?: ErrorBoundaryConfig;
|
|
108
|
+
pwaInstall?: PwaInstallConfig;
|
|
109
|
+
monitor?: MonitorConfig;
|
|
110
|
+
debug?: DebugConfig;
|
|
111
|
+
}
|
|
112
|
+
|
|
91
113
|
export interface AppLayoutProps {
|
|
92
114
|
children: ReactNode;
|
|
93
115
|
|
|
116
|
+
/** Compact routing/layout config */
|
|
117
|
+
layouts?: AppLayoutLayoutsConfig;
|
|
118
|
+
/** Compact providers/base-app config */
|
|
119
|
+
baseApp?: AppLayoutBaseAppConfig;
|
|
120
|
+
|
|
94
121
|
/** Project name — used as default for monitor.project and debug panel title */
|
|
95
122
|
project?: string;
|
|
96
123
|
|
|
@@ -152,6 +179,16 @@ export interface AppLayoutProps {
|
|
|
152
179
|
debug?: DebugConfig;
|
|
153
180
|
}
|
|
154
181
|
|
|
182
|
+
interface AppLayoutContentProps {
|
|
183
|
+
children: ReactNode;
|
|
184
|
+
publicLayout?: LayoutConfig;
|
|
185
|
+
privateLayout?: LayoutConfig;
|
|
186
|
+
adminLayout?: LayoutConfig;
|
|
187
|
+
noLayoutPaths?: string | string[];
|
|
188
|
+
authPath?: string;
|
|
189
|
+
i18n?: I18nLayoutConfig;
|
|
190
|
+
}
|
|
191
|
+
|
|
155
192
|
/**
|
|
156
193
|
* AppLayout Content - Renders layout with all providers
|
|
157
194
|
*
|
|
@@ -166,7 +203,7 @@ function AppLayoutContent({
|
|
|
166
203
|
noLayoutPaths,
|
|
167
204
|
authPath = '/auth',
|
|
168
205
|
i18n,
|
|
169
|
-
}:
|
|
206
|
+
}: AppLayoutContentProps) {
|
|
170
207
|
// Use pathname without locale prefix for route matching
|
|
171
208
|
const pathname = usePathnameWithoutLocale();
|
|
172
209
|
|
|
@@ -270,20 +307,32 @@ function AppLayoutContent({
|
|
|
270
307
|
* AppLayout - Main Component with All Providers
|
|
271
308
|
*/
|
|
272
309
|
export function AppLayout(props: AppLayoutProps) {
|
|
310
|
+
const layoutsConfig = props.layouts;
|
|
311
|
+
const baseAppConfig = props.baseApp;
|
|
312
|
+
|
|
313
|
+
const publicLayout = layoutsConfig?.public ?? props.publicLayout;
|
|
314
|
+
const privateLayout = layoutsConfig?.private ?? props.privateLayout;
|
|
315
|
+
const adminLayout = layoutsConfig?.admin ?? props.adminLayout;
|
|
316
|
+
const noLayoutPaths = layoutsConfig?.noLayoutPaths ?? props.noLayoutPaths;
|
|
317
|
+
const authPath = layoutsConfig?.authPath ?? props.authPath;
|
|
318
|
+
|
|
273
319
|
const {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
auth,
|
|
277
|
-
analytics,
|
|
278
|
-
centrifugo,
|
|
279
|
-
errorTracking,
|
|
280
|
-
errorBoundary,
|
|
281
|
-
swr,
|
|
282
|
-
pwaInstall,
|
|
283
|
-
monitor,
|
|
284
|
-
debug,
|
|
320
|
+
i18n,
|
|
321
|
+
children,
|
|
285
322
|
} = props;
|
|
286
323
|
|
|
324
|
+
const project = baseAppConfig?.project ?? props.project;
|
|
325
|
+
const theme = baseAppConfig?.theme ?? props.theme;
|
|
326
|
+
const auth = baseAppConfig?.auth ?? props.auth;
|
|
327
|
+
const analytics = baseAppConfig?.analytics ?? props.analytics;
|
|
328
|
+
const centrifugo = baseAppConfig?.centrifugo ?? props.centrifugo;
|
|
329
|
+
const errorTracking = baseAppConfig?.errorTracking ?? props.errorTracking;
|
|
330
|
+
const errorBoundary = baseAppConfig?.errorBoundary ?? props.errorBoundary;
|
|
331
|
+
const swr = baseAppConfig?.swr ?? props.swr;
|
|
332
|
+
const pwaInstall = baseAppConfig?.pwaInstall ?? props.pwaInstall;
|
|
333
|
+
const monitor = baseAppConfig?.monitor ?? props.monitor;
|
|
334
|
+
const debug = baseAppConfig?.debug ?? props.debug;
|
|
335
|
+
|
|
287
336
|
return (
|
|
288
337
|
<BaseApp
|
|
289
338
|
project={project}
|
|
@@ -298,7 +347,15 @@ export function AppLayout(props: AppLayoutProps) {
|
|
|
298
347
|
monitor={monitor}
|
|
299
348
|
debug={debug}
|
|
300
349
|
>
|
|
301
|
-
<AppLayoutContent
|
|
350
|
+
<AppLayoutContent
|
|
351
|
+
children={children}
|
|
352
|
+
publicLayout={publicLayout}
|
|
353
|
+
privateLayout={privateLayout}
|
|
354
|
+
adminLayout={adminLayout}
|
|
355
|
+
noLayoutPaths={noLayoutPaths}
|
|
356
|
+
authPath={authPath}
|
|
357
|
+
i18n={i18n}
|
|
358
|
+
/>
|
|
302
359
|
</BaseApp>
|
|
303
360
|
);
|
|
304
361
|
}
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export { AppLayout } from './AppLayout';
|
|
6
|
-
export type {
|
|
6
|
+
export type {
|
|
7
|
+
AppLayoutProps,
|
|
8
|
+
AppLayoutLayoutsConfig,
|
|
9
|
+
AppLayoutBaseAppConfig,
|
|
10
|
+
LayoutMode,
|
|
11
|
+
I18nLayoutConfig,
|
|
12
|
+
} from './AppLayout';
|
|
7
13
|
|
|
8
14
|
export { BaseApp } from './BaseApp';
|
|
9
15
|
export type { BaseAppProps } from './BaseApp';
|
|
@@ -1,47 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Private Layout
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Responsive sidebar with mobile burger menu
|
|
9
|
-
* - Keyboard shortcut (Ctrl/Cmd + B) to toggle sidebar
|
|
10
|
-
* - Header with sidebar trigger and user menu
|
|
11
|
-
* - Configurable content padding
|
|
12
|
-
* - NO SSR - renders only on client to avoid hydration mismatch
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* import { PrivateLayout } from '@djangocfg/layouts';
|
|
17
|
-
*
|
|
18
|
-
* <PrivateLayout
|
|
19
|
-
* sidebar={{
|
|
20
|
-
* items: [
|
|
21
|
-
* { label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' },
|
|
22
|
-
* { label: 'Profile', href: '/profile', icon: 'User' }
|
|
23
|
-
* ]
|
|
24
|
-
* }}
|
|
25
|
-
* header={{
|
|
26
|
-
* title: 'Dashboard',
|
|
27
|
-
* groups: [
|
|
28
|
-
* {
|
|
29
|
-
* title: 'Account',
|
|
30
|
-
* items: [
|
|
31
|
-
* { label: 'Profile', href: '/profile' },
|
|
32
|
-
* { label: 'Settings', href: '/settings' }
|
|
33
|
-
* ]
|
|
34
|
-
* }
|
|
35
|
-
* ],
|
|
36
|
-
* authPath: '/auth'
|
|
37
|
-
* }}
|
|
38
|
-
* >
|
|
39
|
-
* {children}
|
|
40
|
-
* </PrivateLayout>
|
|
41
|
-
*
|
|
42
|
-
* Note: User data (name, email, avatar) is automatically loaded from useAuth() context
|
|
43
|
-
* Keyboard shortcut: Ctrl/Cmd + B to toggle sidebar
|
|
44
|
-
* ```
|
|
4
|
+
* Authenticated shell: collapsible sidebar (icon rail vs expanded) + scrollable content.
|
|
5
|
+
* Toggle lives in the sidebar header on desktop; on narrow viewports a `SidebarTrigger` sits in `PrivateContent` (opens the mobile Sheet).
|
|
6
|
+
* Ctrl/Cmd + B still toggles the sidebar width.
|
|
45
7
|
*/
|
|
46
8
|
|
|
47
9
|
'use client';
|
|
@@ -50,13 +12,12 @@ import React, { ReactNode, useEffect, useState } from 'react';
|
|
|
50
12
|
import { useRouter } from 'next/navigation';
|
|
51
13
|
|
|
52
14
|
import { useAuth } from '@djangocfg/api/auth';
|
|
53
|
-
import {
|
|
54
|
-
Preloader
|
|
55
|
-
} from '@djangocfg/ui-core/components';
|
|
15
|
+
import { Preloader } from '@djangocfg/ui-core/components';
|
|
56
16
|
import { SidebarInset, SidebarProvider } from '@djangocfg/ui-nextjs/components';
|
|
57
17
|
|
|
18
|
+
import type { I18nLayoutConfig } from '../AppLayout/AppLayout';
|
|
58
19
|
import { UserMenuConfig } from '../types';
|
|
59
|
-
import { PrivateContent,
|
|
20
|
+
import { PrivateContent, PrivateSidebar } from './components';
|
|
60
21
|
|
|
61
22
|
import type { LucideIcon as LucideIconType } from 'lucide-react';
|
|
62
23
|
|
|
@@ -86,21 +47,25 @@ export interface SidebarConfig {
|
|
|
86
47
|
}
|
|
87
48
|
|
|
88
49
|
export interface HeaderConfig {
|
|
50
|
+
/** Shown next to the logo when the sidebar is expanded */
|
|
89
51
|
title?: string;
|
|
90
|
-
/** User menu groups */
|
|
52
|
+
/** User menu groups (account panel in the sidebar footer) */
|
|
91
53
|
groups?: UserMenuConfig['groups'];
|
|
92
54
|
/** Auth page path (for sign in button) */
|
|
93
55
|
authPath?: string;
|
|
94
56
|
}
|
|
95
57
|
|
|
96
|
-
import type { I18nLayoutConfig } from '../AppLayout/AppLayout';
|
|
97
|
-
|
|
98
58
|
export interface PrivateLayoutProps {
|
|
99
59
|
children: ReactNode;
|
|
100
60
|
/** Sidebar configuration */
|
|
101
61
|
sidebar?: SidebarConfig;
|
|
102
|
-
/**
|
|
62
|
+
/** Title + account links (no top navbar — title is used in the sidebar chrome) */
|
|
103
63
|
header?: HeaderConfig;
|
|
64
|
+
/**
|
|
65
|
+
* Path for active nav highlighting. With `@djangocfg/nextjs` i18n routing, pass `usePathname()` from
|
|
66
|
+
* `@djangocfg/nextjs/i18n/navigation` (no `/[locale]` segment). If omitted, uses `next/navigation` (includes locale).
|
|
67
|
+
*/
|
|
68
|
+
pathname?: string;
|
|
104
69
|
/** Content padding */
|
|
105
70
|
contentPadding?: 'none' | 'default';
|
|
106
71
|
/** i18n configuration for locale switching */
|
|
@@ -111,6 +76,7 @@ export function PrivateLayout({
|
|
|
111
76
|
children,
|
|
112
77
|
sidebar,
|
|
113
78
|
header,
|
|
79
|
+
pathname,
|
|
114
80
|
contentPadding = 'default',
|
|
115
81
|
i18n,
|
|
116
82
|
}: PrivateLayoutProps) {
|
|
@@ -120,23 +86,18 @@ export function PrivateLayout({
|
|
|
120
86
|
|
|
121
87
|
useEffect(() => {
|
|
122
88
|
if (!isLoading && !isAuthenticated && !isRedirecting) {
|
|
123
|
-
// Save current URL (including query params) before redirecting to auth
|
|
124
89
|
const currentUrl = window.location.pathname + window.location.search;
|
|
125
90
|
saveRedirectUrl(currentUrl);
|
|
126
|
-
|
|
127
|
-
// Set redirecting state to prevent flicker
|
|
128
91
|
setIsRedirecting(true);
|
|
129
92
|
router.push(header?.authPath || '/auth');
|
|
130
93
|
}
|
|
131
94
|
}, [isAuthenticated, isLoading, isRedirecting, router, saveRedirectUrl, header?.authPath]);
|
|
132
95
|
|
|
133
|
-
// Show loading state while auth is being checked or redirecting
|
|
134
|
-
// Note: SSR hydration is handled by ClientOnly wrapper in AppLayout
|
|
135
96
|
if (isLoading || isRedirecting || !isAuthenticated) {
|
|
136
97
|
return (
|
|
137
98
|
<Preloader
|
|
138
99
|
variant="fullscreen"
|
|
139
|
-
text={isRedirecting ?
|
|
100
|
+
text={isRedirecting ? 'Redirecting to login...' : 'Authenticating...'}
|
|
140
101
|
size="lg"
|
|
141
102
|
backdrop={true}
|
|
142
103
|
backdropOpacity={80}
|
|
@@ -146,20 +107,15 @@ export function PrivateLayout({
|
|
|
146
107
|
|
|
147
108
|
return (
|
|
148
109
|
<SidebarProvider defaultOpen={true}>
|
|
149
|
-
{
|
|
150
|
-
|
|
110
|
+
{sidebar && (
|
|
111
|
+
<PrivateSidebar sidebar={sidebar} header={header} i18n={i18n} pathname={pathname} />
|
|
112
|
+
)}
|
|
151
113
|
|
|
152
|
-
{/* Main content area */}
|
|
153
114
|
<SidebarInset className="flex flex-col">
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
)}
|
|
158
|
-
|
|
159
|
-
{/* Page content */}
|
|
160
|
-
<PrivateContent padding={contentPadding}>{children}</PrivateContent>
|
|
115
|
+
<PrivateContent padding={contentPadding} hasSidebar={Boolean(sidebar)}>
|
|
116
|
+
{children}
|
|
117
|
+
</PrivateContent>
|
|
161
118
|
</SidebarInset>
|
|
162
119
|
</SidebarProvider>
|
|
163
120
|
);
|
|
164
121
|
}
|
|
165
|
-
|
|
@@ -1,33 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Private
|
|
3
|
-
*
|
|
4
|
-
* Main content wrapper for PrivateLayout
|
|
2
|
+
* Private layout main column — optional mobile menu strip (`SidebarTrigger`) + scrollable area.
|
|
3
|
+
* On viewports below `md`, the desktop sidebar is off-canvas; the trigger opens the `Sheet` from ui-nextjs sidebar.
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
'use client';
|
|
8
7
|
|
|
9
8
|
import React, { ReactNode } from 'react';
|
|
10
9
|
|
|
10
|
+
import { SidebarTrigger } from '@djangocfg/ui-nextjs/components';
|
|
11
11
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
12
12
|
|
|
13
13
|
interface PrivateContentProps {
|
|
14
14
|
children: ReactNode;
|
|
15
15
|
padding?: 'none' | 'default';
|
|
16
|
+
/** When false, no mobile hamburger (e.g. layout without a sidebar). Default true. */
|
|
17
|
+
hasSidebar?: boolean;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export function PrivateContent({
|
|
19
21
|
children,
|
|
20
22
|
padding = 'default',
|
|
23
|
+
hasSidebar = true,
|
|
21
24
|
}: PrivateContentProps) {
|
|
22
25
|
return (
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
|
|
27
|
+
{hasSidebar && (
|
|
28
|
+
<div
|
|
29
|
+
className={cn(
|
|
30
|
+
'sticky top-0 z-40 flex shrink-0 items-center gap-2 border-b border-border/50 bg-background/95 py-2 pl-2 pr-3 backdrop-blur-md supports-[backdrop-filter]:bg-background/80',
|
|
31
|
+
'md:hidden',
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
34
|
+
<SidebarTrigger
|
|
35
|
+
className="shrink-0"
|
|
36
|
+
aria-label="Open menu"
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
27
39
|
)}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
<div
|
|
41
|
+
className={cn(
|
|
42
|
+
'min-h-0 flex-1 overflow-y-auto',
|
|
43
|
+
padding === 'default' && 'p-4 sm:p-6 lg:p-8',
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
31
49
|
);
|
|
32
50
|
}
|
|
33
|
-
|