xertica-ui 1.0.0
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/App.tsx +182 -0
- package/README.md +330 -0
- package/assets/xertica-logo.svg +38 -0
- package/assets/xertica-x-logo.svg +21 -0
- package/bin/cli.ts +193 -0
- package/components/AssistenteXertica.tsx +2003 -0
- package/components/AudioPlayer.tsx +203 -0
- package/components/CodeBlock.tsx +242 -0
- package/components/DocumentEditor.tsx +504 -0
- package/components/ForgotPasswordPage.tsx +170 -0
- package/components/FormattedDocument.tsx +87 -0
- package/components/HomeContent.tsx +123 -0
- package/components/HomePage.tsx +70 -0
- package/components/LanguageSelector.tsx +54 -0
- package/components/LoginPage.tsx +199 -0
- package/components/MarkdownMessage.tsx +62 -0
- package/components/ModernChatInput.tsx +502 -0
- package/components/PodcastPlayer.tsx +409 -0
- package/components/ResetPasswordPage.tsx +234 -0
- package/components/Sidebar.tsx +489 -0
- package/components/TemplateContent.tsx +629 -0
- package/components/TemplatePage.tsx +70 -0
- package/components/ThemeToggle.tsx +65 -0
- package/components/VerifyEmailPage.tsx +187 -0
- package/components/XerticaLogo.tsx +69 -0
- package/components/XerticaOrbe.tsx +1339 -0
- package/components/XerticaXLogo.tsx +53 -0
- package/components/examples/DrawingMapExample.tsx +530 -0
- package/components/examples/FilterableMapExample.tsx +380 -0
- package/components/examples/LocationPickerExample.tsx +330 -0
- package/components/examples/MapExamples.tsx +280 -0
- package/components/examples/MapShowcase.tsx +446 -0
- package/components/examples/RouteMapExamples.tsx +329 -0
- package/components/examples/SimpleFilterableMap.tsx +192 -0
- package/components/examples/index.ts +52 -0
- package/components/figma/ImageWithFallback.tsx +27 -0
- package/components/index.ts +44 -0
- package/components/media/AudioPlayer.tsx +278 -0
- package/components/media/FloatingMediaWrapper.tsx +166 -0
- package/components/media/VideoPlayer.tsx +285 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +159 -0
- package/components/ui/alert.tsx +91 -0
- package/components/ui/aspect-ratio.tsx +11 -0
- package/components/ui/avatar.tsx +65 -0
- package/components/ui/badge.tsx +55 -0
- package/components/ui/breadcrumb.tsx +109 -0
- package/components/ui/button.tsx +78 -0
- package/components/ui/calendar.tsx +235 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +353 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +177 -0
- package/components/ui/context-menu.tsx +252 -0
- package/components/ui/dialog.tsx +138 -0
- package/components/ui/drawer.tsx +134 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/empty.tsx +90 -0
- package/components/ui/file-upload.tsx +152 -0
- package/components/ui/form.tsx +195 -0
- package/components/ui/google-maps-loader.tsx +379 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/index.ts +242 -0
- package/components/ui/input-otp.tsx +77 -0
- package/components/ui/input.tsx +38 -0
- package/components/ui/label.tsx +24 -0
- package/components/ui/map-config.ts +12 -0
- package/components/ui/map-layers.tsx +129 -0
- package/components/ui/map.exports.ts +31 -0
- package/components/ui/map.tsx +412 -0
- package/components/ui/menubar.tsx +276 -0
- package/components/ui/navigation-menu.tsx +162 -0
- package/components/ui/notification-badge.tsx +61 -0
- package/components/ui/page-header.tsx +229 -0
- package/components/ui/pagination.tsx +127 -0
- package/components/ui/popover.tsx +48 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/radio-group.tsx +56 -0
- package/components/ui/rating.tsx +102 -0
- package/components/ui/resizable.tsx +405 -0
- package/components/ui/route-map.tsx +246 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/search.tsx +70 -0
- package/components/ui/select.tsx +176 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/sheet.tsx +138 -0
- package/components/ui/sidebar.tsx +726 -0
- package/components/ui/simple-map.tsx +92 -0
- package/components/ui/skeleton.tsx +13 -0
- package/components/ui/slider.tsx +58 -0
- package/components/ui/sonner.tsx +77 -0
- package/components/ui/stats-card.tsx +84 -0
- package/components/ui/stepper.tsx +126 -0
- package/components/ui/switch.tsx +34 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/tabs.tsx +66 -0
- package/components/ui/textarea.tsx +26 -0
- package/components/ui/timeline.tsx +140 -0
- package/components/ui/toggle-group.tsx +71 -0
- package/components/ui/toggle.tsx +46 -0
- package/components/ui/tooltip.tsx +61 -0
- package/components/ui/tree-view.tsx +123 -0
- package/components/ui/use-mobile.ts +24 -0
- package/components/ui/utils.ts +6 -0
- package/components/ui/xertica-assistant.tsx +1420 -0
- package/contexts/ApiKeyContext.tsx +123 -0
- package/contexts/AssistenteContext.tsx +118 -0
- package/contexts/BrandColorsContext.tsx +551 -0
- package/contexts/LanguageContext.tsx +36 -0
- package/contexts/ThemeContext.tsx +85 -0
- package/dist/cli.js +20922 -0
- package/eslint.config.js +41 -0
- package/guidelines/Guidelines.md +61 -0
- package/hooks/useTheme.ts +4 -0
- package/imports/Podcast.tsx +389 -0
- package/imports/XerticaAi.tsx +46 -0
- package/imports/XerticaX.tsx +20 -0
- package/imports/svg-aueiaqngck.ts +11 -0
- package/imports/svg-v9krss1ozd.ts +16 -0
- package/imports/svg-vhrdofe3qe.ts +5 -0
- package/index.css +4448 -0
- package/index.html +14 -0
- package/main.tsx +10 -0
- package/package.json +119 -0
- package/postcss.config.js +6 -0
- package/routes.tsx +33 -0
- package/styles/globals.css +15 -0
- package/styles/xertica/app-overrides/chat.css +61 -0
- package/styles/xertica/app-overrides/scrollbar.css +33 -0
- package/styles/xertica/base.css +70 -0
- package/styles/xertica/integrations/google-maps.css +76 -0
- package/styles/xertica/integrations/sonner.css +73 -0
- package/styles/xertica/theme-map.css +88 -0
- package/styles/xertica/tokens.css +190 -0
- package/tsconfig.json +31 -0
- package/tsconfig.node.json +10 -0
- package/utils/gemini.ts +140 -0
- package/vite-env.d.ts +12 -0
- package/vite.config.ts +36 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
interface BrandColors {
|
|
4
|
+
// Main Colors
|
|
5
|
+
primary: string;
|
|
6
|
+
primaryForeground: string;
|
|
7
|
+
primaryLight: string; // Used for backgrounds
|
|
8
|
+
|
|
9
|
+
// Dark Mode Overrides
|
|
10
|
+
primaryDarkMode: string;
|
|
11
|
+
primaryForegroundDark: string;
|
|
12
|
+
|
|
13
|
+
// Sidebar Colors
|
|
14
|
+
sidebarLight: string;
|
|
15
|
+
sidebarDark: string;
|
|
16
|
+
|
|
17
|
+
// Gradients
|
|
18
|
+
gradientStart: string;
|
|
19
|
+
gradientEnd: string;
|
|
20
|
+
gradientStartDark: string;
|
|
21
|
+
gradientEndDark: string;
|
|
22
|
+
|
|
23
|
+
// Chart Colors (Light Mode) - Dark mode will be calculated or fixed
|
|
24
|
+
chart1: string;
|
|
25
|
+
chart2: string;
|
|
26
|
+
chart3: string;
|
|
27
|
+
chart4: string;
|
|
28
|
+
chart5: string;
|
|
29
|
+
|
|
30
|
+
// Optional: Radius override per theme
|
|
31
|
+
radius?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ColorTheme {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
colors: BrandColors;
|
|
39
|
+
preview: {
|
|
40
|
+
primary: string;
|
|
41
|
+
secondary: string;
|
|
42
|
+
accent: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface BrandColorsContextType {
|
|
47
|
+
colors: BrandColors;
|
|
48
|
+
currentTheme: string;
|
|
49
|
+
themes: ColorTheme[];
|
|
50
|
+
setTheme: (themeId: string) => void;
|
|
51
|
+
radius: number;
|
|
52
|
+
setRadius: (radius: number) => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Shadcn Standard Palette Values (approximate hex for compatibility)
|
|
56
|
+
const PALETTE = {
|
|
57
|
+
zinc: { 900: '#18181b', 50: '#fafafa' },
|
|
58
|
+
slate: { 900: '#0f172a', 50: '#f8fafc' },
|
|
59
|
+
stone: { 900: '#1c1917', 50: '#fafaf9' },
|
|
60
|
+
gray: { 900: '#111827', 50: '#f9fafb' },
|
|
61
|
+
neutral: { 900: '#171717', 50: '#fafafa' },
|
|
62
|
+
red: { 600: '#dc2626', 500: '#ef4444' },
|
|
63
|
+
rose: { 600: '#e11d48', 500: '#f43f5e' },
|
|
64
|
+
orange: { 600: '#ea580c', 500: '#f97316' },
|
|
65
|
+
green: { 600: '#16a34a', 500: '#22c55e' },
|
|
66
|
+
blue: { 600: '#2563eb', 500: '#3b82f6' },
|
|
67
|
+
yellow: { 600: '#ca8a04', 500: '#eab308' },
|
|
68
|
+
violet: { 600: '#7c3aed', 500: '#8b5cf6' },
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const colorThemes: ColorTheme[] = [
|
|
72
|
+
{
|
|
73
|
+
id: 'xertica-original',
|
|
74
|
+
name: 'Xertica Original',
|
|
75
|
+
description: 'A identidade visual clássica da Xertica com o gradiente original',
|
|
76
|
+
colors: {
|
|
77
|
+
primary: '#2C275B',
|
|
78
|
+
primaryForeground: '#FFFFFF',
|
|
79
|
+
primaryLight: '#5B568F',
|
|
80
|
+
primaryDarkMode: '#72CDFD',
|
|
81
|
+
primaryForegroundDark: '#09090B',
|
|
82
|
+
|
|
83
|
+
sidebarLight: '#2C275B', // Primary Color
|
|
84
|
+
sidebarDark: '#1a1928', // Original Dark BG
|
|
85
|
+
|
|
86
|
+
gradientStart: '#FDB0F2',
|
|
87
|
+
gradientEnd: '#72CDFD',
|
|
88
|
+
gradientStartDark: '#7B4A7A',
|
|
89
|
+
gradientEndDark: '#3A5C7D',
|
|
90
|
+
|
|
91
|
+
chart1: '#2C275B',
|
|
92
|
+
chart2: '#FDB0F2',
|
|
93
|
+
chart3: '#72CDFD',
|
|
94
|
+
chart4: '#5B568F',
|
|
95
|
+
chart5: '#4F46E5',
|
|
96
|
+
},
|
|
97
|
+
preview: {
|
|
98
|
+
primary: '#2C275B',
|
|
99
|
+
secondary: '#FDB0F2',
|
|
100
|
+
accent: '#72CDFD',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'zinc',
|
|
105
|
+
name: 'Zinc',
|
|
106
|
+
description: 'Minimalista, sério e elegante (Escala de Cinza)',
|
|
107
|
+
colors: {
|
|
108
|
+
primary: '#18181B',
|
|
109
|
+
primaryForeground: '#FAFAFA',
|
|
110
|
+
primaryLight: '#E4E4E7',
|
|
111
|
+
primaryDarkMode: '#FAFAFA',
|
|
112
|
+
primaryForegroundDark: '#18181B',
|
|
113
|
+
|
|
114
|
+
sidebarLight: '#18181B', // Primary Color
|
|
115
|
+
sidebarDark: '#09090B',
|
|
116
|
+
|
|
117
|
+
gradientStart: '#52525B',
|
|
118
|
+
gradientEnd: '#D4D4D8',
|
|
119
|
+
gradientStartDark: '#27272A',
|
|
120
|
+
gradientEndDark: '#52525B',
|
|
121
|
+
|
|
122
|
+
chart1: '#18181B',
|
|
123
|
+
chart2: '#52525B',
|
|
124
|
+
chart3: '#A1A1AA',
|
|
125
|
+
chart4: '#D4D4D8',
|
|
126
|
+
chart5: '#E4E4E7',
|
|
127
|
+
},
|
|
128
|
+
preview: {
|
|
129
|
+
primary: '#18181B',
|
|
130
|
+
secondary: '#52525B',
|
|
131
|
+
accent: '#E4E4E7',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: 'slate',
|
|
136
|
+
name: 'Slate',
|
|
137
|
+
description: 'Profissional com tons de azul acinzentado',
|
|
138
|
+
colors: {
|
|
139
|
+
primary: '#0F172A',
|
|
140
|
+
primaryForeground: '#F8FAFC',
|
|
141
|
+
primaryLight: '#CBD5E1',
|
|
142
|
+
primaryDarkMode: '#F8FAFC',
|
|
143
|
+
primaryForegroundDark: '#0F172A',
|
|
144
|
+
|
|
145
|
+
sidebarLight: '#0F172A', // Primary Color
|
|
146
|
+
sidebarDark: '#020617',
|
|
147
|
+
|
|
148
|
+
gradientStart: '#475569',
|
|
149
|
+
gradientEnd: '#94A3B8',
|
|
150
|
+
gradientStartDark: '#1E293B',
|
|
151
|
+
gradientEndDark: '#475569',
|
|
152
|
+
|
|
153
|
+
chart1: '#0F172A',
|
|
154
|
+
chart2: '#475569',
|
|
155
|
+
chart3: '#94A3B8',
|
|
156
|
+
chart4: '#CBD5E1',
|
|
157
|
+
chart5: '#E2E8F0',
|
|
158
|
+
},
|
|
159
|
+
preview: {
|
|
160
|
+
primary: '#0F172A',
|
|
161
|
+
secondary: '#475569',
|
|
162
|
+
accent: '#94A3B8',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'blue',
|
|
167
|
+
name: 'Blue',
|
|
168
|
+
description: 'Confiável e seguro, padrão corporativo',
|
|
169
|
+
colors: {
|
|
170
|
+
primary: '#2563EB',
|
|
171
|
+
primaryForeground: '#FFFFFF',
|
|
172
|
+
primaryLight: '#DBEAFE',
|
|
173
|
+
primaryDarkMode: '#3B82F6',
|
|
174
|
+
primaryForegroundDark: '#FFFFFF',
|
|
175
|
+
|
|
176
|
+
sidebarLight: '#1E3A8A', // Blue 900 (High Contrast Sidebar)
|
|
177
|
+
sidebarDark: '#172554',
|
|
178
|
+
|
|
179
|
+
gradientStart: '#2563EB',
|
|
180
|
+
gradientEnd: '#60A5FA',
|
|
181
|
+
gradientStartDark: '#1D4ED8',
|
|
182
|
+
gradientEndDark: '#2563EB',
|
|
183
|
+
|
|
184
|
+
chart1: '#2563EB',
|
|
185
|
+
chart2: '#3B82F6',
|
|
186
|
+
chart3: '#60A5FA',
|
|
187
|
+
chart4: '#93C5FD',
|
|
188
|
+
chart5: '#BFDBFE',
|
|
189
|
+
},
|
|
190
|
+
preview: {
|
|
191
|
+
primary: '#2563EB',
|
|
192
|
+
secondary: '#60A5FA',
|
|
193
|
+
accent: '#DBEAFE',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 'violet',
|
|
198
|
+
name: 'Violet',
|
|
199
|
+
description: 'Criativo e vibrante',
|
|
200
|
+
colors: {
|
|
201
|
+
primary: '#7C3AED',
|
|
202
|
+
primaryForeground: '#FFFFFF',
|
|
203
|
+
primaryLight: '#EDE9FE',
|
|
204
|
+
primaryDarkMode: '#8B5CF6',
|
|
205
|
+
primaryForegroundDark: '#FFFFFF',
|
|
206
|
+
|
|
207
|
+
sidebarLight: '#4C1D95', // Violet 900 (High Contrast Sidebar)
|
|
208
|
+
sidebarDark: '#2E1065',
|
|
209
|
+
|
|
210
|
+
gradientStart: '#7C3AED',
|
|
211
|
+
gradientEnd: '#A78BFA',
|
|
212
|
+
gradientStartDark: '#5B21B6',
|
|
213
|
+
gradientEndDark: '#7C3AED',
|
|
214
|
+
|
|
215
|
+
chart1: '#7C3AED',
|
|
216
|
+
chart2: '#8B5CF6',
|
|
217
|
+
chart3: '#A78BFA',
|
|
218
|
+
chart4: '#C4B5FD',
|
|
219
|
+
chart5: '#DDD6FE',
|
|
220
|
+
},
|
|
221
|
+
preview: {
|
|
222
|
+
primary: '#7C3AED',
|
|
223
|
+
secondary: '#A78BFA',
|
|
224
|
+
accent: '#EDE9FE',
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: 'rose',
|
|
229
|
+
name: 'Rose',
|
|
230
|
+
description: 'Elegante e suave',
|
|
231
|
+
colors: {
|
|
232
|
+
primary: '#BE123C', // Rose 700 (Better text contrast for Primary)
|
|
233
|
+
primaryForeground: '#FFFFFF',
|
|
234
|
+
primaryLight: '#FFE4E6',
|
|
235
|
+
primaryDarkMode: '#F43F5E',
|
|
236
|
+
primaryForegroundDark: '#FFFFFF',
|
|
237
|
+
|
|
238
|
+
sidebarLight: '#881337', // Rose 900 (High Contrast Sidebar)
|
|
239
|
+
sidebarDark: '#881337',
|
|
240
|
+
|
|
241
|
+
gradientStart: '#E11D48',
|
|
242
|
+
gradientEnd: '#FB7185',
|
|
243
|
+
gradientStartDark: '#9F1239',
|
|
244
|
+
gradientEndDark: '#E11D48',
|
|
245
|
+
|
|
246
|
+
chart1: '#E11D48',
|
|
247
|
+
chart2: '#F43F5E',
|
|
248
|
+
chart3: '#FB7185',
|
|
249
|
+
chart4: '#FDA4AF',
|
|
250
|
+
chart5: '#FECDD3',
|
|
251
|
+
},
|
|
252
|
+
preview: {
|
|
253
|
+
primary: '#BE123C',
|
|
254
|
+
secondary: '#FB7185',
|
|
255
|
+
accent: '#FFE4E6',
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'emerald',
|
|
260
|
+
name: 'Emerald',
|
|
261
|
+
description: 'Natural e equilibrado',
|
|
262
|
+
colors: {
|
|
263
|
+
primary: '#047857', // Emerald 700 (Better text contrast for Primary)
|
|
264
|
+
primaryForeground: '#FFFFFF',
|
|
265
|
+
primaryLight: '#D1FAE5',
|
|
266
|
+
primaryDarkMode: '#10B981',
|
|
267
|
+
primaryForegroundDark: '#FFFFFF',
|
|
268
|
+
|
|
269
|
+
sidebarLight: '#064E3B', // Emerald 900 (High Contrast Sidebar)
|
|
270
|
+
sidebarDark: '#064E3B',
|
|
271
|
+
|
|
272
|
+
gradientStart: '#059669',
|
|
273
|
+
gradientEnd: '#34D399',
|
|
274
|
+
gradientStartDark: '#064E3B',
|
|
275
|
+
gradientEndDark: '#059669',
|
|
276
|
+
|
|
277
|
+
chart1: '#059669',
|
|
278
|
+
chart2: '#10B981',
|
|
279
|
+
chart3: '#34D399',
|
|
280
|
+
chart4: '#6EE7B7',
|
|
281
|
+
chart5: '#A7F3D0',
|
|
282
|
+
},
|
|
283
|
+
preview: {
|
|
284
|
+
primary: '#047857',
|
|
285
|
+
secondary: '#34D399',
|
|
286
|
+
accent: '#D1FAE5',
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'amber',
|
|
291
|
+
name: 'Amber',
|
|
292
|
+
description: 'Quente e energético',
|
|
293
|
+
colors: {
|
|
294
|
+
primary: '#B45309', // Amber 700 (Better text contrast for Primary)
|
|
295
|
+
primaryForeground: '#FFFFFF',
|
|
296
|
+
primaryLight: '#FEF3C7',
|
|
297
|
+
primaryDarkMode: '#F59E0B',
|
|
298
|
+
primaryForegroundDark: '#FFFFFF',
|
|
299
|
+
|
|
300
|
+
sidebarLight: '#78350F', // Amber 900 (High Contrast Sidebar)
|
|
301
|
+
sidebarDark: '#78350F',
|
|
302
|
+
|
|
303
|
+
gradientStart: '#D97706',
|
|
304
|
+
gradientEnd: '#FBBF24',
|
|
305
|
+
gradientStartDark: '#92400E',
|
|
306
|
+
gradientEndDark: '#D97706',
|
|
307
|
+
|
|
308
|
+
chart1: '#D97706',
|
|
309
|
+
chart2: '#F59E0B',
|
|
310
|
+
chart3: '#FBBF24',
|
|
311
|
+
chart4: '#FDE68A',
|
|
312
|
+
chart5: '#FEF3C7',
|
|
313
|
+
},
|
|
314
|
+
preview: {
|
|
315
|
+
primary: '#B45309',
|
|
316
|
+
secondary: '#FBBF24',
|
|
317
|
+
accent: '#FEF3C7',
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: 'orange',
|
|
322
|
+
name: 'Orange',
|
|
323
|
+
description: 'Vibrante e amigável',
|
|
324
|
+
colors: {
|
|
325
|
+
primary: '#C2410C', // Orange 700 (Better text contrast for Primary)
|
|
326
|
+
primaryForeground: '#FFFFFF',
|
|
327
|
+
primaryLight: '#FFEDD5',
|
|
328
|
+
primaryDarkMode: '#F97316',
|
|
329
|
+
primaryForegroundDark: '#FFFFFF',
|
|
330
|
+
|
|
331
|
+
sidebarLight: '#7C2D12', // Orange 900 (High Contrast Sidebar)
|
|
332
|
+
sidebarDark: '#7C2D12',
|
|
333
|
+
|
|
334
|
+
gradientStart: '#EA580C',
|
|
335
|
+
gradientEnd: '#FB923C',
|
|
336
|
+
gradientStartDark: '#9A3412',
|
|
337
|
+
gradientEndDark: '#EA580C',
|
|
338
|
+
|
|
339
|
+
chart1: '#EA580C',
|
|
340
|
+
chart2: '#F97316',
|
|
341
|
+
chart3: '#FB923C',
|
|
342
|
+
chart4: '#FDBA74',
|
|
343
|
+
chart5: '#FED7AA',
|
|
344
|
+
},
|
|
345
|
+
preview: {
|
|
346
|
+
primary: '#C2410C',
|
|
347
|
+
secondary: '#FB923C',
|
|
348
|
+
accent: '#FFEDD5',
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
const BrandColorsContext = createContext<BrandColorsContextType | undefined>(undefined);
|
|
354
|
+
|
|
355
|
+
export const BrandColorsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
356
|
+
const [currentTheme, setCurrentTheme] = useState<string>(() => {
|
|
357
|
+
const saved = localStorage.getItem('color-theme');
|
|
358
|
+
return saved || 'xertica-original';
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const [radius, setRadius] = useState<number>(0.5);
|
|
362
|
+
|
|
363
|
+
const [colors, setColors] = useState<BrandColors>(() => {
|
|
364
|
+
const theme = colorThemes.find(t => t.id === currentTheme);
|
|
365
|
+
return theme?.colors || colorThemes[0].colors;
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
useEffect(() => {
|
|
369
|
+
const theme = colorThemes.find(t => t.id === currentTheme);
|
|
370
|
+
if (theme) {
|
|
371
|
+
setColors(theme.colors);
|
|
372
|
+
}
|
|
373
|
+
}, [currentTheme]);
|
|
374
|
+
|
|
375
|
+
// Função auxiliar para converter hex para rgb
|
|
376
|
+
const hexToRgb = (hex: string): { r: number; g: number; b: number } | null => {
|
|
377
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
378
|
+
return result
|
|
379
|
+
? {
|
|
380
|
+
r: parseInt(result[1], 16),
|
|
381
|
+
g: parseInt(result[2], 16),
|
|
382
|
+
b: parseInt(result[3], 16),
|
|
383
|
+
}
|
|
384
|
+
: null;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// Função para aplicar as cores baseadas no modo atual e tema
|
|
388
|
+
const applyColors = React.useCallback(() => {
|
|
389
|
+
const root = document.documentElement;
|
|
390
|
+
const isDark = root.classList.contains('dark');
|
|
391
|
+
|
|
392
|
+
// --- Determine Colors based on Mode ---
|
|
393
|
+
const primary = isDark ? colors.primaryDarkMode : colors.primary;
|
|
394
|
+
const primaryForeground = isDark ? colors.primaryForegroundDark : colors.primaryForeground;
|
|
395
|
+
|
|
396
|
+
// Gradient Logic
|
|
397
|
+
const gradientStart = isDark ? colors.gradientStartDark : colors.gradientStart;
|
|
398
|
+
const gradientEnd = isDark ? colors.gradientEndDark : colors.gradientEnd;
|
|
399
|
+
|
|
400
|
+
// --- Semantic Colors (Fixed Standard) ---
|
|
401
|
+
// Success (Green), Warning (Amber/Yellow), Info (Blue), Destructive (Red)
|
|
402
|
+
const semantic = {
|
|
403
|
+
success: isDark ? '#4ade80' : '#22c55e', // Green 400 / 500
|
|
404
|
+
warning: isDark ? '#fbbf24' : '#f59e0b', // Amber 400 / 500
|
|
405
|
+
info: isDark ? '#60a5fa' : '#3b82f6', // Blue 400 / 500
|
|
406
|
+
destructive: isDark ? '#f87171' : '#ef4444', // Red 400 / 500
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// --- Apply CSS Variables ---
|
|
410
|
+
|
|
411
|
+
// 1. Core Primary Colors
|
|
412
|
+
root.style.setProperty('--primary', primary);
|
|
413
|
+
root.style.setProperty('--primary-foreground', primaryForeground);
|
|
414
|
+
|
|
415
|
+
// 2. Semantic Colors
|
|
416
|
+
root.style.setProperty('--success', semantic.success);
|
|
417
|
+
root.style.setProperty('--success-foreground', '#ffffff');
|
|
418
|
+
root.style.setProperty('--warning', semantic.warning);
|
|
419
|
+
root.style.setProperty('--warning-foreground', '#ffffff');
|
|
420
|
+
root.style.setProperty('--info', semantic.info);
|
|
421
|
+
root.style.setProperty('--info-foreground', '#ffffff');
|
|
422
|
+
root.style.setProperty('--destructive', semantic.destructive);
|
|
423
|
+
root.style.setProperty('--destructive-foreground', '#ffffff');
|
|
424
|
+
|
|
425
|
+
// 3. Secondary & Accents (Derived or Specific)
|
|
426
|
+
|
|
427
|
+
// 2. Secondary & Accents (Derived or Specific)
|
|
428
|
+
// We use primaryLight for subtle backgrounds
|
|
429
|
+
// In dark mode, we might want to adjust opacity
|
|
430
|
+
const primaryRGB = hexToRgb(primary);
|
|
431
|
+
if (primaryRGB) {
|
|
432
|
+
// Create a subtle tint for secondary/accent/muted
|
|
433
|
+
const alpha = isDark ? 0.2 : 0.1;
|
|
434
|
+
const subtle = `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, ${alpha})`;
|
|
435
|
+
|
|
436
|
+
// Note: Shadcn usually has specific greys for secondary, but for "Brand" themes
|
|
437
|
+
// we can tint them slightly or stick to standard zinc.
|
|
438
|
+
// For this implementation, we will keep existing "neutral" variables from CSS
|
|
439
|
+
// but override "ring" and "sidebar-ring" to match brand.
|
|
440
|
+
|
|
441
|
+
root.style.setProperty('--ring', isDark ? colors.primaryDarkMode : `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.5)`);
|
|
442
|
+
|
|
443
|
+
// Also update the Xertica specific variables
|
|
444
|
+
root.style.setProperty('--xertica-primary', primary);
|
|
445
|
+
root.style.setProperty('--primary-light', `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.15)`);
|
|
446
|
+
root.style.setProperty('--primary-light-foreground', primary);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// 3. Radius
|
|
450
|
+
root.style.setProperty('--radius', `${radius}rem`);
|
|
451
|
+
|
|
452
|
+
// 4. Charts
|
|
453
|
+
root.style.setProperty('--chart-1', colors.chart1);
|
|
454
|
+
root.style.setProperty('--chart-2', colors.chart2);
|
|
455
|
+
root.style.setProperty('--chart-3', colors.chart3);
|
|
456
|
+
root.style.setProperty('--chart-4', colors.chart4);
|
|
457
|
+
root.style.setProperty('--chart-5', colors.chart5);
|
|
458
|
+
|
|
459
|
+
// 5. Sidebar (Override)
|
|
460
|
+
// Dynamic sidebar colors based on theme and mode
|
|
461
|
+
const sidebarBg = isDark ? colors.sidebarDark : colors.sidebarLight;
|
|
462
|
+
|
|
463
|
+
// Determine if sidebar background is dark or light to set text color
|
|
464
|
+
// We assume if the sidebar background is the Primary Color (which are generally dark/saturated), text should be white.
|
|
465
|
+
// Xertica Original (Purple), Blue, Violet, etc. are all dark.
|
|
466
|
+
// If sidebarLight is White (#FFFFFF) or very light, text should be dark.
|
|
467
|
+
|
|
468
|
+
// Simple check: is sidebarBg roughly White/Light?
|
|
469
|
+
const isSidebarLight = sidebarBg.toLowerCase() === '#ffffff' || sidebarBg.toLowerCase() === '#f3f4f6' || sidebarBg.toLowerCase() === '#fafafa';
|
|
470
|
+
|
|
471
|
+
const sidebarFg = isSidebarLight ? '#0F172A' : '#FFFFFF'; // Dark text for light BG, White text for dark BG
|
|
472
|
+
|
|
473
|
+
root.style.setProperty('--sidebar', sidebarBg);
|
|
474
|
+
root.style.setProperty('--sidebar-foreground', sidebarFg);
|
|
475
|
+
|
|
476
|
+
// Sidebar Accent
|
|
477
|
+
if (primaryRGB) {
|
|
478
|
+
if (isSidebarLight) {
|
|
479
|
+
// Standard behavior for light sidebar
|
|
480
|
+
root.style.setProperty('--sidebar-accent', `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.1)`);
|
|
481
|
+
root.style.setProperty('--sidebar-accent-foreground', primary);
|
|
482
|
+
root.style.setProperty('--sidebar-border', 'rgba(0,0,0,0.1)');
|
|
483
|
+
} else {
|
|
484
|
+
// Behavior for colored/dark sidebar
|
|
485
|
+
// Accent should be a slightly lighter/darker shade of the sidebar bg
|
|
486
|
+
// Or white with low opacity
|
|
487
|
+
root.style.setProperty('--sidebar-accent', 'rgba(255, 255, 255, 0.1)');
|
|
488
|
+
root.style.setProperty('--sidebar-accent-foreground', '#FFFFFF');
|
|
489
|
+
root.style.setProperty('--sidebar-border', 'rgba(255,255,255,0.1)');
|
|
490
|
+
}
|
|
491
|
+
root.style.setProperty('--sidebar-ring', `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.5)`);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Sidebar Primary (Active Item)
|
|
495
|
+
// If sidebar is colored (dark), the primary active item usually looks good as white or a very bright accent.
|
|
496
|
+
// But standard "Primary" might clash if it's the same color as the background.
|
|
497
|
+
if (!isSidebarLight) {
|
|
498
|
+
root.style.setProperty('--sidebar-primary', '#FFFFFF');
|
|
499
|
+
root.style.setProperty('--sidebar-primary-foreground', primary); // Text becomes primary color
|
|
500
|
+
} else {
|
|
501
|
+
root.style.setProperty('--sidebar-primary', primary);
|
|
502
|
+
root.style.setProperty('--sidebar-primary-foreground', primaryForeground);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// 6. Gradients
|
|
506
|
+
root.style.setProperty(
|
|
507
|
+
'--gradient-diagonal',
|
|
508
|
+
`linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)`
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
// Save preferences
|
|
512
|
+
localStorage.setItem('color-theme', currentTheme);
|
|
513
|
+
}, [colors, currentTheme, radius]);
|
|
514
|
+
|
|
515
|
+
// Aplicar cores quando elas mudarem
|
|
516
|
+
useEffect(() => {
|
|
517
|
+
applyColors();
|
|
518
|
+
}, [applyColors]);
|
|
519
|
+
|
|
520
|
+
// Observar mudanças na classe 'dark' do documento
|
|
521
|
+
useEffect(() => {
|
|
522
|
+
const root = document.documentElement;
|
|
523
|
+
const observer = new MutationObserver((mutations) => {
|
|
524
|
+
mutations.forEach((mutation) => {
|
|
525
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
526
|
+
applyColors();
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
observer.observe(root, { attributes: true, attributeFilter: ['class'] });
|
|
531
|
+
return () => observer.disconnect();
|
|
532
|
+
}, [applyColors]);
|
|
533
|
+
|
|
534
|
+
const setTheme = (themeId: string) => {
|
|
535
|
+
setCurrentTheme(themeId);
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
return (
|
|
539
|
+
<BrandColorsContext.Provider value={{ colors, currentTheme, themes: colorThemes, setTheme, radius, setRadius }}>
|
|
540
|
+
{children}
|
|
541
|
+
</BrandColorsContext.Provider>
|
|
542
|
+
);
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
export const useBrandColors = () => {
|
|
546
|
+
const context = useContext(BrandColorsContext);
|
|
547
|
+
if (context === undefined) {
|
|
548
|
+
throw new Error('useBrandColors must be used within a BrandColorsProvider');
|
|
549
|
+
}
|
|
550
|
+
return context;
|
|
551
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export type Language = 'pt' | 'en' | 'es';
|
|
4
|
+
|
|
5
|
+
interface LanguageContextType {
|
|
6
|
+
language: Language;
|
|
7
|
+
setLanguage: (language: Language) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
|
11
|
+
|
|
12
|
+
// Provider simplificado - mantém seletor visual mas funciona apenas em português
|
|
13
|
+
export function LanguageProvider({ children }: { children: ReactNode }) {
|
|
14
|
+
const [language, setLanguage] = useState<Language>(() => {
|
|
15
|
+
const saved = localStorage.getItem('xertica_language');
|
|
16
|
+
return (saved as Language) || 'pt';
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
localStorage.setItem('xertica_language', language);
|
|
21
|
+
}, [language]);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<LanguageContext.Provider value={{ language, setLanguage }}>
|
|
25
|
+
{children}
|
|
26
|
+
</LanguageContext.Provider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useLanguage() {
|
|
31
|
+
const context = useContext(LanguageContext);
|
|
32
|
+
if (context === undefined) {
|
|
33
|
+
throw new Error('useLanguage must be used within a LanguageProvider');
|
|
34
|
+
}
|
|
35
|
+
return context;
|
|
36
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export type Theme = 'light' | 'dark';
|
|
4
|
+
|
|
5
|
+
interface ThemeContextType {
|
|
6
|
+
theme: Theme;
|
|
7
|
+
toggleTheme: () => void;
|
|
8
|
+
setThemeMode: (theme: Theme) => void;
|
|
9
|
+
isDark: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
13
|
+
|
|
14
|
+
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
15
|
+
// Inicialização mais direta - verificar localStorage imediatamente
|
|
16
|
+
const getInitialTheme = (): Theme => {
|
|
17
|
+
// Primeiro, garantir que não há classe dark aplicada por padrão
|
|
18
|
+
if (typeof window !== 'undefined') {
|
|
19
|
+
document.documentElement.classList.remove('dark');
|
|
20
|
+
|
|
21
|
+
const savedTheme = localStorage.getItem('xertica-theme') as Theme;
|
|
22
|
+
if (savedTheme && (savedTheme === 'light' || savedTheme === 'dark')) {
|
|
23
|
+
return savedTheme;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Sempre retornar 'light' como padrão
|
|
27
|
+
return 'light';
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const [theme, setTheme] = useState<Theme>(getInitialTheme);
|
|
31
|
+
|
|
32
|
+
// Aplicar tema ao documento sempre que o tema mudar
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const root = document.documentElement;
|
|
35
|
+
|
|
36
|
+
if (theme === 'dark') {
|
|
37
|
+
root.classList.add('dark');
|
|
38
|
+
} else {
|
|
39
|
+
root.classList.remove('dark');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Salvar no localStorage
|
|
43
|
+
localStorage.setItem('xertica-theme', theme);
|
|
44
|
+
}, [theme]);
|
|
45
|
+
|
|
46
|
+
// Garantir que o tema seja aplicado na inicialização
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const root = document.documentElement;
|
|
49
|
+
if (theme === 'light') {
|
|
50
|
+
root.classList.remove('dark');
|
|
51
|
+
}
|
|
52
|
+
localStorage.setItem('xertica-theme', theme);
|
|
53
|
+
}, []);
|
|
54
|
+
|
|
55
|
+
// Alternar tema
|
|
56
|
+
const toggleTheme = () => {
|
|
57
|
+
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Definir tema específico
|
|
61
|
+
const setThemeMode = (newTheme: Theme) => {
|
|
62
|
+
setTheme(newTheme);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const value: ThemeContextType = {
|
|
66
|
+
theme,
|
|
67
|
+
toggleTheme,
|
|
68
|
+
setThemeMode,
|
|
69
|
+
isDark: theme === 'dark'
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<ThemeContext.Provider value={value}>
|
|
74
|
+
{children}
|
|
75
|
+
</ThemeContext.Provider>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useTheme() {
|
|
80
|
+
const context = useContext(ThemeContext);
|
|
81
|
+
if (context === undefined) {
|
|
82
|
+
throw new Error('useTheme must be used within a ThemeProvider');
|
|
83
|
+
}
|
|
84
|
+
return context;
|
|
85
|
+
}
|