@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.4 → 0.1.6

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.
Files changed (164) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/index.d.ts +131 -131
  3. package/dist/index.esm.js +148 -148
  4. package/dist/index.js +148 -148
  5. package/dist/styles.css +1 -1
  6. package/package.json +1 -1
  7. package/src/components/ui/accessibility-demo.tsx +271 -0
  8. package/src/components/ui/advanced-component-architecture-demo.tsx +916 -0
  9. package/src/components/ui/advanced-transition-system-demo.tsx +670 -0
  10. package/src/components/ui/advanced-transition-system.tsx +395 -0
  11. package/src/components/ui/animation/animated-container.tsx +166 -0
  12. package/src/components/ui/animation/index.ts +19 -0
  13. package/src/components/ui/animation/staggered-container.tsx +68 -0
  14. package/src/components/ui/animation-demo.tsx +250 -0
  15. package/src/components/ui/badge.tsx +33 -0
  16. package/src/components/ui/battery-conscious-animation-demo.tsx +568 -0
  17. package/src/components/ui/border-radius-shadow-demo.tsx +187 -0
  18. package/src/components/ui/button.tsx +36 -0
  19. package/src/components/ui/card.tsx +207 -0
  20. package/src/components/ui/checkbox.tsx +30 -0
  21. package/src/components/ui/color-preview.tsx +411 -0
  22. package/src/components/ui/data-display/chart.tsx +653 -0
  23. package/src/components/ui/data-display/data-grid-simple.tsx +76 -0
  24. package/src/components/ui/data-display/data-grid.tsx +680 -0
  25. package/src/components/ui/data-display/list.tsx +456 -0
  26. package/src/components/ui/data-display/table.tsx +482 -0
  27. package/src/components/ui/data-display/timeline.tsx +441 -0
  28. package/src/components/ui/data-display/tree.tsx +602 -0
  29. package/src/components/ui/data-display/types.ts +536 -0
  30. package/src/components/ui/enterprise-mobile-experience-demo.tsx +749 -0
  31. package/src/components/ui/enterprise-mobile-experience.tsx +464 -0
  32. package/src/components/ui/feedback/alert.tsx +157 -0
  33. package/src/components/ui/feedback/progress.tsx +292 -0
  34. package/src/components/ui/feedback/skeleton.tsx +185 -0
  35. package/src/components/ui/feedback/toast.tsx +280 -0
  36. package/src/components/ui/feedback/types.ts +125 -0
  37. package/src/components/ui/font-preview.tsx +288 -0
  38. package/src/components/ui/form-demo.tsx +553 -0
  39. package/src/components/ui/hardware-acceleration-demo.tsx +547 -0
  40. package/src/components/ui/input.tsx +35 -0
  41. package/src/components/ui/label.tsx +16 -0
  42. package/src/components/ui/layout-demo.tsx +367 -0
  43. package/src/components/ui/layouts/adaptive-layout.tsx +139 -0
  44. package/src/components/ui/layouts/desktop-layout.tsx +224 -0
  45. package/src/components/ui/layouts/index.ts +10 -0
  46. package/src/components/ui/layouts/mobile-layout.tsx +162 -0
  47. package/src/components/ui/layouts/tablet-layout.tsx +197 -0
  48. package/src/components/ui/mobile-form-validation.tsx +451 -0
  49. package/src/components/ui/mobile-input-demo.tsx +201 -0
  50. package/src/components/ui/mobile-input.tsx +281 -0
  51. package/src/components/ui/mobile-skeleton-loading-demo.tsx +638 -0
  52. package/src/components/ui/navigation/breadcrumb.tsx +158 -0
  53. package/src/components/ui/navigation/index.ts +36 -0
  54. package/src/components/ui/navigation/menu.tsx +374 -0
  55. package/src/components/ui/navigation/navigation-demo.tsx +324 -0
  56. package/src/components/ui/navigation/pagination.tsx +272 -0
  57. package/src/components/ui/navigation/sidebar.tsx +383 -0
  58. package/src/components/ui/navigation/stepper.tsx +303 -0
  59. package/src/components/ui/navigation/tabs.tsx +205 -0
  60. package/src/components/ui/navigation/types.ts +299 -0
  61. package/src/components/ui/overlay/backdrop.tsx +81 -0
  62. package/src/components/ui/overlay/focus-manager.tsx +143 -0
  63. package/src/components/ui/overlay/index.ts +36 -0
  64. package/src/components/ui/overlay/modal.tsx +270 -0
  65. package/src/components/ui/overlay/overlay-manager.tsx +110 -0
  66. package/src/components/ui/overlay/popover.tsx +462 -0
  67. package/src/components/ui/overlay/portal.tsx +79 -0
  68. package/src/components/ui/overlay/tooltip.tsx +303 -0
  69. package/src/components/ui/overlay/types.ts +196 -0
  70. package/src/components/ui/performance-demo.tsx +596 -0
  71. package/src/components/ui/semantic-input-system-demo.tsx +502 -0
  72. package/src/components/ui/semantic-input-system-demo.tsx.disabled +873 -0
  73. package/src/components/ui/tablet-layout.tsx +192 -0
  74. package/src/components/ui/theme-customizer.tsx +386 -0
  75. package/src/components/ui/theme-preview.tsx +310 -0
  76. package/src/components/ui/theme-switcher.tsx +264 -0
  77. package/src/components/ui/theme-toggle.tsx +38 -0
  78. package/src/components/ui/token-demo.tsx +195 -0
  79. package/src/components/ui/touch-demo.tsx +462 -0
  80. package/src/components/ui/touch-friendly-interface-demo.tsx +519 -0
  81. package/src/components/ui/touch-friendly-interface.tsx +296 -0
  82. package/src/hooks/index.ts +190 -0
  83. package/src/hooks/use-accessibility-support.ts +518 -0
  84. package/src/hooks/use-adaptive-layout.ts +289 -0
  85. package/src/hooks/use-advanced-patterns.ts +294 -0
  86. package/src/hooks/use-advanced-transition-system.ts +393 -0
  87. package/src/hooks/use-animation-profile.ts +288 -0
  88. package/src/hooks/use-battery-animations.ts +384 -0
  89. package/src/hooks/use-battery-conscious-loading.ts +475 -0
  90. package/src/hooks/use-battery-optimization.ts +330 -0
  91. package/src/hooks/use-battery-status.ts +299 -0
  92. package/src/hooks/use-component-performance.ts +344 -0
  93. package/src/hooks/use-device-loading-states.ts +459 -0
  94. package/src/hooks/use-device.tsx +110 -0
  95. package/src/hooks/use-enterprise-mobile-experience.ts +488 -0
  96. package/src/hooks/use-form-feedback.ts +403 -0
  97. package/src/hooks/use-form-performance.ts +513 -0
  98. package/src/hooks/use-frame-rate.ts +251 -0
  99. package/src/hooks/use-gestures.ts +338 -0
  100. package/src/hooks/use-hardware-acceleration.ts +341 -0
  101. package/src/hooks/use-input-accessibility.ts +455 -0
  102. package/src/hooks/use-input-performance.ts +506 -0
  103. package/src/hooks/use-layout-performance.ts +319 -0
  104. package/src/hooks/use-loading-accessibility.ts +535 -0
  105. package/src/hooks/use-loading-performance.ts +473 -0
  106. package/src/hooks/use-memory-usage.ts +287 -0
  107. package/src/hooks/use-mobile-form-layout.ts +464 -0
  108. package/src/hooks/use-mobile-form-validation.ts +518 -0
  109. package/src/hooks/use-mobile-keyboard-optimization.ts +472 -0
  110. package/src/hooks/use-mobile-layout.ts +302 -0
  111. package/src/hooks/use-mobile-optimization.ts +406 -0
  112. package/src/hooks/use-mobile-skeleton.ts +402 -0
  113. package/src/hooks/use-mobile-touch.ts +414 -0
  114. package/src/hooks/use-performance-throttling.ts +348 -0
  115. package/src/hooks/use-performance.ts +316 -0
  116. package/src/hooks/use-reusable-architecture.ts +414 -0
  117. package/src/hooks/use-semantic-input-types.ts +357 -0
  118. package/src/hooks/use-semantic-input.ts +565 -0
  119. package/src/hooks/use-tablet-layout.ts +384 -0
  120. package/src/hooks/use-touch-friendly-input.ts +524 -0
  121. package/src/hooks/use-touch-friendly-interface.ts +331 -0
  122. package/src/hooks/use-touch-optimization.ts +375 -0
  123. package/src/index.ts +279 -279
  124. package/src/lib/utils.ts +6 -0
  125. package/src/themes/README.md +272 -0
  126. package/src/themes/ThemeContext.tsx +31 -0
  127. package/src/themes/ThemeProvider.tsx +232 -0
  128. package/src/themes/accessibility/index.ts +27 -0
  129. package/src/themes/accessibility.ts +259 -0
  130. package/src/themes/aria-patterns.ts +420 -0
  131. package/src/themes/base-themes.ts +55 -0
  132. package/src/themes/colorManager.ts +380 -0
  133. package/src/themes/examples/dark-theme.ts +154 -0
  134. package/src/themes/examples/minimal-theme.ts +108 -0
  135. package/src/themes/focus-management.ts +701 -0
  136. package/src/themes/fontLoader.ts +201 -0
  137. package/src/themes/high-contrast.ts +621 -0
  138. package/src/themes/index.ts +19 -0
  139. package/src/themes/inheritance.ts +227 -0
  140. package/src/themes/keyboard-navigation.ts +550 -0
  141. package/src/themes/motion-reduction.ts +662 -0
  142. package/src/themes/navigation.ts +238 -0
  143. package/src/themes/screen-reader.ts +645 -0
  144. package/src/themes/systemThemeDetector.ts +182 -0
  145. package/src/themes/themeCSSUpdater.ts +262 -0
  146. package/src/themes/themePersistence.ts +238 -0
  147. package/src/themes/themes/default.ts +586 -0
  148. package/src/themes/themes/harvey.ts +554 -0
  149. package/src/themes/themes/stan-design.ts +683 -0
  150. package/src/themes/types.ts +460 -0
  151. package/src/themes/useSystemTheme.ts +48 -0
  152. package/src/themes/useTheme.ts +87 -0
  153. package/src/themes/validation.ts +462 -0
  154. package/src/tokens/index.ts +34 -0
  155. package/src/tokens/tokenExporter.ts +397 -0
  156. package/src/tokens/tokenGenerator.ts +276 -0
  157. package/src/tokens/tokenManager.ts +248 -0
  158. package/src/tokens/tokenValidator.ts +543 -0
  159. package/src/tokens/types.ts +78 -0
  160. package/src/utils/bundle-analyzer.ts +260 -0
  161. package/src/utils/bundle-splitting.ts +483 -0
  162. package/src/utils/lazy-loading.ts +441 -0
  163. package/src/utils/performance-monitor.ts +513 -0
  164. package/src/utils/tree-shaking.ts +274 -0
@@ -0,0 +1,310 @@
1
+ import React from 'react';
2
+ import { useTheme } from '../../themes';
3
+ import { Card, CardContent, CardHeader, CardTitle } from './card';
4
+ import { Badge } from './badge';
5
+
6
+ export interface ThemePreviewProps {
7
+ themeName?: string;
8
+ showColors?: boolean;
9
+ showFonts?: boolean;
10
+ showNavigation?: boolean;
11
+ showSpacing?: boolean;
12
+ className?: string;
13
+ }
14
+
15
+ export const ThemePreview: React.FC<ThemePreviewProps> = ({
16
+ themeName,
17
+ showColors = true,
18
+ showFonts = true,
19
+ showNavigation = true,
20
+ showSpacing = true,
21
+ className = ''
22
+ }) => {
23
+ const { getTheme, currentTheme } = useTheme();
24
+ const targetTheme = themeName || currentTheme;
25
+ const themeConfig = getTheme(targetTheme);
26
+
27
+ if (!themeConfig) {
28
+ return (
29
+ <div className={`p-4 text-center text-gray-500 ${className}`}>
30
+ Theme "{targetTheme}" not found
31
+ </div>
32
+ );
33
+ }
34
+
35
+ const renderColorPreview = () => {
36
+ if (!showColors) return null;
37
+
38
+ const { colors } = themeConfig;
39
+
40
+ return (
41
+ <div className="space-y-4">
42
+ <h4 className="text-lg font-semibold">Colors</h4>
43
+
44
+ {/* Primary Colors */}
45
+ <div>
46
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
47
+ Primary Colors
48
+ </h5>
49
+ <div className="grid grid-cols-5 gap-2">
50
+ {Object.entries(colors.primary).map(([shade, color]) => (
51
+ <div key={shade} className="text-center">
52
+ <div
53
+ className="w-12 h-12 rounded-lg border border-gray-200 dark:border-gray-700 mb-1"
54
+ style={{ backgroundColor: color }}
55
+ />
56
+ <span className="text-xs text-gray-600 dark:text-gray-400">{shade}</span>
57
+ </div>
58
+ ))}
59
+ </div>
60
+ </div>
61
+
62
+ {/* Semantic Colors */}
63
+ <div>
64
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
65
+ Semantic Colors
66
+ </h5>
67
+ <div className="grid grid-cols-2 gap-2">
68
+ {Object.entries(colors.semantic).map(([name, color]) => (
69
+ <div key={name} className="flex items-center gap-2">
70
+ <div
71
+ className="w-6 h-6 rounded border border-gray-200 dark:border-gray-700"
72
+ style={{ backgroundColor: color }}
73
+ />
74
+ <span className="text-sm text-gray-600 dark:text-gray-400 capitalize">
75
+ {name.replace(/([A-Z])/g, ' $1').trim()}
76
+ </span>
77
+ </div>
78
+ ))}
79
+ </div>
80
+ </div>
81
+
82
+ {/* Neutral Colors */}
83
+ <div>
84
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
85
+ Neutral Colors
86
+ </h5>
87
+ <div className="grid grid-cols-5 gap-2">
88
+ {Object.entries(colors.neutral).map(([shade, color]) => (
89
+ <div key={shade} className="text-center">
90
+ <div
91
+ className="w-12 h-12 rounded-lg border border-gray-200 dark:border-gray-700 mb-1"
92
+ style={{ backgroundColor: color }}
93
+ />
94
+ <span className="text-xs text-gray-600 dark:text-gray-400">{shade}</span>
95
+ </div>
96
+ ))}
97
+ </div>
98
+ </div>
99
+ </div>
100
+ );
101
+ };
102
+
103
+ const renderFontPreview = () => {
104
+ if (!showFonts) return null;
105
+
106
+ const { fonts } = themeConfig;
107
+
108
+ return (
109
+ <div className="space-y-4">
110
+ <h4 className="text-lg font-semibold">Typography</h4>
111
+
112
+ {/* Primary Font */}
113
+ <div>
114
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
115
+ Primary Font: {fonts.primary.family}
116
+ </h5>
117
+ <div className="space-y-2">
118
+ {Object.entries(fonts.primary.sizes).map(([size, value]) => (
119
+ <div key={size} className="flex items-center gap-4">
120
+ <span className="text-xs text-gray-500 w-12">{size}:</span>
121
+ <span
122
+ className="font-primary"
123
+ style={{
124
+ fontSize: value,
125
+ fontWeight: fonts.primary.weights[0] || 400
126
+ }}
127
+ >
128
+ The quick brown fox jumps over the lazy dog
129
+ </span>
130
+ </div>
131
+ ))}
132
+ </div>
133
+ </div>
134
+
135
+ {/* Font Weights */}
136
+ <div>
137
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
138
+ Font Weights
139
+ </h5>
140
+ <div className="space-y-2">
141
+ {fonts.primary.weights.map((weight) => (
142
+ <div key={weight} className="flex items-center gap-4">
143
+ <span className="text-xs text-gray-500 w-12">{weight}:</span>
144
+ <span
145
+ className="font-primary"
146
+ style={{
147
+ fontSize: '1rem',
148
+ fontWeight: weight
149
+ }}
150
+ >
151
+ The quick brown fox jumps over the lazy dog
152
+ </span>
153
+ </div>
154
+ ))}
155
+ </div>
156
+ </div>
157
+ </div>
158
+ );
159
+ };
160
+
161
+ const renderNavigationPreview = () => {
162
+ if (!showNavigation) return null;
163
+
164
+ const { navigation } = themeConfig;
165
+
166
+ return (
167
+ <div className="space-y-4">
168
+ <h4 className="text-lg font-semibold">Navigation</h4>
169
+
170
+ <div className="grid grid-cols-2 gap-4">
171
+ <div>
172
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
173
+ Configuration
174
+ </h5>
175
+ <div className="space-y-2 text-sm">
176
+ <div className="flex justify-between">
177
+ <span className="text-gray-600 dark:text-gray-400">Layout:</span>
178
+ <Badge variant="outline">{navigation.layout}</Badge>
179
+ </div>
180
+ <div className="flex justify-between">
181
+ <span className="text-gray-600 dark:text-gray-400">Style:</span>
182
+ <Badge variant="outline">{navigation.style}</Badge>
183
+ </div>
184
+ <div className="flex justify-between">
185
+ <span className="text-gray-600 dark:text-gray-400">Behavior:</span>
186
+ <Badge variant="outline">{navigation.behavior}</Badge>
187
+ </div>
188
+ <div className="flex justify-between">
189
+ <span className="text-gray-600 dark:text-gray-400">Responsive:</span>
190
+ <Badge variant="outline">{navigation.responsive}</Badge>
191
+ </div>
192
+ </div>
193
+ </div>
194
+
195
+ <div>
196
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
197
+ Animations
198
+ </h5>
199
+ <div className="space-y-2 text-sm">
200
+ <div className="flex justify-between">
201
+ <span className="text-gray-600 dark:text-gray-400">Duration:</span>
202
+ <span className="text-gray-900 dark:text-gray-100">
203
+ {navigation.animations.duration.normal}
204
+ </span>
205
+ </div>
206
+ <div className="flex justify-between">
207
+ <span className="text-gray-600 dark:text-gray-400">Easing:</span>
208
+ <span className="text-gray-900 dark:text-gray-100">
209
+ {navigation.animations.easing.ease}
210
+ </span>
211
+ </div>
212
+ <div className="flex justify-between">
213
+ <span className="text-gray-600 dark:text-gray-400">Stagger:</span>
214
+ <span className="text-gray-900 dark:text-gray-100">
215
+ {navigation.animations.stagger.enabled ? 'Enabled' : 'Disabled'}
216
+ </span>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </div>
222
+ );
223
+ };
224
+
225
+ const renderSpacingPreview = () => {
226
+ if (!showSpacing) return null;
227
+
228
+ const { spacing } = themeConfig;
229
+
230
+ return (
231
+ <div className="space-y-4">
232
+ <h4 className="text-lg font-semibold">Spacing</h4>
233
+
234
+ <div>
235
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
236
+ Scale
237
+ </h5>
238
+ <div className="space-y-2">
239
+ {Object.entries(spacing.scale).map(([size, value]) => (
240
+ <div key={size} className="flex items-center gap-4">
241
+ <span className="text-xs text-gray-500 w-12">{size}:</span>
242
+ <div
243
+ className="bg-blue-500 rounded"
244
+ style={{
245
+ width: value,
246
+ height: '1rem'
247
+ }}
248
+ />
249
+ <span className="text-sm text-gray-600 dark:text-gray-400">{value}</span>
250
+ </div>
251
+ ))}
252
+ </div>
253
+ </div>
254
+
255
+ <div>
256
+ <h5 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
257
+ Component Spacing
258
+ </h5>
259
+ <div className="space-y-2">
260
+ {Object.entries(spacing.component).map(([component, spacingConfig]) => (
261
+ <div key={component} className="border border-gray-200 dark:border-gray-700 rounded p-2">
262
+ <h6 className="text-xs font-medium text-gray-700 dark:text-gray-300 mb-1 capitalize">
263
+ {component}
264
+ </h6>
265
+ <div className="grid grid-cols-3 gap-2 text-xs">
266
+ {Object.entries(spacingConfig).map(([prop, value]) => (
267
+ <div key={prop} className="text-center">
268
+ <div className="text-gray-500 capitalize">{prop}</div>
269
+ <div className="text-gray-900 dark:text-gray-100">{String(value)}</div>
270
+ </div>
271
+ ))}
272
+ </div>
273
+ </div>
274
+ ))}
275
+ </div>
276
+ </div>
277
+ </div>
278
+ );
279
+ };
280
+
281
+ return (
282
+ <div className={`theme-preview ${className}`}>
283
+ <Card>
284
+ <CardHeader>
285
+ <CardTitle className="flex items-center gap-2">
286
+ <span className="text-2xl">
287
+ {targetTheme === 'stan-design' ? '⚡' :
288
+ targetTheme === 'enterprise' ? '🏢' :
289
+ targetTheme === 'harvey' ? '🎨' : '🎯'}
290
+ </span>
291
+ {themeConfig.meta.name} Theme Preview
292
+ </CardTitle>
293
+ <p className="text-sm text-gray-600 dark:text-gray-400">
294
+ {themeConfig.meta.description}
295
+ </p>
296
+ <div className="flex gap-2">
297
+ <Badge variant="outline">{themeConfig.meta.category}</Badge>
298
+ <Badge variant="secondary">v{themeConfig.meta.version}</Badge>
299
+ </div>
300
+ </CardHeader>
301
+ <CardContent className="space-y-6">
302
+ {renderColorPreview()}
303
+ {renderFontPreview()}
304
+ {renderNavigationPreview()}
305
+ {renderSpacingPreview()}
306
+ </CardContent>
307
+ </Card>
308
+ </div>
309
+ );
310
+ };
@@ -0,0 +1,264 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Button } from './button';
3
+ import { useTheme } from '../../themes';
4
+ import type { MultiThemeConfig } from '../../themes';
5
+
6
+ export interface ThemeSwitcherProps {
7
+ themes?: Record<string, MultiThemeConfig>;
8
+ showPreview?: boolean;
9
+ showSystemTheme?: boolean;
10
+ className?: string;
11
+ variant?: 'default' | 'compact' | 'detailed';
12
+ }
13
+
14
+ export const ThemeSwitcher: React.FC<ThemeSwitcherProps> = ({
15
+ showPreview = true,
16
+ showSystemTheme = true,
17
+ className = '',
18
+ variant = 'default'
19
+ }) => {
20
+ const { currentTheme, availableThemes, setTheme, getTheme } = useTheme();
21
+ const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>('light');
22
+ const [showThemePreview, setShowThemePreview] = useState(false);
23
+
24
+ // Detect system theme preference
25
+ useEffect(() => {
26
+ if (!showSystemTheme) return;
27
+
28
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
29
+ const updateSystemTheme = (e: MediaQueryListEvent | MediaQueryList) => {
30
+ setSystemTheme(e.matches ? 'dark' : 'light');
31
+ };
32
+
33
+ updateSystemTheme(mediaQuery);
34
+ mediaQuery.addEventListener('change', updateSystemTheme);
35
+
36
+ return () => mediaQuery.removeEventListener('change', updateSystemTheme);
37
+ }, [showSystemTheme]);
38
+
39
+ const getThemeDisplayName = (theme: string) => {
40
+ const themeNames: Record<string, string> = {
41
+ 'stan-design': 'Stan Design',
42
+ 'enterprise': 'Enterprise',
43
+ 'harvey': 'Harvey Creative'
44
+ };
45
+ return themeNames[theme] || theme;
46
+ };
47
+
48
+ const getThemeEmoji = (theme: string) => {
49
+ const themeEmojis: Record<string, string> = {
50
+ 'stan-design': '⚡',
51
+ 'enterprise': '🏢',
52
+ 'harvey': '🎨'
53
+ };
54
+ return themeEmojis[theme] || '🎯';
55
+ };
56
+
57
+ const getThemeDescription = (theme: string) => {
58
+ const themeConfig = getTheme(theme);
59
+ return themeConfig?.meta?.description || 'Custom theme';
60
+ };
61
+
62
+ const getThemeCategory = (theme: string) => {
63
+ const themeConfig = getTheme(theme);
64
+ return themeConfig?.meta?.category || 'custom';
65
+ };
66
+
67
+ const handleThemeSwitch = (themeName: string) => {
68
+ setTheme(themeName);
69
+ // Close preview if open
70
+ if (showThemePreview) {
71
+ setShowThemePreview(false);
72
+ }
73
+ };
74
+
75
+ const handleSystemThemeToggle = () => {
76
+ // For now, we'll just toggle between light/dark variants
77
+ // In a real implementation, this would switch to a system-aware theme
78
+ const currentConfig = getTheme(currentTheme);
79
+ if (currentConfig) {
80
+ // This is a placeholder - in practice, you'd have system-aware themes
81
+ console.log('System theme toggle - would switch to system-aware variant');
82
+ }
83
+ };
84
+
85
+ const renderCompactSwitcher = () => (
86
+ <div className="flex items-center gap-2">
87
+ <span className="text-sm text-gray-600 dark:text-gray-400">Theme:</span>
88
+ <div className="flex gap-1">
89
+ {availableThemes.map((theme) => (
90
+ <Button
91
+ key={theme}
92
+ variant={currentTheme === theme ? "default" : "outline"}
93
+ size="sm"
94
+ onClick={() => handleThemeSwitch(theme)}
95
+ className="text-xs"
96
+ >
97
+ {getThemeEmoji(theme)} {getThemeDisplayName(theme)}
98
+ </Button>
99
+ ))}
100
+ </div>
101
+ </div>
102
+ );
103
+
104
+ const renderDetailedSwitcher = () => (
105
+ <div className="space-y-4">
106
+ <div className="flex items-center justify-between">
107
+ <h3 className="text-lg font-semibold">Theme Selection</h3>
108
+ <Button
109
+ variant="outline"
110
+ size="sm"
111
+ onClick={() => setShowThemePreview(!showThemePreview)}
112
+ >
113
+ {showThemePreview ? 'Hide' : 'Show'} Preview
114
+ </Button>
115
+ </div>
116
+
117
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
118
+ {availableThemes.map((theme) => {
119
+ const isActive = currentTheme === theme;
120
+
121
+ return (
122
+ <div
123
+ key={theme}
124
+ className={`p-4 rounded-lg border-2 transition-all duration-200 cursor-pointer ${
125
+ isActive
126
+ ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
127
+ : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
128
+ }`}
129
+ onClick={() => handleThemeSwitch(theme)}
130
+ >
131
+ <div className="flex items-center gap-3 mb-3">
132
+ <span className="text-2xl">{getThemeEmoji(theme)}</span>
133
+ <div>
134
+ <h4 className="font-semibold text-gray-900 dark:text-gray-100">
135
+ {getThemeDisplayName(theme)}
136
+ </h4>
137
+ <p className="text-sm text-gray-500 dark:text-gray-400">
138
+ {getThemeCategory(theme)}
139
+ </p>
140
+ </div>
141
+ </div>
142
+
143
+ <p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
144
+ {getThemeDescription(theme)}
145
+ </p>
146
+
147
+ {isActive && (
148
+ <div className="flex items-center gap-2 text-blue-600 dark:text-blue-400">
149
+ <div className="w-2 h-2 bg-blue-600 dark:bg-blue-400 rounded-full"></div>
150
+ <span className="text-sm font-medium">Active</span>
151
+ </div>
152
+ )}
153
+ </div>
154
+ );
155
+ })}
156
+ </div>
157
+
158
+ {showSystemTheme && (
159
+ <div className="mt-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
160
+ <div className="flex items-center justify-between">
161
+ <div>
162
+ <h4 className="font-medium text-gray-900 dark:text-gray-100">
163
+ System Theme Detection
164
+ </h4>
165
+ <p className="text-sm text-gray-600 dark:text-gray-400">
166
+ Current system preference: {systemTheme}
167
+ </p>
168
+ </div>
169
+ <Button
170
+ variant="outline"
171
+ size="sm"
172
+ onClick={handleSystemThemeToggle}
173
+ >
174
+ Follow System
175
+ </Button>
176
+ </div>
177
+ </div>
178
+ )}
179
+ </div>
180
+ );
181
+
182
+ const renderDefaultSwitcher = () => (
183
+ <div className="space-y-3">
184
+ <div className="flex items-center gap-2">
185
+ <span className="text-sm font-medium text-gray-700 dark:text-gray-300">Theme:</span>
186
+ <div className="flex gap-2">
187
+ {availableThemes.map((theme) => (
188
+ <Button
189
+ key={theme}
190
+ variant={currentTheme === theme ? "default" : "outline"}
191
+ size="sm"
192
+ onClick={() => handleThemeSwitch(theme)}
193
+ className="text-sm"
194
+ >
195
+ {getThemeEmoji(theme)} {getThemeDisplayName(theme)}
196
+ </Button>
197
+ ))}
198
+ </div>
199
+ </div>
200
+
201
+ {showPreview && (
202
+ <div className="flex items-center gap-2">
203
+ <Button
204
+ variant="ghost"
205
+ size="sm"
206
+ onClick={() => setShowThemePreview(!showThemePreview)}
207
+ className="text-xs"
208
+ >
209
+ {showThemePreview ? 'Hide' : 'Show'} Theme Details
210
+ </Button>
211
+ </div>
212
+ )}
213
+
214
+ {showThemePreview && (
215
+ <div className="mt-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
216
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
217
+ {availableThemes.map((theme) => {
218
+ const isActive = currentTheme === theme;
219
+
220
+ return (
221
+ <div
222
+ key={theme}
223
+ className={`p-2 rounded border text-xs ${
224
+ isActive
225
+ ? 'border-blue-300 bg-blue-50 dark:bg-blue-900/20'
226
+ : 'border-gray-200 dark:border-gray-700'
227
+ }`}
228
+ >
229
+ <div className="flex items-center gap-2 mb-1">
230
+ <span>{getThemeEmoji(theme)}</span>
231
+ <span className="font-medium">{getThemeDisplayName(theme)}</span>
232
+ {isActive && (
233
+ <span className="text-blue-600 dark:text-blue-400 text-xs">●</span>
234
+ )}
235
+ </div>
236
+ <p className="text-gray-600 dark:text-gray-400">
237
+ {getThemeDescription(theme)}
238
+ </p>
239
+ </div>
240
+ );
241
+ })}
242
+ </div>
243
+ </div>
244
+ )}
245
+ </div>
246
+ );
247
+
248
+ const renderContent = () => {
249
+ switch (variant) {
250
+ case 'compact':
251
+ return renderCompactSwitcher();
252
+ case 'detailed':
253
+ return renderDetailedSwitcher();
254
+ default:
255
+ return renderDefaultSwitcher();
256
+ }
257
+ };
258
+
259
+ return (
260
+ <div className={`theme-switcher ${className}`}>
261
+ {renderContent()}
262
+ </div>
263
+ );
264
+ };
@@ -0,0 +1,38 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Button } from './button';
3
+ import { Moon, Sun } from 'lucide-react';
4
+
5
+ export const ThemeToggle: React.FC = () => {
6
+ const [isDark, setIsDark] = useState(false);
7
+
8
+ useEffect(() => {
9
+ // Check initial theme
10
+ const isDarkMode = document.documentElement.classList.contains('dark');
11
+ setIsDark(isDarkMode);
12
+ }, []);
13
+
14
+ const toggleTheme = () => {
15
+ const newDarkMode = !isDark;
16
+ setIsDark(newDarkMode);
17
+
18
+ if (newDarkMode) {
19
+ document.documentElement.classList.add('dark');
20
+ localStorage.setItem('theme', 'dark');
21
+ } else {
22
+ document.documentElement.classList.remove('dark');
23
+ localStorage.setItem('theme', 'light');
24
+ }
25
+ };
26
+
27
+ return (
28
+ <Button
29
+ variant="ghost"
30
+ size="sm"
31
+ onClick={toggleTheme}
32
+ className="h-8 w-8 px-0"
33
+ aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`}
34
+ >
35
+ {isDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
36
+ </Button>
37
+ );
38
+ };