@djangocfg/layouts 2.1.249 → 2.1.251
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/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/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.251",
|
|
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.251",
|
|
78
|
+
"@djangocfg/centrifugo": "^2.1.251",
|
|
79
|
+
"@djangocfg/i18n": "^2.1.251",
|
|
80
|
+
"@djangocfg/monitor": "^2.1.251",
|
|
81
|
+
"@djangocfg/debuger": "^2.1.251",
|
|
82
|
+
"@djangocfg/ui-core": "^2.1.251",
|
|
83
|
+
"@djangocfg/ui-nextjs": "^2.1.251",
|
|
84
|
+
"@djangocfg/ui-tools": "^2.1.251",
|
|
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.251",
|
|
113
|
+
"@djangocfg/i18n": "^2.1.251",
|
|
114
|
+
"@djangocfg/centrifugo": "^2.1.251",
|
|
115
|
+
"@djangocfg/monitor": "^2.1.251",
|
|
116
|
+
"@djangocfg/debuger": "^2.1.251",
|
|
117
|
+
"@djangocfg/typescript-config": "^2.1.251",
|
|
118
|
+
"@djangocfg/ui-core": "^2.1.251",
|
|
119
|
+
"@djangocfg/ui-nextjs": "^2.1.251",
|
|
120
|
+
"@djangocfg/ui-tools": "^2.1.251",
|
|
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';
|
|
@@ -11,15 +11,19 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```tsx
|
|
14
|
-
* import { PublicLayout } from '@djangocfg/layouts';
|
|
14
|
+
* import { PublicLayout, PublicNavbar } from '@djangocfg/layouts';
|
|
15
15
|
*
|
|
16
16
|
* <PublicLayout
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* navbar={
|
|
18
|
+
* <PublicNavbar
|
|
19
|
+
* logo="/logo.svg"
|
|
20
|
+
* siteName="My App"
|
|
21
|
+
* navigation={[
|
|
22
|
+
* { label: 'Home', href: '/' },
|
|
23
|
+
* { label: 'Docs', href: '/docs' }
|
|
24
|
+
* ]}
|
|
25
|
+
* />
|
|
26
|
+
* }
|
|
23
27
|
* >
|
|
24
28
|
* {children}
|
|
25
29
|
* </PublicLayout>
|
|
@@ -31,36 +35,25 @@
|
|
|
31
35
|
import { usePathname } from 'next/navigation';
|
|
32
36
|
import { ReactNode, useEffect, useMemo, useState } from 'react';
|
|
33
37
|
|
|
34
|
-
import { PublicMobileDrawer, PublicNavigation } from './components';
|
|
35
38
|
import { PublicLayoutProvider } from './context';
|
|
36
39
|
|
|
37
|
-
import type { NavigationItem, UserMenuConfig } from '../types';
|
|
38
|
-
import type { I18nLayoutConfig } from '../AppLayout/AppLayout';
|
|
39
|
-
|
|
40
40
|
export interface PublicLayoutProps {
|
|
41
41
|
children: ReactNode;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
i18n?: I18nLayoutConfig;
|
|
52
|
-
/** Custom className for navbar container (e.g. "max-w-7xl mx-auto") */
|
|
53
|
-
navbarContainerClassName?: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Slots (advanced).
|
|
45
|
+
*
|
|
46
|
+
* Slots-only API: this layout does not render any default navbar/drawer/footer.
|
|
47
|
+
* Pass `navbar`, `mobileDrawer`, `footer` explicitly (or keep them empty).
|
|
48
|
+
*/
|
|
49
|
+
navbar?: ReactNode;
|
|
50
|
+
footer?: ReactNode;
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
export function PublicLayout({
|
|
57
54
|
children,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
navigation = [],
|
|
61
|
-
userMenu,
|
|
62
|
-
i18n,
|
|
63
|
-
navbarContainerClassName,
|
|
55
|
+
navbar,
|
|
56
|
+
footer,
|
|
64
57
|
}: PublicLayoutProps) {
|
|
65
58
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
66
59
|
const pathname = usePathname();
|
|
@@ -71,30 +64,22 @@ export function PublicLayout({
|
|
|
71
64
|
}, [pathname]);
|
|
72
65
|
|
|
73
66
|
const contextValue = useMemo(() => ({
|
|
74
|
-
logo,
|
|
75
|
-
siteName,
|
|
76
|
-
navigation,
|
|
77
|
-
userMenu,
|
|
78
|
-
i18n,
|
|
79
|
-
containerClassName: navbarContainerClassName,
|
|
80
67
|
mobileMenuOpen,
|
|
81
68
|
toggleMobileMenu: () => setMobileMenuOpen((prev) => !prev),
|
|
82
69
|
closeMobileMenu: () => setMobileMenuOpen(false),
|
|
83
|
-
}), [
|
|
70
|
+
}), [
|
|
71
|
+
mobileMenuOpen,
|
|
72
|
+
]);
|
|
84
73
|
|
|
85
74
|
return (
|
|
86
75
|
<PublicLayoutProvider value={contextValue}>
|
|
87
76
|
<div className="min-h-screen flex flex-col">
|
|
88
|
-
{
|
|
89
|
-
<PublicNavigation />
|
|
90
|
-
|
|
91
|
-
{/* Mobile Drawer */}
|
|
92
|
-
<PublicMobileDrawer />
|
|
77
|
+
{navbar ?? null}
|
|
93
78
|
|
|
94
79
|
{/* Main Content */}
|
|
95
80
|
<main className="flex-1">{children}</main>
|
|
96
81
|
|
|
97
|
-
{
|
|
82
|
+
{footer ?? null}
|
|
98
83
|
</div>
|
|
99
84
|
</PublicLayoutProvider>
|
|
100
85
|
);
|
|
@@ -11,13 +11,24 @@ import type { FooterMenuSection } from './types';
|
|
|
11
11
|
|
|
12
12
|
export interface FooterMenuSectionsProps {
|
|
13
13
|
menuSections: FooterMenuSection[];
|
|
14
|
+
minColumnWidth?: number;
|
|
15
|
+
maxColumns?: number;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
export function FooterMenuSections({
|
|
18
|
+
export function FooterMenuSections({
|
|
19
|
+
menuSections,
|
|
20
|
+
minColumnWidth = 180,
|
|
21
|
+
maxColumns = 5,
|
|
22
|
+
}: FooterMenuSectionsProps) {
|
|
17
23
|
if (menuSections.length === 0) return null;
|
|
18
24
|
|
|
25
|
+
const effectiveColumns = Math.max(1, Math.min(maxColumns, menuSections.length));
|
|
26
|
+
|
|
19
27
|
return (
|
|
20
|
-
<div
|
|
28
|
+
<div
|
|
29
|
+
className="w-full grid gap-8 lg:gap-x-12"
|
|
30
|
+
style={{ gridTemplateColumns: `repeat(${effectiveColumns}, minmax(${minColumnWidth}px, 1fr))` }}
|
|
31
|
+
>
|
|
21
32
|
{menuSections.map((section) => (
|
|
22
33
|
<div key={section.title} className="min-w-0">
|
|
23
34
|
<h3 className="text-xs font-medium text-muted-foreground mb-3">
|
|
@@ -13,7 +13,7 @@ import type { LucideIcon } from 'lucide-react';
|
|
|
13
13
|
import type { FooterSocialLinks } from './types';
|
|
14
14
|
|
|
15
15
|
export interface FooterProjectInfoProps {
|
|
16
|
-
siteName
|
|
16
|
+
siteName?: string;
|
|
17
17
|
description?: string;
|
|
18
18
|
logo?: string;
|
|
19
19
|
badge?: {
|
|
@@ -41,16 +41,18 @@ export function FooterProjectInfo({
|
|
|
41
41
|
<div className={isMobile ? 'w-6 h-6 flex items-center justify-center' : 'w-8 h-8 flex items-center justify-center'}>
|
|
42
42
|
<img
|
|
43
43
|
src={logo}
|
|
44
|
-
alt={`${siteName} Logo`}
|
|
44
|
+
alt={`${siteName || 'Project'} Logo`}
|
|
45
45
|
className="w-full h-full object-contain"
|
|
46
46
|
/>
|
|
47
47
|
</div>
|
|
48
48
|
) : (
|
|
49
49
|
<DjangoCFGLogo size={isMobile ? 24 : 32} className="text-foreground" />
|
|
50
50
|
)}
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
51
|
+
{siteName && (
|
|
52
|
+
<span className={isMobile ? 'text-lg font-bold text-foreground' : 'text-lg font-semibold text-foreground'}>
|
|
53
|
+
{siteName}
|
|
54
|
+
</span>
|
|
55
|
+
)}
|
|
54
56
|
</div>
|
|
55
57
|
|
|
56
58
|
{description && (
|