@djangocfg/layouts 2.1.308 → 2.1.310
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
CHANGED
|
@@ -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.310",
|
|
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.310",
|
|
78
|
+
"@djangocfg/centrifugo": "^2.1.310",
|
|
79
|
+
"@djangocfg/debuger": "^2.1.310",
|
|
80
|
+
"@djangocfg/i18n": "^2.1.310",
|
|
81
|
+
"@djangocfg/monitor": "^2.1.310",
|
|
82
|
+
"@djangocfg/ui-core": "^2.1.310",
|
|
83
|
+
"@djangocfg/ui-nextjs": "^2.1.310",
|
|
84
|
+
"@djangocfg/ui-tools": "^2.1.310",
|
|
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.310",
|
|
115
|
+
"@djangocfg/centrifugo": "^2.1.310",
|
|
116
|
+
"@djangocfg/debuger": "^2.1.310",
|
|
117
|
+
"@djangocfg/i18n": "^2.1.310",
|
|
118
|
+
"@djangocfg/monitor": "^2.1.310",
|
|
119
|
+
"@djangocfg/typescript-config": "^2.1.310",
|
|
120
|
+
"@djangocfg/ui-core": "^2.1.310",
|
|
121
|
+
"@djangocfg/ui-nextjs": "^2.1.310",
|
|
122
|
+
"@djangocfg/ui-tools": "^2.1.310",
|
|
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
|
+
}
|
|
@@ -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
|
|
|
@@ -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
|
// ============================================================================
|