@xemahq/ui-kernel 0.1.12 → 0.2.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/biome-host/host-bridge.d.ts +2 -0
- package/dist/lib/biome-host/host-bridge.d.ts.map +1 -1
- package/dist/lib/biome-host/host-bridge.js.map +1 -1
- package/dist/lib/capabilities/capability-provider.d.ts +15 -0
- package/dist/lib/capabilities/capability-provider.d.ts.map +1 -0
- package/dist/lib/capabilities/capability-provider.js +36 -0
- package/dist/lib/capabilities/capability-provider.js.map +1 -0
- package/dist/lib/capabilities/index.d.ts +4 -0
- package/dist/lib/capabilities/index.d.ts.map +1 -0
- package/dist/lib/capabilities/index.js +20 -0
- package/dist/lib/capabilities/index.js.map +1 -0
- package/dist/lib/capabilities/types.d.ts +18 -0
- package/dist/lib/capabilities/types.d.ts.map +1 -0
- package/dist/lib/capabilities/types.js +3 -0
- package/dist/lib/capabilities/types.js.map +1 -0
- package/dist/lib/capabilities/use-capability.d.ts +18 -0
- package/dist/lib/capabilities/use-capability.d.ts.map +1 -0
- package/dist/lib/capabilities/use-capability.js +21 -0
- package/dist/lib/capabilities/use-capability.js.map +1 -0
- package/dist/ui/chrome/AsyncBoundary.d.ts +22 -0
- package/dist/ui/chrome/AsyncBoundary.d.ts.map +1 -0
- package/dist/ui/chrome/AsyncBoundary.js +23 -0
- package/dist/ui/chrome/AsyncBoundary.js.map +1 -0
- package/dist/ui/chrome/EmptyState.d.ts +34 -0
- package/dist/ui/chrome/EmptyState.d.ts.map +1 -0
- package/dist/ui/chrome/EmptyState.js +27 -0
- package/dist/ui/chrome/EmptyState.js.map +1 -0
- package/dist/ui/chrome/ErrorCard.d.ts +11 -0
- package/dist/ui/chrome/ErrorCard.d.ts.map +1 -0
- package/dist/ui/chrome/ErrorCard.js +21 -0
- package/dist/ui/chrome/ErrorCard.js.map +1 -0
- package/dist/ui/chrome/LoadingState.d.ts +10 -0
- package/dist/ui/chrome/LoadingState.d.ts.map +1 -0
- package/dist/ui/chrome/LoadingState.js +17 -0
- package/dist/ui/chrome/LoadingState.js.map +1 -0
- package/dist/ui/chrome/PageHeader.d.ts +20 -0
- package/dist/ui/chrome/PageHeader.d.ts.map +1 -0
- package/dist/ui/chrome/PageHeader.js +26 -0
- package/dist/ui/chrome/PageHeader.js.map +1 -0
- package/dist/ui/chrome/StateCard.d.ts +24 -0
- package/dist/ui/chrome/StateCard.d.ts.map +1 -0
- package/dist/ui/chrome/StateCard.js +17 -0
- package/dist/ui/chrome/StateCard.js.map +1 -0
- package/dist/ui/cn.d.ts +3 -0
- package/dist/ui/cn.d.ts.map +1 -0
- package/dist/ui/cn.js +18 -0
- package/dist/ui/cn.js.map +1 -0
- package/dist/ui/index.d.ts +33 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +61 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/primitives/alert-dialog.d.ts +21 -0
- package/dist/ui/primitives/alert-dialog.d.ts.map +1 -0
- package/dist/ui/primitives/alert-dialog.js +72 -0
- package/dist/ui/primitives/alert-dialog.js.map +1 -0
- package/dist/ui/primitives/badge.d.ts +10 -0
- package/dist/ui/primitives/badge.d.ts.map +1 -0
- package/dist/ui/primitives/badge.js +60 -0
- package/dist/ui/primitives/badge.js.map +1 -0
- package/dist/ui/primitives/button.d.ts +12 -0
- package/dist/ui/primitives/button.d.ts.map +1 -0
- package/dist/ui/primitives/button.js +71 -0
- package/dist/ui/primitives/button.js.map +1 -0
- package/dist/ui/primitives/card.d.ts +9 -0
- package/dist/ui/primitives/card.d.ts.map +1 -0
- package/dist/ui/primitives/card.js +58 -0
- package/dist/ui/primitives/card.js.map +1 -0
- package/dist/ui/primitives/checkbox.d.ts +5 -0
- package/dist/ui/primitives/checkbox.d.ts.map +1 -0
- package/dist/ui/primitives/checkbox.js +45 -0
- package/dist/ui/primitives/checkbox.js.map +1 -0
- package/dist/ui/primitives/collapsible.d.ts +6 -0
- package/dist/ui/primitives/collapsible.d.ts.map +1 -0
- package/dist/ui/primitives/collapsible.js +44 -0
- package/dist/ui/primitives/collapsible.js.map +1 -0
- package/dist/ui/primitives/dialog.d.ts +22 -0
- package/dist/ui/primitives/dialog.d.ts.map +1 -0
- package/dist/ui/primitives/dialog.js +68 -0
- package/dist/ui/primitives/dialog.js.map +1 -0
- package/dist/ui/primitives/dropdown-menu.d.ts +28 -0
- package/dist/ui/primitives/dropdown-menu.d.ts.map +1 -0
- package/dist/ui/primitives/dropdown-menu.js +83 -0
- package/dist/ui/primitives/dropdown-menu.js.map +1 -0
- package/dist/ui/primitives/input.d.ts +4 -0
- package/dist/ui/primitives/input.d.ts.map +1 -0
- package/dist/ui/primitives/input.js +45 -0
- package/dist/ui/primitives/input.js.map +1 -0
- package/dist/ui/primitives/label.d.ts +6 -0
- package/dist/ui/primitives/label.d.ts.map +1 -0
- package/dist/ui/primitives/label.js +46 -0
- package/dist/ui/primitives/label.js.map +1 -0
- package/dist/ui/primitives/overflow-tabs.d.ts +18 -0
- package/dist/ui/primitives/overflow-tabs.d.ts.map +1 -0
- package/dist/ui/primitives/overflow-tabs.js +84 -0
- package/dist/ui/primitives/overflow-tabs.js.map +1 -0
- package/dist/ui/primitives/popover.d.ts +9 -0
- package/dist/ui/primitives/popover.d.ts.map +1 -0
- package/dist/ui/primitives/popover.js +48 -0
- package/dist/ui/primitives/popover.js.map +1 -0
- package/dist/ui/primitives/radio-group.d.ts +6 -0
- package/dist/ui/primitives/radio-group.d.ts.map +1 -0
- package/dist/ui/primitives/radio-group.js +52 -0
- package/dist/ui/primitives/radio-group.js.map +1 -0
- package/dist/ui/primitives/resizable.d.ts +12 -0
- package/dist/ui/primitives/resizable.d.ts.map +1 -0
- package/dist/ui/primitives/resizable.js +18 -0
- package/dist/ui/primitives/resizable.js.map +1 -0
- package/dist/ui/primitives/scroll-area.d.ts +6 -0
- package/dist/ui/primitives/scroll-area.d.ts.map +1 -0
- package/dist/ui/primitives/scroll-area.js +47 -0
- package/dist/ui/primitives/scroll-area.js.map +1 -0
- package/dist/ui/primitives/select.d.ts +14 -0
- package/dist/ui/primitives/select.d.ts.map +1 -0
- package/dist/ui/primitives/select.js +71 -0
- package/dist/ui/primitives/select.js.map +1 -0
- package/dist/ui/primitives/separator.d.ts +5 -0
- package/dist/ui/primitives/separator.d.ts.map +1 -0
- package/dist/ui/primitives/separator.js +44 -0
- package/dist/ui/primitives/separator.js.map +1 -0
- package/dist/ui/primitives/sheet.d.ts +26 -0
- package/dist/ui/primitives/sheet.d.ts.map +1 -0
- package/dist/ui/primitives/sheet.js +82 -0
- package/dist/ui/primitives/sheet.js.map +1 -0
- package/dist/ui/primitives/skeleton.d.ts +13 -0
- package/dist/ui/primitives/skeleton.d.ts.map +1 -0
- package/dist/ui/primitives/skeleton.js +29 -0
- package/dist/ui/primitives/skeleton.js.map +1 -0
- package/dist/ui/primitives/switch.d.ts +5 -0
- package/dist/ui/primitives/switch.d.ts.map +1 -0
- package/dist/ui/primitives/switch.js +44 -0
- package/dist/ui/primitives/switch.js.map +1 -0
- package/dist/ui/primitives/table.d.ts +11 -0
- package/dist/ui/primitives/table.d.ts.map +1 -0
- package/dist/ui/primitives/table.js +64 -0
- package/dist/ui/primitives/table.js.map +1 -0
- package/dist/ui/primitives/tabs.d.ts +8 -0
- package/dist/ui/primitives/tabs.d.ts.map +1 -0
- package/dist/ui/primitives/tabs.js +52 -0
- package/dist/ui/primitives/tabs.js.map +1 -0
- package/dist/ui/primitives/tag-multi-select.d.ts +19 -0
- package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -0
- package/dist/ui/primitives/tag-multi-select.js +92 -0
- package/dist/ui/primitives/tag-multi-select.js.map +1 -0
- package/dist/ui/primitives/textarea.d.ts +5 -0
- package/dist/ui/primitives/textarea.d.ts.map +1 -0
- package/dist/ui/primitives/textarea.js +45 -0
- package/dist/ui/primitives/textarea.js.map +1 -0
- package/dist/ui/primitives/tooltip.d.ts +8 -0
- package/dist/ui/primitives/tooltip.d.ts.map +1 -0
- package/dist/ui/primitives/tooltip.js +50 -0
- package/dist/ui/primitives/tooltip.js.map +1 -0
- package/package.json +24 -1
- package/src/index.ts +1 -0
- package/src/lib/biome-host/host-bridge.ts +10 -0
- package/src/lib/capabilities/capability-provider.tsx +95 -0
- package/src/lib/capabilities/index.ts +16 -0
- package/src/lib/capabilities/types.ts +69 -0
- package/src/lib/capabilities/use-capability.ts +72 -0
- package/src/ui/chrome/AsyncBoundary.tsx +66 -0
- package/src/ui/chrome/EmptyState.tsx +184 -0
- package/src/ui/chrome/ErrorCard.tsx +68 -0
- package/src/ui/chrome/LoadingState.tsx +61 -0
- package/src/ui/chrome/PageHeader.tsx +137 -0
- package/src/ui/chrome/StateCard.tsx +150 -0
- package/src/ui/cn.ts +32 -0
- package/src/ui/index.ts +53 -0
- package/src/ui/primitives/alert-dialog.tsx +104 -0
- package/src/ui/primitives/badge.tsx +32 -0
- package/src/ui/primitives/button.tsx +47 -0
- package/src/ui/primitives/card.tsx +43 -0
- package/src/ui/primitives/checkbox.tsx +26 -0
- package/src/ui/primitives/collapsible.tsx +9 -0
- package/src/ui/primitives/dialog.tsx +103 -0
- package/src/ui/primitives/dropdown-menu.tsx +179 -0
- package/src/ui/primitives/input.tsx +22 -0
- package/src/ui/primitives/label.tsx +17 -0
- package/src/ui/primitives/overflow-tabs.tsx +281 -0
- package/src/ui/primitives/popover.tsx +33 -0
- package/src/ui/primitives/radio-group.tsx +36 -0
- package/src/ui/primitives/resizable.tsx +67 -0
- package/src/ui/primitives/scroll-area.tsx +38 -0
- package/src/ui/primitives/select.tsx +143 -0
- package/src/ui/primitives/separator.tsx +20 -0
- package/src/ui/primitives/sheet.tsx +107 -0
- package/src/ui/primitives/skeleton.tsx +99 -0
- package/src/ui/primitives/switch.tsx +27 -0
- package/src/ui/primitives/table.tsx +72 -0
- package/src/ui/primitives/tabs.tsx +53 -0
- package/src/ui/primitives/tag-multi-select.tsx +241 -0
- package/src/ui/primitives/textarea.tsx +21 -0
- package/src/ui/primitives/tooltip.tsx +30 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
2
|
+
import { Check, ChevronRight, Circle } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from '../cn';
|
|
6
|
+
|
|
7
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
10
|
+
|
|
11
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
12
|
+
|
|
13
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
14
|
+
|
|
15
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
16
|
+
|
|
17
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
18
|
+
|
|
19
|
+
const DropdownMenuSubTrigger = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
22
|
+
inset?: boolean;
|
|
23
|
+
}
|
|
24
|
+
>(({ className, inset, children, ...props }, ref) => (
|
|
25
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
26
|
+
ref={ref}
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-body-1 outline-none data-[state=open]:bg-accent focus:bg-accent",
|
|
29
|
+
inset && "pl-8",
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
<ChevronRight className="ml-auto h-4 w-4" />
|
|
36
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
37
|
+
));
|
|
38
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
39
|
+
|
|
40
|
+
const DropdownMenuSubContent = React.forwardRef<
|
|
41
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
42
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
|
43
|
+
>(({ className, ...props }, ref) => (
|
|
44
|
+
<DropdownMenuPrimitive.SubContent
|
|
45
|
+
ref={ref}
|
|
46
|
+
className={cn(
|
|
47
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
));
|
|
53
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
54
|
+
|
|
55
|
+
const DropdownMenuContent = React.forwardRef<
|
|
56
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
57
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
|
58
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
59
|
+
<DropdownMenuPrimitive.Portal>
|
|
60
|
+
<DropdownMenuPrimitive.Content
|
|
61
|
+
ref={ref}
|
|
62
|
+
sideOffset={sideOffset}
|
|
63
|
+
className={cn(
|
|
64
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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",
|
|
65
|
+
className,
|
|
66
|
+
)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
</DropdownMenuPrimitive.Portal>
|
|
70
|
+
));
|
|
71
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
72
|
+
|
|
73
|
+
const DropdownMenuItem = React.forwardRef<
|
|
74
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
75
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
|
76
|
+
inset?: boolean;
|
|
77
|
+
}
|
|
78
|
+
>(({ className, inset, ...props }, ref) => (
|
|
79
|
+
<DropdownMenuPrimitive.Item
|
|
80
|
+
ref={ref}
|
|
81
|
+
className={cn(
|
|
82
|
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-body-1 outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
|
|
83
|
+
inset && "pl-8",
|
|
84
|
+
className,
|
|
85
|
+
)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
));
|
|
89
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
90
|
+
|
|
91
|
+
const DropdownMenuCheckboxItem = React.forwardRef<
|
|
92
|
+
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
93
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
|
94
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
95
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
96
|
+
ref={ref}
|
|
97
|
+
className={cn(
|
|
98
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-body-1 outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
|
|
99
|
+
className,
|
|
100
|
+
)}
|
|
101
|
+
checked={checked}
|
|
102
|
+
{...props}
|
|
103
|
+
>
|
|
104
|
+
<span className="absolute left-2 flex h-4 w-4 items-center justify-center">
|
|
105
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
106
|
+
<Check className="h-4 w-4" />
|
|
107
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
108
|
+
</span>
|
|
109
|
+
{children}
|
|
110
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
111
|
+
));
|
|
112
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
113
|
+
|
|
114
|
+
const DropdownMenuRadioItem = React.forwardRef<
|
|
115
|
+
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
116
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
|
117
|
+
>(({ className, children, ...props }, ref) => (
|
|
118
|
+
<DropdownMenuPrimitive.RadioItem
|
|
119
|
+
ref={ref}
|
|
120
|
+
className={cn(
|
|
121
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-body-1 outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground",
|
|
122
|
+
className,
|
|
123
|
+
)}
|
|
124
|
+
{...props}
|
|
125
|
+
>
|
|
126
|
+
<span className="absolute left-2 flex h-4 w-4 items-center justify-center">
|
|
127
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
128
|
+
<Circle className="h-2 w-2 fill-current" />
|
|
129
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
130
|
+
</span>
|
|
131
|
+
{children}
|
|
132
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
133
|
+
));
|
|
134
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
135
|
+
|
|
136
|
+
const DropdownMenuLabel = React.forwardRef<
|
|
137
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
138
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
139
|
+
inset?: boolean;
|
|
140
|
+
}
|
|
141
|
+
>(({ className, inset, ...props }, ref) => (
|
|
142
|
+
<DropdownMenuPrimitive.Label
|
|
143
|
+
ref={ref}
|
|
144
|
+
className={cn("px-2 py-1.5 text-body-1 font-semibold", inset && "pl-8", className)}
|
|
145
|
+
{...props}
|
|
146
|
+
/>
|
|
147
|
+
));
|
|
148
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
149
|
+
|
|
150
|
+
const DropdownMenuSeparator = React.forwardRef<
|
|
151
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
152
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
|
153
|
+
>(({ className, ...props }, ref) => (
|
|
154
|
+
<DropdownMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
|
155
|
+
));
|
|
156
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
157
|
+
|
|
158
|
+
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
159
|
+
return <span className={cn("ml-auto text-body-1 tracking-widest opacity-60", className)} {...props} />;
|
|
160
|
+
};
|
|
161
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
162
|
+
|
|
163
|
+
export {
|
|
164
|
+
DropdownMenu,
|
|
165
|
+
DropdownMenuTrigger,
|
|
166
|
+
DropdownMenuContent,
|
|
167
|
+
DropdownMenuItem,
|
|
168
|
+
DropdownMenuCheckboxItem,
|
|
169
|
+
DropdownMenuRadioItem,
|
|
170
|
+
DropdownMenuLabel,
|
|
171
|
+
DropdownMenuSeparator,
|
|
172
|
+
DropdownMenuShortcut,
|
|
173
|
+
DropdownMenuGroup,
|
|
174
|
+
DropdownMenuPortal,
|
|
175
|
+
DropdownMenuSub,
|
|
176
|
+
DropdownMenuSubContent,
|
|
177
|
+
DropdownMenuSubTrigger,
|
|
178
|
+
DropdownMenuRadioGroup,
|
|
179
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from '../cn';
|
|
4
|
+
|
|
5
|
+
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
6
|
+
({ className, type, ...props }, ref) => {
|
|
7
|
+
return (
|
|
8
|
+
<input
|
|
9
|
+
type={type}
|
|
10
|
+
className={cn(
|
|
11
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-body-1 file:font-medium file:text-foreground placeholder:text-ink-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-body-1",
|
|
12
|
+
className,
|
|
13
|
+
)}
|
|
14
|
+
ref={ref}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
Input.displayName = "Input";
|
|
21
|
+
|
|
22
|
+
export { Input };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from '../cn';
|
|
6
|
+
|
|
7
|
+
const labelVariants = cva("text-body-1 font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
|
|
8
|
+
|
|
9
|
+
const Label = React.forwardRef<
|
|
10
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
11
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
|
12
|
+
>(({ className, ...props }, ref) => (
|
|
13
|
+
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
|
14
|
+
));
|
|
15
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
16
|
+
|
|
17
|
+
export { Label };
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OverflowTabs — editorial tab strip that auto-collapses overflowing items
|
|
3
|
+
* into a "More…" dropdown instead of scrolling horizontally.
|
|
4
|
+
*
|
|
5
|
+
* Behavior:
|
|
6
|
+
* 1. All tabs render off-screen for measurement (visibility: hidden, but
|
|
7
|
+
* still laid out so widths are real).
|
|
8
|
+
* 2. A ResizeObserver tracks the container width; once known we walk the
|
|
9
|
+
* tab widths left-to-right and pick the cutoff where they still fit
|
|
10
|
+
* alongside a fixed "More…" trigger.
|
|
11
|
+
* 3. Visible tabs render inline; overflow tabs go into a dropdown.
|
|
12
|
+
*
|
|
13
|
+
* The active tab is always pulled into the visible set, even if its index
|
|
14
|
+
* would be in the overflow bucket — users should never lose sight of "where
|
|
15
|
+
* am I." Same for tabs marked `pinned: true` (indicator dot is sticky).
|
|
16
|
+
*/
|
|
17
|
+
import { ChevronDown, X } from 'lucide-react';
|
|
18
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
DropdownMenu,
|
|
22
|
+
DropdownMenuContent,
|
|
23
|
+
DropdownMenuItem,
|
|
24
|
+
DropdownMenuTrigger,
|
|
25
|
+
} from './dropdown-menu';
|
|
26
|
+
import { cn } from '../cn';
|
|
27
|
+
|
|
28
|
+
export interface OverflowTab {
|
|
29
|
+
readonly value: string;
|
|
30
|
+
readonly label: string;
|
|
31
|
+
/** Optional warning/indicator dot rendered next to the label. */
|
|
32
|
+
readonly indicator?: boolean;
|
|
33
|
+
/** Optional numeric badge. */
|
|
34
|
+
readonly count?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface OverflowTabsProps {
|
|
38
|
+
readonly tabs: readonly OverflowTab[];
|
|
39
|
+
readonly value: string;
|
|
40
|
+
readonly onChange: (value: string) => void;
|
|
41
|
+
/** Reserved px for the "More…" trigger when measuring. Default 88. */
|
|
42
|
+
readonly moreTriggerWidth?: number;
|
|
43
|
+
readonly className?: string;
|
|
44
|
+
/** Each tab's px gap from the next. Matches the visual rendering. Default 4. */
|
|
45
|
+
readonly gap?: number;
|
|
46
|
+
/**
|
|
47
|
+
* When provided, every tab renders a hover `×` that calls this with the
|
|
48
|
+
* tab value. Closable tabs (multi-thread surfaces, etc.) opt in by
|
|
49
|
+
* passing it; surfaces that just navigate omit it.
|
|
50
|
+
*/
|
|
51
|
+
readonly onClose?: (value: string) => void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function OverflowTabs({
|
|
55
|
+
tabs,
|
|
56
|
+
value,
|
|
57
|
+
onChange,
|
|
58
|
+
moreTriggerWidth = 88,
|
|
59
|
+
className,
|
|
60
|
+
gap = 4,
|
|
61
|
+
onClose,
|
|
62
|
+
}: OverflowTabsProps) {
|
|
63
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
64
|
+
const measureRef = useRef<HTMLDivElement>(null);
|
|
65
|
+
const [containerWidth, setContainerWidth] = useState(0);
|
|
66
|
+
const [tabWidths, setTabWidths] = useState<readonly number[]>([]);
|
|
67
|
+
|
|
68
|
+
// Measure container width and react to resizes.
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const el = containerRef.current;
|
|
71
|
+
if (!el) return;
|
|
72
|
+
const ro = new ResizeObserver((entries) => {
|
|
73
|
+
const w = entries[0]?.contentRect.width ?? el.clientWidth;
|
|
74
|
+
setContainerWidth(w);
|
|
75
|
+
});
|
|
76
|
+
ro.observe(el);
|
|
77
|
+
setContainerWidth(el.clientWidth);
|
|
78
|
+
return () => ro.disconnect();
|
|
79
|
+
}, []);
|
|
80
|
+
|
|
81
|
+
// Measure each tab's natural width once after first render. We re-measure
|
|
82
|
+
// when the tab list itself changes (new tabs, label edits).
|
|
83
|
+
useLayoutEffect(() => {
|
|
84
|
+
const el = measureRef.current;
|
|
85
|
+
if (!el) return;
|
|
86
|
+
const children = Array.from(el.children) as HTMLElement[];
|
|
87
|
+
setTabWidths(children.map((c) => c.getBoundingClientRect().width));
|
|
88
|
+
}, [tabs]);
|
|
89
|
+
|
|
90
|
+
// Decide split point: walk left-to-right, summing widths + gaps, stopping
|
|
91
|
+
// when the next tab would exceed (container - moreTrigger). The active
|
|
92
|
+
// tab and any pinned tabs are always pulled into the visible set.
|
|
93
|
+
const { visible, overflow } = useMemo(() => {
|
|
94
|
+
if (tabWidths.length !== tabs.length || containerWidth === 0) {
|
|
95
|
+
return { visible: tabs, overflow: [] as readonly OverflowTab[] };
|
|
96
|
+
}
|
|
97
|
+
const totalNatural =
|
|
98
|
+
tabWidths.reduce((s, w) => s + w, 0) + gap * Math.max(0, tabs.length - 1);
|
|
99
|
+
if (totalNatural <= containerWidth) {
|
|
100
|
+
return { visible: tabs, overflow: [] as readonly OverflowTab[] };
|
|
101
|
+
}
|
|
102
|
+
const budget = containerWidth - moreTriggerWidth;
|
|
103
|
+
const vis: OverflowTab[] = [];
|
|
104
|
+
const over: OverflowTab[] = [];
|
|
105
|
+
let used = 0;
|
|
106
|
+
for (let i = 0; i < tabs.length; i++) {
|
|
107
|
+
const w = tabWidths[i] + (vis.length > 0 ? gap : 0);
|
|
108
|
+
if (used + w <= budget) {
|
|
109
|
+
vis.push(tabs[i]);
|
|
110
|
+
used += w;
|
|
111
|
+
} else {
|
|
112
|
+
over.push(tabs[i]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Force the active tab into the visible set if it ended up in overflow.
|
|
116
|
+
const activeIdx = tabs.findIndex((t) => t.value === value);
|
|
117
|
+
if (activeIdx !== -1) {
|
|
118
|
+
const activeTab = tabs[activeIdx];
|
|
119
|
+
const inVisible = vis.some((t) => t.value === activeTab.value);
|
|
120
|
+
if (!inVisible) {
|
|
121
|
+
// Swap the last visible for the active one to keep the budget OK.
|
|
122
|
+
const dropped = vis.pop();
|
|
123
|
+
if (dropped) over.unshift(dropped);
|
|
124
|
+
vis.push(activeTab);
|
|
125
|
+
const removeIdx = over.findIndex((t) => t.value === activeTab.value);
|
|
126
|
+
if (removeIdx !== -1) over.splice(removeIdx, 1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { visible: vis, overflow: over };
|
|
130
|
+
}, [tabs, tabWidths, containerWidth, gap, moreTriggerWidth, value]);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div className={cn('relative -mb-px flex items-end border-b border-rule', className)}>
|
|
134
|
+
{/* Off-screen measurement layer — renders ALL tabs so widths are real. */}
|
|
135
|
+
<div
|
|
136
|
+
ref={measureRef}
|
|
137
|
+
aria-hidden
|
|
138
|
+
className="pointer-events-none absolute left-0 top-0 flex items-center gap-1 opacity-0"
|
|
139
|
+
style={{ visibility: 'hidden' }}
|
|
140
|
+
>
|
|
141
|
+
{tabs.map((t) => (
|
|
142
|
+
<TabPill key={`measure-${t.value}`} tab={t} active={false} onClick={() => undefined} />
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
{/* Live visible row. */}
|
|
147
|
+
<div
|
|
148
|
+
ref={containerRef}
|
|
149
|
+
role="tablist"
|
|
150
|
+
className="flex flex-1 items-end gap-1 overflow-hidden"
|
|
151
|
+
>
|
|
152
|
+
{visible.map((t) => (
|
|
153
|
+
<TabPill
|
|
154
|
+
key={t.value}
|
|
155
|
+
tab={t}
|
|
156
|
+
active={t.value === value}
|
|
157
|
+
onClick={() => onChange(t.value)}
|
|
158
|
+
onClose={onClose ? () => onClose(t.value) : undefined}
|
|
159
|
+
/>
|
|
160
|
+
))}
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
{overflow.length > 0 && (
|
|
164
|
+
<DropdownMenu>
|
|
165
|
+
<DropdownMenuTrigger asChild>
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
className="ml-1 -mb-px inline-flex shrink-0 items-center gap-1 whitespace-nowrap px-2 py-2 text-body-1 text-ink-3 transition-colors hover:text-ink"
|
|
169
|
+
aria-label="More tabs"
|
|
170
|
+
>
|
|
171
|
+
More
|
|
172
|
+
<span className="rounded bg-paper-elev px-1 font-mono text-[10px] leading-4 text-ink-4">
|
|
173
|
+
{overflow.length}
|
|
174
|
+
</span>
|
|
175
|
+
<ChevronDown className="h-3 w-3" strokeWidth={1.6} />
|
|
176
|
+
</button>
|
|
177
|
+
</DropdownMenuTrigger>
|
|
178
|
+
<DropdownMenuContent align="end" className="min-w-[180px]">
|
|
179
|
+
{overflow.map((t) => (
|
|
180
|
+
<DropdownMenuItem
|
|
181
|
+
key={t.value}
|
|
182
|
+
onClick={() => onChange(t.value)}
|
|
183
|
+
className={cn(t.value === value && 'bg-accent/60 text-foreground')}
|
|
184
|
+
>
|
|
185
|
+
<span className="flex-1 truncate">{t.label}</span>
|
|
186
|
+
{t.count !== undefined && (
|
|
187
|
+
<span className="ml-2 text-caption tabular-nums text-ink-4">{t.count}</span>
|
|
188
|
+
)}
|
|
189
|
+
{t.indicator && (
|
|
190
|
+
<span
|
|
191
|
+
aria-hidden
|
|
192
|
+
className="ml-2 inline-block h-1.5 w-1.5 rounded-full bg-warning"
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
195
|
+
{onClose && (
|
|
196
|
+
<button
|
|
197
|
+
type="button"
|
|
198
|
+
aria-label={`Close ${t.label}`}
|
|
199
|
+
className="ml-2 rounded p-0.5 text-ink-4 hover:bg-paper-elev hover:text-ink"
|
|
200
|
+
onClick={(e) => {
|
|
201
|
+
e.stopPropagation();
|
|
202
|
+
onClose(t.value);
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
<X className="h-3 w-3" strokeWidth={1.8} />
|
|
206
|
+
</button>
|
|
207
|
+
)}
|
|
208
|
+
</DropdownMenuItem>
|
|
209
|
+
))}
|
|
210
|
+
</DropdownMenuContent>
|
|
211
|
+
</DropdownMenu>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function TabPill({
|
|
218
|
+
tab,
|
|
219
|
+
active,
|
|
220
|
+
onClick,
|
|
221
|
+
onClose,
|
|
222
|
+
}: Readonly<{
|
|
223
|
+
tab: OverflowTab;
|
|
224
|
+
active: boolean;
|
|
225
|
+
onClick: () => void;
|
|
226
|
+
onClose?: () => void;
|
|
227
|
+
}>) {
|
|
228
|
+
return (
|
|
229
|
+
<div
|
|
230
|
+
className={cn(
|
|
231
|
+
'group relative -mb-px inline-flex items-center gap-1.5 whitespace-nowrap rounded-t-md border px-3 py-2 text-body-1 transition-colors',
|
|
232
|
+
active
|
|
233
|
+
? 'border-rule border-b-transparent bg-paper-elev text-ink font-medium'
|
|
234
|
+
: 'border-transparent text-ink-3 hover:border-rule/40 hover:bg-paper-elev/60 hover:text-ink-2',
|
|
235
|
+
)}
|
|
236
|
+
>
|
|
237
|
+
<button
|
|
238
|
+
type="button"
|
|
239
|
+
role="tab"
|
|
240
|
+
aria-selected={active}
|
|
241
|
+
onClick={onClick}
|
|
242
|
+
className="inline-flex items-center gap-2 outline-none"
|
|
243
|
+
>
|
|
244
|
+
{tab.label}
|
|
245
|
+
{tab.count !== undefined && (
|
|
246
|
+
<span className="rounded bg-paper-elev px-1 font-mono text-[10px] tabular-nums leading-4 text-ink-4">
|
|
247
|
+
{tab.count}
|
|
248
|
+
</span>
|
|
249
|
+
)}
|
|
250
|
+
{tab.indicator && (
|
|
251
|
+
<span
|
|
252
|
+
aria-hidden
|
|
253
|
+
className="inline-block h-1.5 w-1.5 rounded-full bg-warning"
|
|
254
|
+
/>
|
|
255
|
+
)}
|
|
256
|
+
</button>
|
|
257
|
+
{onClose && (
|
|
258
|
+
<button
|
|
259
|
+
type="button"
|
|
260
|
+
aria-label={`Close ${tab.label}`}
|
|
261
|
+
onClick={(e) => {
|
|
262
|
+
e.stopPropagation();
|
|
263
|
+
onClose();
|
|
264
|
+
}}
|
|
265
|
+
className={cn(
|
|
266
|
+
'rounded p-0.5 text-ink-4 transition-opacity hover:bg-paper hover:text-ink',
|
|
267
|
+
active ? 'opacity-70' : 'opacity-0 group-hover:opacity-70',
|
|
268
|
+
)}
|
|
269
|
+
>
|
|
270
|
+
<X className="h-3 w-3" strokeWidth={1.8} />
|
|
271
|
+
</button>
|
|
272
|
+
)}
|
|
273
|
+
{active && (
|
|
274
|
+
<span
|
|
275
|
+
aria-hidden
|
|
276
|
+
className="absolute inset-x-2 bottom-[-1px] h-[2px] rounded-full bg-primary"
|
|
277
|
+
/>
|
|
278
|
+
)}
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from '../cn';
|
|
5
|
+
|
|
6
|
+
const Popover = PopoverPrimitive.Root;
|
|
7
|
+
|
|
8
|
+
const PopoverTrigger = PopoverPrimitive.Trigger;
|
|
9
|
+
|
|
10
|
+
type PopoverContentProps = React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {
|
|
11
|
+
container?: HTMLElement | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const PopoverContent = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
16
|
+
PopoverContentProps
|
|
17
|
+
>(({ className, align = "center", sideOffset = 4, container, ...props }, ref) => (
|
|
18
|
+
<PopoverPrimitive.Portal container={container ?? undefined}>
|
|
19
|
+
<PopoverPrimitive.Content
|
|
20
|
+
ref={ref}
|
|
21
|
+
align={align}
|
|
22
|
+
sideOffset={sideOffset}
|
|
23
|
+
className={cn(
|
|
24
|
+
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=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",
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
</PopoverPrimitive.Portal>
|
|
30
|
+
));
|
|
31
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
|
32
|
+
|
|
33
|
+
export { Popover, PopoverTrigger, PopoverContent };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
2
|
+
import { Circle } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from '../cn';
|
|
6
|
+
|
|
7
|
+
const RadioGroup = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => {
|
|
11
|
+
return <RadioGroupPrimitive.Root className={cn("grid gap-2", className)} {...props} ref={ref} />;
|
|
12
|
+
});
|
|
13
|
+
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
14
|
+
|
|
15
|
+
const RadioGroupItem = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
|
18
|
+
>(({ className, ...props }, ref) => {
|
|
19
|
+
return (
|
|
20
|
+
<RadioGroupPrimitive.Item
|
|
21
|
+
ref={ref}
|
|
22
|
+
className={cn(
|
|
23
|
+
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
|
29
|
+
<Circle className="h-4 w-4 fill-current text-current" />
|
|
30
|
+
</RadioGroupPrimitive.Indicator>
|
|
31
|
+
</RadioGroupPrimitive.Item>
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
35
|
+
|
|
36
|
+
export { RadioGroup, RadioGroupItem };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { GripVertical, GripHorizontal } from "lucide-react"
|
|
2
|
+
import { Group, Panel, Separator } from "react-resizable-panels"
|
|
3
|
+
|
|
4
|
+
import { cn } from '../cn'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wrapper around react-resizable-panels v4.
|
|
8
|
+
*
|
|
9
|
+
* v4 API changes from v2/v3:
|
|
10
|
+
* - PanelGroup → Group
|
|
11
|
+
* - Panel (unchanged)
|
|
12
|
+
* - PanelResizeHandle → Separator
|
|
13
|
+
* - `direction` prop → `orientation` prop
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
type Direction = "horizontal" | "vertical"
|
|
17
|
+
|
|
18
|
+
const ResizablePanelGroup = ({
|
|
19
|
+
className,
|
|
20
|
+
direction,
|
|
21
|
+
...props
|
|
22
|
+
}: Omit<React.ComponentProps<typeof Group>, "orientation"> & {
|
|
23
|
+
direction?: Direction
|
|
24
|
+
}) => (
|
|
25
|
+
<Group
|
|
26
|
+
orientation={direction}
|
|
27
|
+
className={cn("flex h-full w-full", className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const ResizablePanel = Panel
|
|
33
|
+
|
|
34
|
+
const ResizableHandle = ({
|
|
35
|
+
withHandle,
|
|
36
|
+
className,
|
|
37
|
+
direction,
|
|
38
|
+
...props
|
|
39
|
+
}: React.ComponentProps<typeof Separator> & {
|
|
40
|
+
withHandle?: boolean
|
|
41
|
+
direction?: Direction
|
|
42
|
+
}) => (
|
|
43
|
+
<Separator
|
|
44
|
+
className={cn(
|
|
45
|
+
"relative flex items-center justify-center bg-border/60 hover:bg-border focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 transition-colors",
|
|
46
|
+
direction === "vertical"
|
|
47
|
+
? "h-px w-full cursor-row-resize after:absolute after:left-0 after:h-2 after:w-full after:-translate-y-1/2"
|
|
48
|
+
: "w-[3px] cursor-col-resize after:absolute after:inset-y-0 after:left-1/2 after:w-2 after:-translate-x-1/2",
|
|
49
|
+
className
|
|
50
|
+
)}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{withHandle && (
|
|
54
|
+
<div className={cn(
|
|
55
|
+
"z-10 flex items-center justify-center rounded-sm border bg-border",
|
|
56
|
+
direction === "vertical" ? "w-4 h-3" : "h-4 w-3"
|
|
57
|
+
)}>
|
|
58
|
+
{direction === "vertical"
|
|
59
|
+
? <GripHorizontal className="h-4 w-4" />
|
|
60
|
+
: <GripVertical className="h-4 w-4" />
|
|
61
|
+
}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
</Separator>
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from '../cn';
|
|
5
|
+
|
|
6
|
+
const ScrollArea = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
|
9
|
+
>(({ className, children, ...props }, ref) => (
|
|
10
|
+
<ScrollAreaPrimitive.Root ref={ref} className={cn("relative overflow-hidden", className)} {...props}>
|
|
11
|
+
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">{children}</ScrollAreaPrimitive.Viewport>
|
|
12
|
+
<ScrollBar />
|
|
13
|
+
<ScrollAreaPrimitive.Corner />
|
|
14
|
+
</ScrollAreaPrimitive.Root>
|
|
15
|
+
));
|
|
16
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
17
|
+
|
|
18
|
+
const ScrollBar = React.forwardRef<
|
|
19
|
+
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
|
20
|
+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
21
|
+
>(({ className, orientation = "vertical", ...props }, ref) => (
|
|
22
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
23
|
+
ref={ref}
|
|
24
|
+
orientation={orientation}
|
|
25
|
+
className={cn(
|
|
26
|
+
"flex touch-none select-none transition-colors",
|
|
27
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
28
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
>
|
|
33
|
+
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
|
34
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
35
|
+
));
|
|
36
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
37
|
+
|
|
38
|
+
export { ScrollArea, ScrollBar };
|