@djangocfg/layouts 2.1.279 → 2.1.281
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 +1 -1
- package/package.json +18 -18
- package/src/layouts/AppLayout/AppLayout.tsx +2 -10
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +2 -1
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +1 -1
- package/src/layouts/PublicLayout/README.md +69 -1
- package/src/layouts/PublicLayout/footers/DefaultFooter/DefaultFooter.tsx +8 -7
- package/src/layouts/PublicLayout/footers/DefaultFooter/FooterBottom.tsx +4 -3
- package/src/layouts/PublicLayout/footers/DefaultFooter/FooterMenuSections.tsx +4 -3
- package/src/layouts/PublicLayout/footers/DefaultFooter/types.ts +1 -2
- package/src/layouts/PublicLayout/index.ts +7 -7
- package/src/layouts/PublicLayout/navbars/FloatingNavbar/FloatingNavbar.tsx +18 -11
- package/src/layouts/PublicLayout/navbars/FlushNavbar/FlushNavbar.tsx +18 -11
- package/src/layouts/PublicLayout/navbars/MinimalNavbar/MinimalNavbar.tsx +22 -12
- package/src/layouts/PublicLayout/primitives/LinkComponentContext.tsx +50 -0
- package/src/layouts/PublicLayout/primitives/NavActionItem.tsx +4 -3
- package/src/layouts/PublicLayout/primitives/NavActions.tsx +8 -0
- package/src/layouts/PublicLayout/primitives/NavBrand.tsx +5 -3
- package/src/layouts/PublicLayout/primitives/NavControls.tsx +114 -0
- package/src/layouts/PublicLayout/primitives/NavDesktopItems.tsx +8 -7
- package/src/layouts/PublicLayout/primitives/index.ts +9 -9
- package/src/layouts/PublicLayout/shared/MobileDrawerShell.tsx +32 -8
- package/src/layouts/PublicLayout/shared/NavbarShell.tsx +33 -0
- package/src/layouts/_components/PrivateSidebarAccount.tsx +1 -1
- package/src/layouts/types/index.ts +1 -1
- package/src/layouts/types/layout.types.ts +13 -0
- package/src/layouts/PublicLayout/primitives/ExternalPrefixesContext.tsx +0 -69
- package/src/layouts/PublicLayout/primitives/SmartNavLink.tsx +0 -81
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ Wraps `BaseApp` and picks **admin → private → public** layout by path (`matc
|
|
|
87
87
|
|
|
88
88
|
| Component | Use |
|
|
89
89
|
|---|---|
|
|
90
|
-
| **`PublicLayout`** | Marketing / docs. Slots for navbar + footer. **[See PublicLayout README](./src/layouts/PublicLayout/README.md)** for full props, navbar variants (`FloatingNavbar` / `FlushNavbar` / `MinimalNavbar`), `DefaultFooter`, `NavAction`, and hooks. |
|
|
90
|
+
| **`PublicLayout`** | Marketing / docs. Slots for navbar + footer. Supports `LinkComponentProvider` to inject an i18n-aware `Link` (e.g. `next-intl`) so every anchor picks up the locale prefix. All three navbars accept `controls` + `i18n` to show theme / locale switchers next to UserMenu (same shape as `DefaultFooter.controls`). **[See PublicLayout README](./src/layouts/PublicLayout/README.md)** for full props, navbar variants (`FloatingNavbar` / `FlushNavbar` / `MinimalNavbar`), `DefaultFooter`, `NavAction`, `NavControls`, and hooks. |
|
|
91
91
|
| **`PrivateLayout`** | App shell — sidebar + header. |
|
|
92
92
|
| **`AuthLayout`** | Sign-in flows. |
|
|
93
93
|
| **`AdminLayout`** | Admin console. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.281",
|
|
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/debuger": "^2.1.
|
|
80
|
-
"@djangocfg/i18n": "^2.1.
|
|
81
|
-
"@djangocfg/monitor": "^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.281",
|
|
78
|
+
"@djangocfg/centrifugo": "^2.1.281",
|
|
79
|
+
"@djangocfg/debuger": "^2.1.281",
|
|
80
|
+
"@djangocfg/i18n": "^2.1.281",
|
|
81
|
+
"@djangocfg/monitor": "^2.1.281",
|
|
82
|
+
"@djangocfg/ui-core": "^2.1.281",
|
|
83
|
+
"@djangocfg/ui-nextjs": "^2.1.281",
|
|
84
|
+
"@djangocfg/ui-tools": "^2.1.281",
|
|
85
85
|
"@hookform/resolvers": "^5.2.2",
|
|
86
86
|
"consola": "^3.4.2",
|
|
87
87
|
"lucide-react": "^0.545.0",
|
|
@@ -110,15 +110,15 @@
|
|
|
110
110
|
"uuid": "^11.1.0"
|
|
111
111
|
},
|
|
112
112
|
"devDependencies": {
|
|
113
|
-
"@djangocfg/api": "^2.1.
|
|
114
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
115
|
-
"@djangocfg/debuger": "^2.1.
|
|
116
|
-
"@djangocfg/i18n": "^2.1.
|
|
117
|
-
"@djangocfg/monitor": "^2.1.
|
|
118
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
119
|
-
"@djangocfg/ui-core": "^2.1.
|
|
120
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
121
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
113
|
+
"@djangocfg/api": "^2.1.281",
|
|
114
|
+
"@djangocfg/centrifugo": "^2.1.281",
|
|
115
|
+
"@djangocfg/debuger": "^2.1.281",
|
|
116
|
+
"@djangocfg/i18n": "^2.1.281",
|
|
117
|
+
"@djangocfg/monitor": "^2.1.281",
|
|
118
|
+
"@djangocfg/typescript-config": "^2.1.281",
|
|
119
|
+
"@djangocfg/ui-core": "^2.1.281",
|
|
120
|
+
"@djangocfg/ui-nextjs": "^2.1.281",
|
|
121
|
+
"@djangocfg/ui-tools": "^2.1.281",
|
|
122
122
|
"@types/node": "^24.7.2",
|
|
123
123
|
"@types/react": "^19.1.0",
|
|
124
124
|
"@types/react-dom": "^19.1.0",
|
|
@@ -48,7 +48,9 @@ import type {
|
|
|
48
48
|
SWRConfigOptions,
|
|
49
49
|
PwaInstallConfig,
|
|
50
50
|
DebugConfig,
|
|
51
|
+
I18nLayoutConfig,
|
|
51
52
|
} from '../types';
|
|
53
|
+
export type { I18nLayoutConfig } from '../types';
|
|
52
54
|
import type { AuthConfig } from '@djangocfg/api/auth';
|
|
53
55
|
import type { MonitorConfig } from '@djangocfg/monitor';
|
|
54
56
|
|
|
@@ -152,16 +154,6 @@ function determineLayoutMode(
|
|
|
152
154
|
return 'public';
|
|
153
155
|
}
|
|
154
156
|
|
|
155
|
-
/** i18n configuration for locale switching */
|
|
156
|
-
export interface I18nLayoutConfig {
|
|
157
|
-
/** Current locale */
|
|
158
|
-
locale: string;
|
|
159
|
-
/** Available locales */
|
|
160
|
-
locales: string[];
|
|
161
|
-
/** Callback when locale changes */
|
|
162
|
-
onLocaleChange: (locale: string) => void;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
157
|
/**
|
|
166
158
|
* Props passed to every layout component (`public` / `private` / `admin`).
|
|
167
159
|
* Use `publicChrome` to pass defaults for `FloatingNavbar` / `DefaultFooter` from `AppLayout`.
|
|
@@ -15,7 +15,8 @@ import { useAuth } from '@djangocfg/api/auth';
|
|
|
15
15
|
import { Preloader } from '@djangocfg/ui-core/components';
|
|
16
16
|
import { SidebarInset, SidebarProvider } from '@djangocfg/ui-nextjs/components';
|
|
17
17
|
|
|
18
|
-
import type { AppLayoutPublicChrome
|
|
18
|
+
import type { AppLayoutPublicChrome } from '../AppLayout/AppLayout';
|
|
19
|
+
import type { I18nLayoutConfig } from '../types';
|
|
19
20
|
import { UserMenuConfig } from '../types';
|
|
20
21
|
import { PrivateContent, PrivateSidebar } from './components';
|
|
21
22
|
|
|
@@ -29,7 +29,7 @@ import { cn } from '@djangocfg/ui-core/lib';
|
|
|
29
29
|
import { PrivateSidebarAccount } from '../../_components/PrivateSidebarAccount';
|
|
30
30
|
import { LucideIcon } from '../../../components';
|
|
31
31
|
|
|
32
|
-
import type { I18nLayoutConfig } from '../../
|
|
32
|
+
import type { I18nLayoutConfig } from '../../types';
|
|
33
33
|
import type { HeaderConfig, SidebarItem, SidebarConfig } from '../PrivateLayout';
|
|
34
34
|
|
|
35
35
|
/** Few items → roomier rows; many items → tighter. Same breakpoints for demo, CarAPIS, etc. */
|
|
@@ -23,6 +23,30 @@ import { PublicLayout, FloatingNavbar, DefaultFooter } from '@djangocfg/layouts'
|
|
|
23
23
|
| `contentTopSpacing` | `'auto' \| 'none'` | `'auto'` | Auto pads `<main>` based on the navbar variant. |
|
|
24
24
|
| `contentBottomSpacing` | `'auto' \| 'none' \| 'compact'` | `'auto'` | Bottom breathing room before footer. |
|
|
25
25
|
|
|
26
|
+
## `LinkComponentProvider` — inject a custom Link
|
|
27
|
+
|
|
28
|
+
All internal anchors (navbar, footer, mobile drawer) render through a single
|
|
29
|
+
injectable `Link` component. Default is plain `next/link`. Wrap the layout in
|
|
30
|
+
`LinkComponentProvider` to swap it — for example to pipe in a locale-aware
|
|
31
|
+
`Link` from `next-intl`:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { LinkComponentProvider, PublicLayout, FloatingNavbar } from '@djangocfg/layouts';
|
|
35
|
+
import { Link } from '@/i18n/navigation'; // next-intl createNavigation().Link
|
|
36
|
+
|
|
37
|
+
<LinkComponentProvider value={Link}>
|
|
38
|
+
<PublicLayout navbar={<FloatingNavbar config={…} />} footer={…}>
|
|
39
|
+
{children}
|
|
40
|
+
</PublicLayout>
|
|
41
|
+
</LinkComponentProvider>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Why: with `next-intl`'s `localePrefix: 'as-needed'`, plain `next/link` does
|
|
45
|
+
not prepend the active locale, so every navbar item drops the `/ru/` prefix
|
|
46
|
+
on click. Injecting the i18n-aware `Link` fixes that once for the whole tree.
|
|
47
|
+
|
|
48
|
+
Monolingual apps don't need the provider — the default `next/link` is fine.
|
|
49
|
+
|
|
26
50
|
## Navbar variants
|
|
27
51
|
|
|
28
52
|
Three variants. All share the same core props (below); only the chrome differs.
|
|
@@ -52,6 +76,8 @@ Three variants. All share the same core props (below); only the chrome differs.
|
|
|
52
76
|
| `transparentThreshold` | `number` | `40` | Px past which the nav becomes opaque. |
|
|
53
77
|
| `desktopMaxPrimaryItems` | `number` | auto | Hard cap for primary items before overflow. |
|
|
54
78
|
| `renderDesktopDropdown` | `(ctx) => ReactNode` | — | Replace default popover per-item. |
|
|
79
|
+
| `controls` | `{ showThemeSwitcher?; showLocaleSwitcher? }` | — | Compact theme + locale pills next to UserMenu. See below. |
|
|
80
|
+
| `i18n` | `{ locale, locales, onLocaleChange }` | — | Required for the locale switcher (same shape as `DefaultFooter.i18n`). |
|
|
55
81
|
|
|
56
82
|
### Variant-only
|
|
57
83
|
|
|
@@ -68,6 +94,48 @@ Three variants. All share the same core props (below); only the chrome differs.
|
|
|
68
94
|
- `centered` — all three groups centered in one row.
|
|
69
95
|
- `split` — brand left, actions right, **no desktop nav** (drawer only).
|
|
70
96
|
|
|
97
|
+
## Theme + locale controls (navbar)
|
|
98
|
+
|
|
99
|
+
All three navbars accept an optional `controls` block and `i18n` config that
|
|
100
|
+
mirrors `DefaultFooter`. When enabled, a compact `NavControls` pill is rendered
|
|
101
|
+
on desktop right before `UserMenu`, and an equivalent row appears in the mobile
|
|
102
|
+
drawer footer.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<FloatingNavbar
|
|
106
|
+
config={{
|
|
107
|
+
brand: <BrandLogo />,
|
|
108
|
+
navigation,
|
|
109
|
+
userMenu: { authPath: '/auth' },
|
|
110
|
+
controls: { showThemeSwitcher: true, showLocaleSwitcher: true },
|
|
111
|
+
i18n: { locale, locales, onLocaleChange: changeLocale },
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
| Control | Required | Notes |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `controls.showThemeSwitcher` | — | Light / system / dark pill (uses `useThemeContext`). |
|
|
119
|
+
| `controls.showLocaleSwitcher` | `i18n` | Globe dropdown. Silently hidden when `i18n` is omitted. |
|
|
120
|
+
|
|
121
|
+
Same shape works for `FlushNavbar` and `MinimalNavbar`. If you build a custom
|
|
122
|
+
navbar with `NavbarShell`, the controls node is pre-built and delivered to
|
|
123
|
+
`renderActions(ctx)` as `ctx.controls`.
|
|
124
|
+
|
|
125
|
+
Need to render the pills somewhere custom (e.g. your own navbar)? Import
|
|
126
|
+
`NavControls` directly:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { NavControls } from '@djangocfg/layouts';
|
|
130
|
+
|
|
131
|
+
<NavControls
|
|
132
|
+
showThemeSwitcher
|
|
133
|
+
showLocaleSwitcher
|
|
134
|
+
i18n={{ locale, locales, onLocaleChange }}
|
|
135
|
+
size="compact" // 'compact' (navbar) | 'default' (footer)
|
|
136
|
+
/>
|
|
137
|
+
```
|
|
138
|
+
|
|
71
139
|
## `NavAction`
|
|
72
140
|
|
|
73
141
|
Typed pill used by every navbar's `actions`.
|
|
@@ -131,7 +199,7 @@ Three variants: `full` (default) with brand column + menus + controls; `compact`
|
|
|
131
199
|
|
|
132
200
|
## Primitives
|
|
133
201
|
|
|
134
|
-
`NavBrand`, `NavActions`, `NavActionItem`, `NavDesktopItems`, `ThemeBrandMark`, `ThemeBrandMarkImg`, `PublicLayoutProvider`, `usePublicLayout`.
|
|
202
|
+
`NavBrand`, `NavActions`, `NavActionItem`, `NavControls`, `NavDesktopItems`, `ThemeBrandMark`, `ThemeBrandMarkImg`, `LinkComponentProvider`, `useLinkComponent`, `PublicLayoutProvider`, `usePublicLayout`.
|
|
135
203
|
|
|
136
204
|
## Context
|
|
137
205
|
|
|
@@ -13,7 +13,7 @@ import { Button } from '@djangocfg/ui-core/components';
|
|
|
13
13
|
import { useThemeContext } from '@djangocfg/ui-nextjs/theme';
|
|
14
14
|
|
|
15
15
|
import { LocaleSwitcher } from '../../../_components/LocaleSwitcher';
|
|
16
|
-
import {
|
|
16
|
+
import { useLinkComponent } from '../../primitives/LinkComponentContext';
|
|
17
17
|
import { FooterMenuSections } from './FooterMenuSections';
|
|
18
18
|
import { FooterProjectInfo } from './FooterProjectInfo';
|
|
19
19
|
|
|
@@ -69,6 +69,7 @@ function ThemeModeControl() {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export function DefaultFooter({ config }: DefaultFooterProps) {
|
|
72
|
+
const Link = useLinkComponent();
|
|
72
73
|
const variant = config.variant ?? 'full';
|
|
73
74
|
const shellClass = config.shell?.className;
|
|
74
75
|
const brandSlot = config.brand?.slot;
|
|
@@ -132,13 +133,13 @@ export function DefaultFooter({ config }: DefaultFooterProps) {
|
|
|
132
133
|
{link.label}
|
|
133
134
|
</a>
|
|
134
135
|
) : (
|
|
135
|
-
<
|
|
136
|
+
<Link
|
|
136
137
|
key={link.path}
|
|
137
138
|
href={link.path}
|
|
138
139
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
139
140
|
>
|
|
140
141
|
{link.label}
|
|
141
|
-
</
|
|
142
|
+
</Link>
|
|
142
143
|
)
|
|
143
144
|
)}
|
|
144
145
|
</div>
|
|
@@ -195,13 +196,13 @@ export function DefaultFooter({ config }: DefaultFooterProps) {
|
|
|
195
196
|
{link.label}
|
|
196
197
|
</a>
|
|
197
198
|
) : (
|
|
198
|
-
<
|
|
199
|
+
<Link
|
|
199
200
|
key={link.path}
|
|
200
201
|
href={link.path}
|
|
201
202
|
className="text-xs text-muted-foreground hover:text-primary transition-colors"
|
|
202
203
|
>
|
|
203
204
|
{link.label}
|
|
204
|
-
</
|
|
205
|
+
</Link>
|
|
205
206
|
)
|
|
206
207
|
)}
|
|
207
208
|
</div>
|
|
@@ -302,9 +303,9 @@ export function DefaultFooter({ config }: DefaultFooterProps) {
|
|
|
302
303
|
{link.label}
|
|
303
304
|
</a>
|
|
304
305
|
) : (
|
|
305
|
-
<
|
|
306
|
+
<Link key={link.path} href={link.path} className="hover:text-foreground transition-colors whitespace-nowrap">
|
|
306
307
|
{link.label}
|
|
307
|
-
</
|
|
308
|
+
</Link>
|
|
308
309
|
)
|
|
309
310
|
)}
|
|
310
311
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useLinkComponent } from '../../primitives/LinkComponentContext';
|
|
6
6
|
import { DjangoCFGLogo } from './DjangoCFGLogo';
|
|
7
7
|
|
|
8
8
|
import type { FooterLink } from './types';
|
|
@@ -23,6 +23,7 @@ export function FooterBottom({
|
|
|
23
23
|
links = [],
|
|
24
24
|
variant = 'desktop',
|
|
25
25
|
}: FooterBottomProps) {
|
|
26
|
+
const Link = useLinkComponent();
|
|
26
27
|
const isMobile = variant === 'mobile';
|
|
27
28
|
|
|
28
29
|
if (isMobile) {
|
|
@@ -95,13 +96,13 @@ export function FooterBottom({
|
|
|
95
96
|
{link.label}
|
|
96
97
|
</a>
|
|
97
98
|
) : (
|
|
98
|
-
<
|
|
99
|
+
<Link
|
|
99
100
|
key={link.path}
|
|
100
101
|
href={link.path}
|
|
101
102
|
className="text-xs text-muted-foreground hover:text-primary transition-colors"
|
|
102
103
|
>
|
|
103
104
|
{link.label}
|
|
104
|
-
</
|
|
105
|
+
</Link>
|
|
105
106
|
)
|
|
106
107
|
)}
|
|
107
108
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useLinkComponent } from '../../primitives/LinkComponentContext';
|
|
6
6
|
|
|
7
7
|
import type { FooterMenuSection } from './types';
|
|
8
8
|
|
|
@@ -17,6 +17,7 @@ export function FooterMenuSections({
|
|
|
17
17
|
minColumnWidth = 180,
|
|
18
18
|
maxColumns = 5,
|
|
19
19
|
}: FooterMenuSectionsProps) {
|
|
20
|
+
const Link = useLinkComponent();
|
|
20
21
|
if (menuSections.length === 0) return null;
|
|
21
22
|
|
|
22
23
|
const effectiveColumns = Math.max(1, Math.min(maxColumns, menuSections.length));
|
|
@@ -34,12 +35,12 @@ export function FooterMenuSections({
|
|
|
34
35
|
<ul className="space-y-2">
|
|
35
36
|
{section.items.map((item) => (
|
|
36
37
|
<li key={item.path}>
|
|
37
|
-
<
|
|
38
|
+
<Link
|
|
38
39
|
href={item.path}
|
|
39
40
|
className="text-sm text-foreground/90 hover:text-foreground transition-colors"
|
|
40
41
|
>
|
|
41
42
|
{item.label}
|
|
42
|
-
</
|
|
43
|
+
</Link>
|
|
43
44
|
</li>
|
|
44
45
|
))}
|
|
45
46
|
</ul>
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
import type { LucideIcon } from 'lucide-react';
|
|
6
6
|
import type { ReactNode } from 'react';
|
|
7
7
|
|
|
8
|
-
import type { I18nLayoutConfig } from '../../../
|
|
9
|
-
import type { FooterLink, FooterMenuSection, FooterSocialLinks } from '../../../types';
|
|
8
|
+
import type { FooterLink, FooterMenuSection, FooterSocialLinks, I18nLayoutConfig } from '../../../types';
|
|
10
9
|
|
|
11
10
|
export type { FooterLink, FooterMenuSection, FooterSocialLinks };
|
|
12
11
|
|
|
@@ -33,21 +33,21 @@ export {
|
|
|
33
33
|
NavBrand,
|
|
34
34
|
NavActions,
|
|
35
35
|
NavActionItem,
|
|
36
|
+
NavControls,
|
|
36
37
|
NavDesktopItems,
|
|
37
38
|
ThemeBrandMark,
|
|
38
39
|
ThemeBrandMarkImg,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
isExternalPrefixHref,
|
|
42
|
-
SmartNavLink,
|
|
40
|
+
LinkComponentProvider,
|
|
41
|
+
useLinkComponent,
|
|
43
42
|
} from './primitives';
|
|
44
43
|
export type {
|
|
45
44
|
NavAction,
|
|
45
|
+
NavControlsProps,
|
|
46
46
|
ThemeBrandMarkProps,
|
|
47
47
|
ThemeBrandMarkImgProps,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
LinkComponent,
|
|
49
|
+
LinkComponentProps,
|
|
50
|
+
LinkComponentProviderProps,
|
|
51
51
|
} from './primitives';
|
|
52
52
|
|
|
53
53
|
// Navbar variants
|
|
@@ -11,7 +11,6 @@ import React from 'react';
|
|
|
11
11
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
12
12
|
|
|
13
13
|
import { publicFloatingChromeClassName } from '../../publicShellShadow';
|
|
14
|
-
import { ExternalPrefixesProvider } from '../../primitives/ExternalPrefixesContext';
|
|
15
14
|
import type { NavAction } from '../../primitives/NavActionItem';
|
|
16
15
|
import { NavbarShell } from '../../shared';
|
|
17
16
|
import type {
|
|
@@ -21,7 +20,7 @@ import type {
|
|
|
21
20
|
PublicNavbarShellConfig,
|
|
22
21
|
PublicNavLayout,
|
|
23
22
|
} from '../../navbarTypes';
|
|
24
|
-
import type { NavigationItem, UserMenuConfig } from '../../../types';
|
|
23
|
+
import type { I18nLayoutConfig, NavigationItem, UserMenuConfig } from '../../../types';
|
|
25
24
|
|
|
26
25
|
import { FloatingMobileDrawer } from './FloatingMobileDrawer';
|
|
27
26
|
|
|
@@ -53,13 +52,18 @@ export interface FloatingNavbarConfig {
|
|
|
53
52
|
/** Arbitrary ReactNode after the mobile toggle. */
|
|
54
53
|
actionsTrailingSlot?: React.ReactNode;
|
|
55
54
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* cannot return an RSC payload, so `next/link` client navigation fails.
|
|
60
|
-
* @default []
|
|
55
|
+
* Optional theme + locale controls rendered next to UserMenu on desktop
|
|
56
|
+
* and as a footer row in the mobile drawer. Locale switcher requires
|
|
57
|
+
* `i18n`. Mirrors `DefaultFooter.controls`.
|
|
61
58
|
*/
|
|
62
|
-
|
|
59
|
+
controls?: {
|
|
60
|
+
/** Light / system / dark pill. @default false */
|
|
61
|
+
showThemeSwitcher?: boolean;
|
|
62
|
+
/** Locale dropdown. Requires `i18n`. @default false */
|
|
63
|
+
showLocaleSwitcher?: boolean;
|
|
64
|
+
};
|
|
65
|
+
/** i18n config (current locale + locales + onLocaleChange). */
|
|
66
|
+
i18n?: I18nLayoutConfig;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
export interface FloatingNavbarProps {
|
|
@@ -71,7 +75,6 @@ export function FloatingNavbar({ config }: FloatingNavbarProps) {
|
|
|
71
75
|
const rounding = config.shell?.rounding;
|
|
72
76
|
const containerClassName = config.shell?.className;
|
|
73
77
|
const position = config.navbarPosition ?? 'sticky';
|
|
74
|
-
const externalPrefixes = config.externalPrefixes;
|
|
75
78
|
|
|
76
79
|
const outerClassName = cn(
|
|
77
80
|
position === 'fixed' ? 'fixed' : position === 'static' ? 'static' : 'sticky',
|
|
@@ -87,7 +90,7 @@ export function FloatingNavbar({ config }: FloatingNavbarProps) {
|
|
|
87
90
|
);
|
|
88
91
|
|
|
89
92
|
return (
|
|
90
|
-
|
|
93
|
+
<>
|
|
91
94
|
<NavbarShell
|
|
92
95
|
variant="floating"
|
|
93
96
|
position={position}
|
|
@@ -105,6 +108,8 @@ export function FloatingNavbar({ config }: FloatingNavbarProps) {
|
|
|
105
108
|
actions={config.actions}
|
|
106
109
|
actionsLeadingSlot={config.actionsLeadingSlot}
|
|
107
110
|
actionsTrailingSlot={config.actionsTrailingSlot}
|
|
111
|
+
controls={config.controls}
|
|
112
|
+
i18n={config.i18n}
|
|
108
113
|
outerClassName={outerClassName}
|
|
109
114
|
shapeClassName={shapeClassName}
|
|
110
115
|
shapeForState={({ scrolled, transparent }) =>
|
|
@@ -121,7 +126,9 @@ export function FloatingNavbar({ config }: FloatingNavbarProps) {
|
|
|
121
126
|
userMenu={config.userMenu}
|
|
122
127
|
containerClassName={containerClassName}
|
|
123
128
|
rounding={rounding}
|
|
129
|
+
controls={config.controls}
|
|
130
|
+
i18n={config.i18n}
|
|
124
131
|
/>
|
|
125
|
-
|
|
132
|
+
</>
|
|
126
133
|
);
|
|
127
134
|
}
|
|
@@ -10,7 +10,6 @@ import React from 'react';
|
|
|
10
10
|
|
|
11
11
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
12
12
|
|
|
13
|
-
import { ExternalPrefixesProvider } from '../../primitives/ExternalPrefixesContext';
|
|
14
13
|
import type { NavAction } from '../../primitives/NavActionItem';
|
|
15
14
|
import { NavbarShell } from '../../shared';
|
|
16
15
|
import type {
|
|
@@ -20,7 +19,7 @@ import type {
|
|
|
20
19
|
PublicNavbarShellConfig,
|
|
21
20
|
PublicNavLayout,
|
|
22
21
|
} from '../../navbarTypes';
|
|
23
|
-
import type { NavigationItem, UserMenuConfig } from '../../../types';
|
|
22
|
+
import type { I18nLayoutConfig, NavigationItem, UserMenuConfig } from '../../../types';
|
|
24
23
|
|
|
25
24
|
import { FlushMobileDrawer } from './FlushMobileDrawer';
|
|
26
25
|
|
|
@@ -52,13 +51,18 @@ export interface FlushNavbarConfig {
|
|
|
52
51
|
/** Arbitrary ReactNode after the mobile toggle. */
|
|
53
52
|
actionsTrailingSlot?: React.ReactNode;
|
|
54
53
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* cannot return an RSC payload, so `next/link` client navigation fails.
|
|
59
|
-
* @default []
|
|
54
|
+
* Optional theme + locale controls rendered next to UserMenu on desktop
|
|
55
|
+
* and as a footer row in the mobile drawer. Locale switcher requires
|
|
56
|
+
* `i18n`. Mirrors `DefaultFooter.controls`.
|
|
60
57
|
*/
|
|
61
|
-
|
|
58
|
+
controls?: {
|
|
59
|
+
/** Light / system / dark pill. @default false */
|
|
60
|
+
showThemeSwitcher?: boolean;
|
|
61
|
+
/** Locale dropdown. Requires `i18n`. @default false */
|
|
62
|
+
showLocaleSwitcher?: boolean;
|
|
63
|
+
};
|
|
64
|
+
/** i18n config (current locale + locales + onLocaleChange). */
|
|
65
|
+
i18n?: I18nLayoutConfig;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
export interface FlushNavbarProps {
|
|
@@ -69,7 +73,6 @@ export function FlushNavbar({ config }: FlushNavbarProps) {
|
|
|
69
73
|
const navigation = config.navigation ?? [];
|
|
70
74
|
const containerClassName = config.shell?.className;
|
|
71
75
|
const position = config.navbarPosition ?? 'sticky';
|
|
72
|
-
const externalPrefixes = config.externalPrefixes;
|
|
73
76
|
|
|
74
77
|
const outerClassName = cn(
|
|
75
78
|
position === 'fixed' ? 'fixed' : position === 'static' ? 'static' : 'sticky',
|
|
@@ -83,7 +86,7 @@ export function FlushNavbar({ config }: FlushNavbarProps) {
|
|
|
83
86
|
);
|
|
84
87
|
|
|
85
88
|
return (
|
|
86
|
-
|
|
89
|
+
<>
|
|
87
90
|
<NavbarShell
|
|
88
91
|
variant="flush"
|
|
89
92
|
position={position}
|
|
@@ -101,6 +104,8 @@ export function FlushNavbar({ config }: FlushNavbarProps) {
|
|
|
101
104
|
actions={config.actions}
|
|
102
105
|
actionsLeadingSlot={config.actionsLeadingSlot}
|
|
103
106
|
actionsTrailingSlot={config.actionsTrailingSlot}
|
|
107
|
+
controls={config.controls}
|
|
108
|
+
i18n={config.i18n}
|
|
104
109
|
outerClassName={outerClassName}
|
|
105
110
|
shapeClassName={shapeClassName}
|
|
106
111
|
shapeForState={({ scrolled, transparent }) =>
|
|
@@ -116,7 +121,9 @@ export function FlushNavbar({ config }: FlushNavbarProps) {
|
|
|
116
121
|
navigation={navigation}
|
|
117
122
|
userMenu={config.userMenu}
|
|
118
123
|
containerClassName={containerClassName}
|
|
124
|
+
controls={config.controls}
|
|
125
|
+
i18n={config.i18n}
|
|
119
126
|
/>
|
|
120
|
-
|
|
127
|
+
</>
|
|
121
128
|
);
|
|
122
129
|
}
|
|
@@ -17,7 +17,6 @@ import { Button } from '@djangocfg/ui-core/components';
|
|
|
17
17
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
18
18
|
|
|
19
19
|
import { UserMenu } from '../../../_components/UserMenu';
|
|
20
|
-
import { ExternalPrefixesProvider } from '../../primitives/ExternalPrefixesContext';
|
|
21
20
|
import { NavActionItem, type NavAction } from '../../primitives/NavActionItem';
|
|
22
21
|
import { NavbarShell, type NavbarActionsContext } from '../../shared';
|
|
23
22
|
import type {
|
|
@@ -26,7 +25,7 @@ import type {
|
|
|
26
25
|
PublicNavbarPosition,
|
|
27
26
|
PublicNavLayout,
|
|
28
27
|
} from '../../navbarTypes';
|
|
29
|
-
import type { NavigationItem, UserMenuConfig } from '../../../types';
|
|
28
|
+
import type { I18nLayoutConfig, NavigationItem, UserMenuConfig } from '../../../types';
|
|
30
29
|
|
|
31
30
|
import { MinimalMobileDrawer } from './MinimalMobileDrawer';
|
|
32
31
|
|
|
@@ -68,15 +67,19 @@ export interface MinimalNavbarConfig {
|
|
|
68
67
|
* @default 'mx-auto max-w-[1400px] px-4 sm:px-6 lg:px-10'
|
|
69
68
|
*/
|
|
70
69
|
containerClassName?: string;
|
|
71
|
-
|
|
72
70
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* cannot return an RSC payload, so `next/link` client navigation fails.
|
|
77
|
-
* @default []
|
|
71
|
+
* Optional theme + locale controls rendered next to UserMenu on desktop
|
|
72
|
+
* and as a footer row in the mobile drawer. Locale switcher requires
|
|
73
|
+
* `i18n`. Mirrors `DefaultFooter.controls`.
|
|
78
74
|
*/
|
|
79
|
-
|
|
75
|
+
controls?: {
|
|
76
|
+
/** Light / system / dark pill. @default false */
|
|
77
|
+
showThemeSwitcher?: boolean;
|
|
78
|
+
/** Locale dropdown. Requires `i18n`. @default false */
|
|
79
|
+
showLocaleSwitcher?: boolean;
|
|
80
|
+
};
|
|
81
|
+
/** i18n config (current locale + locales + onLocaleChange). */
|
|
82
|
+
i18n?: I18nLayoutConfig;
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
export interface MinimalNavbarProps {
|
|
@@ -100,6 +103,10 @@ function MinimalActions({
|
|
|
100
103
|
</div>
|
|
101
104
|
)}
|
|
102
105
|
|
|
106
|
+
{ctx.controls && (
|
|
107
|
+
<div className="hidden lg:flex shrink-0 items-center">{ctx.controls}</div>
|
|
108
|
+
)}
|
|
109
|
+
|
|
103
110
|
<div className="hidden lg:flex">
|
|
104
111
|
<UserMenu
|
|
105
112
|
variant="desktop"
|
|
@@ -131,7 +138,6 @@ export function MinimalNavbar({ config }: MinimalNavbarProps) {
|
|
|
131
138
|
const position = config.navbarPosition ?? 'sticky';
|
|
132
139
|
const transparent = config.transparent ?? true;
|
|
133
140
|
const containerClassName = config.containerClassName ?? 'mx-auto max-w-[1400px] px-4 sm:px-6 lg:px-10';
|
|
134
|
-
const externalPrefixes = config.externalPrefixes;
|
|
135
141
|
|
|
136
142
|
const outerClassName = cn(
|
|
137
143
|
position === 'fixed' ? 'fixed' : position === 'static' ? 'static' : 'sticky',
|
|
@@ -142,7 +148,7 @@ export function MinimalNavbar({ config }: MinimalNavbarProps) {
|
|
|
142
148
|
const shapeClassName = 'w-full rounded-none border-0 shadow-none';
|
|
143
149
|
|
|
144
150
|
return (
|
|
145
|
-
|
|
151
|
+
<>
|
|
146
152
|
<NavbarShell
|
|
147
153
|
variant="minimal"
|
|
148
154
|
position={position}
|
|
@@ -157,6 +163,8 @@ export function MinimalNavbar({ config }: MinimalNavbarProps) {
|
|
|
157
163
|
hideNavOnScroll={config.hideNavOnScroll}
|
|
158
164
|
transparent={transparent}
|
|
159
165
|
transparentThreshold={config.transparentThreshold}
|
|
166
|
+
controls={config.controls}
|
|
167
|
+
i18n={config.i18n}
|
|
160
168
|
outerClassName={outerClassName}
|
|
161
169
|
shapeClassName={shapeClassName}
|
|
162
170
|
innerPadding={containerClassName}
|
|
@@ -174,7 +182,9 @@ export function MinimalNavbar({ config }: MinimalNavbarProps) {
|
|
|
174
182
|
navigation={navigation}
|
|
175
183
|
userMenu={config.userMenu}
|
|
176
184
|
containerClassName={containerClassName}
|
|
185
|
+
controls={config.controls}
|
|
186
|
+
i18n={config.i18n}
|
|
177
187
|
/>
|
|
178
|
-
|
|
188
|
+
</>
|
|
179
189
|
);
|
|
180
190
|
}
|