@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.
- package/README.md +110 -0
- package/components.json +21 -0
- package/hooks/use-mobile.tsx +19 -0
- package/lib/utils.ts +6 -0
- package/package.json +103 -0
- package/postcss.config.mjs +8 -0
- package/src/components/ui/accordion.tsx +60 -0
- package/src/components/ui/alert-dialog.tsx +161 -0
- package/src/components/ui/alert.tsx +109 -0
- package/src/components/ui/aspect-ratio.tsx +21 -0
- package/src/components/ui/avatar.tsx +74 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/breadcrumb.tsx +254 -0
- package/src/components/ui/button-group.tsx +89 -0
- package/src/components/ui/button.tsx +122 -0
- package/src/components/ui/calendar.tsx +190 -0
- package/src/components/ui/card.tsx +155 -0
- package/src/components/ui/carousel.tsx +216 -0
- package/src/components/ui/chart.tsx +325 -0
- package/src/components/ui/checkbox.tsx +22 -0
- package/src/components/ui/collapsible.tsx +17 -0
- package/src/components/ui/combobox.tsx +248 -0
- package/src/components/ui/command.tsx +189 -0
- package/src/components/ui/container.tsx +34 -0
- package/src/components/ui/context-menu.tsx +235 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/drawer.tsx +102 -0
- package/src/components/ui/dropdown-menu.tsx +242 -0
- package/src/components/ui/empty.tsx +94 -0
- package/src/components/ui/field.tsx +215 -0
- package/src/components/ui/grid.tsx +135 -0
- package/src/components/ui/heading.tsx +56 -0
- package/src/components/ui/hover-card.tsx +46 -0
- package/src/components/ui/index.ts +61 -0
- package/src/components/ui/input-group.tsx +128 -0
- package/src/components/ui/input-otp.tsx +84 -0
- package/src/components/ui/input.tsx +15 -0
- package/src/components/ui/item.tsx +188 -0
- package/src/components/ui/kbd.tsx +26 -0
- package/src/components/ui/label.tsx +15 -0
- package/src/components/ui/menubar.tsx +163 -0
- package/src/components/ui/navigation-menu.tsx +147 -0
- package/src/components/ui/page-header.tsx +51 -0
- package/src/components/ui/page-layout.tsx +65 -0
- package/src/components/ui/pagination.tsx +104 -0
- package/src/components/ui/popover.tsx +57 -0
- package/src/components/ui/progress.tsx +61 -0
- package/src/components/ui/radio-group.tsx +37 -0
- package/src/components/ui/resizable.tsx +41 -0
- package/src/components/ui/scroll-area.tsx +48 -0
- package/src/components/ui/section.tsx +64 -0
- package/src/components/ui/select.tsx +166 -0
- package/src/components/ui/separator.tsx +17 -0
- package/src/components/ui/sheet.tsx +104 -0
- package/src/components/ui/sidebar.tsx +707 -0
- package/src/components/ui/skeleton.tsx +5 -0
- package/src/components/ui/slider.tsx +51 -0
- package/src/components/ui/sonner.tsx +43 -0
- package/src/components/ui/spinner.tsx +14 -0
- package/src/components/ui/stack.tsx +72 -0
- package/src/components/ui/switch.tsx +26 -0
- package/src/components/ui/table.tsx +65 -0
- package/src/components/ui/tabs.tsx +69 -0
- package/src/components/ui/text.tsx +59 -0
- package/src/components/ui/textarea.tsx +13 -0
- package/src/components/ui/toggle-group.tsx +87 -0
- package/src/components/ui/toggle.tsx +42 -0
- package/src/components/ui/tooltip.tsx +52 -0
- package/src/index.ts +3 -0
- package/src/styles/globals.css +122 -0
- 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 };
|