@djangocfg/layouts 2.1.591 → 2.1.592

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.
Files changed (71) hide show
  1. package/package.json +12 -12
  2. package/src/layouts/PrivateLayout/PrivateLayout.tsx +37 -26
  3. package/src/layouts/PrivateLayout/README.md +20 -12
  4. package/src/layouts/PrivateLayout/components/PrivateContent.tsx +29 -42
  5. package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +23 -92
  6. package/src/layouts/PrivateLayout/components/PrivateSidebarAccount.tsx +32 -62
  7. package/src/layouts/PrivateLayout/components/SidebarBody.tsx +45 -0
  8. package/src/layouts/PrivateLayout/components/SidebarBrand.tsx +64 -152
  9. package/src/layouts/PrivateLayout/components/SidebarBrandSwitcher.tsx +2 -30
  10. package/src/layouts/PrivateLayout/components/SidebarNavGroup.tsx +55 -115
  11. package/src/layouts/PrivateLayout/components/SidebarNavItem.tsx +37 -100
  12. package/src/layouts/PrivateLayout/components/SidebarPeek.tsx +34 -0
  13. package/src/layouts/PrivateLayout/components/SidebarResizeHandle.tsx +76 -0
  14. package/src/layouts/PrivateLayout/components/SidebarSlots.tsx +14 -53
  15. package/src/layouts/PrivateLayout/components/index.ts +0 -4
  16. package/src/layouts/PrivateLayout/context.tsx +10 -115
  17. package/src/layouts/PrivateLayout/geometry.ts +17 -0
  18. package/src/layouts/PrivateLayout/hooks/index.ts +2 -1
  19. package/src/layouts/PrivateLayout/hooks/useLayoutVisual.ts +4 -4
  20. package/src/layouts/PrivateLayout/hooks/useSidebarKeyboard.ts +33 -93
  21. package/src/layouts/PrivateLayout/hooks/useSidebarPeek.tsx +151 -0
  22. package/src/layouts/PrivateLayout/hooks/useSidebarWidth.ts +83 -0
  23. package/src/layouts/PrivateLayout/types.ts +13 -20
  24. package/src/layouts/SettingsLayout/README.md +23 -10
  25. package/src/layouts/SettingsLayout/SettingsDialog.tsx +14 -8
  26. package/src/layouts/SettingsLayout/SettingsForm.tsx +13 -9
  27. package/src/layouts/SettingsLayout/components/ApiKeySection/ApiKeySection.tsx +9 -7
  28. package/src/layouts/SettingsLayout/components/ApiKeySection/context.tsx +6 -41
  29. package/src/layouts/SettingsLayout/components/AvatarSection.tsx +11 -3
  30. package/src/layouts/SettingsLayout/components/SettingsNav.tsx +5 -9
  31. package/src/layouts/SettingsLayout/components/SettingsNavItem.tsx +5 -7
  32. package/src/layouts/SettingsLayout/components/SettingsPanel.tsx +5 -10
  33. package/src/layouts/SettingsLayout/components/SettingsSearch.tsx +5 -3
  34. package/src/layouts/SettingsLayout/components/SettingsShell.tsx +14 -21
  35. package/src/layouts/SettingsLayout/components/TwoFactorSection/TwoFactorSection.tsx +52 -105
  36. package/src/layouts/SettingsLayout/components/index.ts +0 -1
  37. package/src/layouts/SettingsLayout/context/SettingsContext.tsx +5 -2
  38. package/src/layouts/SettingsLayout/i18n/index.ts +39 -0
  39. package/src/layouts/SettingsLayout/i18n/locales/ar.ts +66 -0
  40. package/src/layouts/SettingsLayout/i18n/locales/da.ts +66 -0
  41. package/src/layouts/SettingsLayout/i18n/locales/de.ts +66 -0
  42. package/src/layouts/SettingsLayout/i18n/locales/en.ts +66 -0
  43. package/src/layouts/SettingsLayout/i18n/locales/es.ts +66 -0
  44. package/src/layouts/SettingsLayout/i18n/locales/fr.ts +66 -0
  45. package/src/layouts/SettingsLayout/i18n/locales/it.ts +66 -0
  46. package/src/layouts/SettingsLayout/i18n/locales/ja.ts +66 -0
  47. package/src/layouts/SettingsLayout/i18n/locales/ko.ts +66 -0
  48. package/src/layouts/SettingsLayout/i18n/locales/nl.ts +66 -0
  49. package/src/layouts/SettingsLayout/i18n/locales/no.ts +66 -0
  50. package/src/layouts/SettingsLayout/i18n/locales/pl.ts +66 -0
  51. package/src/layouts/SettingsLayout/i18n/locales/ptBR.ts +66 -0
  52. package/src/layouts/SettingsLayout/i18n/locales/ru.ts +66 -0
  53. package/src/layouts/SettingsLayout/i18n/locales/sv.ts +66 -0
  54. package/src/layouts/SettingsLayout/i18n/locales/tr.ts +66 -0
  55. package/src/layouts/SettingsLayout/i18n/locales/zh.ts +66 -0
  56. package/src/layouts/SettingsLayout/i18n/types.ts +65 -0
  57. package/src/layouts/SettingsLayout/index.ts +0 -1
  58. package/src/layouts/SettingsLayout/sections/AccountSection.tsx +9 -4
  59. package/src/layouts/SettingsLayout/sections/DeleteAccountRow.tsx +4 -1
  60. package/src/layouts/SettingsLayout/sections/GeneralSection.tsx +25 -0
  61. package/src/layouts/SettingsLayout/sections/PreferencesRows.tsx +12 -4
  62. package/src/layouts/SettingsLayout/sections/builtins.tsx +34 -29
  63. package/src/layouts/SettingsLayout/sections/index.ts +2 -2
  64. package/src/layouts/SettingsLayout/types.ts +12 -4
  65. package/src/layouts/types/layout.types.ts +5 -6
  66. package/src/styles/dashboard.css +10 -0
  67. package/src/layouts/PrivateLayout/density.ts +0 -48
  68. package/src/layouts/PrivateLayout/hooks/useHoverExpand.ts +0 -110
  69. package/src/layouts/PrivateLayout/hooks/useShellVisualState.ts +0 -130
  70. package/src/layouts/SettingsLayout/components/SettingsRailFooter.tsx +0 -74
  71. package/src/layouts/SettingsLayout/sections/SecuritySection.tsx +0 -15
@@ -1,110 +0,0 @@
1
- /**
2
- * Hover Expand Hook
3
- *
4
- * Manages hover-expand state for collapsed sidebar with debounce.
5
- * - enterDelay: ms before expanding on hover (default 250)
6
- * - leaveDelay: ms before collapsing on mouse leave (default 150)
7
- * - Listens for `sidebar:blockcollapse` / `sidebar:allowcollapse` custom events
8
- * so descendant components (e.g. account dropdown) can block collapse without
9
- * prop drilling or context churn.
10
- * - Clears timers on unmount / re-hover to prevent false triggers.
11
- */
12
-
13
- 'use client';
14
-
15
- import { useCallback, useEffect, useRef, useState } from 'react';
16
-
17
- interface UseHoverExpandOptions {
18
- /** Delay before expanding on mouse enter (ms). Default 250. */
19
- enterDelay?: number;
20
- /** Delay before collapsing on mouse leave (ms). Default 150. */
21
- leaveDelay?: number;
22
- /** Whether hover-expand is enabled (e.g. only on desktop collapsed rail). */
23
- enabled?: boolean;
24
- }
25
-
26
- interface UseHoverExpandResult {
27
- /** Whether the sidebar is currently hover-expanded */
28
- isHoverExpanded: boolean;
29
- /** Attach to the sidebar root element */
30
- onMouseEnter: () => void;
31
- /** Attach to the sidebar root element */
32
- onMouseLeave: () => void;
33
- /** Programmatically set hover-expanded state (for click-to-expand) */
34
- setHoverExpanded: (value: boolean) => void;
35
- }
36
-
37
- export function useHoverExpand({
38
- enterDelay = 2000,
39
- leaveDelay = 450,
40
- enabled = true,
41
- }: UseHoverExpandOptions = {}): UseHoverExpandResult {
42
- const [isHoverExpanded, setIsHoverExpanded] = useState(false);
43
- const blockedRef = useRef(false);
44
- const enterTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
45
- const leaveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
46
-
47
- const clearTimers = useCallback(() => {
48
- if (enterTimerRef.current) {
49
- clearTimeout(enterTimerRef.current);
50
- enterTimerRef.current = null;
51
- }
52
- if (leaveTimerRef.current) {
53
- clearTimeout(leaveTimerRef.current);
54
- leaveTimerRef.current = null;
55
- }
56
- }, []);
57
-
58
- useEffect(() => {
59
- const onBlock = () => {
60
- blockedRef.current = true;
61
- };
62
- const onAllow = () => {
63
- blockedRef.current = false;
64
- };
65
- window.addEventListener('sidebar:blockcollapse', onBlock);
66
- window.addEventListener('sidebar:allowcollapse', onAllow);
67
- return () => {
68
- window.removeEventListener('sidebar:blockcollapse', onBlock);
69
- window.removeEventListener('sidebar:allowcollapse', onAllow);
70
- };
71
- }, []);
72
-
73
- const onMouseEnter = useCallback(() => {
74
- if (!enabled) return;
75
- clearTimers();
76
- enterTimerRef.current = setTimeout(() => {
77
- setIsHoverExpanded(true);
78
- }, enterDelay);
79
- }, [enabled, enterDelay, clearTimers]);
80
-
81
- const onMouseLeave = useCallback(() => {
82
- if (!enabled) return;
83
- if (blockedRef.current) return;
84
- clearTimers();
85
- leaveTimerRef.current = setTimeout(() => {
86
- setIsHoverExpanded(false);
87
- }, leaveDelay);
88
- }, [enabled, leaveDelay, clearTimers]);
89
-
90
- useEffect(() => {
91
- return () => clearTimers();
92
- }, [clearTimers]);
93
-
94
- const setHoverExpanded = useCallback((value: boolean) => {
95
- clearTimers();
96
- setIsHoverExpanded(value);
97
- }, [clearTimers]);
98
-
99
- return { isHoverExpanded, onMouseEnter, onMouseLeave, setHoverExpanded };
100
- }
101
-
102
- /** Dispatch from any descendant to block sidebar collapse while e.g. a dropdown is open. */
103
- export function blockSidebarCollapse(): void {
104
- window.dispatchEvent(new CustomEvent('sidebar:blockcollapse'));
105
- }
106
-
107
- /** Dispatch when the blocking reason is gone (e.g. dropdown closed). */
108
- export function allowSidebarCollapse(): void {
109
- window.dispatchEvent(new CustomEvent('sidebar:allowcollapse'));
110
- }
@@ -1,130 +0,0 @@
1
- /**
2
- * Shell Visual State Hook
3
- *
4
- * Centralizes ALL visual decisions for the sidebar + content pair:
5
- * - sidebar state: expanded | collapsed-rail
6
- * - layout variant: boxed (inset) | full-bleed
7
- * - what to show/hide in each state
8
- * - CSS modifiers for sidebar root, inner shell, and content inset
9
- *
10
- * Every component reads from this hook — no scattered ternaries.
11
- */
12
-
13
- 'use client';
14
-
15
- import { useMemo } from 'react';
16
-
17
- import { usePrivateLayoutContext } from '../context';
18
- import type { LayoutVisualConfig } from '../../types';
19
-
20
- // ============================================================================
21
- // State flags
22
- // ============================================================================
23
-
24
- export interface ShellVisualFlags {
25
- /** Mobile drawer mode */
26
- isMobile: boolean;
27
- /** Persistent expanded (user toggled or default open) */
28
- isExpanded: boolean;
29
- /** Collapsed to icon rail */
30
- isCollapsed: boolean;
31
- /** True only in desktop collapsed rail */
32
- isRail: boolean;
33
- }
34
-
35
- // ============================================================================
36
- // Content visibility flags
37
- // ============================================================================
38
-
39
- export interface ShellContentFlags {
40
- /** Nav item text labels visible */
41
- showLabels: boolean;
42
- /** Group section headers visible */
43
- showGroupLabels: boolean;
44
- /** Badges render as full text (false = dot only) */
45
- showBadgeText: boolean;
46
- /** Tooltips on nav items (only when labels hidden) */
47
- showTooltips: boolean;
48
- /** Account footer in compact avatar-only mode */
49
- isAccountCompact: boolean;
50
- /** menuStart / menuEnd / featured slots hidden */
51
- hideSlots: boolean;
52
- }
53
-
54
- // ============================================================================
55
- // Chrome flags
56
- // ============================================================================
57
-
58
- export interface ShellChromeFlags {
59
- /** Right border/separator on sidebar */
60
- showBorder: boolean;
61
- /** Content inset should have margin to make room for sidebar */
62
- contentHasSidebarGap: boolean;
63
- /** Sidebar width is fixed to icon-rail */
64
- isRailWidth: boolean;
65
- }
66
-
67
- // ============================================================================
68
- // CSS modifiers
69
- // ============================================================================
70
-
71
- export interface ShellModifiers {
72
- /** Classes for the shadcn <Sidebar> root (fixed container) */
73
- sidebarRoot: string[];
74
- /** Classes for the inner div[data-sidebar=sidebar] */
75
- sidebarInner: string[];
76
- /** Classes for the shadcn <SidebarContent> */
77
- sidebarContent: string[];
78
- }
79
-
80
- // ============================================================================
81
- // Combined result
82
- // ============================================================================
83
-
84
- export interface ShellVisualState {
85
- flags: ShellVisualFlags;
86
- content: ShellContentFlags;
87
- chrome: ShellChromeFlags;
88
- modifiers: ShellModifiers;
89
- }
90
-
91
- // ============================================================================
92
- // Hook
93
- // ============================================================================
94
-
95
- export function useShellVisualState(
96
- layoutVariant?: LayoutVisualConfig['variant'],
97
- ): ShellVisualState {
98
- const { isMobile, isExpanded, isCollapsed } = usePrivateLayoutContext();
99
-
100
- const isRail = !isMobile && isCollapsed;
101
- const variant = layoutVariant ?? 'boxed';
102
-
103
- return useMemo(() => {
104
- const showLabels = isExpanded || isMobile;
105
- const showGroupLabels = showLabels;
106
- const showBadgeText = showLabels;
107
- const showTooltips = isRail;
108
- const isAccountCompact = isRail;
109
- const hideSlots = isRail;
110
-
111
- const contentHasSidebarGap = isExpanded || isRail;
112
- const isRailWidth = isRail;
113
- const showBorder = false;
114
-
115
- const sidebarRoot: string[] = [];
116
- const sidebarInner: string[] = [];
117
- const sidebarContent: string[] = [];
118
-
119
- if (variant === 'boxed' && isExpanded) {
120
- sidebarInner.push('rounded-sm');
121
- }
122
-
123
- return {
124
- flags: { isMobile, isExpanded, isCollapsed, isRail },
125
- content: { showLabels, showGroupLabels, showBadgeText, showTooltips, isAccountCompact, hideSlots },
126
- chrome: { showBorder, contentHasSidebarGap, isRailWidth },
127
- modifiers: { sidebarRoot, sidebarInner, sidebarContent },
128
- };
129
- }, [isMobile, isExpanded, isCollapsed, isRail, variant]);
130
- }
@@ -1,74 +0,0 @@
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
- };
@@ -1,15 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
-
5
- import { TwoFactorSection } from '../components/TwoFactorSection';
6
-
7
- /**
8
- * Built-in "Security" section body — two-factor auth setup/disable.
9
- * Wraps the existing TwoFactorSection; the panel header supplies the title.
10
- */
11
- export const SecuritySection: React.FC = () => (
12
- <div className="mx-auto max-w-2xl space-y-6">
13
- <TwoFactorSection />
14
- </div>
15
- );