@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.
- package/dist/animations.d.mts +36 -0
- package/dist/animations.d.ts +36 -0
- package/dist/hooks.d.mts +364 -0
- package/dist/hooks.d.ts +364 -0
- package/package.json +5 -5
|
@@ -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 };
|
package/dist/hooks.d.mts
ADDED
|
@@ -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/dist/hooks.d.ts
ADDED
|
@@ -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.
|
|
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.
|
|
87
|
-
"@djangocfg/ui-core": "^2.1.
|
|
88
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
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.
|
|
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",
|