@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.
- package/package.json +78 -0
- package/src/components/ui/accordion.tsx +79 -0
- package/src/components/ui/alert-dialog.tsx +198 -0
- package/src/components/ui/alert.tsx +105 -0
- package/src/components/ui/aspect-ratio.tsx +30 -0
- package/src/components/ui/avatar.tsx +125 -0
- package/src/components/ui/badge-variants.ts +166 -0
- package/src/components/ui/badge.tsx +45 -0
- package/src/components/ui/banner.tsx +84 -0
- package/src/components/ui/breadcrumb.tsx +127 -0
- package/src/components/ui/button-group.tsx +99 -0
- package/src/components/ui/button-variants.ts +108 -0
- package/src/components/ui/button.tsx +54 -0
- package/src/components/ui/calendar.tsx +266 -0
- package/src/components/ui/card.tsx +155 -0
- package/src/components/ui/carousel.tsx +319 -0
- package/src/components/ui/chat.tsx +896 -0
- package/src/components/ui/checkbox-group.tsx +44 -0
- package/src/components/ui/checkbox.tsx +34 -0
- package/src/components/ui/chip.tsx +60 -0
- package/src/components/ui/collapsible.tsx +44 -0
- package/src/components/ui/color-picker.tsx +191 -0
- package/src/components/ui/combobox.tsx +317 -0
- package/src/components/ui/command.tsx +187 -0
- package/src/components/ui/confirm-dialog.tsx +125 -0
- package/src/components/ui/context-menu.tsx +254 -0
- package/src/components/ui/copyable-text.tsx +65 -0
- package/src/components/ui/date-picker.tsx +316 -0
- package/src/components/ui/date-time-picker.tsx +1071 -0
- package/src/components/ui/dialog.tsx +176 -0
- package/src/components/ui/direction.tsx +15 -0
- package/src/components/ui/drawer.tsx +178 -0
- package/src/components/ui/dropdown-menu.tsx +247 -0
- package/src/components/ui/empty.tsx +131 -0
- package/src/components/ui/field.tsx +257 -0
- package/src/components/ui/formatted-input.tsx +219 -0
- package/src/components/ui/gallery.tsx +161 -0
- package/src/components/ui/hover-card.tsx +77 -0
- package/src/components/ui/icon-picker.tsx +193 -0
- package/src/components/ui/image-cropper.tsx +787 -0
- package/src/components/ui/image-upload-field.tsx +344 -0
- package/src/components/ui/input-group.tsx +175 -0
- package/src/components/ui/input-otp.tsx +83 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/item.tsx +212 -0
- package/src/components/ui/kbd.tsx +62 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/list.tsx +46 -0
- package/src/components/ui/menu-variants.ts +71 -0
- package/src/components/ui/menubar.tsx +226 -0
- package/src/components/ui/money-input.tsx +96 -0
- package/src/components/ui/native-date-picker.tsx +120 -0
- package/src/components/ui/native-select.tsx +74 -0
- package/src/components/ui/navigation-menu.tsx +183 -0
- package/src/components/ui/pagination.tsx +150 -0
- package/src/components/ui/popover.tsx +104 -0
- package/src/components/ui/profile-header.tsx +196 -0
- package/src/components/ui/progress.tsx +71 -0
- package/src/components/ui/radio-group.tsx +115 -0
- package/src/components/ui/rating.tsx +200 -0
- package/src/components/ui/resizable.tsx +61 -0
- package/src/components/ui/responsive-dialog.tsx +278 -0
- package/src/components/ui/responsive-select.tsx +289 -0
- package/src/components/ui/responsive-sheet.tsx +225 -0
- package/src/components/ui/scroll-area.tsx +51 -0
- package/src/components/ui/section.tsx +106 -0
- package/src/components/ui/segmented-control.tsx +148 -0
- package/src/components/ui/select.tsx +216 -0
- package/src/components/ui/separator.tsx +69 -0
- package/src/components/ui/sheet.tsx +154 -0
- package/src/components/ui/shortcut.tsx +41 -0
- package/src/components/ui/sidebar.tsx +739 -0
- package/src/components/ui/skeleton.tsx +23 -0
- package/src/components/ui/slider.tsx +74 -0
- package/src/components/ui/sonner.tsx +64 -0
- package/src/components/ui/spinner.tsx +19 -0
- package/src/components/ui/stat-card.tsx +138 -0
- package/src/components/ui/stepper.tsx +181 -0
- package/src/components/ui/sticky-action-bar.tsx +42 -0
- package/src/components/ui/switch.tsx +30 -0
- package/src/components/ui/table.tsx +132 -0
- package/src/components/ui/tabs.tsx +77 -0
- package/src/components/ui/textarea.tsx +24 -0
- package/src/components/ui/time-picker.tsx +221 -0
- package/src/components/ui/toggle-group.tsx +86 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +79 -0
- package/src/components/ui/translation-input.tsx +126 -0
- package/src/components/ui/user-avatar.tsx +74 -0
- package/src/css-modules.d.ts +4 -0
- package/src/hooks/use-command-palette.ts +28 -0
- package/src/hooks/use-mobile.ts +21 -0
- package/src/hooks/use-pagination.ts +81 -0
- package/src/hooks/use-picker-state.ts +44 -0
- package/src/lib/cva.ts +18 -0
- package/src/lib/time-math.ts +62 -0
- package/src/lib/utils.ts +14 -0
- package/src/styles/globals.css +36 -0
- package/src/styles.css +10 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
|
|
2
|
+
import { XIcon } from "@phosphor-icons/react";
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
import { Button } from "#/components/ui/button.tsx";
|
|
5
|
+
import { cn } from "#/lib/utils.ts";
|
|
6
|
+
|
|
7
|
+
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
|
|
8
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
|
|
12
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
|
|
16
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
|
|
20
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function DialogOverlay({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: DialogPrimitive.Backdrop.Props) {
|
|
27
|
+
return (
|
|
28
|
+
<DialogPrimitive.Backdrop
|
|
29
|
+
data-slot="dialog-overlay"
|
|
30
|
+
className={cn(
|
|
31
|
+
// `bg-black/10` carries the dimming on light pages; on a near-black
|
|
32
|
+
// dark page the blur alone does the work, so deepen the scrim there.
|
|
33
|
+
"fixed inset-0 isolate z-50 bg-black/10 transition-opacity duration-150 supports-backdrop-filter:backdrop-blur-xs data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none dark:bg-black/40",
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// `xl` is a documented extra beyond the kit's `sm | default | lg` scale for
|
|
42
|
+
// media-heavy dialogs (gallery lightbox, annuaire match review).
|
|
43
|
+
type DialogContentSize = "sm" | "default" | "lg" | "xl";
|
|
44
|
+
|
|
45
|
+
function DialogContent({
|
|
46
|
+
className,
|
|
47
|
+
children,
|
|
48
|
+
size = "sm",
|
|
49
|
+
showCloseButton = true,
|
|
50
|
+
closeButtonLabel = "Close",
|
|
51
|
+
overlayClassName,
|
|
52
|
+
...props
|
|
53
|
+
}: DialogPrimitive.Popup.Props & {
|
|
54
|
+
size?: DialogContentSize;
|
|
55
|
+
showCloseButton?: boolean;
|
|
56
|
+
closeButtonLabel?: string;
|
|
57
|
+
overlayClassName?: string;
|
|
58
|
+
}) {
|
|
59
|
+
return (
|
|
60
|
+
<DialogPortal>
|
|
61
|
+
<DialogOverlay className={overlayClassName} />
|
|
62
|
+
<DialogPrimitive.Popup
|
|
63
|
+
data-slot="dialog-content"
|
|
64
|
+
data-size={size}
|
|
65
|
+
className={cn(
|
|
66
|
+
// The default `sm` reproduces the historical `sm:max-w-sm` width;
|
|
67
|
+
// sizes are styled off `data-size`, like `AlertDialogContent`.
|
|
68
|
+
"fixed top-1/2 left-1/2 z-50 grid max-h-[calc(100dvh-2rem)] w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 transition duration-150 ease-out outline-none data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none data-[size=default]:sm:max-w-md data-[size=lg]:sm:max-w-lg data-[size=sm]:sm:max-w-sm data-[size=xl]:sm:max-w-xl",
|
|
69
|
+
className,
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
>
|
|
73
|
+
{children}
|
|
74
|
+
{showCloseButton && (
|
|
75
|
+
<DialogPrimitive.Close
|
|
76
|
+
data-slot="dialog-close-button"
|
|
77
|
+
render={
|
|
78
|
+
<Button
|
|
79
|
+
variant="ghost"
|
|
80
|
+
className="absolute top-2 right-2"
|
|
81
|
+
size="icon-sm"
|
|
82
|
+
/>
|
|
83
|
+
}
|
|
84
|
+
>
|
|
85
|
+
<XIcon />
|
|
86
|
+
<span className="sr-only">{closeButtonLabel}</span>
|
|
87
|
+
</DialogPrimitive.Close>
|
|
88
|
+
)}
|
|
89
|
+
</DialogPrimitive.Popup>
|
|
90
|
+
</DialogPortal>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
data-slot="dialog-header"
|
|
98
|
+
// `pr-8` reserves room so the title never runs under the X button.
|
|
99
|
+
className={cn("flex flex-col gap-2 pr-8", className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function DialogFooter({
|
|
106
|
+
className,
|
|
107
|
+
closeLabel,
|
|
108
|
+
children,
|
|
109
|
+
...props
|
|
110
|
+
}: React.ComponentProps<"div"> & {
|
|
111
|
+
closeLabel?: string;
|
|
112
|
+
}) {
|
|
113
|
+
return (
|
|
114
|
+
<div
|
|
115
|
+
data-slot="dialog-footer"
|
|
116
|
+
className={cn(
|
|
117
|
+
"-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",
|
|
118
|
+
className,
|
|
119
|
+
)}
|
|
120
|
+
{...props}
|
|
121
|
+
>
|
|
122
|
+
{closeLabel !== undefined && (
|
|
123
|
+
<DialogPrimitive.Close
|
|
124
|
+
data-slot="dialog-footer-close"
|
|
125
|
+
render={<Button variant="outline" />}
|
|
126
|
+
>
|
|
127
|
+
{closeLabel}
|
|
128
|
+
</DialogPrimitive.Close>
|
|
129
|
+
)}
|
|
130
|
+
{children}
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
|
|
136
|
+
return (
|
|
137
|
+
<DialogPrimitive.Title
|
|
138
|
+
data-slot="dialog-title"
|
|
139
|
+
className={cn(
|
|
140
|
+
"text-base leading-none font-medium text-foreground",
|
|
141
|
+
className,
|
|
142
|
+
)}
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function DialogDescription({
|
|
149
|
+
className,
|
|
150
|
+
...props
|
|
151
|
+
}: DialogPrimitive.Description.Props) {
|
|
152
|
+
return (
|
|
153
|
+
<DialogPrimitive.Description
|
|
154
|
+
data-slot="dialog-description"
|
|
155
|
+
className={cn(
|
|
156
|
+
"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-4 *:[a]:hover:text-foreground",
|
|
157
|
+
className,
|
|
158
|
+
)}
|
|
159
|
+
{...props}
|
|
160
|
+
/>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
Dialog,
|
|
166
|
+
DialogClose,
|
|
167
|
+
DialogContent,
|
|
168
|
+
type DialogContentSize,
|
|
169
|
+
DialogDescription,
|
|
170
|
+
DialogFooter,
|
|
171
|
+
DialogHeader,
|
|
172
|
+
DialogOverlay,
|
|
173
|
+
DialogPortal,
|
|
174
|
+
DialogTitle,
|
|
175
|
+
DialogTrigger,
|
|
176
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export of Base UI's reading-direction context.
|
|
3
|
+
*
|
|
4
|
+
* `DirectionProvider` only informs Base UI's logical positioning and keyboard
|
|
5
|
+
* navigation (popup sides like `inline-start`, arrow-key orientation in
|
|
6
|
+
* menus). It does not set the `dir` attribute on the DOM: text alignment,
|
|
7
|
+
* visual ordering, and portaled popups (rendered under `document.body`) still
|
|
8
|
+
* follow the closest DOM `dir` ancestor. When localizing to a right-to-left
|
|
9
|
+
* language, set `dir="rtl"` on `<html>` alongside the provider.
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
DirectionProvider,
|
|
13
|
+
type TextDirection as Direction,
|
|
14
|
+
useDirection,
|
|
15
|
+
} from "@base-ui/react/direction-provider";
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { XIcon } from "@phosphor-icons/react";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
import { Drawer as DrawerPrimitive } from "vaul";
|
|
4
|
+
|
|
5
|
+
import { Button } from "#/components/ui/button.tsx";
|
|
6
|
+
import { cn } from "#/lib/utils.ts";
|
|
7
|
+
|
|
8
|
+
// Unlike the Base UI overlays in this kit, the drawer is Radix-based (vaul):
|
|
9
|
+
// compose with `asChild` (not `render`) and style against `data-state`.
|
|
10
|
+
|
|
11
|
+
function Drawer({
|
|
12
|
+
...props
|
|
13
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
|
14
|
+
return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function DrawerTrigger({
|
|
18
|
+
...props
|
|
19
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
|
|
20
|
+
return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function DrawerPortal({
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
|
|
26
|
+
return <DrawerPrimitive.Portal {...props} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function DrawerClose({
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Close>) {
|
|
32
|
+
return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function DrawerOverlay({
|
|
36
|
+
className,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
|
|
39
|
+
return (
|
|
40
|
+
<DrawerPrimitive.Overlay
|
|
41
|
+
data-slot="drawer-overlay"
|
|
42
|
+
className={cn(
|
|
43
|
+
// `bg-black/10` carries the dimming on light pages; on a near-black
|
|
44
|
+
// dark page the blur alone does the work, so deepen the scrim there.
|
|
45
|
+
"fixed inset-0 isolate z-50 bg-black/10 duration-150 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 motion-reduce:animate-none dark:bg-black/40",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function DrawerHandle({ className, ...props }: React.ComponentProps<"div">) {
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
data-slot="drawer-handle"
|
|
57
|
+
aria-hidden="true"
|
|
58
|
+
className={cn(
|
|
59
|
+
"mx-auto mt-4 h-1 w-24 shrink-0 rounded-full bg-muted-foreground/20",
|
|
60
|
+
className,
|
|
61
|
+
)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function DrawerContent({
|
|
68
|
+
className,
|
|
69
|
+
children,
|
|
70
|
+
showHandle = true,
|
|
71
|
+
showCloseButton = true,
|
|
72
|
+
closeButtonLabel = "Close",
|
|
73
|
+
overlayClassName,
|
|
74
|
+
...props
|
|
75
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Content> & {
|
|
76
|
+
showHandle?: boolean;
|
|
77
|
+
/** Renders an X button on `left`/`right` drawers, which have no grab handle. */
|
|
78
|
+
showCloseButton?: boolean;
|
|
79
|
+
closeButtonLabel?: string;
|
|
80
|
+
overlayClassName?: string;
|
|
81
|
+
}) {
|
|
82
|
+
return (
|
|
83
|
+
<DrawerPortal>
|
|
84
|
+
<DrawerOverlay className={overlayClassName} />
|
|
85
|
+
<DrawerPrimitive.Content
|
|
86
|
+
data-slot="drawer-content"
|
|
87
|
+
className={cn(
|
|
88
|
+
"group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground ring-1 ring-foreground/10 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=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl 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]:rounded-l-xl 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=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
|
89
|
+
className,
|
|
90
|
+
)}
|
|
91
|
+
{...props}
|
|
92
|
+
>
|
|
93
|
+
{showHandle && (
|
|
94
|
+
<DrawerHandle className="hidden group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
|
|
95
|
+
)}
|
|
96
|
+
{children}
|
|
97
|
+
{showCloseButton && (
|
|
98
|
+
<DrawerPrimitive.Close asChild>
|
|
99
|
+
<Button
|
|
100
|
+
data-slot="drawer-close-button"
|
|
101
|
+
variant="ghost"
|
|
102
|
+
size="icon-sm"
|
|
103
|
+
className="absolute top-2 right-2 hidden group-data-[vaul-drawer-direction=left]/drawer-content:inline-flex group-data-[vaul-drawer-direction=right]/drawer-content:inline-flex"
|
|
104
|
+
>
|
|
105
|
+
<XIcon />
|
|
106
|
+
<span className="sr-only">{closeButtonLabel}</span>
|
|
107
|
+
</Button>
|
|
108
|
+
</DrawerPrimitive.Close>
|
|
109
|
+
)}
|
|
110
|
+
</DrawerPrimitive.Content>
|
|
111
|
+
</DrawerPortal>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
data-slot="drawer-header"
|
|
119
|
+
className={cn(
|
|
120
|
+
// `pr-8` on side drawers reserves room so the title never runs under
|
|
121
|
+
// the X button; bottom/top drawers center on mobile, left-align on md+.
|
|
122
|
+
"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=left]/drawer-content:pr-8 group-data-[vaul-drawer-direction=right]/drawer-content:pr-8 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:group-data-[vaul-drawer-direction=bottom]/drawer-content:text-left md:group-data-[vaul-drawer-direction=top]/drawer-content:text-left",
|
|
123
|
+
className,
|
|
124
|
+
)}
|
|
125
|
+
{...props}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
131
|
+
return (
|
|
132
|
+
<div
|
|
133
|
+
data-slot="drawer-footer"
|
|
134
|
+
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
|
135
|
+
{...props}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function DrawerTitle({
|
|
141
|
+
className,
|
|
142
|
+
...props
|
|
143
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Title>) {
|
|
144
|
+
return (
|
|
145
|
+
<DrawerPrimitive.Title
|
|
146
|
+
data-slot="drawer-title"
|
|
147
|
+
className={cn("text-base font-medium text-foreground", className)}
|
|
148
|
+
{...props}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function DrawerDescription({
|
|
154
|
+
className,
|
|
155
|
+
...props
|
|
156
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
|
|
157
|
+
return (
|
|
158
|
+
<DrawerPrimitive.Description
|
|
159
|
+
data-slot="drawer-description"
|
|
160
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
161
|
+
{...props}
|
|
162
|
+
/>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export {
|
|
167
|
+
Drawer,
|
|
168
|
+
DrawerClose,
|
|
169
|
+
DrawerContent,
|
|
170
|
+
DrawerDescription,
|
|
171
|
+
DrawerFooter,
|
|
172
|
+
DrawerHandle,
|
|
173
|
+
DrawerHeader,
|
|
174
|
+
DrawerOverlay,
|
|
175
|
+
DrawerPortal,
|
|
176
|
+
DrawerTitle,
|
|
177
|
+
DrawerTrigger,
|
|
178
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { Menu as MenuPrimitive } from "@base-ui/react/menu";
|
|
2
|
+
import { CaretRightIcon, CheckIcon, CircleIcon } from "@phosphor-icons/react";
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
menuContentVariants,
|
|
6
|
+
menuIndicatorVariants,
|
|
7
|
+
menuItemVariants,
|
|
8
|
+
menuLabelVariants,
|
|
9
|
+
menuSeparatorVariants,
|
|
10
|
+
menuSubTriggerVariants,
|
|
11
|
+
} from "#/components/ui/menu-variants.ts";
|
|
12
|
+
import { Shortcut } from "#/components/ui/shortcut.tsx";
|
|
13
|
+
import { cn } from "#/lib/utils.ts";
|
|
14
|
+
|
|
15
|
+
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
|
|
16
|
+
return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
|
|
20
|
+
return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
|
|
24
|
+
return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function DropdownMenuContent({
|
|
28
|
+
align = "start",
|
|
29
|
+
alignOffset = 0,
|
|
30
|
+
side = "bottom",
|
|
31
|
+
sideOffset = 4,
|
|
32
|
+
className,
|
|
33
|
+
...props
|
|
34
|
+
}: MenuPrimitive.Popup.Props &
|
|
35
|
+
Pick<
|
|
36
|
+
MenuPrimitive.Positioner.Props,
|
|
37
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
38
|
+
>) {
|
|
39
|
+
return (
|
|
40
|
+
<MenuPrimitive.Portal>
|
|
41
|
+
<MenuPrimitive.Positioner
|
|
42
|
+
className="isolate z-50 outline-none"
|
|
43
|
+
align={align}
|
|
44
|
+
alignOffset={alignOffset}
|
|
45
|
+
side={side}
|
|
46
|
+
sideOffset={sideOffset}
|
|
47
|
+
>
|
|
48
|
+
<MenuPrimitive.Popup
|
|
49
|
+
data-slot="dropdown-menu-content"
|
|
50
|
+
className={cn(menuContentVariants(), className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
</MenuPrimitive.Positioner>
|
|
54
|
+
</MenuPrimitive.Portal>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
|
|
59
|
+
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Plain div, not Menu.GroupLabel: Base UI's GroupLabel throws when rendered
|
|
63
|
+
// outside a Menu.Group, and labels are routinely used standalone.
|
|
64
|
+
function DropdownMenuLabel({
|
|
65
|
+
className,
|
|
66
|
+
inset,
|
|
67
|
+
...props
|
|
68
|
+
}: React.ComponentProps<"div"> & {
|
|
69
|
+
inset?: boolean;
|
|
70
|
+
}) {
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
data-slot="dropdown-menu-label"
|
|
74
|
+
data-inset={inset}
|
|
75
|
+
className={cn(menuLabelVariants(), className)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function DropdownMenuItem({
|
|
82
|
+
className,
|
|
83
|
+
inset,
|
|
84
|
+
variant = "default",
|
|
85
|
+
...props
|
|
86
|
+
}: MenuPrimitive.Item.Props & {
|
|
87
|
+
inset?: boolean;
|
|
88
|
+
variant?: "default" | "destructive";
|
|
89
|
+
}) {
|
|
90
|
+
return (
|
|
91
|
+
<MenuPrimitive.Item
|
|
92
|
+
data-slot="dropdown-menu-item"
|
|
93
|
+
data-inset={inset}
|
|
94
|
+
data-variant={variant}
|
|
95
|
+
className={cn(menuItemVariants({ variant }), className)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
|
|
102
|
+
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function DropdownMenuSubTrigger({
|
|
106
|
+
className,
|
|
107
|
+
inset,
|
|
108
|
+
children,
|
|
109
|
+
...props
|
|
110
|
+
}: MenuPrimitive.SubmenuTrigger.Props & {
|
|
111
|
+
inset?: boolean;
|
|
112
|
+
}) {
|
|
113
|
+
return (
|
|
114
|
+
<MenuPrimitive.SubmenuTrigger
|
|
115
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
116
|
+
data-inset={inset}
|
|
117
|
+
className={cn(menuSubTriggerVariants(), className)}
|
|
118
|
+
{...props}
|
|
119
|
+
>
|
|
120
|
+
{children}
|
|
121
|
+
<CaretRightIcon className="ml-auto" />
|
|
122
|
+
</MenuPrimitive.SubmenuTrigger>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function DropdownMenuSubContent({
|
|
127
|
+
alignOffset = -3,
|
|
128
|
+
side = "inline-end",
|
|
129
|
+
sideOffset = 0,
|
|
130
|
+
className,
|
|
131
|
+
...props
|
|
132
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
133
|
+
return (
|
|
134
|
+
<DropdownMenuContent
|
|
135
|
+
data-slot="dropdown-menu-sub-content"
|
|
136
|
+
className={cn("shadow-lg", className)}
|
|
137
|
+
alignOffset={alignOffset}
|
|
138
|
+
side={side}
|
|
139
|
+
sideOffset={sideOffset}
|
|
140
|
+
{...props}
|
|
141
|
+
/>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function DropdownMenuCheckboxItem({
|
|
146
|
+
className,
|
|
147
|
+
children,
|
|
148
|
+
checked,
|
|
149
|
+
inset,
|
|
150
|
+
...props
|
|
151
|
+
}: MenuPrimitive.CheckboxItem.Props & {
|
|
152
|
+
inset?: boolean;
|
|
153
|
+
}) {
|
|
154
|
+
return (
|
|
155
|
+
<MenuPrimitive.CheckboxItem
|
|
156
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
157
|
+
data-inset={inset}
|
|
158
|
+
className={cn(menuItemVariants({ indicator: "end" }), className)}
|
|
159
|
+
checked={checked}
|
|
160
|
+
{...props}
|
|
161
|
+
>
|
|
162
|
+
<span
|
|
163
|
+
className={menuIndicatorVariants()}
|
|
164
|
+
data-slot="dropdown-menu-checkbox-item-indicator"
|
|
165
|
+
>
|
|
166
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
167
|
+
<CheckIcon />
|
|
168
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
169
|
+
</span>
|
|
170
|
+
{children}
|
|
171
|
+
</MenuPrimitive.CheckboxItem>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
|
|
176
|
+
return (
|
|
177
|
+
<MenuPrimitive.RadioGroup
|
|
178
|
+
data-slot="dropdown-menu-radio-group"
|
|
179
|
+
{...props}
|
|
180
|
+
/>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function DropdownMenuRadioItem({
|
|
185
|
+
className,
|
|
186
|
+
children,
|
|
187
|
+
inset,
|
|
188
|
+
...props
|
|
189
|
+
}: MenuPrimitive.RadioItem.Props & {
|
|
190
|
+
inset?: boolean;
|
|
191
|
+
}) {
|
|
192
|
+
return (
|
|
193
|
+
<MenuPrimitive.RadioItem
|
|
194
|
+
data-slot="dropdown-menu-radio-item"
|
|
195
|
+
data-inset={inset}
|
|
196
|
+
className={cn(menuItemVariants({ indicator: "end" }), className)}
|
|
197
|
+
{...props}
|
|
198
|
+
>
|
|
199
|
+
<span
|
|
200
|
+
className={menuIndicatorVariants()}
|
|
201
|
+
data-slot="dropdown-menu-radio-item-indicator"
|
|
202
|
+
>
|
|
203
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
204
|
+
<CircleIcon weight="fill" className="size-2" />
|
|
205
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
206
|
+
</span>
|
|
207
|
+
{children}
|
|
208
|
+
</MenuPrimitive.RadioItem>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function DropdownMenuSeparator({
|
|
213
|
+
className,
|
|
214
|
+
...props
|
|
215
|
+
}: MenuPrimitive.Separator.Props) {
|
|
216
|
+
return (
|
|
217
|
+
<MenuPrimitive.Separator
|
|
218
|
+
data-slot="dropdown-menu-separator"
|
|
219
|
+
className={cn(menuSeparatorVariants(), className)}
|
|
220
|
+
{...props}
|
|
221
|
+
/>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function DropdownMenuShortcut({
|
|
226
|
+
...props
|
|
227
|
+
}: React.ComponentProps<typeof Shortcut>) {
|
|
228
|
+
return <Shortcut data-slot="dropdown-menu-shortcut" {...props} />;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export {
|
|
232
|
+
DropdownMenu,
|
|
233
|
+
DropdownMenuCheckboxItem,
|
|
234
|
+
DropdownMenuContent,
|
|
235
|
+
DropdownMenuGroup,
|
|
236
|
+
DropdownMenuItem,
|
|
237
|
+
DropdownMenuLabel,
|
|
238
|
+
DropdownMenuPortal,
|
|
239
|
+
DropdownMenuRadioGroup,
|
|
240
|
+
DropdownMenuRadioItem,
|
|
241
|
+
DropdownMenuSeparator,
|
|
242
|
+
DropdownMenuShortcut,
|
|
243
|
+
DropdownMenuSub,
|
|
244
|
+
DropdownMenuSubContent,
|
|
245
|
+
DropdownMenuSubTrigger,
|
|
246
|
+
DropdownMenuTrigger,
|
|
247
|
+
};
|