@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.
- package/package.json +12 -12
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +37 -26
- package/src/layouts/PrivateLayout/README.md +20 -12
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +29 -42
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +23 -92
- package/src/layouts/PrivateLayout/components/PrivateSidebarAccount.tsx +32 -62
- package/src/layouts/PrivateLayout/components/SidebarBody.tsx +45 -0
- package/src/layouts/PrivateLayout/components/SidebarBrand.tsx +64 -152
- package/src/layouts/PrivateLayout/components/SidebarBrandSwitcher.tsx +2 -30
- package/src/layouts/PrivateLayout/components/SidebarNavGroup.tsx +55 -115
- package/src/layouts/PrivateLayout/components/SidebarNavItem.tsx +37 -100
- package/src/layouts/PrivateLayout/components/SidebarPeek.tsx +34 -0
- package/src/layouts/PrivateLayout/components/SidebarResizeHandle.tsx +76 -0
- package/src/layouts/PrivateLayout/components/SidebarSlots.tsx +14 -53
- package/src/layouts/PrivateLayout/components/index.ts +0 -4
- package/src/layouts/PrivateLayout/context.tsx +10 -115
- package/src/layouts/PrivateLayout/geometry.ts +17 -0
- package/src/layouts/PrivateLayout/hooks/index.ts +2 -1
- package/src/layouts/PrivateLayout/hooks/useLayoutVisual.ts +4 -4
- package/src/layouts/PrivateLayout/hooks/useSidebarKeyboard.ts +33 -93
- package/src/layouts/PrivateLayout/hooks/useSidebarPeek.tsx +151 -0
- package/src/layouts/PrivateLayout/hooks/useSidebarWidth.ts +83 -0
- package/src/layouts/PrivateLayout/types.ts +13 -20
- package/src/layouts/SettingsLayout/README.md +23 -10
- package/src/layouts/SettingsLayout/SettingsDialog.tsx +14 -8
- package/src/layouts/SettingsLayout/SettingsForm.tsx +13 -9
- package/src/layouts/SettingsLayout/components/ApiKeySection/ApiKeySection.tsx +9 -7
- package/src/layouts/SettingsLayout/components/ApiKeySection/context.tsx +6 -41
- package/src/layouts/SettingsLayout/components/AvatarSection.tsx +11 -3
- package/src/layouts/SettingsLayout/components/SettingsNav.tsx +5 -9
- package/src/layouts/SettingsLayout/components/SettingsNavItem.tsx +5 -7
- package/src/layouts/SettingsLayout/components/SettingsPanel.tsx +5 -10
- package/src/layouts/SettingsLayout/components/SettingsSearch.tsx +5 -3
- package/src/layouts/SettingsLayout/components/SettingsShell.tsx +14 -21
- package/src/layouts/SettingsLayout/components/TwoFactorSection/TwoFactorSection.tsx +52 -105
- package/src/layouts/SettingsLayout/components/index.ts +0 -1
- package/src/layouts/SettingsLayout/context/SettingsContext.tsx +5 -2
- package/src/layouts/SettingsLayout/i18n/index.ts +39 -0
- package/src/layouts/SettingsLayout/i18n/locales/ar.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/da.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/de.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/en.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/es.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/fr.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/it.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ja.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ko.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/nl.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/no.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/pl.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ptBR.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/ru.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/sv.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/tr.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/locales/zh.ts +66 -0
- package/src/layouts/SettingsLayout/i18n/types.ts +65 -0
- package/src/layouts/SettingsLayout/index.ts +0 -1
- package/src/layouts/SettingsLayout/sections/AccountSection.tsx +9 -4
- package/src/layouts/SettingsLayout/sections/DeleteAccountRow.tsx +4 -1
- package/src/layouts/SettingsLayout/sections/GeneralSection.tsx +25 -0
- package/src/layouts/SettingsLayout/sections/PreferencesRows.tsx +12 -4
- package/src/layouts/SettingsLayout/sections/builtins.tsx +34 -29
- package/src/layouts/SettingsLayout/sections/index.ts +2 -2
- package/src/layouts/SettingsLayout/types.ts +12 -4
- package/src/layouts/types/layout.types.ts +5 -6
- package/src/styles/dashboard.css +10 -0
- package/src/layouts/PrivateLayout/density.ts +0 -48
- package/src/layouts/PrivateLayout/hooks/useHoverExpand.ts +0 -110
- package/src/layouts/PrivateLayout/hooks/useShellVisualState.ts +0 -130
- package/src/layouts/SettingsLayout/components/SettingsRailFooter.tsx +0 -74
- 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
|
-
);
|