@tuturuuu/ui 0.0.4

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 (104) hide show
  1. package/.checksum +1 -0
  2. package/README.md +46 -0
  3. package/components.json +20 -0
  4. package/eslint.config.mjs +20 -0
  5. package/jsr.json +10 -0
  6. package/package.json +120 -0
  7. package/postcss.config.mjs +8 -0
  8. package/rollup.config.js +40 -0
  9. package/src/components/ui/accordion.tsx +70 -0
  10. package/src/components/ui/alert-dialog.tsx +156 -0
  11. package/src/components/ui/alert.tsx +58 -0
  12. package/src/components/ui/aspect-ratio.tsx +11 -0
  13. package/src/components/ui/avatar.tsx +52 -0
  14. package/src/components/ui/badge.tsx +49 -0
  15. package/src/components/ui/breadcrumb.tsx +108 -0
  16. package/src/components/ui/button.tsx +61 -0
  17. package/src/components/ui/calendar.tsx +212 -0
  18. package/src/components/ui/card.tsx +74 -0
  19. package/src/components/ui/carousel.tsx +240 -0
  20. package/src/components/ui/chart.tsx +365 -0
  21. package/src/components/ui/checkbox.tsx +31 -0
  22. package/src/components/ui/codeblock.tsx +161 -0
  23. package/src/components/ui/collapsible.tsx +33 -0
  24. package/src/components/ui/color-picker.tsx +143 -0
  25. package/src/components/ui/command.tsx +176 -0
  26. package/src/components/ui/context-menu.tsx +251 -0
  27. package/src/components/ui/custom/autosize-textarea.tsx +111 -0
  28. package/src/components/ui/custom/calendar/core.tsx +61 -0
  29. package/src/components/ui/custom/calendar/day-cell.tsx +74 -0
  30. package/src/components/ui/custom/calendar/month-header.tsx +59 -0
  31. package/src/components/ui/custom/calendar/month-view.tsx +110 -0
  32. package/src/components/ui/custom/calendar/utils.ts +76 -0
  33. package/src/components/ui/custom/calendar/year-calendar.tsx +64 -0
  34. package/src/components/ui/custom/calendar/year-view.tsx +58 -0
  35. package/src/components/ui/custom/combobox.tsx +197 -0
  36. package/src/components/ui/custom/common-footer.tsx +215 -0
  37. package/src/components/ui/custom/compared-date-range-picker.tsx +561 -0
  38. package/src/components/ui/custom/date-input.tsx +279 -0
  39. package/src/components/ui/custom/empty-card.tsx +39 -0
  40. package/src/components/ui/custom/feature-summary.tsx +135 -0
  41. package/src/components/ui/custom/file-uploader.tsx +349 -0
  42. package/src/components/ui/custom/input-field.tsx +29 -0
  43. package/src/components/ui/custom/loading-indicator.tsx +28 -0
  44. package/src/components/ui/custom/modifiable-dialog-trigger.tsx +83 -0
  45. package/src/components/ui/custom/month-picker.tsx +157 -0
  46. package/src/components/ui/custom/report-preview.tsx +175 -0
  47. package/src/components/ui/custom/search-bar.tsx +56 -0
  48. package/src/components/ui/custom/select-field.tsx +78 -0
  49. package/src/components/ui/custom/tables/data-table-column-header.tsx +72 -0
  50. package/src/components/ui/custom/tables/data-table-create-button.tsx +31 -0
  51. package/src/components/ui/custom/tables/data-table-faceted-filter.tsx +142 -0
  52. package/src/components/ui/custom/tables/data-table-pagination.tsx +243 -0
  53. package/src/components/ui/custom/tables/data-table-refresh-button.tsx +45 -0
  54. package/src/components/ui/custom/tables/data-table-toolbar.tsx +133 -0
  55. package/src/components/ui/custom/tables/data-table-view-options.tsx +112 -0
  56. package/src/components/ui/custom/tables/data-table.tsx +228 -0
  57. package/src/components/ui/custom/uploaded-files-card.tsx +50 -0
  58. package/src/components/ui/dialog.tsx +137 -0
  59. package/src/components/ui/drawer.tsx +131 -0
  60. package/src/components/ui/dropdown-menu.tsx +256 -0
  61. package/src/components/ui/form.tsx +167 -0
  62. package/src/components/ui/hover-card.tsx +41 -0
  63. package/src/components/ui/icons.tsx +506 -0
  64. package/src/components/ui/input-otp.tsx +78 -0
  65. package/src/components/ui/input.tsx +18 -0
  66. package/src/components/ui/label.tsx +23 -0
  67. package/src/components/ui/markdown.tsx +7 -0
  68. package/src/components/ui/menubar.tsx +275 -0
  69. package/src/components/ui/navigation-menu.tsx +169 -0
  70. package/src/components/ui/pagination.tsx +126 -0
  71. package/src/components/ui/popover.tsx +47 -0
  72. package/src/components/ui/progress.tsx +30 -0
  73. package/src/components/ui/radio-group.tsx +44 -0
  74. package/src/components/ui/resizable.tsx +55 -0
  75. package/src/components/ui/scroll-area.tsx +57 -0
  76. package/src/components/ui/select.tsx +180 -0
  77. package/src/components/ui/separator.tsx +27 -0
  78. package/src/components/ui/sheet.tsx +138 -0
  79. package/src/components/ui/sidebar.tsx +734 -0
  80. package/src/components/ui/skeleton.tsx +13 -0
  81. package/src/components/ui/slider.tsx +62 -0
  82. package/src/components/ui/sonner.tsx +29 -0
  83. package/src/components/ui/switch.tsx +30 -0
  84. package/src/components/ui/table.tsx +112 -0
  85. package/src/components/ui/tabs.tsx +68 -0
  86. package/src/components/ui/tag-input.tsx +141 -0
  87. package/src/components/ui/textarea.tsx +17 -0
  88. package/src/components/ui/time-picker-input.tsx +117 -0
  89. package/src/components/ui/time-picker-utils.tsx +146 -0
  90. package/src/components/ui/toast.tsx +128 -0
  91. package/src/components/ui/toaster.tsx +35 -0
  92. package/src/components/ui/toggle-group.tsx +72 -0
  93. package/src/components/ui/toggle.tsx +46 -0
  94. package/src/components/ui/tooltip.tsx +60 -0
  95. package/src/globals.css +252 -0
  96. package/src/hooks/use-callback-ref.ts +28 -0
  97. package/src/hooks/use-controllable-state.ts +68 -0
  98. package/src/hooks/use-copy-to-clipboard.ts +46 -0
  99. package/src/hooks/use-form.ts +23 -0
  100. package/src/hooks/use-forwarded-ref.ts +17 -0
  101. package/src/hooks/use-mobile.tsx +21 -0
  102. package/src/hooks/use-toast.ts +191 -0
  103. package/src/resolvers.ts +3 -0
  104. package/tsconfig.json +17 -0
@@ -0,0 +1,228 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Table,
5
+ TableBody,
6
+ TableCell,
7
+ TableHead,
8
+ TableHeader,
9
+ TableRow,
10
+ } from '../../table';
11
+ import { DataTablePagination } from './data-table-pagination';
12
+ import { DataTableToolbar } from './data-table-toolbar';
13
+ import {
14
+ ColumnDef,
15
+ ColumnFiltersState,
16
+ SortingState,
17
+ VisibilityState,
18
+ flexRender,
19
+ getCoreRowModel,
20
+ getFacetedRowModel,
21
+ getFacetedUniqueValues,
22
+ getFilteredRowModel,
23
+ getSortedRowModel,
24
+ useReactTable,
25
+ } from '@tanstack/react-table';
26
+ import { cn } from '@tuturuuu/utils/format';
27
+ import { ReactNode, useState } from 'react';
28
+
29
+ export interface DataTableProps<TData, TValue> {
30
+ hideToolbar?: boolean;
31
+ hidePagination?: boolean;
32
+ columns?: ColumnDef<TData, TValue>[];
33
+ filters?: ReactNode[] | ReactNode;
34
+ extraColumns?: any[];
35
+ extraData?: any;
36
+ newObjectTitle?: string;
37
+ editContent?: ReactNode;
38
+ namespace?: string | undefined;
39
+ data?: TData[];
40
+ count?: number | null;
41
+ pageIndex?: number;
42
+ pageSize?: number;
43
+ defaultQuery?: string;
44
+ defaultVisibility?: VisibilityState;
45
+ disableSearch?: boolean;
46
+ isEmpty?: boolean;
47
+ toolbarImportContent?: ReactNode;
48
+ toolbarExportContent?: ReactNode;
49
+ className?: string;
50
+ onRefresh?: () => void;
51
+ // eslint-disable-next-line no-unused-vars
52
+ onSearch?: (query: string) => void;
53
+ // eslint-disable-next-line no-unused-vars
54
+ setParams?: (params: { page?: number; pageSize?: string }) => void;
55
+ resetParams?: () => void;
56
+ t?: any;
57
+ columnGenerator?: (
58
+ // eslint-disable-next-line no-unused-vars
59
+ t: any,
60
+ // eslint-disable-next-line no-unused-vars
61
+ namespace: string | undefined,
62
+ // eslint-disable-next-line no-unused-vars
63
+ extraColumns?: any[],
64
+ // eslint-disable-next-line no-unused-vars
65
+ extraData?: any
66
+ ) => ColumnDef<TData, TValue>[];
67
+ }
68
+
69
+ export function DataTable<TData, TValue>({
70
+ hideToolbar = false,
71
+ hidePagination = false,
72
+ columns,
73
+ filters,
74
+ extraColumns,
75
+ extraData,
76
+ newObjectTitle,
77
+ editContent,
78
+ namespace,
79
+ data,
80
+ count,
81
+ pageIndex = 0,
82
+ pageSize = 10,
83
+ defaultQuery,
84
+ defaultVisibility = {},
85
+ disableSearch,
86
+ isEmpty,
87
+ t,
88
+ toolbarImportContent,
89
+ toolbarExportContent,
90
+ className,
91
+ onRefresh,
92
+ onSearch,
93
+ setParams,
94
+ resetParams,
95
+ columnGenerator,
96
+ }: DataTableProps<TData, TValue>) {
97
+ const [rowSelection, setRowSelection] = useState({});
98
+ const [columnVisibility, setColumnVisibility] =
99
+ useState<VisibilityState>(defaultVisibility);
100
+ const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
101
+ const [sorting, setSorting] = useState<SortingState>([]);
102
+
103
+ const table = useReactTable({
104
+ data: data || [],
105
+ columns:
106
+ columnGenerator && t
107
+ ? columnGenerator(t, namespace, extraColumns, extraData)
108
+ : columns || [],
109
+ state: {
110
+ sorting,
111
+ columnVisibility,
112
+ rowSelection,
113
+ columnFilters,
114
+ pagination: {
115
+ pageIndex,
116
+ pageSize,
117
+ },
118
+ },
119
+ pageCount:
120
+ count != undefined ? Math.max(Math.ceil(count / pageSize), 1) : undefined,
121
+ enableRowSelection: true,
122
+ autoResetPageIndex: true,
123
+ onRowSelectionChange: setRowSelection,
124
+ onSortingChange: setSorting,
125
+ onColumnFiltersChange: setColumnFilters,
126
+ onColumnVisibilityChange: setColumnVisibility,
127
+ getCoreRowModel: getCoreRowModel(),
128
+ getFilteredRowModel: getFilteredRowModel(),
129
+ getSortedRowModel: getSortedRowModel(),
130
+ getFacetedRowModel: getFacetedRowModel(),
131
+ getFacetedUniqueValues: getFacetedUniqueValues(),
132
+ });
133
+
134
+ return (
135
+ <div className={cn('space-y-4', className)}>
136
+ {hideToolbar || (
137
+ <DataTableToolbar
138
+ hasData={!!data}
139
+ namespace={namespace}
140
+ table={table}
141
+ newObjectTitle={newObjectTitle}
142
+ editContent={editContent}
143
+ filters={filters}
144
+ extraColumns={extraColumns}
145
+ disableSearch={disableSearch}
146
+ t={t}
147
+ isEmpty={isEmpty || !data?.length}
148
+ defaultQuery={defaultQuery}
149
+ onSearch={onSearch || (() => {})}
150
+ onRefresh={onRefresh || (() => {})}
151
+ resetParams={resetParams || (() => {})}
152
+ importContent={toolbarImportContent}
153
+ exportContent={toolbarExportContent}
154
+ />
155
+ )}
156
+ <div className="rounded-md border">
157
+ <Table>
158
+ <TableHeader>
159
+ {table.getHeaderGroups().map((headerGroup) => (
160
+ <TableRow key={headerGroup.id}>
161
+ {headerGroup.headers.map((header) => {
162
+ return (
163
+ <TableHead key={header.id} className="text-foreground/70">
164
+ {header.isPlaceholder
165
+ ? null
166
+ : flexRender(
167
+ header.column.columnDef.header,
168
+ header.getContext()
169
+ )}
170
+ </TableHead>
171
+ );
172
+ })}
173
+ </TableRow>
174
+ ))}
175
+ </TableHeader>
176
+ <TableBody>
177
+ {table.getRowModel().rows?.length ? (
178
+ table.getRowModel().rows.map((row) => (
179
+ <TableRow
180
+ key={`${namespace}-${row.id}`}
181
+ data-state={row.getIsSelected() && 'selected'}
182
+ >
183
+ {row.getVisibleCells().map((cell) => (
184
+ <TableCell key={`${namespace}-${cell.id}`}>
185
+ {flexRender(
186
+ cell.column.columnDef.cell,
187
+ cell.getContext()
188
+ )}
189
+ </TableCell>
190
+ ))}
191
+ </TableRow>
192
+ ))
193
+ ) : (
194
+ <TableRow>
195
+ <TableCell
196
+ colSpan={
197
+ (namespace && columnGenerator?.(t, namespace)?.length) ||
198
+ columns?.length ||
199
+ 1
200
+ }
201
+ className="h-24 text-center opacity-60"
202
+ >
203
+ {data
204
+ ? `${t?.('common.no-results')}.`
205
+ : `${t?.('common.loading')}...`}
206
+ </TableCell>
207
+ </TableRow>
208
+ )}
209
+ </TableBody>
210
+ </Table>
211
+ </div>
212
+
213
+ {hidePagination ||
214
+ (count !== undefined && (
215
+ <>
216
+ <DataTablePagination
217
+ t={t}
218
+ table={table}
219
+ count={count}
220
+ className="rounded-lg border bg-foreground/[0.025] px-4 py-2 backdrop-blur-xl dark:bg-foreground/5"
221
+ setParams={setParams}
222
+ />
223
+ <div className="h-4" />
224
+ </>
225
+ ))}
226
+ </div>
227
+ );
228
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ Card,
3
+ CardContent,
4
+ CardDescription,
5
+ CardHeader,
6
+ CardTitle,
7
+ } from '../card';
8
+ import { ScrollArea, ScrollBar } from '../scroll-area';
9
+ import { EmptyCard } from './empty-card';
10
+
11
+ interface UploadedFilesCardProps {
12
+ uploadedFiles: any[];
13
+ }
14
+
15
+ export function UploadedFilesCard({ uploadedFiles }: UploadedFilesCardProps) {
16
+ return (
17
+ <Card>
18
+ <CardHeader>
19
+ <CardTitle>Uploaded files</CardTitle>
20
+ <CardDescription>View the uploaded files here</CardDescription>
21
+ </CardHeader>
22
+ <CardContent>
23
+ {uploadedFiles.length > 0 ? (
24
+ <ScrollArea className="pb-4">
25
+ <div className="flex w-max space-x-2.5">
26
+ {uploadedFiles.map((file) => (
27
+ <div key={file.key} className="relative aspect-video w-64">
28
+ <img
29
+ src={file.url}
30
+ alt={file.name}
31
+ sizes="(min-width: 640px) 640px, 100vw"
32
+ loading="lazy"
33
+ className="rounded-md object-cover"
34
+ />
35
+ </div>
36
+ ))}
37
+ </div>
38
+ <ScrollBar orientation="horizontal" />
39
+ </ScrollArea>
40
+ ) : (
41
+ <EmptyCard
42
+ title="No files uploaded"
43
+ description="Upload some files to see them here"
44
+ className="w-full"
45
+ />
46
+ )}
47
+ </CardContent>
48
+ </Card>
49
+ );
50
+ }
@@ -0,0 +1,137 @@
1
+ 'use client';
2
+
3
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
4
+ import { cn } from '@tuturuuu/utils/format';
5
+ import { XIcon } from 'lucide-react';
6
+ import * as React from 'react';
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
+ data-slot="dialog-overlay"
39
+ className={cn(
40
+ 'fixed inset-0 z-50 bg-black/80 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',
41
+ className
42
+ )}
43
+ {...props}
44
+ />
45
+ );
46
+ }
47
+
48
+ function DialogContent({
49
+ className,
50
+ children,
51
+ ...props
52
+ }: React.ComponentProps<typeof DialogPrimitive.Content>) {
53
+ return (
54
+ <DialogPortal data-slot="dialog-portal">
55
+ <DialogOverlay />
56
+ <DialogPrimitive.Content
57
+ data-slot="dialog-content"
58
+ className={cn(
59
+ 'fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg',
60
+ className
61
+ )}
62
+ {...props}
63
+ >
64
+ {children}
65
+ <DialogPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
66
+ <XIcon />
67
+ <span className="sr-only">Close</span>
68
+ </DialogPrimitive.Close>
69
+ </DialogPrimitive.Content>
70
+ </DialogPortal>
71
+ );
72
+ }
73
+
74
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
75
+ return (
76
+ <div
77
+ data-slot="dialog-header"
78
+ className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+
84
+ function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
85
+ return (
86
+ <div
87
+ data-slot="dialog-footer"
88
+ className={cn(
89
+ 'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
90
+ className
91
+ )}
92
+ {...props}
93
+ />
94
+ );
95
+ }
96
+
97
+ function DialogTitle({
98
+ className,
99
+ ...props
100
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
101
+ return (
102
+ <DialogPrimitive.Title
103
+ data-slot="dialog-title"
104
+ className={cn(
105
+ 'text-lg leading-none font-semibold tracking-tight',
106
+ className
107
+ )}
108
+ {...props}
109
+ />
110
+ );
111
+ }
112
+
113
+ function DialogDescription({
114
+ className,
115
+ ...props
116
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
117
+ return (
118
+ <DialogPrimitive.Description
119
+ data-slot="dialog-description"
120
+ className={cn('text-sm text-muted-foreground', className)}
121
+ {...props}
122
+ />
123
+ );
124
+ }
125
+
126
+ export {
127
+ Dialog,
128
+ DialogClose,
129
+ DialogContent,
130
+ DialogDescription,
131
+ DialogFooter,
132
+ DialogHeader,
133
+ DialogOverlay,
134
+ DialogPortal,
135
+ DialogTitle,
136
+ DialogTrigger,
137
+ };
@@ -0,0 +1,131 @@
1
+ 'use client';
2
+
3
+ import { cn } from '@tuturuuu/utils/format';
4
+ import * as React from 'react';
5
+ import { Drawer as DrawerPrimitive } from 'vaul';
6
+
7
+ function Drawer({
8
+ ...props
9
+ }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
10
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
11
+ }
12
+
13
+ function DrawerTrigger({
14
+ ...props
15
+ }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
16
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
17
+ }
18
+
19
+ function DrawerPortal({
20
+ ...props
21
+ }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
22
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
23
+ }
24
+
25
+ function DrawerClose({
26
+ ...props
27
+ }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
28
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
29
+ }
30
+
31
+ function DrawerOverlay({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
35
+ return (
36
+ <DrawerPrimitive.Overlay
37
+ data-slot="drawer-overlay"
38
+ className={cn(
39
+ 'fixed inset-0 z-50 bg-black/80 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ function DrawerContent({
48
+ className,
49
+ children,
50
+ ...props
51
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
52
+ return (
53
+ <DrawerPortal data-slot="drawer-portal">
54
+ <DrawerOverlay />
55
+ <DrawerPrimitive.Content
56
+ data-slot="drawer-content"
57
+ className={cn(
58
+ 'group/drawer-content fixed z-50 flex h-auto flex-col bg-background',
59
+ 'data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg',
60
+ 'data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg',
61
+ 'data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:sm:max-w-sm',
62
+ 'data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:sm:max-w-sm',
63
+ className
64
+ )}
65
+ {...props}
66
+ >
67
+ <div className="mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
68
+ {children}
69
+ </DrawerPrimitive.Content>
70
+ </DrawerPortal>
71
+ );
72
+ }
73
+
74
+ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
75
+ return (
76
+ <div
77
+ data-slot="drawer-header"
78
+ className={cn('flex flex-col gap-1.5 p-4', className)}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+
84
+ function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
85
+ return (
86
+ <div
87
+ data-slot="drawer-footer"
88
+ className={cn('mt-auto flex flex-col gap-2 p-4', className)}
89
+ {...props}
90
+ />
91
+ );
92
+ }
93
+
94
+ function DrawerTitle({
95
+ className,
96
+ ...props
97
+ }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
98
+ return (
99
+ <DrawerPrimitive.Title
100
+ data-slot="drawer-title"
101
+ className={cn('font-semibold tracking-tight text-foreground', className)}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ function DrawerDescription({
108
+ className,
109
+ ...props
110
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
111
+ return (
112
+ <DrawerPrimitive.Description
113
+ data-slot="drawer-description"
114
+ className={cn('text-sm text-muted-foreground', className)}
115
+ {...props}
116
+ />
117
+ );
118
+ }
119
+
120
+ export {
121
+ Drawer,
122
+ DrawerClose,
123
+ DrawerContent,
124
+ DrawerDescription,
125
+ DrawerFooter,
126
+ DrawerHeader,
127
+ DrawerOverlay,
128
+ DrawerPortal,
129
+ DrawerTitle,
130
+ DrawerTrigger,
131
+ };