@djangocfg/layouts 2.1.339 → 2.1.341

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.339",
3
+ "version": "2.1.341",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -84,13 +84,13 @@
84
84
  "check": "tsc --noEmit"
85
85
  },
86
86
  "peerDependencies": {
87
- "@djangocfg/api": "^2.1.339",
88
- "@djangocfg/centrifugo": "^2.1.339",
89
- "@djangocfg/debuger": "^2.1.339",
90
- "@djangocfg/i18n": "^2.1.339",
91
- "@djangocfg/monitor": "^2.1.339",
92
- "@djangocfg/ui-core": "^2.1.339",
93
- "@djangocfg/ui-nextjs": "^2.1.339",
87
+ "@djangocfg/api": "^2.1.341",
88
+ "@djangocfg/centrifugo": "^2.1.341",
89
+ "@djangocfg/debuger": "^2.1.341",
90
+ "@djangocfg/i18n": "^2.1.341",
91
+ "@djangocfg/monitor": "^2.1.341",
92
+ "@djangocfg/ui-core": "^2.1.341",
93
+ "@djangocfg/ui-nextjs": "^2.1.341",
94
94
  "@hookform/resolvers": "^5.2.2",
95
95
  "consola": "^3.4.2",
96
96
  "lucide-react": "^0.545.0",
@@ -120,15 +120,15 @@
120
120
  "uuid": "^11.1.0"
121
121
  },
122
122
  "devDependencies": {
123
- "@djangocfg/api": "^2.1.339",
124
- "@djangocfg/centrifugo": "^2.1.339",
125
- "@djangocfg/debuger": "^2.1.339",
126
- "@djangocfg/i18n": "^2.1.339",
127
- "@djangocfg/monitor": "^2.1.339",
128
- "@djangocfg/typescript-config": "^2.1.339",
129
- "@djangocfg/ui-core": "^2.1.339",
130
- "@djangocfg/ui-nextjs": "^2.1.339",
131
- "@djangocfg/ui-tools": "^2.1.339",
123
+ "@djangocfg/api": "^2.1.341",
124
+ "@djangocfg/centrifugo": "^2.1.341",
125
+ "@djangocfg/debuger": "^2.1.341",
126
+ "@djangocfg/i18n": "^2.1.341",
127
+ "@djangocfg/monitor": "^2.1.341",
128
+ "@djangocfg/typescript-config": "^2.1.341",
129
+ "@djangocfg/ui-core": "^2.1.341",
130
+ "@djangocfg/ui-nextjs": "^2.1.341",
131
+ "@djangocfg/ui-tools": "^2.1.341",
132
132
  "@types/node": "^24.7.2",
133
133
  "@types/react": "^19.1.0",
134
134
  "@types/react-dom": "^19.1.0",
@@ -146,6 +146,13 @@ export interface PrivateLayoutProps {
146
146
  pathname?: string;
147
147
  /** Content padding */
148
148
  contentPadding?: 'none' | 'default';
149
+ /**
150
+ * Content scroll behaviour.
151
+ * - `'auto'` (default) — the shell scroll-area scrolls vertically.
152
+ * - `'hidden'` — shell does NOT scroll; use for full-height pages (e.g. Kanban)
153
+ * where children manage their own scroll surfaces.
154
+ */
155
+ contentScroll?: 'auto' | 'hidden';
149
156
  /**
150
157
  * Visual style of the shell. Defaults to `'boxed'` (inset rounded card on a
151
158
  * sidebar-coloured canvas). Pass `{ variant: 'full-bleed' }` for the legacy
@@ -167,6 +174,7 @@ export function PrivateLayout({
167
174
  header,
168
175
  pathname,
169
176
  contentPadding = 'default',
177
+ contentScroll = 'auto',
170
178
  visual,
171
179
  requireAuth = true,
172
180
  }: PrivateLayoutProps) {
@@ -217,6 +225,7 @@ export function PrivateLayout({
217
225
  <SidebarInset className={resolveInsetClassName(visual)}>
218
226
  <PrivateContent
219
227
  padding={contentPadding}
228
+ scroll={contentScroll}
220
229
  hasSidebar={Boolean(sidebar)}
221
230
  visual={visual}
222
231
  >
@@ -286,11 +295,15 @@ const BOXED_BG_CLASS: Record<NonNullable<LayoutVisualConfig['background']>, stri
286
295
  };
287
296
 
288
297
  function resolveProviderClassName(visual: LayoutVisualConfig | undefined): string | undefined {
289
- if ((visual?.variant ?? 'boxed') !== 'boxed') return undefined;
298
+ // h-svh + overflow-hidden: lock the shell to exactly one viewport height so
299
+ // the inner scroll-area (PrivateContent) is the only scroll surface. Without
300
+ // this, SidebarProvider grows via min-h-svh and the whole page scrolls.
301
+ const base = 'h-svh overflow-hidden';
302
+ if ((visual?.variant ?? 'boxed') !== 'boxed') return base;
290
303
  // `max-md:!bg-background` neutralises shadcn-sidebar's built-in
291
304
  // `has-[[data-variant=inset]]:bg-sidebar` below md so the mobile Drawer shell
292
305
  // doesn't paint the whole viewport with the canvas tint.
293
- return `max-md:!bg-background ${BOXED_BG_CLASS[visual?.background ?? 'sidebar']}`;
306
+ return `${base} max-md:!bg-background ${BOXED_BG_CLASS[visual?.background ?? 'sidebar']}`;
294
307
  }
295
308
 
296
309
  function normaliseInset(inset: LayoutVisualConfig['inset']): { x: number; y: number } {
@@ -34,6 +34,40 @@ The auth guard redirects to `header.authPath` when there's no session. Pass `req
34
34
 
35
35
  ---
36
36
 
37
+ ## Content layout props
38
+
39
+ | Prop | Type | Default | Notes |
40
+ |---|---|---|---|
41
+ | `contentPadding` | `'default' \| 'none'` | `'default'` | `'default'` adds responsive horizontal + vertical padding around children. `'none'` strips all padding — children are responsible for their own spacing. |
42
+ | `contentScroll` | `'auto' \| 'hidden'` | `'auto'` | `'auto'` — the shell scroll-area scrolls vertically (standard pages). `'hidden'` — the shell does **not** scroll; use for full-height pages (Kanban, split-pane editors) where individual child regions manage their own scroll surfaces. Always pair with `contentPadding='none'` so children can own their own spacing too. |
43
+
44
+ ### Full-height / no-scroll pages
45
+
46
+ For pages like Kanban boards where the shell must be exactly one viewport tall and columns scroll independently:
47
+
48
+ ```tsx
49
+ <PrivateLayout
50
+ sidebar={sidebar}
51
+ header={header}
52
+ pathname={pathname}
53
+ contentPadding="none"
54
+ contentScroll="hidden"
55
+ >
56
+ {/* Page is responsible for all padding and scroll */}
57
+ <div className="flex flex-col h-full">
58
+ <header className="px-4 sm:px-6 lg:px-8 pt-6 lg:pt-8 pb-3 border-b shrink-0">
59
+ ...
60
+ </header>
61
+ <div className="flex-1 min-h-0 overflow-x-auto flex gap-3 px-4 sm:px-6 lg:px-8 py-4">
62
+ {/* Each column scrolls vertically on its own */}
63
+ <KanbanColumn />
64
+ </div>
65
+ </div>
66
+ </PrivateLayout>
67
+ ```
68
+
69
+ ---
70
+
37
71
  ## Sidebar config
38
72
 
39
73
  `SidebarConfig` (passed as `sidebar` prop):
@@ -15,6 +15,13 @@ import type { LayoutVisualConfig } from '../../types';
15
15
  interface PrivateContentProps {
16
16
  children: ReactNode;
17
17
  padding?: 'none' | 'default';
18
+ /**
19
+ * Controls the overflow behaviour of the scroll surface.
20
+ * - `'auto'` (default) — content scrolls vertically inside the shell.
21
+ * - `'hidden'` — shell does NOT scroll; children must manage their own scroll.
22
+ * Use this for full-height pages like Kanban where columns scroll individually.
23
+ */
24
+ scroll?: 'auto' | 'hidden';
18
25
  /** When false, no mobile hamburger (e.g. layout without a sidebar). Default true. */
19
26
  hasSidebar?: boolean;
20
27
  /** Visual config from PrivateLayout — controls maxWidth in boxed mode. */
@@ -31,6 +38,7 @@ const MAX_WIDTH_CLASS: Record<NonNullable<LayoutVisualConfig['maxWidth']>, strin
31
38
  export function PrivateContent({
32
39
  children,
33
40
  padding = 'default',
41
+ scroll = 'auto',
34
42
  hasSidebar = true,
35
43
  visual,
36
44
  }: PrivateContentProps) {
@@ -55,12 +63,13 @@ export function PrivateContent({
55
63
  : undefined;
56
64
 
57
65
  const scrollAreaClass = cn(
58
- 'min-h-0 flex-1 overflow-y-auto',
59
- padding === 'default' && [
66
+ 'min-h-0 flex-1',
67
+ scroll === 'auto' ? 'overflow-y-auto' : 'overflow-hidden',
68
+ padding === 'default' && scroll === 'auto' && [
60
69
  'px-4 sm:px-6 lg:px-8',
61
70
  'pb-[calc(1rem+env(safe-area-inset-bottom,0px))] sm:pb-[calc(1.5rem+env(safe-area-inset-bottom,0px))] lg:pb-[calc(2rem+env(safe-area-inset-bottom,0px))]',
62
71
  ],
63
- contentTopPaddingClass,
72
+ scroll === 'auto' && contentTopPaddingClass,
64
73
  mobileFabClearance,
65
74
  );
66
75