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,92 @@
1
+ import React from 'react';
2
+ import { Map, MapProps } from './map';
3
+
4
+ /**
5
+ * SimpleMap - Wrapper simplificado para casos de uso comuns
6
+ *
7
+ * Use este componente quando precisar de mapas básicos sem configuração avançada.
8
+ * Para casos mais complexos, use o componente Map diretamente.
9
+ */
10
+
11
+ interface SimpleMapProps extends Omit<MapProps, 'markers' | 'circle' | 'polygon'> {
12
+ /** Endereço ou nome do local */
13
+ address?: string;
14
+ /** Título do marcador */
15
+ markerTitle?: string;
16
+ /** Informação adicional no InfoWindow */
17
+ markerInfo?: string;
18
+ /** Mostrar um marcador no centro */
19
+ showMarker?: boolean;
20
+ }
21
+
22
+ export const SimpleMap = React.forwardRef<HTMLDivElement, SimpleMapProps>(
23
+ (
24
+ {
25
+ center,
26
+ address,
27
+ markerTitle,
28
+ markerInfo,
29
+ showMarker = true,
30
+ zoom = 15,
31
+ height = "350px",
32
+ ...props
33
+ },
34
+ ref
35
+ ) => {
36
+ // Criar marcador automaticamente se showMarker estiver true
37
+ const markers = showMarker && center
38
+ ? [
39
+ {
40
+ position: center,
41
+ title: markerTitle || address || "Local",
42
+ info: markerInfo || address,
43
+ },
44
+ ]
45
+ : [];
46
+
47
+ return (
48
+ <Map
49
+ ref={ref}
50
+ center={center}
51
+ zoom={zoom}
52
+ height={height}
53
+ markers={markers}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+ );
59
+
60
+ SimpleMap.displayName = "SimpleMap";
61
+
62
+ /**
63
+ * EXEMPLOS DE USO:
64
+ *
65
+ * 1. Mapa Simples com Marcador:
66
+ * ```tsx
67
+ * <SimpleMap
68
+ * center={{ lat: -23.5505, lng: -46.6333 }}
69
+ * markerTitle="Escritório Central"
70
+ * markerInfo="Avenida Paulista, 1000"
71
+ * />
72
+ * ```
73
+ *
74
+ * 2. Mapa Sem Marcador (apenas visualização):
75
+ * ```tsx
76
+ * <SimpleMap
77
+ * center={{ lat: -23.5505, lng: -46.6333 }}
78
+ * showMarker={false}
79
+ * zoom={10}
80
+ * />
81
+ * ```
82
+ *
83
+ * 3. Mapa de Localização de Loja:
84
+ * ```tsx
85
+ * <SimpleMap
86
+ * center={{ lat: -23.5505, lng: -46.6333 }}
87
+ * address="Rua Augusta, 2000 - São Paulo"
88
+ * zoom={16}
89
+ * height="400px"
90
+ * />
91
+ * ```
92
+ */
@@ -0,0 +1,13 @@
1
+ import { cn } from "./utils";
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("bg-accent animate-pulse rounded-md", className)}
8
+ {...props}
9
+ />
10
+ );
11
+ }
12
+
13
+ export { Skeleton };
@@ -0,0 +1,58 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SliderPrimitive from "@radix-ui/react-slider"
5
+
6
+ import { cn } from "./utils"
7
+
8
+ const Slider = React.forwardRef<
9
+ React.ElementRef<typeof SliderPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
11
+ >(({ className, ...props }, ref) => {
12
+ const { defaultValue, value, min = 0, max = 100 } = props
13
+ const _values = React.useMemo(
14
+ () =>
15
+ Array.isArray(value)
16
+ ? value
17
+ : Array.isArray(defaultValue)
18
+ ? defaultValue
19
+ : [min, max],
20
+ [value, defaultValue, min, max]
21
+ )
22
+
23
+ return (
24
+ <SliderPrimitive.Root
25
+ ref={ref}
26
+ data-slot="slider"
27
+ className={cn(
28
+ "relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
29
+ className
30
+ )}
31
+ {...props}
32
+ >
33
+ <SliderPrimitive.Track
34
+ data-slot="slider-track"
35
+ className={cn(
36
+ "bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-4 data-[orientation=horizontal]:w-full data-[orientation=horizontal]:mx-2 data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5 data-[orientation=vertical]:my-2"
37
+ )}
38
+ >
39
+ <SliderPrimitive.Range
40
+ data-slot="slider-range"
41
+ className={cn(
42
+ "bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
43
+ )}
44
+ />
45
+ </SliderPrimitive.Track>
46
+ {Array.from({ length: _values.length }, (_, index) => (
47
+ <SliderPrimitive.Thumb
48
+ data-slot="slider-thumb"
49
+ key={index}
50
+ className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
51
+ />
52
+ ))}
53
+ </SliderPrimitive.Root>
54
+ )
55
+ })
56
+ Slider.displayName = SliderPrimitive.Root.displayName
57
+
58
+ export { Slider }
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Toaster as Sonner, ToasterProps } from "sonner";
5
+
6
+ /**
7
+ * Toaster component for displaying toast notifications.
8
+ *
9
+ * @example
10
+ * // In your root layout/app:
11
+ * import { Toaster } from 'xertica-ui';
12
+ *
13
+ * function App() {
14
+ * return (
15
+ * <>
16
+ * <YourApp />
17
+ * <Toaster />
18
+ * </>
19
+ * );
20
+ * }
21
+ *
22
+ * // To show a toast:
23
+ * import { toast } from 'sonner';
24
+ * toast.success('Success message');
25
+ */
26
+ const Toaster = ({ theme, ...props }: ToasterProps) => {
27
+ // If no theme is provided, detect from document
28
+ const [resolvedTheme, setResolvedTheme] = React.useState<ToasterProps["theme"]>(theme);
29
+
30
+ React.useEffect(() => {
31
+ if (theme) {
32
+ setResolvedTheme(theme);
33
+ return;
34
+ }
35
+
36
+ // Auto-detect theme from document
37
+ const detectTheme = () => {
38
+ const isDark = document.documentElement.classList.contains('dark') ||
39
+ document.documentElement.getAttribute('data-theme') === 'dark';
40
+ setResolvedTheme(isDark ? 'dark' : 'light');
41
+ };
42
+
43
+ detectTheme();
44
+
45
+ // Watch for theme changes
46
+ const observer = new MutationObserver(detectTheme);
47
+ observer.observe(document.documentElement, {
48
+ attributes: true,
49
+ attributeFilter: ['class', 'data-theme']
50
+ });
51
+
52
+ return () => observer.disconnect();
53
+ }, [theme]);
54
+
55
+ return (
56
+ <Sonner
57
+ theme={resolvedTheme}
58
+ className="toaster group"
59
+ expand={true}
60
+ toastOptions={{
61
+ classNames: {
62
+ toast: "group toast group-[.toaster]:bg-card group-[.toaster]:text-card-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
63
+ description: "group-[.toast]:text-muted-foreground",
64
+ actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
65
+ cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
66
+ success: "group-[.toaster]:bg-[color:var(--chart-2)]/10 group-[.toaster]:border-l-4 group-[.toaster]:border-l-[color:var(--chart-2)] group-[.toaster]:text-foreground [&>svg]:text-[color:var(--chart-2)]",
67
+ error: "group-[.toaster]:bg-destructive/10 group-[.toaster]:border-l-4 group-[.toaster]:border-l-destructive group-[.toaster]:text-foreground [&>svg]:text-destructive",
68
+ warning: "group-[.toaster]:bg-[color:var(--chart-3)]/10 group-[.toaster]:border-l-4 group-[.toaster]:border-l-[color:var(--chart-3)] group-[.toaster]:text-foreground [&>svg]:text-[color:var(--chart-3)]",
69
+ info: "group-[.toaster]:bg-[color:var(--chart-4)]/10 group-[.toaster]:border-l-4 group-[.toaster]:border-l-[color:var(--chart-4)] group-[.toaster]:text-foreground [&>svg]:text-[color:var(--chart-4)]",
70
+ },
71
+ }}
72
+ {...props}
73
+ />
74
+ );
75
+ };
76
+
77
+ export { Toaster };
@@ -0,0 +1,84 @@
1
+ import * as React from "react";
2
+ import { TrendingUp, TrendingDown, Minus } from "lucide-react";
3
+ import { cn } from "./utils";
4
+ import { Card, CardContent } from "./card";
5
+
6
+ interface StatsCardProps extends React.HTMLAttributes<HTMLDivElement> {
7
+ title: string;
8
+ value: string | number;
9
+ description?: string;
10
+ trend?: {
11
+ value: number;
12
+ label?: string;
13
+ };
14
+ icon?: React.ReactNode;
15
+ }
16
+
17
+ const StatsCard = React.forwardRef<HTMLDivElement, StatsCardProps>(
18
+ ({ className, title, value, description, trend, icon, ...props }, ref) => {
19
+ const getTrendIcon = () => {
20
+ if (!trend) return null;
21
+
22
+ if (trend.value > 0) {
23
+ return <TrendingUp className="h-4 w-4 text-[rgb(5,150,105)]" />;
24
+ } else if (trend.value < 0) {
25
+ return <TrendingDown className="h-4 w-4 text-destructive" />;
26
+ } else {
27
+ return <Minus className="h-4 w-4 text-muted-foreground" />;
28
+ }
29
+ };
30
+
31
+ const getTrendColor = () => {
32
+ if (!trend) return "";
33
+
34
+ if (trend.value > 0) {
35
+ return "text-[rgb(5,150,105)]";
36
+ } else if (trend.value < 0) {
37
+ return "text-destructive";
38
+ } else {
39
+ return "text-muted-foreground";
40
+ }
41
+ };
42
+
43
+ return (
44
+ <Card ref={ref} className={cn("", className)} {...props}>
45
+ <CardContent className="p-4 sm:p-6">
46
+ <div className="flex items-start justify-between gap-3">
47
+ <div className="flex-1 min-w-0">
48
+ <p className="text-muted-foreground mb-1 text-sm">
49
+ {title}
50
+ </p>
51
+ <div className="flex flex-wrap items-baseline gap-2">
52
+ <h3 className="text-foreground text-xl sm:text-2xl">
53
+ {value}
54
+ </h3>
55
+ {trend && (
56
+ <div className={cn("flex items-center gap-1 text-sm", getTrendColor())}>
57
+ {getTrendIcon()}
58
+ <span>
59
+ {Math.abs(trend.value)}%
60
+ </span>
61
+ </div>
62
+ )}
63
+ </div>
64
+ {(description || trend?.label) && (
65
+ <p className="text-muted-foreground mt-1 text-xs sm:text-sm">
66
+ {trend?.label || description}
67
+ </p>
68
+ )}
69
+ </div>
70
+ {icon && (
71
+ <div className="rounded-[var(--radius)] bg-muted p-2 sm:p-3 text-muted-foreground flex-shrink-0">
72
+ {icon}
73
+ </div>
74
+ )}
75
+ </div>
76
+ </CardContent>
77
+ </Card>
78
+ );
79
+ }
80
+ );
81
+ StatsCard.displayName = "StatsCard";
82
+
83
+ export { StatsCard };
84
+ export type { StatsCardProps };
@@ -0,0 +1,126 @@
1
+ import * as React from "react";
2
+ import { Check } from "lucide-react";
3
+ import { cn } from "./utils";
4
+
5
+ interface StepperContextValue {
6
+ currentStep: number;
7
+ totalSteps: number;
8
+ }
9
+
10
+ const StepperContext = React.createContext<StepperContextValue | undefined>(
11
+ undefined
12
+ );
13
+
14
+ const useStepper = () => {
15
+ const context = React.useContext(StepperContext);
16
+ if (!context) {
17
+ throw new Error("useStepper must be used within a Stepper");
18
+ }
19
+ return context;
20
+ };
21
+
22
+ interface StepperProps extends React.HTMLAttributes<HTMLDivElement> {
23
+ currentStep: number;
24
+ }
25
+
26
+ const Stepper = React.forwardRef<HTMLDivElement, StepperProps>(
27
+ ({ currentStep, className, children, ...props }, ref) => {
28
+ const totalSteps = React.Children.count(children);
29
+
30
+ return (
31
+ <StepperContext.Provider value={{ currentStep, totalSteps }}>
32
+ <div
33
+ ref={ref}
34
+ className={cn("w-full", className)}
35
+ {...props}
36
+ >
37
+ <div className="flex items-center justify-between">
38
+ {children}
39
+ </div>
40
+ </div>
41
+ </StepperContext.Provider>
42
+ );
43
+ }
44
+ );
45
+ Stepper.displayName = "Stepper";
46
+
47
+ interface StepProps extends React.HTMLAttributes<HTMLDivElement> {
48
+ step: number;
49
+ label: string;
50
+ description?: string;
51
+ }
52
+
53
+ const Step = React.forwardRef<HTMLDivElement, StepProps>(
54
+ ({ step, label, description, className, ...props }, ref) => {
55
+ const { currentStep, totalSteps } = useStepper();
56
+ const isActive = step === currentStep;
57
+ const isCompleted = step < currentStep;
58
+ const isFirst = step === 1;
59
+ const isLast = step === totalSteps;
60
+
61
+ return (
62
+ <div
63
+ ref={ref}
64
+ className={cn("flex flex-1 flex-col items-center", className)}
65
+ {...props}
66
+ >
67
+ <div className="flex w-full items-center">
68
+ {step > 1 && (
69
+ <div
70
+ className={cn(
71
+ "h-0.5 flex-1 transition-colors",
72
+ step <= currentStep ? "bg-primary" : "bg-muted"
73
+ )}
74
+ />
75
+ )}
76
+ <div
77
+ className={cn(
78
+ "relative flex h-10 w-10 items-center justify-center rounded-full border-2 transition-colors",
79
+ isActive && "border-primary bg-primary text-primary-foreground",
80
+ isCompleted && "border-primary bg-primary text-primary-foreground",
81
+ !isActive && !isCompleted && "border-muted bg-background text-muted-foreground"
82
+ )}
83
+ >
84
+ {isCompleted ? (
85
+ <Check className="h-5 w-5" />
86
+ ) : (
87
+ <span>{step}</span>
88
+ )}
89
+ </div>
90
+ {step < totalSteps && (
91
+ <div
92
+ className={cn(
93
+ "h-0.5 flex-1 transition-colors",
94
+ step < currentStep ? "bg-primary" : "bg-muted"
95
+ )}
96
+ />
97
+ )}
98
+ </div>
99
+ <div className={cn(
100
+ "mt-2",
101
+ isFirst && "text-left self-start",
102
+ isLast && "text-right self-end",
103
+ !isFirst && !isLast && "text-center"
104
+ )}>
105
+ <div
106
+ className={cn(
107
+ "transition-colors",
108
+ isActive && "text-foreground",
109
+ !isActive && "text-muted-foreground"
110
+ )}
111
+ >
112
+ {label}
113
+ </div>
114
+ {description && (
115
+ <div className="mt-1 text-muted-foreground">
116
+ {description}
117
+ </div>
118
+ )}
119
+ </div>
120
+ </div>
121
+ );
122
+ }
123
+ );
124
+ Step.displayName = "Step";
125
+
126
+ export { Stepper, Step, useStepper };
@@ -0,0 +1,34 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as SwitchPrimitive from "@radix-ui/react-switch";
5
+
6
+ import { cn } from "./utils";
7
+
8
+ const Switch = React.forwardRef<
9
+ React.ElementRef<typeof SwitchPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>
11
+ >(({ className, ...props }, ref) => (
12
+ <SwitchPrimitive.Root
13
+ className={cn(
14
+ "peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50",
15
+ "data-[state=checked]:bg-primary",
16
+ "data-[state=unchecked]:bg-muted",
17
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
18
+ className
19
+ )}
20
+ {...props}
21
+ ref={ref}
22
+ >
23
+ <SwitchPrimitive.Thumb
24
+ className={cn(
25
+ "pointer-events-none block size-4 rounded-full ring-0 transition-transform shadow-sm",
26
+ "bg-background",
27
+ "data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
28
+ )}
29
+ />
30
+ </SwitchPrimitive.Root>
31
+ ));
32
+ Switch.displayName = SwitchPrimitive.Root.displayName;
33
+
34
+ export { Switch };
@@ -0,0 +1,116 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { cn } from "./utils";
6
+
7
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
8
+ return (
9
+ <div
10
+ data-slot="table-container"
11
+ className="relative w-full overflow-x-auto"
12
+ >
13
+ <table
14
+ data-slot="table"
15
+ className={cn("w-full caption-bottom text-sm", className)}
16
+ {...props}
17
+ />
18
+ </div>
19
+ );
20
+ }
21
+
22
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
23
+ return (
24
+ <thead
25
+ data-slot="table-header"
26
+ className={cn("[&_tr]:border-b", className)}
27
+ {...props}
28
+ />
29
+ );
30
+ }
31
+
32
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
33
+ return (
34
+ <tbody
35
+ data-slot="table-body"
36
+ className={cn("[&_tr:last-child]:border-0", className)}
37
+ {...props}
38
+ />
39
+ );
40
+ }
41
+
42
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
43
+ return (
44
+ <tfoot
45
+ data-slot="table-footer"
46
+ className={cn(
47
+ "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
48
+ className,
49
+ )}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
56
+ return (
57
+ <tr
58
+ data-slot="table-row"
59
+ className={cn(
60
+ "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
61
+ className,
62
+ )}
63
+ {...props}
64
+ />
65
+ );
66
+ }
67
+
68
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
69
+ return (
70
+ <th
71
+ data-slot="table-head"
72
+ className={cn(
73
+ "text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
74
+ className,
75
+ )}
76
+ {...props}
77
+ />
78
+ );
79
+ }
80
+
81
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
82
+ return (
83
+ <td
84
+ data-slot="table-cell"
85
+ className={cn(
86
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
87
+ className,
88
+ )}
89
+ {...props}
90
+ />
91
+ );
92
+ }
93
+
94
+ function TableCaption({
95
+ className,
96
+ ...props
97
+ }: React.ComponentProps<"caption">) {
98
+ return (
99
+ <caption
100
+ data-slot="table-caption"
101
+ className={cn("text-muted-foreground mt-4 text-sm", className)}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ export {
108
+ Table,
109
+ TableHeader,
110
+ TableBody,
111
+ TableFooter,
112
+ TableHead,
113
+ TableRow,
114
+ TableCell,
115
+ TableCaption,
116
+ };
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
5
+
6
+ import { cn } from "./utils";
7
+
8
+ function Tabs({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof TabsPrimitive.Root>) {
12
+ return (
13
+ <TabsPrimitive.Root
14
+ data-slot="tabs"
15
+ className={cn("flex flex-col gap-2", className)}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ function TabsList({
22
+ className,
23
+ ...props
24
+ }: React.ComponentProps<typeof TabsPrimitive.List>) {
25
+ return (
26
+ <TabsPrimitive.List
27
+ data-slot="tabs-list"
28
+ className={cn(
29
+ "relative inline-flex h-12 w-fit items-end justify-start border-b border-border bg-transparent",
30
+ className,
31
+ )}
32
+ {...props}
33
+ />
34
+ );
35
+ }
36
+
37
+ function TabsTrigger({
38
+ className,
39
+ ...props
40
+ }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
41
+ return (
42
+ <TabsPrimitive.Trigger
43
+ data-slot="tabs-trigger"
44
+ className={cn(
45
+ "relative inline-flex h-12 items-center justify-center gap-1.5 bg-transparent px-4 py-3 text-sm font-medium whitespace-nowrap transition-all duration-200 ease-in-out border-b-2 border-transparent text-muted-foreground hover:text-foreground hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-primary data-[state=active]:border-primary data-[state=active]:bg-transparent [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
46
+ className,
47
+ )}
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ function TabsContent({
54
+ className,
55
+ ...props
56
+ }: React.ComponentProps<typeof TabsPrimitive.Content>) {
57
+ return (
58
+ <TabsPrimitive.Content
59
+ data-slot="tabs-content"
60
+ className={cn("flex-1 outline-none", className)}
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+
66
+ export { Tabs, TabsList, TabsTrigger, TabsContent };
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "./utils";
4
+
5
+ const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<"textarea">>(
6
+ ({ className, ...props }, ref) => {
7
+ return (
8
+ <textarea
9
+ data-slot="textarea"
10
+ className={cn(
11
+ "flex min-h-16 w-full px-3 py-2 text-base bg-background rounded-[var(--radius)] border border-border transition-colors outline-none resize-none text-foreground",
12
+ "placeholder:text-muted-foreground",
13
+ "focus:ring-2 focus:ring-primary focus:border-transparent",
14
+ "disabled:cursor-not-allowed disabled:opacity-50",
15
+ "md:text-sm",
16
+ className,
17
+ )}
18
+ ref={ref}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+ );
24
+ Textarea.displayName = "Textarea";
25
+
26
+ export { Textarea };