@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,216 @@
|
|
|
1
|
+
import { Select as SelectPrimitive } from "@base-ui/react/select";
|
|
2
|
+
import { CaretDownIcon, CaretUpIcon, CheckIcon } from "@phosphor-icons/react";
|
|
3
|
+
import { cn } from "#/lib/utils.ts";
|
|
4
|
+
|
|
5
|
+
/* Pass `items` (value → label) so the trigger renders the selected item's
|
|
6
|
+
* label; without it Base UI's Value falls back to the raw value string. */
|
|
7
|
+
function Select<Value, Multiple extends boolean | undefined = false>(
|
|
8
|
+
props: SelectPrimitive.Root.Props<Value, Multiple>,
|
|
9
|
+
) {
|
|
10
|
+
return <SelectPrimitive.Root data-slot="select" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
|
|
14
|
+
return (
|
|
15
|
+
<SelectPrimitive.Group
|
|
16
|
+
data-slot="select-group"
|
|
17
|
+
className={cn("scroll-my-1", className)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
|
|
24
|
+
return (
|
|
25
|
+
<SelectPrimitive.Value
|
|
26
|
+
data-slot="select-value"
|
|
27
|
+
className={cn("flex flex-1 text-left", className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function SelectTrigger({
|
|
34
|
+
className,
|
|
35
|
+
size = "default",
|
|
36
|
+
children,
|
|
37
|
+
...props
|
|
38
|
+
}: SelectPrimitive.Trigger.Props & {
|
|
39
|
+
size?: "sm" | "default";
|
|
40
|
+
}) {
|
|
41
|
+
return (
|
|
42
|
+
<SelectPrimitive.Trigger
|
|
43
|
+
data-slot="select-trigger"
|
|
44
|
+
data-size={size}
|
|
45
|
+
className={cn(
|
|
46
|
+
"flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none hover:bg-muted focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),12px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
47
|
+
className,
|
|
48
|
+
)}
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
<SelectPrimitive.Icon
|
|
53
|
+
render={
|
|
54
|
+
<CaretDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
|
55
|
+
}
|
|
56
|
+
/>
|
|
57
|
+
</SelectPrimitive.Trigger>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* With the default `alignItemWithTrigger` the popup overlaps the trigger
|
|
62
|
+
* macOS-style and intentionally suppresses the open/close animation; pass
|
|
63
|
+
* `alignItemWithTrigger={false}` to get the popover-style zoom/fade. */
|
|
64
|
+
function SelectContent({
|
|
65
|
+
className,
|
|
66
|
+
children,
|
|
67
|
+
side = "bottom",
|
|
68
|
+
sideOffset = 4,
|
|
69
|
+
align = "center",
|
|
70
|
+
alignOffset = 0,
|
|
71
|
+
alignItemWithTrigger = true,
|
|
72
|
+
collisionPadding,
|
|
73
|
+
...props
|
|
74
|
+
}: SelectPrimitive.Popup.Props &
|
|
75
|
+
Pick<
|
|
76
|
+
SelectPrimitive.Positioner.Props,
|
|
77
|
+
| "align"
|
|
78
|
+
| "alignOffset"
|
|
79
|
+
| "side"
|
|
80
|
+
| "sideOffset"
|
|
81
|
+
| "alignItemWithTrigger"
|
|
82
|
+
| "collisionPadding"
|
|
83
|
+
>) {
|
|
84
|
+
return (
|
|
85
|
+
<SelectPrimitive.Portal>
|
|
86
|
+
<SelectPrimitive.Positioner
|
|
87
|
+
side={side}
|
|
88
|
+
sideOffset={sideOffset}
|
|
89
|
+
align={align}
|
|
90
|
+
alignOffset={alignOffset}
|
|
91
|
+
alignItemWithTrigger={alignItemWithTrigger}
|
|
92
|
+
collisionPadding={collisionPadding}
|
|
93
|
+
className="isolate z-50"
|
|
94
|
+
>
|
|
95
|
+
<SelectPrimitive.Popup
|
|
96
|
+
data-slot="select-content"
|
|
97
|
+
data-align-trigger={alignItemWithTrigger}
|
|
98
|
+
className={cn(
|
|
99
|
+
"relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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",
|
|
100
|
+
className,
|
|
101
|
+
)}
|
|
102
|
+
{...props}
|
|
103
|
+
>
|
|
104
|
+
<SelectScrollUpButton />
|
|
105
|
+
<SelectPrimitive.List className="p-1">
|
|
106
|
+
{children}
|
|
107
|
+
</SelectPrimitive.List>
|
|
108
|
+
<SelectScrollDownButton />
|
|
109
|
+
</SelectPrimitive.Popup>
|
|
110
|
+
</SelectPrimitive.Positioner>
|
|
111
|
+
</SelectPrimitive.Portal>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function SelectLabel({
|
|
116
|
+
className,
|
|
117
|
+
...props
|
|
118
|
+
}: SelectPrimitive.GroupLabel.Props) {
|
|
119
|
+
return (
|
|
120
|
+
<SelectPrimitive.GroupLabel
|
|
121
|
+
data-slot="select-label"
|
|
122
|
+
className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
|
|
123
|
+
{...props}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function SelectItem({
|
|
129
|
+
className,
|
|
130
|
+
children,
|
|
131
|
+
...props
|
|
132
|
+
}: SelectPrimitive.Item.Props) {
|
|
133
|
+
return (
|
|
134
|
+
<SelectPrimitive.Item
|
|
135
|
+
data-slot="select-item"
|
|
136
|
+
className={cn(
|
|
137
|
+
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
138
|
+
className,
|
|
139
|
+
)}
|
|
140
|
+
{...props}
|
|
141
|
+
>
|
|
142
|
+
<SelectPrimitive.ItemText className="flex flex-1 shrink-0 gap-2 whitespace-nowrap">
|
|
143
|
+
{children}
|
|
144
|
+
</SelectPrimitive.ItemText>
|
|
145
|
+
<SelectPrimitive.ItemIndicator
|
|
146
|
+
render={
|
|
147
|
+
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
|
|
148
|
+
}
|
|
149
|
+
>
|
|
150
|
+
<CheckIcon className="pointer-events-none" />
|
|
151
|
+
</SelectPrimitive.ItemIndicator>
|
|
152
|
+
</SelectPrimitive.Item>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function SelectSeparator({
|
|
157
|
+
className,
|
|
158
|
+
...props
|
|
159
|
+
}: SelectPrimitive.Separator.Props) {
|
|
160
|
+
return (
|
|
161
|
+
<SelectPrimitive.Separator
|
|
162
|
+
data-slot="select-separator"
|
|
163
|
+
className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
|
|
164
|
+
{...props}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function SelectScrollUpButton({
|
|
170
|
+
className,
|
|
171
|
+
...props
|
|
172
|
+
}: SelectPrimitive.ScrollUpArrow.Props) {
|
|
173
|
+
return (
|
|
174
|
+
<SelectPrimitive.ScrollUpArrow
|
|
175
|
+
data-slot="select-scroll-up-button"
|
|
176
|
+
className={cn(
|
|
177
|
+
"top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
178
|
+
className,
|
|
179
|
+
)}
|
|
180
|
+
{...props}
|
|
181
|
+
>
|
|
182
|
+
<CaretUpIcon />
|
|
183
|
+
</SelectPrimitive.ScrollUpArrow>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function SelectScrollDownButton({
|
|
188
|
+
className,
|
|
189
|
+
...props
|
|
190
|
+
}: SelectPrimitive.ScrollDownArrow.Props) {
|
|
191
|
+
return (
|
|
192
|
+
<SelectPrimitive.ScrollDownArrow
|
|
193
|
+
data-slot="select-scroll-down-button"
|
|
194
|
+
className={cn(
|
|
195
|
+
"bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
196
|
+
className,
|
|
197
|
+
)}
|
|
198
|
+
{...props}
|
|
199
|
+
>
|
|
200
|
+
<CaretDownIcon />
|
|
201
|
+
</SelectPrimitive.ScrollDownArrow>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export {
|
|
206
|
+
Select,
|
|
207
|
+
SelectContent,
|
|
208
|
+
SelectGroup,
|
|
209
|
+
SelectItem,
|
|
210
|
+
SelectLabel,
|
|
211
|
+
SelectScrollDownButton,
|
|
212
|
+
SelectScrollUpButton,
|
|
213
|
+
SelectSeparator,
|
|
214
|
+
SelectTrigger,
|
|
215
|
+
SelectValue,
|
|
216
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
|
|
2
|
+
|
|
3
|
+
import { cn } from "#/lib/utils.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Visual divider. Pass `children` for a labeled divider (the auth-screen
|
|
7
|
+
* "OU" pattern): the element keeps its separator semantics while two
|
|
8
|
+
* decorative lines flank the label, so it works on any background - no
|
|
9
|
+
* `bg-background` punch-through needed.
|
|
10
|
+
*
|
|
11
|
+
* Vertical separators `self-stretch` to fill the row. To shorten one inside
|
|
12
|
+
* a taller row, pass a height plus `my-auto` (e.g. `className="my-auto h-4"`):
|
|
13
|
+
* a definite height makes `self-stretch` start-align, and the auto margins
|
|
14
|
+
* re-center it.
|
|
15
|
+
*/
|
|
16
|
+
function Separator({
|
|
17
|
+
className,
|
|
18
|
+
orientation = "horizontal",
|
|
19
|
+
children,
|
|
20
|
+
...props
|
|
21
|
+
}: SeparatorPrimitive.Props) {
|
|
22
|
+
if (children == null) {
|
|
23
|
+
return (
|
|
24
|
+
<SeparatorPrimitive
|
|
25
|
+
data-slot="separator"
|
|
26
|
+
orientation={orientation}
|
|
27
|
+
className={cn(
|
|
28
|
+
"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return (
|
|
36
|
+
<SeparatorPrimitive
|
|
37
|
+
data-slot="separator"
|
|
38
|
+
orientation={orientation}
|
|
39
|
+
className={cn(
|
|
40
|
+
"flex shrink-0 items-center gap-2 text-sm text-muted-foreground data-[orientation=horizontal]:w-full data-[orientation=vertical]:flex-col data-[orientation=vertical]:self-stretch",
|
|
41
|
+
className,
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
<SeparatorLine orientation={orientation} />
|
|
46
|
+
<span data-slot="separator-label">{children}</span>
|
|
47
|
+
<SeparatorLine orientation={orientation} />
|
|
48
|
+
</SeparatorPrimitive>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function SeparatorLine({
|
|
53
|
+
orientation,
|
|
54
|
+
}: {
|
|
55
|
+
orientation: NonNullable<SeparatorPrimitive.Props["orientation"]>;
|
|
56
|
+
}) {
|
|
57
|
+
return (
|
|
58
|
+
<span
|
|
59
|
+
aria-hidden
|
|
60
|
+
data-slot="separator-line"
|
|
61
|
+
className={cn(
|
|
62
|
+
"flex-1 bg-border",
|
|
63
|
+
orientation === "vertical" ? "w-px" : "h-px",
|
|
64
|
+
)}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { Separator };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Dialog as SheetPrimitive } 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 Sheet({ ...props }: SheetPrimitive.Root.Props) {
|
|
8
|
+
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {
|
|
12
|
+
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function SheetClose({ ...props }: SheetPrimitive.Close.Props) {
|
|
16
|
+
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {
|
|
20
|
+
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
|
|
24
|
+
return (
|
|
25
|
+
<SheetPrimitive.Backdrop
|
|
26
|
+
data-slot="sheet-overlay"
|
|
27
|
+
className={cn(
|
|
28
|
+
// `bg-black/10` carries the dimming on light pages; on a near-black
|
|
29
|
+
// dark page the blur alone does the work, so deepen the scrim there.
|
|
30
|
+
"fixed inset-0 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",
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// `xl` and `full` are documented extras beyond the kit's `sm | default | lg`
|
|
39
|
+
// scale for wide editing panels and full-bleed surfaces.
|
|
40
|
+
type SheetContentSize = "sm" | "default" | "lg" | "xl" | "full";
|
|
41
|
+
|
|
42
|
+
function SheetContent({
|
|
43
|
+
className,
|
|
44
|
+
children,
|
|
45
|
+
side = "right",
|
|
46
|
+
size = "default",
|
|
47
|
+
showCloseButton = true,
|
|
48
|
+
closeButtonLabel = "Close",
|
|
49
|
+
...props
|
|
50
|
+
}: SheetPrimitive.Popup.Props & {
|
|
51
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
52
|
+
size?: SheetContentSize;
|
|
53
|
+
showCloseButton?: boolean;
|
|
54
|
+
closeButtonLabel?: string;
|
|
55
|
+
}) {
|
|
56
|
+
return (
|
|
57
|
+
<SheetPortal>
|
|
58
|
+
<SheetOverlay />
|
|
59
|
+
<SheetPrimitive.Popup
|
|
60
|
+
data-slot="sheet-content"
|
|
61
|
+
data-side={side}
|
|
62
|
+
data-size={size}
|
|
63
|
+
className={cn(
|
|
64
|
+
"fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:rounded-t-xl data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:rounded-r-xl data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:rounded-l-xl data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:rounded-b-xl data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem]",
|
|
65
|
+
// `size` only constrains the left/right panels (top/bottom are
|
|
66
|
+
// content-height, full-bleed). Styled off `data-size` (like
|
|
67
|
+
// `AlertDialogContent`); the default reproduces the historical
|
|
68
|
+
// `sm:max-w-sm` width.
|
|
69
|
+
"data-[size=default]:data-[side=left]:sm:max-w-sm data-[size=default]:data-[side=right]:sm:max-w-sm data-[size=full]:data-[side=left]:w-full data-[size=full]:data-[side=left]:max-w-none data-[size=full]:data-[side=right]:w-full data-[size=full]:data-[side=right]:max-w-none data-[size=lg]:data-[side=left]:sm:max-w-md data-[size=lg]:data-[side=right]:sm:max-w-md data-[size=sm]:data-[side=left]:sm:max-w-xs data-[size=sm]:data-[side=right]:sm:max-w-xs data-[size=xl]:data-[side=left]:sm:max-w-lg data-[size=xl]:data-[side=right]:sm:max-w-lg",
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
{...props}
|
|
73
|
+
>
|
|
74
|
+
{children}
|
|
75
|
+
{showCloseButton && (
|
|
76
|
+
<SheetPrimitive.Close
|
|
77
|
+
data-slot="sheet-close-button"
|
|
78
|
+
render={
|
|
79
|
+
<Button
|
|
80
|
+
variant="ghost"
|
|
81
|
+
className="absolute top-3 right-3"
|
|
82
|
+
size="icon-sm"
|
|
83
|
+
/>
|
|
84
|
+
}
|
|
85
|
+
>
|
|
86
|
+
<XIcon />
|
|
87
|
+
<span className="sr-only">{closeButtonLabel}</span>
|
|
88
|
+
</SheetPrimitive.Close>
|
|
89
|
+
)}
|
|
90
|
+
</SheetPrimitive.Popup>
|
|
91
|
+
</SheetPortal>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
data-slot="sheet-header"
|
|
99
|
+
className={cn("flex flex-col gap-0.5 p-4", className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
106
|
+
return (
|
|
107
|
+
<div
|
|
108
|
+
data-slot="sheet-footer"
|
|
109
|
+
className={cn(
|
|
110
|
+
// Extra bottom padding keeps actions above the iOS home indicator.
|
|
111
|
+
"mt-auto flex flex-col gap-2 p-4 pb-[calc(1rem+env(safe-area-inset-bottom))]",
|
|
112
|
+
className,
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {
|
|
120
|
+
return (
|
|
121
|
+
<SheetPrimitive.Title
|
|
122
|
+
data-slot="sheet-title"
|
|
123
|
+
className={cn("text-base font-medium text-foreground", className)}
|
|
124
|
+
{...props}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function SheetDescription({
|
|
130
|
+
className,
|
|
131
|
+
...props
|
|
132
|
+
}: SheetPrimitive.Description.Props) {
|
|
133
|
+
return (
|
|
134
|
+
<SheetPrimitive.Description
|
|
135
|
+
data-slot="sheet-description"
|
|
136
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
137
|
+
{...props}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export {
|
|
143
|
+
Sheet,
|
|
144
|
+
SheetClose,
|
|
145
|
+
SheetContent,
|
|
146
|
+
type SheetContentSize,
|
|
147
|
+
SheetDescription,
|
|
148
|
+
SheetFooter,
|
|
149
|
+
SheetHeader,
|
|
150
|
+
SheetOverlay,
|
|
151
|
+
SheetPortal,
|
|
152
|
+
SheetTitle,
|
|
153
|
+
SheetTrigger,
|
|
154
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import { Kbd, KbdGroup } from "#/components/ui/kbd.tsx";
|
|
3
|
+
import { menuShortcutVariants } from "#/components/ui/menu-variants.ts";
|
|
4
|
+
import { cn } from "#/lib/utils.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Right-aligned keyboard hint for menu rows, shared by
|
|
8
|
+
* `DropdownMenuShortcut`, `ContextMenuShortcut`, `MenubarShortcut` and
|
|
9
|
+
* `CommandShortcut`. Pass plain children ("⌘K") for the classic text hint, or
|
|
10
|
+
* `keys` to render each key as a small `Kbd` chip instead.
|
|
11
|
+
*/
|
|
12
|
+
function Shortcut({
|
|
13
|
+
className,
|
|
14
|
+
keys,
|
|
15
|
+
children,
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<"span"> & {
|
|
18
|
+
keys?: ReadonlyArray<string>;
|
|
19
|
+
}) {
|
|
20
|
+
return (
|
|
21
|
+
<span
|
|
22
|
+
data-slot="shortcut"
|
|
23
|
+
className={cn(menuShortcutVariants(), className)}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
{keys === undefined ? (
|
|
27
|
+
children
|
|
28
|
+
) : (
|
|
29
|
+
<KbdGroup>
|
|
30
|
+
{keys.map((key) => (
|
|
31
|
+
<Kbd key={key} size="sm">
|
|
32
|
+
{key}
|
|
33
|
+
</Kbd>
|
|
34
|
+
))}
|
|
35
|
+
</KbdGroup>
|
|
36
|
+
)}
|
|
37
|
+
</span>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { Shortcut };
|