@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,5 @@
1
+ function Skeleton({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
2
+ return <div data-slot="skeleton" className="bg-muted rounded-md animate-pulse" {...props} />;
3
+ }
4
+
5
+ export { Skeleton };
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+
3
+ import { Slider as SliderPrimitive } from '@base-ui/react/slider';
4
+ import * as React from 'react';
5
+
6
+ function Slider({
7
+ defaultValue,
8
+ value,
9
+ min = 0,
10
+ max = 100,
11
+ ...props
12
+ }: Omit<SliderPrimitive.Root.Props, 'className'>) {
13
+ const _values = React.useMemo(
14
+ () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]),
15
+ [value, defaultValue, min, max],
16
+ );
17
+
18
+ return (
19
+ <SliderPrimitive.Root
20
+ className="data-horizontal:w-full data-vertical:h-full"
21
+ data-slot="slider"
22
+ defaultValue={defaultValue}
23
+ value={value}
24
+ min={min}
25
+ max={max}
26
+ thumbAlignment="edge"
27
+ {...props}
28
+ >
29
+ <SliderPrimitive.Control className="data-vertical:min-h-40 relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:w-auto data-vertical:flex-col">
30
+ <SliderPrimitive.Track
31
+ data-slot="slider-track"
32
+ className="bg-muted rounded-full data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1.5 relative overflow-hidden select-none"
33
+ >
34
+ <SliderPrimitive.Indicator
35
+ data-slot="slider-range"
36
+ className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
37
+ />
38
+ </SliderPrimitive.Track>
39
+ {Array.from({ length: _values.length }, (_, index) => (
40
+ <SliderPrimitive.Thumb
41
+ data-slot="slider-thumb"
42
+ key={index}
43
+ className="border-primary ring-ring/50 size-4 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden block shrink-0 select-none disabled:pointer-events-none disabled:opacity-50"
44
+ />
45
+ ))}
46
+ </SliderPrimitive.Control>
47
+ </SliderPrimitive.Root>
48
+ );
49
+ }
50
+
51
+ export { Slider };
@@ -0,0 +1,43 @@
1
+ import {
2
+ CircleCheckIcon,
3
+ InfoIcon,
4
+ Loader2Icon,
5
+ OctagonXIcon,
6
+ TriangleAlertIcon,
7
+ } from 'lucide-react';
8
+ import { useTheme } from 'next-themes';
9
+ import { Toaster as Sonner, type ToasterProps } from 'sonner';
10
+
11
+ const Toaster = ({ ...props }: ToasterProps) => {
12
+ const { theme = 'system' } = useTheme();
13
+
14
+ return (
15
+ <Sonner
16
+ theme={theme as ToasterProps['theme']}
17
+ className="toaster group"
18
+ icons={{
19
+ success: <CircleCheckIcon className="size-4" />,
20
+ info: <InfoIcon className="size-4" />,
21
+ warning: <TriangleAlertIcon className="size-4" />,
22
+ error: <OctagonXIcon className="size-4" />,
23
+ loading: <Loader2Icon className="size-4 animate-spin" />,
24
+ }}
25
+ style={
26
+ {
27
+ '--normal-bg': 'var(--popover)',
28
+ '--normal-text': 'var(--popover-foreground)',
29
+ '--normal-border': 'var(--border)',
30
+ '--border-radius': 'var(--radius)',
31
+ } as React.CSSProperties
32
+ }
33
+ toastOptions={{
34
+ classNames: {
35
+ toast: 'cn-toast',
36
+ },
37
+ }}
38
+ {...props}
39
+ />
40
+ );
41
+ };
42
+
43
+ export { Toaster };
@@ -0,0 +1,14 @@
1
+ import { Loader2Icon } from 'lucide-react';
2
+
3
+ function Spinner({ ...props }: Omit<React.ComponentProps<'svg'>, 'className'>) {
4
+ return (
5
+ <Loader2Icon
6
+ role="status"
7
+ aria-label="Loading"
8
+ className="size-4 shrink-0 animate-spin"
9
+ {...props}
10
+ />
11
+ );
12
+ }
13
+
14
+ export { Spinner };
@@ -0,0 +1,72 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+
4
+ const stackVariants = cva('flex', {
5
+ variants: {
6
+ direction: {
7
+ row: 'flex-row',
8
+ column: 'flex-col',
9
+ 'row-reverse': 'flex-row-reverse',
10
+ 'column-reverse': 'flex-col-reverse',
11
+ },
12
+ align: {
13
+ start: 'items-start',
14
+ center: 'items-center',
15
+ end: 'items-end',
16
+ stretch: 'items-stretch',
17
+ baseline: 'items-baseline',
18
+ },
19
+ textAlign: {
20
+ left: 'text-left',
21
+ center: 'text-center',
22
+ right: 'text-right',
23
+ },
24
+ justify: {
25
+ start: 'justify-start',
26
+ center: 'justify-center',
27
+ end: 'justify-end',
28
+ between: 'justify-between',
29
+ around: 'justify-around',
30
+ evenly: 'justify-evenly',
31
+ },
32
+ gap: {
33
+ '0': 'gap-0',
34
+ '0.5': 'gap-0.5',
35
+ '1': 'gap-1',
36
+ '1.5': 'gap-1.5',
37
+ '2': 'gap-2',
38
+ '2.5': 'gap-2.5',
39
+ '3': 'gap-3',
40
+ '4': 'gap-4',
41
+ '5': 'gap-5',
42
+ '6': 'gap-6',
43
+ '8': 'gap-8',
44
+ '10': 'gap-10',
45
+ '12': 'gap-12',
46
+ },
47
+ wrap: {
48
+ true: 'flex-wrap',
49
+ false: 'flex-nowrap',
50
+ },
51
+ },
52
+ defaultVariants: {
53
+ direction: 'column',
54
+ gap: '0',
55
+ wrap: false,
56
+ },
57
+ });
58
+
59
+ interface StackProps
60
+ extends Omit<React.ComponentProps<'div'>, 'className'>, VariantProps<typeof stackVariants> {}
61
+
62
+ function Stack({ direction, align, justify, gap, wrap, textAlign, ...props }: StackProps) {
63
+ return (
64
+ <div
65
+ data-slot="stack"
66
+ className={stackVariants({ direction, align, justify, gap, wrap, textAlign })}
67
+ {...props}
68
+ />
69
+ );
70
+ }
71
+
72
+ export { Stack, stackVariants };
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import { Switch as SwitchPrimitive } from '@base-ui/react/switch';
4
+
5
+ function Switch({
6
+ size = 'default',
7
+ ...props
8
+ }: Omit<SwitchPrimitive.Root.Props, 'className'> & {
9
+ size?: 'sm' | 'default';
10
+ }) {
11
+ return (
12
+ <SwitchPrimitive.Root
13
+ data-slot="switch"
14
+ data-size={size}
15
+ className="data-checked:bg-primary data-unchecked:bg-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 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent shadow-xs focus-visible:ring-[3px] aria-invalid:ring-[3px] data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] peer group/switch relative inline-flex items-center transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-disabled:cursor-not-allowed data-disabled:opacity-50"
16
+ {...props}
17
+ >
18
+ <SwitchPrimitive.Thumb
19
+ data-slot="switch-thumb"
20
+ className="bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground rounded-full group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 pointer-events-none block ring-0 transition-transform"
21
+ />
22
+ </SwitchPrimitive.Root>
23
+ );
24
+ }
25
+
26
+ export { Switch };
@@ -0,0 +1,65 @@
1
+ import * as React from 'react';
2
+
3
+ function Table({ ...props }: Omit<React.ComponentProps<'table'>, 'className'>) {
4
+ return (
5
+ <div data-slot="table-container" className="relative w-full overflow-x-auto">
6
+ <table data-slot="table" className="w-full caption-bottom text-sm" {...props} />
7
+ </div>
8
+ );
9
+ }
10
+
11
+ function TableHeader({ ...props }: Omit<React.ComponentProps<'thead'>, 'className'>) {
12
+ return <thead data-slot="table-header" className="[&_tr]:border-b" {...props} />;
13
+ }
14
+
15
+ function TableBody({ ...props }: Omit<React.ComponentProps<'tbody'>, 'className'>) {
16
+ return <tbody data-slot="table-body" className="[&_tr:last-child]:border-0" {...props} />;
17
+ }
18
+
19
+ function TableFooter({ ...props }: Omit<React.ComponentProps<'tfoot'>, 'className'>) {
20
+ return (
21
+ <tfoot
22
+ data-slot="table-footer"
23
+ className="bg-muted/50 border-t font-medium [&>tr]:last:border-b-0"
24
+ {...props}
25
+ />
26
+ );
27
+ }
28
+
29
+ function TableRow({ ...props }: Omit<React.ComponentProps<'tr'>, 'className'>) {
30
+ return (
31
+ <tr
32
+ data-slot="table-row"
33
+ className="hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors"
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function TableHead({ ...props }: Omit<React.ComponentProps<'th'>, 'className'>) {
40
+ return (
41
+ <th
42
+ data-slot="table-head"
43
+ className="text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0"
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function TableCell({ ...props }: Omit<React.ComponentProps<'td'>, 'className'>) {
50
+ return (
51
+ <td
52
+ data-slot="table-cell"
53
+ className="p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0"
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ function TableCaption({ ...props }: Omit<React.ComponentProps<'caption'>, 'className'>) {
60
+ return (
61
+ <caption data-slot="table-caption" className="text-muted-foreground mt-4 text-sm" {...props} />
62
+ );
63
+ }
64
+
65
+ export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow };
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ import { Tabs as TabsPrimitive } from '@base-ui/react/tabs';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+
6
+ function Tabs({
7
+ orientation = 'horizontal',
8
+ ...props
9
+ }: Omit<TabsPrimitive.Root.Props, 'className'>) {
10
+ return (
11
+ <TabsPrimitive.Root
12
+ data-slot="tabs"
13
+ data-orientation={orientation}
14
+ className="gap-2 group/tabs flex data-[orientation=horizontal]:flex-col"
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ const tabsListVariants = cva(
21
+ 'rounded-lg p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col',
22
+ {
23
+ variants: {
24
+ variant: {
25
+ default: 'bg-muted',
26
+ line: 'gap-1 bg-transparent',
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: 'default',
31
+ },
32
+ },
33
+ );
34
+
35
+ function TabsList({
36
+ variant = 'default',
37
+ ...props
38
+ }: Omit<TabsPrimitive.List.Props, 'className'> & VariantProps<typeof tabsListVariants>) {
39
+ return (
40
+ <TabsPrimitive.List
41
+ data-slot="tabs-list"
42
+ data-variant={variant}
43
+ className={tabsListVariants({ variant })}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function TabsTrigger({ ...props }: Omit<TabsPrimitive.Tab.Props, 'className'>) {
50
+ return (
51
+ <TabsPrimitive.Tab
52
+ data-slot="tabs-trigger"
53
+ className="gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg:not([class*='size-'])]:size-4 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent data-active:bg-background dark:data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 data-active:text-foreground after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100"
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ function TabsContent({ ...props }: Omit<TabsPrimitive.Panel.Props, 'className'>) {
60
+ return (
61
+ <TabsPrimitive.Panel
62
+ data-slot="tabs-content"
63
+ className="text-sm flex-1 outline-none"
64
+ {...props}
65
+ />
66
+ );
67
+ }
68
+
69
+ export { Tabs, TabsContent, TabsList, tabsListVariants, TabsTrigger };
@@ -0,0 +1,59 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+
4
+ const textVariants = cva('', {
5
+ variants: {
6
+ size: {
7
+ xs: 'text-xs',
8
+ sm: 'text-sm',
9
+ base: 'text-base',
10
+ lg: 'text-lg',
11
+ },
12
+ variant: {
13
+ default: 'text-foreground',
14
+ muted: 'text-muted-foreground',
15
+ primary: 'text-primary',
16
+ destructive: 'text-destructive',
17
+ success: 'text-green-600 dark:text-green-400',
18
+ },
19
+ weight: {
20
+ normal: 'font-normal',
21
+ medium: 'font-medium',
22
+ semibold: 'font-semibold',
23
+ },
24
+ leading: {
25
+ tight: 'leading-tight',
26
+ snug: 'leading-snug',
27
+ normal: 'leading-normal',
28
+ relaxed: 'leading-relaxed',
29
+ },
30
+ font: {
31
+ sans: 'font-sans',
32
+ mono: 'font-mono tabular-nums',
33
+ },
34
+ },
35
+ defaultVariants: {
36
+ size: 'base',
37
+ variant: 'default',
38
+ weight: 'normal',
39
+ leading: 'normal',
40
+ font: 'sans',
41
+ },
42
+ });
43
+
44
+ interface TextProps
45
+ extends Omit<React.HTMLAttributes<HTMLElement>, 'className'>, VariantProps<typeof textVariants> {
46
+ as?: 'p' | 'span' | 'div';
47
+ }
48
+
49
+ function Text({ as: Component = 'p', size, variant, weight, leading, font, ...props }: TextProps) {
50
+ return (
51
+ <Component
52
+ data-slot="text"
53
+ className={textVariants({ size, variant, weight, leading, font })}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ export { Text, textVariants };
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+
3
+ function Textarea({ ...props }: Omit<React.ComponentProps<'textarea'>, 'className'>) {
4
+ return (
5
+ <textarea
6
+ data-slot="textarea"
7
+ className="border-input dark:bg-input/30 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 rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] 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"
8
+ {...props}
9
+ />
10
+ );
11
+ }
12
+
13
+ export { Textarea };
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+
3
+ import { Toggle as TogglePrimitive } from '@base-ui/react/toggle';
4
+ import { ToggleGroup as ToggleGroupPrimitive } from '@base-ui/react/toggle-group';
5
+ import { type VariantProps } from 'class-variance-authority';
6
+ import * as React from 'react';
7
+
8
+ import { cn } from '../../../lib/utils';
9
+ import { toggleVariants } from './toggle';
10
+
11
+ const ToggleGroupContext = React.createContext<
12
+ VariantProps<typeof toggleVariants> & {
13
+ spacing?: number;
14
+ orientation?: 'horizontal' | 'vertical';
15
+ }
16
+ >({
17
+ size: 'default',
18
+ variant: 'default',
19
+ spacing: 0,
20
+ orientation: 'horizontal',
21
+ });
22
+
23
+ type ToggleGroupProps = Omit<ToggleGroupPrimitive.Props, 'className'> &
24
+ VariantProps<typeof toggleVariants> & {
25
+ spacing?: number;
26
+ orientation?: 'horizontal' | 'vertical';
27
+ };
28
+
29
+ function ToggleGroup({
30
+ variant,
31
+ size,
32
+ spacing = 0,
33
+ orientation = 'horizontal',
34
+ children,
35
+ ...props
36
+ }: ToggleGroupProps) {
37
+ return (
38
+ <ToggleGroupPrimitive
39
+ data-slot="toggle-group"
40
+ data-variant={variant}
41
+ data-size={size}
42
+ data-spacing={spacing}
43
+ data-orientation={orientation}
44
+ style={{ '--gap': spacing } as React.CSSProperties}
45
+ className="rounded-md data-[spacing=0]:data-[variant=outline]:shadow-xs group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch"
46
+ {...props}
47
+ >
48
+ <ToggleGroupContext.Provider value={{ variant, size, spacing, orientation }}>
49
+ {children}
50
+ </ToggleGroupContext.Provider>
51
+ </ToggleGroupPrimitive>
52
+ );
53
+ }
54
+
55
+ type ToggleGroupItemProps = Omit<TogglePrimitive.Props, 'className'> &
56
+ VariantProps<typeof toggleVariants>;
57
+
58
+ function ToggleGroupItem({
59
+ children,
60
+ variant = 'default',
61
+ size = 'default',
62
+ ...props
63
+ }: ToggleGroupItemProps) {
64
+ const context = React.useContext(ToggleGroupContext);
65
+
66
+ return (
67
+ <TogglePrimitive
68
+ data-slot="toggle-group-item"
69
+ data-variant={context.variant || variant}
70
+ data-size={context.size || size}
71
+ data-spacing={context.spacing}
72
+ className={cn(
73
+ 'data-[state=on]:bg-muted group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md shrink-0 focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t',
74
+ toggleVariants({
75
+ variant: context.variant || variant,
76
+ size: context.size || size,
77
+ }),
78
+ )}
79
+ {...props}
80
+ >
81
+ {children}
82
+ </TogglePrimitive>
83
+ );
84
+ }
85
+
86
+ export { ToggleGroup, ToggleGroupItem };
87
+ export type { ToggleGroupItemProps, ToggleGroupProps };
@@ -0,0 +1,42 @@
1
+ import { Toggle as TogglePrimitive } from '@base-ui/react/toggle';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+
4
+ import { cn } from '../../../lib/utils';
5
+
6
+ const toggleVariants = cva(
7
+ "hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive gap-1 rounded-md text-sm font-medium transition-[color,box-shadow] [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-muted inline-flex items-center justify-center whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-transparent',
12
+ outline: 'border-input hover:bg-muted border bg-transparent shadow-xs',
13
+ },
14
+ size: {
15
+ default: 'h-9 min-w-9 px-2',
16
+ sm: 'h-8 min-w-8 px-1.5',
17
+ lg: 'h-10 min-w-10 px-2.5',
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: 'default',
22
+ size: 'default',
23
+ },
24
+ },
25
+ );
26
+
27
+ function Toggle({
28
+ className,
29
+ variant = 'default',
30
+ size = 'default',
31
+ ...props
32
+ }: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
33
+ return (
34
+ <TogglePrimitive
35
+ data-slot="toggle"
36
+ className={cn(toggleVariants({ variant, size, className }))}
37
+ {...props}
38
+ />
39
+ );
40
+ }
41
+
42
+ export { Toggle, toggleVariants };
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+
3
+ import { Tooltip as TooltipPrimitive } from '@base-ui/react/tooltip';
4
+
5
+ function TooltipProvider({ delay = 0, ...props }: TooltipPrimitive.Provider.Props) {
6
+ return <TooltipPrimitive.Provider data-slot="tooltip-provider" delay={delay} {...props} />;
7
+ }
8
+
9
+ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
10
+ return (
11
+ <TooltipProvider>
12
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
13
+ </TooltipProvider>
14
+ );
15
+ }
16
+
17
+ function TooltipTrigger({ ...props }: Omit<TooltipPrimitive.Trigger.Props, 'className'>) {
18
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
19
+ }
20
+
21
+ function TooltipContent({
22
+ side = 'top',
23
+ sideOffset = 4,
24
+ align = 'center',
25
+ alignOffset = 0,
26
+ children,
27
+ ...props
28
+ }: Omit<TooltipPrimitive.Popup.Props, 'className'> &
29
+ Pick<TooltipPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
30
+ return (
31
+ <TooltipPrimitive.Portal>
32
+ <TooltipPrimitive.Positioner
33
+ align={align}
34
+ alignOffset={alignOffset}
35
+ side={side}
36
+ sideOffset={sideOffset}
37
+ className="isolate z-50"
38
+ >
39
+ <TooltipPrimitive.Popup
40
+ data-slot="tooltip-content"
41
+ className="data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-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 rounded-md px-3 py-1.5 text-xs bg-foreground text-background z-50 w-fit max-w-xs origin-(--transform-origin)"
42
+ {...props}
43
+ >
44
+ {children}
45
+ <TooltipPrimitive.Arrow className="size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground z-50 data-[side=bottom]:top-1 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
46
+ </TooltipPrimitive.Popup>
47
+ </TooltipPrimitive.Positioner>
48
+ </TooltipPrimitive.Portal>
49
+ );
50
+ }
51
+
52
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { cn } from '../lib/utils';
2
+
3
+ export * from './components/ui';