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.
Files changed (141) hide show
  1. package/App.tsx +182 -0
  2. package/README.md +330 -0
  3. package/assets/xertica-logo.svg +38 -0
  4. package/assets/xertica-x-logo.svg +21 -0
  5. package/bin/cli.ts +193 -0
  6. package/components/AssistenteXertica.tsx +2003 -0
  7. package/components/AudioPlayer.tsx +203 -0
  8. package/components/CodeBlock.tsx +242 -0
  9. package/components/DocumentEditor.tsx +504 -0
  10. package/components/ForgotPasswordPage.tsx +170 -0
  11. package/components/FormattedDocument.tsx +87 -0
  12. package/components/HomeContent.tsx +123 -0
  13. package/components/HomePage.tsx +70 -0
  14. package/components/LanguageSelector.tsx +54 -0
  15. package/components/LoginPage.tsx +199 -0
  16. package/components/MarkdownMessage.tsx +62 -0
  17. package/components/ModernChatInput.tsx +502 -0
  18. package/components/PodcastPlayer.tsx +409 -0
  19. package/components/ResetPasswordPage.tsx +234 -0
  20. package/components/Sidebar.tsx +489 -0
  21. package/components/TemplateContent.tsx +629 -0
  22. package/components/TemplatePage.tsx +70 -0
  23. package/components/ThemeToggle.tsx +65 -0
  24. package/components/VerifyEmailPage.tsx +187 -0
  25. package/components/XerticaLogo.tsx +69 -0
  26. package/components/XerticaOrbe.tsx +1339 -0
  27. package/components/XerticaXLogo.tsx +53 -0
  28. package/components/examples/DrawingMapExample.tsx +530 -0
  29. package/components/examples/FilterableMapExample.tsx +380 -0
  30. package/components/examples/LocationPickerExample.tsx +330 -0
  31. package/components/examples/MapExamples.tsx +280 -0
  32. package/components/examples/MapShowcase.tsx +446 -0
  33. package/components/examples/RouteMapExamples.tsx +329 -0
  34. package/components/examples/SimpleFilterableMap.tsx +192 -0
  35. package/components/examples/index.ts +52 -0
  36. package/components/figma/ImageWithFallback.tsx +27 -0
  37. package/components/index.ts +44 -0
  38. package/components/media/AudioPlayer.tsx +278 -0
  39. package/components/media/FloatingMediaWrapper.tsx +166 -0
  40. package/components/media/VideoPlayer.tsx +285 -0
  41. package/components/ui/accordion.tsx +66 -0
  42. package/components/ui/alert-dialog.tsx +159 -0
  43. package/components/ui/alert.tsx +91 -0
  44. package/components/ui/aspect-ratio.tsx +11 -0
  45. package/components/ui/avatar.tsx +65 -0
  46. package/components/ui/badge.tsx +55 -0
  47. package/components/ui/breadcrumb.tsx +109 -0
  48. package/components/ui/button.tsx +78 -0
  49. package/components/ui/calendar.tsx +235 -0
  50. package/components/ui/card.tsx +92 -0
  51. package/components/ui/carousel.tsx +241 -0
  52. package/components/ui/chart.tsx +353 -0
  53. package/components/ui/checkbox.tsx +32 -0
  54. package/components/ui/collapsible.tsx +33 -0
  55. package/components/ui/command.tsx +177 -0
  56. package/components/ui/context-menu.tsx +252 -0
  57. package/components/ui/dialog.tsx +138 -0
  58. package/components/ui/drawer.tsx +134 -0
  59. package/components/ui/dropdown-menu.tsx +257 -0
  60. package/components/ui/empty.tsx +90 -0
  61. package/components/ui/file-upload.tsx +152 -0
  62. package/components/ui/form.tsx +195 -0
  63. package/components/ui/google-maps-loader.tsx +379 -0
  64. package/components/ui/hover-card.tsx +44 -0
  65. package/components/ui/index.ts +242 -0
  66. package/components/ui/input-otp.tsx +77 -0
  67. package/components/ui/input.tsx +38 -0
  68. package/components/ui/label.tsx +24 -0
  69. package/components/ui/map-config.ts +12 -0
  70. package/components/ui/map-layers.tsx +129 -0
  71. package/components/ui/map.exports.ts +31 -0
  72. package/components/ui/map.tsx +412 -0
  73. package/components/ui/menubar.tsx +276 -0
  74. package/components/ui/navigation-menu.tsx +162 -0
  75. package/components/ui/notification-badge.tsx +61 -0
  76. package/components/ui/page-header.tsx +229 -0
  77. package/components/ui/pagination.tsx +127 -0
  78. package/components/ui/popover.tsx +48 -0
  79. package/components/ui/progress.tsx +31 -0
  80. package/components/ui/radio-group.tsx +56 -0
  81. package/components/ui/rating.tsx +102 -0
  82. package/components/ui/resizable.tsx +405 -0
  83. package/components/ui/route-map.tsx +246 -0
  84. package/components/ui/scroll-area.tsx +58 -0
  85. package/components/ui/search.tsx +70 -0
  86. package/components/ui/select.tsx +176 -0
  87. package/components/ui/separator.tsx +28 -0
  88. package/components/ui/sheet.tsx +138 -0
  89. package/components/ui/sidebar.tsx +726 -0
  90. package/components/ui/simple-map.tsx +92 -0
  91. package/components/ui/skeleton.tsx +13 -0
  92. package/components/ui/slider.tsx +58 -0
  93. package/components/ui/sonner.tsx +77 -0
  94. package/components/ui/stats-card.tsx +84 -0
  95. package/components/ui/stepper.tsx +126 -0
  96. package/components/ui/switch.tsx +34 -0
  97. package/components/ui/table.tsx +116 -0
  98. package/components/ui/tabs.tsx +66 -0
  99. package/components/ui/textarea.tsx +26 -0
  100. package/components/ui/timeline.tsx +140 -0
  101. package/components/ui/toggle-group.tsx +71 -0
  102. package/components/ui/toggle.tsx +46 -0
  103. package/components/ui/tooltip.tsx +61 -0
  104. package/components/ui/tree-view.tsx +123 -0
  105. package/components/ui/use-mobile.ts +24 -0
  106. package/components/ui/utils.ts +6 -0
  107. package/components/ui/xertica-assistant.tsx +1420 -0
  108. package/contexts/ApiKeyContext.tsx +123 -0
  109. package/contexts/AssistenteContext.tsx +118 -0
  110. package/contexts/BrandColorsContext.tsx +551 -0
  111. package/contexts/LanguageContext.tsx +36 -0
  112. package/contexts/ThemeContext.tsx +85 -0
  113. package/dist/cli.js +20922 -0
  114. package/eslint.config.js +41 -0
  115. package/guidelines/Guidelines.md +61 -0
  116. package/hooks/useTheme.ts +4 -0
  117. package/imports/Podcast.tsx +389 -0
  118. package/imports/XerticaAi.tsx +46 -0
  119. package/imports/XerticaX.tsx +20 -0
  120. package/imports/svg-aueiaqngck.ts +11 -0
  121. package/imports/svg-v9krss1ozd.ts +16 -0
  122. package/imports/svg-vhrdofe3qe.ts +5 -0
  123. package/index.css +4448 -0
  124. package/index.html +14 -0
  125. package/main.tsx +10 -0
  126. package/package.json +119 -0
  127. package/postcss.config.js +6 -0
  128. package/routes.tsx +33 -0
  129. package/styles/globals.css +15 -0
  130. package/styles/xertica/app-overrides/chat.css +61 -0
  131. package/styles/xertica/app-overrides/scrollbar.css +33 -0
  132. package/styles/xertica/base.css +70 -0
  133. package/styles/xertica/integrations/google-maps.css +76 -0
  134. package/styles/xertica/integrations/sonner.css +73 -0
  135. package/styles/xertica/theme-map.css +88 -0
  136. package/styles/xertica/tokens.css +190 -0
  137. package/tsconfig.json +31 -0
  138. package/tsconfig.node.json +10 -0
  139. package/utils/gemini.ts +140 -0
  140. package/vite-env.d.ts +12 -0
  141. package/vite.config.ts +36 -0
@@ -0,0 +1,285 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { Play, Pause, Volume2, VolumeX, Maximize, Settings, PictureInPicture } from 'lucide-react';
3
+ import { Slider } from '../ui/slider';
4
+ import { Button } from '../ui/button';
5
+ import { FloatingMediaWrapper } from './FloatingMediaWrapper';
6
+ import { cn } from '../ui/utils';
7
+
8
+ export interface VideoPlayerProps {
9
+ src: string;
10
+ poster?: string;
11
+ title?: string;
12
+ autoPlay?: boolean;
13
+ className?: string;
14
+ }
15
+
16
+ export function VideoPlayer({
17
+ src,
18
+ poster,
19
+ title = "Untitled Video",
20
+ autoPlay = false,
21
+ className
22
+ }: VideoPlayerProps) {
23
+ const videoRef = useRef<HTMLVideoElement>(null);
24
+ const containerRef = useRef<HTMLDivElement>(null);
25
+ const [isPlaying, setIsPlaying] = useState(false);
26
+ const [progress, setProgress] = useState(0);
27
+ const [volume, setVolume] = useState(1);
28
+ const [isMuted, setIsMuted] = useState(false);
29
+ const [currentTime, setCurrentTime] = useState(0);
30
+ const [duration, setDuration] = useState(0);
31
+ const [isFloating, setIsFloating] = useState(false);
32
+ const [isManualFloating, setIsManualFloating] = useState(false);
33
+ const [showControls, setShowControls] = useState(true);
34
+
35
+ let controlsTimeout: NodeJS.Timeout;
36
+
37
+ // Wrapper for setIsFloating to handle scroll-on-restore and sync state
38
+ const handleSetFloating = (floating: boolean) => {
39
+ if (videoRef.current) {
40
+ setCurrentTime(videoRef.current.currentTime);
41
+ }
42
+
43
+ setIsFloating(floating);
44
+
45
+ if (!floating) {
46
+ setIsManualFloating(false);
47
+ if (containerRef.current) {
48
+ containerRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
49
+ }
50
+ }
51
+ };
52
+
53
+ // Restore state when switching modes (remounting video element)
54
+ useEffect(() => {
55
+ const video = videoRef.current;
56
+ if (video) {
57
+ if (Math.abs(video.currentTime - currentTime) > 0.5) {
58
+ video.currentTime = currentTime;
59
+ }
60
+ if (isPlaying) {
61
+ const playPromise = video.play();
62
+ if (playPromise !== undefined) {
63
+ playPromise.catch(error => {
64
+ console.log("Playback interrupted during switch:", error);
65
+ });
66
+ }
67
+ }
68
+ }
69
+ }, [isFloating]);
70
+
71
+ // Auto-float on scroll logic
72
+ useEffect(() => {
73
+ const container = containerRef.current;
74
+ if (!container) return;
75
+
76
+ const observer = new IntersectionObserver(
77
+ ([entry]) => {
78
+ if (isPlaying && !entry.isIntersecting && !isFloating) {
79
+ if (videoRef.current) setCurrentTime(videoRef.current.currentTime);
80
+ setIsFloating(true);
81
+ } else if (entry.isIntersecting && isFloating && !isManualFloating) {
82
+ if (videoRef.current) setCurrentTime(videoRef.current.currentTime);
83
+ handleSetFloating(false);
84
+ }
85
+ },
86
+ { threshold: 0.2 }
87
+ );
88
+
89
+ observer.observe(container);
90
+ return () => observer.disconnect();
91
+ }, [isPlaying, isFloating, isManualFloating]);
92
+
93
+ // Video Event Listeners
94
+ useEffect(() => {
95
+ const video = videoRef.current;
96
+ if (!video) return;
97
+
98
+ const updateTime = () => {
99
+ setCurrentTime(video.currentTime);
100
+ };
101
+
102
+ const updateDuration = () => setDuration(video.duration);
103
+ const onPlay = () => setIsPlaying(true);
104
+ const onPause = () => setIsPlaying(false);
105
+
106
+ video.addEventListener('timeupdate', updateTime);
107
+ video.addEventListener('loadedmetadata', updateDuration);
108
+ video.addEventListener('play', onPlay);
109
+ video.addEventListener('pause', onPause);
110
+
111
+ // Initial volume set
112
+ video.volume = volume;
113
+ video.muted = isMuted;
114
+
115
+ return () => {
116
+ video.removeEventListener('timeupdate', updateTime);
117
+ video.removeEventListener('loadedmetadata', updateDuration);
118
+ video.removeEventListener('play', onPlay);
119
+ video.removeEventListener('pause', onPause);
120
+ };
121
+ }, [isFloating]); // Re-attach when element changes
122
+
123
+ const togglePlay = () => {
124
+ if (videoRef.current) {
125
+ if (isPlaying) {
126
+ videoRef.current.pause();
127
+ } else {
128
+ videoRef.current.play();
129
+ }
130
+ }
131
+ };
132
+
133
+ const handleSeek = (value: number[]) => {
134
+ if (videoRef.current) {
135
+ videoRef.current.currentTime = value[0];
136
+ setCurrentTime(value[0]);
137
+ }
138
+ };
139
+
140
+ const handleVolumeChange = (value: number[]) => {
141
+ const newVolume = value[0];
142
+ if (videoRef.current) {
143
+ videoRef.current.volume = newVolume;
144
+ setVolume(newVolume);
145
+ setIsMuted(newVolume === 0);
146
+ }
147
+ };
148
+
149
+ const toggleMute = () => {
150
+ if (videoRef.current) {
151
+ const newMutedState = !isMuted;
152
+ videoRef.current.muted = newMutedState;
153
+ setIsMuted(newMutedState);
154
+ if (newMutedState) {
155
+ setVolume(0);
156
+ } else {
157
+ setVolume(1);
158
+ videoRef.current.volume = 1;
159
+ }
160
+ }
161
+ };
162
+
163
+ const handleMouseMove = () => {
164
+ setShowControls(true);
165
+ clearTimeout(controlsTimeout);
166
+ controlsTimeout = setTimeout(() => {
167
+ if (isPlaying) setShowControls(false);
168
+ }, 2500);
169
+ };
170
+
171
+ const formatTime = (time: number) => {
172
+ const minutes = Math.floor(time / 60);
173
+ const seconds = Math.floor(time % 60);
174
+ return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
175
+ };
176
+
177
+ return (
178
+ <div ref={containerRef} className={className}>
179
+ <FloatingMediaWrapper
180
+ isFloating={isFloating}
181
+ setIsFloating={handleSetFloating}
182
+ onCloseMedia={() => {
183
+ setIsPlaying(false);
184
+ setIsFloating(false);
185
+ if (videoRef.current) videoRef.current.pause();
186
+ }}
187
+ title={title}
188
+ aspectRatio={16/9}
189
+ className="w-full h-full"
190
+ >
191
+ <div
192
+ className="relative w-full h-full bg-black group overflow-hidden"
193
+ onMouseMove={handleMouseMove}
194
+ onMouseLeave={() => isPlaying && setShowControls(false)}
195
+ >
196
+ <video
197
+ ref={videoRef}
198
+ src={src}
199
+ poster={poster}
200
+ className="w-full h-full object-contain"
201
+ onClick={togglePlay}
202
+ autoPlay={autoPlay}
203
+ />
204
+
205
+ {!isPlaying && (
206
+ <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
207
+ <div className="w-16 h-16 rounded-full bg-black/40 backdrop-blur-sm flex items-center justify-center border border-white/20 shadow-2xl">
208
+ <Play className="w-8 h-8 text-white ml-1" fill="white" />
209
+ </div>
210
+ </div>
211
+ )}
212
+
213
+ <div
214
+ className={cn(
215
+ "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-4 transition-opacity duration-300",
216
+ showControls ? "opacity-100" : "opacity-0"
217
+ )}
218
+ >
219
+ <div className="mb-4 group/slider">
220
+ <Slider
221
+ value={[currentTime]}
222
+ max={duration || 100}
223
+ step={1}
224
+ onValueChange={handleSeek}
225
+ className="cursor-pointer"
226
+ />
227
+ </div>
228
+
229
+ <div className="flex items-center justify-between">
230
+ <div className="flex items-center gap-4">
231
+ <Button
232
+ variant="ghost"
233
+ size="icon"
234
+ className="text-white hover:bg-white/20 h-8 w-8"
235
+ onClick={togglePlay}
236
+ >
237
+ {isPlaying ? <Pause className="w-5 h-5" fill="currentColor" /> : <Play className="w-5 h-5" fill="currentColor" />}
238
+ </Button>
239
+
240
+ <div className="flex items-center gap-2 group/vol">
241
+ <Button
242
+ variant="ghost"
243
+ size="icon"
244
+ className="text-white hover:bg-white/20 h-8 w-8"
245
+ onClick={toggleMute}
246
+ >
247
+ {isMuted || volume === 0 ? <VolumeX className="w-5 h-5" /> : <Volume2 className="w-5 h-5" />}
248
+ </Button>
249
+ <div className="w-0 overflow-hidden group-hover/vol:w-20 transition-all duration-300">
250
+ <Slider
251
+ value={[isMuted ? 0 : volume]}
252
+ max={1}
253
+ step={0.01}
254
+ onValueChange={handleVolumeChange}
255
+ className="w-20"
256
+ />
257
+ </div>
258
+ </div>
259
+
260
+ <div className="text-white/90 text-xs font-medium tabular-nums select-none">
261
+ {formatTime(currentTime)} / {formatTime(duration)}
262
+ </div>
263
+ </div>
264
+
265
+ <div className="flex items-center gap-2">
266
+ <Button
267
+ variant="ghost"
268
+ size="icon"
269
+ className="text-white hover:bg-white/20 h-8 w-8"
270
+ onClick={() => {
271
+ if (!isFloating) setIsManualFloating(true);
272
+ handleSetFloating(!isFloating);
273
+ }}
274
+ title={isFloating ? "Restaurar" : "Pop-out Player"}
275
+ >
276
+ {isFloating ? <Maximize className="w-4 h-4" /> : <PictureInPicture className="w-4 h-4" />}
277
+ </Button>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ </FloatingMediaWrapper>
283
+ </div>
284
+ );
285
+ }
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as AccordionPrimitive from "@radix-ui/react-accordion";
5
+ import { ChevronDownIcon } from "lucide-react";
6
+
7
+ import { cn } from "./utils";
8
+
9
+ function Accordion({
10
+ ...props
11
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
12
+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
13
+ }
14
+
15
+ function AccordionItem({
16
+ className,
17
+ ...props
18
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
19
+ return (
20
+ <AccordionPrimitive.Item
21
+ data-slot="accordion-item"
22
+ className={cn("border-b last:border-b-0", className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function AccordionTrigger({
29
+ className,
30
+ children,
31
+ ...props
32
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
33
+ return (
34
+ <AccordionPrimitive.Header className="flex">
35
+ <AccordionPrimitive.Trigger
36
+ data-slot="accordion-trigger"
37
+ className={cn(
38
+ "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
45
+ </AccordionPrimitive.Trigger>
46
+ </AccordionPrimitive.Header>
47
+ );
48
+ }
49
+
50
+ function AccordionContent({
51
+ className,
52
+ children,
53
+ ...props
54
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
55
+ return (
56
+ <AccordionPrimitive.Content
57
+ data-slot="accordion-content"
58
+ className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
59
+ {...props}
60
+ >
61
+ <div className={cn("pt-0 pb-4", className)}>{children}</div>
62
+ </AccordionPrimitive.Content>
63
+ );
64
+ }
65
+
66
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -0,0 +1,159 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
5
+
6
+ import { cn } from "./utils";
7
+ import { buttonVariants } from "./button";
8
+
9
+ function AlertDialog({
10
+ ...props
11
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
12
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
13
+ }
14
+
15
+ function AlertDialogTrigger({
16
+ ...props
17
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
18
+ return (
19
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
20
+ );
21
+ }
22
+
23
+ function AlertDialogPortal({
24
+ ...props
25
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
26
+ return (
27
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
28
+ );
29
+ }
30
+
31
+ const AlertDialogOverlay = React.forwardRef<
32
+ React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
33
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
34
+ >(({ className, ...props }, ref) => {
35
+ return (
36
+ <AlertDialogPrimitive.Overlay
37
+ ref={ref}
38
+ data-slot="alert-dialog-overlay"
39
+ className={cn(
40
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
41
+ className,
42
+ )}
43
+ {...props}
44
+ />
45
+ );
46
+ });
47
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
48
+
49
+ function AlertDialogContent({
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
53
+ return (
54
+ <AlertDialogPortal>
55
+ <AlertDialogOverlay />
56
+ <AlertDialogPrimitive.Content
57
+ data-slot="alert-dialog-content"
58
+ className={cn(
59
+ "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
60
+ className,
61
+ )}
62
+ {...props}
63
+ />
64
+ </AlertDialogPortal>
65
+ );
66
+ }
67
+
68
+ function AlertDialogHeader({
69
+ className,
70
+ ...props
71
+ }: React.ComponentProps<"div">) {
72
+ return (
73
+ <div
74
+ data-slot="alert-dialog-header"
75
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
76
+ {...props}
77
+ />
78
+ );
79
+ }
80
+
81
+ function AlertDialogFooter({
82
+ className,
83
+ ...props
84
+ }: React.ComponentProps<"div">) {
85
+ return (
86
+ <div
87
+ data-slot="alert-dialog-footer"
88
+ className={cn(
89
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
90
+ className,
91
+ )}
92
+ {...props}
93
+ />
94
+ );
95
+ }
96
+
97
+ function AlertDialogTitle({
98
+ className,
99
+ ...props
100
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
101
+ return (
102
+ <AlertDialogPrimitive.Title
103
+ data-slot="alert-dialog-title"
104
+ className={cn("text-lg font-semibold", className)}
105
+ {...props}
106
+ />
107
+ );
108
+ }
109
+
110
+ function AlertDialogDescription({
111
+ className,
112
+ ...props
113
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
114
+ return (
115
+ <AlertDialogPrimitive.Description
116
+ data-slot="alert-dialog-description"
117
+ className={cn("text-muted-foreground text-sm", className)}
118
+ {...props}
119
+ />
120
+ );
121
+ }
122
+
123
+ function AlertDialogAction({
124
+ className,
125
+ ...props
126
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
127
+ return (
128
+ <AlertDialogPrimitive.Action
129
+ className={cn(buttonVariants(), className)}
130
+ {...props}
131
+ />
132
+ );
133
+ }
134
+
135
+ function AlertDialogCancel({
136
+ className,
137
+ ...props
138
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
139
+ return (
140
+ <AlertDialogPrimitive.Cancel
141
+ className={cn(buttonVariants({ variant: "outline" }), className)}
142
+ {...props}
143
+ />
144
+ );
145
+ }
146
+
147
+ export {
148
+ AlertDialog,
149
+ AlertDialogPortal,
150
+ AlertDialogOverlay,
151
+ AlertDialogTrigger,
152
+ AlertDialogContent,
153
+ AlertDialogHeader,
154
+ AlertDialogFooter,
155
+ AlertDialogTitle,
156
+ AlertDialogDescription,
157
+ AlertDialogAction,
158
+ AlertDialogCancel,
159
+ };
@@ -0,0 +1,91 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { CheckCircle, Info, AlertTriangle, XCircle } from "lucide-react";
4
+
5
+ import { cn } from "./utils";
6
+
7
+ const alertVariants = cva(
8
+ "relative w-full rounded-[var(--radius)] border-l-4 px-4 py-3 grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-5 [&>svg]:translate-y-0.5 [&>svg]:text-current",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-muted/50 border-l-border text-foreground [&>svg]:text-muted-foreground",
13
+ success: "bg-[color:var(--success)]/10 dark:bg-[color:var(--success)]/20 border-l-[color:var(--success)] text-foreground [&>svg]:text-[color:var(--success)]",
14
+ info: "bg-[color:var(--info)]/10 dark:bg-[color:var(--info)]/20 border-l-[color:var(--info)] text-foreground [&>svg]:text-[color:var(--info)]",
15
+ warning: "bg-[color:var(--warning)]/10 dark:bg-[color:var(--warning)]/20 border-l-[color:var(--warning)] text-foreground [&>svg]:text-[color:var(--warning)]",
16
+ error: "bg-[color:var(--destructive)]/10 dark:bg-[color:var(--destructive)]/20 border-l-[color:var(--destructive)] text-foreground [&>svg]:text-[color:var(--destructive)]",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ },
22
+ },
23
+ );
24
+
25
+ const alertIcons = {
26
+ default: Info,
27
+ success: CheckCircle,
28
+ info: Info,
29
+ warning: AlertTriangle,
30
+ error: XCircle,
31
+ } as const;
32
+
33
+ type AlertVariantType = keyof typeof alertIcons;
34
+
35
+ function Alert({
36
+ className,
37
+ variant,
38
+ children,
39
+ ...props
40
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
41
+ // Determinar a variante válida com fallback seguro
42
+ const alertVariant: AlertVariantType =
43
+ variant && variant in alertIcons
44
+ ? (variant as AlertVariantType)
45
+ : "default";
46
+
47
+ const Icon = alertIcons[alertVariant];
48
+
49
+ return (
50
+ <div
51
+ data-slot="alert"
52
+ role="alert"
53
+ className={cn(alertVariants({ variant }), className)}
54
+ {...props}
55
+ >
56
+ <Icon className="flex-shrink-0" />
57
+ <div className="flex-1">{children}</div>
58
+ </div>
59
+ );
60
+ }
61
+
62
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
63
+ return (
64
+ <div
65
+ data-slot="alert-title"
66
+ className={cn(
67
+ "font-medium mb-1 leading-tight",
68
+ className,
69
+ )}
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ function AlertDescription({
76
+ className,
77
+ ...props
78
+ }: React.ComponentProps<"div">) {
79
+ return (
80
+ <div
81
+ data-slot="alert-description"
82
+ className={cn(
83
+ "leading-relaxed",
84
+ className,
85
+ )}
86
+ {...props}
87
+ />
88
+ );
89
+ }
90
+
91
+ export { Alert, AlertTitle, AlertDescription };
@@ -0,0 +1,11 @@
1
+ "use client";
2
+
3
+ import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
4
+
5
+ function AspectRatio({
6
+ ...props
7
+ }: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
8
+ return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />;
9
+ }
10
+
11
+ export { AspectRatio };
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
5
+
6
+ import { cn } from "./utils";
7
+
8
+ function Avatar({
9
+ className,
10
+ size = "md",
11
+ ...props
12
+ }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
13
+ size?: "xs" | "sm" | "md" | "lg" | "xl";
14
+ }) {
15
+ const sizeClasses = {
16
+ xs: "size-6",
17
+ sm: "size-8",
18
+ md: "size-10",
19
+ lg: "size-12",
20
+ xl: "size-16",
21
+ };
22
+
23
+ return (
24
+ <AvatarPrimitive.Root
25
+ data-slot="avatar"
26
+ className={cn(
27
+ "relative flex shrink-0 overflow-hidden rounded-full",
28
+ sizeClasses[size],
29
+ className,
30
+ )}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ function AvatarImage({
37
+ className,
38
+ ...props
39
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
40
+ return (
41
+ <AvatarPrimitive.Image
42
+ data-slot="avatar-image"
43
+ className={cn("aspect-square size-full", className)}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function AvatarFallback({
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
53
+ return (
54
+ <AvatarPrimitive.Fallback
55
+ data-slot="avatar-fallback"
56
+ className={cn(
57
+ "bg-muted flex size-full items-center justify-center rounded-full",
58
+ className,
59
+ )}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ export { Avatar, AvatarImage, AvatarFallback };