@djangocfg/layouts 1.2.5 → 1.2.7
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/context/AuthContext.tsx +10 -6
- package/src/layouts/AppLayout/AppLayout.tsx +82 -55
- package/src/layouts/AppLayout/components/PackageVersions/packageVersions.config.ts +8 -8
- package/src/layouts/AppLayout/index.ts +12 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/CfgLayout.tsx +104 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/README.md +389 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/components/ParentSync.tsx +149 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/components/index.ts +1 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/hooks/index.ts +6 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/hooks/useApp.ts +282 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/index.ts +20 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/types/index.ts +45 -0
- package/src/layouts/UILayout/config/components/data.config.tsx +125 -0
- package/src/layouts/UILayout/config/components/feedback.config.tsx +44 -0
- package/src/layouts/UILayout/config/components/forms.config.tsx +194 -0
- package/src/layouts/UILayout/config/components/layout.config.tsx +95 -0
- package/src/layouts/UILayout/config/components/navigation.config.tsx +50 -0
- package/src/layouts/UILayout/config/components/specialized.config.tsx +250 -0
- package/src/layouts/index.ts +3 -0
- package/src/utils/logger.ts +4 -2
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# CfgLayout - 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:** CfgLayout is now integrated into AppLayout. For most use cases, use `AppLayout` with `isCfgAdmin={true}` instead of using CfgLayout 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 CfgLayout
|
|
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 CfgLayout directly:
|
|
49
|
+
|
|
50
|
+
### 1. Wrap your app with CfgLayout (Zero Config!)
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// apps/admin/src/pages/_app.tsx
|
|
54
|
+
import { CfgLayout, AppLayout } from '@djangocfg/layouts';
|
|
55
|
+
|
|
56
|
+
function MyApp({ Component, pageProps }) {
|
|
57
|
+
return (
|
|
58
|
+
<CfgLayout>
|
|
59
|
+
<AppLayout config={appLayoutConfig}>
|
|
60
|
+
<Component {...pageProps} />
|
|
61
|
+
</AppLayout>
|
|
62
|
+
</CfgLayout>
|
|
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
|
+
<CfgLayout
|
|
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
|
+
</CfgLayout>
|
|
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
|
+
### CfgLayout Props
|
|
144
|
+
|
|
145
|
+
| Prop | Type | Default | Description |
|
|
146
|
+
|------|------|---------|-------------|
|
|
147
|
+
| `children` | `ReactNode` | Required | Your app content |
|
|
148
|
+
| `config` | `CfgLayoutConfig` | `{}` | Configuration object |
|
|
149
|
+
| `enableParentSync` | `boolean` | `true` | Enable automatic theme/auth sync |
|
|
150
|
+
|
|
151
|
+
### CfgLayoutConfig
|
|
152
|
+
|
|
153
|
+
**All options are optional!** CfgLayout works with zero configuration.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
interface CfgLayoutConfig {
|
|
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 |
|
|
205
|
+
| `iframe-resize` | `{ height }` | Content height changed |
|
|
206
|
+
| `iframe-navigation` | `{ path, route }` | User navigated to new page |
|
|
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
|
+
<CfgLayout enableParentSync={false}>
|
|
234
|
+
{/* Handle sync manually */}
|
|
235
|
+
</CfgLayout>
|
|
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
|
+
## Security Considerations
|
|
282
|
+
|
|
283
|
+
- All postMessage communications validate origins
|
|
284
|
+
- Tokens are only accepted from expected parent origins
|
|
285
|
+
- iframe uses sandbox attribute with specific permissions
|
|
286
|
+
- localStorage is used for token persistence (XSS protection needed)
|
|
287
|
+
|
|
288
|
+
## Migration from Old API
|
|
289
|
+
|
|
290
|
+
**Old approach:**
|
|
291
|
+
```tsx
|
|
292
|
+
import { AppLayout, CfgLayout, useCfgApp } from '@djangocfg/layouts';
|
|
293
|
+
|
|
294
|
+
function AppLayoutWrapper() {
|
|
295
|
+
const { disableLayout, isEmbedded } = useCfgApp();
|
|
296
|
+
return (
|
|
297
|
+
<AppLayout disableLayout={disableLayout}>
|
|
298
|
+
{children}
|
|
299
|
+
</AppLayout>
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export default function App() {
|
|
304
|
+
return (
|
|
305
|
+
<CfgLayout>
|
|
306
|
+
<AppLayoutWrapper>
|
|
307
|
+
{children}
|
|
308
|
+
</AppLayoutWrapper>
|
|
309
|
+
</CfgLayout>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**New approach:**
|
|
315
|
+
```tsx
|
|
316
|
+
import { AppLayout } from '@djangocfg/layouts';
|
|
317
|
+
|
|
318
|
+
export default function App({ Component, pageProps }) {
|
|
319
|
+
return (
|
|
320
|
+
<AppLayout config={appLayoutConfig} isCfgAdmin={true}>
|
|
321
|
+
<Component {...pageProps} />
|
|
322
|
+
</AppLayout>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Troubleshooting
|
|
328
|
+
|
|
329
|
+
### Theme not syncing
|
|
330
|
+
- Check parent window is sending `parent-theme` messages
|
|
331
|
+
- Verify ThemeProvider is in component tree
|
|
332
|
+
- Enable debug logging in `useApp.ts`
|
|
333
|
+
|
|
334
|
+
### Auth tokens not working
|
|
335
|
+
- Tokens are automatically set in API client
|
|
336
|
+
- Check parent window sends `parent-auth` message
|
|
337
|
+
- Inspect localStorage for tokens: `auth_token`, `refresh_token`
|
|
338
|
+
|
|
339
|
+
### Layout not hiding in iframe
|
|
340
|
+
- Use `isCfgAdmin={true}` on AppLayout (recommended)
|
|
341
|
+
- Or manually pass `disableLayout` from `useCfgApp()` hook
|
|
342
|
+
- Try URL override: `?embed=true`
|
|
343
|
+
|
|
344
|
+
### Static export build fails
|
|
345
|
+
- Make sure ParentSync is wrapped with SSR protection (already handled)
|
|
346
|
+
- Check that `useAuth()` is only called on client-side
|
|
347
|
+
- Verify `'use client'` directive is present in components
|
|
348
|
+
|
|
349
|
+
## Examples
|
|
350
|
+
|
|
351
|
+
**Simple Django CFG Admin:**
|
|
352
|
+
```tsx
|
|
353
|
+
<AppLayout config={config} isCfgAdmin={true}>
|
|
354
|
+
<Component {...pageProps} />
|
|
355
|
+
</AppLayout>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**With custom font:**
|
|
359
|
+
```tsx
|
|
360
|
+
<AppLayout
|
|
361
|
+
config={config}
|
|
362
|
+
isCfgAdmin={true}
|
|
363
|
+
fontFamily={manrope.style.fontFamily}
|
|
364
|
+
>
|
|
365
|
+
<Component {...pageProps} />
|
|
366
|
+
</AppLayout>
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**With custom auth logic:**
|
|
370
|
+
```tsx
|
|
371
|
+
<CfgLayout
|
|
372
|
+
config={{
|
|
373
|
+
onAuthTokenReceived: (token, refresh) => {
|
|
374
|
+
console.log('Tokens received from parent');
|
|
375
|
+
// Additional custom logic
|
|
376
|
+
}
|
|
377
|
+
}}
|
|
378
|
+
>
|
|
379
|
+
<AppLayout config={config}>
|
|
380
|
+
<Component {...pageProps} />
|
|
381
|
+
</AppLayout>
|
|
382
|
+
</CfgLayout>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Related Files
|
|
386
|
+
|
|
387
|
+
- Django Template: `src/django_cfg/templates/admin/index.html`
|
|
388
|
+
- Django Views: `src/django_cfg/apps/frontend/views.py`
|
|
389
|
+
- Example App: `src/@frontend/apps/admin/src/pages/_app.tsx`
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Parent Sync Component
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Handles all synchronization between iframe and parent window:
|
|
5
|
+
// - Theme sync (parent → iframe)
|
|
6
|
+
// - Auth status sync (iframe → parent)
|
|
7
|
+
// Must be used inside AuthProvider and ThemeProvider contexts
|
|
8
|
+
|
|
9
|
+
'use client';
|
|
10
|
+
|
|
11
|
+
import { useEffect, useState } from 'react';
|
|
12
|
+
import { useAuth } from '../../../../../auth';
|
|
13
|
+
import { useThemeContext } from '@djangocfg/ui';
|
|
14
|
+
import { useCfgApp } from '../hooks/useApp';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ParentSync Component
|
|
18
|
+
*
|
|
19
|
+
* Handles bidirectional communication with parent window when embedded:
|
|
20
|
+
* 1. Receives theme updates from parent (Django Unfold) and applies to ThemeProvider
|
|
21
|
+
* 2. Sends auth status to parent when authentication state changes
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* - Place inside AppLayout (after AuthProvider and ThemeProvider)
|
|
25
|
+
* - Automatically handles all iframe ↔ parent communication
|
|
26
|
+
*
|
|
27
|
+
* Note: Safe for static export - only runs on client-side after hydration
|
|
28
|
+
*/
|
|
29
|
+
export function ParentSync() {
|
|
30
|
+
const [isClient, setIsClient] = useState(false);
|
|
31
|
+
|
|
32
|
+
// Wait for client-side hydration to avoid SSR/static export errors
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setIsClient(true);
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
// Early return during SSR/static export
|
|
38
|
+
if (!isClient) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return <ParentSyncClient />;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Client-only component that uses auth context
|
|
47
|
+
* Separated to avoid SSR issues during static export
|
|
48
|
+
*/
|
|
49
|
+
function ParentSyncClient() {
|
|
50
|
+
const auth = useAuth();
|
|
51
|
+
const { setTheme } = useThemeContext();
|
|
52
|
+
const { isEmbedded, isMounted, parentTheme } = useCfgApp();
|
|
53
|
+
|
|
54
|
+
// 1. Sync theme from parent → iframe
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (isEmbedded && parentTheme) {
|
|
57
|
+
// console.log('[ParentSync] 🎨 Syncing theme from parent:', parentTheme);
|
|
58
|
+
setTheme(parentTheme);
|
|
59
|
+
}
|
|
60
|
+
}, [isEmbedded, parentTheme, setTheme]);
|
|
61
|
+
|
|
62
|
+
// 2. Send auth status from iframe → parent
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
// Only send if embedded and mounted
|
|
65
|
+
if (!isEmbedded || !isMounted) {
|
|
66
|
+
// console.log('[ParentSync] Skipping auth sync - not embedded or not mounted:', {
|
|
67
|
+
// isEmbedded,
|
|
68
|
+
// isMounted
|
|
69
|
+
// });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const authData = {
|
|
74
|
+
isAuthenticated: auth.isAuthenticated,
|
|
75
|
+
isLoading: auth.isLoading,
|
|
76
|
+
hasUser: !!auth.user
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// console.log('[ParentSync] 📤 Sending auth status to parent:', authData);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
window.parent.postMessage({
|
|
83
|
+
type: 'iframe-auth-status',
|
|
84
|
+
data: authData
|
|
85
|
+
}, '*');
|
|
86
|
+
// console.log('[ParentSync] ✅ Auth status sent successfully');
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error('[ParentSync] ❌ Failed to send auth status:', e);
|
|
89
|
+
}
|
|
90
|
+
}, [auth.isAuthenticated, auth.isLoading, auth.user, isEmbedded, isMounted]);
|
|
91
|
+
|
|
92
|
+
// 3. Send iframe height changes to parent for auto-resize
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
// Only send if embedded and mounted
|
|
95
|
+
if (!isEmbedded || !isMounted) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Function to send height to parent
|
|
100
|
+
const sendHeight = () => {
|
|
101
|
+
const height = document.documentElement.scrollHeight;
|
|
102
|
+
// console.log('[ParentSync] 📏 Sending height to parent:', height);
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
window.parent.postMessage({
|
|
106
|
+
type: 'iframe-resize',
|
|
107
|
+
data: { height }
|
|
108
|
+
}, '*');
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.error('[ParentSync] ❌ Failed to send height:', e);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Send initial height
|
|
115
|
+
sendHeight();
|
|
116
|
+
|
|
117
|
+
// Watch for content height changes with ResizeObserver
|
|
118
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
119
|
+
sendHeight();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Observe body element for size changes
|
|
123
|
+
resizeObserver.observe(document.body);
|
|
124
|
+
|
|
125
|
+
// Also observe on route changes (for Next.js)
|
|
126
|
+
const handleRouteChange = () => {
|
|
127
|
+
// Delay to allow content to render
|
|
128
|
+
setTimeout(sendHeight, 100);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Listen for Next.js router events if available
|
|
132
|
+
if (typeof window !== 'undefined') {
|
|
133
|
+
window.addEventListener('popstate', handleRouteChange);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return () => {
|
|
137
|
+
resizeObserver.disconnect();
|
|
138
|
+
if (typeof window !== 'undefined') {
|
|
139
|
+
window.removeEventListener('popstate', handleRouteChange);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}, [isEmbedded, isMounted]);
|
|
143
|
+
|
|
144
|
+
// This component doesn't render anything
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Export with old name for backward compatibility
|
|
149
|
+
export { ParentSync as AuthStatusSync };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ParentSync, AuthStatusSync } from './ParentSync';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { useCfgApp } from './useApp';
|
|
2
|
+
export type { UseCfgAppReturn, UseCfgAppOptions } from './useApp';
|
|
3
|
+
|
|
4
|
+
// Backward compatibility alias
|
|
5
|
+
export { useCfgApp as useApp } from './useApp';
|
|
6
|
+
export type { UseCfgAppReturn as UseAppReturn, UseCfgAppOptions as UseAppOptions } from './useApp';
|