@exxatdesignux/ui 0.0.5
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.
- package/package.json +72 -0
- package/src/components/ui/avatar.tsx +384 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/banner.tsx +364 -0
- package/src/components/ui/breadcrumb.tsx +120 -0
- package/src/components/ui/button.tsx +66 -0
- package/src/components/ui/calendar.tsx +220 -0
- package/src/components/ui/card.tsx +136 -0
- package/src/components/ui/chart.tsx +378 -0
- package/src/components/ui/checkbox.tsx +160 -0
- package/src/components/ui/coach-mark.tsx +361 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +232 -0
- package/src/components/ui/date-picker-field.tsx +186 -0
- package/src/components/ui/dialog.tsx +171 -0
- package/src/components/ui/drag-handle-grip.tsx +10 -0
- package/src/components/ui/drawer.tsx +134 -0
- package/src/components/ui/dropdown-menu.tsx +422 -0
- package/src/components/ui/field.tsx +238 -0
- package/src/components/ui/form.tsx +137 -0
- package/src/components/ui/input-group.tsx +156 -0
- package/src/components/ui/input-mask.tsx +135 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/kbd.tsx +55 -0
- package/src/components/ui/label.tsx +25 -0
- package/src/components/ui/payment-card-fields.tsx +65 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/radio-group.tsx +217 -0
- package/src/components/ui/select.tsx +191 -0
- package/src/components/ui/selection-tile-grid.tsx +246 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +147 -0
- package/src/components/ui/sidebar.tsx +716 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +39 -0
- package/src/components/ui/status-badge.tsx +109 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +90 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tip.tsx +21 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle-switch.tsx +31 -0
- package/src/components/ui/toggle.tsx +48 -0
- package/src/components/ui/tooltip.tsx +59 -0
- package/src/components/ui/view-segmented-control.tsx +160 -0
- package/src/globals.css +1795 -0
- package/src/hooks/.gitkeep +0 -0
- package/src/hooks/use-app-theme.ts +172 -0
- package/src/hooks/use-coach-mark.ts +342 -0
- package/src/hooks/use-mobile.ts +31 -0
- package/src/hooks/use-mod-key-label.ts +29 -0
- package/src/index.ts +55 -0
- package/src/lib/compose-refs.ts +15 -0
- package/src/lib/date-filter.ts +67 -0
- package/src/lib/utils.ts +6 -0
- package/src/theme/apply-windows-contrast-theme.ts +29 -0
- package/src/theme/windows-contrast-theme.json +147 -0
- package/src/theme.css +1130 -0
- package/src/types/react-payment-inputs.d.ts +20 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
import { formatDateUS } from "../../lib/date-filter"
|
|
7
|
+
import { Button } from "./button"
|
|
8
|
+
import { Calendar } from "./calendar"
|
|
9
|
+
import {
|
|
10
|
+
InputGroup,
|
|
11
|
+
InputGroupAddon,
|
|
12
|
+
InputGroupButton,
|
|
13
|
+
} from "./input-group"
|
|
14
|
+
import { MaskedInput, exxatMaskPatterns } from "./input-mask"
|
|
15
|
+
import {
|
|
16
|
+
Popover,
|
|
17
|
+
PopoverContent,
|
|
18
|
+
PopoverTrigger,
|
|
19
|
+
} from "./popover"
|
|
20
|
+
|
|
21
|
+
export const DATE_PICKER_ICON_CLASS = "fa-light fa-calendar"
|
|
22
|
+
|
|
23
|
+
export interface DatePickerFieldProps {
|
|
24
|
+
value: Date | undefined
|
|
25
|
+
onChange: (d: Date | undefined) => void
|
|
26
|
+
id?: string
|
|
27
|
+
disabled?: boolean
|
|
28
|
+
/** Passed to the trigger `Button` (e.g. `h-8 text-sm` in compact drawers). */
|
|
29
|
+
triggerClassName?: string
|
|
30
|
+
fromYear?: number
|
|
31
|
+
toYear?: number
|
|
32
|
+
popoverAlign?: "start" | "center" | "end"
|
|
33
|
+
popoverClassName?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Calendar + popover trigger — same pattern as New Placement schedule dates (WCAG: button exposes label, not raw text input).
|
|
38
|
+
*/
|
|
39
|
+
export function DatePickerField({
|
|
40
|
+
value,
|
|
41
|
+
onChange,
|
|
42
|
+
id,
|
|
43
|
+
disabled,
|
|
44
|
+
triggerClassName,
|
|
45
|
+
fromYear = 2020,
|
|
46
|
+
toYear = 2032,
|
|
47
|
+
popoverAlign = "start",
|
|
48
|
+
popoverClassName,
|
|
49
|
+
}: DatePickerFieldProps) {
|
|
50
|
+
return (
|
|
51
|
+
<Popover>
|
|
52
|
+
<PopoverTrigger asChild>
|
|
53
|
+
<Button
|
|
54
|
+
id={id}
|
|
55
|
+
type="button"
|
|
56
|
+
variant="outline"
|
|
57
|
+
disabled={disabled}
|
|
58
|
+
className={cn(
|
|
59
|
+
"w-full justify-start text-left font-normal",
|
|
60
|
+
triggerClassName,
|
|
61
|
+
)}
|
|
62
|
+
aria-label={value ? formatDateUS(value.toISOString()) : "Pick a date"}
|
|
63
|
+
>
|
|
64
|
+
<i className={cn(DATE_PICKER_ICON_CLASS, "mr-2 shrink-0 text-muted-foreground")} aria-hidden="true" />
|
|
65
|
+
<span className={cn(!value && "text-muted-foreground")}>
|
|
66
|
+
{value ? formatDateUS(value.toISOString()) : "MM/DD/YYYY"}
|
|
67
|
+
</span>
|
|
68
|
+
</Button>
|
|
69
|
+
</PopoverTrigger>
|
|
70
|
+
<PopoverContent className={cn("z-[80] w-auto p-0", popoverClassName)} align={popoverAlign}>
|
|
71
|
+
<Calendar
|
|
72
|
+
mode="single"
|
|
73
|
+
selected={value}
|
|
74
|
+
onSelect={onChange}
|
|
75
|
+
initialFocus
|
|
76
|
+
fromYear={fromYear}
|
|
77
|
+
toYear={toYear}
|
|
78
|
+
captionLayout="dropdown"
|
|
79
|
+
/>
|
|
80
|
+
</PopoverContent>
|
|
81
|
+
</Popover>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface DateTextInputFieldProps {
|
|
86
|
+
value: string
|
|
87
|
+
onValueChange: (value: string) => void
|
|
88
|
+
"aria-label": string
|
|
89
|
+
id?: string
|
|
90
|
+
placeholder?: string
|
|
91
|
+
className?: string
|
|
92
|
+
inputClassName?: string
|
|
93
|
+
autoFocus?: boolean
|
|
94
|
+
disabled?: boolean
|
|
95
|
+
fromYear?: number
|
|
96
|
+
toYear?: number
|
|
97
|
+
iconButtonVariant?: "ghost" | "outline"
|
|
98
|
+
popoverAlign?: "start" | "center" | "end"
|
|
99
|
+
popoverClassName?: string
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseMdyToLocalDate(raw: string): Date | undefined {
|
|
103
|
+
const match = raw.trim().match(/^(\d{2})\/(\d{2})\/(\d{4})$/)
|
|
104
|
+
if (!match) return undefined
|
|
105
|
+
const month = Number(match[1])
|
|
106
|
+
const day = Number(match[2])
|
|
107
|
+
const year = Number(match[3])
|
|
108
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) return undefined
|
|
109
|
+
const date = new Date(year, month - 1, day, 12, 0, 0, 0)
|
|
110
|
+
if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) return undefined
|
|
111
|
+
return date
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function formatLocalDateToMdy(date: Date): string {
|
|
115
|
+
const month = String(date.getMonth() + 1).padStart(2, "0")
|
|
116
|
+
const day = String(date.getDate()).padStart(2, "0")
|
|
117
|
+
const year = String(date.getFullYear())
|
|
118
|
+
return `${month}/${day}/${year}`
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function DateTextInputField({
|
|
122
|
+
value,
|
|
123
|
+
onValueChange,
|
|
124
|
+
"aria-label": ariaLabel,
|
|
125
|
+
id,
|
|
126
|
+
placeholder = "MM/DD/YYYY",
|
|
127
|
+
className,
|
|
128
|
+
inputClassName,
|
|
129
|
+
autoFocus,
|
|
130
|
+
disabled,
|
|
131
|
+
fromYear = 2020,
|
|
132
|
+
toYear = 2032,
|
|
133
|
+
iconButtonVariant = "ghost",
|
|
134
|
+
popoverAlign = "end",
|
|
135
|
+
popoverClassName,
|
|
136
|
+
}: DateTextInputFieldProps) {
|
|
137
|
+
return (
|
|
138
|
+
<InputGroup className={className}>
|
|
139
|
+
<MaskedInput
|
|
140
|
+
data-slot="input-group-control"
|
|
141
|
+
id={id}
|
|
142
|
+
mask={exxatMaskPatterns.dateMDY}
|
|
143
|
+
aria-label={ariaLabel}
|
|
144
|
+
placeholder={placeholder}
|
|
145
|
+
value={value}
|
|
146
|
+
onChange={(event) => onValueChange(event.target.value)}
|
|
147
|
+
className={cn(
|
|
148
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
|
|
149
|
+
inputClassName,
|
|
150
|
+
)}
|
|
151
|
+
autoFocus={autoFocus}
|
|
152
|
+
disabled={disabled}
|
|
153
|
+
/>
|
|
154
|
+
<InputGroupAddon align="inline-end">
|
|
155
|
+
<Popover>
|
|
156
|
+
<PopoverTrigger asChild>
|
|
157
|
+
<InputGroupButton
|
|
158
|
+
type="button"
|
|
159
|
+
variant={iconButtonVariant}
|
|
160
|
+
size="icon-xs"
|
|
161
|
+
disabled={disabled}
|
|
162
|
+
aria-label={`${ariaLabel} pick date`}
|
|
163
|
+
>
|
|
164
|
+
<i className={DATE_PICKER_ICON_CLASS} aria-hidden="true" />
|
|
165
|
+
</InputGroupButton>
|
|
166
|
+
</PopoverTrigger>
|
|
167
|
+
<PopoverContent
|
|
168
|
+
className={cn("z-[80] w-auto p-0", popoverClassName)}
|
|
169
|
+
align={popoverAlign}
|
|
170
|
+
sideOffset={6}
|
|
171
|
+
>
|
|
172
|
+
<Calendar
|
|
173
|
+
mode="single"
|
|
174
|
+
selected={parseMdyToLocalDate(value)}
|
|
175
|
+
onSelect={(next) => onValueChange(next ? formatLocalDateToMdy(next) : "")}
|
|
176
|
+
initialFocus
|
|
177
|
+
fromYear={fromYear}
|
|
178
|
+
toYear={toYear}
|
|
179
|
+
captionLayout="dropdown"
|
|
180
|
+
/>
|
|
181
|
+
</PopoverContent>
|
|
182
|
+
</Popover>
|
|
183
|
+
</InputGroupAddon>
|
|
184
|
+
</InputGroup>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Dialog as DialogPrimitive } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
import { Button } from "./button"
|
|
8
|
+
import { XIcon } from "lucide-react"
|
|
9
|
+
|
|
10
|
+
function Dialog({
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
13
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DialogTrigger({
|
|
17
|
+
...props
|
|
18
|
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
19
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function DialogPortal({
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
25
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function DialogClose({
|
|
29
|
+
...props
|
|
30
|
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
31
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function DialogOverlay({
|
|
35
|
+
className,
|
|
36
|
+
...props
|
|
37
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
38
|
+
return (
|
|
39
|
+
<DialogPrimitive.Overlay
|
|
40
|
+
data-slot="dialog-overlay"
|
|
41
|
+
className={cn(
|
|
42
|
+
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function DialogContent({
|
|
51
|
+
className,
|
|
52
|
+
children,
|
|
53
|
+
showCloseButton = true,
|
|
54
|
+
overlayClassName,
|
|
55
|
+
...props
|
|
56
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
57
|
+
showCloseButton?: boolean
|
|
58
|
+
/** Merged onto `DialogOverlay` (e.g. `bg-transparent` for a palette with no dimmed backdrop). */
|
|
59
|
+
overlayClassName?: string
|
|
60
|
+
}) {
|
|
61
|
+
return (
|
|
62
|
+
<DialogPortal>
|
|
63
|
+
<DialogOverlay className={overlayClassName ?? undefined} />
|
|
64
|
+
<DialogPrimitive.Content
|
|
65
|
+
data-slot="dialog-content"
|
|
66
|
+
className={cn(
|
|
67
|
+
"fixed top-1/2 start-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 rtl:translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
68
|
+
className
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
{showCloseButton && (
|
|
74
|
+
<DialogPrimitive.Close data-slot="dialog-close" asChild>
|
|
75
|
+
<Button
|
|
76
|
+
variant="ghost"
|
|
77
|
+
className="absolute top-2 end-2"
|
|
78
|
+
size="icon-sm"
|
|
79
|
+
>
|
|
80
|
+
<XIcon
|
|
81
|
+
/>
|
|
82
|
+
<span className="sr-only">Close</span>
|
|
83
|
+
</Button>
|
|
84
|
+
</DialogPrimitive.Close>
|
|
85
|
+
)}
|
|
86
|
+
</DialogPrimitive.Content>
|
|
87
|
+
</DialogPortal>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
data-slot="dialog-header"
|
|
95
|
+
className={cn("flex flex-col gap-2", className)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function DialogFooter({
|
|
102
|
+
className,
|
|
103
|
+
showCloseButton = false,
|
|
104
|
+
children,
|
|
105
|
+
...props
|
|
106
|
+
}: React.ComponentProps<"div"> & {
|
|
107
|
+
showCloseButton?: boolean
|
|
108
|
+
}) {
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
data-slot="dialog-footer"
|
|
112
|
+
className={cn(
|
|
113
|
+
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
|
|
114
|
+
className
|
|
115
|
+
)}
|
|
116
|
+
{...props}
|
|
117
|
+
>
|
|
118
|
+
{children}
|
|
119
|
+
{showCloseButton && (
|
|
120
|
+
<DialogPrimitive.Close asChild>
|
|
121
|
+
<Button variant="outline">Close</Button>
|
|
122
|
+
</DialogPrimitive.Close>
|
|
123
|
+
)}
|
|
124
|
+
</div>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function DialogTitle({
|
|
129
|
+
className,
|
|
130
|
+
...props
|
|
131
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
132
|
+
return (
|
|
133
|
+
<DialogPrimitive.Title
|
|
134
|
+
data-slot="dialog-title"
|
|
135
|
+
className={cn(
|
|
136
|
+
"font-heading text-base leading-none font-medium",
|
|
137
|
+
className
|
|
138
|
+
)}
|
|
139
|
+
{...props}
|
|
140
|
+
/>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function DialogDescription({
|
|
145
|
+
className,
|
|
146
|
+
...props
|
|
147
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
148
|
+
return (
|
|
149
|
+
<DialogPrimitive.Description
|
|
150
|
+
data-slot="dialog-description"
|
|
151
|
+
className={cn(
|
|
152
|
+
"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
153
|
+
className
|
|
154
|
+
)}
|
|
155
|
+
{...props}
|
|
156
|
+
/>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export {
|
|
161
|
+
Dialog,
|
|
162
|
+
DialogClose,
|
|
163
|
+
DialogContent,
|
|
164
|
+
DialogDescription,
|
|
165
|
+
DialogFooter,
|
|
166
|
+
DialogHeader,
|
|
167
|
+
DialogOverlay,
|
|
168
|
+
DialogPortal,
|
|
169
|
+
DialogTitle,
|
|
170
|
+
DialogTrigger,
|
|
171
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Solid grip icon for drag handles — use anywhere rows/cards reorder (dashboard, Properties, etc.).
|
|
5
|
+
*/
|
|
6
|
+
export function DragHandleGripIcon({ className }: { className?: string }) {
|
|
7
|
+
return (
|
|
8
|
+
<i className={cn("fa-solid fa-grip-dots-vertical shrink-0", className)} aria-hidden="true" />
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Drawer as DrawerPrimitive } from "vaul"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
function Drawer({
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
|
11
|
+
return <DrawerPrimitive.Root data-slot="drawer" {...props} />
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function DrawerTrigger({
|
|
15
|
+
...props
|
|
16
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
|
|
17
|
+
return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function DrawerPortal({
|
|
21
|
+
...props
|
|
22
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
|
|
23
|
+
return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function DrawerClose({
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Close>) {
|
|
29
|
+
return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function DrawerOverlay({
|
|
33
|
+
className,
|
|
34
|
+
...props
|
|
35
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
|
|
36
|
+
return (
|
|
37
|
+
<DrawerPrimitive.Overlay
|
|
38
|
+
data-slot="drawer-overlay"
|
|
39
|
+
className={cn(
|
|
40
|
+
"fixed inset-0 z-50 bg-overlay duration-300 ease-out supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function DrawerContent({
|
|
49
|
+
className,
|
|
50
|
+
children,
|
|
51
|
+
...props
|
|
52
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Content>) {
|
|
53
|
+
return (
|
|
54
|
+
<DrawerPortal data-slot="drawer-portal">
|
|
55
|
+
<DrawerOverlay />
|
|
56
|
+
<DrawerPrimitive.Content
|
|
57
|
+
data-slot="drawer-content"
|
|
58
|
+
className={cn(
|
|
59
|
+
"group/drawer-content fixed z-50 flex h-auto flex-col bg-background text-sm duration-300 ease-out outline-none 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-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:start-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-e-xl data-[vaul-drawer-direction=left]:border-e data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:end-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-s-xl data-[vaul-drawer-direction=right]:border-s 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-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
|
60
|
+
className
|
|
61
|
+
)}
|
|
62
|
+
{...props}
|
|
63
|
+
>
|
|
64
|
+
<div className="mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
|
|
65
|
+
{children}
|
|
66
|
+
</DrawerPrimitive.Content>
|
|
67
|
+
</DrawerPortal>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
data-slot="drawer-header"
|
|
75
|
+
className={cn(
|
|
76
|
+
"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-0.5 md:text-start",
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
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(
|
|
102
|
+
"font-heading text-base font-medium text-foreground",
|
|
103
|
+
className
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function DrawerDescription({
|
|
111
|
+
className,
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
|
|
114
|
+
return (
|
|
115
|
+
<DrawerPrimitive.Description
|
|
116
|
+
data-slot="drawer-description"
|
|
117
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export {
|
|
124
|
+
Drawer,
|
|
125
|
+
DrawerPortal,
|
|
126
|
+
DrawerOverlay,
|
|
127
|
+
DrawerTrigger,
|
|
128
|
+
DrawerClose,
|
|
129
|
+
DrawerContent,
|
|
130
|
+
DrawerHeader,
|
|
131
|
+
DrawerFooter,
|
|
132
|
+
DrawerTitle,
|
|
133
|
+
DrawerDescription,
|
|
134
|
+
}
|