@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.
Files changed (65) hide show
  1. package/.turbo/turbo-lint.log +4 -0
  2. package/components.json +21 -0
  3. package/eslint.config.js +4 -0
  4. package/index.ts +2 -0
  5. package/package.json +67 -0
  6. package/postcss.config.mjs +6 -0
  7. package/src/components/accordion.tsx +84 -0
  8. package/src/components/alert-dialog.tsx +198 -0
  9. package/src/components/alert.tsx +77 -0
  10. package/src/components/aspect-ratio.tsx +11 -0
  11. package/src/components/avatar.tsx +109 -0
  12. package/src/components/badge.tsx +50 -0
  13. package/src/components/breadcrumb.tsx +118 -0
  14. package/src/components/button-group.tsx +84 -0
  15. package/src/components/button.tsx +68 -0
  16. package/src/components/calendar.tsx +223 -0
  17. package/src/components/card.tsx +102 -0
  18. package/src/components/carousel.tsx +241 -0
  19. package/src/components/chart.tsx +373 -0
  20. package/src/components/checkbox.tsx +32 -0
  21. package/src/components/collapsible.tsx +33 -0
  22. package/src/components/combobox.tsx +299 -0
  23. package/src/components/command.tsx +194 -0
  24. package/src/components/context-menu.tsx +272 -0
  25. package/src/components/dialog.tsx +171 -0
  26. package/src/components/direction.tsx +20 -0
  27. package/src/components/drawer.tsx +130 -0
  28. package/src/components/dropdown-menu.tsx +278 -0
  29. package/src/components/empty.tsx +102 -0
  30. package/src/components/field.tsx +237 -0
  31. package/src/components/hover-card.tsx +43 -0
  32. package/src/components/input-group.tsx +157 -0
  33. package/src/components/input.tsx +18 -0
  34. package/src/components/item.tsx +197 -0
  35. package/src/components/kbd.tsx +26 -0
  36. package/src/components/label.tsx +21 -0
  37. package/src/components/menubar.tsx +283 -0
  38. package/src/components/native-select.tsx +64 -0
  39. package/src/components/navigation-menu.tsx +166 -0
  40. package/src/components/pagination.tsx +131 -0
  41. package/src/components/popover.tsx +88 -0
  42. package/src/components/progress.tsx +30 -0
  43. package/src/components/radio-group.tsx +46 -0
  44. package/src/components/resizable.tsx +49 -0
  45. package/src/components/scroll-area.tsx +52 -0
  46. package/src/components/select.tsx +209 -0
  47. package/src/components/separator.tsx +25 -0
  48. package/src/components/sheet.tsx +152 -0
  49. package/src/components/sidebar.tsx +703 -0
  50. package/src/components/skeleton.tsx +13 -0
  51. package/src/components/slider.tsx +58 -0
  52. package/src/components/sonner.tsx +45 -0
  53. package/src/components/spinner.tsx +15 -0
  54. package/src/components/switch.tsx +32 -0
  55. package/src/components/table.tsx +115 -0
  56. package/src/components/tabs.tsx +89 -0
  57. package/src/components/textarea.tsx +17 -0
  58. package/src/components/toggle-group.tsx +86 -0
  59. package/src/components/toggle.tsx +48 -0
  60. package/src/components/tooltip.tsx +56 -0
  61. package/src/hooks/use-mobile.ts +19 -0
  62. package/src/lib/portal-container.ts +11 -0
  63. package/src/lib/utils.ts +8 -0
  64. package/src/theme.css +125 -0
  65. package/tsconfig.json +15 -0
@@ -0,0 +1,4 @@
1
+
2
+ > @bupple/ui@0.0.0 lint /Users/ahmad/Studyo/mono/packages/ui
3
+ > eslint .
4
+
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "radix-nova",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
@@ -0,0 +1,4 @@
1
+ import { config } from '@bupple/eslint-config/react-internal'
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default config
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './src/lib/utils'
2
+ export * from './src/lib/portal-container'
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@bupple/vss-ui",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "sideEffects": [
6
+ "**/*.css"
7
+ ],
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./index.ts",
15
+ "default": "./index.ts"
16
+ },
17
+ "./theme.css": "./src/theme.css",
18
+ "./components/*": "./src/components/*.tsx",
19
+ "./hooks/*": "./src/hooks/*.ts",
20
+ "./lib/*": "./src/lib/*.ts"
21
+ },
22
+ "peerDependencies": {
23
+ "next-themes": ">=0.4.6",
24
+ "react": ">=18 <20",
25
+ "react-dom": ">=18 <20",
26
+ "tailwindcss": ">=4"
27
+ },
28
+ "peerDependenciesMeta": {
29
+ "react-dom": {
30
+ "optional": false
31
+ }
32
+ },
33
+ "dependencies": {
34
+ "@base-ui/react": "^1.3.0",
35
+ "@tanstack/react-table": "^8.21.3",
36
+ "class-variance-authority": "^0.7.1",
37
+ "clsx": "^2.1.1",
38
+ "cmdk": "^1.1.1",
39
+ "date-fns": "^3.6.0",
40
+ "embla-carousel-react": "^8.6.0",
41
+ "input-otp": "^1.4.2",
42
+ "lucide-react": "^0.575.0",
43
+ "next-themes": "^0.4.6",
44
+ "radix-ui": "^1.4.3",
45
+ "react-day-picker": "^9.14.0",
46
+ "react-resizable-panels": "^4.9.0",
47
+ "recharts": "3.8.0",
48
+ "sonner": "^2.0.7",
49
+ "tailwind-merge": "^3.4.0",
50
+ "tw-animate-css": "^1.4.0",
51
+ "vaul": "^1.1.2"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22",
55
+ "@types/react": "^19",
56
+ "@types/react-dom": "^19",
57
+ "eslint": "^9.20.1",
58
+ "tailwindcss": "^4.1.18",
59
+ "typescript": "^5.9.3",
60
+ "@bupple/eslint-config": "0.0.0",
61
+ "@bupple/typescript-config": "0.0.0"
62
+ },
63
+ "scripts": {
64
+ "lint": "eslint .",
65
+ "type-check": "tsc --noEmit"
66
+ }
67
+ }
@@ -0,0 +1,6 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: { "@tailwindcss/postcss": {} },
4
+ };
5
+
6
+ export default config;
@@ -0,0 +1,84 @@
1
+ import { cn } from '@bupple/vss-ui/lib/utils'
2
+ import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
3
+ import { Accordion as AccordionPrimitive } from 'radix-ui'
4
+ import * as React from 'react'
5
+
6
+ function Accordion({
7
+ className,
8
+ ...props
9
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
10
+ return (
11
+ <AccordionPrimitive.Root
12
+ data-slot='accordion'
13
+ className={cn('flex w-full flex-col', className)}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ function AccordionItem({
20
+ className,
21
+ ...props
22
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
23
+ return (
24
+ <AccordionPrimitive.Item
25
+ data-slot='accordion-item'
26
+ className={cn('not-last:border-b', className)}
27
+ {...props}
28
+ />
29
+ )
30
+ }
31
+
32
+ function AccordionTrigger({
33
+ className,
34
+ children,
35
+ ...props
36
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
37
+ return (
38
+ <AccordionPrimitive.Header className='flex'>
39
+ <AccordionPrimitive.Trigger
40
+ data-slot='accordion-trigger'
41
+ className={cn(
42
+ 'group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground',
43
+ className,
44
+ )}
45
+ {...props}
46
+ >
47
+ {children}
48
+ <ChevronDownIcon
49
+ data-slot='accordion-trigger-icon'
50
+ className='pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden'
51
+ />
52
+ <ChevronUpIcon
53
+ data-slot='accordion-trigger-icon'
54
+ className='pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline'
55
+ />
56
+ </AccordionPrimitive.Trigger>
57
+ </AccordionPrimitive.Header>
58
+ )
59
+ }
60
+
61
+ function AccordionContent({
62
+ className,
63
+ children,
64
+ ...props
65
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
66
+ return (
67
+ <AccordionPrimitive.Content
68
+ data-slot='accordion-content'
69
+ className='overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up'
70
+ {...props}
71
+ >
72
+ <div
73
+ className={cn(
74
+ 'h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
75
+ className,
76
+ )}
77
+ >
78
+ {children}
79
+ </div>
80
+ </AccordionPrimitive.Content>
81
+ )
82
+ }
83
+
84
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
@@ -0,0 +1,198 @@
1
+ 'use client'
2
+
3
+ import { Button } from '@bupple/vss-ui/components/button'
4
+ import { cn } from '@bupple/vss-ui/lib/utils'
5
+ import { AlertDialog as AlertDialogPrimitive } from 'radix-ui'
6
+ import * as React from 'react'
7
+
8
+ function AlertDialog({
9
+ ...props
10
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
11
+ return <AlertDialogPrimitive.Root data-slot='alert-dialog' {...props} />
12
+ }
13
+
14
+ function AlertDialogTrigger({
15
+ ...props
16
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
17
+ return (
18
+ <AlertDialogPrimitive.Trigger data-slot='alert-dialog-trigger' {...props} />
19
+ )
20
+ }
21
+
22
+ function AlertDialogPortal({
23
+ ...props
24
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
25
+ return (
26
+ <AlertDialogPrimitive.Portal data-slot='alert-dialog-portal' {...props} />
27
+ )
28
+ }
29
+
30
+ function AlertDialogOverlay({
31
+ className,
32
+ ...props
33
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
34
+ return (
35
+ <AlertDialogPrimitive.Overlay
36
+ data-slot='alert-dialog-overlay'
37
+ className={cn(
38
+ 'fixed inset-0 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',
39
+ className,
40
+ )}
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ function AlertDialogContent({
47
+ className,
48
+ size = 'default',
49
+ ...props
50
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
51
+ size?: 'default' | 'sm'
52
+ }) {
53
+ return (
54
+ <AlertDialogPortal>
55
+ <AlertDialogOverlay />
56
+ <AlertDialogPrimitive.Content
57
+ data-slot='alert-dialog-content'
58
+ data-size={size}
59
+ className={cn(
60
+ 'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]: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',
61
+ className,
62
+ )}
63
+ {...props}
64
+ />
65
+ </AlertDialogPortal>
66
+ )
67
+ }
68
+
69
+ function AlertDialogHeader({
70
+ className,
71
+ ...props
72
+ }: React.ComponentProps<'div'>) {
73
+ return (
74
+ <div
75
+ data-slot='alert-dialog-header'
76
+ className={cn(
77
+ 'grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]',
78
+ className,
79
+ )}
80
+ {...props}
81
+ />
82
+ )
83
+ }
84
+
85
+ function AlertDialogFooter({
86
+ className,
87
+ ...props
88
+ }: React.ComponentProps<'div'>) {
89
+ return (
90
+ <div
91
+ data-slot='alert-dialog-footer'
92
+ className={cn(
93
+ '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',
94
+ className,
95
+ )}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ function AlertDialogMedia({
102
+ className,
103
+ ...props
104
+ }: React.ComponentProps<'div'>) {
105
+ return (
106
+ <div
107
+ data-slot='alert-dialog-media'
108
+ className={cn(
109
+ "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
110
+ className,
111
+ )}
112
+ {...props}
113
+ />
114
+ )
115
+ }
116
+
117
+ function AlertDialogTitle({
118
+ className,
119
+ ...props
120
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
121
+ return (
122
+ <AlertDialogPrimitive.Title
123
+ data-slot='alert-dialog-title'
124
+ className={cn(
125
+ 'text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2',
126
+ className,
127
+ )}
128
+ {...props}
129
+ />
130
+ )
131
+ }
132
+
133
+ function AlertDialogDescription({
134
+ className,
135
+ ...props
136
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
137
+ return (
138
+ <AlertDialogPrimitive.Description
139
+ data-slot='alert-dialog-description'
140
+ className={cn(
141
+ 'text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
142
+ className,
143
+ )}
144
+ {...props}
145
+ />
146
+ )
147
+ }
148
+
149
+ function AlertDialogAction({
150
+ className,
151
+ variant = 'default',
152
+ size = 'default',
153
+ ...props
154
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
155
+ Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {
156
+ return (
157
+ <Button variant={variant} size={size} asChild>
158
+ <AlertDialogPrimitive.Action
159
+ data-slot='alert-dialog-action'
160
+ className={cn(className)}
161
+ {...props}
162
+ />
163
+ </Button>
164
+ )
165
+ }
166
+
167
+ function AlertDialogCancel({
168
+ className,
169
+ variant = 'outline',
170
+ size = 'default',
171
+ ...props
172
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
173
+ Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {
174
+ return (
175
+ <Button variant={variant} size={size} asChild>
176
+ <AlertDialogPrimitive.Cancel
177
+ data-slot='alert-dialog-cancel'
178
+ className={cn(className)}
179
+ {...props}
180
+ />
181
+ </Button>
182
+ )
183
+ }
184
+
185
+ export {
186
+ AlertDialog,
187
+ AlertDialogAction,
188
+ AlertDialogCancel,
189
+ AlertDialogContent,
190
+ AlertDialogDescription,
191
+ AlertDialogFooter,
192
+ AlertDialogHeader,
193
+ AlertDialogMedia,
194
+ AlertDialogOverlay,
195
+ AlertDialogPortal,
196
+ AlertDialogTitle,
197
+ AlertDialogTrigger,
198
+ }
@@ -0,0 +1,77 @@
1
+ import type { VariantProps } from 'class-variance-authority'
2
+
3
+ import { cn } from '@bupple/vss-ui/lib/utils'
4
+ import { cva } from 'class-variance-authority'
5
+ import * as React from 'react'
6
+
7
+ const alertVariants = cva(
8
+ "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'bg-card text-card-foreground',
13
+ destructive:
14
+ 'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current',
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: 'default',
19
+ },
20
+ },
21
+ )
22
+
23
+ function Alert({
24
+ className,
25
+ variant,
26
+ ...props
27
+ }: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
28
+ return (
29
+ <div
30
+ data-slot='alert'
31
+ role='alert'
32
+ className={cn(alertVariants({ variant }), className)}
33
+ {...props}
34
+ />
35
+ )
36
+ }
37
+
38
+ function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
39
+ return (
40
+ <div
41
+ data-slot='alert-title'
42
+ className={cn(
43
+ 'font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground',
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ function AlertDescription({
52
+ className,
53
+ ...props
54
+ }: React.ComponentProps<'div'>) {
55
+ return (
56
+ <div
57
+ data-slot='alert-description'
58
+ className={cn(
59
+ 'text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
60
+ className,
61
+ )}
62
+ {...props}
63
+ />
64
+ )
65
+ }
66
+
67
+ function AlertAction({ className, ...props }: React.ComponentProps<'div'>) {
68
+ return (
69
+ <div
70
+ data-slot='alert-action'
71
+ className={cn('absolute top-2 right-2', className)}
72
+ {...props}
73
+ />
74
+ )
75
+ }
76
+
77
+ export { Alert, AlertTitle, AlertDescription, AlertAction }
@@ -0,0 +1,11 @@
1
+ 'use client'
2
+
3
+ import { AspectRatio as AspectRatioPrimitive } from 'radix-ui'
4
+
5
+ function AspectRatio({
6
+ ...props
7
+ }: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
8
+ return <AspectRatioPrimitive.Root data-slot='aspect-ratio' {...props} />
9
+ }
10
+
11
+ export { AspectRatio }
@@ -0,0 +1,109 @@
1
+ import { cn } from '@bupple/vss-ui/lib/utils'
2
+ import { Avatar as AvatarPrimitive } from 'radix-ui'
3
+ import * as React from 'react'
4
+
5
+ function Avatar({
6
+ className,
7
+ size = 'default',
8
+ ...props
9
+ }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
10
+ size?: 'default' | 'sm' | 'lg'
11
+ }) {
12
+ return (
13
+ <AvatarPrimitive.Root
14
+ data-slot='avatar'
15
+ data-size={size}
16
+ className={cn(
17
+ 'group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten',
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ )
23
+ }
24
+
25
+ function AvatarImage({
26
+ className,
27
+ ...props
28
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
29
+ return (
30
+ <AvatarPrimitive.Image
31
+ data-slot='avatar-image'
32
+ className={cn(
33
+ 'aspect-square size-full rounded-full object-cover',
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function AvatarFallback({
42
+ className,
43
+ ...props
44
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
45
+ return (
46
+ <AvatarPrimitive.Fallback
47
+ data-slot='avatar-fallback'
48
+ className={cn(
49
+ 'flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs',
50
+ className,
51
+ )}
52
+ {...props}
53
+ />
54
+ )
55
+ }
56
+
57
+ function AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {
58
+ return (
59
+ <span
60
+ data-slot='avatar-badge'
61
+ className={cn(
62
+ 'absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none',
63
+ 'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden',
64
+ 'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2',
65
+ 'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2',
66
+ className,
67
+ )}
68
+ {...props}
69
+ />
70
+ )
71
+ }
72
+
73
+ function AvatarGroup({ className, ...props }: React.ComponentProps<'div'>) {
74
+ return (
75
+ <div
76
+ data-slot='avatar-group'
77
+ className={cn(
78
+ 'group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background',
79
+ className,
80
+ )}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function AvatarGroupCount({
87
+ className,
88
+ ...props
89
+ }: React.ComponentProps<'div'>) {
90
+ return (
91
+ <div
92
+ data-slot='avatar-group-count'
93
+ className={cn(
94
+ 'relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3',
95
+ className,
96
+ )}
97
+ {...props}
98
+ />
99
+ )
100
+ }
101
+
102
+ export {
103
+ Avatar,
104
+ AvatarImage,
105
+ AvatarFallback,
106
+ AvatarGroup,
107
+ AvatarGroupCount,
108
+ AvatarBadge,
109
+ }
@@ -0,0 +1,50 @@
1
+ import type { VariantProps } from 'class-variance-authority'
2
+
3
+ import { cn } from '@bupple/vss-ui/lib/utils'
4
+ import { cva } from 'class-variance-authority'
5
+ import { Slot } from 'radix-ui'
6
+ import * as React from 'react'
7
+
8
+ const badgeVariants = cva(
9
+ 'group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!',
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
14
+ secondary:
15
+ 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
16
+ destructive:
17
+ 'bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20',
18
+ outline:
19
+ 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
20
+ ghost:
21
+ 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
22
+ link: 'text-primary underline-offset-4 hover:underline',
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: 'default',
27
+ },
28
+ },
29
+ )
30
+
31
+ function Badge({
32
+ className,
33
+ variant = 'default',
34
+ asChild = false,
35
+ ...props
36
+ }: React.ComponentProps<'span'> &
37
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
38
+ const Comp = asChild ? Slot.Root : 'span'
39
+
40
+ return (
41
+ <Comp
42
+ data-slot='badge'
43
+ data-variant={variant}
44
+ className={cn(badgeVariants({ variant }), className)}
45
+ {...props}
46
+ />
47
+ )
48
+ }
49
+
50
+ export { Badge, badgeVariants }