@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.4 → 0.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/CHANGELOG.md +1 -1
- package/dist/index.d.ts +131 -131
- package/dist/index.esm.js +148 -148
- package/dist/index.js +148 -148
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/ui/accessibility-demo.tsx +271 -0
- package/src/components/ui/advanced-component-architecture-demo.tsx +916 -0
- package/src/components/ui/advanced-transition-system-demo.tsx +670 -0
- package/src/components/ui/advanced-transition-system.tsx +395 -0
- package/src/components/ui/animation/animated-container.tsx +166 -0
- package/src/components/ui/animation/index.ts +19 -0
- package/src/components/ui/animation/staggered-container.tsx +68 -0
- package/src/components/ui/animation-demo.tsx +250 -0
- package/src/components/ui/badge.tsx +33 -0
- package/src/components/ui/battery-conscious-animation-demo.tsx +568 -0
- package/src/components/ui/border-radius-shadow-demo.tsx +187 -0
- package/src/components/ui/button.tsx +36 -0
- package/src/components/ui/card.tsx +207 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/color-preview.tsx +411 -0
- package/src/components/ui/data-display/chart.tsx +653 -0
- package/src/components/ui/data-display/data-grid-simple.tsx +76 -0
- package/src/components/ui/data-display/data-grid.tsx +680 -0
- package/src/components/ui/data-display/list.tsx +456 -0
- package/src/components/ui/data-display/table.tsx +482 -0
- package/src/components/ui/data-display/timeline.tsx +441 -0
- package/src/components/ui/data-display/tree.tsx +602 -0
- package/src/components/ui/data-display/types.ts +536 -0
- package/src/components/ui/enterprise-mobile-experience-demo.tsx +749 -0
- package/src/components/ui/enterprise-mobile-experience.tsx +464 -0
- package/src/components/ui/feedback/alert.tsx +157 -0
- package/src/components/ui/feedback/progress.tsx +292 -0
- package/src/components/ui/feedback/skeleton.tsx +185 -0
- package/src/components/ui/feedback/toast.tsx +280 -0
- package/src/components/ui/feedback/types.ts +125 -0
- package/src/components/ui/font-preview.tsx +288 -0
- package/src/components/ui/form-demo.tsx +553 -0
- package/src/components/ui/hardware-acceleration-demo.tsx +547 -0
- package/src/components/ui/input.tsx +35 -0
- package/src/components/ui/label.tsx +16 -0
- package/src/components/ui/layout-demo.tsx +367 -0
- package/src/components/ui/layouts/adaptive-layout.tsx +139 -0
- package/src/components/ui/layouts/desktop-layout.tsx +224 -0
- package/src/components/ui/layouts/index.ts +10 -0
- package/src/components/ui/layouts/mobile-layout.tsx +162 -0
- package/src/components/ui/layouts/tablet-layout.tsx +197 -0
- package/src/components/ui/mobile-form-validation.tsx +451 -0
- package/src/components/ui/mobile-input-demo.tsx +201 -0
- package/src/components/ui/mobile-input.tsx +281 -0
- package/src/components/ui/mobile-skeleton-loading-demo.tsx +638 -0
- package/src/components/ui/navigation/breadcrumb.tsx +158 -0
- package/src/components/ui/navigation/index.ts +36 -0
- package/src/components/ui/navigation/menu.tsx +374 -0
- package/src/components/ui/navigation/navigation-demo.tsx +324 -0
- package/src/components/ui/navigation/pagination.tsx +272 -0
- package/src/components/ui/navigation/sidebar.tsx +383 -0
- package/src/components/ui/navigation/stepper.tsx +303 -0
- package/src/components/ui/navigation/tabs.tsx +205 -0
- package/src/components/ui/navigation/types.ts +299 -0
- package/src/components/ui/overlay/backdrop.tsx +81 -0
- package/src/components/ui/overlay/focus-manager.tsx +143 -0
- package/src/components/ui/overlay/index.ts +36 -0
- package/src/components/ui/overlay/modal.tsx +270 -0
- package/src/components/ui/overlay/overlay-manager.tsx +110 -0
- package/src/components/ui/overlay/popover.tsx +462 -0
- package/src/components/ui/overlay/portal.tsx +79 -0
- package/src/components/ui/overlay/tooltip.tsx +303 -0
- package/src/components/ui/overlay/types.ts +196 -0
- package/src/components/ui/performance-demo.tsx +596 -0
- package/src/components/ui/semantic-input-system-demo.tsx +502 -0
- package/src/components/ui/semantic-input-system-demo.tsx.disabled +873 -0
- package/src/components/ui/tablet-layout.tsx +192 -0
- package/src/components/ui/theme-customizer.tsx +386 -0
- package/src/components/ui/theme-preview.tsx +310 -0
- package/src/components/ui/theme-switcher.tsx +264 -0
- package/src/components/ui/theme-toggle.tsx +38 -0
- package/src/components/ui/token-demo.tsx +195 -0
- package/src/components/ui/touch-demo.tsx +462 -0
- package/src/components/ui/touch-friendly-interface-demo.tsx +519 -0
- package/src/components/ui/touch-friendly-interface.tsx +296 -0
- package/src/hooks/index.ts +190 -0
- package/src/hooks/use-accessibility-support.ts +518 -0
- package/src/hooks/use-adaptive-layout.ts +289 -0
- package/src/hooks/use-advanced-patterns.ts +294 -0
- package/src/hooks/use-advanced-transition-system.ts +393 -0
- package/src/hooks/use-animation-profile.ts +288 -0
- package/src/hooks/use-battery-animations.ts +384 -0
- package/src/hooks/use-battery-conscious-loading.ts +475 -0
- package/src/hooks/use-battery-optimization.ts +330 -0
- package/src/hooks/use-battery-status.ts +299 -0
- package/src/hooks/use-component-performance.ts +344 -0
- package/src/hooks/use-device-loading-states.ts +459 -0
- package/src/hooks/use-device.tsx +110 -0
- package/src/hooks/use-enterprise-mobile-experience.ts +488 -0
- package/src/hooks/use-form-feedback.ts +403 -0
- package/src/hooks/use-form-performance.ts +513 -0
- package/src/hooks/use-frame-rate.ts +251 -0
- package/src/hooks/use-gestures.ts +338 -0
- package/src/hooks/use-hardware-acceleration.ts +341 -0
- package/src/hooks/use-input-accessibility.ts +455 -0
- package/src/hooks/use-input-performance.ts +506 -0
- package/src/hooks/use-layout-performance.ts +319 -0
- package/src/hooks/use-loading-accessibility.ts +535 -0
- package/src/hooks/use-loading-performance.ts +473 -0
- package/src/hooks/use-memory-usage.ts +287 -0
- package/src/hooks/use-mobile-form-layout.ts +464 -0
- package/src/hooks/use-mobile-form-validation.ts +518 -0
- package/src/hooks/use-mobile-keyboard-optimization.ts +472 -0
- package/src/hooks/use-mobile-layout.ts +302 -0
- package/src/hooks/use-mobile-optimization.ts +406 -0
- package/src/hooks/use-mobile-skeleton.ts +402 -0
- package/src/hooks/use-mobile-touch.ts +414 -0
- package/src/hooks/use-performance-throttling.ts +348 -0
- package/src/hooks/use-performance.ts +316 -0
- package/src/hooks/use-reusable-architecture.ts +414 -0
- package/src/hooks/use-semantic-input-types.ts +357 -0
- package/src/hooks/use-semantic-input.ts +565 -0
- package/src/hooks/use-tablet-layout.ts +384 -0
- package/src/hooks/use-touch-friendly-input.ts +524 -0
- package/src/hooks/use-touch-friendly-interface.ts +331 -0
- package/src/hooks/use-touch-optimization.ts +375 -0
- package/src/index.ts +279 -279
- package/src/lib/utils.ts +6 -0
- package/src/themes/README.md +272 -0
- package/src/themes/ThemeContext.tsx +31 -0
- package/src/themes/ThemeProvider.tsx +232 -0
- package/src/themes/accessibility/index.ts +27 -0
- package/src/themes/accessibility.ts +259 -0
- package/src/themes/aria-patterns.ts +420 -0
- package/src/themes/base-themes.ts +55 -0
- package/src/themes/colorManager.ts +380 -0
- package/src/themes/examples/dark-theme.ts +154 -0
- package/src/themes/examples/minimal-theme.ts +108 -0
- package/src/themes/focus-management.ts +701 -0
- package/src/themes/fontLoader.ts +201 -0
- package/src/themes/high-contrast.ts +621 -0
- package/src/themes/index.ts +19 -0
- package/src/themes/inheritance.ts +227 -0
- package/src/themes/keyboard-navigation.ts +550 -0
- package/src/themes/motion-reduction.ts +662 -0
- package/src/themes/navigation.ts +238 -0
- package/src/themes/screen-reader.ts +645 -0
- package/src/themes/systemThemeDetector.ts +182 -0
- package/src/themes/themeCSSUpdater.ts +262 -0
- package/src/themes/themePersistence.ts +238 -0
- package/src/themes/themes/default.ts +586 -0
- package/src/themes/themes/harvey.ts +554 -0
- package/src/themes/themes/stan-design.ts +683 -0
- package/src/themes/types.ts +460 -0
- package/src/themes/useSystemTheme.ts +48 -0
- package/src/themes/useTheme.ts +87 -0
- package/src/themes/validation.ts +462 -0
- package/src/tokens/index.ts +34 -0
- package/src/tokens/tokenExporter.ts +397 -0
- package/src/tokens/tokenGenerator.ts +276 -0
- package/src/tokens/tokenManager.ts +248 -0
- package/src/tokens/tokenValidator.ts +543 -0
- package/src/tokens/types.ts +78 -0
- package/src/utils/bundle-analyzer.ts +260 -0
- package/src/utils/bundle-splitting.ts +483 -0
- package/src/utils/lazy-loading.ts +441 -0
- package/src/utils/performance-monitor.ts +513 -0
- package/src/utils/tree-shaking.ts +274 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { useState, useCallback, useRef, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface DeviceLoadingStatesConfig {
|
|
4
|
+
enableMobileStates?: boolean
|
|
5
|
+
enableTabletStates?: boolean
|
|
6
|
+
enableDesktopStates?: boolean
|
|
7
|
+
enableAdaptiveBehavior?: boolean
|
|
8
|
+
enableOrientationSupport?: boolean
|
|
9
|
+
enablePerformanceAdaptation?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LoadingState {
|
|
13
|
+
id: string
|
|
14
|
+
name: string
|
|
15
|
+
type: 'skeleton' | 'spinner' | 'progress' | 'skeleton-spinner' | 'custom'
|
|
16
|
+
duration: number
|
|
17
|
+
complexity: 'low' | 'medium' | 'high'
|
|
18
|
+
mobileOptimized: boolean
|
|
19
|
+
tabletOptimized: boolean
|
|
20
|
+
desktopOptimized: boolean
|
|
21
|
+
orientation: 'portrait' | 'landscape' | 'both'
|
|
22
|
+
performance: 'low' | 'medium' | 'high'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DeviceSpecificState {
|
|
26
|
+
deviceType: 'mobile' | 'tablet' | 'desktop'
|
|
27
|
+
orientation: 'portrait' | 'landscape'
|
|
28
|
+
screenSize: 'small' | 'medium' | 'large'
|
|
29
|
+
performanceLevel: 'low' | 'medium' | 'high'
|
|
30
|
+
batteryLevel: 'critical' | 'low' | 'medium' | 'high'
|
|
31
|
+
loadingStates: LoadingState[]
|
|
32
|
+
activeState: LoadingState | null
|
|
33
|
+
adaptiveBehavior: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DeviceLoadingStatesCallbacks {
|
|
37
|
+
onLoadingStateChanged?: (state: LoadingState) => void
|
|
38
|
+
onDeviceStateChanged?: (deviceState: DeviceSpecificState) => void
|
|
39
|
+
onAdaptiveBehaviorEnabled?: (behavior: string) => void
|
|
40
|
+
onPerformanceAdapted?: (adaptation: string) => void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const useDeviceLoadingStates = (
|
|
44
|
+
config: DeviceLoadingStatesConfig = {},
|
|
45
|
+
callbacks: DeviceLoadingStatesCallbacks = {}
|
|
46
|
+
) => {
|
|
47
|
+
const {
|
|
48
|
+
enableMobileStates = true,
|
|
49
|
+
enableTabletStates = true,
|
|
50
|
+
enableDesktopStates = true,
|
|
51
|
+
enableAdaptiveBehavior: enableAdaptiveBehaviorConfig = true,
|
|
52
|
+
enablePerformanceAdaptation = true
|
|
53
|
+
} = config
|
|
54
|
+
|
|
55
|
+
const [deviceState, setDeviceState] = useState<DeviceSpecificState>({
|
|
56
|
+
deviceType: 'desktop',
|
|
57
|
+
orientation: 'landscape',
|
|
58
|
+
screenSize: 'large',
|
|
59
|
+
performanceLevel: 'high',
|
|
60
|
+
batteryLevel: 'high',
|
|
61
|
+
loadingStates: [],
|
|
62
|
+
activeState: null,
|
|
63
|
+
adaptiveBehavior: false
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const [isAdapting, setIsAdapting] = useState(false)
|
|
67
|
+
const [adaptations, setAdaptations] = useState<string[]>([])
|
|
68
|
+
const [performanceMetrics, setPerformanceMetrics] = useState({
|
|
69
|
+
frameRate: 60,
|
|
70
|
+
renderTime: 0,
|
|
71
|
+
memoryUsage: 0,
|
|
72
|
+
batteryDrain: 'low'
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
const performanceTimerRef = useRef<number | null>(null)
|
|
77
|
+
const batteryCheckRef = useRef<number | null>(null)
|
|
78
|
+
|
|
79
|
+
// Device detection
|
|
80
|
+
const detectDevice = useCallback(() => {
|
|
81
|
+
const width = window.innerWidth
|
|
82
|
+
const height = window.innerHeight
|
|
83
|
+
const isPortrait = height > width
|
|
84
|
+
|
|
85
|
+
let deviceType: 'mobile' | 'tablet' | 'desktop'
|
|
86
|
+
let screenSize: 'small' | 'medium' | 'large'
|
|
87
|
+
let performanceLevel: 'low' | 'medium' | 'high'
|
|
88
|
+
|
|
89
|
+
if (width <= 768) {
|
|
90
|
+
deviceType = 'mobile'
|
|
91
|
+
screenSize = 'small'
|
|
92
|
+
performanceLevel = 'low'
|
|
93
|
+
} else if (width <= 1024) {
|
|
94
|
+
deviceType = 'tablet'
|
|
95
|
+
screenSize = 'medium'
|
|
96
|
+
performanceLevel = 'medium'
|
|
97
|
+
} else {
|
|
98
|
+
deviceType = 'desktop'
|
|
99
|
+
screenSize = 'large'
|
|
100
|
+
performanceLevel = 'high'
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const newDeviceState: DeviceSpecificState = {
|
|
104
|
+
...deviceState,
|
|
105
|
+
deviceType,
|
|
106
|
+
orientation: isPortrait ? 'portrait' : 'landscape',
|
|
107
|
+
screenSize,
|
|
108
|
+
performanceLevel
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setDeviceState(newDeviceState)
|
|
112
|
+
callbacks.onDeviceStateChanged?.(newDeviceState)
|
|
113
|
+
|
|
114
|
+
return newDeviceState
|
|
115
|
+
}, [deviceState, callbacks])
|
|
116
|
+
|
|
117
|
+
// Create loading states
|
|
118
|
+
const createLoadingState = useCallback((
|
|
119
|
+
state: Omit<LoadingState, 'id'>
|
|
120
|
+
) => {
|
|
121
|
+
const id = `loading-state-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
|
122
|
+
const newState: LoadingState = {
|
|
123
|
+
...state,
|
|
124
|
+
id
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setDeviceState(prev => ({
|
|
128
|
+
...prev,
|
|
129
|
+
loadingStates: [...prev.loadingStates, newState]
|
|
130
|
+
}))
|
|
131
|
+
|
|
132
|
+
return id
|
|
133
|
+
}, [])
|
|
134
|
+
|
|
135
|
+
// Generate device-specific loading states
|
|
136
|
+
const generateDeviceLoadingStates = useCallback(() => {
|
|
137
|
+
const mobileStates: LoadingState[] = [
|
|
138
|
+
{
|
|
139
|
+
id: 'mobile-skeleton',
|
|
140
|
+
name: 'Mobile Skeleton',
|
|
141
|
+
type: 'skeleton',
|
|
142
|
+
duration: 1000,
|
|
143
|
+
complexity: 'low',
|
|
144
|
+
mobileOptimized: true,
|
|
145
|
+
tabletOptimized: false,
|
|
146
|
+
desktopOptimized: false,
|
|
147
|
+
orientation: 'both',
|
|
148
|
+
performance: 'low'
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: 'mobile-spinner',
|
|
152
|
+
name: 'Mobile Spinner',
|
|
153
|
+
type: 'spinner',
|
|
154
|
+
duration: 800,
|
|
155
|
+
complexity: 'low',
|
|
156
|
+
mobileOptimized: true,
|
|
157
|
+
tabletOptimized: false,
|
|
158
|
+
desktopOptimized: false,
|
|
159
|
+
orientation: 'both',
|
|
160
|
+
performance: 'low'
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
const tabletStates: LoadingState[] = [
|
|
165
|
+
{
|
|
166
|
+
id: 'tablet-skeleton-spinner',
|
|
167
|
+
name: 'Tablet Skeleton + Spinner',
|
|
168
|
+
type: 'skeleton-spinner',
|
|
169
|
+
duration: 1200,
|
|
170
|
+
complexity: 'medium',
|
|
171
|
+
mobileOptimized: false,
|
|
172
|
+
tabletOptimized: true,
|
|
173
|
+
desktopOptimized: false,
|
|
174
|
+
orientation: 'both',
|
|
175
|
+
performance: 'medium'
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'tablet-progress',
|
|
179
|
+
name: 'Tablet Progress',
|
|
180
|
+
type: 'progress',
|
|
181
|
+
duration: 1500,
|
|
182
|
+
complexity: 'medium',
|
|
183
|
+
mobileOptimized: false,
|
|
184
|
+
tabletOptimized: true,
|
|
185
|
+
desktopOptimized: false,
|
|
186
|
+
orientation: 'both',
|
|
187
|
+
performance: 'medium'
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
const desktopStates: LoadingState[] = [
|
|
192
|
+
{
|
|
193
|
+
id: 'desktop-skeleton',
|
|
194
|
+
name: 'Desktop Skeleton',
|
|
195
|
+
type: 'skeleton',
|
|
196
|
+
duration: 2000,
|
|
197
|
+
complexity: 'high',
|
|
198
|
+
mobileOptimized: false,
|
|
199
|
+
tabletOptimized: false,
|
|
200
|
+
desktopOptimized: true,
|
|
201
|
+
orientation: 'both',
|
|
202
|
+
performance: 'high'
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 'desktop-custom',
|
|
206
|
+
name: 'Desktop Custom',
|
|
207
|
+
type: 'custom',
|
|
208
|
+
duration: 2500,
|
|
209
|
+
complexity: 'high',
|
|
210
|
+
mobileOptimized: false,
|
|
211
|
+
tabletOptimized: false,
|
|
212
|
+
desktopOptimized: true,
|
|
213
|
+
orientation: 'both',
|
|
214
|
+
performance: 'high'
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
const allStates = [
|
|
219
|
+
...(enableMobileStates ? mobileStates : []),
|
|
220
|
+
...(enableTabletStates ? tabletStates : []),
|
|
221
|
+
...(enableDesktopStates ? desktopStates : [])
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
setDeviceState(prev => ({
|
|
225
|
+
...prev,
|
|
226
|
+
loadingStates: allStates
|
|
227
|
+
}))
|
|
228
|
+
|
|
229
|
+
return allStates
|
|
230
|
+
}, [enableMobileStates, enableTabletStates, enableDesktopStates])
|
|
231
|
+
|
|
232
|
+
// Set active loading state
|
|
233
|
+
const setActiveLoadingState = useCallback((stateId: string) => {
|
|
234
|
+
const state = deviceState.loadingStates.find(s => s.id === stateId)
|
|
235
|
+
if (state) {
|
|
236
|
+
setDeviceState(prev => ({
|
|
237
|
+
...prev,
|
|
238
|
+
activeState: state
|
|
239
|
+
}))
|
|
240
|
+
callbacks.onLoadingStateChanged?.(state)
|
|
241
|
+
}
|
|
242
|
+
}, [deviceState.loadingStates, callbacks])
|
|
243
|
+
|
|
244
|
+
// Adaptive behavior
|
|
245
|
+
const enableAdaptiveBehavior = useCallback(() => {
|
|
246
|
+
if (!enableAdaptiveBehaviorConfig) return
|
|
247
|
+
|
|
248
|
+
setIsAdapting(true)
|
|
249
|
+
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
const adaptiveFeatures = [
|
|
252
|
+
'Device-specific loading state adaptation enabled',
|
|
253
|
+
'Performance-based state selection active',
|
|
254
|
+
'Battery-conscious state switching enabled',
|
|
255
|
+
'Orientation-aware state adaptation active'
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
setAdaptations(prev => [...prev, ...adaptiveFeatures])
|
|
259
|
+
setDeviceState(prev => ({
|
|
260
|
+
...prev,
|
|
261
|
+
adaptiveBehavior: true
|
|
262
|
+
}))
|
|
263
|
+
setIsAdapting(false)
|
|
264
|
+
|
|
265
|
+
adaptiveFeatures.forEach(feature => {
|
|
266
|
+
callbacks.onAdaptiveBehaviorEnabled?.(feature)
|
|
267
|
+
})
|
|
268
|
+
}, 300)
|
|
269
|
+
}, [enableAdaptiveBehaviorConfig, callbacks])
|
|
270
|
+
|
|
271
|
+
// Performance adaptation
|
|
272
|
+
const adaptToPerformance = useCallback(() => {
|
|
273
|
+
if (!enablePerformanceAdaptation) return
|
|
274
|
+
|
|
275
|
+
setIsAdapting(true)
|
|
276
|
+
|
|
277
|
+
setTimeout(() => {
|
|
278
|
+
const performanceAdaptations = [
|
|
279
|
+
'Loading state complexity adapted to performance',
|
|
280
|
+
'Animation duration optimized for device capability',
|
|
281
|
+
'Memory usage optimized for loading states',
|
|
282
|
+
'Frame rate adaptation for smooth animations'
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
setAdaptations(prev => [...prev, ...performanceAdaptations])
|
|
286
|
+
setIsAdapting(false)
|
|
287
|
+
|
|
288
|
+
performanceAdaptations.forEach(adaptation => {
|
|
289
|
+
callbacks.onPerformanceAdapted?.(adaptation)
|
|
290
|
+
})
|
|
291
|
+
}, 250)
|
|
292
|
+
}, [enablePerformanceAdaptation, callbacks])
|
|
293
|
+
|
|
294
|
+
// Auto-adapt based on device state
|
|
295
|
+
const autoAdapt = useCallback(() => {
|
|
296
|
+
if (deviceState.adaptiveBehavior) {
|
|
297
|
+
enableAdaptiveBehavior()
|
|
298
|
+
adaptToPerformance()
|
|
299
|
+
}
|
|
300
|
+
}, [deviceState.adaptiveBehavior, enableAdaptiveBehavior, adaptToPerformance])
|
|
301
|
+
|
|
302
|
+
// Performance monitoring
|
|
303
|
+
const startPerformanceMonitoring = useCallback(() => {
|
|
304
|
+
if (!enablePerformanceAdaptation) return () => {}
|
|
305
|
+
|
|
306
|
+
let frameCount = 0
|
|
307
|
+
let lastTime = performance.now()
|
|
308
|
+
|
|
309
|
+
const measurePerformance = () => {
|
|
310
|
+
const currentTime = performance.now()
|
|
311
|
+
frameCount++
|
|
312
|
+
|
|
313
|
+
if (currentTime - lastTime >= 1000) {
|
|
314
|
+
const fps = Math.round((frameCount * 1000) / (currentTime - lastTime))
|
|
315
|
+
const renderTime = currentTime - lastTime
|
|
316
|
+
|
|
317
|
+
setPerformanceMetrics(prev => ({
|
|
318
|
+
...prev,
|
|
319
|
+
frameRate: fps,
|
|
320
|
+
renderTime
|
|
321
|
+
}))
|
|
322
|
+
|
|
323
|
+
frameCount = 0
|
|
324
|
+
lastTime = currentTime
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
performanceTimerRef.current = window.setTimeout(measurePerformance, 16)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
performanceTimerRef.current = window.setTimeout(measurePerformance, 16)
|
|
331
|
+
|
|
332
|
+
return () => {
|
|
333
|
+
if (performanceTimerRef.current) {
|
|
334
|
+
clearTimeout(performanceTimerRef.current)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}, [enablePerformanceAdaptation])
|
|
338
|
+
|
|
339
|
+
// Battery monitoring
|
|
340
|
+
const startBatteryMonitoring = useCallback(() => {
|
|
341
|
+
if (!enablePerformanceAdaptation) return () => {}
|
|
342
|
+
|
|
343
|
+
const checkBattery = async () => {
|
|
344
|
+
try {
|
|
345
|
+
if ('getBattery' in navigator) {
|
|
346
|
+
const battery = await (navigator as any).getBattery()
|
|
347
|
+
const level = battery.level
|
|
348
|
+
|
|
349
|
+
let batteryLevel: 'critical' | 'low' | 'medium' | 'high'
|
|
350
|
+
if (level <= 0.1) batteryLevel = 'critical'
|
|
351
|
+
else if (level <= 0.3) batteryLevel = 'low'
|
|
352
|
+
else if (level <= 0.7) batteryLevel = 'medium'
|
|
353
|
+
else batteryLevel = 'high'
|
|
354
|
+
|
|
355
|
+
setDeviceState(prev => ({
|
|
356
|
+
...prev,
|
|
357
|
+
batteryLevel
|
|
358
|
+
}))
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.warn('Battery monitoring failed:', error)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
batteryCheckRef.current = window.setInterval(checkBattery, 10000)
|
|
366
|
+
|
|
367
|
+
return () => {
|
|
368
|
+
if (batteryCheckRef.current) {
|
|
369
|
+
clearInterval(batteryCheckRef.current)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}, [enablePerformanceAdaptation])
|
|
373
|
+
|
|
374
|
+
// Initialize device detection and loading states
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
detectDevice()
|
|
377
|
+
generateDeviceLoadingStates()
|
|
378
|
+
|
|
379
|
+
// Set up resize listener
|
|
380
|
+
const handleResize = () => {
|
|
381
|
+
detectDevice()
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
window.addEventListener('resize', handleResize)
|
|
385
|
+
|
|
386
|
+
// Set up orientation change listener
|
|
387
|
+
const handleOrientationChange = () => {
|
|
388
|
+
setTimeout(detectDevice, 100)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
window.addEventListener('orientationchange', handleOrientationChange)
|
|
392
|
+
|
|
393
|
+
return () => {
|
|
394
|
+
window.removeEventListener('resize', handleResize)
|
|
395
|
+
window.removeEventListener('orientationchange', handleOrientationChange)
|
|
396
|
+
}
|
|
397
|
+
}, [detectDevice, generateDeviceLoadingStates])
|
|
398
|
+
|
|
399
|
+
// Auto-adapt when device state changes
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
autoAdapt()
|
|
402
|
+
}, [autoAdapt])
|
|
403
|
+
|
|
404
|
+
// Start monitoring
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
const stopPerformanceMonitoring = startPerformanceMonitoring()
|
|
407
|
+
const stopBatteryMonitoring = startBatteryMonitoring()
|
|
408
|
+
|
|
409
|
+
return () => {
|
|
410
|
+
stopPerformanceMonitoring()
|
|
411
|
+
stopBatteryMonitoring()
|
|
412
|
+
}
|
|
413
|
+
}, [startPerformanceMonitoring, startBatteryMonitoring])
|
|
414
|
+
|
|
415
|
+
// Cleanup on unmount
|
|
416
|
+
useEffect(() => {
|
|
417
|
+
return () => {
|
|
418
|
+
if (performanceTimerRef.current) {
|
|
419
|
+
clearTimeout(performanceTimerRef.current)
|
|
420
|
+
}
|
|
421
|
+
if (batteryCheckRef.current) {
|
|
422
|
+
clearInterval(batteryCheckRef.current)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}, [])
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
// State
|
|
429
|
+
deviceState,
|
|
430
|
+
isAdapting,
|
|
431
|
+
adaptations,
|
|
432
|
+
performanceMetrics,
|
|
433
|
+
|
|
434
|
+
// Functions
|
|
435
|
+
createLoadingState,
|
|
436
|
+
generateDeviceLoadingStates,
|
|
437
|
+
setActiveLoadingState,
|
|
438
|
+
enableAdaptiveBehavior,
|
|
439
|
+
adaptToPerformance,
|
|
440
|
+
autoAdapt,
|
|
441
|
+
|
|
442
|
+
// Utility functions
|
|
443
|
+
isMobileDevice: () => deviceState.deviceType === 'mobile',
|
|
444
|
+
isTabletDevice: () => deviceState.deviceType === 'tablet',
|
|
445
|
+
isDesktopDevice: () => deviceState.deviceType === 'desktop',
|
|
446
|
+
getActiveState: () => deviceState.activeState,
|
|
447
|
+
getLoadingStatesForDevice: () => deviceState.loadingStates.filter(state => {
|
|
448
|
+
switch (deviceState.deviceType) {
|
|
449
|
+
case 'mobile': return state.mobileOptimized
|
|
450
|
+
case 'tablet': return state.tabletOptimized
|
|
451
|
+
case 'desktop': return state.desktopOptimized
|
|
452
|
+
default: return false
|
|
453
|
+
}
|
|
454
|
+
}),
|
|
455
|
+
clearAdaptations: () => setAdaptations([])
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export default useDeviceLoadingStates
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface DeviceInfo {
|
|
4
|
+
isMobile: boolean
|
|
5
|
+
isTablet: boolean
|
|
6
|
+
isDesktop: boolean
|
|
7
|
+
screenSize: 'mobile' | 'tablet' | 'desktop'
|
|
8
|
+
orientation: 'portrait' | 'landscape'
|
|
9
|
+
touchDevice: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Breakpoints based on common device sizes
|
|
13
|
+
const BREAKPOINTS = {
|
|
14
|
+
mobile: 768,
|
|
15
|
+
tablet: 1024,
|
|
16
|
+
desktop: 1440
|
|
17
|
+
} as const
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Enhanced device detection hook with tablet-specific support
|
|
21
|
+
* Provides detailed device information including orientation and touch capabilities
|
|
22
|
+
*/
|
|
23
|
+
export const useDevice = (): DeviceInfo => {
|
|
24
|
+
const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>({
|
|
25
|
+
isMobile: false,
|
|
26
|
+
isTablet: false,
|
|
27
|
+
isDesktop: false,
|
|
28
|
+
screenSize: 'desktop',
|
|
29
|
+
orientation: 'landscape',
|
|
30
|
+
touchDevice: false
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const updateDeviceInfo = () => {
|
|
35
|
+
const width = window.innerWidth
|
|
36
|
+
const height = window.innerHeight
|
|
37
|
+
|
|
38
|
+
// Determine device type based on width
|
|
39
|
+
const isMobile = width < BREAKPOINTS.mobile
|
|
40
|
+
const isTablet = width >= BREAKPOINTS.mobile && width < BREAKPOINTS.tablet
|
|
41
|
+
const isDesktop = width >= BREAKPOINTS.tablet
|
|
42
|
+
|
|
43
|
+
// Determine screen size category
|
|
44
|
+
let screenSize: DeviceInfo['screenSize']
|
|
45
|
+
if (isMobile) screenSize = 'mobile'
|
|
46
|
+
else if (isTablet) screenSize = 'tablet'
|
|
47
|
+
else screenSize = 'desktop'
|
|
48
|
+
|
|
49
|
+
// Determine orientation
|
|
50
|
+
const orientation: DeviceInfo['orientation'] = width > height ? 'landscape' : 'portrait'
|
|
51
|
+
|
|
52
|
+
// Detect touch capability
|
|
53
|
+
const touchDevice = 'ontouchstart' in window ||
|
|
54
|
+
navigator.maxTouchPoints > 0 ||
|
|
55
|
+
// @ts-ignore - some browsers use this property
|
|
56
|
+
navigator.msMaxTouchPoints > 0
|
|
57
|
+
|
|
58
|
+
const newDeviceInfo = {
|
|
59
|
+
isMobile,
|
|
60
|
+
isTablet,
|
|
61
|
+
isDesktop,
|
|
62
|
+
screenSize,
|
|
63
|
+
orientation,
|
|
64
|
+
touchDevice
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setDeviceInfo(newDeviceInfo)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Initial detection
|
|
71
|
+
updateDeviceInfo()
|
|
72
|
+
|
|
73
|
+
// Listen for resize events
|
|
74
|
+
window.addEventListener('resize', updateDeviceInfo)
|
|
75
|
+
|
|
76
|
+
// Listen for orientation changes
|
|
77
|
+
window.addEventListener('orientationchange', () => {
|
|
78
|
+
// Small delay to ensure dimensions are updated
|
|
79
|
+
setTimeout(updateDeviceInfo, 100)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return () => {
|
|
83
|
+
window.removeEventListener('resize', updateDeviceInfo)
|
|
84
|
+
window.removeEventListener('orientationchange', updateDeviceInfo)
|
|
85
|
+
}
|
|
86
|
+
}, [])
|
|
87
|
+
|
|
88
|
+
return deviceInfo
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Convenience hooks for specific use cases
|
|
92
|
+
export const useIsMobile = () => useDevice().isMobile
|
|
93
|
+
export const useIsTablet = () => useDevice().isTablet
|
|
94
|
+
export const useIsDesktop = () => useDevice().isDesktop
|
|
95
|
+
export const useOrientation = () => useDevice().orientation
|
|
96
|
+
export const useIsTouchDevice = () => useDevice().touchDevice
|
|
97
|
+
|
|
98
|
+
// Utility function to get device-specific classes
|
|
99
|
+
export const getDeviceClasses = (deviceInfo: DeviceInfo): string => {
|
|
100
|
+
const classes = []
|
|
101
|
+
|
|
102
|
+
classes.push(`screen-${deviceInfo.screenSize}`)
|
|
103
|
+
classes.push(`orientation-${deviceInfo.orientation}`)
|
|
104
|
+
|
|
105
|
+
if (deviceInfo.touchDevice) {
|
|
106
|
+
classes.push('touch-device')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return classes.join(' ')
|
|
110
|
+
}
|