@djangocfg/ui-nextjs 2.1.30 → 2.1.31

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-nextjs",
3
- "version": "2.1.30",
3
+ "version": "2.1.31",
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",
@@ -58,8 +58,8 @@
58
58
  "check": "tsc --noEmit"
59
59
  },
60
60
  "peerDependencies": {
61
- "@djangocfg/api": "^2.1.30",
62
- "@djangocfg/ui-core": "^2.1.30",
61
+ "@djangocfg/api": "^2.1.31",
62
+ "@djangocfg/ui-core": "^2.1.31",
63
63
  "@types/react": "^19.1.0",
64
64
  "@types/react-dom": "^19.1.0",
65
65
  "consola": "^3.4.2",
@@ -106,7 +106,7 @@
106
106
  "vidstack": "next"
107
107
  },
108
108
  "devDependencies": {
109
- "@djangocfg/typescript-config": "^2.1.30",
109
+ "@djangocfg/typescript-config": "^2.1.31",
110
110
  "@types/node": "^24.7.2",
111
111
  "eslint": "^9.37.0",
112
112
  "tailwindcss-animate": "1.0.7",
@@ -26,3 +26,7 @@ export type { UseHotkeyOptions, HotkeyCallback, Keys } from './useHotkey';
26
26
  // Device detection
27
27
  export { useDeviceDetect } from './useDeviceDetect';
28
28
  export type { DeviceDetectResult } from './useDeviceDetect';
29
+
30
+ // Browser detection (advanced - detects Chromium browsers correctly)
31
+ export { useBrowserDetect } from './useBrowserDetect';
32
+ export type { BrowserInfo } from './useBrowserDetect';
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Advanced browser detection hook
3
+ *
4
+ * Detects modern browsers including Chromium-based browsers that may
5
+ * incorrectly report as Safari (Arc, Brave, Vivaldi, Comet, etc.)
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import { useMemo } from 'react';
11
+
12
+ export interface BrowserInfo {
13
+ // Core browser types
14
+ isChrome: boolean;
15
+ isChromium: boolean; // Any Chromium-based browser
16
+ isSafari: boolean; // Real Safari (WebKit on macOS/iOS)
17
+ isFirefox: boolean;
18
+ isEdge: boolean;
19
+ isOpera: boolean;
20
+
21
+ // Modern Chromium-based browsers
22
+ isBrave: boolean;
23
+ isArc: boolean;
24
+ isVivaldi: boolean;
25
+ isYandex: boolean;
26
+ isSamsungBrowser: boolean;
27
+ isUCBrowser: boolean;
28
+
29
+ // Browser name
30
+ browserName: string;
31
+
32
+ // Engine
33
+ isWebKit: boolean; // Safari's engine
34
+ isBlink: boolean; // Chromium's engine
35
+ isGecko: boolean; // Firefox's engine
36
+
37
+ // For debugging
38
+ userAgent: string;
39
+ }
40
+
41
+ /**
42
+ * Detect browser with improved accuracy for Chromium-based browsers
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * const browser = useBrowserDetect();
47
+ *
48
+ * if (browser.isSafari && !browser.isChromium) {
49
+ * // Real Safari
50
+ * }
51
+ *
52
+ * if (browser.isChromium) {
53
+ * // Any Chromium-based browser (Chrome, Edge, Brave, Arc, etc.)
54
+ * }
55
+ * ```
56
+ */
57
+ export function useBrowserDetect(): BrowserInfo {
58
+ return useMemo(() => {
59
+ if (typeof window === 'undefined') {
60
+ return {
61
+ isChrome: false,
62
+ isChromium: false,
63
+ isSafari: false,
64
+ isFirefox: false,
65
+ isEdge: false,
66
+ isOpera: false,
67
+ isBrave: false,
68
+ isArc: false,
69
+ isVivaldi: false,
70
+ isYandex: false,
71
+ isSamsungBrowser: false,
72
+ isUCBrowser: false,
73
+ browserName: 'unknown',
74
+ isWebKit: false,
75
+ isBlink: false,
76
+ isGecko: false,
77
+ userAgent: '',
78
+ };
79
+ }
80
+
81
+ const ua = window.navigator.userAgent.toLowerCase();
82
+
83
+ // Check for specific browsers first (most specific to least specific)
84
+
85
+ // Edge (Chromium-based)
86
+ const isEdge = ua.includes('edg/') || ua.includes('edge/');
87
+
88
+ // Brave (check for Brave-specific API)
89
+ const isBrave = !!(window.navigator as any).brave;
90
+
91
+ // Arc (check for Arc-specific markers in UA)
92
+ const isArc = ua.includes('arc/');
93
+
94
+ // Vivaldi
95
+ const isVivaldi = ua.includes('vivaldi');
96
+
97
+ // Yandex Browser
98
+ const isYandex = ua.includes('yabrowser');
99
+
100
+ // Samsung Internet
101
+ const isSamsungBrowser = ua.includes('samsungbrowser');
102
+
103
+ // UC Browser
104
+ const isUCBrowser = ua.includes('ucbrowser') || ua.includes('uc browser');
105
+
106
+ // Opera (modern Chromium-based)
107
+ const isOpera = ua.includes('opr/') || ua.includes('opera');
108
+
109
+ // Chrome (not Edge, not other Chromium browsers)
110
+ const isChrome = ua.includes('chrome') &&
111
+ !isEdge &&
112
+ !isOpera &&
113
+ !isYandex &&
114
+ !isSamsungBrowser &&
115
+ !isVivaldi &&
116
+ !isArc &&
117
+ !isBrave;
118
+
119
+ // Firefox
120
+ const isFirefox = ua.includes('firefox') && !ua.includes('seamonkey');
121
+
122
+ // Safari (real Safari, not Chromium pretending to be Safari)
123
+ // Safari will have 'safari' in UA but NOT 'chrome' or 'chromium'
124
+ // Real Safari uses WebKit engine
125
+ // Additional check: Safari has 'version/' in UA, Chromium browsers don't combine it with Safari
126
+ const hasSafariUA = ua.includes('safari');
127
+ const hasChrome = ua.includes('chrome') || ua.includes('crios');
128
+ const hasVersion = ua.includes('version/'); // Real Safari includes Version/XX.X
129
+ const isSafari = hasSafariUA && !hasChrome && hasVersion;
130
+
131
+ // Chromium detection (any browser using Chromium/Blink engine)
132
+ // If it has "chrome" in UA or is one of the known Chromium browsers
133
+ const isChromium = hasChrome ||
134
+ isEdge ||
135
+ isOpera ||
136
+ isYandex ||
137
+ isSamsungBrowser ||
138
+ isVivaldi ||
139
+ isArc ||
140
+ isBrave ||
141
+ isUCBrowser;
142
+
143
+ // Engine detection
144
+ const isWebKit = !isChromium && isSafari;
145
+ const isBlink = isChromium;
146
+ const isGecko = isFirefox;
147
+
148
+ // Determine browser name
149
+ let browserName = 'unknown';
150
+ if (isBrave) browserName = 'Brave';
151
+ else if (isArc) browserName = 'Arc';
152
+ else if (isVivaldi) browserName = 'Vivaldi';
153
+ else if (isYandex) browserName = 'Yandex';
154
+ else if (isSamsungBrowser) browserName = 'Samsung Internet';
155
+ else if (isUCBrowser) browserName = 'UC Browser';
156
+ else if (isEdge) browserName = 'Edge';
157
+ else if (isOpera) browserName = 'Opera';
158
+ else if (isChrome) browserName = 'Chrome';
159
+ else if (isSafari) browserName = 'Safari';
160
+ else if (isFirefox) browserName = 'Firefox';
161
+
162
+ return {
163
+ isChrome,
164
+ isChromium,
165
+ isSafari,
166
+ isFirefox,
167
+ isEdge,
168
+ isOpera,
169
+ isBrave,
170
+ isArc,
171
+ isVivaldi,
172
+ isYandex,
173
+ isSamsungBrowser,
174
+ isUCBrowser,
175
+ browserName,
176
+ isWebKit,
177
+ isBlink,
178
+ isGecko,
179
+ userAgent: window.navigator.userAgent,
180
+ };
181
+ }, []);
182
+ }