@djangocfg/layouts 1.4.27 → 1.4.28
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 +5 -5
- package/src/auth/middlewares/index.ts +1 -1
- package/src/auth/middlewares/proxy.ts +10 -2
- package/src/layouts/index.ts +0 -3
- package/src/snippets/ContactForm/ContactPage.tsx +16 -3
- package/src/layouts/UILayout/README.md +0 -267
- package/src/layouts/UILayout/SUMMARY.md +0 -298
- package/src/layouts/UILayout/TOOLS_INTEGRATION.md +0 -216
- package/src/layouts/UILayout/components/AutoComponentDemo.tsx +0 -77
- package/src/layouts/UILayout/components/CategoryRenderer.tsx +0 -45
- package/src/layouts/UILayout/components/TailwindGuideRenderer.tsx +0 -138
- package/src/layouts/UILayout/components/index.ts +0 -15
- package/src/layouts/UILayout/components/layout/Header/CopyAIButton.tsx +0 -58
- package/src/layouts/UILayout/components/layout/Header/Header.tsx +0 -60
- package/src/layouts/UILayout/components/layout/Header/HeaderDesktop.tsx +0 -51
- package/src/layouts/UILayout/components/layout/Header/HeaderMobile.tsx +0 -71
- package/src/layouts/UILayout/components/layout/Header/TestValidationButton.tsx +0 -268
- package/src/layouts/UILayout/components/layout/Header/index.ts +0 -11
- package/src/layouts/UILayout/components/layout/MobileOverlay/MobileOverlay.tsx +0 -47
- package/src/layouts/UILayout/components/layout/MobileOverlay/index.ts +0 -6
- package/src/layouts/UILayout/components/layout/Sidebar/Sidebar.tsx +0 -95
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarCategory.tsx +0 -54
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarContent.tsx +0 -93
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarFooter.tsx +0 -49
- package/src/layouts/UILayout/components/layout/Sidebar/index.ts +0 -9
- package/src/layouts/UILayout/components/layout/index.ts +0 -8
- package/src/layouts/UILayout/components/shared/Badge/CountBadge.tsx +0 -38
- package/src/layouts/UILayout/components/shared/Badge/index.ts +0 -5
- package/src/layouts/UILayout/components/shared/CodeBlock/CodeBlock.tsx +0 -48
- package/src/layouts/UILayout/components/shared/CodeBlock/CopyButton.tsx +0 -49
- package/src/layouts/UILayout/components/shared/CodeBlock/index.ts +0 -6
- package/src/layouts/UILayout/components/shared/Section/Section.tsx +0 -63
- package/src/layouts/UILayout/components/shared/Section/index.ts +0 -5
- package/src/layouts/UILayout/components/shared/index.ts +0 -8
- package/src/layouts/UILayout/config/ai-export.config.ts +0 -89
- package/src/layouts/UILayout/config/categories.config.tsx +0 -122
- package/src/layouts/UILayout/config/components/blocks.config.tsx +0 -239
- package/src/layouts/UILayout/config/components/data.config.tsx +0 -433
- package/src/layouts/UILayout/config/components/feedback.config.tsx +0 -290
- package/src/layouts/UILayout/config/components/forms.config.tsx +0 -996
- package/src/layouts/UILayout/config/components/hooks.config.tsx +0 -168
- package/src/layouts/UILayout/config/components/index.ts +0 -72
- package/src/layouts/UILayout/config/components/layout.config.tsx +0 -246
- package/src/layouts/UILayout/config/components/navigation.config.tsx +0 -352
- package/src/layouts/UILayout/config/components/overlay.config.tsx +0 -569
- package/src/layouts/UILayout/config/components/specialized.config.tsx +0 -400
- package/src/layouts/UILayout/config/components/tools.config.tsx +0 -234
- package/src/layouts/UILayout/config/components/types.ts +0 -14
- package/src/layouts/UILayout/config/index.ts +0 -42
- package/src/layouts/UILayout/config/tailwind.config.ts +0 -131
- package/src/layouts/UILayout/constants.ts +0 -23
- package/src/layouts/UILayout/context/ShowcaseContext.tsx +0 -81
- package/src/layouts/UILayout/context/index.ts +0 -1
- package/src/layouts/UILayout/core/UIGuideApp.client.tsx +0 -18
- package/src/layouts/UILayout/core/UIGuideApp.tsx +0 -33
- package/src/layouts/UILayout/core/UIGuideLanding.tsx +0 -172
- package/src/layouts/UILayout/core/UIGuideView.tsx +0 -61
- package/src/layouts/UILayout/core/UILayout.tsx +0 -125
- package/src/layouts/UILayout/core/UILayoutSidebar.tsx +0 -11
- package/src/layouts/UILayout/core/index.ts +0 -10
- package/src/layouts/UILayout/hooks/index.ts +0 -9
- package/src/layouts/UILayout/hooks/useAIExport.ts +0 -78
- package/src/layouts/UILayout/hooks/useCategoryNavigation.ts +0 -92
- package/src/layouts/UILayout/hooks/useComponentSearch.ts +0 -81
- package/src/layouts/UILayout/hooks/useSidebarState.ts +0 -36
- package/src/layouts/UILayout/index.ts +0 -160
- package/src/layouts/UILayout/types/component.ts +0 -45
- package/src/layouts/UILayout/types/index.ts +0 -23
- package/src/layouts/UILayout/types/layout.ts +0 -57
- package/src/layouts/UILayout/types/navigation.ts +0 -33
- package/src/layouts/UILayout/utils/ai-export/formatters.ts +0 -71
- package/src/layouts/UILayout/utils/ai-export/index.ts +0 -5
- package/src/layouts/UILayout/utils/component-helpers/filter.ts +0 -109
- package/src/layouts/UILayout/utils/component-helpers/index.ts +0 -6
- package/src/layouts/UILayout/utils/component-helpers/search.ts +0 -95
- package/src/layouts/UILayout/utils/index.ts +0 -6
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HeaderMobile Component
|
|
3
|
-
* Mobile version of the header
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { Button } from '@djangocfg/ui';
|
|
10
|
-
import { Menu, X } from 'lucide-react';
|
|
11
|
-
import { CopyAIButton } from './CopyAIButton';
|
|
12
|
-
|
|
13
|
-
interface HeaderMobileProps {
|
|
14
|
-
/** Project name */
|
|
15
|
-
projectName?: string;
|
|
16
|
-
/** Logo component */
|
|
17
|
-
logo?: React.ReactNode;
|
|
18
|
-
/** Is sidebar open */
|
|
19
|
-
isSidebarOpen?: boolean;
|
|
20
|
-
/** Toggle sidebar callback */
|
|
21
|
-
onToggleSidebar?: () => void;
|
|
22
|
-
/** Copy for AI callback - must return string */
|
|
23
|
-
onCopyForAI?: () => string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Mobile Header
|
|
28
|
-
* Compact header for mobile devices with hamburger menu
|
|
29
|
-
*/
|
|
30
|
-
export function HeaderMobile({
|
|
31
|
-
projectName = 'Django CFG',
|
|
32
|
-
logo,
|
|
33
|
-
isSidebarOpen = false,
|
|
34
|
-
onToggleSidebar,
|
|
35
|
-
onCopyForAI,
|
|
36
|
-
}: HeaderMobileProps) {
|
|
37
|
-
return (
|
|
38
|
-
<header className="w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
39
|
-
<div className="flex h-14 items-center px-4 gap-4">
|
|
40
|
-
{/* Hamburger Menu Button */}
|
|
41
|
-
<Button
|
|
42
|
-
variant="ghost"
|
|
43
|
-
size="icon"
|
|
44
|
-
className="h-9 w-9"
|
|
45
|
-
onClick={onToggleSidebar}
|
|
46
|
-
aria-label={isSidebarOpen ? 'Close menu' : 'Open menu'}
|
|
47
|
-
>
|
|
48
|
-
{isSidebarOpen ? (
|
|
49
|
-
<X className="h-5 w-5" />
|
|
50
|
-
) : (
|
|
51
|
-
<Menu className="h-5 w-5" />
|
|
52
|
-
)}
|
|
53
|
-
</Button>
|
|
54
|
-
|
|
55
|
-
{/* Logo and Project Name */}
|
|
56
|
-
<div className="flex items-center gap-2 flex-1">
|
|
57
|
-
{logo}
|
|
58
|
-
<span className="font-semibold text-sm">{projectName}</span>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
{/* Copy for AI Button */}
|
|
62
|
-
<CopyAIButton
|
|
63
|
-
onCopyForAI={onCopyForAI}
|
|
64
|
-
size="sm"
|
|
65
|
-
showLabel={false}
|
|
66
|
-
className="gap-2"
|
|
67
|
-
/>
|
|
68
|
-
</div>
|
|
69
|
-
</header>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TestValidationButton Component
|
|
3
|
-
*
|
|
4
|
-
* Developer tool for testing Zod validation error events
|
|
5
|
-
* Dispatches mock CustomEvent to test ValidationErrorProvider
|
|
6
|
-
*
|
|
7
|
-
* Only visible in development mode
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
'use client';
|
|
11
|
-
|
|
12
|
-
import React, { useState } from 'react';
|
|
13
|
-
import { Button } from '@djangocfg/ui';
|
|
14
|
-
import {
|
|
15
|
-
DropdownMenu,
|
|
16
|
-
DropdownMenuContent,
|
|
17
|
-
DropdownMenuItem,
|
|
18
|
-
DropdownMenuLabel,
|
|
19
|
-
DropdownMenuSeparator,
|
|
20
|
-
DropdownMenuTrigger,
|
|
21
|
-
} from '@djangocfg/ui';
|
|
22
|
-
import { Bug, AlertCircle } from 'lucide-react';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Mock Zod error factory
|
|
26
|
-
*/
|
|
27
|
-
function createMockZodError(type: 'simple' | 'multiple' | 'nested' | 'array') {
|
|
28
|
-
const errors: Record<string, any> = {
|
|
29
|
-
simple: {
|
|
30
|
-
issues: [
|
|
31
|
-
{
|
|
32
|
-
path: ['email'],
|
|
33
|
-
message: 'Invalid email format',
|
|
34
|
-
code: 'invalid_string',
|
|
35
|
-
expected: 'email',
|
|
36
|
-
received: 'string',
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
},
|
|
40
|
-
multiple: {
|
|
41
|
-
issues: [
|
|
42
|
-
{
|
|
43
|
-
path: ['email'],
|
|
44
|
-
message: 'Invalid email format',
|
|
45
|
-
code: 'invalid_string',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
path: ['age'],
|
|
49
|
-
message: 'Number must be greater than 0',
|
|
50
|
-
code: 'too_small',
|
|
51
|
-
minimum: 0,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
path: ['username'],
|
|
55
|
-
message: 'Required field',
|
|
56
|
-
code: 'invalid_type',
|
|
57
|
-
expected: 'string',
|
|
58
|
-
received: 'undefined',
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
},
|
|
62
|
-
nested: {
|
|
63
|
-
issues: [
|
|
64
|
-
{
|
|
65
|
-
path: ['address', 'street'],
|
|
66
|
-
message: 'Street is required',
|
|
67
|
-
code: 'invalid_type',
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
path: ['address', 'zipCode'],
|
|
71
|
-
message: 'Invalid zip code format',
|
|
72
|
-
code: 'invalid_string',
|
|
73
|
-
}
|
|
74
|
-
]
|
|
75
|
-
},
|
|
76
|
-
array: {
|
|
77
|
-
issues: [
|
|
78
|
-
{
|
|
79
|
-
path: ['items', 0, 'quantity'],
|
|
80
|
-
message: 'Quantity must be at least 1',
|
|
81
|
-
code: 'too_small',
|
|
82
|
-
minimum: 1,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
path: ['items', 1, 'price'],
|
|
86
|
-
message: 'Price is required',
|
|
87
|
-
code: 'invalid_type',
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
return errors[type];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Dispatch validation error event
|
|
98
|
-
*/
|
|
99
|
-
function dispatchValidationError(type: 'simple' | 'multiple' | 'nested' | 'array') {
|
|
100
|
-
const operations: Record<string, { operation: string; path: string; method: string; response: any }> = {
|
|
101
|
-
simple: {
|
|
102
|
-
operation: 'createUser',
|
|
103
|
-
path: '/api/users',
|
|
104
|
-
method: 'POST',
|
|
105
|
-
response: { email: 'not-an-email@' },
|
|
106
|
-
},
|
|
107
|
-
multiple: {
|
|
108
|
-
operation: 'updateProfile',
|
|
109
|
-
path: '/api/profile/123',
|
|
110
|
-
method: 'PATCH',
|
|
111
|
-
response: { email: 'bad', age: -5 },
|
|
112
|
-
},
|
|
113
|
-
nested: {
|
|
114
|
-
operation: 'createAddress',
|
|
115
|
-
path: '/api/addresses',
|
|
116
|
-
method: 'POST',
|
|
117
|
-
response: { address: { street: null, zipCode: '12' } },
|
|
118
|
-
},
|
|
119
|
-
array: {
|
|
120
|
-
operation: 'createOrder',
|
|
121
|
-
path: '/api/orders',
|
|
122
|
-
method: 'POST',
|
|
123
|
-
response: { items: [{ quantity: 0 }, { price: undefined }] },
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const config = operations[type];
|
|
128
|
-
const error = createMockZodError(type);
|
|
129
|
-
|
|
130
|
-
const event = new CustomEvent('zod-validation-error', {
|
|
131
|
-
detail: {
|
|
132
|
-
operation: config.operation,
|
|
133
|
-
path: config.path,
|
|
134
|
-
method: config.method,
|
|
135
|
-
error: error,
|
|
136
|
-
response: config.response,
|
|
137
|
-
timestamp: new Date(),
|
|
138
|
-
},
|
|
139
|
-
bubbles: true,
|
|
140
|
-
cancelable: false,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
window.dispatchEvent(event);
|
|
144
|
-
|
|
145
|
-
console.group('🧪 Test Validation Error Dispatched');
|
|
146
|
-
console.log('Type:', type);
|
|
147
|
-
console.log('Operation:', config.operation);
|
|
148
|
-
console.log('Endpoint:', config.method, config.path);
|
|
149
|
-
console.log('Issues:', error.issues);
|
|
150
|
-
console.groupEnd();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export interface TestValidationButtonProps {
|
|
154
|
-
/** Show in production (default: false - dev only) */
|
|
155
|
-
showInProduction?: boolean;
|
|
156
|
-
/** Button size */
|
|
157
|
-
size?: 'default' | 'sm' | 'lg' | 'icon';
|
|
158
|
-
/** Additional CSS classes */
|
|
159
|
-
className?: string;
|
|
160
|
-
/** Always show label (default: responsive - hidden on mobile) */
|
|
161
|
-
showLabel?: boolean;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Test Validation Button
|
|
166
|
-
*
|
|
167
|
-
* Dropdown menu with different validation error test cases
|
|
168
|
-
* Only visible in development by default
|
|
169
|
-
*/
|
|
170
|
-
export function TestValidationButton({
|
|
171
|
-
showInProduction = false,
|
|
172
|
-
size = 'sm',
|
|
173
|
-
className,
|
|
174
|
-
showLabel = false,
|
|
175
|
-
}: TestValidationButtonProps) {
|
|
176
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
177
|
-
|
|
178
|
-
// Hide in production unless explicitly enabled
|
|
179
|
-
if (!showInProduction && process.env.NODE_ENV === 'production') {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return (
|
|
184
|
-
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
|
|
185
|
-
<DropdownMenuTrigger asChild>
|
|
186
|
-
<Button
|
|
187
|
-
variant="outline"
|
|
188
|
-
size={size}
|
|
189
|
-
className={className}
|
|
190
|
-
>
|
|
191
|
-
<Bug className="h-4 w-4" />
|
|
192
|
-
<span className={showLabel ? "ml-2" : "ml-2 hidden sm:inline"}>Test Validation</span>
|
|
193
|
-
</Button>
|
|
194
|
-
</DropdownMenuTrigger>
|
|
195
|
-
|
|
196
|
-
<DropdownMenuContent align="end" className="w-[240px]">
|
|
197
|
-
<DropdownMenuLabel className="flex items-center gap-2">
|
|
198
|
-
<AlertCircle className="h-4 w-4 text-orange-500" />
|
|
199
|
-
<span>Test Validation Errors</span>
|
|
200
|
-
</DropdownMenuLabel>
|
|
201
|
-
|
|
202
|
-
<DropdownMenuSeparator />
|
|
203
|
-
|
|
204
|
-
<DropdownMenuItem
|
|
205
|
-
onClick={() => {
|
|
206
|
-
dispatchValidationError('simple');
|
|
207
|
-
setIsOpen(false);
|
|
208
|
-
}}
|
|
209
|
-
>
|
|
210
|
-
<div className="flex flex-col gap-1">
|
|
211
|
-
<div className="font-medium">Simple Error</div>
|
|
212
|
-
<div className="text-xs text-muted-foreground">
|
|
213
|
-
Single field validation
|
|
214
|
-
</div>
|
|
215
|
-
</div>
|
|
216
|
-
</DropdownMenuItem>
|
|
217
|
-
|
|
218
|
-
<DropdownMenuItem
|
|
219
|
-
onClick={() => {
|
|
220
|
-
dispatchValidationError('multiple');
|
|
221
|
-
setIsOpen(false);
|
|
222
|
-
}}
|
|
223
|
-
>
|
|
224
|
-
<div className="flex flex-col gap-1">
|
|
225
|
-
<div className="font-medium">Multiple Errors</div>
|
|
226
|
-
<div className="text-xs text-muted-foreground">
|
|
227
|
-
3 validation errors
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
</DropdownMenuItem>
|
|
231
|
-
|
|
232
|
-
<DropdownMenuItem
|
|
233
|
-
onClick={() => {
|
|
234
|
-
dispatchValidationError('nested');
|
|
235
|
-
setIsOpen(false);
|
|
236
|
-
}}
|
|
237
|
-
>
|
|
238
|
-
<div className="flex flex-col gap-1">
|
|
239
|
-
<div className="font-medium">Nested Object</div>
|
|
240
|
-
<div className="text-xs text-muted-foreground">
|
|
241
|
-
Nested property errors
|
|
242
|
-
</div>
|
|
243
|
-
</div>
|
|
244
|
-
</DropdownMenuItem>
|
|
245
|
-
|
|
246
|
-
<DropdownMenuItem
|
|
247
|
-
onClick={() => {
|
|
248
|
-
dispatchValidationError('array');
|
|
249
|
-
setIsOpen(false);
|
|
250
|
-
}}
|
|
251
|
-
>
|
|
252
|
-
<div className="flex flex-col gap-1">
|
|
253
|
-
<div className="font-medium">Array Validation</div>
|
|
254
|
-
<div className="text-xs text-muted-foreground">
|
|
255
|
-
Array item errors
|
|
256
|
-
</div>
|
|
257
|
-
</div>
|
|
258
|
-
</DropdownMenuItem>
|
|
259
|
-
|
|
260
|
-
<DropdownMenuSeparator />
|
|
261
|
-
|
|
262
|
-
<div className="px-2 py-1.5 text-xs text-muted-foreground">
|
|
263
|
-
💡 Check console and toast notifications
|
|
264
|
-
</div>
|
|
265
|
-
</DropdownMenuContent>
|
|
266
|
-
</DropdownMenu>
|
|
267
|
-
);
|
|
268
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Header Components
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { Header } from './Header';
|
|
6
|
-
export type { HeaderProps } from './Header';
|
|
7
|
-
export { HeaderMobile } from './HeaderMobile';
|
|
8
|
-
export { HeaderDesktop } from './HeaderDesktop';
|
|
9
|
-
export { CopyAIButton } from './CopyAIButton';
|
|
10
|
-
export { TestValidationButton } from './TestValidationButton';
|
|
11
|
-
export type { TestValidationButtonProps } from './TestValidationButton';
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MobileOverlay Component
|
|
3
|
-
* Dark overlay for mobile sidebar
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { createPortal } from 'react-dom';
|
|
10
|
-
import { cn } from '@djangocfg/ui/lib';
|
|
11
|
-
|
|
12
|
-
export interface MobileOverlayProps {
|
|
13
|
-
/** Is overlay visible */
|
|
14
|
-
isOpen?: boolean;
|
|
15
|
-
/** Close callback */
|
|
16
|
-
onClose?: () => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Mobile Overlay
|
|
21
|
-
* Darkens background when mobile sidebar is open
|
|
22
|
-
* Closes sidebar when clicked
|
|
23
|
-
*/
|
|
24
|
-
export function MobileOverlay({ isOpen = false, onClose }: MobileOverlayProps) {
|
|
25
|
-
const [mounted, setMounted] = React.useState(false);
|
|
26
|
-
|
|
27
|
-
React.useEffect(() => {
|
|
28
|
-
setMounted(true);
|
|
29
|
-
}, []);
|
|
30
|
-
|
|
31
|
-
if (!isOpen || !mounted || typeof window === 'undefined') {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return createPortal(
|
|
36
|
-
<div
|
|
37
|
-
className={cn(
|
|
38
|
-
'fixed inset-0 z-150 transition-opacity duration-300',
|
|
39
|
-
isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'
|
|
40
|
-
)}
|
|
41
|
-
style={{ backgroundColor: 'rgb(0 0 0 / 0.5)' }}
|
|
42
|
-
onClick={onClose}
|
|
43
|
-
aria-hidden="true"
|
|
44
|
-
/>,
|
|
45
|
-
document.body
|
|
46
|
-
);
|
|
47
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sidebar Component
|
|
3
|
-
* Navigation sidebar for component categories
|
|
4
|
-
* Desktop: sticky with react-sticky-box
|
|
5
|
-
* Mobile: Drawer component (Vaul-based)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import React from 'react';
|
|
11
|
-
import { useIsMobile, Sticky, Drawer, DrawerContent } from '@djangocfg/ui';
|
|
12
|
-
import type { ComponentCategory } from '../../../types';
|
|
13
|
-
import { SidebarContent } from './SidebarContent';
|
|
14
|
-
|
|
15
|
-
export interface SidebarProps {
|
|
16
|
-
/** Available categories */
|
|
17
|
-
categories: ComponentCategory[];
|
|
18
|
-
/** Current selected category */
|
|
19
|
-
currentCategory?: string;
|
|
20
|
-
/** Category change callback */
|
|
21
|
-
onCategoryChange?: (categoryId: string) => void;
|
|
22
|
-
/** Is sidebar open (mobile only) */
|
|
23
|
-
isOpen?: boolean;
|
|
24
|
-
/** Close sidebar callback (mobile only) */
|
|
25
|
-
onClose?: () => void;
|
|
26
|
-
/** Function to generate AI context */
|
|
27
|
-
onCopyForAI?: () => string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Sidebar Component
|
|
32
|
-
* Desktop: Always visible static sidebar
|
|
33
|
-
* Mobile: Drawer-based sidebar with slide animation
|
|
34
|
-
*/
|
|
35
|
-
export function Sidebar({
|
|
36
|
-
categories,
|
|
37
|
-
currentCategory,
|
|
38
|
-
onCategoryChange,
|
|
39
|
-
isOpen = false,
|
|
40
|
-
onClose,
|
|
41
|
-
onCopyForAI,
|
|
42
|
-
}: SidebarProps) {
|
|
43
|
-
const isMobile = useIsMobile();
|
|
44
|
-
|
|
45
|
-
// Desktop sidebar - uses react-sticky-box via Sticky component
|
|
46
|
-
// Sticks to viewport top (with navbar offset) but stops at parent container boundary (footer)
|
|
47
|
-
// Parent UILayout has isolate + zIndex:0 to prevent overlapping navbar
|
|
48
|
-
if (!isMobile) {
|
|
49
|
-
return (
|
|
50
|
-
<div
|
|
51
|
-
className="w-64 flex-shrink-0 self-stretch bg-background"
|
|
52
|
-
style={{ boxShadow: '-1px 0 0 0 hsl(var(--border))' }}
|
|
53
|
-
>
|
|
54
|
-
<Sticky
|
|
55
|
-
offsetTop={56}
|
|
56
|
-
offsetBottom={0}
|
|
57
|
-
disableOnMobile={false}
|
|
58
|
-
className="w-64"
|
|
59
|
-
>
|
|
60
|
-
<aside
|
|
61
|
-
className="overflow-y-auto w-64 bg-background"
|
|
62
|
-
style={{ maxHeight: 'calc(100vh - 56px)' }}
|
|
63
|
-
>
|
|
64
|
-
<SidebarContent
|
|
65
|
-
categories={categories}
|
|
66
|
-
currentCategory={currentCategory}
|
|
67
|
-
onCategoryChange={onCategoryChange}
|
|
68
|
-
isMobile={false}
|
|
69
|
-
onCopyForAI={onCopyForAI}
|
|
70
|
-
/>
|
|
71
|
-
</aside>
|
|
72
|
-
</Sticky>
|
|
73
|
-
</div>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Mobile sidebar - uses Drawer component (Vaul-based)
|
|
78
|
-
return (
|
|
79
|
-
<Drawer
|
|
80
|
-
open={isOpen}
|
|
81
|
-
onOpenChange={(open) => !open && onClose?.()}
|
|
82
|
-
direction="right"
|
|
83
|
-
>
|
|
84
|
-
<DrawerContent direction="right" className="w-64">
|
|
85
|
-
<SidebarContent
|
|
86
|
-
categories={categories}
|
|
87
|
-
currentCategory={currentCategory}
|
|
88
|
-
onCategoryChange={onCategoryChange}
|
|
89
|
-
isMobile={true}
|
|
90
|
-
onCopyForAI={onCopyForAI}
|
|
91
|
-
/>
|
|
92
|
-
</DrawerContent>
|
|
93
|
-
</Drawer>
|
|
94
|
-
);
|
|
95
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SidebarCategory Component
|
|
3
|
-
* Single category item in sidebar navigation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { cn } from '@djangocfg/ui/lib';
|
|
10
|
-
import type { ComponentCategory } from '../../../types';
|
|
11
|
-
import { CountBadge } from '../../shared/Badge';
|
|
12
|
-
|
|
13
|
-
interface SidebarCategoryProps {
|
|
14
|
-
/** Category data */
|
|
15
|
-
category: ComponentCategory;
|
|
16
|
-
/** Is this category active */
|
|
17
|
-
active?: boolean;
|
|
18
|
-
/** Click handler */
|
|
19
|
-
onClick?: () => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Sidebar Category Item
|
|
24
|
-
* Displays a category with icon, label, and optional count badge
|
|
25
|
-
*/
|
|
26
|
-
export function SidebarCategory({
|
|
27
|
-
category,
|
|
28
|
-
active = false,
|
|
29
|
-
onClick,
|
|
30
|
-
}: SidebarCategoryProps) {
|
|
31
|
-
return (
|
|
32
|
-
<button
|
|
33
|
-
onClick={onClick}
|
|
34
|
-
className={cn(
|
|
35
|
-
'w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-colors',
|
|
36
|
-
active
|
|
37
|
-
? 'bg-primary text-primary-foreground'
|
|
38
|
-
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
39
|
-
)}
|
|
40
|
-
title={category.description}
|
|
41
|
-
>
|
|
42
|
-
{/* Icon */}
|
|
43
|
-
<span className="flex-shrink-0">{category.icon}</span>
|
|
44
|
-
|
|
45
|
-
{/* Label */}
|
|
46
|
-
<span className="flex-1 text-left break-words">{category.label}</span>
|
|
47
|
-
|
|
48
|
-
{/* Count Badge */}
|
|
49
|
-
{category.count !== undefined && (
|
|
50
|
-
<CountBadge count={category.count} active={active} />
|
|
51
|
-
)}
|
|
52
|
-
</button>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SidebarContent Component
|
|
3
|
-
* Main content of the sidebar (logo, navigation, footer)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import type { ComponentCategory } from '../../../types';
|
|
10
|
-
import { SidebarCategory } from './SidebarCategory';
|
|
11
|
-
import { CopyAIButton } from '../Header/CopyAIButton';
|
|
12
|
-
import { TestValidationButton } from '../Header/TestValidationButton';
|
|
13
|
-
|
|
14
|
-
interface SidebarContentProps {
|
|
15
|
-
/** Available categories */
|
|
16
|
-
categories: ComponentCategory[];
|
|
17
|
-
/** Current selected category */
|
|
18
|
-
currentCategory?: string;
|
|
19
|
-
/** Category change callback */
|
|
20
|
-
onCategoryChange?: (categoryId: string) => void;
|
|
21
|
-
/** Is mobile view */
|
|
22
|
-
isMobile: boolean;
|
|
23
|
-
/** Function to generate AI context */
|
|
24
|
-
onCopyForAI?: () => string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Sidebar Content
|
|
29
|
-
* Contains logo (desktop only), navigation, and footer
|
|
30
|
-
*/
|
|
31
|
-
export function SidebarContent({
|
|
32
|
-
categories,
|
|
33
|
-
currentCategory,
|
|
34
|
-
onCategoryChange,
|
|
35
|
-
isMobile,
|
|
36
|
-
onCopyForAI,
|
|
37
|
-
}: SidebarContentProps) {
|
|
38
|
-
return (
|
|
39
|
-
<div className="flex flex-col h-full overflow-hidden">
|
|
40
|
-
|
|
41
|
-
{/* Developer Tools */}
|
|
42
|
-
{!isMobile && (
|
|
43
|
-
<div className="border-b flex-shrink-0 px-4 py-3 flex gap-2">
|
|
44
|
-
{onCopyForAI && (
|
|
45
|
-
<CopyAIButton
|
|
46
|
-
onCopyForAI={onCopyForAI}
|
|
47
|
-
size="sm"
|
|
48
|
-
showLabel
|
|
49
|
-
className="flex-1"
|
|
50
|
-
/>
|
|
51
|
-
)}
|
|
52
|
-
<TestValidationButton
|
|
53
|
-
size="sm"
|
|
54
|
-
showLabel={false}
|
|
55
|
-
/>
|
|
56
|
-
</div>
|
|
57
|
-
)}
|
|
58
|
-
|
|
59
|
-
{/* Navigation */}
|
|
60
|
-
<div className="flex-1 overflow-y-auto scrollbar-thin">
|
|
61
|
-
<div className="p-4">
|
|
62
|
-
<nav>
|
|
63
|
-
{/* Section Header */}
|
|
64
|
-
<div className="mb-4">
|
|
65
|
-
<h3 className="mb-2 px-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
66
|
-
Component Categories
|
|
67
|
-
</h3>
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
{/* Categories List */}
|
|
71
|
-
<div className="space-y-1">
|
|
72
|
-
{categories.map((category) => (
|
|
73
|
-
<SidebarCategory
|
|
74
|
-
key={category.id}
|
|
75
|
-
category={category}
|
|
76
|
-
active={currentCategory === category.id}
|
|
77
|
-
onClick={() => onCategoryChange?.(category.id)}
|
|
78
|
-
/>
|
|
79
|
-
))}
|
|
80
|
-
</div>
|
|
81
|
-
</nav>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
{/* Footer */}
|
|
86
|
-
<div className="border-t p-4 bg-muted/30">
|
|
87
|
-
<p className="text-xs text-muted-foreground">
|
|
88
|
-
Built with Tailwind CSS v4
|
|
89
|
-
</p>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SidebarFooter Component
|
|
3
|
-
* Footer section with developer tools (Copy for AI, Test Validation)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import React from 'react';
|
|
9
|
-
import { CopyAIButton } from '../Header/CopyAIButton';
|
|
10
|
-
import { TestValidationButton } from '../Header/TestValidationButton';
|
|
11
|
-
|
|
12
|
-
export interface SidebarFooterProps {
|
|
13
|
-
/** Function to generate AI context */
|
|
14
|
-
onCopyForAI?: () => string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Sidebar Footer
|
|
19
|
-
* Developer tools: Copy for AI and Test Validation buttons
|
|
20
|
-
*/
|
|
21
|
-
export function SidebarFooter({ onCopyForAI }: SidebarFooterProps) {
|
|
22
|
-
return (
|
|
23
|
-
<div className="border-t p-4 bg-muted/30 space-y-3">
|
|
24
|
-
{/* Developer Tools */}
|
|
25
|
-
<div className="flex flex-col gap-2">
|
|
26
|
-
{onCopyForAI && (
|
|
27
|
-
<CopyAIButton
|
|
28
|
-
onCopyForAI={onCopyForAI}
|
|
29
|
-
size="sm"
|
|
30
|
-
showLabel
|
|
31
|
-
className="w-full justify-start"
|
|
32
|
-
/>
|
|
33
|
-
)}
|
|
34
|
-
<TestValidationButton
|
|
35
|
-
size="sm"
|
|
36
|
-
showLabel
|
|
37
|
-
className="w-full justify-start"
|
|
38
|
-
/>
|
|
39
|
-
</div>
|
|
40
|
-
|
|
41
|
-
{/* Tailwind v4 info */}
|
|
42
|
-
<div className="pt-2 border-t border-border/50">
|
|
43
|
-
<p className="text-xs text-muted-foreground">
|
|
44
|
-
Built with Tailwind CSS v4
|
|
45
|
-
</p>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
}
|