@djangocfg/ui-nextjs 2.1.102 → 2.1.103

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.
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+
3
+ type BackgroundVariant = 'aurora-borealis' | 'mesh-gradient' | 'floating-orbs' | 'geometric-flow' | 'liquid-gradient' | 'spotlight' | 'none' | 'gradient-mesh' | 'dot-matrix' | 'grid-lines' | 'aurora' | 'particles' | 'waves';
4
+ interface AnimatedBackgroundProps {
5
+ variant?: BackgroundVariant;
6
+ className?: string;
7
+ intensity?: 'subtle' | 'medium' | 'strong';
8
+ /** Color scheme - 'vibrant' uses multiple colors, 'monochrome' uses primary only */
9
+ colorScheme?: 'vibrant' | 'monochrome' | 'cool' | 'warm';
10
+ }
11
+ declare const AnimatedBackground: React.FC<AnimatedBackgroundProps>;
12
+
13
+ type MouseFollowerVariant = 'glow' | 'spotlight' | 'gradient-blob' | 'ring' | 'trail';
14
+ interface MouseFollowerProps {
15
+ /** Visual style of the follower */
16
+ variant?: MouseFollowerVariant;
17
+ /** Size of the effect in pixels */
18
+ size?: number;
19
+ /** Color - can be CSS color or 'primary' to use theme */
20
+ color?: string;
21
+ /** Smoothness of following (0.05 = very smooth, 0.3 = snappy) */
22
+ smoothness?: number;
23
+ /** Opacity of the effect (0-1) */
24
+ opacity?: number;
25
+ /** Blur amount for glow effects */
26
+ blur?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
27
+ /** Additional className for the container */
28
+ className?: string;
29
+ /** Whether to show on mobile (touch devices) */
30
+ showOnMobile?: boolean;
31
+ /** Disable the effect */
32
+ disabled?: boolean;
33
+ }
34
+ declare const MouseFollower: React.FC<MouseFollowerProps>;
35
+
36
+ export { AnimatedBackground, type BackgroundVariant, MouseFollower, type MouseFollowerVariant };
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+
3
+ type BackgroundVariant = 'aurora-borealis' | 'mesh-gradient' | 'floating-orbs' | 'geometric-flow' | 'liquid-gradient' | 'spotlight' | 'none' | 'gradient-mesh' | 'dot-matrix' | 'grid-lines' | 'aurora' | 'particles' | 'waves';
4
+ interface AnimatedBackgroundProps {
5
+ variant?: BackgroundVariant;
6
+ className?: string;
7
+ intensity?: 'subtle' | 'medium' | 'strong';
8
+ /** Color scheme - 'vibrant' uses multiple colors, 'monochrome' uses primary only */
9
+ colorScheme?: 'vibrant' | 'monochrome' | 'cool' | 'warm';
10
+ }
11
+ declare const AnimatedBackground: React.FC<AnimatedBackgroundProps>;
12
+
13
+ type MouseFollowerVariant = 'glow' | 'spotlight' | 'gradient-blob' | 'ring' | 'trail';
14
+ interface MouseFollowerProps {
15
+ /** Visual style of the follower */
16
+ variant?: MouseFollowerVariant;
17
+ /** Size of the effect in pixels */
18
+ size?: number;
19
+ /** Color - can be CSS color or 'primary' to use theme */
20
+ color?: string;
21
+ /** Smoothness of following (0.05 = very smooth, 0.3 = snappy) */
22
+ smoothness?: number;
23
+ /** Opacity of the effect (0-1) */
24
+ opacity?: number;
25
+ /** Blur amount for glow effects */
26
+ blur?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
27
+ /** Additional className for the container */
28
+ className?: string;
29
+ /** Whether to show on mobile (touch devices) */
30
+ showOnMobile?: boolean;
31
+ /** Disable the effect */
32
+ disabled?: boolean;
33
+ }
34
+ declare const MouseFollower: React.FC<MouseFollowerProps>;
35
+
36
+ export { AnimatedBackground, type BackgroundVariant, MouseFollower, type MouseFollowerVariant };
@@ -0,0 +1,364 @@
1
+ export * from '@djangocfg/ui-core/hooks';
2
+ import { RefObject } from 'react';
3
+ import { Keys, HotkeyCallback, Options } from 'react-hotkeys-hook';
4
+ export { HotkeyCallback, HotkeysProvider, Keys, isHotkeyPressed, useHotkeysContext } from 'react-hotkeys-hook';
5
+
6
+ type ResolvedTheme = 'light' | 'dark';
7
+ /**
8
+ * Hook to detect the current resolved theme (light or dark)
9
+ *
10
+ * Standalone hook - doesn't require ThemeProvider.
11
+ * Detects theme from:
12
+ * 1. 'dark' class on html element
13
+ * 2. System preference (prefers-color-scheme)
14
+ *
15
+ * For full theme control (setTheme, toggleTheme), use useThemeContext instead.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const theme = useResolvedTheme(); // 'light' | 'dark'
20
+ * ```
21
+ */
22
+ declare const useResolvedTheme: () => ResolvedTheme;
23
+
24
+ /**
25
+ * useQueryParams Hook
26
+ *
27
+ * Safe hook to access URL query parameters without requiring Suspense boundary.
28
+ * Works on client-side only, returns empty URLSearchParams during SSR/prerendering.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * const params = useQueryParams();
33
+ * const flow = params.get('flow');
34
+ * const hasFlow = params.has('flow');
35
+ * const allTags = params.getAll('tags');
36
+ * ```
37
+ */
38
+ /**
39
+ * Hook to safely access URL query parameters without useSearchParams()
40
+ *
41
+ * This hook reads query parameters directly from window.location.search,
42
+ * avoiding the need for Suspense boundaries that useSearchParams() requires.
43
+ *
44
+ * Automatically updates when URL changes (navigation, back/forward, etc.)
45
+ * Uses pathname from Next.js to detect route changes and polls for query param changes.
46
+ *
47
+ * Returns a URLSearchParams object with get(), getAll(), has(), etc.
48
+ *
49
+ * @returns URLSearchParams object (empty during SSR)
50
+ */
51
+ declare function useQueryParams(): URLSearchParams;
52
+
53
+ /**
54
+ * Universal Router Hook with BasePath Support
55
+ *
56
+ * Wrapper around Next.js useRouter that automatically handles basePath
57
+ * for static builds served via iframe or subdirectory
58
+ *
59
+ * IMPORTANT: In Next.js 15 App Router, router.push() does NOT automatically
60
+ * handle basePath (unlike Pages Router). This is a breaking change in App Router.
61
+ *
62
+ * This hook ensures basePath is always included when navigating, especially
63
+ * important for static exports served via iframe where basePath is critical.
64
+ *
65
+ * @see https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration
66
+ */
67
+ /**
68
+ * Router with basePath support
69
+ *
70
+ * Automatically adds basePath to all navigation methods when basePath is configured.
71
+ * In Next.js 15 App Router, router.push() doesn't handle basePath automatically,
72
+ * so this hook uses window.location to ensure basePath is always included.
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * const router = useCfgRouter();
77
+ *
78
+ * // With basePath='/cfg/admin':
79
+ * router.push('/dashboard'); // Client-side navigation to '/cfg/admin/dashboard'
80
+ * router.replace('/auth'); // Client-side replace with '/cfg/admin/auth'
81
+ * router.hardPush('/dashboard'); // Full page reload to '/cfg/admin/dashboard'
82
+ * router.hardReplace('/auth'); // Full page replace with '/cfg/admin/auth'
83
+ * ```
84
+ */
85
+ declare function useCfgRouter(): {
86
+ push: (href: string, options?: {
87
+ scroll?: boolean;
88
+ }) => void;
89
+ replace: (href: string, options?: {
90
+ scroll?: boolean;
91
+ }) => void;
92
+ hardPush: (href: string) => void;
93
+ hardReplace: (href: string) => void;
94
+ prefetch: (href: string) => void;
95
+ back: () => void;
96
+ forward: () => void;
97
+ refresh: () => void;
98
+ };
99
+
100
+ /**
101
+ * Options for the useHotkey hook
102
+ */
103
+ interface UseHotkeyOptions extends Omit<Options, 'enabled'> {
104
+ /** Whether the hotkey is enabled (default: true) */
105
+ enabled?: boolean;
106
+ /** Scope for the hotkey - useful for context-specific shortcuts */
107
+ scope?: string;
108
+ /** Only trigger when focus is within a specific element */
109
+ scopes?: string[];
110
+ /** Prevent default browser behavior */
111
+ preventDefault?: boolean;
112
+ /** Enable in input fields and textareas */
113
+ enableOnFormTags?: boolean | readonly ('input' | 'textarea' | 'select')[];
114
+ /** Enable when contentEditable element is focused */
115
+ enableOnContentEditable?: boolean;
116
+ /** Split key for multiple hotkey combinations (default: ',') */
117
+ splitKey?: string;
118
+ /** Key up/down events */
119
+ keyup?: boolean;
120
+ keydown?: boolean;
121
+ /** Description for the hotkey (useful for help dialogs) */
122
+ description?: string;
123
+ }
124
+ /**
125
+ * Simple wrapper hook for react-hotkeys-hook
126
+ *
127
+ * @example
128
+ * // Single key
129
+ * useHotkey('escape', () => closeModal());
130
+ *
131
+ * @example
132
+ * // Key combination
133
+ * useHotkey('ctrl+s', (e) => {
134
+ * e.preventDefault();
135
+ * saveDocument();
136
+ * });
137
+ *
138
+ * @example
139
+ * // Multiple keys (any of them will trigger)
140
+ * useHotkey(['ArrowLeft', '['], () => goToPrevious());
141
+ * useHotkey(['ArrowRight', ']'], () => goToNext());
142
+ *
143
+ * @example
144
+ * // With options
145
+ * useHotkey('/', () => focusSearch(), {
146
+ * preventDefault: true,
147
+ * enableOnFormTags: false,
148
+ * description: 'Focus search input'
149
+ * });
150
+ *
151
+ * @example
152
+ * // Scoped hotkeys
153
+ * useHotkey('delete', () => deleteItem(), { scopes: ['list-view'] });
154
+ *
155
+ * @param keys - Hotkey or array of hotkeys (e.g., 'ctrl+s', 'ArrowLeft', ['[', 'ArrowLeft'])
156
+ * @param callback - Function to call when hotkey is pressed
157
+ * @param options - Configuration options
158
+ * @returns Ref to attach to element for scoped hotkeys
159
+ */
160
+ declare function useHotkey<T extends HTMLElement = HTMLElement>(keys: Keys, callback: HotkeyCallback, options?: UseHotkeyOptions): RefObject<T | null>;
161
+
162
+ /**
163
+ * Device detection hook wrapper for react-device-detect
164
+ *
165
+ * Provides a convenient interface to access device information including:
166
+ * - Device type (mobile, tablet, desktop, etc.)
167
+ * - Browser information (name, version, etc.)
168
+ * - OS information (name, version, etc.)
169
+ * - Orientation (portrait/landscape)
170
+ *
171
+ * @param userAgent - Optional user agent string (useful for SSR)
172
+ * @returns Device detection object with all available information
173
+ *
174
+ * @example
175
+ * ```tsx
176
+ * const device = useDeviceDetect();
177
+ *
178
+ * if (device.isMobile) {
179
+ * return <MobileView />;
180
+ * }
181
+ *
182
+ * return <DesktopView />;
183
+ * ```
184
+ */
185
+ declare function useDeviceDetect(userAgent?: string): {
186
+ isMobile: boolean;
187
+ isTablet: boolean;
188
+ isDesktop: boolean;
189
+ isBrowser: boolean;
190
+ isMobileOnly: boolean;
191
+ isSmartTV: boolean;
192
+ isConsole: boolean;
193
+ isWearable: boolean;
194
+ isEmbedded: boolean;
195
+ isAndroid: boolean;
196
+ isIOS: boolean;
197
+ isWindows: boolean;
198
+ isMacOs: boolean;
199
+ isWinPhone: boolean;
200
+ isChrome: boolean;
201
+ isFirefox: boolean;
202
+ isSafari: boolean;
203
+ isOpera: boolean;
204
+ isIE: boolean;
205
+ isEdge: boolean;
206
+ isEdgeChromium: boolean;
207
+ isLegacyEdge: boolean;
208
+ isChromium: boolean;
209
+ isMobileSafari: boolean;
210
+ isYandex: boolean;
211
+ isMIUI: boolean;
212
+ isSamsungBrowser: boolean;
213
+ isElectron: boolean;
214
+ isIOS13: boolean;
215
+ isIPad13: boolean;
216
+ isIPhone13: boolean;
217
+ isIPod13: boolean;
218
+ deviceType: string;
219
+ osName: string;
220
+ osVersion: string;
221
+ browserName: string;
222
+ browserVersion: string;
223
+ fullBrowserVersion: string;
224
+ mobileVendor: string;
225
+ mobileModel: string;
226
+ engineName: string;
227
+ engineVersion: string;
228
+ getUA: string;
229
+ isPortrait: boolean;
230
+ isLandscape: boolean;
231
+ orientation: "portrait" | "landscape";
232
+ selectors: {
233
+ isMobile: boolean;
234
+ isTablet: boolean;
235
+ isDesktop: boolean;
236
+ isBrowser: boolean;
237
+ isMobileOnly: boolean;
238
+ isSmartTV: boolean;
239
+ isConsole: boolean;
240
+ isWearable: boolean;
241
+ isEmbedded: boolean;
242
+ isAndroid: boolean;
243
+ isIOS: boolean;
244
+ isWindows: boolean;
245
+ isMacOs: boolean;
246
+ isWinPhone: boolean;
247
+ isChrome: boolean;
248
+ isFirefox: boolean;
249
+ isSafari: boolean;
250
+ isOpera: boolean;
251
+ isIE: boolean;
252
+ isEdge: boolean;
253
+ isEdgeChromium: boolean;
254
+ isLegacyEdge: boolean;
255
+ isChromium: boolean;
256
+ isMobileSafari: boolean;
257
+ isYandex: boolean;
258
+ isMIUI: boolean;
259
+ isSamsungBrowser: boolean;
260
+ isElectron: boolean;
261
+ osVersion: string;
262
+ osName: string;
263
+ fullBrowserVersion: string;
264
+ browserVersion: string;
265
+ browserName: string;
266
+ mobileVendor: string;
267
+ mobileModel: string;
268
+ engineName: string;
269
+ engineVersion: string;
270
+ getUA: string;
271
+ deviceType: string;
272
+ isIOS13: boolean;
273
+ isIPad13: boolean;
274
+ isIPhone13: boolean;
275
+ isIPod13: boolean;
276
+ };
277
+ deviceData: {
278
+ deviceType: string;
279
+ osName: string;
280
+ osVersion: string;
281
+ browserName: string;
282
+ browserVersion: string;
283
+ fullBrowserVersion: string;
284
+ mobileVendor: string;
285
+ mobileModel: string;
286
+ engineName: string;
287
+ engineVersion: string;
288
+ getUA: string;
289
+ };
290
+ };
291
+ type DeviceDetectResult = ReturnType<typeof useDeviceDetect>;
292
+
293
+ /**
294
+ * Advanced browser detection hook
295
+ *
296
+ * Detects modern browsers including Chromium-based browsers that may
297
+ * incorrectly report as Safari (Arc, Brave, Vivaldi, Comet, etc.)
298
+ */
299
+ interface BrowserInfo {
300
+ isChrome: boolean;
301
+ isChromium: boolean;
302
+ isSafari: boolean;
303
+ isFirefox: boolean;
304
+ isEdge: boolean;
305
+ isOpera: boolean;
306
+ isBrave: boolean;
307
+ isArc: boolean;
308
+ isVivaldi: boolean;
309
+ isYandex: boolean;
310
+ isSamsungBrowser: boolean;
311
+ isUCBrowser: boolean;
312
+ isComet: boolean;
313
+ isOperaMini: boolean;
314
+ isIE: boolean;
315
+ isFacebookInApp: boolean;
316
+ isInstagramInApp: boolean;
317
+ isTikTokInApp: boolean;
318
+ isSnapchatInApp: boolean;
319
+ isWeChatInApp: boolean;
320
+ isThreadsInApp: boolean;
321
+ isLinkedInInApp: boolean;
322
+ isTwitterInApp: boolean;
323
+ isInAppBrowser: boolean;
324
+ isWebView: boolean;
325
+ browserName: string;
326
+ isWebKit: boolean;
327
+ isBlink: boolean;
328
+ isGecko: boolean;
329
+ /**
330
+ * Whether the browser supports Web Push Notifications.
331
+ * Returns false for browsers known to NOT support push:
332
+ * - Opera Mini (no service worker support)
333
+ * - Internet Explorer (deprecated, no Push API)
334
+ * - UC Browser (unreliable push support)
335
+ * - In-App browsers (Facebook, Instagram, TikTok, Snapchat, etc.)
336
+ * - Generic WebViews (except Twitter/LinkedIn on Android which use Chrome WebView)
337
+ *
338
+ * Note: Comet (Perplexity) is Chromium-based and DOES support push notifications.
339
+ * Note: This is a browser-level check. For full push support,
340
+ * also check 'serviceWorker' in navigator && 'PushManager' in window
341
+ */
342
+ supportsPushNotifications: boolean;
343
+ isIOSBrowser: boolean;
344
+ userAgent: string;
345
+ }
346
+ /**
347
+ * Detect browser with improved accuracy for Chromium-based browsers
348
+ *
349
+ * @example
350
+ * ```tsx
351
+ * const browser = useBrowserDetect();
352
+ *
353
+ * if (browser.isSafari && !browser.isChromium) {
354
+ * // Real Safari
355
+ * }
356
+ *
357
+ * if (browser.isChromium) {
358
+ * // Any Chromium-based browser (Chrome, Edge, Brave, Arc, etc.)
359
+ * }
360
+ * ```
361
+ */
362
+ declare function useBrowserDetect(): BrowserInfo;
363
+
364
+ export { type BrowserInfo, type DeviceDetectResult, type ResolvedTheme, type UseHotkeyOptions, useBrowserDetect, useCfgRouter, useDeviceDetect, useHotkey, useQueryParams, useResolvedTheme };
@@ -0,0 +1,364 @@
1
+ export * from '@djangocfg/ui-core/hooks';
2
+ import { RefObject } from 'react';
3
+ import { Keys, HotkeyCallback, Options } from 'react-hotkeys-hook';
4
+ export { HotkeyCallback, HotkeysProvider, Keys, isHotkeyPressed, useHotkeysContext } from 'react-hotkeys-hook';
5
+
6
+ type ResolvedTheme = 'light' | 'dark';
7
+ /**
8
+ * Hook to detect the current resolved theme (light or dark)
9
+ *
10
+ * Standalone hook - doesn't require ThemeProvider.
11
+ * Detects theme from:
12
+ * 1. 'dark' class on html element
13
+ * 2. System preference (prefers-color-scheme)
14
+ *
15
+ * For full theme control (setTheme, toggleTheme), use useThemeContext instead.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const theme = useResolvedTheme(); // 'light' | 'dark'
20
+ * ```
21
+ */
22
+ declare const useResolvedTheme: () => ResolvedTheme;
23
+
24
+ /**
25
+ * useQueryParams Hook
26
+ *
27
+ * Safe hook to access URL query parameters without requiring Suspense boundary.
28
+ * Works on client-side only, returns empty URLSearchParams during SSR/prerendering.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * const params = useQueryParams();
33
+ * const flow = params.get('flow');
34
+ * const hasFlow = params.has('flow');
35
+ * const allTags = params.getAll('tags');
36
+ * ```
37
+ */
38
+ /**
39
+ * Hook to safely access URL query parameters without useSearchParams()
40
+ *
41
+ * This hook reads query parameters directly from window.location.search,
42
+ * avoiding the need for Suspense boundaries that useSearchParams() requires.
43
+ *
44
+ * Automatically updates when URL changes (navigation, back/forward, etc.)
45
+ * Uses pathname from Next.js to detect route changes and polls for query param changes.
46
+ *
47
+ * Returns a URLSearchParams object with get(), getAll(), has(), etc.
48
+ *
49
+ * @returns URLSearchParams object (empty during SSR)
50
+ */
51
+ declare function useQueryParams(): URLSearchParams;
52
+
53
+ /**
54
+ * Universal Router Hook with BasePath Support
55
+ *
56
+ * Wrapper around Next.js useRouter that automatically handles basePath
57
+ * for static builds served via iframe or subdirectory
58
+ *
59
+ * IMPORTANT: In Next.js 15 App Router, router.push() does NOT automatically
60
+ * handle basePath (unlike Pages Router). This is a breaking change in App Router.
61
+ *
62
+ * This hook ensures basePath is always included when navigating, especially
63
+ * important for static exports served via iframe where basePath is critical.
64
+ *
65
+ * @see https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration
66
+ */
67
+ /**
68
+ * Router with basePath support
69
+ *
70
+ * Automatically adds basePath to all navigation methods when basePath is configured.
71
+ * In Next.js 15 App Router, router.push() doesn't handle basePath automatically,
72
+ * so this hook uses window.location to ensure basePath is always included.
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * const router = useCfgRouter();
77
+ *
78
+ * // With basePath='/cfg/admin':
79
+ * router.push('/dashboard'); // Client-side navigation to '/cfg/admin/dashboard'
80
+ * router.replace('/auth'); // Client-side replace with '/cfg/admin/auth'
81
+ * router.hardPush('/dashboard'); // Full page reload to '/cfg/admin/dashboard'
82
+ * router.hardReplace('/auth'); // Full page replace with '/cfg/admin/auth'
83
+ * ```
84
+ */
85
+ declare function useCfgRouter(): {
86
+ push: (href: string, options?: {
87
+ scroll?: boolean;
88
+ }) => void;
89
+ replace: (href: string, options?: {
90
+ scroll?: boolean;
91
+ }) => void;
92
+ hardPush: (href: string) => void;
93
+ hardReplace: (href: string) => void;
94
+ prefetch: (href: string) => void;
95
+ back: () => void;
96
+ forward: () => void;
97
+ refresh: () => void;
98
+ };
99
+
100
+ /**
101
+ * Options for the useHotkey hook
102
+ */
103
+ interface UseHotkeyOptions extends Omit<Options, 'enabled'> {
104
+ /** Whether the hotkey is enabled (default: true) */
105
+ enabled?: boolean;
106
+ /** Scope for the hotkey - useful for context-specific shortcuts */
107
+ scope?: string;
108
+ /** Only trigger when focus is within a specific element */
109
+ scopes?: string[];
110
+ /** Prevent default browser behavior */
111
+ preventDefault?: boolean;
112
+ /** Enable in input fields and textareas */
113
+ enableOnFormTags?: boolean | readonly ('input' | 'textarea' | 'select')[];
114
+ /** Enable when contentEditable element is focused */
115
+ enableOnContentEditable?: boolean;
116
+ /** Split key for multiple hotkey combinations (default: ',') */
117
+ splitKey?: string;
118
+ /** Key up/down events */
119
+ keyup?: boolean;
120
+ keydown?: boolean;
121
+ /** Description for the hotkey (useful for help dialogs) */
122
+ description?: string;
123
+ }
124
+ /**
125
+ * Simple wrapper hook for react-hotkeys-hook
126
+ *
127
+ * @example
128
+ * // Single key
129
+ * useHotkey('escape', () => closeModal());
130
+ *
131
+ * @example
132
+ * // Key combination
133
+ * useHotkey('ctrl+s', (e) => {
134
+ * e.preventDefault();
135
+ * saveDocument();
136
+ * });
137
+ *
138
+ * @example
139
+ * // Multiple keys (any of them will trigger)
140
+ * useHotkey(['ArrowLeft', '['], () => goToPrevious());
141
+ * useHotkey(['ArrowRight', ']'], () => goToNext());
142
+ *
143
+ * @example
144
+ * // With options
145
+ * useHotkey('/', () => focusSearch(), {
146
+ * preventDefault: true,
147
+ * enableOnFormTags: false,
148
+ * description: 'Focus search input'
149
+ * });
150
+ *
151
+ * @example
152
+ * // Scoped hotkeys
153
+ * useHotkey('delete', () => deleteItem(), { scopes: ['list-view'] });
154
+ *
155
+ * @param keys - Hotkey or array of hotkeys (e.g., 'ctrl+s', 'ArrowLeft', ['[', 'ArrowLeft'])
156
+ * @param callback - Function to call when hotkey is pressed
157
+ * @param options - Configuration options
158
+ * @returns Ref to attach to element for scoped hotkeys
159
+ */
160
+ declare function useHotkey<T extends HTMLElement = HTMLElement>(keys: Keys, callback: HotkeyCallback, options?: UseHotkeyOptions): RefObject<T | null>;
161
+
162
+ /**
163
+ * Device detection hook wrapper for react-device-detect
164
+ *
165
+ * Provides a convenient interface to access device information including:
166
+ * - Device type (mobile, tablet, desktop, etc.)
167
+ * - Browser information (name, version, etc.)
168
+ * - OS information (name, version, etc.)
169
+ * - Orientation (portrait/landscape)
170
+ *
171
+ * @param userAgent - Optional user agent string (useful for SSR)
172
+ * @returns Device detection object with all available information
173
+ *
174
+ * @example
175
+ * ```tsx
176
+ * const device = useDeviceDetect();
177
+ *
178
+ * if (device.isMobile) {
179
+ * return <MobileView />;
180
+ * }
181
+ *
182
+ * return <DesktopView />;
183
+ * ```
184
+ */
185
+ declare function useDeviceDetect(userAgent?: string): {
186
+ isMobile: boolean;
187
+ isTablet: boolean;
188
+ isDesktop: boolean;
189
+ isBrowser: boolean;
190
+ isMobileOnly: boolean;
191
+ isSmartTV: boolean;
192
+ isConsole: boolean;
193
+ isWearable: boolean;
194
+ isEmbedded: boolean;
195
+ isAndroid: boolean;
196
+ isIOS: boolean;
197
+ isWindows: boolean;
198
+ isMacOs: boolean;
199
+ isWinPhone: boolean;
200
+ isChrome: boolean;
201
+ isFirefox: boolean;
202
+ isSafari: boolean;
203
+ isOpera: boolean;
204
+ isIE: boolean;
205
+ isEdge: boolean;
206
+ isEdgeChromium: boolean;
207
+ isLegacyEdge: boolean;
208
+ isChromium: boolean;
209
+ isMobileSafari: boolean;
210
+ isYandex: boolean;
211
+ isMIUI: boolean;
212
+ isSamsungBrowser: boolean;
213
+ isElectron: boolean;
214
+ isIOS13: boolean;
215
+ isIPad13: boolean;
216
+ isIPhone13: boolean;
217
+ isIPod13: boolean;
218
+ deviceType: string;
219
+ osName: string;
220
+ osVersion: string;
221
+ browserName: string;
222
+ browserVersion: string;
223
+ fullBrowserVersion: string;
224
+ mobileVendor: string;
225
+ mobileModel: string;
226
+ engineName: string;
227
+ engineVersion: string;
228
+ getUA: string;
229
+ isPortrait: boolean;
230
+ isLandscape: boolean;
231
+ orientation: "portrait" | "landscape";
232
+ selectors: {
233
+ isMobile: boolean;
234
+ isTablet: boolean;
235
+ isDesktop: boolean;
236
+ isBrowser: boolean;
237
+ isMobileOnly: boolean;
238
+ isSmartTV: boolean;
239
+ isConsole: boolean;
240
+ isWearable: boolean;
241
+ isEmbedded: boolean;
242
+ isAndroid: boolean;
243
+ isIOS: boolean;
244
+ isWindows: boolean;
245
+ isMacOs: boolean;
246
+ isWinPhone: boolean;
247
+ isChrome: boolean;
248
+ isFirefox: boolean;
249
+ isSafari: boolean;
250
+ isOpera: boolean;
251
+ isIE: boolean;
252
+ isEdge: boolean;
253
+ isEdgeChromium: boolean;
254
+ isLegacyEdge: boolean;
255
+ isChromium: boolean;
256
+ isMobileSafari: boolean;
257
+ isYandex: boolean;
258
+ isMIUI: boolean;
259
+ isSamsungBrowser: boolean;
260
+ isElectron: boolean;
261
+ osVersion: string;
262
+ osName: string;
263
+ fullBrowserVersion: string;
264
+ browserVersion: string;
265
+ browserName: string;
266
+ mobileVendor: string;
267
+ mobileModel: string;
268
+ engineName: string;
269
+ engineVersion: string;
270
+ getUA: string;
271
+ deviceType: string;
272
+ isIOS13: boolean;
273
+ isIPad13: boolean;
274
+ isIPhone13: boolean;
275
+ isIPod13: boolean;
276
+ };
277
+ deviceData: {
278
+ deviceType: string;
279
+ osName: string;
280
+ osVersion: string;
281
+ browserName: string;
282
+ browserVersion: string;
283
+ fullBrowserVersion: string;
284
+ mobileVendor: string;
285
+ mobileModel: string;
286
+ engineName: string;
287
+ engineVersion: string;
288
+ getUA: string;
289
+ };
290
+ };
291
+ type DeviceDetectResult = ReturnType<typeof useDeviceDetect>;
292
+
293
+ /**
294
+ * Advanced browser detection hook
295
+ *
296
+ * Detects modern browsers including Chromium-based browsers that may
297
+ * incorrectly report as Safari (Arc, Brave, Vivaldi, Comet, etc.)
298
+ */
299
+ interface BrowserInfo {
300
+ isChrome: boolean;
301
+ isChromium: boolean;
302
+ isSafari: boolean;
303
+ isFirefox: boolean;
304
+ isEdge: boolean;
305
+ isOpera: boolean;
306
+ isBrave: boolean;
307
+ isArc: boolean;
308
+ isVivaldi: boolean;
309
+ isYandex: boolean;
310
+ isSamsungBrowser: boolean;
311
+ isUCBrowser: boolean;
312
+ isComet: boolean;
313
+ isOperaMini: boolean;
314
+ isIE: boolean;
315
+ isFacebookInApp: boolean;
316
+ isInstagramInApp: boolean;
317
+ isTikTokInApp: boolean;
318
+ isSnapchatInApp: boolean;
319
+ isWeChatInApp: boolean;
320
+ isThreadsInApp: boolean;
321
+ isLinkedInInApp: boolean;
322
+ isTwitterInApp: boolean;
323
+ isInAppBrowser: boolean;
324
+ isWebView: boolean;
325
+ browserName: string;
326
+ isWebKit: boolean;
327
+ isBlink: boolean;
328
+ isGecko: boolean;
329
+ /**
330
+ * Whether the browser supports Web Push Notifications.
331
+ * Returns false for browsers known to NOT support push:
332
+ * - Opera Mini (no service worker support)
333
+ * - Internet Explorer (deprecated, no Push API)
334
+ * - UC Browser (unreliable push support)
335
+ * - In-App browsers (Facebook, Instagram, TikTok, Snapchat, etc.)
336
+ * - Generic WebViews (except Twitter/LinkedIn on Android which use Chrome WebView)
337
+ *
338
+ * Note: Comet (Perplexity) is Chromium-based and DOES support push notifications.
339
+ * Note: This is a browser-level check. For full push support,
340
+ * also check 'serviceWorker' in navigator && 'PushManager' in window
341
+ */
342
+ supportsPushNotifications: boolean;
343
+ isIOSBrowser: boolean;
344
+ userAgent: string;
345
+ }
346
+ /**
347
+ * Detect browser with improved accuracy for Chromium-based browsers
348
+ *
349
+ * @example
350
+ * ```tsx
351
+ * const browser = useBrowserDetect();
352
+ *
353
+ * if (browser.isSafari && !browser.isChromium) {
354
+ * // Real Safari
355
+ * }
356
+ *
357
+ * if (browser.isChromium) {
358
+ * // Any Chromium-based browser (Chrome, Edge, Brave, Arc, etc.)
359
+ * }
360
+ * ```
361
+ */
362
+ declare function useBrowserDetect(): BrowserInfo;
363
+
364
+ export { type BrowserInfo, type DeviceDetectResult, type ResolvedTheme, type UseHotkeyOptions, useBrowserDetect, useCfgRouter, useDeviceDetect, useHotkey, useQueryParams, useResolvedTheme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-nextjs",
3
- "version": "2.1.102",
3
+ "version": "2.1.103",
4
4
  "description": "Next.js UI component library with Radix UI primitives, Tailwind CSS styling, charts, and form components",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -83,9 +83,9 @@
83
83
  "check": "tsc --noEmit"
84
84
  },
85
85
  "peerDependencies": {
86
- "@djangocfg/api": "^2.1.102",
87
- "@djangocfg/ui-core": "^2.1.102",
88
- "@djangocfg/ui-tools": "^2.1.102",
86
+ "@djangocfg/api": "^2.1.103",
87
+ "@djangocfg/ui-core": "^2.1.103",
88
+ "@djangocfg/ui-tools": "^2.1.103",
89
89
  "@types/react": "^19.1.0",
90
90
  "@types/react-dom": "^19.1.0",
91
91
  "consola": "^3.4.2",
@@ -109,7 +109,7 @@
109
109
  "react-hotkeys-hook": "^5.2.1"
110
110
  },
111
111
  "devDependencies": {
112
- "@djangocfg/typescript-config": "^2.1.102",
112
+ "@djangocfg/typescript-config": "^2.1.103",
113
113
  "@radix-ui/react-dropdown-menu": "^2.1.16",
114
114
  "@radix-ui/react-slot": "^1.2.4",
115
115
  "@types/node": "^24.7.2",