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,246 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { cn } from './utils';
3
+ import { useGoogleMapsLoader } from './google-maps-loader';
4
+
5
+ export interface RouteMapProps extends React.HTMLAttributes<HTMLDivElement> {
6
+ origin: { lat: number; lng: number };
7
+ destination: { lat: number; lng: number };
8
+ waypoints?: Array<{ lat: number; lng: number }>;
9
+ travelMode?: 'DRIVING' | 'WALKING' | 'BICYCLING' | 'TRANSIT';
10
+ height?: string;
11
+ apiKey?: string;
12
+ mapContainerClassName?: string;
13
+ disableDefaultUI?: boolean;
14
+ zoomControl?: boolean;
15
+ streetViewControl?: boolean;
16
+ mapTypeControl?: boolean;
17
+ fullscreenControl?: boolean;
18
+ onRouteCalculated?: (distance: string, duration: string) => void;
19
+ }
20
+
21
+ const RouteMapContent = React.forwardRef<HTMLDivElement, RouteMapProps & { apiKey: string }>(
22
+ ({ apiKey, ...props }, ref) => {
23
+ const { isLoaded, loadError } = useGoogleMapsLoader();
24
+
25
+ const {
26
+ origin,
27
+ destination,
28
+ waypoints = [],
29
+ travelMode = 'DRIVING',
30
+ height = "450px",
31
+ mapContainerClassName,
32
+ disableDefaultUI = false,
33
+ zoomControl = true,
34
+ streetViewControl = false,
35
+ mapTypeControl = false,
36
+ fullscreenControl = true,
37
+ onRouteCalculated,
38
+ className,
39
+ ...divProps
40
+ } = props;
41
+
42
+ const mapRef = useRef<google.maps.Map | null>(null);
43
+ const mapContainerRef = useRef<HTMLDivElement>(null);
44
+ const directionsRendererRef = useRef<google.maps.DirectionsRenderer | null>(null);
45
+ const isInitializedRef = useRef(false);
46
+ const isCalculatingRef = useRef(false);
47
+
48
+ // Initialize map once
49
+ useEffect(() => {
50
+ if (!isLoaded || !mapContainerRef.current || isInitializedRef.current) return;
51
+
52
+ isInitializedRef.current = true;
53
+ const map = new google.maps.Map(mapContainerRef.current, {
54
+ center: origin,
55
+ zoom: 13,
56
+ mapId: 'xertica-route-map',
57
+ disableDefaultUI,
58
+ zoomControl,
59
+ streetViewControl,
60
+ mapTypeControl,
61
+ fullscreenControl,
62
+ });
63
+
64
+ mapRef.current = map;
65
+
66
+ // Create directions renderer
67
+ const computedStyle = getComputedStyle(document.documentElement);
68
+ const primaryColor = computedStyle.getPropertyValue('--primary').trim() || '#4F46E5';
69
+
70
+ directionsRendererRef.current = new google.maps.DirectionsRenderer({
71
+ map: map,
72
+ suppressMarkers: false,
73
+ polylineOptions: {
74
+ strokeColor: primaryColor,
75
+ strokeWeight: 5,
76
+ strokeOpacity: 0.8,
77
+ },
78
+ });
79
+
80
+ return () => {
81
+ if (directionsRendererRef.current) {
82
+ directionsRendererRef.current.setMap(null);
83
+ directionsRendererRef.current = null;
84
+ }
85
+ isInitializedRef.current = false;
86
+ mapRef.current = null;
87
+ };
88
+ }, [isLoaded]);
89
+
90
+ // Calculate route
91
+ useEffect(() => {
92
+ const map = mapRef.current;
93
+ const renderer = directionsRendererRef.current;
94
+
95
+ if (!map || !renderer || !isLoaded || isCalculatingRef.current) return;
96
+ if (!origin || !destination) return;
97
+
98
+ isCalculatingRef.current = true;
99
+
100
+ const directionsService = new google.maps.DirectionsService();
101
+
102
+ const request: google.maps.DirectionsRequest = {
103
+ origin: origin,
104
+ destination: destination,
105
+ waypoints: waypoints.map(wp => ({
106
+ location: wp,
107
+ stopover: true
108
+ })),
109
+ travelMode: google.maps.TravelMode[travelMode],
110
+ };
111
+
112
+ directionsService.route(request, (result, status) => {
113
+ isCalculatingRef.current = false;
114
+
115
+ if (status === 'OK' && result) {
116
+ renderer.setDirections(result);
117
+
118
+ // Calculate total distance and duration
119
+ const route = result.routes[0];
120
+ if (route?.legs?.length > 0 && onRouteCalculated) {
121
+ let totalDistance = 0;
122
+ let totalDuration = 0;
123
+
124
+ route.legs.forEach(leg => {
125
+ if (leg.distance) totalDistance += leg.distance.value;
126
+ if (leg.duration) totalDuration += leg.duration.value;
127
+ });
128
+
129
+ const distanceKm = (totalDistance / 1000).toFixed(1);
130
+ const distanceText = `${distanceKm} km`;
131
+
132
+ const hours = Math.floor(totalDuration / 3600);
133
+ const minutes = Math.floor((totalDuration % 3600) / 60);
134
+ const durationText = hours > 0 ? `${hours}h ${minutes}min` : `${minutes} min`;
135
+
136
+ onRouteCalculated(distanceText, durationText);
137
+ }
138
+ }
139
+ });
140
+ }, [isLoaded, origin.lat, origin.lng, destination.lat, destination.lng, travelMode]);
141
+
142
+ if (loadError) {
143
+ return (
144
+ <div
145
+ ref={ref}
146
+ className={cn(
147
+ "relative rounded-[var(--radius-card)] border border-destructive/50 overflow-hidden bg-destructive/5",
148
+ className
149
+ )}
150
+ style={{ height }}
151
+ {...divProps}
152
+ >
153
+ <div className="absolute inset-0 flex items-center justify-center">
154
+ <div className="text-center space-y-2 p-6">
155
+ <div className="text-destructive">
156
+ <svg className="w-12 h-12 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
157
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
158
+ </svg>
159
+ </div>
160
+ <p className="text-sm text-destructive">Failed to load Google Maps</p>
161
+ <p className="text-xs text-muted-foreground">Check API key in Settings</p>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ );
166
+ }
167
+
168
+ if (!isLoaded) {
169
+ return (
170
+ <div
171
+ ref={ref}
172
+ className={cn(
173
+ "relative rounded-[var(--radius-card)] border border-border overflow-hidden bg-muted animate-pulse",
174
+ className
175
+ )}
176
+ style={{ height }}
177
+ {...divProps}
178
+ />
179
+ );
180
+ }
181
+
182
+ return (
183
+ <div
184
+ ref={ref}
185
+ className={cn(
186
+ "relative rounded-[var(--radius-card)] border border-border overflow-hidden",
187
+ className
188
+ )}
189
+ {...divProps}
190
+ >
191
+ <div
192
+ ref={mapContainerRef}
193
+ style={{ height }}
194
+ className={cn("rounded-[var(--radius-card)] w-full", mapContainerClassName)}
195
+ />
196
+ </div>
197
+ );
198
+ }
199
+ );
200
+ RouteMapContent.displayName = "RouteMapContent";
201
+
202
+ export const RouteMap = React.forwardRef<HTMLDivElement, RouteMapProps>(
203
+ (props, ref) => {
204
+ const effectiveApiKey = props.apiKey || "";
205
+
206
+ const isValidKey = effectiveApiKey &&
207
+ effectiveApiKey !== "YOUR_GOOGLE_MAPS_API_KEY_HERE" &&
208
+ effectiveApiKey.startsWith("AIza");
209
+
210
+ if (!isValidKey) {
211
+ const {
212
+ origin, destination, waypoints, travelMode, height, apiKey,
213
+ mapContainerClassName, disableDefaultUI, zoomControl,
214
+ streetViewControl, mapTypeControl, fullscreenControl,
215
+ onRouteCalculated, ...divProps
216
+ } = props;
217
+
218
+ return (
219
+ <div
220
+ ref={ref}
221
+ className={cn(
222
+ "relative rounded-[var(--radius-card)] border border-border overflow-hidden bg-muted",
223
+ props.className
224
+ )}
225
+ style={{ height: props.height || "450px" }}
226
+ {...divProps}
227
+ >
228
+ <div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-muted/50 to-muted">
229
+ <div className="text-center space-y-3 p-6">
230
+ <div className="w-16 h-16 mx-auto bg-primary/10 rounded-full flex items-center justify-center">
231
+ <svg className="w-8 h-8 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
232
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
233
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
234
+ </svg>
235
+ </div>
236
+ <p className="text-sm text-muted-foreground">Configure Google Maps API Key in Settings</p>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ );
241
+ }
242
+
243
+ return <RouteMapContent ref={ref} {...props} apiKey={effectiveApiKey} />;
244
+ }
245
+ );
246
+ RouteMap.displayName = "RouteMap";
@@ -0,0 +1,58 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
5
+
6
+ import { cn } from "./utils";
7
+
8
+ function ScrollArea({
9
+ className,
10
+ children,
11
+ ...props
12
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
13
+ return (
14
+ <ScrollAreaPrimitive.Root
15
+ data-slot="scroll-area"
16
+ className={cn("relative", className)}
17
+ {...props}
18
+ >
19
+ <ScrollAreaPrimitive.Viewport
20
+ data-slot="scroll-area-viewport"
21
+ className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
22
+ >
23
+ {children}
24
+ </ScrollAreaPrimitive.Viewport>
25
+ <ScrollBar />
26
+ <ScrollAreaPrimitive.Corner />
27
+ </ScrollAreaPrimitive.Root>
28
+ );
29
+ }
30
+
31
+ function ScrollBar({
32
+ className,
33
+ orientation = "vertical",
34
+ ...props
35
+ }: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
36
+ return (
37
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
38
+ data-slot="scroll-area-scrollbar"
39
+ orientation={orientation}
40
+ className={cn(
41
+ "flex touch-none p-px transition-colors select-none",
42
+ orientation === "vertical" &&
43
+ "h-full w-2.5 border-l border-l-transparent",
44
+ orientation === "horizontal" &&
45
+ "h-2.5 flex-col border-t border-t-transparent",
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ <ScrollAreaPrimitive.ScrollAreaThumb
51
+ data-slot="scroll-area-thumb"
52
+ className="bg-border relative flex-1 rounded-full"
53
+ />
54
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
55
+ );
56
+ }
57
+
58
+ export { ScrollArea, ScrollBar };
@@ -0,0 +1,70 @@
1
+ import * as React from "react";
2
+ import { Search as SearchIcon, X } from "lucide-react";
3
+ import { cn } from "./utils";
4
+
5
+ export interface SearchProps extends React.InputHTMLAttributes<HTMLInputElement> {
6
+ onSearch?: (value: string) => void;
7
+ onClear?: () => void;
8
+ containerClassName?: string;
9
+ }
10
+
11
+ const Search = React.forwardRef<HTMLInputElement, SearchProps>(
12
+ ({ className, containerClassName, onSearch, onClear, onChange, ...props }, ref) => {
13
+ const [value, setValue] = React.useState(props.value || "");
14
+
15
+ React.useEffect(() => {
16
+ if (props.value !== undefined) {
17
+ setValue(props.value);
18
+ }
19
+ }, [props.value]);
20
+
21
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
22
+ const newValue = e.target.value;
23
+ setValue(newValue);
24
+ onChange?.(e);
25
+ onSearch?.(newValue);
26
+ };
27
+
28
+ const handleClear = () => {
29
+ setValue("");
30
+ onClear?.();
31
+ onSearch?.("");
32
+ };
33
+
34
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
35
+ if (e.key === "Escape") {
36
+ handleClear();
37
+ }
38
+ };
39
+
40
+ return (
41
+ <div className={cn("relative w-full", containerClassName)}>
42
+ <SearchIcon className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
43
+ <input
44
+ ref={ref}
45
+ type="search"
46
+ value={value}
47
+ onChange={handleChange}
48
+ onKeyDown={handleKeyDown}
49
+ className={cn(
50
+ "flex h-10 w-full rounded-[var(--radius)] border border-input bg-input-background px-10 py-2 text-foreground ring-offset-background transition-colors file:border-0 file:bg-transparent placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
51
+ className
52
+ )}
53
+ {...props}
54
+ />
55
+ {value && (
56
+ <button
57
+ type="button"
58
+ onClick={handleClear}
59
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground focus:outline-none"
60
+ >
61
+ <X className="h-4 w-4" />
62
+ </button>
63
+ )}
64
+ </div>
65
+ );
66
+ }
67
+ );
68
+ Search.displayName = "Search";
69
+
70
+ export { Search };
@@ -0,0 +1,176 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SelectPrimitive from "@radix-ui/react-select"
5
+ import { Check, ChevronDown, ChevronUp } from "lucide-react"
6
+
7
+ import { cn } from "./utils"
8
+
9
+ const Select = SelectPrimitive.Root
10
+
11
+ const SelectGroup = SelectPrimitive.Group
12
+
13
+ const SelectValue = SelectPrimitive.Value
14
+
15
+ const SelectTrigger = React.forwardRef<
16
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
17
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
18
+ size?: "sm" | "default";
19
+ }
20
+ >(({ className, children, size = "default", ...props }, ref) => (
21
+ <SelectPrimitive.Trigger
22
+ ref={ref}
23
+ data-slot="select-trigger"
24
+ data-size={size}
25
+ className={cn(
26
+ "flex w-full items-center justify-between gap-2 px-3 py-2 text-sm bg-background rounded-[var(--radius)] border border-border transition-colors outline-none whitespace-nowrap text-foreground",
27
+ "data-[placeholder]:text-muted-foreground",
28
+ "focus:ring-2 focus:ring-primary focus:border-transparent",
29
+ "disabled:cursor-not-allowed disabled:opacity-50",
30
+ "data-[size=default]:h-9 data-[size=sm]:h-8",
31
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
32
+ "*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2",
33
+ className
34
+ )}
35
+ {...props}
36
+ >
37
+ {children}
38
+ <SelectPrimitive.Icon asChild>
39
+ <ChevronDown className="size-4 opacity-50" />
40
+ </SelectPrimitive.Icon>
41
+ </SelectPrimitive.Trigger>
42
+ ))
43
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
44
+
45
+ const SelectScrollUpButton = React.forwardRef<
46
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
47
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
48
+ >(({ className, ...props }, ref) => (
49
+ <SelectPrimitive.ScrollUpButton
50
+ ref={ref}
51
+ data-slot="select-scroll-up-button"
52
+ className={cn(
53
+ "flex cursor-default items-center justify-center py-1",
54
+ className
55
+ )}
56
+ {...props}
57
+ >
58
+ <ChevronUp className="size-4" />
59
+ </SelectPrimitive.ScrollUpButton>
60
+ ))
61
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
62
+
63
+ const SelectScrollDownButton = React.forwardRef<
64
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
65
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
66
+ >(({ className, ...props }, ref) => (
67
+ <SelectPrimitive.ScrollDownButton
68
+ ref={ref}
69
+ data-slot="select-scroll-down-button"
70
+ className={cn(
71
+ "flex cursor-default items-center justify-center py-1",
72
+ className
73
+ )}
74
+ {...props}
75
+ >
76
+ <ChevronDown className="size-4" />
77
+ </SelectPrimitive.ScrollDownButton>
78
+ ))
79
+ SelectScrollDownButton.displayName =
80
+ SelectPrimitive.ScrollDownButton.displayName
81
+
82
+ const SelectContent = React.forwardRef<
83
+ React.ElementRef<typeof SelectPrimitive.Content>,
84
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
85
+ >(({ className, children, position = "popper", ...props }, ref) => (
86
+ <SelectPrimitive.Portal>
87
+ <SelectPrimitive.Content
88
+ ref={ref}
89
+ data-slot="select-content"
90
+ className={cn(
91
+ "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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--radius)] border border-border shadow-md",
92
+ position === "popper" &&
93
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
94
+ className
95
+ )}
96
+ position={position}
97
+ {...props}
98
+ >
99
+ <SelectScrollUpButton />
100
+ <SelectPrimitive.Viewport
101
+ className={cn(
102
+ "p-1",
103
+ position === "popper" &&
104
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
105
+ )}
106
+ >
107
+ {children}
108
+ </SelectPrimitive.Viewport>
109
+ <SelectScrollDownButton />
110
+ </SelectPrimitive.Content>
111
+ </SelectPrimitive.Portal>
112
+ ))
113
+ SelectContent.displayName = SelectPrimitive.Content.displayName
114
+
115
+ const SelectLabel = React.forwardRef<
116
+ React.ElementRef<typeof SelectPrimitive.Label>,
117
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
118
+ >(({ className, ...props }, ref) => (
119
+ <SelectPrimitive.Label
120
+ ref={ref}
121
+ data-slot="select-label"
122
+ className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
123
+ {...props}
124
+ />
125
+ ))
126
+ SelectLabel.displayName = SelectPrimitive.Label.displayName
127
+
128
+ const SelectItem = React.forwardRef<
129
+ React.ElementRef<typeof SelectPrimitive.Item>,
130
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
131
+ >(({ className, children, ...props }, ref) => (
132
+ <SelectPrimitive.Item
133
+ ref={ref}
134
+ data-slot="select-item"
135
+ className={cn(
136
+ "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
137
+ className
138
+ )}
139
+ {...props}
140
+ >
141
+ <span className="absolute right-2 flex size-3.5 items-center justify-center">
142
+ <SelectPrimitive.ItemIndicator>
143
+ <Check className="size-4" />
144
+ </SelectPrimitive.ItemIndicator>
145
+ </span>
146
+
147
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
148
+ </SelectPrimitive.Item>
149
+ ))
150
+ SelectItem.displayName = SelectPrimitive.Item.displayName
151
+
152
+ const SelectSeparator = React.forwardRef<
153
+ React.ElementRef<typeof SelectPrimitive.Separator>,
154
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
155
+ >(({ className, ...props }, ref) => (
156
+ <SelectPrimitive.Separator
157
+ ref={ref}
158
+ data-slot="select-separator"
159
+ className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
160
+ {...props}
161
+ />
162
+ ))
163
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName
164
+
165
+ export {
166
+ Select,
167
+ SelectGroup,
168
+ SelectValue,
169
+ SelectTrigger,
170
+ SelectContent,
171
+ SelectLabel,
172
+ SelectItem,
173
+ SelectSeparator,
174
+ SelectScrollUpButton,
175
+ SelectScrollDownButton,
176
+ }
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
5
+
6
+ import { cn } from "./utils";
7
+
8
+ function Separator({
9
+ className,
10
+ orientation = "horizontal",
11
+ decorative = true,
12
+ ...props
13
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
14
+ return (
15
+ <SeparatorPrimitive.Root
16
+ data-slot="separator-root"
17
+ decorative={decorative}
18
+ orientation={orientation}
19
+ className={cn(
20
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
21
+ className,
22
+ )}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ export { Separator };