@djangocfg/layouts 1.4.26 → 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/ContactForm.tsx +59 -12
- package/src/snippets/ContactForm/ContactPage.tsx +21 -20
- package/src/snippets/ContactForm/dynamic.tsx +55 -0
- package/src/snippets/ContactForm/index.ts +6 -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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.28",
|
|
4
4
|
"description": "Pre-built dashboard layouts, authentication pages, and admin templates for Next.js applications with Tailwind CSS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
@@ -85,9 +85,9 @@
|
|
|
85
85
|
"check": "tsc --noEmit"
|
|
86
86
|
},
|
|
87
87
|
"peerDependencies": {
|
|
88
|
-
"@djangocfg/api": "^1.4.
|
|
89
|
-
"@djangocfg/og-image": "^1.4.
|
|
90
|
-
"@djangocfg/ui": "^1.4.
|
|
88
|
+
"@djangocfg/api": "^1.4.28",
|
|
89
|
+
"@djangocfg/og-image": "^1.4.28",
|
|
90
|
+
"@djangocfg/ui": "^1.4.28",
|
|
91
91
|
"@hookform/resolvers": "^5.2.0",
|
|
92
92
|
"consola": "^3.4.2",
|
|
93
93
|
"lucide-react": "^0.468.0",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"vidstack": "0.6.15"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
|
-
"@djangocfg/typescript-config": "^1.4.
|
|
112
|
+
"@djangocfg/typescript-config": "^1.4.28",
|
|
113
113
|
"@types/node": "^24.7.2",
|
|
114
114
|
"@types/react": "19.2.2",
|
|
115
115
|
"@types/react-dom": "19.2.1",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { proxyMiddleware, proxyMiddlewareConfig } from './proxy';
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Proxy middleware for media and API endpoints
|
|
5
|
+
* Use this in your middleware.ts file
|
|
6
|
+
*/
|
|
7
|
+
export function proxyMiddleware(request: NextRequest) {
|
|
4
8
|
const { pathname, search } = request.nextUrl;
|
|
5
9
|
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
|
6
10
|
|
|
@@ -19,6 +23,10 @@ export function middleware(request: NextRequest) {
|
|
|
19
23
|
return NextResponse.next();
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Recommended matcher config for proxy middleware
|
|
28
|
+
* Add this to your middleware.ts config
|
|
29
|
+
*/
|
|
30
|
+
export const proxyMiddlewareConfig = {
|
|
23
31
|
matcher: ['/media/:path*', '/api/:path*'],
|
|
24
32
|
};
|
package/src/layouts/index.ts
CHANGED
|
@@ -9,8 +9,5 @@ export * from './PaymentsLayout';
|
|
|
9
9
|
export * from './AppLayout';
|
|
10
10
|
export * from './ErrorLayout';
|
|
11
11
|
|
|
12
|
-
// UILayout - Config-driven UI documentation layout
|
|
13
|
-
export * from './UILayout';
|
|
14
|
-
|
|
15
12
|
// Note: CfgLayout is now part of AppLayout exports
|
|
16
13
|
// Import it via: import { CfgLayout, useCfgApp } from '@djangocfg/layouts';
|
|
@@ -58,17 +58,17 @@ export interface ContactFormProps {
|
|
|
58
58
|
// ============================================================================
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
61
|
+
* ContactFormBase - Contact form using Django-CFG Lead API
|
|
62
|
+
*
|
|
63
|
+
* NOTE: Use ContactForm from index.ts which is dynamically imported (ssr: false)
|
|
62
64
|
*
|
|
63
65
|
* @example
|
|
64
66
|
* ```tsx
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* onSuccess={(result) => console.log('Lead ID:', result.lead_id)}
|
|
68
|
-
* />
|
|
67
|
+
* import { ContactForm } from '@djangocfg/layouts';
|
|
68
|
+
* <ContactForm apiUrl="https://api.example.com" />
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
|
-
export function
|
|
71
|
+
export function ContactFormBase({ apiUrl, ...props }: ContactFormProps) {
|
|
72
72
|
return (
|
|
73
73
|
<ContactFormProvider apiUrl={apiUrl}>
|
|
74
74
|
<ContactFormInner {...props} />
|
|
@@ -112,20 +112,42 @@ function ContactFormInner({
|
|
|
112
112
|
const t = { ...DEFAULT_FORM_TEXTS, ...texts };
|
|
113
113
|
const [draft, setDraft, clearDraft] = useLocalStorage<FormDraft>(STORAGE_KEY, emptyDraft);
|
|
114
114
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
115
|
+
const [isHydrated, setIsHydrated] = useState(false);
|
|
115
116
|
|
|
116
117
|
const form = useForm<FormData>({
|
|
117
118
|
resolver: zodResolver(Schemas.LeadSubmissionRequestSchema),
|
|
119
|
+
// Start with empty values to match SSR
|
|
118
120
|
defaultValues: {
|
|
119
121
|
...emptyDraft,
|
|
120
|
-
|
|
121
|
-
site_url: typeof window !== 'undefined' ? window.location.href : '',
|
|
122
|
+
site_url: '',
|
|
122
123
|
},
|
|
123
124
|
});
|
|
124
125
|
|
|
126
|
+
// Hydrate form with localStorage draft and site_url after mount
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (isHydrated) return;
|
|
129
|
+
setIsHydrated(true);
|
|
130
|
+
|
|
131
|
+
// Apply draft from localStorage and set site_url
|
|
132
|
+
const currentValues = form.getValues();
|
|
133
|
+
const hasDraftData = draft.name || draft.email || draft.company || draft.subject || draft.message;
|
|
134
|
+
|
|
135
|
+
if (hasDraftData || !currentValues.site_url) {
|
|
136
|
+
form.reset({
|
|
137
|
+
...emptyDraft,
|
|
138
|
+
...draft,
|
|
139
|
+
site_url: window.location.href,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}, [draft, form, isHydrated]);
|
|
143
|
+
|
|
125
144
|
// Watch form values and save to localStorage
|
|
126
145
|
const watchedValues = useWatch({ control: form.control });
|
|
127
146
|
|
|
128
147
|
useEffect(() => {
|
|
148
|
+
// Only save to localStorage after hydration to avoid unnecessary writes
|
|
149
|
+
if (!isHydrated) return;
|
|
150
|
+
|
|
129
151
|
const { name, email, company, subject, message } = watchedValues;
|
|
130
152
|
if (name || email || company || subject || message) {
|
|
131
153
|
setDraft({
|
|
@@ -136,14 +158,30 @@ function ContactFormInner({
|
|
|
136
158
|
message: message || '',
|
|
137
159
|
});
|
|
138
160
|
}
|
|
139
|
-
}, [watchedValues, setDraft]);
|
|
161
|
+
}, [watchedValues, setDraft, isHydrated]);
|
|
140
162
|
|
|
141
163
|
const handleSubmit = async (data: FormData) => {
|
|
142
164
|
try {
|
|
143
165
|
const result = await submit(data);
|
|
144
166
|
if (resetOnSuccess) {
|
|
145
|
-
|
|
146
|
-
|
|
167
|
+
// Keep contact info (name, email, company), clear only message content
|
|
168
|
+
const currentValues = form.getValues();
|
|
169
|
+
form.reset({
|
|
170
|
+
name: currentValues.name,
|
|
171
|
+
email: currentValues.email,
|
|
172
|
+
company: currentValues.company,
|
|
173
|
+
subject: '',
|
|
174
|
+
message: '',
|
|
175
|
+
site_url: currentValues.site_url,
|
|
176
|
+
});
|
|
177
|
+
// Update draft to keep contact info
|
|
178
|
+
setDraft({
|
|
179
|
+
name: currentValues.name || '',
|
|
180
|
+
email: currentValues.email || '',
|
|
181
|
+
company: currentValues.company || '',
|
|
182
|
+
subject: '',
|
|
183
|
+
message: '',
|
|
184
|
+
});
|
|
147
185
|
}
|
|
148
186
|
setIsSuccess(true);
|
|
149
187
|
onSuccess?.(result);
|
|
@@ -156,7 +194,16 @@ function ContactFormInner({
|
|
|
156
194
|
|
|
157
195
|
const handleReset = () => {
|
|
158
196
|
setIsSuccess(false);
|
|
159
|
-
form
|
|
197
|
+
// Keep contact info when returning to form
|
|
198
|
+
const currentDraft = draft;
|
|
199
|
+
form.reset({
|
|
200
|
+
name: currentDraft.name,
|
|
201
|
+
email: currentDraft.email,
|
|
202
|
+
company: currentDraft.company,
|
|
203
|
+
subject: '',
|
|
204
|
+
message: '',
|
|
205
|
+
site_url: typeof window !== 'undefined' ? window.location.href : '',
|
|
206
|
+
});
|
|
160
207
|
};
|
|
161
208
|
|
|
162
209
|
// Success state
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Mail, MapPin, Calendar } from 'lucide-react';
|
|
5
|
-
import { ContactForm } from './ContactForm';
|
|
4
|
+
import { Mail, MapPin, Calendar, MessageCircle } from 'lucide-react';
|
|
5
|
+
import { ContactFormBase as ContactForm } from './ContactForm';
|
|
6
6
|
import { ContactInfo } from './ContactInfo';
|
|
7
7
|
import type { ContactDetail, LeadSubmissionResult } from './types';
|
|
8
8
|
|
|
@@ -15,6 +15,7 @@ const isDev = process.env.NODE_ENV === 'development';
|
|
|
15
15
|
const DEFAULT_CONFIG = {
|
|
16
16
|
apiUrl: isDev ? 'http://localhost:8000' : 'https://api.reforms.ai',
|
|
17
17
|
email: 'markolofsen@gmail.com',
|
|
18
|
+
telegram: '+62 813 39646301',
|
|
18
19
|
calendly: 'https://calendly.com/markolofsen/meeting',
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -27,6 +28,8 @@ export interface ContactPageProps {
|
|
|
27
28
|
apiUrl?: string;
|
|
28
29
|
/** Override email */
|
|
29
30
|
email?: string;
|
|
31
|
+
/** Override telegram */
|
|
32
|
+
telegram?: string;
|
|
30
33
|
/** Override calendly link */
|
|
31
34
|
calendlyUrl?: string;
|
|
32
35
|
/** Page title */
|
|
@@ -48,31 +51,20 @@ export interface ContactPageProps {
|
|
|
48
51
|
// ============================================================================
|
|
49
52
|
|
|
50
53
|
/**
|
|
51
|
-
*
|
|
54
|
+
* ContactPageBase - Pre-configured contact page component
|
|
52
55
|
*
|
|
53
|
-
*
|
|
56
|
+
* NOTE: Use ContactPage from index.ts which is dynamically imported (ssr: false)
|
|
54
57
|
*
|
|
55
58
|
* @example
|
|
56
59
|
* ```tsx
|
|
57
|
-
*
|
|
60
|
+
* import { ContactPage } from '@djangocfg/layouts';
|
|
58
61
|
* <ContactPage />
|
|
59
|
-
*
|
|
60
|
-
* // With custom title
|
|
61
|
-
* <ContactPage
|
|
62
|
-
* title={<>Contact <span className="text-primary">Us</span></>}
|
|
63
|
-
* subtitle="We'd love to hear from you"
|
|
64
|
-
* />
|
|
65
|
-
*
|
|
66
|
-
* // Override defaults
|
|
67
|
-
* <ContactPage
|
|
68
|
-
* apiUrl="https://api.myproject.com"
|
|
69
|
-
* email="hello@myproject.com"
|
|
70
|
-
* />
|
|
71
62
|
* ```
|
|
72
63
|
*/
|
|
73
|
-
export function
|
|
64
|
+
export function ContactPageBase({
|
|
74
65
|
apiUrl = DEFAULT_CONFIG.apiUrl,
|
|
75
66
|
email = DEFAULT_CONFIG.email,
|
|
67
|
+
telegram = DEFAULT_CONFIG.telegram,
|
|
76
68
|
calendlyUrl = DEFAULT_CONFIG.calendly,
|
|
77
69
|
title = 'Get in Touch',
|
|
78
70
|
subtitle = "Have a question or want to work together? We'd love to hear from you.",
|
|
@@ -81,6 +73,9 @@ export function ContactPage({
|
|
|
81
73
|
className,
|
|
82
74
|
onSuccess,
|
|
83
75
|
}: ContactPageProps) {
|
|
76
|
+
// Format phone for tel: link (remove spaces)
|
|
77
|
+
const telegramPhone = telegram.replace(/\s/g, '');
|
|
78
|
+
|
|
84
79
|
const contactDetails: ContactDetail[] = [
|
|
85
80
|
{
|
|
86
81
|
icon: <Mail className="h-5 w-5" />,
|
|
@@ -88,6 +83,12 @@ export function ContactPage({
|
|
|
88
83
|
value: email,
|
|
89
84
|
href: `mailto:${email}`,
|
|
90
85
|
},
|
|
86
|
+
{
|
|
87
|
+
icon: <MessageCircle className="h-5 w-5" />,
|
|
88
|
+
label: 'Telegram',
|
|
89
|
+
value: telegram,
|
|
90
|
+
href: `https://t.me/${telegramPhone}`,
|
|
91
|
+
},
|
|
91
92
|
{
|
|
92
93
|
icon: <MapPin className="h-5 w-5" />,
|
|
93
94
|
label: 'Location',
|
|
@@ -108,8 +109,8 @@ export function ContactPage({
|
|
|
108
109
|
</div>
|
|
109
110
|
|
|
110
111
|
{/* Content Grid */}
|
|
111
|
-
<div className="grid grid-cols-1 lg:grid-cols-
|
|
112
|
-
<div
|
|
112
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 w-full">
|
|
113
|
+
<div>
|
|
113
114
|
<ContactForm apiUrl={apiUrl} onSuccess={onSuccess} />
|
|
114
115
|
</div>
|
|
115
116
|
<div>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic ContactForm components (ssr: false)
|
|
3
|
+
*
|
|
4
|
+
* Avoids hydration mismatch when using localStorage for form drafts.
|
|
5
|
+
* Components are loaded client-side only.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use client';
|
|
9
|
+
|
|
10
|
+
import dynamic from 'next/dynamic';
|
|
11
|
+
import { Skeleton } from '@djangocfg/ui';
|
|
12
|
+
import type { ContactFormProps } from './ContactForm';
|
|
13
|
+
import type { ContactPageProps } from './ContactPage';
|
|
14
|
+
|
|
15
|
+
function ContactFormSkeleton() {
|
|
16
|
+
return (
|
|
17
|
+
<div className="space-y-4">
|
|
18
|
+
<Skeleton className="w-full h-10" />
|
|
19
|
+
<Skeleton className="w-full h-10" />
|
|
20
|
+
<Skeleton className="w-full h-10" />
|
|
21
|
+
<Skeleton className="w-full h-24" />
|
|
22
|
+
<Skeleton className="w-full h-10" />
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function ContactPageSkeleton() {
|
|
28
|
+
return (
|
|
29
|
+
<div>
|
|
30
|
+
<div className="text-center mb-8 md:mb-12">
|
|
31
|
+
<Skeleton className="w-64 h-12 mx-auto mb-4" />
|
|
32
|
+
<Skeleton className="w-96 h-6 mx-auto" />
|
|
33
|
+
</div>
|
|
34
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
35
|
+
<div className="lg:col-span-2">
|
|
36
|
+
<ContactFormSkeleton />
|
|
37
|
+
</div>
|
|
38
|
+
<div className="space-y-4">
|
|
39
|
+
<Skeleton className="w-full h-32" />
|
|
40
|
+
<Skeleton className="w-full h-24" />
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const ContactForm = dynamic<ContactFormProps>(
|
|
48
|
+
() => import('./ContactForm').then((mod) => mod.ContactFormBase),
|
|
49
|
+
{ ssr: false, loading: () => <ContactFormSkeleton /> }
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export const ContactPage = dynamic<ContactPageProps>(
|
|
53
|
+
() => import('./ContactPage').then((mod) => mod.ContactPageBase),
|
|
54
|
+
{ ssr: false, loading: () => <ContactPageSkeleton /> }
|
|
55
|
+
);
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
// ContactForm - Contact form using Django-CFG Lead API
|
|
3
3
|
// ============================================================================
|
|
4
4
|
|
|
5
|
-
// Components
|
|
6
|
-
export { ContactForm,
|
|
5
|
+
// Components (dynamic import, ssr: false - no hydration issues)
|
|
6
|
+
export { ContactForm, ContactPage } from './dynamic';
|
|
7
7
|
export { ContactInfo } from './ContactInfo';
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
// Types
|
|
10
|
+
export type { ContactFormProps } from './ContactForm';
|
|
11
|
+
export type { ContactPageProps } from './ContactPage';
|
|
9
12
|
|
|
10
13
|
// Provider & Hooks
|
|
11
14
|
export {
|
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
# UILayout - Config-Driven UI Documentation System
|
|
2
|
-
|
|
3
|
-
Modern, type-safe layout system for showcasing UI component libraries with built-in "Copy for AI" functionality.
|
|
4
|
-
|
|
5
|
-
## 🎯 Key Features
|
|
6
|
-
|
|
7
|
-
- **Config-Driven**: Single source of truth for all component documentation
|
|
8
|
-
- **Type-Safe**: Full TypeScript support with strict typing
|
|
9
|
-
- **Auto-Rendering**: Components automatically rendered from config
|
|
10
|
-
- **AI-Ready**: One-click export of entire documentation for AI consumption
|
|
11
|
-
- **Responsive**: Mobile-first design with adaptive sidebar
|
|
12
|
-
- **Dark Mode**: Built-in theme support
|
|
13
|
-
- **Organized**: Modular config structure by category
|
|
14
|
-
|
|
15
|
-
## 📁 Structure
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
UILayout/
|
|
19
|
-
├── config/ # All configuration (Single Source of Truth)
|
|
20
|
-
│ ├── components/ # Component configs by category
|
|
21
|
-
│ │ ├── forms.config.tsx # 8 form components
|
|
22
|
-
│ │ ├── layout.config.tsx # 5 layout components
|
|
23
|
-
│ │ ├── navigation.config.tsx # 4 navigation components
|
|
24
|
-
│ │ ├── overlay.config.tsx # 11 overlay components
|
|
25
|
-
│ │ ├── feedback.config.tsx # 5 feedback components
|
|
26
|
-
│ │ ├── data.config.tsx # 5 data display components
|
|
27
|
-
│ │ ├── specialized.config.tsx # 2 specialized components
|
|
28
|
-
│ │ ├── blocks.config.tsx # 7 landing page blocks
|
|
29
|
-
│ │ ├── hooks.config.tsx # 6 custom hooks
|
|
30
|
-
│ │ └── index.ts # Aggregates all configs
|
|
31
|
-
│ ├── categories.config.tsx # Category definitions
|
|
32
|
-
│ ├── tailwind.config.ts # Tailwind 4 guidelines
|
|
33
|
-
│ ├── ai-export.config.ts # AI context generator
|
|
34
|
-
│ └── index.ts # Main config exports
|
|
35
|
-
├── components/ # React components
|
|
36
|
-
│ ├── AutoComponentDemo.tsx # Auto-renders from config
|
|
37
|
-
│ ├── CategoryRenderer.tsx # Renders entire category
|
|
38
|
-
│ ├── TailwindGuideRenderer.tsx # Renders Tailwind guide
|
|
39
|
-
│ ├── Header.tsx # Header with "Copy for AI"
|
|
40
|
-
│ ├── Sidebar.tsx
|
|
41
|
-
│ └── MobileOverlay.tsx
|
|
42
|
-
├── context/ # React Context
|
|
43
|
-
│ └── ShowcaseContext.tsx # Navigation state management
|
|
44
|
-
├── UILayout.tsx # Main layout component
|
|
45
|
-
├── UIGuideView.tsx # Complete UI guide view
|
|
46
|
-
├── UIGuideLanding.tsx # Landing page
|
|
47
|
-
└── UIGuideApp.tsx # Full app wrapper
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## 🚀 Quick Start
|
|
51
|
-
|
|
52
|
-
### Basic Usage
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
import { UILayout, CATEGORIES } from '@djangocfg/layouts';
|
|
56
|
-
|
|
57
|
-
function MyComponentGuide() {
|
|
58
|
-
const [category, setCategory] = useState('forms');
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<UILayout
|
|
62
|
-
title="My Component Library"
|
|
63
|
-
categories={CATEGORIES}
|
|
64
|
-
currentCategory={category}
|
|
65
|
-
onCategoryChange={setCategory}
|
|
66
|
-
>
|
|
67
|
-
{/* Your content */}
|
|
68
|
-
</UILayout>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Using Category Renderer (Auto-render from config)
|
|
74
|
-
|
|
75
|
-
```tsx
|
|
76
|
-
import { UILayout, CATEGORIES, CategoryRenderer } from '@djangocfg/layouts';
|
|
77
|
-
|
|
78
|
-
function MyComponentGuide() {
|
|
79
|
-
const [category, setCategory] = useState('forms');
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<UILayout
|
|
83
|
-
title="My Component Library"
|
|
84
|
-
categories={CATEGORIES}
|
|
85
|
-
currentCategory={category}
|
|
86
|
-
onCategoryChange={setCategory}
|
|
87
|
-
>
|
|
88
|
-
{/* Automatically renders all components in category */}
|
|
89
|
-
<CategoryRenderer categoryId={category} />
|
|
90
|
-
</UILayout>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Using Complete UI Guide
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
import { UIGuideApp } from '@djangocfg/layouts';
|
|
99
|
-
|
|
100
|
-
// Complete pre-configured UI guide with all components
|
|
101
|
-
export default function Page() {
|
|
102
|
-
return <UIGuideApp />;
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## 📝 Adding New Components
|
|
107
|
-
|
|
108
|
-
### 1. Add to Config
|
|
109
|
-
|
|
110
|
-
Edit the appropriate config file in `config/components/`:
|
|
111
|
-
|
|
112
|
-
```tsx
|
|
113
|
-
// config/components/forms.config.tsx
|
|
114
|
-
export const FORM_COMPONENTS: ComponentConfig[] = [
|
|
115
|
-
{
|
|
116
|
-
name: 'MyComponent',
|
|
117
|
-
category: 'forms',
|
|
118
|
-
description: 'A custom form component',
|
|
119
|
-
importPath: "import { MyComponent } from '@mylib/ui';",
|
|
120
|
-
example: `<MyComponent value="test" onChange={handler} />`,
|
|
121
|
-
preview: <MyComponent value="test" onChange={() => {}} />
|
|
122
|
-
},
|
|
123
|
-
// ... other components
|
|
124
|
-
];
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### 2. That's It!
|
|
128
|
-
|
|
129
|
-
No need to:
|
|
130
|
-
- ❌ Create separate demo files
|
|
131
|
-
- ❌ Write duplicate rendering code
|
|
132
|
-
- ❌ Update multiple places
|
|
133
|
-
|
|
134
|
-
The component automatically:
|
|
135
|
-
- ✅ Appears in the UI guide
|
|
136
|
-
- ✅ Gets included in "Copy for AI"
|
|
137
|
-
- ✅ Shows in the category with proper formatting
|
|
138
|
-
|
|
139
|
-
## 🤖 Copy for AI Feature
|
|
140
|
-
|
|
141
|
-
Click the "Copy for AI" button in the header to export entire documentation including:
|
|
142
|
-
|
|
143
|
-
- ✅ Tailwind CSS v4 guidelines and best practices
|
|
144
|
-
- ✅ All 53 components with full examples
|
|
145
|
-
- ✅ Import statements
|
|
146
|
-
- ✅ Usage examples
|
|
147
|
-
- ✅ Properly formatted for AI consumption
|
|
148
|
-
|
|
149
|
-
Perfect for giving AI assistants complete context about your UI library!
|
|
150
|
-
|
|
151
|
-
## 📊 Component Statistics
|
|
152
|
-
|
|
153
|
-
| Category | Components |
|
|
154
|
-
|----------|------------|
|
|
155
|
-
| Forms | 8 |
|
|
156
|
-
| Layout | 5 |
|
|
157
|
-
| Navigation | 4 |
|
|
158
|
-
| Overlay | 11 |
|
|
159
|
-
| Feedback | 5 |
|
|
160
|
-
| Data Display | 5 |
|
|
161
|
-
| Specialized | 2 |
|
|
162
|
-
| Blocks | 7 |
|
|
163
|
-
| Hooks | 6 |
|
|
164
|
-
| **Total** | **53** |
|
|
165
|
-
|
|
166
|
-
## 🎨 Customization
|
|
167
|
-
|
|
168
|
-
### Custom Categories
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
import type { ComponentCategory } from '@djangocfg/layouts';
|
|
172
|
-
|
|
173
|
-
const customCategories: ComponentCategory[] = [
|
|
174
|
-
{
|
|
175
|
-
id: 'custom',
|
|
176
|
-
label: 'My Category',
|
|
177
|
-
icon: <MyIcon />,
|
|
178
|
-
count: 5,
|
|
179
|
-
description: 'Custom component category'
|
|
180
|
-
}
|
|
181
|
-
];
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### Custom Config
|
|
185
|
-
|
|
186
|
-
```tsx
|
|
187
|
-
import type { ComponentConfig } from '@djangocfg/layouts';
|
|
188
|
-
|
|
189
|
-
const customComponents: ComponentConfig[] = [
|
|
190
|
-
{
|
|
191
|
-
name: 'Component',
|
|
192
|
-
category: 'custom',
|
|
193
|
-
description: '...',
|
|
194
|
-
importPath: '...',
|
|
195
|
-
example: '...',
|
|
196
|
-
preview: <Component />
|
|
197
|
-
}
|
|
198
|
-
];
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## 🔧 API Reference
|
|
202
|
-
|
|
203
|
-
### UILayout Props
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
interface UILayoutProps {
|
|
207
|
-
children: ReactNode;
|
|
208
|
-
title?: string;
|
|
209
|
-
description?: string;
|
|
210
|
-
categories: ComponentCategory[];
|
|
211
|
-
currentCategory?: string;
|
|
212
|
-
onCategoryChange?: (categoryId: string) => void;
|
|
213
|
-
logo?: ReactNode;
|
|
214
|
-
projectName?: string;
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
### ComponentConfig Type
|
|
219
|
-
|
|
220
|
-
```typescript
|
|
221
|
-
interface ComponentConfig {
|
|
222
|
-
name: string; // Component name
|
|
223
|
-
category: string; // Category ID
|
|
224
|
-
description: string; // Short description
|
|
225
|
-
importPath: string; // Import statement
|
|
226
|
-
example: string; // Code example
|
|
227
|
-
preview: ReactNode; // Live preview component
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Available Exports
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
// Components
|
|
235
|
-
export { UILayout, CategoryRenderer, AutoComponentDemo };
|
|
236
|
-
|
|
237
|
-
// Views
|
|
238
|
-
export { UIGuideView, UIGuideApp, UIGuideLanding };
|
|
239
|
-
|
|
240
|
-
// Config
|
|
241
|
-
export {
|
|
242
|
-
CATEGORIES,
|
|
243
|
-
COMPONENTS_CONFIG,
|
|
244
|
-
generateAIContext
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// Types
|
|
248
|
-
export type {
|
|
249
|
-
ComponentConfig,
|
|
250
|
-
ComponentCategory,
|
|
251
|
-
UILayoutProps
|
|
252
|
-
};
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
## 🎯 Benefits
|
|
256
|
-
|
|
257
|
-
1. **Single Source of Truth**: All component data in one place
|
|
258
|
-
2. **Zero Duplication**: Write component config once, use everywhere
|
|
259
|
-
3. **Type-Safe**: Full TypeScript support prevents errors
|
|
260
|
-
4. **Auto-Update**: Add to config, automatically appears everywhere
|
|
261
|
-
5. **AI-Friendly**: Built-in export for AI assistants
|
|
262
|
-
6. **Maintainable**: Easy to update and extend
|
|
263
|
-
7. **Scalable**: Add categories and components effortlessly
|
|
264
|
-
|
|
265
|
-
## 📖 More Info
|
|
266
|
-
|
|
267
|
-
See the main package README for complete documentation and examples.
|