@djangocfg/layouts 1.4.30 → 2.0.2
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/README.md +277 -18
- package/package.json +15 -24
- package/src/auth/context/AuthContext.tsx +5 -5
- package/src/auth/hooks/useAuthGuard.ts +1 -1
- package/src/auth/hooks/useAutoAuth.ts +8 -7
- package/src/components/ErrorBoundary.tsx +78 -0
- package/src/components/JsonLd.tsx +31 -0
- package/src/components/LucideIcon.tsx +91 -0
- package/src/components/PageProgress.tsx +127 -0
- package/src/components/Suspense.tsx +29 -0
- package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
- package/src/components/index.ts +10 -0
- package/src/index.ts +25 -7
- package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
- package/src/layouts/AdminLayout/index.ts +7 -0
- package/src/layouts/AppLayout/AppLayout.tsx +278 -326
- package/src/layouts/AppLayout/index.ts +2 -39
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
- package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
- package/src/layouts/AuthLayout/index.ts +24 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
- package/src/layouts/PrivateLayout/components/index.ts +8 -0
- package/src/layouts/PrivateLayout/index.ts +7 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
- package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
- package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
- package/src/layouts/PublicLayout/components/index.ts +8 -0
- package/src/layouts/PublicLayout/index.ts +7 -0
- package/src/layouts/_components/UserMenu.tsx +160 -0
- package/src/layouts/_components/index.ts +7 -0
- package/src/layouts/index.ts +15 -8
- package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
- package/src/snippets/Analytics/useAnalytics.ts +11 -21
- package/src/snippets/Chat/ChatWidget.tsx +4 -4
- package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
- package/src/snippets/ContactForm/ContactPage.tsx +2 -4
- package/src/snippets/ContactForm/types.ts +3 -2
- package/src/snippets/index.ts +0 -1
- package/src/layouts/AppLayout/README.md +0 -204
- package/src/layouts/AppLayout/SUMMARY.md +0 -240
- package/src/layouts/AppLayout/USAGE.md +0 -312
- package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
- package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
- package/src/layouts/AppLayout/components/Seo.tsx +0 -171
- package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
- package/src/layouts/AppLayout/components/index.ts +0 -11
- package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
- package/src/layouts/AppLayout/context/index.ts +0 -5
- package/src/layouts/AppLayout/hooks/index.ts +0 -8
- package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
- package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
- package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
- package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
- package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
- package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
- package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
- package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
- package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
- package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
- package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
- package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
- package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/index.ts +0 -7
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
- package/src/layouts/AppLayout/providers/index.ts +0 -5
- package/src/layouts/AppLayout/types/config.ts +0 -79
- package/src/layouts/AppLayout/types/index.ts +0 -11
- package/src/layouts/AppLayout/types/layout.ts +0 -54
- package/src/layouts/AppLayout/types/navigation.ts +0 -43
- package/src/layouts/AppLayout/types/page.ts +0 -80
- package/src/layouts/AppLayout/types/routes.ts +0 -43
- package/src/layouts/AppLayout/utils/index.ts +0 -5
- package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
- package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
- package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
- package/src/layouts/ErrorLayout/index.ts +0 -8
- package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
- package/src/layouts/SimpleLayout/index.ts +0 -3
- package/src/snippets/VideoPlayer/README.md +0 -238
- package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
- package/src/snippets/VideoPlayer/index.ts +0 -8
- package/src/snippets/VideoPlayer/types.ts +0 -61
- package/src/types/index.ts +0 -2
- package/src/types/pageConfig.ts +0 -100
- /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
- /package/src/{layouts/AppLayout/components → components}/UpdateNotifier/index.ts +0 -0
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
# AdminLayout - Django CFG Layout with iframe Integration
|
|
2
|
-
|
|
3
|
-
Universal layout component for Django CFG applications that handles iframe embedding, theme synchronization, and authentication communication.
|
|
4
|
-
|
|
5
|
-
**Note:** AdminLayout is now integrated into AppLayout. For most use cases, use `AppLayout` with `isCfgAdmin={true}` instead of using AdminLayout directly.
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
- 🖼️ **iframe Embedding Detection** - Automatically detects when running inside an iframe
|
|
10
|
-
- 🎨 **Theme Synchronization** - Syncs theme from Django Unfold parent window
|
|
11
|
-
- 🔐 **Auth Token Passing** - Receives JWT tokens from parent window via postMessage
|
|
12
|
-
- 📤 **Auth Status Reporting** - Sends authentication status to parent window
|
|
13
|
-
- 📏 **Auto Resize** - Automatically adjusts iframe height based on content
|
|
14
|
-
- 🔒 **Secure Communication** - Origin validation for postMessage security
|
|
15
|
-
- ⚡ **Lightweight** - Minimal overhead, works with any layout system
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
Already included in `@djangocfg/layouts` package.
|
|
20
|
-
|
|
21
|
-
## Recommended Usage (via AppLayout)
|
|
22
|
-
|
|
23
|
-
### Simple - Enable Django CFG Admin Mode
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
// apps/admin/src/pages/_app.tsx
|
|
27
|
-
import { AppLayout } from '@djangocfg/layouts';
|
|
28
|
-
|
|
29
|
-
function MyApp({ Component, pageProps }) {
|
|
30
|
-
return (
|
|
31
|
-
<AppProviders>
|
|
32
|
-
<AppLayout config={appLayoutConfig} isCfgAdmin={true}>
|
|
33
|
-
<Component {...pageProps} />
|
|
34
|
-
</AppLayout>
|
|
35
|
-
</AppProviders>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**That's it!** Setting `isCfgAdmin={true}` automatically:
|
|
41
|
-
- Wraps your app with AdminLayout
|
|
42
|
-
- Sets up iframe communication
|
|
43
|
-
- Handles auth token passing
|
|
44
|
-
- Syncs theme from parent window
|
|
45
|
-
|
|
46
|
-
## Direct Usage (Advanced)
|
|
47
|
-
|
|
48
|
-
If you need more control, you can use AdminLayout directly:
|
|
49
|
-
|
|
50
|
-
### 1. Wrap your app with AdminLayout (Zero Config!)
|
|
51
|
-
|
|
52
|
-
```tsx
|
|
53
|
-
// apps/admin/src/pages/_app.tsx
|
|
54
|
-
import { AdminLayout, AppLayout } from '@djangocfg/layouts';
|
|
55
|
-
|
|
56
|
-
function MyApp({ Component, pageProps }) {
|
|
57
|
-
return (
|
|
58
|
-
<AdminLayout>
|
|
59
|
-
<AppLayout config={appLayoutConfig}>
|
|
60
|
-
<Component {...pageProps} />
|
|
61
|
-
</AppLayout>
|
|
62
|
-
</AdminLayout>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**That's it!** Auth tokens are automatically set in the API client when received from parent window.
|
|
68
|
-
|
|
69
|
-
### 2. With custom auth handler (optional)
|
|
70
|
-
|
|
71
|
-
```tsx
|
|
72
|
-
// Only if you need additional logic when tokens are received
|
|
73
|
-
<AdminLayout
|
|
74
|
-
config={{
|
|
75
|
-
onAuthTokenReceived: (authToken, refreshToken) => {
|
|
76
|
-
// Tokens are already set in API client automatically
|
|
77
|
-
console.log('Tokens received!');
|
|
78
|
-
// Your custom logic here
|
|
79
|
-
}
|
|
80
|
-
}}
|
|
81
|
-
>
|
|
82
|
-
<AppLayout config={appLayoutConfig}>
|
|
83
|
-
<Component {...pageProps} />
|
|
84
|
-
</AppLayout>
|
|
85
|
-
</AdminLayout>
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Hook Usage
|
|
89
|
-
|
|
90
|
-
### Use useCfgApp hook to access embedding state
|
|
91
|
-
|
|
92
|
-
```tsx
|
|
93
|
-
import { useCfgApp } from '@djangocfg/layouts';
|
|
94
|
-
|
|
95
|
-
function MyComponent() {
|
|
96
|
-
const { isEmbedded, disableLayout, parentTheme } = useCfgApp();
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<div>
|
|
100
|
-
{isEmbedded && <p>Running in iframe mode</p>}
|
|
101
|
-
<p>Current theme from parent: {parentTheme}</p>
|
|
102
|
-
</div>
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Use with AppLayout's disableLayout prop
|
|
108
|
-
|
|
109
|
-
```tsx
|
|
110
|
-
import { AppLayout, useCfgApp } from '@djangocfg/layouts';
|
|
111
|
-
|
|
112
|
-
function MyApp({ Component, pageProps }) {
|
|
113
|
-
const { disableLayout } = useCfgApp();
|
|
114
|
-
|
|
115
|
-
return (
|
|
116
|
-
<AppLayout
|
|
117
|
-
config={config}
|
|
118
|
-
disableLayout={disableLayout}
|
|
119
|
-
isCfgAdmin={true}
|
|
120
|
-
>
|
|
121
|
-
<Component {...pageProps} />
|
|
122
|
-
</AppLayout>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## API Reference
|
|
128
|
-
|
|
129
|
-
### AppLayout with isCfgAdmin
|
|
130
|
-
|
|
131
|
-
**Recommended approach:**
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
<AppLayout
|
|
135
|
-
config={appLayoutConfig}
|
|
136
|
-
isCfgAdmin={true} // ← Enables Django CFG admin mode
|
|
137
|
-
fontFamily={manrope.style.fontFamily}
|
|
138
|
-
>
|
|
139
|
-
{children}
|
|
140
|
-
</AppLayout>
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### AdminLayout Props
|
|
144
|
-
|
|
145
|
-
| Prop | Type | Default | Description |
|
|
146
|
-
|------|------|---------|-------------|
|
|
147
|
-
| `children` | `ReactNode` | Required | Your app content |
|
|
148
|
-
| `config` | `AdminLayoutConfig` | `{}` | Configuration object |
|
|
149
|
-
| `enableParentSync` | `boolean` | `true` | Enable automatic theme/auth sync |
|
|
150
|
-
|
|
151
|
-
### AdminLayoutConfig
|
|
152
|
-
|
|
153
|
-
**All options are optional!** AdminLayout works with zero configuration.
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
interface AdminLayoutConfig {
|
|
157
|
-
// Optional: Called when auth tokens are received from parent
|
|
158
|
-
// Note: Tokens are ALWAYS automatically set in API client
|
|
159
|
-
// Use this only if you need additional custom logic
|
|
160
|
-
onAuthTokenReceived?: (authToken, refreshToken?) => void;
|
|
161
|
-
|
|
162
|
-
// Enable/disable theme sync (default: true)
|
|
163
|
-
enableThemeSync?: boolean;
|
|
164
|
-
|
|
165
|
-
// Enable/disable auth status sync (default: true)
|
|
166
|
-
enableAuthStatusSync?: boolean;
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### useCfgApp Hook
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
const {
|
|
174
|
-
isEmbedded, // true if running in iframe
|
|
175
|
-
isStandalone, // true if running as PWA
|
|
176
|
-
disableLayout, // true if layout should be disabled
|
|
177
|
-
referrer, // parent URL if embedded
|
|
178
|
-
isMounted, // true after client-side mount
|
|
179
|
-
parentTheme, // 'light' | 'dark' | null
|
|
180
|
-
parentThemeMode, // 'auto' | 'light' | 'dark' | null
|
|
181
|
-
} = useCfgApp({
|
|
182
|
-
onAuthTokenReceived: (authToken, refreshToken) => {
|
|
183
|
-
// Handle tokens (optional - tokens are auto-set in API client)
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**Note:** `useApp` is also exported as an alias for backward compatibility.
|
|
189
|
-
|
|
190
|
-
## postMessage Communication Protocol
|
|
191
|
-
|
|
192
|
-
### Parent → iframe
|
|
193
|
-
|
|
194
|
-
| Message Type | Data | Description |
|
|
195
|
-
|-------------|------|-------------|
|
|
196
|
-
| `parent-theme` | `{ theme: 'dark'\|'light', themeMode: 'auto'\|'dark'\|'light' }` | Theme from Django Unfold |
|
|
197
|
-
| `parent-auth` | `{ authToken, refreshToken }` | JWT authentication tokens |
|
|
198
|
-
|
|
199
|
-
### iframe → Parent
|
|
200
|
-
|
|
201
|
-
| Message Type | Data | Description |
|
|
202
|
-
|-------------|------|-------------|
|
|
203
|
-
| `iframe-ready` | `{ url, referrer }` | iframe loaded successfully |
|
|
204
|
-
| `iframe-auth-status` | `{ isAuthenticated, isLoading, hasUser }` | Auth status update (sent on auth state change) |
|
|
205
|
-
| `iframe-resize` | `{ height }` | Content height changed (sent on ResizeObserver trigger) |
|
|
206
|
-
| `iframe-navigation` | `{ path }` | User navigated to new page (sent via Next.js Router.events) |
|
|
207
|
-
|
|
208
|
-
## Advanced Usage
|
|
209
|
-
|
|
210
|
-
### Manual Theme Sync
|
|
211
|
-
|
|
212
|
-
```tsx
|
|
213
|
-
import { useCfgApp } from '@djangocfg/layouts';
|
|
214
|
-
import { useThemeContext } from '@djangocfg/ui';
|
|
215
|
-
|
|
216
|
-
function MyThemeSync() {
|
|
217
|
-
const { isEmbedded, parentTheme } = useCfgApp();
|
|
218
|
-
const { setTheme } = useThemeContext();
|
|
219
|
-
|
|
220
|
-
useEffect(() => {
|
|
221
|
-
if (isEmbedded && parentTheme) {
|
|
222
|
-
setTheme(parentTheme);
|
|
223
|
-
}
|
|
224
|
-
}, [isEmbedded, parentTheme, setTheme]);
|
|
225
|
-
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### Disable Auto Sync
|
|
231
|
-
|
|
232
|
-
```tsx
|
|
233
|
-
<AdminLayout enableParentSync={false}>
|
|
234
|
-
{/* Handle sync manually */}
|
|
235
|
-
</AdminLayout>
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### URL Override
|
|
239
|
-
|
|
240
|
-
Force iframe mode via URL:
|
|
241
|
-
```
|
|
242
|
-
https://example.com/admin/?embed=true
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
Disable iframe mode:
|
|
246
|
-
```
|
|
247
|
-
https://example.com/admin/?embed=false
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## Integration with Django
|
|
251
|
-
|
|
252
|
-
See Django template at: `src/django_cfg/templates/admin/index.html`
|
|
253
|
-
|
|
254
|
-
The Django template uses an iframe to load the Next.js app:
|
|
255
|
-
```html
|
|
256
|
-
<iframe
|
|
257
|
-
id="nextjs-dashboard-iframe"
|
|
258
|
-
src="{% nextjs_admin_url 'private' %}"
|
|
259
|
-
title="Next.js Dashboard"
|
|
260
|
-
></iframe>
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
## Components
|
|
264
|
-
|
|
265
|
-
### ParentSync
|
|
266
|
-
|
|
267
|
-
Handles bidirectional communication with parent window. Automatically included when using `isCfgAdmin={true}`.
|
|
268
|
-
|
|
269
|
-
```tsx
|
|
270
|
-
import { ParentSync } from '@djangocfg/layouts';
|
|
271
|
-
|
|
272
|
-
// Usually placed inside AppLayout (automatic with isCfgAdmin={true})
|
|
273
|
-
<AuthProvider>
|
|
274
|
-
<ThemeProvider>
|
|
275
|
-
<ParentSync />
|
|
276
|
-
{children}
|
|
277
|
-
</ThemeProvider>
|
|
278
|
-
</AuthProvider>
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
**What ParentSync does:**
|
|
282
|
-
|
|
283
|
-
1. **Theme Sync** (Parent → iframe): Receives theme updates from Django Unfold and applies them
|
|
284
|
-
2. **Auth Status Sync** (iframe → Parent): Sends authentication status when it changes
|
|
285
|
-
3. **Auto Resize** (iframe → Parent): Sends content height for iframe resizing
|
|
286
|
-
4. **Navigation Tracking** (iframe → Parent): Sends route changes for "Open in New Window" button
|
|
287
|
-
|
|
288
|
-
**Navigation Tracking:**
|
|
289
|
-
ParentSync automatically tracks route changes using Next.js Router and sends them to the parent window:
|
|
290
|
-
|
|
291
|
-
```typescript
|
|
292
|
-
// Automatically sent on every route change
|
|
293
|
-
window.parent.postMessage({
|
|
294
|
-
type: 'iframe-navigation',
|
|
295
|
-
data: { path: '/admin/stocks' }
|
|
296
|
-
}, '*');
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
This enables the "Open in New Window" button in the Django admin to open the exact page the user is currently viewing, not just the home page.
|
|
300
|
-
|
|
301
|
-
## Security Considerations
|
|
302
|
-
|
|
303
|
-
- All postMessage communications validate origins
|
|
304
|
-
- Tokens are only accepted from expected parent origins
|
|
305
|
-
- iframe uses sandbox attribute with specific permissions
|
|
306
|
-
- localStorage is used for token persistence (XSS protection needed)
|
|
307
|
-
|
|
308
|
-
## Migration from Old API
|
|
309
|
-
|
|
310
|
-
**Old approach:**
|
|
311
|
-
```tsx
|
|
312
|
-
import { AppLayout, AdminLayout, useCfgApp } from '@djangocfg/layouts';
|
|
313
|
-
|
|
314
|
-
function AppLayoutWrapper() {
|
|
315
|
-
const { disableLayout, isEmbedded } = useCfgApp();
|
|
316
|
-
return (
|
|
317
|
-
<AppLayout disableLayout={disableLayout}>
|
|
318
|
-
{children}
|
|
319
|
-
</AppLayout>
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
export default function App() {
|
|
324
|
-
return (
|
|
325
|
-
<AdminLayout>
|
|
326
|
-
<AppLayoutWrapper>
|
|
327
|
-
{children}
|
|
328
|
-
</AppLayoutWrapper>
|
|
329
|
-
</AdminLayout>
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
**New approach:**
|
|
335
|
-
```tsx
|
|
336
|
-
import { AppLayout } from '@djangocfg/layouts';
|
|
337
|
-
|
|
338
|
-
export default function App({ Component, pageProps }) {
|
|
339
|
-
return (
|
|
340
|
-
<AppLayout config={appLayoutConfig} isCfgAdmin={true}>
|
|
341
|
-
<Component {...pageProps} />
|
|
342
|
-
</AppLayout>
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
## Troubleshooting
|
|
348
|
-
|
|
349
|
-
### Theme not syncing
|
|
350
|
-
- Check parent window is sending `parent-theme` messages
|
|
351
|
-
- Verify ThemeProvider is in component tree
|
|
352
|
-
- Enable debug logging in `useApp.ts`
|
|
353
|
-
|
|
354
|
-
### Auth tokens not working
|
|
355
|
-
- Tokens are automatically set in API client
|
|
356
|
-
- Check parent window sends `parent-auth` message
|
|
357
|
-
- Inspect localStorage for tokens: `auth_token`, `refresh_token`
|
|
358
|
-
|
|
359
|
-
### Layout not hiding in iframe
|
|
360
|
-
- Use `isCfgAdmin={true}` on AppLayout (recommended)
|
|
361
|
-
- Or manually pass `disableLayout` from `useCfgApp()` hook
|
|
362
|
-
- Try URL override: `?embed=true`
|
|
363
|
-
|
|
364
|
-
### Static export build fails
|
|
365
|
-
- Make sure ParentSync is wrapped with SSR protection (already handled)
|
|
366
|
-
- Check that `useAuth()` is only called on client-side
|
|
367
|
-
- Verify `'use client'` directive is present in components
|
|
368
|
-
|
|
369
|
-
## Examples
|
|
370
|
-
|
|
371
|
-
**Simple Django CFG Admin:**
|
|
372
|
-
```tsx
|
|
373
|
-
<AppLayout config={config} isCfgAdmin={true}>
|
|
374
|
-
<Component {...pageProps} />
|
|
375
|
-
</AppLayout>
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
**With custom font:**
|
|
379
|
-
```tsx
|
|
380
|
-
<AppLayout
|
|
381
|
-
config={config}
|
|
382
|
-
isCfgAdmin={true}
|
|
383
|
-
fontFamily={manrope.style.fontFamily}
|
|
384
|
-
>
|
|
385
|
-
<Component {...pageProps} />
|
|
386
|
-
</AppLayout>
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
**With custom auth logic:**
|
|
390
|
-
```tsx
|
|
391
|
-
<AdminLayout
|
|
392
|
-
config={{
|
|
393
|
-
onAuthTokenReceived: (token, refresh) => {
|
|
394
|
-
console.log('Tokens received from parent');
|
|
395
|
-
// Additional custom logic
|
|
396
|
-
}
|
|
397
|
-
}}
|
|
398
|
-
>
|
|
399
|
-
<AppLayout config={config}>
|
|
400
|
-
<Component {...pageProps} />
|
|
401
|
-
</AppLayout>
|
|
402
|
-
</AdminLayout>
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
## Related Files
|
|
406
|
-
|
|
407
|
-
- Django Template: `src/django_cfg/templates/admin/index.html`
|
|
408
|
-
- Django Views: `src/django_cfg/apps/frontend/views.py`
|
|
409
|
-
- Example App: `src/django_admin/apps/admin/src/pages/_app.tsx`
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PagePreloader - Usage Examples
|
|
3
|
-
*
|
|
4
|
-
* This file contains examples of how to use the PagePreloader component
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { PagePreloader, PagePreloaderDark } from './PagePreloader';
|
|
8
|
-
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// Example 1: Basic Usage
|
|
11
|
-
// ============================================================================
|
|
12
|
-
export function Example1_Basic() {
|
|
13
|
-
return <PagePreloader />;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// Example 2: Custom Text
|
|
18
|
-
// ============================================================================
|
|
19
|
-
export function Example2_CustomText() {
|
|
20
|
-
return <PagePreloader text="Loading your dashboard..." />;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// ============================================================================
|
|
24
|
-
// Example 3: Different Sizes
|
|
25
|
-
// ============================================================================
|
|
26
|
-
export function Example3_DifferentSizes() {
|
|
27
|
-
return (
|
|
28
|
-
<>
|
|
29
|
-
<PagePreloader size="sm" text="Small" />
|
|
30
|
-
<PagePreloader size="md" text="Medium" />
|
|
31
|
-
<PagePreloader size="lg" text="Large" />
|
|
32
|
-
<PagePreloader size="xl" text="Extra Large" />
|
|
33
|
-
</>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// ============================================================================
|
|
38
|
-
// Example 4: Dark Variant
|
|
39
|
-
// ============================================================================
|
|
40
|
-
export function Example4_Dark() {
|
|
41
|
-
return <PagePreloaderDark text="Loading..." />;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ============================================================================
|
|
45
|
-
// Example 5: No Text
|
|
46
|
-
// ============================================================================
|
|
47
|
-
export function Example5_NoText() {
|
|
48
|
-
return <PagePreloader showText={false} />;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ============================================================================
|
|
52
|
-
// Example 6: No Backdrop
|
|
53
|
-
// ============================================================================
|
|
54
|
-
export function Example6_NoBackdrop() {
|
|
55
|
-
return <PagePreloader backdrop={false} />;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// Example 7: Custom Backdrop Opacity
|
|
60
|
-
// ============================================================================
|
|
61
|
-
export function Example7_CustomBackdropOpacity() {
|
|
62
|
-
return <PagePreloader backdropOpacity={50} text="50% opacity backdrop" />;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ============================================================================
|
|
66
|
-
// Example 8: Conditional Loading
|
|
67
|
-
// ============================================================================
|
|
68
|
-
export function Example8_ConditionalLoading({ isLoading }: { isLoading: boolean }) {
|
|
69
|
-
if (!isLoading) return null;
|
|
70
|
-
return <PagePreloader text="Loading data..." />;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ============================================================================
|
|
74
|
-
// Example 9: With Custom Styling
|
|
75
|
-
// ============================================================================
|
|
76
|
-
export function Example9_CustomStyling() {
|
|
77
|
-
return (
|
|
78
|
-
<PagePreloader
|
|
79
|
-
className="bg-gradient-to-br from-blue-500 to-purple-600"
|
|
80
|
-
text="Loading..."
|
|
81
|
-
/>
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ============================================================================
|
|
86
|
-
// Example 10: In AdminLayout
|
|
87
|
-
// ============================================================================
|
|
88
|
-
export function Example10_InAdminLayout() {
|
|
89
|
-
// Usage in AdminLayout component
|
|
90
|
-
const isLoading = true; // Replace with actual loading state
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<>
|
|
94
|
-
{isLoading && <PagePreloader text="Loading application..." />}
|
|
95
|
-
{/* Rest of your app */}
|
|
96
|
-
</>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PagePreloader Component
|
|
3
|
-
*
|
|
4
|
-
* Full-page loading indicator with Lottie animation
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use client';
|
|
8
|
-
|
|
9
|
-
import React from 'react';
|
|
10
|
-
import { LottiePlayer } from '@djangocfg/ui/tools';
|
|
11
|
-
import energizingAnimation from '../lottie/energizing.json';
|
|
12
|
-
|
|
13
|
-
export interface PagePreloaderProps {
|
|
14
|
-
/**
|
|
15
|
-
* Custom loading text
|
|
16
|
-
* @default 'Loading...'
|
|
17
|
-
*/
|
|
18
|
-
text?: string;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Show loading text
|
|
22
|
-
* @default true
|
|
23
|
-
*/
|
|
24
|
-
showText?: boolean;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Animation size
|
|
28
|
-
* @default 'lg'
|
|
29
|
-
*/
|
|
30
|
-
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Whether to show backdrop
|
|
34
|
-
* @default true
|
|
35
|
-
*/
|
|
36
|
-
backdrop?: boolean;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Backdrop opacity (0-100)
|
|
40
|
-
* @default 80
|
|
41
|
-
*/
|
|
42
|
-
backdropOpacity?: number;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Additional CSS classes
|
|
46
|
-
*/
|
|
47
|
-
className?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* PagePreloader - Full-page loading indicator with energizing animation
|
|
52
|
-
*
|
|
53
|
-
* Features:
|
|
54
|
-
* - Lottie animation with energizing effect
|
|
55
|
-
* - Customizable text and size
|
|
56
|
-
* - Optional backdrop
|
|
57
|
-
* - Smooth fade-in animation
|
|
58
|
-
*
|
|
59
|
-
* Usage:
|
|
60
|
-
* ```tsx
|
|
61
|
-
* // Basic usage
|
|
62
|
-
* <PagePreloader />
|
|
63
|
-
*
|
|
64
|
-
* // With custom text
|
|
65
|
-
* <PagePreloader text="Loading your data..." />
|
|
66
|
-
*
|
|
67
|
-
* // Different size
|
|
68
|
-
* <PagePreloader size="xl" />
|
|
69
|
-
*
|
|
70
|
-
* // Without backdrop
|
|
71
|
-
* <PagePreloader backdrop={false} />
|
|
72
|
-
*
|
|
73
|
-
* // Custom styling
|
|
74
|
-
* <PagePreloader className="bg-gradient-to-br from-blue-500 to-purple-600" />
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
|
-
export function PagePreloader({
|
|
78
|
-
text = 'Loading...',
|
|
79
|
-
showText = true,
|
|
80
|
-
size = 'lg',
|
|
81
|
-
backdrop = true,
|
|
82
|
-
backdropOpacity = 80,
|
|
83
|
-
className,
|
|
84
|
-
}: PagePreloaderProps) {
|
|
85
|
-
// Generate backdrop opacity classes based on opacity value with explicit light/dark variants
|
|
86
|
-
const getBackdropClasses = () => {
|
|
87
|
-
if (!backdrop) return 'bg-transparent';
|
|
88
|
-
|
|
89
|
-
// Use explicit light/dark classes for reliable theme support
|
|
90
|
-
if (backdropOpacity >= 90) {
|
|
91
|
-
return 'bg-white dark:bg-gray-950'; // Full opacity
|
|
92
|
-
} else if (backdropOpacity >= 70) {
|
|
93
|
-
return 'bg-white/90 dark:bg-gray-950/90'; // 90% opacity
|
|
94
|
-
} else if (backdropOpacity >= 50) {
|
|
95
|
-
return 'bg-white/80 dark:bg-gray-950/80'; // 80% opacity
|
|
96
|
-
} else {
|
|
97
|
-
return 'bg-white/60 dark:bg-gray-950/60'; // 60% opacity
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<div
|
|
103
|
-
className={`fixed inset-0 z-50 flex items-center justify-center ${getBackdropClasses()} ${className || ''}`}
|
|
104
|
-
>
|
|
105
|
-
<div className="flex flex-col items-center gap-6 animate-in fade-in duration-300">
|
|
106
|
-
{/* Lottie Animation */}
|
|
107
|
-
<div className="relative">
|
|
108
|
-
<LottiePlayer
|
|
109
|
-
src={energizingAnimation}
|
|
110
|
-
size={size}
|
|
111
|
-
autoplay
|
|
112
|
-
loop
|
|
113
|
-
speed={1}
|
|
114
|
-
/>
|
|
115
|
-
</div>
|
|
116
|
-
|
|
117
|
-
{/* Loading Text */}
|
|
118
|
-
{showText && (
|
|
119
|
-
<div className="flex flex-col items-center gap-2">
|
|
120
|
-
<p className="text-lg font-medium text-gray-900 dark:text-gray-100">
|
|
121
|
-
{text}
|
|
122
|
-
</p>
|
|
123
|
-
<div className="flex gap-1">
|
|
124
|
-
<span className="h-2 w-2 animate-bounce rounded-full bg-blue-600 dark:bg-blue-400" style={{ animationDelay: '0ms' }} />
|
|
125
|
-
<span className="h-2 w-2 animate-bounce rounded-full bg-blue-600 dark:bg-blue-400" style={{ animationDelay: '150ms' }} />
|
|
126
|
-
<span className="h-2 w-2 animate-bounce rounded-full bg-blue-600 dark:bg-blue-400" style={{ animationDelay: '300ms' }} />
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
)}
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* PagePreloaderDark - Dark variant of PagePreloader
|
|
137
|
-
*
|
|
138
|
-
* Same as PagePreloader but forces dark backdrop
|
|
139
|
-
* Note: Regular PagePreloader now uses semantic theme colors,
|
|
140
|
-
* so this is only needed for specific dark-only use cases
|
|
141
|
-
*/
|
|
142
|
-
export function PagePreloaderDark(props: Omit<PagePreloaderProps, 'className'>) {
|
|
143
|
-
return (
|
|
144
|
-
<PagePreloader
|
|
145
|
-
{...props}
|
|
146
|
-
className="bg-gray-950/95"
|
|
147
|
-
/>
|
|
148
|
-
);
|
|
149
|
-
}
|