@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,163 @@
1
+ import { Menu as MenuPrimitive } from '@base-ui/react/menu';
2
+ import { Menubar as MenubarPrimitive } from '@base-ui/react/menubar';
3
+ import * as React from 'react';
4
+
5
+ import { CheckIcon } from 'lucide-react';
6
+ import {
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuGroup,
10
+ DropdownMenuItem,
11
+ DropdownMenuLabel,
12
+ DropdownMenuPortal,
13
+ DropdownMenuRadioGroup,
14
+ DropdownMenuSeparator,
15
+ DropdownMenuShortcut,
16
+ DropdownMenuSub,
17
+ DropdownMenuSubContent,
18
+ DropdownMenuSubTrigger,
19
+ DropdownMenuTrigger,
20
+ } from './dropdown-menu';
21
+
22
+ function Menubar({ ...props }: Omit<MenubarPrimitive.Props, 'className'>) {
23
+ return (
24
+ <MenubarPrimitive
25
+ data-slot="menubar"
26
+ className="bg-background h-9 gap-1 rounded-md border p-1 shadow-xs flex items-center"
27
+ {...props}
28
+ />
29
+ );
30
+ }
31
+
32
+ function MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) {
33
+ return <DropdownMenu data-slot="menubar-menu" {...props} />;
34
+ }
35
+
36
+ function MenubarGroup({ ...props }: React.ComponentProps<typeof DropdownMenuGroup>) {
37
+ return <DropdownMenuGroup data-slot="menubar-group" {...props} />;
38
+ }
39
+
40
+ function MenubarPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPortal>) {
41
+ return <DropdownMenuPortal data-slot="menubar-portal" {...props} />;
42
+ }
43
+
44
+ function MenubarTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuTrigger>) {
45
+ return <DropdownMenuTrigger data-slot="menubar-trigger" variant="menubar" {...props} />;
46
+ }
47
+
48
+ function MenubarContent({
49
+ align = 'start',
50
+ alignOffset = -4,
51
+ sideOffset = 8,
52
+ ...props
53
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
54
+ return (
55
+ <DropdownMenuContent
56
+ data-slot="menubar-content"
57
+ align={align}
58
+ alignOffset={alignOffset}
59
+ sideOffset={sideOffset}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ function MenubarItem({
66
+ inset,
67
+ variant = 'default',
68
+ ...props
69
+ }: React.ComponentProps<typeof DropdownMenuItem>) {
70
+ return <DropdownMenuItem data-slot="menubar-item" inset={inset} variant={variant} {...props} />;
71
+ }
72
+
73
+ function MenubarCheckboxItem({
74
+ children,
75
+ checked,
76
+ ...props
77
+ }: Omit<MenuPrimitive.CheckboxItem.Props, 'className'>) {
78
+ return (
79
+ <MenuPrimitive.CheckboxItem
80
+ data-slot="menubar-checkbox-item"
81
+ className="focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm data-disabled:opacity-50 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0"
82
+ checked={checked}
83
+ {...props}
84
+ >
85
+ <span className="left-2 size-4 [&_svg:not([class*='size-'])]:size-4 pointer-events-none absolute flex items-center justify-center">
86
+ <MenuPrimitive.CheckboxItemIndicator>
87
+ <CheckIcon />
88
+ </MenuPrimitive.CheckboxItemIndicator>
89
+ </span>
90
+ {children}
91
+ </MenuPrimitive.CheckboxItem>
92
+ );
93
+ }
94
+
95
+ function MenubarRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuRadioGroup>) {
96
+ return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />;
97
+ }
98
+
99
+ function MenubarRadioItem({
100
+ children,
101
+ ...props
102
+ }: Omit<MenuPrimitive.RadioItem.Props, 'className'>) {
103
+ return (
104
+ <MenuPrimitive.RadioItem
105
+ data-slot="menubar-radio-item"
106
+ className="focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0"
107
+ {...props}
108
+ >
109
+ <span className="left-2 size-4 [&_svg:not([class*='size-'])]:size-4 pointer-events-none absolute flex items-center justify-center">
110
+ <MenuPrimitive.RadioItemIndicator>
111
+ <CheckIcon />
112
+ </MenuPrimitive.RadioItemIndicator>
113
+ </span>
114
+ {children}
115
+ </MenuPrimitive.RadioItem>
116
+ );
117
+ }
118
+
119
+ function MenubarLabel({ inset, ...props }: React.ComponentProps<typeof DropdownMenuLabel>) {
120
+ return <DropdownMenuLabel data-slot="menubar-label" inset={inset} {...props} />;
121
+ }
122
+
123
+ function MenubarSeparator({ ...props }: React.ComponentProps<typeof DropdownMenuSeparator>) {
124
+ return <DropdownMenuSeparator data-slot="menubar-separator" {...props} />;
125
+ }
126
+
127
+ function MenubarShortcut({ ...props }: React.ComponentProps<typeof DropdownMenuShortcut>) {
128
+ return <DropdownMenuShortcut data-slot="menubar-shortcut" {...props} />;
129
+ }
130
+
131
+ function MenubarSub({ ...props }: React.ComponentProps<typeof DropdownMenuSub>) {
132
+ return <DropdownMenuSub data-slot="menubar-sub" {...props} />;
133
+ }
134
+
135
+ function MenubarSubTrigger({
136
+ inset,
137
+ ...props
138
+ }: React.ComponentProps<typeof DropdownMenuSubTrigger>) {
139
+ return <DropdownMenuSubTrigger data-slot="menubar-sub-trigger" inset={inset} {...props} />;
140
+ }
141
+
142
+ function MenubarSubContent({ ...props }: React.ComponentProps<typeof DropdownMenuSubContent>) {
143
+ return <DropdownMenuSubContent data-slot="menubar-sub-content" {...props} />;
144
+ }
145
+
146
+ export {
147
+ Menubar,
148
+ MenubarCheckboxItem,
149
+ MenubarContent,
150
+ MenubarGroup,
151
+ MenubarItem,
152
+ MenubarLabel,
153
+ MenubarMenu,
154
+ MenubarPortal,
155
+ MenubarRadioGroup,
156
+ MenubarRadioItem,
157
+ MenubarSeparator,
158
+ MenubarShortcut,
159
+ MenubarSub,
160
+ MenubarSubContent,
161
+ MenubarSubTrigger,
162
+ MenubarTrigger,
163
+ };
@@ -0,0 +1,147 @@
1
+ import { NavigationMenu as NavigationMenuPrimitive } from '@base-ui/react/navigation-menu';
2
+ import { cva } from 'class-variance-authority';
3
+
4
+ import { ChevronDownIcon } from 'lucide-react';
5
+ import { cn } from '../../../lib/utils';
6
+
7
+ function NavigationMenu({ className, children, ...props }: NavigationMenuPrimitive.Root.Props) {
8
+ return (
9
+ <NavigationMenuPrimitive.Root
10
+ data-slot="navigation-menu"
11
+ className={cn(
12
+ 'max-w-max group/navigation-menu relative flex max-w-max flex-1 items-center justify-center',
13
+ className,
14
+ )}
15
+ {...props}
16
+ >
17
+ {children}
18
+ <NavigationMenuPositioner />
19
+ </NavigationMenuPrimitive.Root>
20
+ );
21
+ }
22
+
23
+ function NavigationMenuList({ className, ...props }: NavigationMenuPrimitive.List.Props) {
24
+ return (
25
+ <NavigationMenuPrimitive.List
26
+ data-slot="navigation-menu-list"
27
+ className={cn('gap-0 group flex flex-1 list-none items-center justify-center', className)}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
33
+ function NavigationMenuItem({ className, ...props }: NavigationMenuPrimitive.Item.Props) {
34
+ return (
35
+ <NavigationMenuPrimitive.Item
36
+ data-slot="navigation-menu-item"
37
+ className={cn('relative', className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ const navigationMenuTriggerStyle = cva(
44
+ 'bg-background hover:bg-muted focus:bg-muted data-open:hover:bg-muted data-open:focus:bg-muted data-open:bg-muted/50 focus-visible:ring-ring/50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted rounded-md px-4 py-2 text-sm font-medium transition-all focus-visible:ring-[3px] focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center disabled:pointer-events-none outline-none',
45
+ );
46
+
47
+ function NavigationMenuTrigger({
48
+ className,
49
+ children,
50
+ ...props
51
+ }: NavigationMenuPrimitive.Trigger.Props) {
52
+ return (
53
+ <NavigationMenuPrimitive.Trigger
54
+ data-slot="navigation-menu-trigger"
55
+ className={cn(navigationMenuTriggerStyle(), 'group', className)}
56
+ {...props}
57
+ >
58
+ {children}{' '}
59
+ <ChevronDownIcon
60
+ className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-open/navigation-menu-trigger:rotate-180 group-data-popup-open/navigation-menu-trigger:rotate-180"
61
+ aria-hidden="true"
62
+ />
63
+ </NavigationMenuPrimitive.Trigger>
64
+ );
65
+ }
66
+
67
+ function NavigationMenuContent({ className, ...props }: NavigationMenuPrimitive.Content.Props) {
68
+ return (
69
+ <NavigationMenuPrimitive.Content
70
+ data-slot="navigation-menu-content"
71
+ className={cn(
72
+ 'data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:ring-foreground/10 p-2 pr-2.5 ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:duration-300 h-full w-auto **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none',
73
+ className,
74
+ )}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ function NavigationMenuPositioner({
81
+ className,
82
+ side = 'bottom',
83
+ sideOffset = 8,
84
+ align = 'start',
85
+ alignOffset = 0,
86
+ ...props
87
+ }: NavigationMenuPrimitive.Positioner.Props) {
88
+ return (
89
+ <NavigationMenuPrimitive.Portal>
90
+ <NavigationMenuPrimitive.Positioner
91
+ side={side}
92
+ sideOffset={sideOffset}
93
+ align={align}
94
+ alignOffset={alignOffset}
95
+ className={cn(
96
+ 'transition-[top,left,right,bottom] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0 isolate z-50 h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] data-[instant]:transition-none',
97
+ className,
98
+ )}
99
+ {...props}
100
+ >
101
+ <NavigationMenuPrimitive.Popup className="bg-popover text-popover-foreground ring-foreground/10 rounded-lg shadow ring-1 transition-all ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:scale-90 data-[starting-style]:opacity-0 xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin)">
102
+ <NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />
103
+ </NavigationMenuPrimitive.Popup>
104
+ </NavigationMenuPrimitive.Positioner>
105
+ </NavigationMenuPrimitive.Portal>
106
+ );
107
+ }
108
+
109
+ function NavigationMenuLink({ className, ...props }: NavigationMenuPrimitive.Link.Props) {
110
+ return (
111
+ <NavigationMenuPrimitive.Link
112
+ data-slot="navigation-menu-link"
113
+ className={cn(
114
+ "data-[active=true]:focus:bg-muted data-[active=true]:hover:bg-muted data-[active=true]:bg-muted/50 focus-visible:ring-ring/50 hover:bg-muted focus:bg-muted flex items-center gap-1.5 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
115
+ className,
116
+ )}
117
+ {...props}
118
+ />
119
+ );
120
+ }
121
+
122
+ function NavigationMenuIndicator({ className, ...props }: NavigationMenuPrimitive.Icon.Props) {
123
+ return (
124
+ <NavigationMenuPrimitive.Icon
125
+ data-slot="navigation-menu-indicator"
126
+ className={cn(
127
+ 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden',
128
+ className,
129
+ )}
130
+ {...props}
131
+ >
132
+ <div className="bg-border rounded-tl-sm shadow-md relative top-[60%] h-2 w-2 rotate-45" />
133
+ </NavigationMenuPrimitive.Icon>
134
+ );
135
+ }
136
+
137
+ export {
138
+ NavigationMenu,
139
+ NavigationMenuContent,
140
+ NavigationMenuIndicator,
141
+ NavigationMenuItem,
142
+ NavigationMenuLink,
143
+ NavigationMenuList,
144
+ NavigationMenuPositioner,
145
+ NavigationMenuTrigger,
146
+ navigationMenuTriggerStyle,
147
+ };
@@ -0,0 +1,51 @@
1
+ import * as React from 'react';
2
+
3
+ import { Heading } from './heading';
4
+ import { Stack } from './stack';
5
+ import { Text } from './text';
6
+
7
+ interface PageHeaderProps extends Omit<React.ComponentProps<'div'>, 'className'> {
8
+ title: string;
9
+ description?: string;
10
+ /** Additional descriptive text below description */
11
+ meta?: string;
12
+ actions?: React.ReactNode;
13
+ }
14
+
15
+ function PageHeader({ title, description, meta, actions, children, ...props }: PageHeaderProps) {
16
+ return (
17
+ <div data-slot="page-header" className="flex items-center justify-between gap-4" {...props}>
18
+ <Stack gap="1">
19
+ <Heading level="1">{title}</Heading>
20
+ {description && (
21
+ <Text size="sm" variant="muted">
22
+ {description}
23
+ </Text>
24
+ )}
25
+ {meta && (
26
+ <Text size="xs" variant="muted">
27
+ {meta}
28
+ </Text>
29
+ )}
30
+ {children}
31
+ </Stack>
32
+ {actions && <div className="flex shrink-0 items-center gap-3">{actions}</div>}
33
+ </div>
34
+ );
35
+ }
36
+
37
+ function PageHeaderTitle({ ...props }: Omit<React.ComponentProps<typeof Heading>, 'className'>) {
38
+ return <Heading data-slot="page-header-title" level="1" {...props} />;
39
+ }
40
+
41
+ function PageHeaderDescription({ ...props }: Omit<React.ComponentProps<typeof Text>, 'className'>) {
42
+ return <Text data-slot="page-header-description" size="sm" variant="muted" {...props} />;
43
+ }
44
+
45
+ function PageHeaderActions({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
46
+ return (
47
+ <div data-slot="page-header-actions" className="flex shrink-0 items-center gap-3" {...props} />
48
+ );
49
+ }
50
+
51
+ export { PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderTitle };
@@ -0,0 +1,65 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import * as React from 'react';
3
+
4
+ const pageLayoutVariants = cva('min-h-dvh bg-background text-foreground', {
5
+ variants: {
6
+ variant: {
7
+ default: 'flex flex-col',
8
+ center: 'flex items-center justify-center',
9
+ },
10
+ contentWidth: {
11
+ auto: '',
12
+ sm: 'w-full max-w-sm',
13
+ md: 'w-full max-w-md',
14
+ lg: 'w-full max-w-lg',
15
+ xl: 'w-full max-w-xl',
16
+ '2xl': 'w-full max-w-2xl',
17
+ '3xl': 'w-full max-w-3xl',
18
+ full: 'w-full max-w-full',
19
+ },
20
+ padding: {
21
+ none: '',
22
+ sm: 'px-4 py-6',
23
+ default: 'px-4 py-10 sm:px-6 lg:px-8',
24
+ lg: 'px-6 py-12 sm:px-8 lg:px-12',
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: 'default',
29
+ contentWidth: 'auto',
30
+ padding: 'default',
31
+ },
32
+ });
33
+
34
+ interface PageLayoutProps
35
+ extends
36
+ Omit<React.ComponentProps<'div'>, 'className' | 'children'>,
37
+ VariantProps<typeof pageLayoutVariants> {
38
+ children?: React.ReactNode;
39
+ }
40
+
41
+ function PageLayout({ variant, padding, contentWidth, children, ...props }: PageLayoutProps) {
42
+ const resolvedContentWidth = contentWidth ?? (variant === 'center' ? 'lg' : 'auto');
43
+
44
+ return (
45
+ <div
46
+ data-slot="page-layout"
47
+ data-variant={variant}
48
+ className={pageLayoutVariants({ variant, padding })}
49
+ {...props}
50
+ >
51
+ {resolvedContentWidth !== 'auto' ? (
52
+ <div
53
+ data-slot="page-layout-content"
54
+ className={pageLayoutVariants({ contentWidth: resolvedContentWidth })}
55
+ >
56
+ {children}
57
+ </div>
58
+ ) : (
59
+ children
60
+ )}
61
+ </div>
62
+ );
63
+ }
64
+
65
+ export { PageLayout, pageLayoutVariants };
@@ -0,0 +1,104 @@
1
+ import { Button as ButtonPrimitive } from '@base-ui/react/button';
2
+ import * as React from 'react';
3
+
4
+ import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from 'lucide-react';
5
+ import { cn } from '../../../lib/utils';
6
+ import { buttonVariants } from './button';
7
+
8
+ function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
9
+ return (
10
+ <nav
11
+ role="navigation"
12
+ aria-label="pagination"
13
+ data-slot="pagination"
14
+ className={cn('mx-auto flex w-full justify-center', className)}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ function PaginationContent({ className, ...props }: React.ComponentProps<'ul'>) {
21
+ return (
22
+ <ul
23
+ data-slot="pagination-content"
24
+ className={cn('gap-1 flex items-center', className)}
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
31
+ return <li data-slot="pagination-item" {...props} />;
32
+ }
33
+
34
+ type PaginationLinkProps = {
35
+ isActive?: boolean;
36
+ size?: 'default' | 'icon';
37
+ } & Omit<React.ComponentProps<'a'>, 'className'>;
38
+
39
+ function PaginationLink({ isActive, size = 'icon', ...props }: PaginationLinkProps) {
40
+ return (
41
+ <ButtonPrimitive
42
+ className={buttonVariants({ variant: isActive ? 'outline' : 'ghost', size })}
43
+ render={
44
+ <a
45
+ aria-current={isActive ? 'page' : undefined}
46
+ data-slot="pagination-link"
47
+ data-active={isActive}
48
+ {...props}
49
+ />
50
+ }
51
+ />
52
+ );
53
+ }
54
+
55
+ function PaginationPrevious({ ...props }: Omit<PaginationLinkProps, 'size'>) {
56
+ return (
57
+ <ButtonPrimitive
58
+ className={cn(buttonVariants({ variant: 'ghost', size: 'default' }), 'pl-2')}
59
+ render={<a aria-label="Go to previous page" data-slot="pagination-link" {...props} />}
60
+ >
61
+ <ChevronLeftIcon data-icon="inline-start" />
62
+ <span className="hidden sm:block">Previous</span>
63
+ </ButtonPrimitive>
64
+ );
65
+ }
66
+
67
+ function PaginationNext({ ...props }: Omit<PaginationLinkProps, 'size'>) {
68
+ return (
69
+ <ButtonPrimitive
70
+ className={cn(buttonVariants({ variant: 'ghost', size: 'default' }), 'pr-2')}
71
+ render={<a aria-label="Go to next page" data-slot="pagination-link" {...props} />}
72
+ >
73
+ <span className="hidden sm:block">Next</span>
74
+ <ChevronRightIcon data-icon="inline-end" />
75
+ </ButtonPrimitive>
76
+ );
77
+ }
78
+
79
+ function PaginationEllipsis({ className, ...props }: React.ComponentProps<'span'>) {
80
+ return (
81
+ <span
82
+ aria-hidden
83
+ data-slot="pagination-ellipsis"
84
+ className={cn(
85
+ "size-9 items-center justify-center [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center",
86
+ className,
87
+ )}
88
+ {...props}
89
+ >
90
+ <MoreHorizontalIcon />
91
+ <span className="sr-only">More pages</span>
92
+ </span>
93
+ );
94
+ }
95
+
96
+ export {
97
+ Pagination,
98
+ PaginationContent,
99
+ PaginationEllipsis,
100
+ PaginationItem,
101
+ PaginationLink,
102
+ PaginationNext,
103
+ PaginationPrevious,
104
+ };
@@ -0,0 +1,57 @@
1
+ import { Popover as PopoverPrimitive } from '@base-ui/react/popover';
2
+ import * as React from 'react';
3
+
4
+ function Popover({ ...props }: PopoverPrimitive.Root.Props) {
5
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />;
6
+ }
7
+
8
+ function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) {
9
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
10
+ }
11
+
12
+ function PopoverContent({
13
+ align = 'center',
14
+ alignOffset = 0,
15
+ side = 'bottom',
16
+ sideOffset = 4,
17
+ ...props
18
+ }: Omit<PopoverPrimitive.Popup.Props, 'className'> &
19
+ Pick<PopoverPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
20
+ return (
21
+ <PopoverPrimitive.Portal>
22
+ <PopoverPrimitive.Positioner
23
+ align={align}
24
+ alignOffset={alignOffset}
25
+ side={side}
26
+ sideOffset={sideOffset}
27
+ className="isolate z-50"
28
+ >
29
+ <PopoverPrimitive.Popup
30
+ data-slot="popover-content"
31
+ className="bg-popover text-popover-foreground 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 flex flex-col gap-2.5 rounded-lg p-2.5 text-sm shadow-md ring-1 duration-100 z-50 w-72 origin-(--transform-origin) outline-hidden"
32
+ {...props}
33
+ />
34
+ </PopoverPrimitive.Positioner>
35
+ </PopoverPrimitive.Portal>
36
+ );
37
+ }
38
+
39
+ function PopoverHeader({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
40
+ return <div data-slot="popover-header" className="flex flex-col gap-0.5 text-sm" {...props} />;
41
+ }
42
+
43
+ function PopoverTitle({ ...props }: Omit<PopoverPrimitive.Title.Props, 'className'>) {
44
+ return <PopoverPrimitive.Title data-slot="popover-title" className="font-medium" {...props} />;
45
+ }
46
+
47
+ function PopoverDescription({ ...props }: Omit<PopoverPrimitive.Description.Props, 'className'>) {
48
+ return (
49
+ <PopoverPrimitive.Description
50
+ data-slot="popover-description"
51
+ className="text-muted-foreground"
52
+ {...props}
53
+ />
54
+ );
55
+ }
56
+
57
+ export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger };
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import { Progress as ProgressPrimitive } from '@base-ui/react/progress';
4
+
5
+ function Progress({ children, value, ...props }: Omit<ProgressPrimitive.Root.Props, 'className'>) {
6
+ return (
7
+ <ProgressPrimitive.Root
8
+ value={value}
9
+ data-slot="progress"
10
+ className="flex flex-wrap gap-3"
11
+ {...props}
12
+ >
13
+ {children}
14
+ <ProgressTrack>
15
+ <ProgressIndicator />
16
+ </ProgressTrack>
17
+ </ProgressPrimitive.Root>
18
+ );
19
+ }
20
+
21
+ function ProgressTrack({ ...props }: Omit<ProgressPrimitive.Track.Props, 'className'>) {
22
+ return (
23
+ <ProgressPrimitive.Track
24
+ className="bg-muted h-1.5 rounded-full relative flex w-full items-center overflow-x-hidden"
25
+ data-slot="progress-track"
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function ProgressIndicator({ ...props }: Omit<ProgressPrimitive.Indicator.Props, 'className'>) {
32
+ return (
33
+ <ProgressPrimitive.Indicator
34
+ data-slot="progress-indicator"
35
+ className="bg-primary h-full transition-all"
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function ProgressLabel({ ...props }: Omit<ProgressPrimitive.Label.Props, 'className'>) {
42
+ return (
43
+ <ProgressPrimitive.Label
44
+ className="text-sm font-medium"
45
+ data-slot="progress-label"
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ function ProgressValue({ ...props }: Omit<ProgressPrimitive.Value.Props, 'className'>) {
52
+ return (
53
+ <ProgressPrimitive.Value
54
+ className="text-muted-foreground ml-auto text-sm tabular-nums"
55
+ data-slot="progress-value"
56
+ {...props}
57
+ />
58
+ );
59
+ }
60
+
61
+ export { Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue };
@@ -0,0 +1,37 @@
1
+ import { Radio as RadioPrimitive } from '@base-ui/react/radio';
2
+ import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group';
3
+
4
+ import { CircleIcon } from 'lucide-react';
5
+ import { cn } from '../../../lib/utils';
6
+
7
+ function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
8
+ return (
9
+ <RadioGroupPrimitive
10
+ data-slot="radio-group"
11
+ className={cn('grid gap-2 w-full', className)}
12
+ {...props}
13
+ />
14
+ );
15
+ }
16
+
17
+ function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {
18
+ return (
19
+ <RadioPrimitive.Root
20
+ data-slot="radio-group-item"
21
+ className={cn(
22
+ 'border-input text-primary 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 flex size-4 rounded-full shadow-xs focus-visible:ring-[3px] aria-invalid:ring-[3px] group/radio-group-item peer relative aspect-square shrink-0 border outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50',
23
+ className,
24
+ )}
25
+ {...props}
26
+ >
27
+ <RadioPrimitive.Indicator
28
+ data-slot="radio-group-indicator"
29
+ className="group-aria-invalid/radio-group-item:text-destructive text-primary flex size-4 items-center justify-center"
30
+ >
31
+ <CircleIcon className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-current" />
32
+ </RadioPrimitive.Indicator>
33
+ </RadioPrimitive.Root>
34
+ );
35
+ }
36
+
37
+ export { RadioGroup, RadioGroupItem };