@djangocfg/layouts 2.1.3 → 2.1.5

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 CHANGED
@@ -10,6 +10,72 @@ Simple, straightforward layout components for Next.js - import and use with prop
10
10
  pnpm add @djangocfg/layouts
11
11
  ```
12
12
 
13
+ ## Core Components
14
+
15
+ ### BaseApp
16
+
17
+ Core providers wrapper - use when you need providers without layout routing:
18
+
19
+ ```tsx
20
+ import { BaseApp } from '@djangocfg/layouts';
21
+
22
+ // app/layout.tsx
23
+ export default function RootLayout({ children }) {
24
+ return (
25
+ <html lang="en" suppressHydrationWarning>
26
+ <body>
27
+ <BaseApp theme={{ defaultTheme: 'dark' }}>
28
+ {children}
29
+ </BaseApp>
30
+ </body>
31
+ </html>
32
+ );
33
+ }
34
+ ```
35
+
36
+ **Included providers:**
37
+ - ThemeProvider (light/dark/system)
38
+ - TooltipProvider
39
+ - SWRConfig
40
+ - AuthProvider
41
+ - ErrorTrackingProvider
42
+ - Toaster + PageProgress
43
+
44
+ ### AppLayout
45
+
46
+ Smart layout router built on BaseApp - automatically selects layout based on route:
47
+
48
+ ```tsx
49
+ import { AppLayout } from '@djangocfg/layouts';
50
+ import { PublicLayout } from './_layouts/PublicLayout';
51
+ import { PrivateLayout } from './_layouts/PrivateLayout';
52
+
53
+ // app/layout.tsx
54
+ export default function RootLayout({ children }) {
55
+ return (
56
+ <html lang="en" suppressHydrationWarning>
57
+ <body>
58
+ <AppLayout
59
+ publicLayout={{
60
+ component: PublicLayout,
61
+ enabledPath: ['/', '/legal', '/contact']
62
+ }}
63
+ privateLayout={{
64
+ component: PrivateLayout,
65
+ enabledPath: ['/dashboard', '/profile']
66
+ }}
67
+ theme={{ defaultTheme: 'system' }}
68
+ >
69
+ {children}
70
+ </AppLayout>
71
+ </body>
72
+ </html>
73
+ );
74
+ }
75
+ ```
76
+
77
+ **Layout priority:** Admin → Private → Public → Fallback
78
+
13
79
  ## Layouts
14
80
 
15
81
  Simple, props-based layout components. No complex configs needed!
@@ -20,25 +86,25 @@ Simple, props-based layout components. No complex configs needed!
20
86
  import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
21
87
 
22
88
  // Public page
23
- <PublicLayout
24
- logo="/logo.svg"
25
- siteName="My App"
89
+ <PublicLayout
90
+ logo="/logo.svg"
91
+ siteName="My App"
26
92
  navigation={navItems}
27
93
  >
28
94
  {children}
29
95
  </PublicLayout>
30
96
 
31
97
  // Private page
32
- <PrivateLayout
33
- sidebar={{ items: menuItems }}
98
+ <PrivateLayout
99
+ sidebar={{ items: menuItems }}
34
100
  header={{ title: 'Dashboard' }}
35
101
  >
36
102
  {children}
37
103
  </PrivateLayout>
38
104
 
39
105
  // Auth page
40
- <AuthLayout
41
- logo="/logo.svg"
106
+ <AuthLayout
107
+ logo="/logo.svg"
42
108
  title="Sign In"
43
109
  subtitle="Welcome back"
44
110
  >
@@ -48,27 +114,16 @@ import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
48
114
 
49
115
  | Layout | Description |
50
116
  |--------|-------------|
117
+ | `BaseApp` | Core providers wrapper (Theme, Auth, SWR, ErrorTracking, Toaster) |
118
+ | `AppLayout` | Smart layout router with route-based layout switching |
51
119
  | `PublicLayout` | Public pages (home, docs, contact, legal) |
52
120
  | `PrivateLayout` | Authenticated user pages (dashboard, profile) |
53
121
  | `AuthLayout` | Authentication pages (login, signup, password reset) |
54
122
  | `AdminLayout` | Admin panel layout |
55
- | `AppLayout` | Smart layout router with config-based setup |
56
123
  | `ProfileLayout` | User profile pages |
57
124
  | `SupportLayout` | Support/help pages with ticket system |
58
125
  | `PaymentsLayout` | Payment flows and billing |
59
126
 
60
- ### AppLayout
61
-
62
- Smart layout component that automatically selects the right layout based on config:
63
-
64
- ```tsx
65
- import { AppLayout } from '@djangocfg/layouts';
66
-
67
- <AppLayout config={appLayoutConfig}>
68
- {children}
69
- </AppLayout>
70
- ```
71
-
72
127
  ## Auth
73
128
 
74
129
  Complete authentication system with OTP and OAuth support.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -92,9 +92,9 @@
92
92
  "check": "tsc --noEmit"
93
93
  },
94
94
  "peerDependencies": {
95
- "@djangocfg/api": "^2.1.3",
96
- "@djangocfg/centrifugo": "^2.1.3",
97
- "@djangocfg/ui-nextjs": "^2.1.3",
95
+ "@djangocfg/api": "^2.1.5",
96
+ "@djangocfg/centrifugo": "^2.1.5",
97
+ "@djangocfg/ui-nextjs": "^2.1.5",
98
98
  "@hookform/resolvers": "^5.2.0",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -114,7 +114,7 @@
114
114
  "uuid": "^11.1.0"
115
115
  },
116
116
  "devDependencies": {
117
- "@djangocfg/typescript-config": "^2.1.3",
117
+ "@djangocfg/typescript-config": "^2.1.5",
118
118
  "@types/node": "^24.7.2",
119
119
  "@types/react": "^19.1.0",
120
120
  "@types/react-dom": "^19.1.0",
@@ -38,15 +38,13 @@
38
38
 
39
39
  import React, { ReactNode, useMemo } from 'react';
40
40
  import { usePathname } from 'next/navigation';
41
- import { SWRConfig } from 'swr';
42
- import { ThemeProvider, Toaster, TooltipProvider } from '@djangocfg/ui-nextjs';
43
41
  import { CentrifugoProvider } from '@djangocfg/centrifugo';
44
42
  import { ErrorBoundary } from '../../components/errors/ErrorBoundary';
45
- import { AuthProvider, type AuthConfig } from '../../auth/context';
46
- import { ErrorTrackingProvider, type ValidationErrorConfig, type CORSErrorConfig, type NetworkErrorConfig } from '../../components/errors/ErrorsTracker';
43
+ import { type AuthConfig } from '../../auth/context';
44
+ import { type ValidationErrorConfig, type CORSErrorConfig, type NetworkErrorConfig } from '../../components/errors/ErrorsTracker';
47
45
  import { AnalyticsProvider } from '../../snippets/Analytics';
48
- import { PageProgress } from '../../components/core/PageProgress';
49
46
  import { Suspense, ClientOnly } from '../../components/core';
47
+ import { BaseApp } from './BaseApp';
50
48
 
51
49
  export type LayoutMode = 'public' | 'private' | 'admin';
52
50
 
@@ -243,14 +241,6 @@ function AppLayoutContent({
243
241
  </CentrifugoProvider>
244
242
  );
245
243
 
246
- // Global toast notifications (always rendered)
247
- content = (
248
- <>
249
- {content}
250
- <PageProgress />
251
- <Toaster />
252
- </>
253
- );
254
244
 
255
245
  // Wrap with ErrorBoundary if enabled
256
246
  if (enableErrorBoundary) {
@@ -272,44 +262,14 @@ function AppLayoutContent({
272
262
  */
273
263
  export function AppLayout(props: AppLayoutProps) {
274
264
  const { theme, auth, errorTracking } = props;
275
-
276
- // Convert 'system' to actual theme based on system preference
277
- const getResolvedTheme = (): 'light' | 'dark' => {
278
- if (theme?.defaultTheme === 'system') {
279
- if (typeof window !== 'undefined') {
280
- return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
281
- }
282
- return 'light'; // Default fallback for SSR
283
- }
284
- return theme?.defaultTheme || 'light';
285
- };
286
-
265
+
287
266
  return (
288
- <ThemeProvider
289
- defaultTheme={getResolvedTheme()}
290
- storageKey={theme?.storageKey}
267
+ <BaseApp
268
+ theme={theme}
269
+ auth={auth}
270
+ errorTracking={errorTracking}
291
271
  >
292
- <TooltipProvider>
293
- <SWRConfig
294
- value={{
295
- // Default SWR configuration
296
- revalidateOnFocus: true,
297
- revalidateOnReconnect: true,
298
- dedupingInterval: 2000,
299
- }}
300
- >
301
- <AuthProvider config={auth}>
302
- <ErrorTrackingProvider
303
- validation={errorTracking?.validation}
304
- cors={errorTracking?.cors}
305
- network={errorTracking?.network}
306
- onError={errorTracking?.onError}
307
- >
308
- <AppLayoutContent {...props} />
309
- </ErrorTrackingProvider>
310
- </AuthProvider>
311
- </SWRConfig>
312
- </TooltipProvider>
313
- </ThemeProvider>
272
+ <AppLayoutContent {...props} />
273
+ </BaseApp>
314
274
  );
315
275
  }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * BaseApp - Core Providers Wrapper
3
+ *
4
+ * Provides essential app-wide providers:
5
+ * - ThemeProvider (light/dark/system theme)
6
+ * - TooltipProvider (tooltip positioning)
7
+ * - SWRConfig (data fetching)
8
+ * - AuthProvider (authentication context)
9
+ * - ErrorTrackingProvider (error handling)
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import { BaseApp } from '@djangocfg/layouts';
14
+ *
15
+ * <BaseApp
16
+ * theme={{ defaultTheme: 'system' }}
17
+ * auth={{ loginPath: '/auth/login' }}
18
+ * >
19
+ * {children}
20
+ * </BaseApp>
21
+ * ```
22
+ */
23
+
24
+ 'use client';
25
+
26
+ import { ReactNode } from 'react';
27
+ import { SWRConfig } from 'swr';
28
+ import { ThemeProvider, Toaster, TooltipProvider } from '@djangocfg/ui-nextjs';
29
+ import { AuthProvider, type AuthConfig } from '../../auth/context';
30
+ import { ErrorTrackingProvider, type ValidationErrorConfig, type CORSErrorConfig, type NetworkErrorConfig } from '../../components/errors/ErrorsTracker';
31
+ import { PageProgress } from '../../components/core/PageProgress';
32
+
33
+ export interface BaseAppProps {
34
+ children: ReactNode;
35
+
36
+ /** Theme configuration */
37
+ theme?: {
38
+ defaultTheme?: 'light' | 'dark' | 'system';
39
+ storageKey?: string;
40
+ };
41
+
42
+ /** Auth configuration */
43
+ auth?: AuthConfig;
44
+
45
+ /** Error tracking configuration */
46
+ errorTracking?: {
47
+ validation?: Partial<ValidationErrorConfig>;
48
+ cors?: Partial<CORSErrorConfig>;
49
+ network?: Partial<NetworkErrorConfig>;
50
+ onError?: (error: any) => boolean | void;
51
+ };
52
+
53
+ /** SWR configuration */
54
+ swr?: {
55
+ revalidateOnFocus?: boolean;
56
+ revalidateOnReconnect?: boolean;
57
+ dedupingInterval?: number;
58
+ };
59
+ }
60
+
61
+ /**
62
+ * BaseApp - Core providers wrapper for any React/Next.js app
63
+ *
64
+ * Includes: ThemeProvider, TooltipProvider, SWRConfig, AuthProvider, ErrorTrackingProvider
65
+ * Also renders global Toaster and PageProgress components
66
+ */
67
+ export function BaseApp({
68
+ children,
69
+ theme,
70
+ auth,
71
+ errorTracking,
72
+ swr,
73
+ }: BaseAppProps) {
74
+ return (
75
+ <ThemeProvider
76
+ defaultTheme={theme?.defaultTheme || 'system'}
77
+ storageKey={theme?.storageKey}
78
+ >
79
+ <TooltipProvider>
80
+ <SWRConfig
81
+ value={{
82
+ revalidateOnFocus: swr?.revalidateOnFocus ?? true,
83
+ revalidateOnReconnect: swr?.revalidateOnReconnect ?? true,
84
+ dedupingInterval: swr?.dedupingInterval ?? 2000,
85
+ }}
86
+ >
87
+ <AuthProvider config={auth}>
88
+ <ErrorTrackingProvider
89
+ validation={errorTracking?.validation}
90
+ cors={errorTracking?.cors}
91
+ network={errorTracking?.network}
92
+ onError={errorTracking?.onError}
93
+ >
94
+ {children}
95
+ <PageProgress />
96
+ <Toaster />
97
+ </ErrorTrackingProvider>
98
+ </AuthProvider>
99
+ </SWRConfig>
100
+ </TooltipProvider>
101
+ </ThemeProvider>
102
+ );
103
+ }
@@ -5,3 +5,6 @@
5
5
  export { AppLayout } from './AppLayout';
6
6
  export type { AppLayoutProps, LayoutMode } from './AppLayout';
7
7
 
8
+ export { BaseApp } from './BaseApp';
9
+ export type { BaseAppProps } from './BaseApp';
10
+
@@ -66,7 +66,7 @@ export interface AIChatWidgetProps extends ChatWidgetConfig {
66
66
  * Internal AI chat widget that uses context
67
67
  */
68
68
  const AIChatWidgetInternal = React.memo<{ className?: string }>(({ className }) => {
69
- const { config, displayMode, openChat } = useAIChatContext();
69
+ const { config, displayMode, openChat, isMobile } = useAIChatContext();
70
70
 
71
71
  // Use layout hook for consistent positioning
72
72
  const { getFabStyles, getFloatingStyles } = useChatLayout();
@@ -165,7 +165,28 @@ const AIChatWidgetInternal = React.memo<{ className?: string }>(({ className })
165
165
  );
166
166
  }
167
167
 
168
- // Mode: floating - floating panel
168
+ // Mode: floating - fullscreen on mobile (via Portal), floating panel on desktop
169
+ if (isMobile) {
170
+ return (
171
+ <Portal>
172
+ <div
173
+ className="z-[400] overflow-hidden"
174
+ style={{
175
+ position: 'fixed',
176
+ top: 0,
177
+ left: 0,
178
+ right: 0,
179
+ bottom: 0,
180
+ width: '100vw',
181
+ height: '100dvh',
182
+ }}
183
+ >
184
+ <ChatPanel />
185
+ </div>
186
+ </Portal>
187
+ );
188
+ }
189
+
169
190
  return (
170
191
  <Portal>
171
192
  <div style={floatingStyles} className={className || ''}>
@@ -23,12 +23,19 @@ export const ChatPanel = React.memo(() => {
23
23
  // Mobile: fullscreen, Desktop: floating panel
24
24
  const panelStyles: React.CSSProperties = isMobile
25
25
  ? {
26
+ position: 'absolute',
27
+ top: 0,
28
+ left: 0,
29
+ right: 0,
30
+ bottom: 0,
26
31
  width: '100%',
27
- height: '100dvh',
32
+ height: '100%',
28
33
  maxHeight: '100dvh',
29
34
  borderRadius: 0,
30
35
  display: 'flex',
31
36
  flexDirection: 'column',
37
+ margin: 0,
38
+ border: 'none',
32
39
  }
33
40
  : {
34
41
  width: '380px',
@@ -38,7 +45,7 @@ export const ChatPanel = React.memo(() => {
38
45
 
39
46
  return (
40
47
  <Card
41
- className={`flex flex-col shadow-2xl border-border/50 ${isMobile ? 'rounded-none' : ''}`}
48
+ className={`flex flex-col ${isMobile ? 'rounded-none border-0 shadow-none' : 'shadow-2xl border-border/50'}`}
42
49
  style={panelStyles}
43
50
  >
44
51
  {/* Header */}