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,379 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, ReactNode, useState, useEffect, useRef } from 'react';
|
|
4
|
+
import { GOOGLE_MAPS_LIBRARIES, GOOGLE_MAPS_ID } from './map-config';
|
|
5
|
+
|
|
6
|
+
interface GoogleMapsContextType {
|
|
7
|
+
isLoaded: boolean;
|
|
8
|
+
loadError: Error | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const GoogleMapsContext = createContext<GoogleMapsContextType>({
|
|
12
|
+
isLoaded: false,
|
|
13
|
+
loadError: undefined,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Singleton global para prevenir múltiplos carregamentos
|
|
17
|
+
declare global {
|
|
18
|
+
interface Window {
|
|
19
|
+
__XERTICA_GOOGLE_MAPS_LOADER__?: {
|
|
20
|
+
isLoaded: boolean;
|
|
21
|
+
loadError: Error | undefined;
|
|
22
|
+
listeners: Set<(state: GoogleMapsContextType) => void>;
|
|
23
|
+
scriptElement?: HTMLScriptElement;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Função auxiliar para ler a API key do localStorage de forma síncrona
|
|
30
|
+
*/
|
|
31
|
+
function getInitialApiKey(): string | undefined {
|
|
32
|
+
if (typeof window === 'undefined') return undefined;
|
|
33
|
+
|
|
34
|
+
const savedKey = localStorage.getItem('xertica-googlemaps-api-key');
|
|
35
|
+
|
|
36
|
+
if (savedKey && savedKey.trim().length > 0) {
|
|
37
|
+
return savedKey;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Remove o script existente do Google Maps
|
|
45
|
+
*/
|
|
46
|
+
function removeExistingScript(): void {
|
|
47
|
+
if (typeof window === 'undefined') return;
|
|
48
|
+
|
|
49
|
+
// Remover callback global se existir
|
|
50
|
+
if ((window as any).__googleMapsCallback) {
|
|
51
|
+
delete (window as any).__googleMapsCallback;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Remover script existente
|
|
55
|
+
const existingScript = document.querySelector(`script[src*=\"maps.googleapis.com/maps/api/js\"]`);
|
|
56
|
+
if (existingScript) {
|
|
57
|
+
existingScript.remove();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Limpar Google Maps do window
|
|
61
|
+
if ((window as any).google?.maps) {
|
|
62
|
+
delete (window as any).google.maps;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Limpar singleton para permitir novo carregamento
|
|
66
|
+
if (window.__XERTICA_GOOGLE_MAPS_LOADER__) {
|
|
67
|
+
window.__XERTICA_GOOGLE_MAPS_LOADER__.isLoaded = false;
|
|
68
|
+
window.__XERTICA_GOOGLE_MAPS_LOADER__.loadError = undefined;
|
|
69
|
+
window.__XERTICA_GOOGLE_MAPS_LOADER__.scriptElement = undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Verifica se o Google Maps já está carregado
|
|
75
|
+
*/
|
|
76
|
+
function isGoogleMapsAlreadyLoaded(): boolean {
|
|
77
|
+
if (typeof window === 'undefined') return false;
|
|
78
|
+
return window.google?.maps?.version !== undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Verifica se a biblioteca marker (AdvancedMarkerElement) está disponível
|
|
83
|
+
*/
|
|
84
|
+
function isMarkerLibraryLoaded(): boolean {
|
|
85
|
+
if (typeof window === 'undefined') return false;
|
|
86
|
+
return window.google?.maps?.marker?.AdvancedMarkerElement !== undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Carrega o script do Google Maps manualmente
|
|
91
|
+
*/
|
|
92
|
+
function loadGoogleMapsScript(apiKey?: string): Promise<void> {
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
if (typeof window === 'undefined') {
|
|
95
|
+
reject(new Error('Window is undefined'));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Validar API key antes de carregar
|
|
100
|
+
if (!apiKey || apiKey.length < 10) {
|
|
101
|
+
reject(new Error('Invalid or missing Google Maps API key. Please configure your API key in Settings.'));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Se já está carregado com a mesma key, resolver imediatamente
|
|
106
|
+
if (isGoogleMapsAlreadyLoaded() && isMarkerLibraryLoaded()) {
|
|
107
|
+
// Verificar se a API key atual é a mesma
|
|
108
|
+
const existingScript = document.querySelector(`script[src*="maps.googleapis.com/maps/api/js"]`) as HTMLScriptElement;
|
|
109
|
+
if (existingScript && existingScript.src.includes(`key=${apiKey}`)) {
|
|
110
|
+
resolve();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Verificar se o script já existe com KEY DIFERENTE
|
|
116
|
+
const existing = document.querySelector(`script[src*="maps.googleapis.com/maps/api/js"]`) as HTMLScriptElement;
|
|
117
|
+
if (existing) {
|
|
118
|
+
// Se existe mas com chave diferente, precisamos recarregar a página
|
|
119
|
+
if (!existing.src.includes(`key=${apiKey}`)) {
|
|
120
|
+
console.warn('[GoogleMapsLoader] API key changed, page reload required');
|
|
121
|
+
reject(new Error('API key changed. Please reload the page to apply changes.'));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Aguardar o script carregar
|
|
126
|
+
if (isGoogleMapsAlreadyLoaded() && isMarkerLibraryLoaded()) {
|
|
127
|
+
resolve();
|
|
128
|
+
} else {
|
|
129
|
+
existing.addEventListener('load', () => {
|
|
130
|
+
// Aguardar um pouco para garantir que todas as bibliotecas foram carregadas
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
if (isMarkerLibraryLoaded()) {
|
|
133
|
+
resolve();
|
|
134
|
+
} else {
|
|
135
|
+
reject(new Error('Marker library failed to load'));
|
|
136
|
+
}
|
|
137
|
+
}, 100);
|
|
138
|
+
});
|
|
139
|
+
existing.addEventListener('error', () => reject(new Error('Failed to load Google Maps')));
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Criar novo script
|
|
145
|
+
const script = document.createElement('script');
|
|
146
|
+
const params = new URLSearchParams({
|
|
147
|
+
key: apiKey, // SEMPRE incluir a API key
|
|
148
|
+
callback: '__googleMapsCallback',
|
|
149
|
+
libraries: GOOGLE_MAPS_LIBRARIES.join(','),
|
|
150
|
+
v: 'quarterly', // Usar 'quarterly' para versão estável ao invés de 'weekly'
|
|
151
|
+
loading: 'async',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
script.src = `https://maps.googleapis.com/maps/api/js?${params.toString()}`;
|
|
155
|
+
script.async = true;
|
|
156
|
+
script.defer = true;
|
|
157
|
+
script.id = GOOGLE_MAPS_ID;
|
|
158
|
+
|
|
159
|
+
// Callback global
|
|
160
|
+
(window as any).__googleMapsCallback = () => {
|
|
161
|
+
delete (window as any).__googleMapsCallback;
|
|
162
|
+
// Aguardar um pouco para garantir que todas as bibliotecas foram carregadas
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
if (isMarkerLibraryLoaded()) {
|
|
165
|
+
resolve();
|
|
166
|
+
} else {
|
|
167
|
+
reject(new Error('Marker library failed to load'));
|
|
168
|
+
}
|
|
169
|
+
}, 100);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
script.addEventListener('error', () => {
|
|
173
|
+
delete (window as any).__googleMapsCallback;
|
|
174
|
+
reject(new Error('Failed to load Google Maps script. Please check your API key and ensure Maps JavaScript API is enabled.'));
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
document.head.appendChild(script);
|
|
178
|
+
|
|
179
|
+
// Salvar referência ao script
|
|
180
|
+
const singleton = getOrCreateSingleton();
|
|
181
|
+
if (singleton) {
|
|
182
|
+
singleton.scriptElement = script;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Inicializa o singleton global
|
|
189
|
+
*/
|
|
190
|
+
function getOrCreateSingleton() {
|
|
191
|
+
if (typeof window === 'undefined') return null;
|
|
192
|
+
|
|
193
|
+
if (!window.__XERTICA_GOOGLE_MAPS_LOADER__) {
|
|
194
|
+
const isPreloaded = isGoogleMapsAlreadyLoaded();
|
|
195
|
+
|
|
196
|
+
window.__XERTICA_GOOGLE_MAPS_LOADER__ = {
|
|
197
|
+
isLoaded: isPreloaded,
|
|
198
|
+
loadError: undefined,
|
|
199
|
+
listeners: new Set(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return window.__XERTICA_GOOGLE_MAPS_LOADER__;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Atualiza o estado do singleton e notifica listeners
|
|
208
|
+
*/
|
|
209
|
+
function updateSingleton(state: Partial<GoogleMapsContextType>) {
|
|
210
|
+
const singleton = getOrCreateSingleton();
|
|
211
|
+
if (!singleton) return;
|
|
212
|
+
|
|
213
|
+
if (state.isLoaded !== undefined) singleton.isLoaded = state.isLoaded;
|
|
214
|
+
if (state.loadError !== undefined) singleton.loadError = state.loadError;
|
|
215
|
+
|
|
216
|
+
const newState = { isLoaded: singleton.isLoaded, loadError: singleton.loadError };
|
|
217
|
+
singleton.listeners.forEach(listener => listener(newState));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Componente que gerencia os listeners do singleton
|
|
222
|
+
*/
|
|
223
|
+
const SingletonLoaderWrapper = ({ children }: { children: ReactNode }) => {
|
|
224
|
+
const [state, setState] = useState<GoogleMapsContextType>(() => {
|
|
225
|
+
const singleton = getOrCreateSingleton();
|
|
226
|
+
if (!singleton) return { isLoaded: false, loadError: undefined };
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
isLoaded: singleton.isLoaded,
|
|
230
|
+
loadError: singleton.loadError,
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
const singleton = getOrCreateSingleton();
|
|
236
|
+
if (!singleton) return;
|
|
237
|
+
|
|
238
|
+
const listener = (newState: GoogleMapsContextType) => {
|
|
239
|
+
setState(newState);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
singleton.listeners.add(listener);
|
|
243
|
+
|
|
244
|
+
// Sincronizar estado inicial
|
|
245
|
+
listener({ isLoaded: singleton.isLoaded, loadError: singleton.loadError });
|
|
246
|
+
|
|
247
|
+
return () => {
|
|
248
|
+
singleton.listeners.delete(listener);
|
|
249
|
+
};
|
|
250
|
+
}, []);
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<GoogleMapsContext.Provider value={state}>
|
|
254
|
+
{children}
|
|
255
|
+
</GoogleMapsContext.Provider>
|
|
256
|
+
);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Componente que carrega o Google Maps manualmente
|
|
261
|
+
*/
|
|
262
|
+
const LoaderInitializer = () => {
|
|
263
|
+
const hasInitializedRef = useRef(false);
|
|
264
|
+
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
// Prevenir múltiplas execuções
|
|
267
|
+
if (hasInitializedRef.current) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const singleton = getOrCreateSingleton();
|
|
272
|
+
if (!singleton) return;
|
|
273
|
+
|
|
274
|
+
// Se já está carregado, atualizar estado
|
|
275
|
+
if (isGoogleMapsAlreadyLoaded() && isMarkerLibraryLoaded()) {
|
|
276
|
+
updateSingleton({ isLoaded: true, loadError: undefined });
|
|
277
|
+
hasInitializedRef.current = true;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
hasInitializedRef.current = true;
|
|
282
|
+
|
|
283
|
+
const apiKey = getInitialApiKey();
|
|
284
|
+
|
|
285
|
+
// Se não houver API key, apenas marcar como não carregado (sem erro)
|
|
286
|
+
if (!apiKey) {
|
|
287
|
+
updateSingleton({
|
|
288
|
+
isLoaded: false,
|
|
289
|
+
loadError: undefined // Não definir erro quando não há API key
|
|
290
|
+
});
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
loadGoogleMapsScript(apiKey)
|
|
295
|
+
.then(() => {
|
|
296
|
+
updateSingleton({ isLoaded: true, loadError: undefined });
|
|
297
|
+
})
|
|
298
|
+
.catch((error) => {
|
|
299
|
+
updateSingleton({ isLoaded: false, loadError: error });
|
|
300
|
+
});
|
|
301
|
+
}, []);
|
|
302
|
+
|
|
303
|
+
return null;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* GoogleMapsLoaderProvider
|
|
308
|
+
*
|
|
309
|
+
* Provider global que gerencia o carregamento da API do Google Maps.
|
|
310
|
+
* Usa carregamento manual do script para evitar conflitos com custom elements.
|
|
311
|
+
*/
|
|
312
|
+
export const GoogleMapsLoaderProvider = ({ children }: { children: ReactNode }) => {
|
|
313
|
+
const [shouldInitialize] = useState(() => {
|
|
314
|
+
const singleton = getOrCreateSingleton();
|
|
315
|
+
if (!singleton) return false;
|
|
316
|
+
|
|
317
|
+
// Se já está carregado, não inicializar
|
|
318
|
+
if (singleton.isLoaded || isGoogleMapsAlreadyLoaded() || isMarkerLibraryLoaded()) {
|
|
319
|
+
singleton.isLoaded = true;
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return true;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<>
|
|
328
|
+
{shouldInitialize && <LoaderInitializer />}
|
|
329
|
+
<SingletonLoaderWrapper>{children}</SingletonLoaderWrapper>
|
|
330
|
+
</>
|
|
331
|
+
);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
export const useGoogleMapsLoader = () => useContext(GoogleMapsContext);
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Recarrega o Google Maps com uma nova API key
|
|
338
|
+
*/
|
|
339
|
+
export function reloadGoogleMaps(newApiKey: string): Promise<void> {
|
|
340
|
+
return new Promise((resolve, reject) => {
|
|
341
|
+
if (typeof window === 'undefined') {
|
|
342
|
+
reject(new Error('Window is undefined'));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Validar API key
|
|
347
|
+
if (!newApiKey || newApiKey.length < 10) {
|
|
348
|
+
reject(new Error('Invalid or missing Google Maps API key'));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Verificar se a key atual é a mesma
|
|
353
|
+
const existingScript = document.querySelector(`script[src*=\"maps.googleapis.com/maps/api/js\"]`) as HTMLScriptElement;
|
|
354
|
+
if (existingScript && existingScript.src.includes(`key=${newApiKey}`)) {
|
|
355
|
+
// Mesma key, apenas resolver
|
|
356
|
+
if (isGoogleMapsAlreadyLoaded() && isMarkerLibraryLoaded()) {
|
|
357
|
+
resolve();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Remover script antigo
|
|
363
|
+
removeExistingScript();
|
|
364
|
+
|
|
365
|
+
// Atualizar singleton
|
|
366
|
+
updateSingleton({ isLoaded: false, loadError: undefined });
|
|
367
|
+
|
|
368
|
+
// Carregar novo script
|
|
369
|
+
loadGoogleMapsScript(newApiKey)
|
|
370
|
+
.then(() => {
|
|
371
|
+
updateSingleton({ isLoaded: true, loadError: undefined });
|
|
372
|
+
resolve();
|
|
373
|
+
})
|
|
374
|
+
.catch((error) => {
|
|
375
|
+
updateSingleton({ isLoaded: false, loadError: error });
|
|
376
|
+
reject(error);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
5
|
+
|
|
6
|
+
import { cn } from "./utils";
|
|
7
|
+
|
|
8
|
+
function HoverCard({
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
|
|
11
|
+
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function HoverCardTrigger({
|
|
15
|
+
...props
|
|
16
|
+
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
|
17
|
+
return (
|
|
18
|
+
<HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function HoverCardContent({
|
|
23
|
+
className,
|
|
24
|
+
align = "center",
|
|
25
|
+
sideOffset = 4,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
|
|
28
|
+
return (
|
|
29
|
+
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
|
30
|
+
<HoverCardPrimitive.Content
|
|
31
|
+
data-slot="hover-card-content"
|
|
32
|
+
align={align}
|
|
33
|
+
sideOffset={sideOffset}
|
|
34
|
+
className={cn(
|
|
35
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
36
|
+
className,
|
|
37
|
+
)}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
</HoverCardPrimitive.Portal>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { HoverCard, HoverCardTrigger, HoverCardContent };
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Xertica UI - Design System Components
|
|
3
|
+
*
|
|
4
|
+
* A comprehensive React component library built with Tailwind CSS v4.0
|
|
5
|
+
* following the CLI-first architecture (shadcn model).
|
|
6
|
+
*
|
|
7
|
+
* @package xertica-ui
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Utilities
|
|
11
|
+
export { cn } from "./utils";
|
|
12
|
+
|
|
13
|
+
// Layout & Structure
|
|
14
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent } from "./card";
|
|
15
|
+
export { Separator } from "./separator";
|
|
16
|
+
export { AspectRatio } from "./aspect-ratio";
|
|
17
|
+
export { Resizable, ResizableHandle, ResizablePanel } from "./resizable";
|
|
18
|
+
|
|
19
|
+
// Navigation
|
|
20
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent } from "./tabs";
|
|
21
|
+
export { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "./breadcrumb";
|
|
22
|
+
export {
|
|
23
|
+
NavigationMenu,
|
|
24
|
+
NavigationMenuList,
|
|
25
|
+
NavigationMenuItem,
|
|
26
|
+
NavigationMenuContent,
|
|
27
|
+
NavigationMenuTrigger,
|
|
28
|
+
NavigationMenuLink,
|
|
29
|
+
NavigationMenuIndicator,
|
|
30
|
+
NavigationMenuViewport,
|
|
31
|
+
navigationMenuTriggerStyle
|
|
32
|
+
} from "./navigation-menu";
|
|
33
|
+
export {
|
|
34
|
+
Sidebar,
|
|
35
|
+
SidebarContent,
|
|
36
|
+
SidebarFooter,
|
|
37
|
+
SidebarGroup,
|
|
38
|
+
SidebarGroupAction,
|
|
39
|
+
SidebarGroupContent,
|
|
40
|
+
SidebarGroupLabel,
|
|
41
|
+
SidebarHeader,
|
|
42
|
+
SidebarInput,
|
|
43
|
+
SidebarInset,
|
|
44
|
+
SidebarMenu,
|
|
45
|
+
SidebarMenuAction,
|
|
46
|
+
SidebarMenuBadge,
|
|
47
|
+
SidebarMenuButton,
|
|
48
|
+
SidebarMenuItem,
|
|
49
|
+
SidebarMenuSkeleton,
|
|
50
|
+
SidebarMenuSub,
|
|
51
|
+
SidebarMenuSubButton,
|
|
52
|
+
SidebarMenuSubItem,
|
|
53
|
+
SidebarProvider,
|
|
54
|
+
SidebarRail,
|
|
55
|
+
SidebarSeparator,
|
|
56
|
+
SidebarTrigger,
|
|
57
|
+
useSidebar
|
|
58
|
+
} from "./sidebar";
|
|
59
|
+
export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "./pagination";
|
|
60
|
+
|
|
61
|
+
// Buttons & Interactions
|
|
62
|
+
export { Button, buttonVariants } from "./button";
|
|
63
|
+
export { Toggle, toggleVariants } from "./toggle";
|
|
64
|
+
export { ToggleGroup, ToggleGroupItem } from "./toggle-group";
|
|
65
|
+
|
|
66
|
+
// Forms & Inputs
|
|
67
|
+
export { Input } from "./input";
|
|
68
|
+
export { Textarea } from "./textarea";
|
|
69
|
+
export { Label } from "./label";
|
|
70
|
+
export { Checkbox } from "./checkbox";
|
|
71
|
+
export { RadioGroup, RadioGroupItem } from "./radio-group";
|
|
72
|
+
export { Switch } from "./switch";
|
|
73
|
+
export { Slider } from "./slider";
|
|
74
|
+
export {
|
|
75
|
+
Select,
|
|
76
|
+
SelectGroup,
|
|
77
|
+
SelectValue,
|
|
78
|
+
SelectTrigger,
|
|
79
|
+
SelectContent,
|
|
80
|
+
SelectLabel,
|
|
81
|
+
SelectItem,
|
|
82
|
+
SelectSeparator,
|
|
83
|
+
SelectScrollUpButton,
|
|
84
|
+
SelectScrollDownButton
|
|
85
|
+
} from "./select";
|
|
86
|
+
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "./input-otp";
|
|
87
|
+
export {
|
|
88
|
+
Form,
|
|
89
|
+
FormItem,
|
|
90
|
+
FormLabel,
|
|
91
|
+
FormControl,
|
|
92
|
+
FormDescription,
|
|
93
|
+
FormMessage,
|
|
94
|
+
FormField,
|
|
95
|
+
useFormField
|
|
96
|
+
} from "./form";
|
|
97
|
+
export { Calendar } from "./calendar";
|
|
98
|
+
|
|
99
|
+
// Overlays & Dialogs
|
|
100
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./dialog";
|
|
101
|
+
export {
|
|
102
|
+
AlertDialog,
|
|
103
|
+
AlertDialogAction,
|
|
104
|
+
AlertDialogCancel,
|
|
105
|
+
AlertDialogContent,
|
|
106
|
+
AlertDialogDescription,
|
|
107
|
+
AlertDialogFooter,
|
|
108
|
+
AlertDialogHeader,
|
|
109
|
+
AlertDialogOverlay,
|
|
110
|
+
AlertDialogPortal,
|
|
111
|
+
AlertDialogTitle,
|
|
112
|
+
AlertDialogTrigger
|
|
113
|
+
} from "./alert-dialog";
|
|
114
|
+
export { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger } from "./sheet";
|
|
115
|
+
export { Drawer, DrawerContent, DrawerDescription, DrawerFooter, DrawerHandle, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from "./drawer";
|
|
116
|
+
export { Popover, PopoverContent, PopoverTrigger } from "./popover";
|
|
117
|
+
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
|
|
118
|
+
export { HoverCard, HoverCardContent, HoverCardTrigger } from "./hover-card";
|
|
119
|
+
|
|
120
|
+
// Menus
|
|
121
|
+
export {
|
|
122
|
+
DropdownMenu,
|
|
123
|
+
DropdownMenuPortal,
|
|
124
|
+
DropdownMenuTrigger,
|
|
125
|
+
DropdownMenuContent,
|
|
126
|
+
DropdownMenuGroup,
|
|
127
|
+
DropdownMenuLabel,
|
|
128
|
+
DropdownMenuItem,
|
|
129
|
+
DropdownMenuCheckboxItem,
|
|
130
|
+
DropdownMenuRadioGroup,
|
|
131
|
+
DropdownMenuRadioItem,
|
|
132
|
+
DropdownMenuSeparator,
|
|
133
|
+
DropdownMenuShortcut,
|
|
134
|
+
DropdownMenuSub,
|
|
135
|
+
DropdownMenuSubTrigger,
|
|
136
|
+
DropdownMenuSubContent
|
|
137
|
+
} from "./dropdown-menu";
|
|
138
|
+
export {
|
|
139
|
+
ContextMenu,
|
|
140
|
+
ContextMenuTrigger,
|
|
141
|
+
ContextMenuContent,
|
|
142
|
+
ContextMenuItem,
|
|
143
|
+
ContextMenuCheckboxItem,
|
|
144
|
+
ContextMenuRadioItem,
|
|
145
|
+
ContextMenuLabel,
|
|
146
|
+
ContextMenuSeparator,
|
|
147
|
+
ContextMenuShortcut,
|
|
148
|
+
ContextMenuGroup,
|
|
149
|
+
ContextMenuPortal,
|
|
150
|
+
ContextMenuSub,
|
|
151
|
+
ContextMenuSubContent,
|
|
152
|
+
ContextMenuSubTrigger,
|
|
153
|
+
ContextMenuRadioGroup
|
|
154
|
+
} from "./context-menu";
|
|
155
|
+
export {
|
|
156
|
+
Menubar,
|
|
157
|
+
MenubarMenu,
|
|
158
|
+
MenubarTrigger,
|
|
159
|
+
MenubarContent,
|
|
160
|
+
MenubarItem,
|
|
161
|
+
MenubarSeparator,
|
|
162
|
+
MenubarLabel,
|
|
163
|
+
MenubarCheckboxItem,
|
|
164
|
+
MenubarRadioGroup,
|
|
165
|
+
MenubarRadioItem,
|
|
166
|
+
MenubarPortal,
|
|
167
|
+
MenubarSubContent,
|
|
168
|
+
MenubarSubTrigger,
|
|
169
|
+
MenubarGroup,
|
|
170
|
+
MenubarSub,
|
|
171
|
+
MenubarShortcut
|
|
172
|
+
} from "./menubar";
|
|
173
|
+
export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "./command";
|
|
174
|
+
|
|
175
|
+
// Feedback & Notifications
|
|
176
|
+
export { Alert, AlertTitle, AlertDescription } from "./alert";
|
|
177
|
+
export { Badge, badgeVariants } from "./badge";
|
|
178
|
+
export { Progress } from "./progress";
|
|
179
|
+
export { Skeleton } from "./skeleton";
|
|
180
|
+
export { Toaster } from "./sonner";
|
|
181
|
+
export { NotificationBadge } from "./notification-badge";
|
|
182
|
+
export type { NotificationBadgeProps } from "./notification-badge";
|
|
183
|
+
|
|
184
|
+
// Data Display
|
|
185
|
+
export { Avatar, AvatarImage, AvatarFallback } from "./avatar";
|
|
186
|
+
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption } from "./table";
|
|
187
|
+
export {
|
|
188
|
+
ChartContainer,
|
|
189
|
+
ChartTooltip,
|
|
190
|
+
ChartTooltipContent,
|
|
191
|
+
ChartLegend,
|
|
192
|
+
ChartLegendContent,
|
|
193
|
+
ChartStyle
|
|
194
|
+
} from "./chart";
|
|
195
|
+
export { Empty } from "./empty";
|
|
196
|
+
export { StatsCard } from "./stats-card";
|
|
197
|
+
export type { StatsCardProps } from "./stats-card";
|
|
198
|
+
export {
|
|
199
|
+
Timeline,
|
|
200
|
+
TimelineItem,
|
|
201
|
+
TimelineDot,
|
|
202
|
+
TimelineContent,
|
|
203
|
+
TimelineHeading,
|
|
204
|
+
TimelineTime,
|
|
205
|
+
TimelineDescription
|
|
206
|
+
} from "./timeline";
|
|
207
|
+
export { TreeView } from "./tree-view";
|
|
208
|
+
export type { TreeViewProps, TreeNode } from "./tree-view";
|
|
209
|
+
|
|
210
|
+
// Utility Components
|
|
211
|
+
export { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./collapsible";
|
|
212
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./accordion";
|
|
213
|
+
export { ScrollArea, ScrollBar } from "./scroll-area";
|
|
214
|
+
export { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, type CarouselApi } from "./carousel";
|
|
215
|
+
export { Stepper, Step, useStepper } from "./stepper";
|
|
216
|
+
export { FileUpload } from "./file-upload";
|
|
217
|
+
export type { FileUploadProps } from "./file-upload";
|
|
218
|
+
export { Rating } from "./rating";
|
|
219
|
+
export type { RatingProps } from "./rating";
|
|
220
|
+
export { Search } from "./search";
|
|
221
|
+
export type { SearchProps } from "./search";
|
|
222
|
+
|
|
223
|
+
// Maps
|
|
224
|
+
export { Map } from "./map";
|
|
225
|
+
export { RouteMap } from "./route-map";
|
|
226
|
+
export type { MapProps } from "./map";
|
|
227
|
+
export type { RouteMapProps } from "./route-map";
|
|
228
|
+
export type { MapLayerType, MapLayersConfig } from "./map-layers";
|
|
229
|
+
|
|
230
|
+
// Xertica Custom Components
|
|
231
|
+
export { XerticaAssistant } from "./xertica-assistant";
|
|
232
|
+
export type { MessageType, AttachmentType } from "./xertica-assistant";
|
|
233
|
+
|
|
234
|
+
// Page Components
|
|
235
|
+
export { PageHeader, PageHeaderDescription, PageHeaderHeading } from "./page-header";
|
|
236
|
+
export type { BreadcrumbItem as PageHeaderBreadcrumbItem, Language as PageHeaderLanguage, PageHeaderProps } from "./page-header";
|
|
237
|
+
|
|
238
|
+
// Chart Types
|
|
239
|
+
export type { ChartConfig } from "./chart";
|
|
240
|
+
|
|
241
|
+
// Hooks
|
|
242
|
+
export { useMobile, useIsMobile } from "./use-mobile";
|