@djangocfg/layouts 2.1.501 → 2.1.503

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.501",
3
+ "version": "2.1.503",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -89,12 +89,12 @@
89
89
  "check": "tsc --noEmit"
90
90
  },
91
91
  "peerDependencies": {
92
- "@djangocfg/analytics": "^2.1.501",
93
- "@djangocfg/api": "^2.1.501",
94
- "@djangocfg/centrifugo": "^2.1.501",
95
- "@djangocfg/devtools": "^2.1.501",
96
- "@djangocfg/i18n": "^2.1.501",
97
- "@djangocfg/ui-core": "^2.1.501",
92
+ "@djangocfg/analytics": "^2.1.503",
93
+ "@djangocfg/api": "^2.1.503",
94
+ "@djangocfg/centrifugo": "^2.1.503",
95
+ "@djangocfg/devtools": "^2.1.503",
96
+ "@djangocfg/i18n": "^2.1.503",
97
+ "@djangocfg/ui-core": "^2.1.503",
98
98
  "@hookform/resolvers": "^5.2.2",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -124,14 +124,14 @@
124
124
  "uuid": "^11.1.1"
125
125
  },
126
126
  "devDependencies": {
127
- "@djangocfg/analytics": "^2.1.501",
128
- "@djangocfg/api": "^2.1.501",
129
- "@djangocfg/centrifugo": "^2.1.501",
130
- "@djangocfg/devtools": "^2.1.501",
131
- "@djangocfg/i18n": "^2.1.501",
132
- "@djangocfg/typescript-config": "^2.1.501",
133
- "@djangocfg/ui-core": "^2.1.501",
134
- "@djangocfg/ui-tools": "^2.1.501",
127
+ "@djangocfg/analytics": "^2.1.503",
128
+ "@djangocfg/api": "^2.1.503",
129
+ "@djangocfg/centrifugo": "^2.1.503",
130
+ "@djangocfg/devtools": "^2.1.503",
131
+ "@djangocfg/i18n": "^2.1.503",
132
+ "@djangocfg/typescript-config": "^2.1.503",
133
+ "@djangocfg/ui-core": "^2.1.503",
134
+ "@djangocfg/ui-tools": "^2.1.503",
135
135
  "@types/node": "^25.9.5",
136
136
  "@types/react": "19.2.15",
137
137
  "@types/react-dom": "19.2.3",
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+
5
+ import { cn } from '@djangocfg/ui-core/lib';
6
+ import { ThemeSegmented } from '@djangocfg/ui-core/theme';
7
+
8
+ import { LocaleSwitcherDropdown } from '../../_components/locale-switcher';
9
+ import { useLayoutI18nOptional } from '../../AppLayout/LayoutI18nProvider';
10
+
11
+ interface SettingsRailFooterProps {
12
+ /**
13
+ * Horizontal layout (theme + language side-by-side on a single row) for the
14
+ * mobile header strip. Default is the stacked vertical layout used at the
15
+ * bottom of the desktop rail.
16
+ */
17
+ horizontal?: boolean;
18
+ className?: string;
19
+ }
20
+
21
+ /**
22
+ * Global preferences pinned to the bottom-left of the settings rail — the
23
+ * "chrome" controls (appearance + language) that belong with the shell, not
24
+ * inside the Account tab. Sits below a hairline, always visible regardless of
25
+ * the active section (mirrors the app-shell pattern: quick prefs live in the
26
+ * corner, not buried in a form).
27
+ *
28
+ * Language only renders when a layout i18n context is present.
29
+ */
30
+ export const SettingsRailFooter: React.FC<SettingsRailFooterProps> = ({
31
+ horizontal = false,
32
+ className,
33
+ }) => {
34
+ const layoutI18n = useLayoutI18nOptional();
35
+
36
+ return (
37
+ <div
38
+ className={cn(
39
+ 'border-t border-border/70',
40
+ horizontal
41
+ ? 'flex items-center justify-end gap-2'
42
+ : 'flex flex-col gap-2 px-1 pt-3',
43
+ className,
44
+ )}
45
+ >
46
+ {!horizontal && (
47
+ <span className="px-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/70">
48
+ Preferences
49
+ </span>
50
+ )}
51
+
52
+ <div
53
+ className={cn(
54
+ horizontal ? 'flex items-center gap-2' : 'flex items-center justify-between gap-2 px-0.5',
55
+ )}
56
+ >
57
+ <ThemeSegmented size="compact" />
58
+ {layoutI18n && (
59
+ <LocaleSwitcherDropdown
60
+ locale={layoutI18n.locale}
61
+ locales={layoutI18n.locales}
62
+ onChange={layoutI18n.onLocaleChange}
63
+ variant="outline"
64
+ size="sm"
65
+ showCode
66
+ showIcon={false}
67
+ showFlag
68
+ showTriggerLabel
69
+ />
70
+ )}
71
+ </div>
72
+ </div>
73
+ );
74
+ };
@@ -8,6 +8,7 @@ import { cn } from '@djangocfg/ui-core/lib';
8
8
  import { useSettingsContext } from '../context';
9
9
  import { SettingsNav } from './SettingsNav';
10
10
  import { SettingsPanel } from './SettingsPanel';
11
+ import { SettingsRailFooter } from './SettingsRailFooter';
11
12
  import { SettingsTabs } from './SettingsTabs';
12
13
 
13
14
  interface SettingsShellProps {
@@ -45,8 +46,13 @@ export const SettingsShell: React.FC<SettingsShellProps> = ({ bare = false, clas
45
46
  <div className={frame}>
46
47
  <div className="shrink-0 bg-muted">
47
48
  {/* Title row — `pr-12` leaves room for the dialog's corner close button. */}
48
- <div className="px-4 pb-2 pt-3.5 pr-12">
49
- <h2 className="text-base font-semibold tracking-tight text-foreground">{title}</h2>
49
+ <div className="flex items-center gap-3 px-4 pb-2 pt-3.5 pr-12">
50
+ <h2 className="flex-1 truncate text-base font-semibold tracking-tight text-foreground">
51
+ {title}
52
+ </h2>
53
+ {/* Global prefs (theme + language) mirror the desktop rail footer —
54
+ on phones they ride the header instead of a bottom rail. */}
55
+ <SettingsRailFooter horizontal className="border-t-0" />
50
56
  </div>
51
57
  {/* Tabs on their own row (below the header), so nothing overlaps the X. */}
52
58
  <div className="border-b border-border px-1.5 pb-2">
@@ -63,9 +69,14 @@ export const SettingsShell: React.FC<SettingsShellProps> = ({ bare = false, clas
63
69
  // ── Desktop: rail + panel ──
64
70
  return (
65
71
  <div className={cn(frame, 'flex-row')}>
66
- {/* Nav rail — recessed surface, one notch below the content panel. */}
67
- <aside className="shrink-0 border-r border-border bg-muted p-3 md:w-52">
68
- <SettingsNav />
72
+ {/* Nav rail — recessed surface, one notch below the content panel.
73
+ Column layout so the section list scrolls in the middle and the
74
+ global-preferences footer (theme + language) pins to the bottom. */}
75
+ <aside className="flex shrink-0 flex-col border-r border-border bg-muted p-3 md:w-52">
76
+ <div className="min-h-0 flex-1">
77
+ <SettingsNav />
78
+ </div>
79
+ <SettingsRailFooter className="mt-3" />
69
80
  </aside>
70
81
 
71
82
  {/* Detail panel — the lighter, primary surface. */}
@@ -4,3 +4,4 @@ export { SettingsTabs } from './SettingsTabs';
4
4
  export { SettingsNavItem } from './SettingsNavItem';
5
5
  export { SettingsPanel } from './SettingsPanel';
6
6
  export { SettingsSearch } from './SettingsSearch';
7
+ export { SettingsRailFooter } from './SettingsRailFooter';
@@ -9,7 +9,6 @@ import { Button, SettingRow, SettingsBlock } from '@djangocfg/ui-core/components
9
9
  import { AvatarSection } from '../components/AvatarSection';
10
10
  import { useProfileSave } from '../hooks/useProfileSave';
11
11
  import { DeleteAccountRow } from './DeleteAccountRow';
12
- import { PreferencesRows } from './PreferencesRows';
13
12
 
14
13
  interface AccountSectionProps {
15
14
  /** Render the "Danger zone" delete-account control. Default: true. */
@@ -80,9 +79,8 @@ export const AccountSection: React.FC<AccountSectionProps> = ({
80
79
  />
81
80
  </SettingsBlock>
82
81
 
83
- <SettingsBlock title={labels.preferences ?? 'Preferences'}>
84
- <PreferencesRows />
85
- </SettingsBlock>
82
+ {/* Language + Theme moved to the settings-rail footer (bottom-left of the
83
+ dialog) — they are app-shell "chrome" prefs, not per-account fields. */}
86
84
 
87
85
  <SettingsBlock title={labels.actions}>
88
86
  <SettingRow