@hobenakicoffee/libraries 1.11.0 → 1.13.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 (76) hide show
  1. package/README.md +25 -4
  2. package/package.json +84 -9
  3. package/src/App.tsx +28 -0
  4. package/src/components/turnstile-captcha.tsx +47 -0
  5. package/src/components/ui/alert-dialog.tsx +196 -0
  6. package/src/components/ui/alert.tsx +76 -0
  7. package/src/components/ui/avatar.tsx +110 -0
  8. package/src/components/ui/badge.tsx +49 -0
  9. package/src/components/ui/breadcrumb.tsx +122 -0
  10. package/src/components/ui/button-group.tsx +82 -0
  11. package/src/components/ui/button.tsx +77 -0
  12. package/src/components/ui/calendar.tsx +235 -0
  13. package/src/components/ui/card.tsx +100 -0
  14. package/src/components/ui/chart.tsx +364 -0
  15. package/src/components/ui/checkbox.tsx +30 -0
  16. package/src/components/ui/dialog.tsx +162 -0
  17. package/src/components/ui/drawer.tsx +126 -0
  18. package/src/components/ui/dropdown-menu.tsx +267 -0
  19. package/src/components/ui/empty-minimal.tsx +20 -0
  20. package/src/components/ui/empty.tsx +101 -0
  21. package/src/components/ui/field.tsx +235 -0
  22. package/src/components/ui/input-group.tsx +170 -0
  23. package/src/components/ui/input-otp.tsx +84 -0
  24. package/src/components/ui/input.tsx +37 -0
  25. package/src/components/ui/item.tsx +196 -0
  26. package/src/components/ui/label.tsx +19 -0
  27. package/src/components/ui/popover.tsx +87 -0
  28. package/src/components/ui/radio-group.tsx +47 -0
  29. package/src/components/ui/select.tsx +205 -0
  30. package/src/components/ui/separator.tsx +26 -0
  31. package/src/components/ui/sheet.tsx +141 -0
  32. package/src/components/ui/sidebar.tsx +699 -0
  33. package/src/components/ui/skeleton.tsx +13 -0
  34. package/src/components/ui/sonner.tsx +74 -0
  35. package/src/components/ui/spinner.tsx +18 -0
  36. package/src/components/ui/table.tsx +114 -0
  37. package/src/components/ui/tabs.tsx +88 -0
  38. package/src/components/ui/textarea.tsx +35 -0
  39. package/src/components/ui/toggle-group.tsx +91 -0
  40. package/src/components/ui/toggle.tsx +44 -0
  41. package/src/components/ui/tooltip.tsx +59 -0
  42. package/src/constants/common.test.ts +1 -1
  43. package/src/constants/legal.test.ts +1 -1
  44. package/src/constants/payment.test.ts +9 -9
  45. package/src/constants/platforms.test.ts +1 -1
  46. package/src/constants/services.test.ts +1 -1
  47. package/src/hooks/use-mobile.ts +19 -0
  48. package/src/index.css +135 -0
  49. package/src/lib/utils.ts +6 -0
  50. package/src/main.tsx +16 -0
  51. package/src/moderation/datasets/bn.ts +708 -708
  52. package/src/moderation/normalizer.test.ts +1 -1
  53. package/src/moderation/normalizer.ts +16 -16
  54. package/src/moderation/profanity-service.test.ts +3 -3
  55. package/src/providers/theme-provider.tsx +73 -0
  56. package/src/types/supabase.ts +751 -647
  57. package/src/utils/check-moderation.test.ts +12 -12
  58. package/src/utils/format-number.test.ts +1 -1
  59. package/src/utils/format-plain-text.test.ts +1 -1
  60. package/src/utils/get-social-handle.test.ts +3 -3
  61. package/src/utils/get-social-link.test.ts +9 -9
  62. package/src/utils/get-social-link.ts +5 -3
  63. package/src/utils/get-user-name-initials.test.ts +1 -1
  64. package/src/utils/get-user-name-initials.ts +4 -4
  65. package/src/utils/get-user-page-link.ts +1 -1
  66. package/src/utils/index.ts +5 -5
  67. package/src/utils/open-to-new-window.ts +3 -1
  68. package/src/utils/post-to-facebook.test.ts +1 -1
  69. package/src/utils/post-to-facebook.ts +9 -3
  70. package/src/utils/post-to-instagram.test.ts +1 -1
  71. package/src/utils/post-to-linkedin.test.ts +1 -1
  72. package/src/utils/post-to-linkedin.ts +9 -3
  73. package/src/utils/post-to-x.test.ts +1 -1
  74. package/src/utils/post-to-x.ts +12 -4
  75. package/src/utils/to-human-readable.ts +6 -2
  76. package/src/utils/validate-phone-number.test.ts +1 -1
@@ -0,0 +1,364 @@
1
+ import {
2
+ type ComponentProps,
3
+ type ComponentType,
4
+ type CSSProperties,
5
+ createContext,
6
+ type ReactNode,
7
+ useContext,
8
+ useId,
9
+ useMemo,
10
+ } from "react";
11
+ import {
12
+ Legend,
13
+ type LegendProps,
14
+ ResponsiveContainer,
15
+ Tooltip,
16
+ } from "recharts";
17
+ import { cn } from "@/lib/utils";
18
+
19
+ // Format: { THEME_NAME: CSS_SELECTOR }
20
+ const THEMES = { light: "", dark: ".dark" } as const;
21
+
22
+ export type ChartConfig = {
23
+ [k in string]: {
24
+ label?: ReactNode;
25
+ icon?: ComponentType;
26
+ } & (
27
+ | { color?: string; theme?: never }
28
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
29
+ );
30
+ };
31
+
32
+ type ChartContextProps = {
33
+ config: ChartConfig;
34
+ };
35
+
36
+ const ChartContext = createContext<ChartContextProps | null>(null);
37
+
38
+ function useChart() {
39
+ const context = useContext(ChartContext);
40
+
41
+ if (!context) {
42
+ throw new Error("useChart must be used within a <ChartContainer />");
43
+ }
44
+
45
+ return context;
46
+ }
47
+
48
+ function ChartContainer({
49
+ id,
50
+ className,
51
+ children,
52
+ config,
53
+ ...props
54
+ }: ComponentProps<"div"> & {
55
+ config: ChartConfig;
56
+ children: ComponentProps<typeof ResponsiveContainer>["children"];
57
+ }) {
58
+ const uniqueId = useId();
59
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
60
+
61
+ return (
62
+ <ChartContext.Provider value={{ config }}>
63
+ <div
64
+ className={cn(
65
+ "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden",
66
+ className
67
+ )}
68
+ data-chart={chartId}
69
+ data-slot="chart"
70
+ {...props}
71
+ >
72
+ <ChartStyle config={config} id={chartId} />
73
+ <ResponsiveContainer>{children}</ResponsiveContainer>
74
+ </div>
75
+ </ChartContext.Provider>
76
+ );
77
+ }
78
+
79
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
80
+ const colorConfig = Object.entries(config).filter(
81
+ ([, config]) => config.theme || config.color
82
+ );
83
+
84
+ if (!colorConfig.length) {
85
+ return null;
86
+ }
87
+
88
+ return (
89
+ <style
90
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: <properly sanitized />
91
+ dangerouslySetInnerHTML={{
92
+ __html: Object.entries(THEMES)
93
+ .map(
94
+ ([theme, prefix]) => `
95
+ ${prefix} [data-chart=${id}] {
96
+ ${colorConfig
97
+ .map(([key, itemConfig]) => {
98
+ const color =
99
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
100
+ itemConfig.color;
101
+ return color ? ` --color-${key}: ${color};` : null;
102
+ })
103
+ .join("\n")}
104
+ }
105
+ `
106
+ )
107
+ .join("\n"),
108
+ }}
109
+ />
110
+ );
111
+ };
112
+
113
+ const ChartTooltip = Tooltip;
114
+
115
+ function ChartTooltipContent({
116
+ active,
117
+ payload,
118
+ className,
119
+ indicator = "dot",
120
+ hideLabel = false,
121
+ hideIndicator = false,
122
+ label,
123
+ labelFormatter,
124
+ labelClassName,
125
+ formatter,
126
+ color,
127
+ nameKey,
128
+ labelKey,
129
+ }: ComponentProps<typeof Tooltip> &
130
+ ComponentProps<"div"> & {
131
+ hideLabel?: boolean;
132
+ hideIndicator?: boolean;
133
+ indicator?: "line" | "dot" | "dashed";
134
+ nameKey?: string;
135
+ labelKey?: string;
136
+ }) {
137
+ const { config } = useChart();
138
+
139
+ const tooltipLabel = useMemo(() => {
140
+ if (hideLabel || !payload?.length) {
141
+ return null;
142
+ }
143
+
144
+ const [item] = payload;
145
+ const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
146
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
147
+ const value =
148
+ !labelKey && typeof label === "string"
149
+ ? config[label as keyof typeof config]?.label || label
150
+ : itemConfig?.label;
151
+
152
+ if (labelFormatter) {
153
+ return (
154
+ <div className={cn("font-medium", labelClassName)}>
155
+ {labelFormatter(value, payload)}
156
+ </div>
157
+ );
158
+ }
159
+
160
+ if (!value) {
161
+ return null;
162
+ }
163
+
164
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>;
165
+ }, [
166
+ label,
167
+ labelFormatter,
168
+ payload,
169
+ hideLabel,
170
+ labelClassName,
171
+ config,
172
+ labelKey,
173
+ ]);
174
+
175
+ if (!(active && payload?.length)) {
176
+ return null;
177
+ }
178
+
179
+ const nestLabel = payload.length === 1 && indicator !== "dot";
180
+
181
+ return (
182
+ <div
183
+ className={cn(
184
+ "grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
185
+ className
186
+ )}
187
+ >
188
+ {nestLabel ? null : tooltipLabel}
189
+ <div className="grid gap-1.5">
190
+ {payload
191
+ .filter((item) => item.type !== "none")
192
+ .map((item, index) => {
193
+ const key = `${nameKey || item.name || item.dataKey || "value"}`;
194
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
195
+ const indicatorColor = color || item.payload.fill || item.color;
196
+
197
+ return (
198
+ <div
199
+ className={cn(
200
+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
201
+ indicator === "dot" && "items-center"
202
+ )}
203
+ key={item.dataKey}
204
+ >
205
+ {formatter && item?.value !== undefined && item.name ? (
206
+ formatter(item.value, item.name, item, index, item.payload)
207
+ ) : (
208
+ <>
209
+ {itemConfig?.icon ? (
210
+ <itemConfig.icon />
211
+ ) : (
212
+ !hideIndicator && (
213
+ <div
214
+ className={cn(
215
+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
216
+ {
217
+ "h-2.5 w-2.5": indicator === "dot",
218
+ "w-1": indicator === "line",
219
+ "w-0 border-[1.5px] border-dashed bg-transparent":
220
+ indicator === "dashed",
221
+ "my-0.5": nestLabel && indicator === "dashed",
222
+ }
223
+ )}
224
+ style={
225
+ {
226
+ "--color-bg": indicatorColor,
227
+ "--color-border": indicatorColor,
228
+ } as CSSProperties
229
+ }
230
+ />
231
+ )
232
+ )}
233
+ <div
234
+ className={cn(
235
+ "flex flex-1 justify-between leading-none",
236
+ nestLabel ? "items-end" : "items-center"
237
+ )}
238
+ >
239
+ <div className="grid gap-1.5">
240
+ {nestLabel ? tooltipLabel : null}
241
+ <span className="text-muted-foreground">
242
+ {itemConfig?.label || item.name}
243
+ </span>
244
+ </div>
245
+ {item.value && (
246
+ <span className="font-medium font-mono text-foreground tabular-nums">
247
+ {item.value.toLocaleString()}
248
+ </span>
249
+ )}
250
+ </div>
251
+ </>
252
+ )}
253
+ </div>
254
+ );
255
+ })}
256
+ </div>
257
+ </div>
258
+ );
259
+ }
260
+
261
+ const ChartLegend = Legend;
262
+
263
+ function ChartLegendContent({
264
+ className,
265
+ hideIcon = false,
266
+ payload,
267
+ verticalAlign = "bottom",
268
+ nameKey,
269
+ }: ComponentProps<"div"> &
270
+ Pick<LegendProps, "payload" | "verticalAlign"> & {
271
+ hideIcon?: boolean;
272
+ nameKey?: string;
273
+ }) {
274
+ const { config } = useChart();
275
+
276
+ if (!payload?.length) {
277
+ return null;
278
+ }
279
+
280
+ return (
281
+ <div
282
+ className={cn(
283
+ "flex items-center justify-center gap-4",
284
+ verticalAlign === "top" ? "pb-3" : "pt-3",
285
+ className
286
+ )}
287
+ >
288
+ {payload
289
+ .filter((item) => item.type !== "none")
290
+ .map((item) => {
291
+ const key = `${nameKey || item.dataKey || "value"}`;
292
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
293
+
294
+ return (
295
+ <div
296
+ className={cn(
297
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
298
+ )}
299
+ key={item.value}
300
+ >
301
+ {itemConfig?.icon && !hideIcon ? (
302
+ <itemConfig.icon />
303
+ ) : (
304
+ <div
305
+ className="h-2 w-2 shrink-0 rounded-[2px]"
306
+ style={{
307
+ backgroundColor: item.color,
308
+ }}
309
+ />
310
+ )}
311
+ {itemConfig?.label}
312
+ </div>
313
+ );
314
+ })}
315
+ </div>
316
+ );
317
+ }
318
+
319
+ function getPayloadConfigFromPayload(
320
+ config: ChartConfig,
321
+ payload: unknown,
322
+ key: string
323
+ ) {
324
+ if (typeof payload !== "object" || payload === null) {
325
+ return undefined;
326
+ }
327
+
328
+ const payloadPayload =
329
+ "payload" in payload &&
330
+ typeof payload.payload === "object" &&
331
+ payload.payload !== null
332
+ ? payload.payload
333
+ : undefined;
334
+
335
+ let configLabelKey: string = key;
336
+
337
+ if (
338
+ key in payload &&
339
+ typeof payload[key as keyof typeof payload] === "string"
340
+ ) {
341
+ configLabelKey = payload[key as keyof typeof payload] as string;
342
+ } else if (
343
+ payloadPayload &&
344
+ key in payloadPayload &&
345
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
346
+ ) {
347
+ configLabelKey = payloadPayload[
348
+ key as keyof typeof payloadPayload
349
+ ] as string;
350
+ }
351
+
352
+ return configLabelKey in config
353
+ ? config[configLabelKey]
354
+ : config[key as keyof typeof config];
355
+ }
356
+
357
+ export {
358
+ ChartContainer,
359
+ ChartTooltip,
360
+ ChartTooltipContent,
361
+ ChartLegend,
362
+ ChartLegendContent,
363
+ ChartStyle,
364
+ };
@@ -0,0 +1,30 @@
1
+ import { Tick02Icon } from "@hugeicons/core-free-icons";
2
+ import { HugeiconsIcon } from "@hugeicons/react";
3
+ import { Checkbox as CheckboxPrimitive } from "radix-ui";
4
+ import type * as React from "react";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function Checkbox({
8
+ className,
9
+ ...props
10
+ }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
11
+ return (
12
+ <CheckboxPrimitive.Root
13
+ className={cn(
14
+ "peer relative flex size-5.5 shrink-0 items-center justify-center rounded-md border border-input shadow-xs outline-none transition-shadow after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 group-has-disabled/field:opacity-50 aria-invalid:border-destructive aria-invalid:ring aria-invalid:ring-destructive aria-invalid:aria-checked:border-primary data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:bg-input/30 dark:data-checked:bg-primary dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
15
+ className
16
+ )}
17
+ data-slot="checkbox"
18
+ {...props}
19
+ >
20
+ <CheckboxPrimitive.Indicator
21
+ className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
22
+ data-slot="checkbox-indicator"
23
+ >
24
+ <HugeiconsIcon icon={Tick02Icon} strokeWidth={2} />
25
+ </CheckboxPrimitive.Indicator>
26
+ </CheckboxPrimitive.Root>
27
+ );
28
+ }
29
+
30
+ export { Checkbox };
@@ -0,0 +1,162 @@
1
+ import { Cancel01Icon } from "@hugeicons/core-free-icons";
2
+ import { HugeiconsIcon } from "@hugeicons/react";
3
+ import { Dialog as DialogPrimitive } from "radix-ui";
4
+ import type * as React from "react";
5
+ import { Button } from "@/components/ui/button";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function Dialog({
9
+ ...props
10
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
11
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
12
+ }
13
+
14
+ function DialogTrigger({
15
+ ...props
16
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
17
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
18
+ }
19
+
20
+ function DialogPortal({
21
+ ...props
22
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
23
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
24
+ }
25
+
26
+ function DialogClose({
27
+ ...props
28
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
29
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
30
+ }
31
+
32
+ function DialogOverlay({
33
+ className,
34
+ ...props
35
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
36
+ return (
37
+ <DialogPrimitive.Overlay
38
+ className={cn(
39
+ "data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 isolate z-50 bg-black/70 duration-100 data-closed:animate-out data-open:animate-in supports-backdrop-filter:backdrop-blur-xs",
40
+ className
41
+ )}
42
+ data-slot="dialog-overlay"
43
+ {...props}
44
+ />
45
+ );
46
+ }
47
+
48
+ function DialogContent({
49
+ className,
50
+ children,
51
+ showCloseButton = true,
52
+ ...props
53
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
54
+ showCloseButton?: boolean;
55
+ }) {
56
+ return (
57
+ <DialogPortal>
58
+ <DialogOverlay />
59
+ <DialogPrimitive.Content
60
+ className={cn(
61
+ "data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-xl bg-background p-6 text-sm ring-1 ring-foreground/10 duration-100 data-closed:animate-out data-open:animate-in sm:max-w-md",
62
+ className
63
+ )}
64
+ data-slot="dialog-content"
65
+ {...props}
66
+ >
67
+ {children}
68
+ {showCloseButton && (
69
+ <DialogPrimitive.Close asChild data-slot="dialog-close">
70
+ <Button
71
+ className="absolute top-4 right-4"
72
+ size="icon-sm"
73
+ variant="ghost"
74
+ >
75
+ <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} />
76
+ <span className="sr-only">Close</span>
77
+ </Button>
78
+ </DialogPrimitive.Close>
79
+ )}
80
+ </DialogPrimitive.Content>
81
+ </DialogPortal>
82
+ );
83
+ }
84
+
85
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
86
+ return (
87
+ <div
88
+ className={cn("flex flex-col gap-2", className)}
89
+ data-slot="dialog-header"
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ function DialogFooter({
96
+ className,
97
+ showCloseButton = false,
98
+ children,
99
+ ...props
100
+ }: React.ComponentProps<"div"> & {
101
+ showCloseButton?: boolean;
102
+ }) {
103
+ return (
104
+ <div
105
+ className={cn(
106
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-between",
107
+ className
108
+ )}
109
+ data-slot="dialog-footer"
110
+ {...props}
111
+ >
112
+ {children}
113
+ {showCloseButton && (
114
+ <DialogPrimitive.Close asChild>
115
+ <Button variant="outline">Close</Button>
116
+ </DialogPrimitive.Close>
117
+ )}
118
+ </div>
119
+ );
120
+ }
121
+
122
+ function DialogTitle({
123
+ className,
124
+ ...props
125
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
126
+ return (
127
+ <DialogPrimitive.Title
128
+ className={cn("font-medium text-xl leading-none", className)}
129
+ data-slot="dialog-title"
130
+ {...props}
131
+ />
132
+ );
133
+ }
134
+
135
+ function DialogDescription({
136
+ className,
137
+ ...props
138
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
139
+ return (
140
+ <DialogPrimitive.Description
141
+ className={cn(
142
+ "text-muted-foreground text-sm *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
143
+ className
144
+ )}
145
+ data-slot="dialog-description"
146
+ {...props}
147
+ />
148
+ );
149
+ }
150
+
151
+ export {
152
+ Dialog,
153
+ DialogClose,
154
+ DialogContent,
155
+ DialogDescription,
156
+ DialogFooter,
157
+ DialogHeader,
158
+ DialogOverlay,
159
+ DialogPortal,
160
+ DialogTitle,
161
+ DialogTrigger,
162
+ };
@@ -0,0 +1,126 @@
1
+ import type { ComponentProps } from "react";
2
+ import { Drawer as DrawerPrimitive } from "vaul";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ function Drawer({ ...props }: ComponentProps<typeof DrawerPrimitive.Root>) {
6
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
7
+ }
8
+
9
+ function DrawerTrigger({
10
+ ...props
11
+ }: ComponentProps<typeof DrawerPrimitive.Trigger>) {
12
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
13
+ }
14
+
15
+ function DrawerPortal({
16
+ ...props
17
+ }: ComponentProps<typeof DrawerPrimitive.Portal>) {
18
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
19
+ }
20
+
21
+ function DrawerClose({
22
+ ...props
23
+ }: ComponentProps<typeof DrawerPrimitive.Close>) {
24
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
25
+ }
26
+
27
+ function DrawerOverlay({
28
+ className,
29
+ ...props
30
+ }: ComponentProps<typeof DrawerPrimitive.Overlay>) {
31
+ return (
32
+ <DrawerPrimitive.Overlay
33
+ className={cn(
34
+ "data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/10 data-closed:animate-out data-open:animate-in supports-backdrop-filter:backdrop-blur-xs",
35
+ className
36
+ )}
37
+ data-slot="drawer-overlay"
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function DrawerContent({
44
+ className,
45
+ children,
46
+ ...props
47
+ }: ComponentProps<typeof DrawerPrimitive.Content>) {
48
+ return (
49
+ <DrawerPortal data-slot="drawer-portal">
50
+ <DrawerOverlay />
51
+ <DrawerPrimitive.Content
52
+ className={cn(
53
+ "group/drawer-content fixed z-50 flex h-auto flex-col bg-background text-sm data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm",
54
+ className
55
+ )}
56
+ data-slot="drawer-content"
57
+ {...props}
58
+ >
59
+ <div className="mx-auto mt-4 hidden h-1.5 w-25 shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
60
+ {children}
61
+ </DrawerPrimitive.Content>
62
+ </DrawerPortal>
63
+ );
64
+ }
65
+
66
+ function DrawerHeader({ className, ...props }: ComponentProps<"div">) {
67
+ return (
68
+ <div
69
+ className={cn(
70
+ "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
71
+ className
72
+ )}
73
+ data-slot="drawer-header"
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ function DrawerFooter({ className, ...props }: ComponentProps<"div">) {
80
+ return (
81
+ <div
82
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
83
+ data-slot="drawer-footer"
84
+ {...props}
85
+ />
86
+ );
87
+ }
88
+
89
+ function DrawerTitle({
90
+ className,
91
+ ...props
92
+ }: ComponentProps<typeof DrawerPrimitive.Title>) {
93
+ return (
94
+ <DrawerPrimitive.Title
95
+ className={cn("font-medium text-foreground", className)}
96
+ data-slot="drawer-title"
97
+ {...props}
98
+ />
99
+ );
100
+ }
101
+
102
+ function DrawerDescription({
103
+ className,
104
+ ...props
105
+ }: ComponentProps<typeof DrawerPrimitive.Description>) {
106
+ return (
107
+ <DrawerPrimitive.Description
108
+ className={cn("text-muted-foreground text-sm", className)}
109
+ data-slot="drawer-description"
110
+ {...props}
111
+ />
112
+ );
113
+ }
114
+
115
+ export {
116
+ Drawer,
117
+ DrawerPortal,
118
+ DrawerOverlay,
119
+ DrawerTrigger,
120
+ DrawerClose,
121
+ DrawerContent,
122
+ DrawerHeader,
123
+ DrawerFooter,
124
+ DrawerTitle,
125
+ DrawerDescription,
126
+ };