@trycompai/design-system 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 (71) hide show
  1. package/README.md +110 -0
  2. package/components.json +21 -0
  3. package/hooks/use-mobile.tsx +19 -0
  4. package/lib/utils.ts +6 -0
  5. package/package.json +103 -0
  6. package/postcss.config.mjs +8 -0
  7. package/src/components/ui/accordion.tsx +60 -0
  8. package/src/components/ui/alert-dialog.tsx +161 -0
  9. package/src/components/ui/alert.tsx +109 -0
  10. package/src/components/ui/aspect-ratio.tsx +21 -0
  11. package/src/components/ui/avatar.tsx +74 -0
  12. package/src/components/ui/badge.tsx +48 -0
  13. package/src/components/ui/breadcrumb.tsx +254 -0
  14. package/src/components/ui/button-group.tsx +89 -0
  15. package/src/components/ui/button.tsx +122 -0
  16. package/src/components/ui/calendar.tsx +190 -0
  17. package/src/components/ui/card.tsx +155 -0
  18. package/src/components/ui/carousel.tsx +216 -0
  19. package/src/components/ui/chart.tsx +325 -0
  20. package/src/components/ui/checkbox.tsx +22 -0
  21. package/src/components/ui/collapsible.tsx +17 -0
  22. package/src/components/ui/combobox.tsx +248 -0
  23. package/src/components/ui/command.tsx +189 -0
  24. package/src/components/ui/container.tsx +34 -0
  25. package/src/components/ui/context-menu.tsx +235 -0
  26. package/src/components/ui/dialog.tsx +122 -0
  27. package/src/components/ui/drawer.tsx +102 -0
  28. package/src/components/ui/dropdown-menu.tsx +242 -0
  29. package/src/components/ui/empty.tsx +94 -0
  30. package/src/components/ui/field.tsx +215 -0
  31. package/src/components/ui/grid.tsx +135 -0
  32. package/src/components/ui/heading.tsx +56 -0
  33. package/src/components/ui/hover-card.tsx +46 -0
  34. package/src/components/ui/index.ts +61 -0
  35. package/src/components/ui/input-group.tsx +128 -0
  36. package/src/components/ui/input-otp.tsx +84 -0
  37. package/src/components/ui/input.tsx +15 -0
  38. package/src/components/ui/item.tsx +188 -0
  39. package/src/components/ui/kbd.tsx +26 -0
  40. package/src/components/ui/label.tsx +15 -0
  41. package/src/components/ui/menubar.tsx +163 -0
  42. package/src/components/ui/navigation-menu.tsx +147 -0
  43. package/src/components/ui/page-header.tsx +51 -0
  44. package/src/components/ui/page-layout.tsx +65 -0
  45. package/src/components/ui/pagination.tsx +104 -0
  46. package/src/components/ui/popover.tsx +57 -0
  47. package/src/components/ui/progress.tsx +61 -0
  48. package/src/components/ui/radio-group.tsx +37 -0
  49. package/src/components/ui/resizable.tsx +41 -0
  50. package/src/components/ui/scroll-area.tsx +48 -0
  51. package/src/components/ui/section.tsx +64 -0
  52. package/src/components/ui/select.tsx +166 -0
  53. package/src/components/ui/separator.tsx +17 -0
  54. package/src/components/ui/sheet.tsx +104 -0
  55. package/src/components/ui/sidebar.tsx +707 -0
  56. package/src/components/ui/skeleton.tsx +5 -0
  57. package/src/components/ui/slider.tsx +51 -0
  58. package/src/components/ui/sonner.tsx +43 -0
  59. package/src/components/ui/spinner.tsx +14 -0
  60. package/src/components/ui/stack.tsx +72 -0
  61. package/src/components/ui/switch.tsx +26 -0
  62. package/src/components/ui/table.tsx +65 -0
  63. package/src/components/ui/tabs.tsx +69 -0
  64. package/src/components/ui/text.tsx +59 -0
  65. package/src/components/ui/textarea.tsx +13 -0
  66. package/src/components/ui/toggle-group.tsx +87 -0
  67. package/src/components/ui/toggle.tsx +42 -0
  68. package/src/components/ui/tooltip.tsx +52 -0
  69. package/src/index.ts +3 -0
  70. package/src/styles/globals.css +122 -0
  71. package/tailwind.config.ts +59 -0
@@ -0,0 +1,56 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+
4
+ const headingVariants = cva('font-semibold', {
5
+ variants: {
6
+ level: {
7
+ '1': 'text-2xl tracking-tight',
8
+ '2': 'text-xl',
9
+ '3': 'text-lg font-medium',
10
+ '4': 'text-base font-medium',
11
+ '5': 'text-sm font-medium',
12
+ '6': 'text-sm font-medium',
13
+ },
14
+ variant: {
15
+ default: 'text-foreground',
16
+ muted: 'text-muted-foreground',
17
+ },
18
+ tracking: {
19
+ default: '',
20
+ tight: 'tracking-tight',
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ level: '1',
25
+ variant: 'default',
26
+ tracking: 'default',
27
+ },
28
+ });
29
+
30
+ type HeadingLevel = '1' | '2' | '3' | '4' | '5' | '6';
31
+
32
+ type HeadingProps<T extends HeadingLevel = '1'> = {
33
+ level?: T;
34
+ as?: `h${HeadingLevel}`;
35
+ } & Omit<VariantProps<typeof headingVariants>, 'level'> &
36
+ Omit<React.ComponentProps<`h${T}`>, 'className'>;
37
+
38
+ function Heading<T extends HeadingLevel = '1'>({
39
+ level = '1' as T,
40
+ as,
41
+ variant,
42
+ tracking,
43
+ ...props
44
+ }: HeadingProps<T>) {
45
+ const Component = as ?? (`h${level}` as `h${T}`);
46
+
47
+ return (
48
+ <Component
49
+ data-slot="heading"
50
+ className={headingVariants({ level, variant, tracking })}
51
+ {...props}
52
+ />
53
+ );
54
+ }
55
+
56
+ export { Heading, headingVariants };
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { PreviewCard as PreviewCardPrimitive } from '@base-ui/react/preview-card';
4
+
5
+ import { cn } from '../../../lib/utils';
6
+
7
+ function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
8
+ return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />;
9
+ }
10
+
11
+ function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
12
+ return <PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />;
13
+ }
14
+
15
+ function HoverCardContent({
16
+ className,
17
+ side = 'bottom',
18
+ sideOffset = 4,
19
+ align = 'center',
20
+ alignOffset = 4,
21
+ ...props
22
+ }: PreviewCardPrimitive.Popup.Props &
23
+ Pick<PreviewCardPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
24
+ return (
25
+ <PreviewCardPrimitive.Portal data-slot="hover-card-portal">
26
+ <PreviewCardPrimitive.Positioner
27
+ align={align}
28
+ alignOffset={alignOffset}
29
+ side={side}
30
+ sideOffset={sideOffset}
31
+ className="isolate z-50"
32
+ >
33
+ <PreviewCardPrimitive.Popup
34
+ data-slot="hover-card-content"
35
+ className={cn(
36
+ 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 bg-popover text-popover-foreground w-64 rounded-lg p-4 text-sm shadow-md ring-1 duration-100 z-50 origin-(--transform-origin) outline-hidden',
37
+ className,
38
+ )}
39
+ {...props}
40
+ />
41
+ </PreviewCardPrimitive.Positioner>
42
+ </PreviewCardPrimitive.Portal>
43
+ );
44
+ }
45
+
46
+ export { HoverCard, HoverCardContent, HoverCardTrigger };
@@ -0,0 +1,61 @@
1
+ export * from './accordion';
2
+ export * from './alert';
3
+ export * from './alert-dialog';
4
+ export * from './aspect-ratio';
5
+ export * from './avatar';
6
+ export * from './badge';
7
+ export * from './breadcrumb';
8
+ export * from './button';
9
+ export * from './button-group';
10
+ export * from './calendar';
11
+ export * from './card';
12
+ export * from './carousel';
13
+ export * from './chart';
14
+ export * from './checkbox';
15
+ export * from './collapsible';
16
+ export * from './combobox';
17
+ export * from './command';
18
+ export * from './container';
19
+ export * from './context-menu';
20
+ export * from './dialog';
21
+ export * from './drawer';
22
+ export * from './dropdown-menu';
23
+ export * from './empty';
24
+ export * from './field';
25
+ export * from './grid';
26
+ export * from './heading';
27
+ export * from './hover-card';
28
+ export * from './input';
29
+ export * from './input-group';
30
+ export * from './input-otp';
31
+ export * from './item';
32
+ export * from './kbd';
33
+ export * from './label';
34
+ export * from './menubar';
35
+ export * from './navigation-menu';
36
+ export * from './page-header';
37
+ export * from './page-layout';
38
+ export * from './pagination';
39
+ export * from './popover';
40
+ export * from './progress';
41
+ export * from './radio-group';
42
+ export * from './resizable';
43
+ export * from './scroll-area';
44
+ export * from './section';
45
+ export * from './select';
46
+ export * from './separator';
47
+ export * from './sheet';
48
+ export * from './sidebar';
49
+ export * from './skeleton';
50
+ export * from './slider';
51
+ export * from './sonner';
52
+ export * from './spinner';
53
+ export * from './stack';
54
+ export * from './switch';
55
+ export * from './table';
56
+ export * from './tabs';
57
+ export * from './text';
58
+ export * from './textarea';
59
+ export * from './toggle';
60
+ export * from './toggle-group';
61
+ export * from './tooltip';
@@ -0,0 +1,128 @@
1
+ import { Button as ButtonPrimitive } from '@base-ui/react/button';
2
+ import { Input as InputPrimitive } from '@base-ui/react/input';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+ import * as React from 'react';
5
+
6
+ import { cn } from '../../../lib/utils';
7
+ import { buttonVariants } from './button';
8
+
9
+ function InputGroup({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
10
+ return (
11
+ <div
12
+ data-slot="input-group"
13
+ role="group"
14
+ className="border-input dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-disabled:bg-input/50 dark:has-disabled:bg-input/80 h-8 rounded-lg border transition-colors has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot][aria-invalid=true]]:ring-[3px] 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-[>[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 [[data-slot=combobox-content]_&]:focus-within:border-inherit [[data-slot=combobox-content]_&]:focus-within:ring-0 group/input-group relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto"
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ const inputGroupAddonVariants = cva(
21
+ "text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none",
22
+ {
23
+ variants: {
24
+ align: {
25
+ 'inline-start': 'pl-2 has-[>button]:ml-[-0.25rem] has-[>kbd]:ml-[-0.15rem] order-first',
26
+ 'inline-end': 'pr-2 has-[>button]:mr-[-0.25rem] has-[>kbd]:mr-[-0.15rem] order-last',
27
+ 'block-start':
28
+ 'px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start',
29
+ 'block-end':
30
+ 'px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start',
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ align: 'inline-start',
35
+ },
36
+ },
37
+ );
38
+
39
+ function InputGroupAddon({
40
+ align = 'inline-start',
41
+ ...props
42
+ }: Omit<React.ComponentProps<'div'>, 'className'> & VariantProps<typeof inputGroupAddonVariants>) {
43
+ return (
44
+ <div
45
+ role="group"
46
+ data-slot="input-group-addon"
47
+ data-align={align}
48
+ className={inputGroupAddonVariants({ align })}
49
+ onClick={(e) => {
50
+ if ((e.target as HTMLElement).closest('button')) {
51
+ return;
52
+ }
53
+ e.currentTarget.parentElement?.querySelector('input')?.focus();
54
+ }}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ const inputGroupButtonVariants = cva('gap-2 text-sm shadow-none flex items-center', {
61
+ variants: {
62
+ size: {
63
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
64
+ sm: '',
65
+ 'icon-xs': 'size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0',
66
+ 'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
67
+ },
68
+ },
69
+ defaultVariants: {
70
+ size: 'xs',
71
+ },
72
+ });
73
+
74
+ function InputGroupButton({
75
+ type = 'button',
76
+ variant = 'ghost',
77
+ size = 'xs',
78
+ ...props
79
+ }: Omit<ButtonPrimitive.Props, 'className' | 'type'> &
80
+ VariantProps<typeof inputGroupButtonVariants> & {
81
+ variant?: 'default' | 'outline' | 'secondary' | 'ghost' | 'destructive' | 'link';
82
+ type?: 'button' | 'submit' | 'reset';
83
+ }) {
84
+ return (
85
+ <ButtonPrimitive
86
+ type={type}
87
+ data-size={size}
88
+ className={cn(buttonVariants({ variant }), inputGroupButtonVariants({ size }))}
89
+ {...props}
90
+ />
91
+ );
92
+ }
93
+
94
+ function InputGroupText({
95
+ align = 'inline-start',
96
+ ...props
97
+ }: Omit<React.ComponentProps<'div'>, 'className'> & VariantProps<typeof inputGroupAddonVariants>) {
98
+ return <InputGroupAddon align={align} {...props} />;
99
+ }
100
+
101
+ function InputGroupInput({ ...props }: Omit<React.ComponentProps<'input'>, 'className'>) {
102
+ return (
103
+ <InputPrimitive
104
+ data-slot="input-group-control"
105
+ className="h-8 rounded-none border-0 bg-transparent px-2.5 py-1 text-base shadow-none ring-0 transition-colors focus-visible:ring-0 aria-invalid:ring-0 md:text-sm placeholder:text-muted-foreground w-full min-w-0 outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 dark:bg-transparent flex-1"
106
+ {...props}
107
+ />
108
+ );
109
+ }
110
+
111
+ function InputGroupTextarea({ ...props }: Omit<React.ComponentProps<'textarea'>, 'className'>) {
112
+ return (
113
+ <textarea
114
+ data-slot="input-group-control"
115
+ className="rounded-none border-0 bg-transparent px-2.5 py-2 text-base shadow-none ring-0 transition-colors focus-visible:ring-0 aria-invalid:ring-0 md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50 dark:bg-transparent flex-1 resize-none"
116
+ {...props}
117
+ />
118
+ );
119
+ }
120
+
121
+ export {
122
+ InputGroup,
123
+ InputGroupAddon,
124
+ InputGroupButton,
125
+ InputGroupInput,
126
+ InputGroupText,
127
+ InputGroupTextarea,
128
+ };
@@ -0,0 +1,84 @@
1
+ import { OTPInput, OTPInputContext } from 'input-otp';
2
+ import * as React from 'react';
3
+
4
+ import { MinusIcon } from 'lucide-react';
5
+ import { cn } from '../../../lib/utils';
6
+
7
+ function InputOTP({
8
+ className,
9
+ containerClassName,
10
+ ...props
11
+ }: React.ComponentProps<typeof OTPInput> & {
12
+ containerClassName?: string;
13
+ }) {
14
+ return (
15
+ <OTPInput
16
+ data-slot="input-otp"
17
+ containerClassName={cn(
18
+ 'cn-input-otp flex items-center has-disabled:opacity-50',
19
+ containerClassName,
20
+ )}
21
+ spellCheck={false}
22
+ className={cn('disabled:cursor-not-allowed', className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {
29
+ return (
30
+ <div
31
+ data-slot="input-otp-group"
32
+ className={cn(
33
+ 'has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive rounded-md has-aria-invalid:ring-[3px] flex items-center',
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function InputOTPSlot({
42
+ index,
43
+ className,
44
+ ...props
45
+ }: React.ComponentProps<'div'> & {
46
+ index: number;
47
+ }) {
48
+ const inputOTPContext = React.useContext(OTPInputContext);
49
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
50
+
51
+ return (
52
+ <div
53
+ data-slot="input-otp-slot"
54
+ data-active={isActive}
55
+ className={cn(
56
+ 'dark:bg-input/30 border-input data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive size-9 border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:ring-[3px] relative flex items-center justify-center data-[active=true]:z-10',
57
+ className,
58
+ )}
59
+ {...props}
60
+ >
61
+ {char}
62
+ {hasFakeCaret && (
63
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
64
+ <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000 bg-foreground h-4 w-px" />
65
+ </div>
66
+ )}
67
+ </div>
68
+ );
69
+ }
70
+
71
+ function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {
72
+ return (
73
+ <div
74
+ data-slot="input-otp-separator"
75
+ className="[&_svg:not([class*='size-'])]:size-4 flex items-center"
76
+ role="separator"
77
+ {...props}
78
+ >
79
+ <MinusIcon />
80
+ </div>
81
+ );
82
+ }
83
+
84
+ export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };
@@ -0,0 +1,15 @@
1
+ import { Input as InputPrimitive } from '@base-ui/react/input';
2
+ import * as React from 'react';
3
+
4
+ function Input({ type, ...props }: Omit<React.ComponentProps<'input'>, 'className'>) {
5
+ return (
6
+ <InputPrimitive
7
+ type={type}
8
+ data-slot="input"
9
+ className="dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ export { Input };
@@ -0,0 +1,188 @@
1
+ import { mergeProps } from '@base-ui/react/merge-props';
2
+ import { useRender } from '@base-ui/react/use-render';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+ import * as React from 'react';
5
+
6
+ import { Separator as SeparatorPrimitive } from '@base-ui/react/separator';
7
+ import { cn } from '../../../lib/utils';
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
+ 'gap-4 has-[[data-size=sm]]:gap-2.5 has-[[data-size=xs]]:gap-2 group/item-group flex w-full flex-col',
16
+ className,
17
+ )}
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ function ItemSeparator({ ...props }: Omit<SeparatorPrimitive.Props, 'className'>) {
24
+ return (
25
+ <SeparatorPrimitive
26
+ data-slot="item-separator"
27
+ orientation="horizontal"
28
+ className="bg-border shrink-0 h-px w-full my-2"
29
+ {...props}
30
+ />
31
+ );
32
+ }
33
+
34
+ const itemVariants = cva(
35
+ '[a]:hover:bg-muted rounded-md border text-sm w-full group/item focus-visible:border-ring focus-visible:ring-ring/50 flex items-center flex-wrap outline-none transition-colors duration-100 focus-visible:ring-[3px] [a]:transition-colors',
36
+ {
37
+ variants: {
38
+ variant: {
39
+ default: 'border-transparent',
40
+ outline: 'border-border',
41
+ muted: 'bg-muted/50 border-transparent',
42
+ },
43
+ size: {
44
+ default: 'gap-3.5 px-4 py-3.5',
45
+ sm: 'gap-2.5 px-3 py-2.5',
46
+ xs: 'gap-2 px-2.5 py-2 [[data-slot=dropdown-menu-content]_&]:p-0',
47
+ },
48
+ },
49
+ defaultVariants: {
50
+ variant: 'default',
51
+ size: 'default',
52
+ },
53
+ },
54
+ );
55
+
56
+ function Item({
57
+ className,
58
+ variant = 'default',
59
+ size = 'default',
60
+ render,
61
+ ...props
62
+ }: useRender.ComponentProps<'div'> & VariantProps<typeof itemVariants>) {
63
+ return useRender({
64
+ defaultTagName: 'div',
65
+ props: mergeProps<'div'>(
66
+ {
67
+ className: cn(itemVariants({ variant, size, className })),
68
+ },
69
+ props,
70
+ ),
71
+ render,
72
+ state: {
73
+ slot: 'item',
74
+ variant,
75
+ size,
76
+ },
77
+ });
78
+ }
79
+
80
+ const itemMediaVariants = cva(
81
+ 'gap-2 group-has-[[data-slot=item-description]]/item:translate-y-0.5 group-has-[[data-slot=item-description]]/item:self-start flex shrink-0 items-center justify-center [&_svg]:pointer-events-none',
82
+ {
83
+ variants: {
84
+ variant: {
85
+ default: 'bg-transparent',
86
+ icon: "[&_svg:not([class*='size-'])]:size-4",
87
+ image:
88
+ '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',
89
+ },
90
+ },
91
+ defaultVariants: {
92
+ variant: 'default',
93
+ },
94
+ },
95
+ );
96
+
97
+ function ItemMedia({
98
+ className,
99
+ variant = 'default',
100
+ ...props
101
+ }: React.ComponentProps<'div'> & VariantProps<typeof itemMediaVariants>) {
102
+ return (
103
+ <div
104
+ data-slot="item-media"
105
+ data-variant={variant}
106
+ className={cn(itemMediaVariants({ variant, className }))}
107
+ {...props}
108
+ />
109
+ );
110
+ }
111
+
112
+ function ItemContent({ className, ...props }: React.ComponentProps<'div'>) {
113
+ return (
114
+ <div
115
+ data-slot="item-content"
116
+ className={cn(
117
+ 'gap-1 group-data-[size=xs]/item:gap-0 flex flex-1 flex-col [&+[data-slot=item-content]]:flex-none',
118
+ className,
119
+ )}
120
+ {...props}
121
+ />
122
+ );
123
+ }
124
+
125
+ function ItemTitle({ className, ...props }: React.ComponentProps<'div'>) {
126
+ return (
127
+ <div
128
+ data-slot="item-title"
129
+ className={cn(
130
+ 'gap-2 text-sm leading-snug font-medium underline-offset-4 line-clamp-1 flex w-fit items-center',
131
+ className,
132
+ )}
133
+ {...props}
134
+ />
135
+ );
136
+ }
137
+
138
+ function ItemDescription({ className, ...props }: React.ComponentProps<'p'>) {
139
+ return (
140
+ <p
141
+ data-slot="item-description"
142
+ className={cn(
143
+ 'text-muted-foreground text-left text-sm leading-normal group-data-[size=xs]/item:text-xs [&>a:hover]:text-primary line-clamp-2 font-normal [&>a]:underline [&>a]:underline-offset-4',
144
+ className,
145
+ )}
146
+ {...props}
147
+ />
148
+ );
149
+ }
150
+
151
+ function ItemActions({ className, ...props }: React.ComponentProps<'div'>) {
152
+ return (
153
+ <div data-slot="item-actions" className={cn('gap-2 flex items-center', className)} {...props} />
154
+ );
155
+ }
156
+
157
+ function ItemHeader({ className, ...props }: React.ComponentProps<'div'>) {
158
+ return (
159
+ <div
160
+ data-slot="item-header"
161
+ className={cn('gap-2 flex basis-full items-center justify-between', className)}
162
+ {...props}
163
+ />
164
+ );
165
+ }
166
+
167
+ function ItemFooter({ className, ...props }: React.ComponentProps<'div'>) {
168
+ return (
169
+ <div
170
+ data-slot="item-footer"
171
+ className={cn('gap-2 flex basis-full items-center justify-between', className)}
172
+ {...props}
173
+ />
174
+ );
175
+ }
176
+
177
+ export {
178
+ Item,
179
+ ItemActions,
180
+ ItemContent,
181
+ ItemDescription,
182
+ ItemFooter,
183
+ ItemGroup,
184
+ ItemHeader,
185
+ ItemMedia,
186
+ ItemSeparator,
187
+ ItemTitle,
188
+ };
@@ -0,0 +1,26 @@
1
+ import { cn } from '../../../lib/utils';
2
+
3
+ function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
4
+ return (
5
+ <kbd
6
+ data-slot="kbd"
7
+ className={cn(
8
+ "bg-muted text-muted-foreground [[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10 h-5 w-fit min-w-5 gap-1 rounded-sm px-1 font-sans text-xs font-medium [&_svg:not([class*='size-'])]:size-3 pointer-events-none inline-flex items-center justify-center select-none",
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('gap-1 inline-flex items-center', className)}
21
+ {...props}
22
+ />
23
+ );
24
+ }
25
+
26
+ export { Kbd, KbdGroup };
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+
5
+ function Label({ ...props }: Omit<React.ComponentProps<'label'>, 'className'>) {
6
+ return (
7
+ <label
8
+ data-slot="label"
9
+ className="gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed"
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ export { Label };