@voila.dev/ui 1.1.9

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 (99) hide show
  1. package/package.json +78 -0
  2. package/src/components/ui/accordion.tsx +79 -0
  3. package/src/components/ui/alert-dialog.tsx +198 -0
  4. package/src/components/ui/alert.tsx +105 -0
  5. package/src/components/ui/aspect-ratio.tsx +30 -0
  6. package/src/components/ui/avatar.tsx +125 -0
  7. package/src/components/ui/badge-variants.ts +166 -0
  8. package/src/components/ui/badge.tsx +45 -0
  9. package/src/components/ui/banner.tsx +84 -0
  10. package/src/components/ui/breadcrumb.tsx +127 -0
  11. package/src/components/ui/button-group.tsx +99 -0
  12. package/src/components/ui/button-variants.ts +108 -0
  13. package/src/components/ui/button.tsx +54 -0
  14. package/src/components/ui/calendar.tsx +266 -0
  15. package/src/components/ui/card.tsx +155 -0
  16. package/src/components/ui/carousel.tsx +319 -0
  17. package/src/components/ui/chat.tsx +896 -0
  18. package/src/components/ui/checkbox-group.tsx +44 -0
  19. package/src/components/ui/checkbox.tsx +34 -0
  20. package/src/components/ui/chip.tsx +60 -0
  21. package/src/components/ui/collapsible.tsx +44 -0
  22. package/src/components/ui/color-picker.tsx +191 -0
  23. package/src/components/ui/combobox.tsx +317 -0
  24. package/src/components/ui/command.tsx +187 -0
  25. package/src/components/ui/confirm-dialog.tsx +125 -0
  26. package/src/components/ui/context-menu.tsx +254 -0
  27. package/src/components/ui/copyable-text.tsx +65 -0
  28. package/src/components/ui/date-picker.tsx +316 -0
  29. package/src/components/ui/date-time-picker.tsx +1071 -0
  30. package/src/components/ui/dialog.tsx +176 -0
  31. package/src/components/ui/direction.tsx +15 -0
  32. package/src/components/ui/drawer.tsx +178 -0
  33. package/src/components/ui/dropdown-menu.tsx +247 -0
  34. package/src/components/ui/empty.tsx +131 -0
  35. package/src/components/ui/field.tsx +257 -0
  36. package/src/components/ui/formatted-input.tsx +219 -0
  37. package/src/components/ui/gallery.tsx +161 -0
  38. package/src/components/ui/hover-card.tsx +77 -0
  39. package/src/components/ui/icon-picker.tsx +193 -0
  40. package/src/components/ui/image-cropper.tsx +787 -0
  41. package/src/components/ui/image-upload-field.tsx +344 -0
  42. package/src/components/ui/input-group.tsx +175 -0
  43. package/src/components/ui/input-otp.tsx +83 -0
  44. package/src/components/ui/input.tsx +21 -0
  45. package/src/components/ui/item.tsx +212 -0
  46. package/src/components/ui/kbd.tsx +62 -0
  47. package/src/components/ui/label.tsx +19 -0
  48. package/src/components/ui/list.tsx +46 -0
  49. package/src/components/ui/menu-variants.ts +71 -0
  50. package/src/components/ui/menubar.tsx +226 -0
  51. package/src/components/ui/money-input.tsx +96 -0
  52. package/src/components/ui/native-date-picker.tsx +120 -0
  53. package/src/components/ui/native-select.tsx +74 -0
  54. package/src/components/ui/navigation-menu.tsx +183 -0
  55. package/src/components/ui/pagination.tsx +150 -0
  56. package/src/components/ui/popover.tsx +104 -0
  57. package/src/components/ui/profile-header.tsx +196 -0
  58. package/src/components/ui/progress.tsx +71 -0
  59. package/src/components/ui/radio-group.tsx +115 -0
  60. package/src/components/ui/rating.tsx +200 -0
  61. package/src/components/ui/resizable.tsx +61 -0
  62. package/src/components/ui/responsive-dialog.tsx +278 -0
  63. package/src/components/ui/responsive-select.tsx +289 -0
  64. package/src/components/ui/responsive-sheet.tsx +225 -0
  65. package/src/components/ui/scroll-area.tsx +51 -0
  66. package/src/components/ui/section.tsx +106 -0
  67. package/src/components/ui/segmented-control.tsx +148 -0
  68. package/src/components/ui/select.tsx +216 -0
  69. package/src/components/ui/separator.tsx +69 -0
  70. package/src/components/ui/sheet.tsx +154 -0
  71. package/src/components/ui/shortcut.tsx +41 -0
  72. package/src/components/ui/sidebar.tsx +739 -0
  73. package/src/components/ui/skeleton.tsx +23 -0
  74. package/src/components/ui/slider.tsx +74 -0
  75. package/src/components/ui/sonner.tsx +64 -0
  76. package/src/components/ui/spinner.tsx +19 -0
  77. package/src/components/ui/stat-card.tsx +138 -0
  78. package/src/components/ui/stepper.tsx +181 -0
  79. package/src/components/ui/sticky-action-bar.tsx +42 -0
  80. package/src/components/ui/switch.tsx +30 -0
  81. package/src/components/ui/table.tsx +132 -0
  82. package/src/components/ui/tabs.tsx +77 -0
  83. package/src/components/ui/textarea.tsx +24 -0
  84. package/src/components/ui/time-picker.tsx +221 -0
  85. package/src/components/ui/toggle-group.tsx +86 -0
  86. package/src/components/ui/toggle.tsx +43 -0
  87. package/src/components/ui/tooltip.tsx +79 -0
  88. package/src/components/ui/translation-input.tsx +126 -0
  89. package/src/components/ui/user-avatar.tsx +74 -0
  90. package/src/css-modules.d.ts +4 -0
  91. package/src/hooks/use-command-palette.ts +28 -0
  92. package/src/hooks/use-mobile.ts +21 -0
  93. package/src/hooks/use-pagination.ts +81 -0
  94. package/src/hooks/use-picker-state.ts +44 -0
  95. package/src/lib/cva.ts +18 -0
  96. package/src/lib/time-math.ts +62 -0
  97. package/src/lib/utils.ts +14 -0
  98. package/src/styles/globals.css +36 -0
  99. package/src/styles.css +10 -0
@@ -0,0 +1,319 @@
1
+ import { CaretLeftIcon, CaretRightIcon } from "@phosphor-icons/react";
2
+ import useEmblaCarousel, {
3
+ type UseEmblaCarouselType,
4
+ } from "embla-carousel-react";
5
+ import * as React from "react";
6
+ import { Button } from "#/components/ui/button.tsx";
7
+ import { cn } from "#/lib/utils.ts";
8
+
9
+ type CarouselApi = UseEmblaCarouselType[1];
10
+ type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
11
+ type CarouselOptions = UseCarouselParameters[0];
12
+ type CarouselPlugin = UseCarouselParameters[1];
13
+
14
+ type CarouselProps = {
15
+ opts?: CarouselOptions;
16
+ plugins?: CarouselPlugin;
17
+ orientation?: "horizontal" | "vertical";
18
+ setApi?: (api: CarouselApi) => void;
19
+ };
20
+
21
+ type CarouselContextProps = {
22
+ carouselRef: ReturnType<typeof useEmblaCarousel>[0];
23
+ api: ReturnType<typeof useEmblaCarousel>[1];
24
+ scrollPrev: () => void;
25
+ scrollNext: () => void;
26
+ scrollTo: (index: number) => void;
27
+ canScrollPrev: boolean;
28
+ canScrollNext: boolean;
29
+ selectedIndex: number;
30
+ scrollSnaps: number[];
31
+ } & CarouselProps;
32
+
33
+ const CarouselContext = React.createContext<CarouselContextProps | null>(null);
34
+
35
+ function useCarousel() {
36
+ const context = React.useContext(CarouselContext);
37
+
38
+ if (!context) {
39
+ throw new Error("useCarousel must be used within a <Carousel />");
40
+ }
41
+
42
+ return context;
43
+ }
44
+
45
+ function prefersReducedMotion() {
46
+ return (
47
+ typeof window !== "undefined" &&
48
+ window.matchMedia("(prefers-reduced-motion: reduce)").matches
49
+ );
50
+ }
51
+
52
+ function Carousel({
53
+ orientation = "horizontal",
54
+ opts,
55
+ setApi,
56
+ plugins,
57
+ className,
58
+ children,
59
+ ...props
60
+ }: React.ComponentProps<"div"> & CarouselProps) {
61
+ const [carouselRef, api] = useEmblaCarousel(
62
+ {
63
+ ...opts,
64
+ axis: orientation === "horizontal" ? "x" : "y",
65
+ },
66
+ plugins,
67
+ );
68
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
69
+ const [canScrollNext, setCanScrollNext] = React.useState(false);
70
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
71
+ const [scrollSnaps, setScrollSnaps] = React.useState<number[]>([]);
72
+
73
+ const onSelect = React.useCallback((api: CarouselApi) => {
74
+ if (!api) return;
75
+ setCanScrollPrev(api.canScrollPrev());
76
+ setCanScrollNext(api.canScrollNext());
77
+ setSelectedIndex(api.selectedScrollSnap());
78
+ }, []);
79
+
80
+ const scrollPrev = React.useCallback(() => {
81
+ api?.scrollPrev(prefersReducedMotion());
82
+ }, [api]);
83
+
84
+ const scrollNext = React.useCallback(() => {
85
+ api?.scrollNext(prefersReducedMotion());
86
+ }, [api]);
87
+
88
+ const scrollTo = React.useCallback(
89
+ (index: number) => {
90
+ api?.scrollTo(index, prefersReducedMotion());
91
+ },
92
+ [api],
93
+ );
94
+
95
+ const handleKeyDown = React.useCallback(
96
+ (event: React.KeyboardEvent<HTMLDivElement>) => {
97
+ const previousKey =
98
+ orientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
99
+ const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown";
100
+
101
+ if (event.key === previousKey) {
102
+ event.preventDefault();
103
+ scrollPrev();
104
+ } else if (event.key === nextKey) {
105
+ event.preventDefault();
106
+ scrollNext();
107
+ }
108
+ },
109
+ [orientation, scrollPrev, scrollNext],
110
+ );
111
+
112
+ React.useEffect(() => {
113
+ if (!api || !setApi) return;
114
+ setApi(api);
115
+ }, [api, setApi]);
116
+
117
+ React.useEffect(() => {
118
+ if (!api) return;
119
+ const onReInit = (api: CarouselApi) => {
120
+ if (!api) return;
121
+ setScrollSnaps(api.scrollSnapList());
122
+ onSelect(api);
123
+ };
124
+ setScrollSnaps(api.scrollSnapList());
125
+ onSelect(api);
126
+ api.on("reInit", onReInit);
127
+ api.on("select", onSelect);
128
+
129
+ return () => {
130
+ api.off("reInit", onReInit);
131
+ api.off("select", onSelect);
132
+ };
133
+ }, [api, onSelect]);
134
+
135
+ return (
136
+ <CarouselContext.Provider
137
+ value={{
138
+ carouselRef,
139
+ api,
140
+ opts,
141
+ orientation,
142
+ scrollPrev,
143
+ scrollNext,
144
+ scrollTo,
145
+ canScrollPrev,
146
+ canScrollNext,
147
+ selectedIndex,
148
+ scrollSnaps,
149
+ }}
150
+ >
151
+ <div
152
+ onKeyDownCapture={handleKeyDown}
153
+ className={cn("relative", className)}
154
+ role="region"
155
+ aria-roledescription="carousel"
156
+ data-slot="carousel"
157
+ data-orientation={orientation}
158
+ {...props}
159
+ >
160
+ {children}
161
+ </div>
162
+ </CarouselContext.Provider>
163
+ );
164
+ }
165
+
166
+ function CarouselContent({
167
+ className,
168
+ containerClassName,
169
+ ...props
170
+ }: React.ComponentProps<"div"> & { containerClassName?: string }) {
171
+ const { carouselRef, orientation } = useCarousel();
172
+
173
+ return (
174
+ <div
175
+ ref={carouselRef}
176
+ className={cn("overflow-hidden", containerClassName)}
177
+ data-slot="carousel-content"
178
+ data-orientation={orientation}
179
+ >
180
+ <div
181
+ className={cn(
182
+ "flex data-[orientation=horizontal]:-ml-4 data-[orientation=vertical]:-mt-4 data-[orientation=vertical]:flex-col",
183
+ className,
184
+ )}
185
+ data-slot="carousel-track"
186
+ data-orientation={orientation}
187
+ {...props}
188
+ />
189
+ </div>
190
+ );
191
+ }
192
+
193
+ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
194
+ const { orientation } = useCarousel();
195
+
196
+ return (
197
+ <div
198
+ role="group"
199
+ aria-roledescription="slide"
200
+ data-slot="carousel-item"
201
+ data-orientation={orientation}
202
+ className={cn(
203
+ "min-w-0 shrink-0 grow-0 basis-full data-[orientation=horizontal]:pl-4 data-[orientation=vertical]:pt-4",
204
+ className,
205
+ )}
206
+ {...props}
207
+ />
208
+ );
209
+ }
210
+
211
+ function CarouselPrevious({
212
+ className,
213
+ variant = "outline",
214
+ size = "icon-sm",
215
+ inset = false,
216
+ ...props
217
+ }: React.ComponentProps<typeof Button> & { inset?: boolean }) {
218
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel();
219
+
220
+ return (
221
+ <Button
222
+ data-slot="carousel-previous"
223
+ data-orientation={orientation}
224
+ variant={variant}
225
+ size={size}
226
+ className={cn(
227
+ "absolute touch-manipulation rounded-full dark:border-foreground/25 dark:bg-background",
228
+ "data-[orientation=horizontal]:top-1/2 data-[orientation=horizontal]:-translate-y-1/2",
229
+ "data-[orientation=vertical]:left-1/2 data-[orientation=vertical]:-translate-x-1/2 data-[orientation=vertical]:rotate-90",
230
+ inset
231
+ ? "data-[orientation=horizontal]:left-2 data-[orientation=vertical]:top-2"
232
+ : "data-[orientation=horizontal]:-left-12 data-[orientation=vertical]:-top-12",
233
+ className,
234
+ )}
235
+ disabled={!canScrollPrev}
236
+ onClick={scrollPrev}
237
+ {...props}
238
+ >
239
+ <CaretLeftIcon />
240
+ <span className="sr-only">Previous slide</span>
241
+ </Button>
242
+ );
243
+ }
244
+
245
+ function CarouselNext({
246
+ className,
247
+ variant = "outline",
248
+ size = "icon-sm",
249
+ inset = false,
250
+ ...props
251
+ }: React.ComponentProps<typeof Button> & { inset?: boolean }) {
252
+ const { orientation, scrollNext, canScrollNext } = useCarousel();
253
+
254
+ return (
255
+ <Button
256
+ data-slot="carousel-next"
257
+ data-orientation={orientation}
258
+ variant={variant}
259
+ size={size}
260
+ className={cn(
261
+ "absolute touch-manipulation rounded-full dark:border-foreground/25 dark:bg-background",
262
+ "data-[orientation=horizontal]:top-1/2 data-[orientation=horizontal]:-translate-y-1/2",
263
+ "data-[orientation=vertical]:left-1/2 data-[orientation=vertical]:-translate-x-1/2 data-[orientation=vertical]:rotate-90",
264
+ inset
265
+ ? "data-[orientation=horizontal]:right-2 data-[orientation=vertical]:bottom-2"
266
+ : "data-[orientation=horizontal]:-right-12 data-[orientation=vertical]:-bottom-12",
267
+ className,
268
+ )}
269
+ disabled={!canScrollNext}
270
+ onClick={scrollNext}
271
+ {...props}
272
+ >
273
+ <CaretRightIcon />
274
+ <span className="sr-only">Next slide</span>
275
+ </Button>
276
+ );
277
+ }
278
+
279
+ function CarouselDots({ className, ...props }: React.ComponentProps<"div">) {
280
+ const { orientation, scrollSnaps, selectedIndex, scrollTo } = useCarousel();
281
+
282
+ return (
283
+ <div
284
+ data-slot="carousel-dots"
285
+ data-orientation={orientation}
286
+ className={cn(
287
+ "flex items-center justify-center gap-2 pt-4 data-[orientation=vertical]:flex-col",
288
+ className,
289
+ )}
290
+ {...props}
291
+ >
292
+ {scrollSnaps.map((_snap, index) => (
293
+ <button
294
+ key={index}
295
+ type="button"
296
+ data-slot="carousel-dot"
297
+ data-state={index === selectedIndex ? "active" : "inactive"}
298
+ aria-label={`Go to slide ${index + 1}`}
299
+ aria-current={index === selectedIndex ? "true" : undefined}
300
+ className="size-2.5 touch-manipulation rounded-full bg-border transition-colors hover:bg-muted-foreground/50 data-[state=active]:bg-primary"
301
+ onClick={() => scrollTo(index)}
302
+ />
303
+ ))}
304
+ </div>
305
+ );
306
+ }
307
+
308
+ export {
309
+ Carousel,
310
+ type CarouselApi,
311
+ CarouselContent,
312
+ CarouselDots,
313
+ CarouselItem,
314
+ CarouselNext,
315
+ type CarouselOptions,
316
+ type CarouselPlugin,
317
+ CarouselPrevious,
318
+ useCarousel,
319
+ };