@bupple/vss-ui 1.0.0
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/.turbo/turbo-lint.log +4 -0
- package/components.json +21 -0
- package/eslint.config.js +4 -0
- package/index.ts +2 -0
- package/package.json +67 -0
- package/postcss.config.mjs +6 -0
- package/src/components/accordion.tsx +84 -0
- package/src/components/alert-dialog.tsx +198 -0
- package/src/components/alert.tsx +77 -0
- package/src/components/aspect-ratio.tsx +11 -0
- package/src/components/avatar.tsx +109 -0
- package/src/components/badge.tsx +50 -0
- package/src/components/breadcrumb.tsx +118 -0
- package/src/components/button-group.tsx +84 -0
- package/src/components/button.tsx +68 -0
- package/src/components/calendar.tsx +223 -0
- package/src/components/card.tsx +102 -0
- package/src/components/carousel.tsx +241 -0
- package/src/components/chart.tsx +373 -0
- package/src/components/checkbox.tsx +32 -0
- package/src/components/collapsible.tsx +33 -0
- package/src/components/combobox.tsx +299 -0
- package/src/components/command.tsx +194 -0
- package/src/components/context-menu.tsx +272 -0
- package/src/components/dialog.tsx +171 -0
- package/src/components/direction.tsx +20 -0
- package/src/components/drawer.tsx +130 -0
- package/src/components/dropdown-menu.tsx +278 -0
- package/src/components/empty.tsx +102 -0
- package/src/components/field.tsx +237 -0
- package/src/components/hover-card.tsx +43 -0
- package/src/components/input-group.tsx +157 -0
- package/src/components/input.tsx +18 -0
- package/src/components/item.tsx +197 -0
- package/src/components/kbd.tsx +26 -0
- package/src/components/label.tsx +21 -0
- package/src/components/menubar.tsx +283 -0
- package/src/components/native-select.tsx +64 -0
- package/src/components/navigation-menu.tsx +166 -0
- package/src/components/pagination.tsx +131 -0
- package/src/components/popover.tsx +88 -0
- package/src/components/progress.tsx +30 -0
- package/src/components/radio-group.tsx +46 -0
- package/src/components/resizable.tsx +49 -0
- package/src/components/scroll-area.tsx +52 -0
- package/src/components/select.tsx +209 -0
- package/src/components/separator.tsx +25 -0
- package/src/components/sheet.tsx +152 -0
- package/src/components/sidebar.tsx +703 -0
- package/src/components/skeleton.tsx +13 -0
- package/src/components/slider.tsx +58 -0
- package/src/components/sonner.tsx +45 -0
- package/src/components/spinner.tsx +15 -0
- package/src/components/switch.tsx +32 -0
- package/src/components/table.tsx +115 -0
- package/src/components/tabs.tsx +89 -0
- package/src/components/textarea.tsx +17 -0
- package/src/components/toggle-group.tsx +86 -0
- package/src/components/toggle.tsx +48 -0
- package/src/components/tooltip.tsx +56 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/portal-container.ts +11 -0
- package/src/lib/utils.ts +8 -0
- package/src/theme.css +125 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
4
|
+
|
|
5
|
+
import { Button } from '@bupple/vss-ui/components/button'
|
|
6
|
+
import { Input } from '@bupple/vss-ui/components/input'
|
|
7
|
+
import { Textarea } from '@bupple/vss-ui/components/textarea'
|
|
8
|
+
import { cn } from '@bupple/vss-ui/lib/utils'
|
|
9
|
+
import { cva } from 'class-variance-authority'
|
|
10
|
+
import * as React from 'react'
|
|
11
|
+
|
|
12
|
+
function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
data-slot='input-group'
|
|
16
|
+
role='group'
|
|
17
|
+
className={cn(
|
|
18
|
+
'group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5',
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const inputGroupAddonVariants = cva(
|
|
27
|
+
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
|
|
28
|
+
{
|
|
29
|
+
variants: {
|
|
30
|
+
align: {
|
|
31
|
+
'inline-start':
|
|
32
|
+
'order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]',
|
|
33
|
+
'inline-end':
|
|
34
|
+
'order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]',
|
|
35
|
+
'block-start':
|
|
36
|
+
'order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2',
|
|
37
|
+
'block-end':
|
|
38
|
+
'order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultVariants: {
|
|
42
|
+
align: 'inline-start',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
function InputGroupAddon({
|
|
48
|
+
className,
|
|
49
|
+
align = 'inline-start',
|
|
50
|
+
...props
|
|
51
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
role='group'
|
|
55
|
+
data-slot='input-group-addon'
|
|
56
|
+
data-align={align}
|
|
57
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
58
|
+
onClick={(e) => {
|
|
59
|
+
if ((e.target as HTMLElement).closest('button')) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
e.currentTarget.parentElement?.querySelector('input')?.focus()
|
|
63
|
+
}}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const inputGroupButtonVariants = cva(
|
|
70
|
+
'flex items-center gap-2 text-sm shadow-none',
|
|
71
|
+
{
|
|
72
|
+
variants: {
|
|
73
|
+
size: {
|
|
74
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
|
|
75
|
+
sm: '',
|
|
76
|
+
'icon-xs':
|
|
77
|
+
'size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0',
|
|
78
|
+
'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
defaultVariants: {
|
|
82
|
+
size: 'xs',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
function InputGroupButton({
|
|
88
|
+
className,
|
|
89
|
+
type = 'button',
|
|
90
|
+
variant = 'ghost',
|
|
91
|
+
size = 'xs',
|
|
92
|
+
...props
|
|
93
|
+
}: Omit<React.ComponentProps<typeof Button>, 'size'> &
|
|
94
|
+
VariantProps<typeof inputGroupButtonVariants>) {
|
|
95
|
+
return (
|
|
96
|
+
<Button
|
|
97
|
+
type={type}
|
|
98
|
+
data-size={size}
|
|
99
|
+
variant={variant}
|
|
100
|
+
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
|
|
107
|
+
return (
|
|
108
|
+
<span
|
|
109
|
+
className={cn(
|
|
110
|
+
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
111
|
+
className,
|
|
112
|
+
)}
|
|
113
|
+
{...props}
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function InputGroupInput({
|
|
119
|
+
className,
|
|
120
|
+
...props
|
|
121
|
+
}: React.ComponentProps<'input'>) {
|
|
122
|
+
return (
|
|
123
|
+
<Input
|
|
124
|
+
data-slot='input-group-control'
|
|
125
|
+
className={cn(
|
|
126
|
+
'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',
|
|
127
|
+
className,
|
|
128
|
+
)}
|
|
129
|
+
{...props}
|
|
130
|
+
/>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function InputGroupTextarea({
|
|
135
|
+
className,
|
|
136
|
+
...props
|
|
137
|
+
}: React.ComponentProps<'textarea'>) {
|
|
138
|
+
return (
|
|
139
|
+
<Textarea
|
|
140
|
+
data-slot='input-group-control'
|
|
141
|
+
className={cn(
|
|
142
|
+
'flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent',
|
|
143
|
+
className,
|
|
144
|
+
)}
|
|
145
|
+
{...props}
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
InputGroup,
|
|
152
|
+
InputGroupAddon,
|
|
153
|
+
InputGroupButton,
|
|
154
|
+
InputGroupText,
|
|
155
|
+
InputGroupInput,
|
|
156
|
+
InputGroupTextarea,
|
|
157
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cn } from '@bupple/vss-ui/lib/utils'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
|
|
4
|
+
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
|
5
|
+
return (
|
|
6
|
+
<input
|
|
7
|
+
type={type}
|
|
8
|
+
data-slot='input'
|
|
9
|
+
className={cn(
|
|
10
|
+
'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Input }
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority'
|
|
2
|
+
|
|
3
|
+
import { Separator } from '@bupple/vss-ui/components/separator'
|
|
4
|
+
import { cn } from '@bupple/vss-ui/lib/utils'
|
|
5
|
+
import { cva } from 'class-variance-authority'
|
|
6
|
+
import { Slot } from 'radix-ui'
|
|
7
|
+
import * as React from 'react'
|
|
8
|
+
|
|
9
|
+
function ItemGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
role='list'
|
|
13
|
+
data-slot='item-group'
|
|
14
|
+
className={cn(
|
|
15
|
+
'group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2',
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ItemSeparator({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<typeof Separator>) {
|
|
27
|
+
return (
|
|
28
|
+
<Separator
|
|
29
|
+
data-slot='item-separator'
|
|
30
|
+
orientation='horizontal'
|
|
31
|
+
className={cn('my-2', className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const itemVariants = cva(
|
|
38
|
+
'group/item flex w-full flex-wrap items-center rounded-lg border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted',
|
|
39
|
+
{
|
|
40
|
+
variants: {
|
|
41
|
+
variant: {
|
|
42
|
+
default: 'border-transparent',
|
|
43
|
+
outline: 'border-border',
|
|
44
|
+
muted: 'border-transparent bg-muted/50',
|
|
45
|
+
},
|
|
46
|
+
size: {
|
|
47
|
+
default: 'gap-2.5 px-3 py-2.5',
|
|
48
|
+
sm: 'gap-2.5 px-3 py-2.5',
|
|
49
|
+
xs: 'gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
defaultVariants: {
|
|
53
|
+
variant: 'default',
|
|
54
|
+
size: 'default',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
function Item({
|
|
60
|
+
className,
|
|
61
|
+
variant = 'default',
|
|
62
|
+
size = 'default',
|
|
63
|
+
asChild = false,
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<'div'> &
|
|
66
|
+
VariantProps<typeof itemVariants> & { asChild?: boolean }) {
|
|
67
|
+
const Comp = asChild ? Slot.Root : 'div'
|
|
68
|
+
return (
|
|
69
|
+
<Comp
|
|
70
|
+
data-slot='item'
|
|
71
|
+
data-variant={variant}
|
|
72
|
+
data-size={size}
|
|
73
|
+
className={cn(itemVariants({ variant, size, className }))}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const itemMediaVariants = cva(
|
|
80
|
+
'flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none',
|
|
81
|
+
{
|
|
82
|
+
variants: {
|
|
83
|
+
variant: {
|
|
84
|
+
default: 'bg-transparent',
|
|
85
|
+
icon: "[&_svg:not([class*='size-'])]:size-4",
|
|
86
|
+
image:
|
|
87
|
+
'size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
defaultVariants: {
|
|
91
|
+
variant: 'default',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
function ItemMedia({
|
|
97
|
+
className,
|
|
98
|
+
variant = 'default',
|
|
99
|
+
...props
|
|
100
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof itemMediaVariants>) {
|
|
101
|
+
return (
|
|
102
|
+
<div
|
|
103
|
+
data-slot='item-media'
|
|
104
|
+
data-variant={variant}
|
|
105
|
+
className={cn(itemMediaVariants({ variant, className }))}
|
|
106
|
+
{...props}
|
|
107
|
+
/>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function ItemContent({ className, ...props }: React.ComponentProps<'div'>) {
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
data-slot='item-content'
|
|
115
|
+
className={cn(
|
|
116
|
+
'flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none',
|
|
117
|
+
className,
|
|
118
|
+
)}
|
|
119
|
+
{...props}
|
|
120
|
+
/>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ItemTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
|
125
|
+
return (
|
|
126
|
+
<div
|
|
127
|
+
data-slot='item-title'
|
|
128
|
+
className={cn(
|
|
129
|
+
'line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4',
|
|
130
|
+
className,
|
|
131
|
+
)}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function ItemDescription({ className, ...props }: React.ComponentProps<'p'>) {
|
|
138
|
+
return (
|
|
139
|
+
<p
|
|
140
|
+
data-slot='item-description'
|
|
141
|
+
className={cn(
|
|
142
|
+
'line-clamp-2 text-left text-sm leading-normal font-normal text-muted-foreground group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
|
|
143
|
+
className,
|
|
144
|
+
)}
|
|
145
|
+
{...props}
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function ItemActions({ className, ...props }: React.ComponentProps<'div'>) {
|
|
151
|
+
return (
|
|
152
|
+
<div
|
|
153
|
+
data-slot='item-actions'
|
|
154
|
+
className={cn('flex items-center gap-2', className)}
|
|
155
|
+
{...props}
|
|
156
|
+
/>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function ItemHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
161
|
+
return (
|
|
162
|
+
<div
|
|
163
|
+
data-slot='item-header'
|
|
164
|
+
className={cn(
|
|
165
|
+
'flex basis-full items-center justify-between gap-2',
|
|
166
|
+
className,
|
|
167
|
+
)}
|
|
168
|
+
{...props}
|
|
169
|
+
/>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function ItemFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
174
|
+
return (
|
|
175
|
+
<div
|
|
176
|
+
data-slot='item-footer'
|
|
177
|
+
className={cn(
|
|
178
|
+
'flex basis-full items-center justify-between gap-2',
|
|
179
|
+
className,
|
|
180
|
+
)}
|
|
181
|
+
{...props}
|
|
182
|
+
/>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
Item,
|
|
188
|
+
ItemMedia,
|
|
189
|
+
ItemContent,
|
|
190
|
+
ItemActions,
|
|
191
|
+
ItemGroup,
|
|
192
|
+
ItemSeparator,
|
|
193
|
+
ItemTitle,
|
|
194
|
+
ItemDescription,
|
|
195
|
+
ItemHeader,
|
|
196
|
+
ItemFooter,
|
|
197
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { cn } from '@bupple/vss-ui/lib/utils'
|
|
2
|
+
|
|
3
|
+
function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
|
|
4
|
+
return (
|
|
5
|
+
<kbd
|
|
6
|
+
data-slot='kbd'
|
|
7
|
+
className={cn(
|
|
8
|
+
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
|
|
9
|
+
className,
|
|
10
|
+
)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
|
17
|
+
return (
|
|
18
|
+
<kbd
|
|
19
|
+
data-slot='kbd-group'
|
|
20
|
+
className={cn('inline-flex items-center gap-1', className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { Kbd, KbdGroup }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { cn } from '@bupple/vss-ui/lib/utils'
|
|
2
|
+
import { Label as LabelPrimitive } from 'radix-ui'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
function Label({
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
9
|
+
return (
|
|
10
|
+
<LabelPrimitive.Root
|
|
11
|
+
data-slot='label'
|
|
12
|
+
className={cn(
|
|
13
|
+
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Label }
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { cn } from '@bupple/vss-ui/lib/utils'
|
|
4
|
+
import { CheckIcon, ChevronRightIcon } from 'lucide-react'
|
|
5
|
+
import { Menubar as MenubarPrimitive } from 'radix-ui'
|
|
6
|
+
import * as React from 'react'
|
|
7
|
+
|
|
8
|
+
function Menubar({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<MenubarPrimitive.Root
|
|
14
|
+
data-slot='menubar'
|
|
15
|
+
className={cn(
|
|
16
|
+
'flex h-8 items-center gap-0.5 rounded-lg border p-[3px]',
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function MenubarMenu({
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
|
|
27
|
+
return <MenubarPrimitive.Menu data-slot='menubar-menu' {...props} />
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function MenubarGroup({
|
|
31
|
+
...props
|
|
32
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Group>) {
|
|
33
|
+
return <MenubarPrimitive.Group data-slot='menubar-group' {...props} />
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function MenubarPortal({
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
|
|
39
|
+
return <MenubarPrimitive.Portal data-slot='menubar-portal' {...props} />
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function MenubarRadioGroup({
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
|
|
45
|
+
return (
|
|
46
|
+
<MenubarPrimitive.RadioGroup data-slot='menubar-radio-group' {...props} />
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function MenubarTrigger({
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {
|
|
54
|
+
return (
|
|
55
|
+
<MenubarPrimitive.Trigger
|
|
56
|
+
data-slot='menubar-trigger'
|
|
57
|
+
className={cn(
|
|
58
|
+
'flex items-center rounded-sm px-1.5 py-[2px] text-sm font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted',
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function MenubarContent({
|
|
67
|
+
className,
|
|
68
|
+
align = 'start',
|
|
69
|
+
alignOffset = -4,
|
|
70
|
+
sideOffset = 8,
|
|
71
|
+
...props
|
|
72
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Content>) {
|
|
73
|
+
return (
|
|
74
|
+
<MenubarPortal>
|
|
75
|
+
<MenubarPrimitive.Content
|
|
76
|
+
data-slot='menubar-content'
|
|
77
|
+
align={align}
|
|
78
|
+
alignOffset={alignOffset}
|
|
79
|
+
sideOffset={sideOffset}
|
|
80
|
+
className={cn(
|
|
81
|
+
'z-50 min-w-36 origin-(--radix-menubar-content-transform-origin) overflow-hidden 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',
|
|
82
|
+
className,
|
|
83
|
+
)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
</MenubarPortal>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function MenubarItem({
|
|
91
|
+
className,
|
|
92
|
+
inset,
|
|
93
|
+
variant = 'default',
|
|
94
|
+
...props
|
|
95
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Item> & {
|
|
96
|
+
inset?: boolean
|
|
97
|
+
variant?: 'default' | 'destructive'
|
|
98
|
+
}) {
|
|
99
|
+
return (
|
|
100
|
+
<MenubarPrimitive.Item
|
|
101
|
+
data-slot='menubar-item'
|
|
102
|
+
data-inset={inset}
|
|
103
|
+
data-variant={variant}
|
|
104
|
+
className={cn(
|
|
105
|
+
"group/menubar-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!",
|
|
106
|
+
className,
|
|
107
|
+
)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function MenubarCheckboxItem({
|
|
114
|
+
className,
|
|
115
|
+
children,
|
|
116
|
+
checked,
|
|
117
|
+
inset,
|
|
118
|
+
...props
|
|
119
|
+
}: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem> & {
|
|
120
|
+
inset?: boolean
|
|
121
|
+
}) {
|
|
122
|
+
return (
|
|
123
|
+
<MenubarPrimitive.CheckboxItem
|
|
124
|
+
data-slot='menubar-checkbox-item'
|
|
125
|
+
data-inset={inset}
|
|
126
|
+
className={cn(
|
|
127
|
+
'relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 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 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
|
128
|
+
className,
|
|
129
|
+
)}
|
|
130
|
+
checked={checked}
|
|
131
|
+
{...props}
|
|
132
|
+
>
|
|
133
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
134
|
+
<MenubarPrimitive.ItemIndicator>
|
|
135
|
+
<CheckIcon />
|
|
136
|
+
</MenubarPrimitive.ItemIndicator>
|
|
137
|
+
</span>
|
|
138
|
+
{children}
|
|
139
|
+
</MenubarPrimitive.CheckboxItem>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function MenubarRadioItem({
|
|
144
|
+
className,
|
|
145
|
+
children,
|
|
146
|
+
inset,
|
|
147
|
+
...props
|
|
148
|
+
}: React.ComponentProps<typeof MenubarPrimitive.RadioItem> & {
|
|
149
|
+
inset?: boolean
|
|
150
|
+
}) {
|
|
151
|
+
return (
|
|
152
|
+
<MenubarPrimitive.RadioItem
|
|
153
|
+
data-slot='menubar-radio-item'
|
|
154
|
+
data-inset={inset}
|
|
155
|
+
className={cn(
|
|
156
|
+
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 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",
|
|
157
|
+
className,
|
|
158
|
+
)}
|
|
159
|
+
{...props}
|
|
160
|
+
>
|
|
161
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
162
|
+
<MenubarPrimitive.ItemIndicator>
|
|
163
|
+
<CheckIcon />
|
|
164
|
+
</MenubarPrimitive.ItemIndicator>
|
|
165
|
+
</span>
|
|
166
|
+
{children}
|
|
167
|
+
</MenubarPrimitive.RadioItem>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function MenubarLabel({
|
|
172
|
+
className,
|
|
173
|
+
inset,
|
|
174
|
+
...props
|
|
175
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Label> & {
|
|
176
|
+
inset?: boolean
|
|
177
|
+
}) {
|
|
178
|
+
return (
|
|
179
|
+
<MenubarPrimitive.Label
|
|
180
|
+
data-slot='menubar-label'
|
|
181
|
+
data-inset={inset}
|
|
182
|
+
className={cn(
|
|
183
|
+
'px-1.5 py-1 text-sm font-medium data-inset:pl-7',
|
|
184
|
+
className,
|
|
185
|
+
)}
|
|
186
|
+
{...props}
|
|
187
|
+
/>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function MenubarSeparator({
|
|
192
|
+
className,
|
|
193
|
+
...props
|
|
194
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Separator>) {
|
|
195
|
+
return (
|
|
196
|
+
<MenubarPrimitive.Separator
|
|
197
|
+
data-slot='menubar-separator'
|
|
198
|
+
className={cn('-mx-1 my-1 h-px bg-border', className)}
|
|
199
|
+
{...props}
|
|
200
|
+
/>
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function MenubarShortcut({
|
|
205
|
+
className,
|
|
206
|
+
...props
|
|
207
|
+
}: React.ComponentProps<'span'>) {
|
|
208
|
+
return (
|
|
209
|
+
<span
|
|
210
|
+
data-slot='menubar-shortcut'
|
|
211
|
+
className={cn(
|
|
212
|
+
'ml-auto text-xs tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground',
|
|
213
|
+
className,
|
|
214
|
+
)}
|
|
215
|
+
{...props}
|
|
216
|
+
/>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function MenubarSub({
|
|
221
|
+
...props
|
|
222
|
+
}: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
|
|
223
|
+
return <MenubarPrimitive.Sub data-slot='menubar-sub' {...props} />
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function MenubarSubTrigger({
|
|
227
|
+
className,
|
|
228
|
+
inset,
|
|
229
|
+
children,
|
|
230
|
+
...props
|
|
231
|
+
}: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
|
|
232
|
+
inset?: boolean
|
|
233
|
+
}) {
|
|
234
|
+
return (
|
|
235
|
+
<MenubarPrimitive.SubTrigger
|
|
236
|
+
data-slot='menubar-sub-trigger'
|
|
237
|
+
data-inset={inset}
|
|
238
|
+
className={cn(
|
|
239
|
+
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg:not([class*='size-'])]:size-4",
|
|
240
|
+
className,
|
|
241
|
+
)}
|
|
242
|
+
{...props}
|
|
243
|
+
>
|
|
244
|
+
{children}
|
|
245
|
+
<ChevronRightIcon className='ml-auto size-4' />
|
|
246
|
+
</MenubarPrimitive.SubTrigger>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function MenubarSubContent({
|
|
251
|
+
className,
|
|
252
|
+
...props
|
|
253
|
+
}: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {
|
|
254
|
+
return (
|
|
255
|
+
<MenubarPrimitive.SubContent
|
|
256
|
+
data-slot='menubar-sub-content'
|
|
257
|
+
className={cn(
|
|
258
|
+
'z-50 min-w-32 origin-(--radix-menubar-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',
|
|
259
|
+
className,
|
|
260
|
+
)}
|
|
261
|
+
{...props}
|
|
262
|
+
/>
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export {
|
|
267
|
+
Menubar,
|
|
268
|
+
MenubarPortal,
|
|
269
|
+
MenubarMenu,
|
|
270
|
+
MenubarTrigger,
|
|
271
|
+
MenubarContent,
|
|
272
|
+
MenubarGroup,
|
|
273
|
+
MenubarSeparator,
|
|
274
|
+
MenubarLabel,
|
|
275
|
+
MenubarItem,
|
|
276
|
+
MenubarShortcut,
|
|
277
|
+
MenubarCheckboxItem,
|
|
278
|
+
MenubarRadioGroup,
|
|
279
|
+
MenubarRadioItem,
|
|
280
|
+
MenubarSub,
|
|
281
|
+
MenubarSubTrigger,
|
|
282
|
+
MenubarSubContent,
|
|
283
|
+
}
|