@djangocfg/layouts 2.1.307 → 2.1.309
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 +7 -2
- package/package.json +20 -18
- package/src/layouts/AppLayout/BaseApp.tsx +8 -1
- package/src/layouts/AppLayout/NextIntlLinkBridge.tsx +67 -0
- package/src/layouts/PublicLayout/README.md +18 -27
- package/src/layouts/_components/locale-switcher/LocaleSwitcherDropdown.tsx +28 -12
- package/src/layouts/_components/locale-switcher/LocaleSwitcherTrigger.tsx +29 -0
- package/src/layouts/types/layout.types.ts +17 -0
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. All anchors render through `<Link>` from `@djangocfg/ui-core/components` — wrap with `LinkProvider` higher in the tree to inject a locale-aware Link (e.g. `next-intl`). All three navbars accept `controls`
|
|
90
|
+
| **`PublicLayout`** | Marketing / docs. Slots for navbar + footer. All anchors render through `<Link>` from `@djangocfg/ui-core/components` — wrap with `LinkProvider` higher in the tree to inject a locale-aware Link (e.g. `next-intl`). All three navbars accept a `controls` block to show theme / locale switchers next to `UserMenu`; locale data flows from `LayoutI18nProvider` (mounted by `AppLayout` / `BaseApp` when you pass `i18n`). The locale switcher renders SVG country flags and matches the `UserMenu` avatar size at `size="icon"`. **[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. Defaults to the `boxed` visual (inset rounded card on a sidebar-coloured canvas); pass `visual={{ variant: 'full-bleed' }}` for the legacy edge-to-edge layout. |
|
|
92
92
|
| **`AuthLayout`** | Sign-in flows. |
|
|
93
93
|
| **`AdminLayout`** | Admin console. |
|
|
@@ -139,11 +139,16 @@ Wraps `BaseApp` and picks **admin → private → public** layout by path (`matc
|
|
|
139
139
|
|
|
140
140
|
```tsx
|
|
141
141
|
import { useLocaleSwitcher } from '@djangocfg/nextjs/i18n/client';
|
|
142
|
+
import { routing } from '@djangocfg/nextjs/i18n/routing';
|
|
142
143
|
|
|
143
144
|
const { locale, locales, changeLocale } = useLocaleSwitcher();
|
|
144
|
-
<AppLayout i18n={{ locale, locales, onLocaleChange: changeLocale }}>
|
|
145
|
+
<AppLayout i18n={{ locale, locales, onLocaleChange: changeLocale, routing }}>
|
|
146
|
+
{children}
|
|
147
|
+
</AppLayout>
|
|
145
148
|
```
|
|
146
149
|
|
|
150
|
+
`i18n.routing` is the object returned by `defineRouting()` (`next-intl/routing`). When passed, `BaseApp` builds a locale-aware `Link` adapter from it and mounts a `LinkProvider`, so every `<Link>` rendered inside layouts (navbar, footer, drawer, …) keeps the active locale prefix on click. Drop it for default-locale-only apps — raw `next/link` works fine.
|
|
151
|
+
|
|
147
152
|
---
|
|
148
153
|
|
|
149
154
|
## Monitor & debug
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.309",
|
|
4
4
|
"description": "Simple, straightforward layout components for Next.js - import and use with props",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
@@ -74,19 +74,20 @@
|
|
|
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.309",
|
|
78
|
+
"@djangocfg/centrifugo": "^2.1.309",
|
|
79
|
+
"@djangocfg/debuger": "^2.1.309",
|
|
80
|
+
"@djangocfg/i18n": "^2.1.309",
|
|
81
|
+
"@djangocfg/monitor": "^2.1.309",
|
|
82
|
+
"@djangocfg/ui-core": "^2.1.309",
|
|
83
|
+
"@djangocfg/ui-nextjs": "^2.1.309",
|
|
84
|
+
"@djangocfg/ui-tools": "^2.1.309",
|
|
85
85
|
"@hookform/resolvers": "^5.2.2",
|
|
86
86
|
"consola": "^3.4.2",
|
|
87
87
|
"lucide-react": "^0.545.0",
|
|
88
88
|
"moment": "^2.30.1",
|
|
89
89
|
"next": "^16.2.2",
|
|
90
|
+
"next-intl": "^4.9.1",
|
|
90
91
|
"p-retry": "^7.0.0",
|
|
91
92
|
"react": "^19.1.0",
|
|
92
93
|
"react-dom": "^19.1.0",
|
|
@@ -110,19 +111,20 @@
|
|
|
110
111
|
"uuid": "^11.1.0"
|
|
111
112
|
},
|
|
112
113
|
"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.
|
|
114
|
+
"@djangocfg/api": "^2.1.309",
|
|
115
|
+
"@djangocfg/centrifugo": "^2.1.309",
|
|
116
|
+
"@djangocfg/debuger": "^2.1.309",
|
|
117
|
+
"@djangocfg/i18n": "^2.1.309",
|
|
118
|
+
"@djangocfg/monitor": "^2.1.309",
|
|
119
|
+
"@djangocfg/typescript-config": "^2.1.309",
|
|
120
|
+
"@djangocfg/ui-core": "^2.1.309",
|
|
121
|
+
"@djangocfg/ui-nextjs": "^2.1.309",
|
|
122
|
+
"@djangocfg/ui-tools": "^2.1.309",
|
|
122
123
|
"@types/node": "^24.7.2",
|
|
123
124
|
"@types/react": "^19.1.0",
|
|
124
125
|
"@types/react-dom": "^19.1.0",
|
|
125
126
|
"eslint": "^9.37.0",
|
|
127
|
+
"next-intl": "^4.9.1",
|
|
126
128
|
"typescript": "^5.9.3"
|
|
127
129
|
},
|
|
128
130
|
"publishConfig": {
|
|
@@ -49,6 +49,7 @@ import { CentrifugoProvider } from '@djangocfg/centrifugo';
|
|
|
49
49
|
import { Toaster, TooltipProvider } from '@djangocfg/ui-core/components';
|
|
50
50
|
import { DialogProvider } from '@djangocfg/ui-core/lib/dialog-service';
|
|
51
51
|
import { NextRouterAdapter, NextLinkProvider } from '@djangocfg/ui-core/adapters/nextjs';
|
|
52
|
+
import { NextIntlLinkBridge } from './NextIntlLinkBridge';
|
|
52
53
|
import { ThemeProvider } from '@djangocfg/ui-nextjs/theme';
|
|
53
54
|
import { ThemeStyleBridge } from '../../theme/ThemeStyleBridge';
|
|
54
55
|
import { ErrorBoundary } from '../../components/errors/ErrorBoundary';
|
|
@@ -158,7 +159,13 @@ export function BaseApp({
|
|
|
158
159
|
onMonitorCapture={(d) => FrontendMonitor.capture(errorDetailToMonitorEvent(d))}
|
|
159
160
|
>
|
|
160
161
|
<MonitorProvider {...monitorConfig} />
|
|
161
|
-
<LayoutI18nProvider value={i18n}>
|
|
162
|
+
<LayoutI18nProvider value={i18n}>
|
|
163
|
+
{i18n?.routing ? (
|
|
164
|
+
<NextIntlLinkBridge routing={i18n.routing}>{children}</NextIntlLinkBridge>
|
|
165
|
+
) : (
|
|
166
|
+
children
|
|
167
|
+
)}
|
|
168
|
+
</LayoutI18nProvider>
|
|
162
169
|
<NextTopLoader
|
|
163
170
|
color="hsl(var(--primary))"
|
|
164
171
|
height={3}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NextIntlLinkBridge
|
|
5
|
+
*
|
|
6
|
+
* Bridges next-intl's locale-aware `Link` (built from a `routing` object via
|
|
7
|
+
* `createNavigation()`) into our framework-agnostic `LinkProvider`. Mounted
|
|
8
|
+
* by `BaseApp` only when the consumer passes `i18n.routing`.
|
|
9
|
+
*
|
|
10
|
+
* Why this lives in @djangocfg/layouts (not @djangocfg/ui-core):
|
|
11
|
+
* ui-core stays free of next-intl. Layouts already depend on next + nextjs
|
|
12
|
+
* plumbing, so taking a peer on next-intl here is honest about scope and
|
|
13
|
+
* keeps consumer apps from having to wire `createNavigation` themselves.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { forwardRef, useMemo, type ReactNode } from 'react';
|
|
17
|
+
import { createNavigation } from 'next-intl/navigation';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
LinkProvider,
|
|
21
|
+
type LinkComponent,
|
|
22
|
+
type LinkComponentProps,
|
|
23
|
+
} from '@djangocfg/ui-core/components';
|
|
24
|
+
|
|
25
|
+
import type { NextIntlRouting } from '../types/layout.types';
|
|
26
|
+
|
|
27
|
+
export interface NextIntlLinkBridgeProps {
|
|
28
|
+
/** The `routing` object returned by `defineRouting()` from `next-intl/routing`. */
|
|
29
|
+
routing: NextIntlRouting;
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function NextIntlLinkBridge({ routing, children }: NextIntlLinkBridgeProps) {
|
|
34
|
+
const adapter = useMemo<LinkComponent>(() => {
|
|
35
|
+
// The `createNavigation` overload signature splits between localized and
|
|
36
|
+
// shared routing variants and TS can't narrow either way through our
|
|
37
|
+
// opaque `NextIntlRouting` alias — cast to satisfy the localized branch
|
|
38
|
+
// and rely on next-intl to handle both at runtime.
|
|
39
|
+
const { Link: IntlLink } = createNavigation(
|
|
40
|
+
routing as unknown as Parameters<typeof createNavigation>[0],
|
|
41
|
+
);
|
|
42
|
+
return forwardRef<HTMLAnchorElement, LinkComponentProps>(
|
|
43
|
+
function NextIntlLinkAdapter(
|
|
44
|
+
{ href, replace, scroll, prefetch, children: kids, ...rest },
|
|
45
|
+
ref,
|
|
46
|
+
) {
|
|
47
|
+
// next-intl's Link accepts the same props we expose. The cast is safe
|
|
48
|
+
// because LinkComponentProps is a structural subset.
|
|
49
|
+
const Link = IntlLink as unknown as LinkComponent;
|
|
50
|
+
return (
|
|
51
|
+
<Link
|
|
52
|
+
href={href}
|
|
53
|
+
replace={replace}
|
|
54
|
+
scroll={scroll}
|
|
55
|
+
prefetch={prefetch}
|
|
56
|
+
ref={ref}
|
|
57
|
+
{...rest}
|
|
58
|
+
>
|
|
59
|
+
{kids}
|
|
60
|
+
</Link>
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
}, [routing]);
|
|
65
|
+
|
|
66
|
+
return <LinkProvider value={adapter}>{children}</LinkProvider>;
|
|
67
|
+
}
|
|
@@ -7,7 +7,7 @@ import { PublicLayout, FloatingNavbar, DefaultFooter } from '@djangocfg/layouts'
|
|
|
7
7
|
|
|
8
8
|
<PublicLayout
|
|
9
9
|
navbar={<FloatingNavbar config={{ brand, navigation, userMenu, actions }} />}
|
|
10
|
-
footer={<DefaultFooter config={{ variant: 'full', menus: { sections }
|
|
10
|
+
footer={<DefaultFooter config={{ variant: 'full', menus: { sections } }} />}
|
|
11
11
|
>
|
|
12
12
|
{children}
|
|
13
13
|
</PublicLayout>
|
|
@@ -29,20 +29,11 @@ All internal anchors (navbar, footer, mobile drawer) use `<Link>` from
|
|
|
29
29
|
`@djangocfg/ui-core/components`. In Next.js apps it renders through `next/link`
|
|
30
30
|
automatically (via `NextLinkProvider`, which `BaseApp` mounts for you).
|
|
31
31
|
|
|
32
|
-
For
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
import { LinkProvider } from '@djangocfg/ui-core/components';
|
|
38
|
-
import { Link as I18nLink } from '@/i18n/navigation';
|
|
39
|
-
|
|
40
|
-
<LinkProvider value={I18nLink}>
|
|
41
|
-
<PublicLayout navbar={<FloatingNavbar config={…} />} footer={…}>
|
|
42
|
-
{children}
|
|
43
|
-
</PublicLayout>
|
|
44
|
-
</LinkProvider>
|
|
45
|
-
```
|
|
32
|
+
For multi-locale apps (`next-intl` with `localePrefix: 'always' | 'as-needed'`)
|
|
33
|
+
pass `i18n.routing` to `AppLayout` / `BaseApp`. The bridge inside `BaseApp`
|
|
34
|
+
calls `createNavigation(routing)` and swaps the default `next/link` adapter
|
|
35
|
+
for the locale-aware one — every navbar / footer / drawer link keeps the
|
|
36
|
+
active locale prefix on click. See `@djangocfg/layouts` README → **i18n**.
|
|
46
37
|
|
|
47
38
|
## Navbar variants
|
|
48
39
|
|
|
@@ -74,8 +65,7 @@ Three variants. All share the same core props (below); only the chrome differs.
|
|
|
74
65
|
| `transparentThreshold` | `number` | `40` | Px past which the nav becomes opaque. |
|
|
75
66
|
| `desktopMaxPrimaryItems` | `number` | auto | Hard cap for primary items before overflow. |
|
|
76
67
|
| `renderDesktopDropdown` | `(ctx) => ReactNode` | — | Replace default popover per-item. |
|
|
77
|
-
| `controls` | `{ showThemeSwitcher?; showLocaleSwitcher? }` | — | Compact theme + locale pills next to UserMenu. See below. |
|
|
78
|
-
| `i18n` | `{ locale, locales, onLocaleChange }` | — | Required for the locale switcher (same shape as `DefaultFooter.i18n`). |
|
|
68
|
+
| `controls` | `{ showThemeSwitcher?; showLocaleSwitcher? }` | — | Compact theme + locale pills next to UserMenu. Locale data flows from `LayoutI18nProvider` (mounted by `BaseApp`); the switcher hides itself when no provider is present. See below. |
|
|
79
69
|
|
|
80
70
|
### Variant-only
|
|
81
71
|
|
|
@@ -94,10 +84,9 @@ Three variants. All share the same core props (below); only the chrome differs.
|
|
|
94
84
|
|
|
95
85
|
## Theme + locale controls (navbar)
|
|
96
86
|
|
|
97
|
-
All three navbars accept an optional `controls` block
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
drawer footer.
|
|
87
|
+
All three navbars accept an optional `controls` block. When enabled, a compact
|
|
88
|
+
`NavControls` pill is rendered on desktop right before `UserMenu`, and an
|
|
89
|
+
equivalent row appears in the mobile drawer footer.
|
|
101
90
|
|
|
102
91
|
```tsx
|
|
103
92
|
<FloatingNavbar
|
|
@@ -106,7 +95,6 @@ drawer footer.
|
|
|
106
95
|
navigation,
|
|
107
96
|
userMenu: { authPath: '/auth' },
|
|
108
97
|
controls: { showThemeSwitcher: true, showLocaleSwitcher: true },
|
|
109
|
-
i18n: { locale, locales, onLocaleChange: changeLocale },
|
|
110
98
|
}}
|
|
111
99
|
/>
|
|
112
100
|
```
|
|
@@ -114,7 +102,12 @@ drawer footer.
|
|
|
114
102
|
| Control | Required | Notes |
|
|
115
103
|
|---|---|---|
|
|
116
104
|
| `controls.showThemeSwitcher` | — | Light / system / dark pill (uses `useThemeContext`). |
|
|
117
|
-
| `controls.showLocaleSwitcher` | `
|
|
105
|
+
| `controls.showLocaleSwitcher` | `LayoutI18nProvider` | Locale switcher (icon-size pill matching `UserMenu` avatar). Hidden when no provider is mounted. |
|
|
106
|
+
|
|
107
|
+
Locale data (`locale`, `locales`, `onLocaleChange`) is read from
|
|
108
|
+
`LayoutI18nProvider`, which `BaseApp` (and `AppLayout`) mounts automatically
|
|
109
|
+
when you pass an `i18n` config. The switcher renders SVG country flags via
|
|
110
|
+
`country-flag-icons` — no emoji.
|
|
118
111
|
|
|
119
112
|
Same shape works for `FlushNavbar` and `MinimalNavbar`. If you build a custom
|
|
120
113
|
navbar with `NavbarShell`, the controls node is pre-built and delivered to
|
|
@@ -129,7 +122,6 @@ import { NavControls } from '@djangocfg/layouts';
|
|
|
129
122
|
<NavControls
|
|
130
123
|
showThemeSwitcher
|
|
131
124
|
showLocaleSwitcher
|
|
132
|
-
i18n={{ locale, locales, onLocaleChange }}
|
|
133
125
|
size="compact" // 'compact' (navbar) | 'default' (footer)
|
|
134
126
|
/>
|
|
135
127
|
```
|
|
@@ -160,7 +152,7 @@ actions={[
|
|
|
160
152
|
Three variants: `full` (default) with brand column + menus + controls; `compact` (one row + bottom); `simple` (copyright only).
|
|
161
153
|
|
|
162
154
|
```tsx
|
|
163
|
-
<DefaultFooter config={{ variant: 'full', menus: { sections },
|
|
155
|
+
<DefaultFooter config={{ variant: 'full', menus: { sections }, slots }} />
|
|
164
156
|
```
|
|
165
157
|
|
|
166
158
|
| Prop (`config.*`) | Type | Default | Role |
|
|
@@ -178,9 +170,8 @@ Three variants: `full` (default) with brand column + menus + controls; `compact`
|
|
|
178
170
|
| `social` | `FooterSocialLinks` | — | Social icon row under brand. |
|
|
179
171
|
| `meta.copyright` | `string` | `© ${year}. All rights reserved.` | |
|
|
180
172
|
| `meta.credits` | `{ text: string; url?: string }` | — | |
|
|
181
|
-
| `i18n` | `{ locale, locales, onLocaleChange }` | — | Required for the locale switcher. |
|
|
182
173
|
| `controls.showThemeSwitcher` | `boolean` | `true` | Full variant only. |
|
|
183
|
-
| `controls.showLocaleSwitcher` | `boolean` | `true`
|
|
174
|
+
| `controls.showLocaleSwitcher` | `boolean` | `true` | Full variant only. Reads locale from `LayoutI18nProvider`; hidden when no provider is mounted. |
|
|
184
175
|
| `slots.aboveMenus` | `ReactNode` | — | Above the brand/menus grid (full variant). |
|
|
185
176
|
| `slots.belowMenus` | `ReactNode` | — | Between menus and bottom row. |
|
|
186
177
|
| `slots.bottomStart` | `ReactNode` | — | Next to copyright. |
|
|
@@ -44,22 +44,38 @@ export function LocaleSwitcherDropdown({
|
|
|
44
44
|
}: LocaleSwitcherDropdownProps) {
|
|
45
45
|
const currentMeta = getLocaleMeta(locale, labels);
|
|
46
46
|
const currentLabel = showCode ? locale.toUpperCase() : currentMeta.native;
|
|
47
|
+
const isIconOnly = size === 'icon';
|
|
47
48
|
|
|
48
49
|
return (
|
|
49
50
|
<DropdownMenu>
|
|
50
51
|
<DropdownMenuTrigger asChild>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
{isIconOnly ? (
|
|
53
|
+
<Button
|
|
54
|
+
variant={variant}
|
|
55
|
+
size={size}
|
|
56
|
+
aria-label={currentLabel}
|
|
57
|
+
className={cn('overflow-hidden rounded-full p-0', className)}
|
|
58
|
+
>
|
|
59
|
+
{showFlag ? (
|
|
60
|
+
<LanguageFlag code={locale} className="h-full w-full object-cover" />
|
|
61
|
+
) : (
|
|
62
|
+
<Globe className="h-4 w-4" aria-hidden />
|
|
63
|
+
)}
|
|
64
|
+
</Button>
|
|
65
|
+
) : (
|
|
66
|
+
<Button variant={variant} size={size} className={className}>
|
|
67
|
+
{showFlag ? (
|
|
68
|
+
<LanguageFlag
|
|
69
|
+
code={locale}
|
|
70
|
+
rounded
|
|
71
|
+
className={cn('h-3 w-4 shrink-0', showTriggerLabel && 'mr-1.5')}
|
|
72
|
+
/>
|
|
73
|
+
) : showIcon ? (
|
|
74
|
+
<Globe className={cn('h-4 w-4 shrink-0', showTriggerLabel && 'mr-1')} />
|
|
75
|
+
) : null}
|
|
76
|
+
{showTriggerLabel && <span>{currentLabel}</span>}
|
|
77
|
+
</Button>
|
|
78
|
+
)}
|
|
63
79
|
</DropdownMenuTrigger>
|
|
64
80
|
<DropdownMenuContent align="end">
|
|
65
81
|
{locales.map((code) => {
|
|
@@ -48,6 +48,35 @@ export const LocaleSwitcherTrigger = React.forwardRef<
|
|
|
48
48
|
) {
|
|
49
49
|
const meta = getLocaleMeta(locale, labels);
|
|
50
50
|
const labelText = showCode ? locale.toUpperCase() : meta.native;
|
|
51
|
+
const isIconOnly = size === 'icon';
|
|
52
|
+
|
|
53
|
+
// Icon-only trigger: full-bleed flag inside a circular pill that lines up
|
|
54
|
+
// with neighbouring avatars / icon buttons. No padding, no inner gap.
|
|
55
|
+
if (isIconOnly) {
|
|
56
|
+
return (
|
|
57
|
+
<Button
|
|
58
|
+
ref={ref}
|
|
59
|
+
type="button"
|
|
60
|
+
variant={variant}
|
|
61
|
+
size={size}
|
|
62
|
+
onClick={onClick}
|
|
63
|
+
aria-label={ariaLabel ?? labelText}
|
|
64
|
+
className={cn(
|
|
65
|
+
'overflow-hidden rounded-full p-0',
|
|
66
|
+
className,
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
{showFlag ? (
|
|
70
|
+
<LanguageFlag
|
|
71
|
+
code={locale}
|
|
72
|
+
className="h-full w-full object-cover"
|
|
73
|
+
/>
|
|
74
|
+
) : (
|
|
75
|
+
<Globe className="h-4 w-4" aria-hidden />
|
|
76
|
+
)}
|
|
77
|
+
</Button>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
51
80
|
|
|
52
81
|
return (
|
|
53
82
|
<Button
|
|
@@ -13,6 +13,15 @@ import type { AnalyticsConfig } from '../../snippets/Analytics/types';
|
|
|
13
13
|
import type { PwaInstallConfig } from '@djangocfg/ui-nextjs/pwa';
|
|
14
14
|
import type { ErrorBoundaryConfig, ErrorTrackingConfig } from '../../components/errors/types';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Opaque alias for the `routing` object returned by `defineRouting()` from
|
|
18
|
+
* `next-intl/routing`. Typed as `unknown` so consumer apps can pass shared
|
|
19
|
+
* (no `pathnames`) or localized routing without fighting next-intl's
|
|
20
|
+
* conditional generics. The bridge inside `BaseApp` casts it back when it
|
|
21
|
+
* calls `createNavigation(routing)`.
|
|
22
|
+
*/
|
|
23
|
+
export type NextIntlRouting = object & { __nextIntlRouting?: never };
|
|
24
|
+
|
|
16
25
|
// Import local provider configs
|
|
17
26
|
import type { ThemeConfig, SWRConfigOptions, CentrifugoConfig } from './providers.types';
|
|
18
27
|
import type { MonitorConfig } from '@djangocfg/monitor';
|
|
@@ -117,6 +126,14 @@ export interface I18nLayoutConfig {
|
|
|
117
126
|
brand?: I18nBrandConfig;
|
|
118
127
|
/** Custom UI strings for the locale-switcher dialog. */
|
|
119
128
|
dialogLabels?: I18nDialogLabels;
|
|
129
|
+
/**
|
|
130
|
+
* Optional `routing` object from `next-intl`'s `defineRouting()`. When
|
|
131
|
+
* passed, `BaseApp` builds a locale-aware Link adapter from it and mounts
|
|
132
|
+
* a `LinkProvider` so every `<Link>` rendered inside layouts keeps the
|
|
133
|
+
* active locale prefix on click. Without it, non-default-locale users get
|
|
134
|
+
* sent back to the default locale on every navbar / footer click.
|
|
135
|
+
*/
|
|
136
|
+
routing?: NextIntlRouting;
|
|
120
137
|
}
|
|
121
138
|
|
|
122
139
|
// ============================================================================
|