@djangocfg/layouts 2.1.294 → 2.1.298

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
@@ -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. 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. |
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` + `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.294",
3
+ "version": "2.1.298",
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.294",
78
- "@djangocfg/centrifugo": "^2.1.294",
79
- "@djangocfg/debuger": "^2.1.294",
80
- "@djangocfg/i18n": "^2.1.294",
81
- "@djangocfg/monitor": "^2.1.294",
82
- "@djangocfg/ui-core": "^2.1.294",
83
- "@djangocfg/ui-nextjs": "^2.1.294",
84
- "@djangocfg/ui-tools": "^2.1.294",
77
+ "@djangocfg/api": "^2.1.298",
78
+ "@djangocfg/centrifugo": "^2.1.298",
79
+ "@djangocfg/debuger": "^2.1.298",
80
+ "@djangocfg/i18n": "^2.1.298",
81
+ "@djangocfg/monitor": "^2.1.298",
82
+ "@djangocfg/ui-core": "^2.1.298",
83
+ "@djangocfg/ui-nextjs": "^2.1.298",
84
+ "@djangocfg/ui-tools": "^2.1.298",
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.294",
114
- "@djangocfg/centrifugo": "^2.1.294",
115
- "@djangocfg/debuger": "^2.1.294",
116
- "@djangocfg/i18n": "^2.1.294",
117
- "@djangocfg/monitor": "^2.1.294",
118
- "@djangocfg/typescript-config": "^2.1.294",
119
- "@djangocfg/ui-core": "^2.1.294",
120
- "@djangocfg/ui-nextjs": "^2.1.294",
121
- "@djangocfg/ui-tools": "^2.1.294",
113
+ "@djangocfg/api": "^2.1.298",
114
+ "@djangocfg/centrifugo": "^2.1.298",
115
+ "@djangocfg/debuger": "^2.1.298",
116
+ "@djangocfg/i18n": "^2.1.298",
117
+ "@djangocfg/monitor": "^2.1.298",
118
+ "@djangocfg/typescript-config": "^2.1.298",
119
+ "@djangocfg/ui-core": "^2.1.298",
120
+ "@djangocfg/ui-nextjs": "^2.1.298",
121
+ "@djangocfg/ui-tools": "^2.1.298",
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,7 @@ import { AuthProvider } from '@djangocfg/api/auth';
48
48
  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
- import { NextRouterAdapter } from '@djangocfg/ui-core/adapters/nextjs';
51
+ import { NextRouterAdapter, NextLinkProvider } from '@djangocfg/ui-core/adapters/nextjs';
52
52
  import { ThemeProvider } from '@djangocfg/ui-nextjs/theme';
53
53
  import { ThemeStyleBridge } from '../../theme/ThemeStyleBridge';
54
54
  import { ErrorBoundary } from '../../components/errors/ErrorBoundary';
@@ -126,6 +126,7 @@ export function BaseApp({
126
126
  * (Auth, Centrifugo, anything that may navigate) gets it.
127
127
  */}
128
128
  <NextRouterAdapter>
129
+ <NextLinkProvider>
129
130
  <DialogProvider>
130
131
  <TooltipProvider>
131
132
  <SWRConfig
@@ -191,6 +192,7 @@ export function BaseApp({
191
192
  </SWRConfig>
192
193
  </TooltipProvider>
193
194
  </DialogProvider>
195
+ </NextLinkProvider>
194
196
  </NextRouterAdapter>
195
197
  </ThemeProvider>
196
198
  );
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use client';
7
7
 
8
- import Link from 'next/link';
8
+ import { Link } from '@djangocfg/ui-core/components';
9
9
  import { usePathname as useNextPathname } from 'next/navigation';
10
10
  import React from 'react';
11
11
 
@@ -23,30 +23,27 @@ 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
26
+ ## Links
27
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`:
28
+ All internal anchors (navbar, footer, mobile drawer) use `<Link>` from
29
+ `@djangocfg/ui-core/components`. In Next.js apps it renders through `next/link`
30
+ automatically (via `NextLinkProvider`, which `BaseApp` mounts for you).
31
+
32
+ For a locale-aware Link (e.g. `next-intl`'s `createNavigation().Link`), swap
33
+ the underlying component by mounting your own `LinkProvider` from
34
+ `@djangocfg/ui-core/components` higher in the tree:
32
35
 
33
36
  ```tsx
34
- import { LinkComponentProvider, PublicLayout, FloatingNavbar } from '@djangocfg/layouts';
35
- import { Link } from '@/i18n/navigation'; // next-intl createNavigation().Link
37
+ import { LinkProvider } from '@djangocfg/ui-core/components';
38
+ import { Link as I18nLink } from '@/i18n/navigation';
36
39
 
37
- <LinkComponentProvider value={Link}>
40
+ <LinkProvider value={I18nLink}>
38
41
  <PublicLayout navbar={<FloatingNavbar config={…} />} footer={…}>
39
42
  {children}
40
43
  </PublicLayout>
41
- </LinkComponentProvider>
44
+ </LinkProvider>
42
45
  ```
43
46
 
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
-
50
47
  ## Navbar variants
51
48
 
52
49
  Three variants. All share the same core props (below); only the chrome differs.
@@ -200,7 +197,7 @@ Three variants: `full` (default) with brand column + menus + controls; `compact`
200
197
 
201
198
  ## Primitives
202
199
 
203
- `NavBrand`, `NavActions`, `NavActionItem`, `NavControls`, `NavDesktopItems`, `ThemeBrandMark`, `ThemeBrandMarkImg`, `LinkComponentProvider`, `useLinkComponent`, `PublicLayoutProvider`, `usePublicLayout`.
200
+ `NavBrand`, `NavActions`, `NavActionItem`, `NavControls`, `NavDesktopItems`, `ThemeBrandMark`, `ThemeBrandMarkImg`, `PublicLayoutProvider`, `usePublicLayout`.
204
201
 
205
202
  ## Context
206
203
 
@@ -12,8 +12,9 @@ import { Laptop, Moon, Sun } from 'lucide-react';
12
12
  import { Button } from '@djangocfg/ui-core/components';
13
13
  import { useForcedTheme, useThemeContext } from '@djangocfg/ui-nextjs/theme';
14
14
 
15
+ import { Link } from '@djangocfg/ui-core/components';
16
+
15
17
  import { LocaleSwitcher } from '../../../_components/LocaleSwitcher';
16
- import { useLinkComponent } from '../../primitives/LinkComponentContext';
17
18
  import { FooterMenuSections } from './FooterMenuSections';
18
19
  import { FooterProjectInfo } from './FooterProjectInfo';
19
20
 
@@ -103,7 +104,6 @@ function ThemeModeControl({
103
104
  }
104
105
 
105
106
  export function DefaultFooter({ config }: DefaultFooterProps) {
106
- const Link = useLinkComponent();
107
107
  const variant = config.variant ?? 'full';
108
108
  const shellClass = config.shell?.className;
109
109
  const brandSlot = config.brand?.slot;
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React from 'react';
4
4
 
5
- import { useLinkComponent } from '../../primitives/LinkComponentContext';
5
+ import { Link } from '@djangocfg/ui-core/components';
6
6
  import { DjangoCFGLogo } from './DjangoCFGLogo';
7
7
 
8
8
  import type { FooterLink } from './types';
@@ -23,7 +23,6 @@ export function FooterBottom({
23
23
  links = [],
24
24
  variant = 'desktop',
25
25
  }: FooterBottomProps) {
26
- const Link = useLinkComponent();
27
26
  const isMobile = variant === 'mobile';
28
27
 
29
28
  if (isMobile) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React from 'react';
4
4
 
5
- import { useLinkComponent } from '../../primitives/LinkComponentContext';
5
+ import { Link } from '@djangocfg/ui-core/components';
6
6
 
7
7
  import type { FooterMenuSection } from './types';
8
8
 
@@ -17,7 +17,6 @@ export function FooterMenuSections({
17
17
  minColumnWidth = 180,
18
18
  maxColumns = 5,
19
19
  }: FooterMenuSectionsProps) {
20
- const Link = useLinkComponent();
21
20
  if (menuSections.length === 0) return null;
22
21
 
23
22
  const effectiveColumns = Math.max(1, Math.min(maxColumns, menuSections.length));
@@ -38,17 +38,12 @@ export {
38
38
  NavDesktopItems,
39
39
  ThemeBrandMark,
40
40
  ThemeBrandMarkImg,
41
- LinkComponentProvider,
42
- useLinkComponent,
43
41
  } from './primitives';
44
42
  export type {
45
43
  NavAction,
46
44
  NavControlsProps,
47
45
  ThemeBrandMarkProps,
48
46
  ThemeBrandMarkImgProps,
49
- LinkComponent,
50
- LinkComponentProps,
51
- LinkComponentProviderProps,
52
47
  } from './primitives';
53
48
 
54
49
  // Navbar variants
@@ -9,10 +9,9 @@
9
9
 
10
10
  import React, { type ReactNode } from 'react';
11
11
 
12
+ import { Link } from '@djangocfg/ui-core/components';
12
13
  import { cn } from '@djangocfg/ui-core/lib';
13
14
 
14
- import { useLinkComponent } from './LinkComponentContext';
15
-
16
15
  export interface NavAction {
17
16
  label: string;
18
17
  href: string;
@@ -55,7 +54,6 @@ interface NavActionItemProps {
55
54
  }
56
55
 
57
56
  export function NavActionItem({ action, className }: NavActionItemProps) {
58
- const Link = useLinkComponent();
59
57
  const variant = action.variant ?? 'ghost';
60
58
  const cls = cn(
61
59
  baseCls,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { type ReactNode } from 'react';
4
4
 
5
- import { useLinkComponent } from './LinkComponentContext';
5
+ import { Link } from '@djangocfg/ui-core/components';
6
6
 
7
7
  interface NavBrandProps {
8
8
  brand?: ReactNode;
@@ -10,8 +10,6 @@ interface NavBrandProps {
10
10
  }
11
11
 
12
12
  export function NavBrand({ brand, brandHref = '/' }: NavBrandProps) {
13
- const Link = useLinkComponent();
14
-
15
13
  if (brand == null || brand === '' || brand === false) return null;
16
14
 
17
15
  if (typeof brand === 'string') {
@@ -3,14 +3,13 @@
3
3
  import { ChevronDown } from 'lucide-react';
4
4
  import React from 'react';
5
5
 
6
- import { Button } from '@djangocfg/ui-core/components';
6
+ import { Button, Link } from '@djangocfg/ui-core/components';
7
7
  import { cn } from '@djangocfg/ui-core/lib';
8
8
 
9
9
  import type { NavigationItem } from '../../types';
10
10
  import type { UseDropdownMenuReturn } from '../hooks/useDropdownMenu';
11
11
  import { useResponsiveOverflow } from '../hooks/useResponsiveOverflow';
12
12
  import type { PublicDesktopDropdownRenderer } from '../navbarTypes';
13
- import { useLinkComponent } from './LinkComponentContext';
14
13
 
15
14
  interface NavDesktopItemsProps {
16
15
  /** Full ordered item list; overflow is computed responsively. */
@@ -57,7 +56,6 @@ export function NavDesktopItems({
57
56
  dropdown,
58
57
  renderDesktopDropdown,
59
58
  }: NavDesktopItemsProps) {
60
- const Link = useLinkComponent();
61
59
  const { openDropdownKey, scheduleOpen, scheduleClose, closeDropdown } = dropdown;
62
60
 
63
61
  const { visibleCount, containerRef, itemRef, measured } = useResponsiveOverflow({
@@ -7,12 +7,3 @@ export type { NavControlsProps } from './NavControls';
7
7
  export { NavDesktopItems } from './NavDesktopItems';
8
8
  export { ThemeBrandMark, ThemeBrandMarkImg } from './ThemeBrandMark';
9
9
  export type { ThemeBrandMarkProps, ThemeBrandMarkImgProps } from './ThemeBrandMark';
10
- export {
11
- LinkComponentProvider,
12
- useLinkComponent,
13
- } from './LinkComponentContext';
14
- export type {
15
- LinkComponent,
16
- LinkComponentProps,
17
- LinkComponentProviderProps,
18
- } from './LinkComponentContext';
@@ -12,7 +12,7 @@ import React, { useMemo } from 'react';
12
12
 
13
13
  import { useAuth } from '@djangocfg/api/auth';
14
14
  import { useAppT } from '@djangocfg/i18n';
15
- import { Button } from '@djangocfg/ui-core/components';
15
+ import { Button, Link } from '@djangocfg/ui-core/components';
16
16
  import { useBodyScrollLock, useIsTabletOrBelow } from '@djangocfg/ui-core/hooks';
17
17
  import { cn } from '@djangocfg/ui-core/lib';
18
18
 
@@ -20,7 +20,6 @@ import { usePathnameWithoutLocale } from '../../../hooks';
20
20
  import { UserMenu } from '../../_components/UserMenu';
21
21
  import { usePublicLayoutOptional } from '../context';
22
22
  import { useMobileNavPanel } from '../hooks';
23
- import { useLinkComponent } from '../primitives/LinkComponentContext';
24
23
  import { NavControls } from '../primitives/NavControls';
25
24
 
26
25
  import type { I18nLayoutConfig, NavigationItem, UserMenuConfig } from '../../types';
@@ -44,7 +43,6 @@ export interface MobileDrawerShellProps {
44
43
  }
45
44
 
46
45
  export function MobileDrawerShell(props: MobileDrawerShellProps) {
47
- const Link = useLinkComponent();
48
46
  const context = usePublicLayoutOptional();
49
47
  const mobileMenuOpen = props.isOpen ?? context?.mobileMenuOpen ?? false;
50
48
  const closeMobileMenu = props.onClose ?? context?.closeMobileMenu ?? (() => {});
@@ -6,7 +6,7 @@
6
6
  'use client';
7
7
 
8
8
  import { ChevronDown, LogOut } from 'lucide-react';
9
- import Link from 'next/link';
9
+ import { Link } from '@djangocfg/ui-core/components';
10
10
  import React from 'react';
11
11
 
12
12
  import { useAuth } from '@djangocfg/api/auth';
@@ -31,7 +31,7 @@
31
31
  'use client';
32
32
 
33
33
  import { ArrowRight, Globe, LogOut } from 'lucide-react';
34
- import Link from 'next/link';
34
+ import { Link } from '@djangocfg/ui-core/components';
35
35
  import React, { useMemo } from 'react';
36
36
 
37
37
  import { useAuth } from '@djangocfg/api/auth';
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { ChevronRight, Home } from 'lucide-react';
3
- import Link from 'next/link';
4
- import { usePathname } from 'next/navigation';
3
+ import { Link } from '@djangocfg/ui-core/components';
4
+ import { useLocation } from '@djangocfg/ui-core/hooks';
5
5
  import React, { useMemo } from 'react';
6
6
 
7
7
  import { useAppT } from '@djangocfg/i18n';
@@ -18,7 +18,7 @@ interface BreadcrumbsProps {
18
18
  }
19
19
 
20
20
  const Breadcrumbs: React.FC<BreadcrumbsProps> = ({ items, className = "" }) => {
21
- const pathname = usePathname();
21
+ const { pathname } = useLocation();
22
22
  const t = useAppT();
23
23
 
24
24
  const labels = useMemo(() => ({
@@ -1,50 +0,0 @@
1
- /**
2
- * LinkComponentContext
3
- *
4
- * Dependency-injects the Link component used by navbars, footers, and mobile
5
- * drawers. Apps wire in their own i18n-aware Link (e.g. next-intl's
6
- * `createNavigation().Link`) so internal navigation picks up the locale
7
- * prefix. Default falls back to plain `next/link`.
8
- */
9
-
10
- 'use client';
11
-
12
- import NextLink, { type LinkProps as NextLinkProps } from 'next/link';
13
- import React, {
14
- createContext,
15
- useContext,
16
- type AnchorHTMLAttributes,
17
- type ComponentType,
18
- type ReactNode,
19
- } from 'react';
20
-
21
- type AnchorProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>;
22
-
23
- export type LinkComponentProps = Omit<NextLinkProps, 'passHref' | 'legacyBehavior'> &
24
- AnchorProps & {
25
- children?: ReactNode;
26
- };
27
-
28
- export type LinkComponent = ComponentType<LinkComponentProps>;
29
-
30
- const LinkComponentContext = createContext<LinkComponent>(NextLink as LinkComponent);
31
-
32
- export interface LinkComponentProviderProps {
33
- value: LinkComponent;
34
- children: ReactNode;
35
- }
36
-
37
- export function LinkComponentProvider({
38
- value,
39
- children,
40
- }: LinkComponentProviderProps) {
41
- return (
42
- <LinkComponentContext.Provider value={value}>
43
- {children}
44
- </LinkComponentContext.Provider>
45
- );
46
- }
47
-
48
- export function useLinkComponent(): LinkComponent {
49
- return useContext(LinkComponentContext);
50
- }