@octanejs/shadcn 0.0.1
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/LICENSE +21 -0
- package/README.md +43 -0
- package/package.json +63 -0
- package/src/hooks/use-mobile.ts +23 -0
- package/src/index.ts +263 -0
- package/src/lib/types.ts +10 -0
- package/src/lib/utils.ts +8 -0
- package/src/styles/theme.css +104 -0
- package/src/ui/accordion.tsrx +72 -0
- package/src/ui/alert-dialog.tsrx +161 -0
- package/src/ui/alert.tsrx +69 -0
- package/src/ui/aspect-ratio.tsrx +10 -0
- package/src/ui/avatar.tsrx +79 -0
- package/src/ui/badge.tsrx +50 -0
- package/src/ui/breadcrumb.tsrx +89 -0
- package/src/ui/button.tsrx +71 -0
- package/src/ui/card.tsrx +75 -0
- package/src/ui/checkbox.tsrx +31 -0
- package/src/ui/collapsible.tsrx +21 -0
- package/src/ui/context-menu.tsrx +179 -0
- package/src/ui/dialog.tsrx +133 -0
- package/src/ui/dropdown-menu.tsrx +189 -0
- package/src/ui/empty.tsrx +86 -0
- package/src/ui/field.tsrx +188 -0
- package/src/ui/hover-card.tsrx +48 -0
- package/src/ui/input.tsrx +18 -0
- package/src/ui/item.tsrx +171 -0
- package/src/ui/kbd.tsrx +26 -0
- package/src/ui/label.tsrx +21 -0
- package/src/ui/menubar.tsrx +198 -0
- package/src/ui/native-select.tsrx +55 -0
- package/src/ui/navigation-menu.tsrx +116 -0
- package/src/ui/pagination.tsrx +113 -0
- package/src/ui/popover.tsrx +73 -0
- package/src/ui/progress.tsrx +27 -0
- package/src/ui/radio-group.tsrx +37 -0
- package/src/ui/scroll-area.tsrx +43 -0
- package/src/ui/select.tsrx +165 -0
- package/src/ui/separator.tsrx +30 -0
- package/src/ui/sheet.tsrx +114 -0
- package/src/ui/sidebar.tsrx +661 -0
- package/src/ui/skeleton.tsrx +14 -0
- package/src/ui/slider.tsrx +69 -0
- package/src/ui/sonner.tsrx +45 -0
- package/src/ui/spinner.tsrx +23 -0
- package/src/ui/switch.tsrx +30 -0
- package/src/ui/table.tsrx +80 -0
- package/src/ui/tabs.tsrx +69 -0
- package/src/ui/textarea.tsrx +20 -0
- package/src/ui/toggle-group.tsrx +100 -0
- package/src/ui/toggle.tsrx +47 -0
- package/src/ui/tooltip.tsrx +65 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// ContextMenu — maintainer-supplied default-Tailwind flavor, class strings
|
|
2
|
+
// verbatim. Octane adaptations: "use client" dropped, lucide-react →
|
|
3
|
+
// @octanejs/lucide, radix-ui → @octanejs/radix. Content composes its Portal
|
|
4
|
+
// subtree with createElement (radix's Portal slots its child); parts that
|
|
5
|
+
// interleave consumer children with sibling descriptors use the
|
|
6
|
+
// {props.children} lowering in template JSX.
|
|
7
|
+
import { createElement, type OctaneNode } from 'octane';
|
|
8
|
+
import { ContextMenu as ContextMenuPrimitive } from '@octanejs/radix';
|
|
9
|
+
import { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';
|
|
10
|
+
|
|
11
|
+
import { cn } from '../lib/utils';
|
|
12
|
+
|
|
13
|
+
type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
export function ContextMenu(props: Record<string, unknown>) @{
|
|
16
|
+
<ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function ContextMenuTrigger({ className, ...props }: Props) @{
|
|
20
|
+
<ContextMenuPrimitive.Trigger
|
|
21
|
+
data-slot="context-menu-trigger"
|
|
22
|
+
className={cn('select-none', className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function ContextMenuGroup(props: Record<string, unknown>) @{
|
|
28
|
+
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function ContextMenuPortal(props: Record<string, unknown>) @{
|
|
32
|
+
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function ContextMenuSub(props: Record<string, unknown>) @{
|
|
36
|
+
<ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function ContextMenuRadioGroup(props: Record<string, unknown>) @{
|
|
40
|
+
<ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} />
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function ContextMenuContent({ className, ...props }: Props & {
|
|
44
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
45
|
+
}) {
|
|
46
|
+
return createElement(
|
|
47
|
+
ContextMenuPrimitive.Portal,
|
|
48
|
+
null,
|
|
49
|
+
createElement(ContextMenuPrimitive.Content, {
|
|
50
|
+
'data-slot': 'context-menu-content',
|
|
51
|
+
className: cn(
|
|
52
|
+
'z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-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',
|
|
53
|
+
className,
|
|
54
|
+
),
|
|
55
|
+
...props,
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function ContextMenuItem({ className, inset, variant = 'default', ...props }: Props & {
|
|
61
|
+
inset?: boolean;
|
|
62
|
+
variant?: 'default' | 'destructive';
|
|
63
|
+
}) @{
|
|
64
|
+
<ContextMenuPrimitive.Item
|
|
65
|
+
data-slot="context-menu-item"
|
|
66
|
+
data-inset={inset}
|
|
67
|
+
data-variant={variant}
|
|
68
|
+
className={cn(
|
|
69
|
+
'group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive',
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function ContextMenuSubTrigger(props: Props & { inset?: boolean }) @{
|
|
77
|
+
const { className, children: _children, inset, ...rest } = props;
|
|
78
|
+
|
|
79
|
+
<ContextMenuPrimitive.SubTrigger
|
|
80
|
+
data-slot="context-menu-sub-trigger"
|
|
81
|
+
data-inset={inset}
|
|
82
|
+
className={cn(
|
|
83
|
+
'flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
84
|
+
className,
|
|
85
|
+
)}
|
|
86
|
+
{...rest}
|
|
87
|
+
>
|
|
88
|
+
{props.children}
|
|
89
|
+
<ChevronRightIcon className="cn-rtl-flip ml-auto" />
|
|
90
|
+
</ContextMenuPrimitive.SubTrigger>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function ContextMenuSubContent({ className, ...props }: Props) @{
|
|
94
|
+
<ContextMenuPrimitive.SubContent
|
|
95
|
+
data-slot="context-menu-sub-content"
|
|
96
|
+
className={cn(
|
|
97
|
+
'z-50 min-w-32 origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg duration-100 data-[side=bottom]:slide-in-from-top-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',
|
|
98
|
+
className,
|
|
99
|
+
)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function ContextMenuCheckboxItem(props: Props & {
|
|
105
|
+
checked?: boolean | 'indeterminate';
|
|
106
|
+
inset?: boolean;
|
|
107
|
+
}) @{
|
|
108
|
+
const { className, children: _children, checked, inset, ...rest } = props;
|
|
109
|
+
|
|
110
|
+
<ContextMenuPrimitive.CheckboxItem
|
|
111
|
+
data-slot="context-menu-checkbox-item"
|
|
112
|
+
data-inset={inset}
|
|
113
|
+
className={cn(
|
|
114
|
+
'relative flex 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 data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
115
|
+
className,
|
|
116
|
+
)}
|
|
117
|
+
checked={checked}
|
|
118
|
+
{...rest}
|
|
119
|
+
>
|
|
120
|
+
<span className="pointer-events-none absolute right-2">
|
|
121
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
122
|
+
<CheckIcon />
|
|
123
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
124
|
+
</span>
|
|
125
|
+
{props.children}
|
|
126
|
+
</ContextMenuPrimitive.CheckboxItem>
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function ContextMenuRadioItem(props: Props & { inset?: boolean }) @{
|
|
130
|
+
const { className, children: _children, inset, ...rest } = props;
|
|
131
|
+
|
|
132
|
+
<ContextMenuPrimitive.RadioItem
|
|
133
|
+
data-slot="context-menu-radio-item"
|
|
134
|
+
data-inset={inset}
|
|
135
|
+
className={cn(
|
|
136
|
+
'relative flex 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 data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
137
|
+
className,
|
|
138
|
+
)}
|
|
139
|
+
{...rest}
|
|
140
|
+
>
|
|
141
|
+
<span className="pointer-events-none absolute right-2">
|
|
142
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
143
|
+
<CheckIcon />
|
|
144
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
145
|
+
</span>
|
|
146
|
+
{props.children}
|
|
147
|
+
</ContextMenuPrimitive.RadioItem>
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function ContextMenuLabel({ className, inset, ...props }: Props & { inset?: boolean }) @{
|
|
151
|
+
<ContextMenuPrimitive.Label
|
|
152
|
+
data-slot="context-menu-label"
|
|
153
|
+
data-inset={inset}
|
|
154
|
+
className={cn(
|
|
155
|
+
'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',
|
|
156
|
+
className,
|
|
157
|
+
)}
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function ContextMenuSeparator({ className, ...props }: Props) @{
|
|
163
|
+
<ContextMenuPrimitive.Separator
|
|
164
|
+
data-slot="context-menu-separator"
|
|
165
|
+
className={cn('-mx-1 my-1 h-px bg-border', className)}
|
|
166
|
+
{...props}
|
|
167
|
+
/>
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function ContextMenuShortcut({ className, ...props }: Props) @{
|
|
171
|
+
<span
|
|
172
|
+
data-slot="context-menu-shortcut"
|
|
173
|
+
className={cn(
|
|
174
|
+
'ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground',
|
|
175
|
+
className,
|
|
176
|
+
)}
|
|
177
|
+
{...props}
|
|
178
|
+
/>
|
|
179
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Default-Tailwind dialog flavor (package canonical) — user-supplied styling,
|
|
2
|
+
// class strings verbatim. Octane adaptations: "use client" dropped,
|
|
3
|
+
// lucide-react → @octanejs/lucide, radix-ui → @octanejs/radix. DialogContent
|
|
4
|
+
// composes Portal > Overlay + Content with createElement (radix's Portal
|
|
5
|
+
// slots its child — descriptors required); consumer children flow through
|
|
6
|
+
// the props.children channel. Template parts use the {props.children}
|
|
7
|
+
// lowering.
|
|
8
|
+
import { createElement, type OctaneNode } from 'octane';
|
|
9
|
+
import { Dialog as DialogPrimitive } from '@octanejs/radix';
|
|
10
|
+
import { XIcon } from '@octanejs/lucide';
|
|
11
|
+
|
|
12
|
+
import { cn } from '../lib/utils';
|
|
13
|
+
import { Button } from './button.tsrx';
|
|
14
|
+
|
|
15
|
+
type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
|
|
16
|
+
|
|
17
|
+
export function Dialog(props: Record<string, unknown>) @{
|
|
18
|
+
<DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function DialogTrigger(props: Record<string, unknown>) @{
|
|
22
|
+
<DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function DialogPortal(props: Record<string, unknown>) @{
|
|
26
|
+
<DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function DialogClose(props: Record<string, unknown>) @{
|
|
30
|
+
<DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function DialogOverlay({ className, ...props }: Props) @{
|
|
34
|
+
<DialogPrimitive.Overlay
|
|
35
|
+
data-slot="dialog-overlay"
|
|
36
|
+
className={cn(
|
|
37
|
+
'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',
|
|
38
|
+
className,
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface DialogContentProps extends Props {
|
|
45
|
+
showCloseButton?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function DialogContent({
|
|
49
|
+
className,
|
|
50
|
+
children,
|
|
51
|
+
showCloseButton = true,
|
|
52
|
+
...props
|
|
53
|
+
}: DialogContentProps) {
|
|
54
|
+
return createElement(
|
|
55
|
+
DialogPrimitive.Portal,
|
|
56
|
+
{ 'data-slot': 'dialog-portal' },
|
|
57
|
+
createElement(DialogOverlay, { key: 'overlay' }),
|
|
58
|
+
createElement(
|
|
59
|
+
DialogPrimitive.Content,
|
|
60
|
+
{
|
|
61
|
+
key: 'content',
|
|
62
|
+
'data-slot': 'dialog-content',
|
|
63
|
+
className: cn(
|
|
64
|
+
'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-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',
|
|
65
|
+
className,
|
|
66
|
+
),
|
|
67
|
+
...props,
|
|
68
|
+
},
|
|
69
|
+
children,
|
|
70
|
+
showCloseButton
|
|
71
|
+
? createElement(
|
|
72
|
+
DialogPrimitive.Close,
|
|
73
|
+
{ key: 'close', 'data-slot': 'dialog-close', asChild: true },
|
|
74
|
+
createElement(
|
|
75
|
+
Button,
|
|
76
|
+
{ variant: 'ghost', className: 'absolute top-2 right-2', size: 'icon-sm' },
|
|
77
|
+
createElement(XIcon, { key: 'icon' }),
|
|
78
|
+
createElement('span', { key: 'sr', className: 'sr-only' }, 'Close'),
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
: null,
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function DialogHeader({ className, ...props }: Props) @{
|
|
87
|
+
<div data-slot="dialog-header" className={cn('flex flex-col gap-2', className)} {...props} />
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface DialogFooterProps extends Props {
|
|
91
|
+
showCloseButton?: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function DialogFooter(props: DialogFooterProps) @{
|
|
95
|
+
const { className, showCloseButton = false, children: _children, ...rest } = props;
|
|
96
|
+
|
|
97
|
+
<div
|
|
98
|
+
data-slot="dialog-footer"
|
|
99
|
+
className={cn(
|
|
100
|
+
'-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',
|
|
101
|
+
className,
|
|
102
|
+
)}
|
|
103
|
+
{...rest}
|
|
104
|
+
>
|
|
105
|
+
{props.children}
|
|
106
|
+
{showCloseButton
|
|
107
|
+
? createElement(
|
|
108
|
+
DialogPrimitive.Close,
|
|
109
|
+
{ asChild: true },
|
|
110
|
+
createElement(Button, { variant: 'outline' }, 'Close'),
|
|
111
|
+
)
|
|
112
|
+
: null}
|
|
113
|
+
</div>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function DialogTitle({ className, ...props }: Props) @{
|
|
117
|
+
<DialogPrimitive.Title
|
|
118
|
+
data-slot="dialog-title"
|
|
119
|
+
className={cn('cn-font-heading text-base leading-none font-medium', className)}
|
|
120
|
+
{...props}
|
|
121
|
+
/>
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function DialogDescription({ className, ...props }: Props) @{
|
|
125
|
+
<DialogPrimitive.Description
|
|
126
|
+
data-slot="dialog-description"
|
|
127
|
+
className={cn(
|
|
128
|
+
'text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
|
|
129
|
+
className,
|
|
130
|
+
)}
|
|
131
|
+
{...props}
|
|
132
|
+
/>
|
|
133
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// DropdownMenu — maintainer-supplied default-Tailwind flavor, class strings
|
|
2
|
+
// verbatim. Octane adaptations: "use client" dropped, lucide-react →
|
|
3
|
+
// @octanejs/lucide, radix-ui → @octanejs/radix. Content composes its Portal
|
|
4
|
+
// subtree with createElement (radix's Portal slots its child, so the child must
|
|
5
|
+
// be a real element descriptor); the parts that interleave consumer children
|
|
6
|
+
// with sibling descriptors use the {props.children} lowering in template JSX.
|
|
7
|
+
import { createElement, type OctaneNode } from 'octane';
|
|
8
|
+
import { DropdownMenu as DropdownMenuPrimitive } from '@octanejs/radix';
|
|
9
|
+
import { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';
|
|
10
|
+
|
|
11
|
+
import { cn } from '../lib/utils';
|
|
12
|
+
|
|
13
|
+
type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
export function DropdownMenu(props: Record<string, unknown>) @{
|
|
16
|
+
<DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function DropdownMenuPortal(props: Record<string, unknown>) @{
|
|
20
|
+
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function DropdownMenuTrigger(props: Record<string, unknown>) @{
|
|
24
|
+
<DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function DropdownMenuContent({
|
|
28
|
+
className,
|
|
29
|
+
align = 'start',
|
|
30
|
+
sideOffset = 4,
|
|
31
|
+
...props
|
|
32
|
+
}: Props & {
|
|
33
|
+
align?: string;
|
|
34
|
+
sideOffset?: number;
|
|
35
|
+
}) {
|
|
36
|
+
return createElement(
|
|
37
|
+
DropdownMenuPrimitive.Portal,
|
|
38
|
+
null,
|
|
39
|
+
createElement(DropdownMenuPrimitive.Content, {
|
|
40
|
+
'data-slot': 'dropdown-menu-content',
|
|
41
|
+
sideOffset,
|
|
42
|
+
align,
|
|
43
|
+
className: cn(
|
|
44
|
+
'z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-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-[state=closed]:overflow-hidden 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',
|
|
45
|
+
className,
|
|
46
|
+
),
|
|
47
|
+
...props,
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function DropdownMenuGroup(props: Record<string, unknown>) @{
|
|
53
|
+
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function DropdownMenuItem({ className, inset, variant = 'default', ...props }: Props & {
|
|
57
|
+
inset?: boolean;
|
|
58
|
+
variant?: 'default' | 'destructive';
|
|
59
|
+
}) @{
|
|
60
|
+
<DropdownMenuPrimitive.Item
|
|
61
|
+
data-slot="dropdown-menu-item"
|
|
62
|
+
data-inset={inset}
|
|
63
|
+
data-variant={variant}
|
|
64
|
+
className={cn(
|
|
65
|
+
'group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive',
|
|
66
|
+
className,
|
|
67
|
+
)}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function DropdownMenuCheckboxItem(props: Props & {
|
|
73
|
+
checked?: boolean | 'indeterminate';
|
|
74
|
+
inset?: boolean;
|
|
75
|
+
}) @{
|
|
76
|
+
const { className, children: _children, checked, inset, ...rest } = props;
|
|
77
|
+
|
|
78
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
79
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
80
|
+
data-inset={inset}
|
|
81
|
+
className={cn(
|
|
82
|
+
'relative flex 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 focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
checked={checked}
|
|
86
|
+
{...rest}
|
|
87
|
+
>
|
|
88
|
+
<span
|
|
89
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
90
|
+
data-slot="dropdown-menu-checkbox-item-indicator"
|
|
91
|
+
>
|
|
92
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
93
|
+
<CheckIcon />
|
|
94
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
95
|
+
</span>
|
|
96
|
+
{props.children}
|
|
97
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function DropdownMenuRadioGroup(props: Record<string, unknown>) @{
|
|
101
|
+
<DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function DropdownMenuRadioItem(props: Props & { inset?: boolean }) @{
|
|
105
|
+
const { className, children: _children, inset, ...rest } = props;
|
|
106
|
+
|
|
107
|
+
<DropdownMenuPrimitive.RadioItem
|
|
108
|
+
data-slot="dropdown-menu-radio-item"
|
|
109
|
+
data-inset={inset}
|
|
110
|
+
className={cn(
|
|
111
|
+
'relative flex 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 focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
112
|
+
className,
|
|
113
|
+
)}
|
|
114
|
+
{...rest}
|
|
115
|
+
>
|
|
116
|
+
<span
|
|
117
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
118
|
+
data-slot="dropdown-menu-radio-item-indicator"
|
|
119
|
+
>
|
|
120
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
121
|
+
<CheckIcon />
|
|
122
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
123
|
+
</span>
|
|
124
|
+
{props.children}
|
|
125
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function DropdownMenuLabel({ className, inset, ...props }: Props & { inset?: boolean }) @{
|
|
129
|
+
<DropdownMenuPrimitive.Label
|
|
130
|
+
data-slot="dropdown-menu-label"
|
|
131
|
+
data-inset={inset}
|
|
132
|
+
className={cn(
|
|
133
|
+
'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',
|
|
134
|
+
className,
|
|
135
|
+
)}
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function DropdownMenuSeparator({ className, ...props }: Props) @{
|
|
141
|
+
<DropdownMenuPrimitive.Separator
|
|
142
|
+
data-slot="dropdown-menu-separator"
|
|
143
|
+
className={cn('-mx-1 my-1 h-px bg-border', className)}
|
|
144
|
+
{...props}
|
|
145
|
+
/>
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function DropdownMenuShortcut({ className, ...props }: Props) @{
|
|
149
|
+
<span
|
|
150
|
+
data-slot="dropdown-menu-shortcut"
|
|
151
|
+
className={cn(
|
|
152
|
+
'ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground',
|
|
153
|
+
className,
|
|
154
|
+
)}
|
|
155
|
+
{...props}
|
|
156
|
+
/>
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function DropdownMenuSub(props: Record<string, unknown>) @{
|
|
160
|
+
<DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function DropdownMenuSubTrigger(props: Props & { inset?: boolean }) @{
|
|
164
|
+
const { className, children: _children, inset, ...rest } = props;
|
|
165
|
+
|
|
166
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
167
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
168
|
+
data-inset={inset}
|
|
169
|
+
className={cn(
|
|
170
|
+
'flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
171
|
+
className,
|
|
172
|
+
)}
|
|
173
|
+
{...rest}
|
|
174
|
+
>
|
|
175
|
+
{props.children}
|
|
176
|
+
<ChevronRightIcon className="cn-rtl-flip ml-auto" />
|
|
177
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function DropdownMenuSubContent({ className, ...props }: Props) @{
|
|
181
|
+
<DropdownMenuPrimitive.SubContent
|
|
182
|
+
data-slot="dropdown-menu-sub-content"
|
|
183
|
+
className={cn(
|
|
184
|
+
'z-50 min-w-[96px] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-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',
|
|
185
|
+
className,
|
|
186
|
+
)}
|
|
187
|
+
{...props}
|
|
188
|
+
/>
|
|
189
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Empty — maintainer-supplied default-Tailwind flavor, class strings verbatim.
|
|
2
|
+
// Upstream renders EmptyMedia with data-slot="empty-icon" and EmptyDescription
|
|
3
|
+
// as a <div> (typed as "p") — both preserved as-is.
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
+
|
|
6
|
+
import { cn } from '../lib/utils';
|
|
7
|
+
|
|
8
|
+
type DivProps = { className?: string } & Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
export function Empty({ className, ...props }: DivProps) @{
|
|
11
|
+
<div
|
|
12
|
+
data-slot="empty"
|
|
13
|
+
className={cn(
|
|
14
|
+
'flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance',
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function EmptyHeader({ className, ...props }: DivProps) @{
|
|
22
|
+
<div
|
|
23
|
+
data-slot="empty-header"
|
|
24
|
+
className={cn('flex max-w-sm flex-col items-center gap-2', className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const emptyMediaVariants = cva(
|
|
30
|
+
'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
|
31
|
+
{
|
|
32
|
+
variants: {
|
|
33
|
+
variant: {
|
|
34
|
+
default: 'bg-transparent',
|
|
35
|
+
icon: 'flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*=\'size-\'])]:size-4',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: 'default',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export interface EmptyMediaProps extends Record<string, unknown> {
|
|
45
|
+
className?: string;
|
|
46
|
+
variant?: VariantProps<typeof emptyMediaVariants>['variant'];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function EmptyMedia({ className, variant = 'default', ...props }: EmptyMediaProps) @{
|
|
50
|
+
<div
|
|
51
|
+
data-slot="empty-icon"
|
|
52
|
+
data-variant={variant}
|
|
53
|
+
className={cn(emptyMediaVariants({ variant, className }))}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function EmptyTitle({ className, ...props }: DivProps) @{
|
|
59
|
+
<div
|
|
60
|
+
data-slot="empty-title"
|
|
61
|
+
className={cn('cn-font-heading text-sm font-medium tracking-tight', className)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function EmptyDescription({ className, ...props }: DivProps) @{
|
|
67
|
+
<div
|
|
68
|
+
data-slot="empty-description"
|
|
69
|
+
className={cn(
|
|
70
|
+
'text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
|
|
71
|
+
className,
|
|
72
|
+
)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function EmptyContent({ className, ...props }: DivProps) @{
|
|
78
|
+
<div
|
|
79
|
+
data-slot="empty-content"
|
|
80
|
+
className={cn(
|
|
81
|
+
'flex w-full max-w-sm min-w-0 flex-col items-center gap-2.5 text-sm text-balance',
|
|
82
|
+
className,
|
|
83
|
+
)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
}
|