canopy-ui 0.2.0 → 0.4.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.
@@ -0,0 +1,226 @@
1
+ import * as React from "react"
2
+ import { Menu } from "@base-ui/react/menu"
3
+ import { Check, ChevronRight, Circle } from "lucide-react"
4
+
5
+ import { cn } from "../lib/cn"
6
+
7
+ const DropdownMenu = Menu.Root
8
+
9
+ const DropdownMenuGroup = Menu.Group
10
+
11
+ function DropdownMenuTrigger({
12
+ className,
13
+ ...props
14
+ }: React.ComponentProps<typeof Menu.Trigger>) {
15
+ return (
16
+ <Menu.Trigger
17
+ data-slot="dropdown-menu-trigger"
18
+ className={cn("outline-none", className)}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ function DropdownMenuContent({
25
+ className,
26
+ sideOffset = 4,
27
+ ...props
28
+ }: React.ComponentProps<typeof Menu.Popup> & { sideOffset?: number; align?: string }) {
29
+ return (
30
+ <Menu.Portal>
31
+ <Menu.Positioner sideOffset={sideOffset}>
32
+ <Menu.Popup
33
+ data-slot="dropdown-menu-content"
34
+ className={cn(
35
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md transition-all data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
36
+ className
37
+ )}
38
+ {...props}
39
+ />
40
+ </Menu.Positioner>
41
+ </Menu.Portal>
42
+ )
43
+ }
44
+
45
+ function DropdownMenuItem({
46
+ className,
47
+ inset,
48
+ ...props
49
+ }: React.ComponentProps<typeof Menu.Item> & { inset?: boolean }) {
50
+ return (
51
+ <Menu.Item
52
+ data-slot="dropdown-menu-item"
53
+ className={cn(
54
+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
55
+ inset && "pl-8",
56
+ className
57
+ )}
58
+ {...props}
59
+ />
60
+ )
61
+ }
62
+
63
+ function DropdownMenuCheckboxItem({
64
+ className,
65
+ children,
66
+ checked,
67
+ ...props
68
+ }: React.ComponentProps<typeof Menu.CheckboxItem>) {
69
+ return (
70
+ <Menu.CheckboxItem
71
+ data-slot="dropdown-menu-checkbox-item"
72
+ className={cn(
73
+ "relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
74
+ className
75
+ )}
76
+ checked={checked}
77
+ {...props}
78
+ >
79
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
80
+ <Menu.CheckboxItemIndicator>
81
+ <Check className="h-4 w-4" />
82
+ </Menu.CheckboxItemIndicator>
83
+ </span>
84
+ {children}
85
+ </Menu.CheckboxItem>
86
+ )
87
+ }
88
+
89
+ function DropdownMenuRadioGroup(props: React.ComponentProps<typeof Menu.RadioGroup>) {
90
+ return <Menu.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
91
+ }
92
+
93
+ function DropdownMenuRadioItem({
94
+ className,
95
+ children,
96
+ ...props
97
+ }: React.ComponentProps<typeof Menu.RadioItem>) {
98
+ return (
99
+ <Menu.RadioItem
100
+ data-slot="dropdown-menu-radio-item"
101
+ className={cn(
102
+ "relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
103
+ className
104
+ )}
105
+ {...props}
106
+ >
107
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
108
+ <Menu.RadioItemIndicator>
109
+ <Circle className="h-2 w-2 fill-current" />
110
+ </Menu.RadioItemIndicator>
111
+ </span>
112
+ {children}
113
+ </Menu.RadioItem>
114
+ )
115
+ }
116
+
117
+ function DropdownMenuLabel({
118
+ className,
119
+ inset,
120
+ ...props
121
+ }: React.HTMLAttributes<HTMLDivElement> & { inset?: boolean }) {
122
+ return (
123
+ <div
124
+ data-slot="dropdown-menu-label"
125
+ className={cn(
126
+ "px-2 py-1.5 text-sm font-semibold",
127
+ inset && "pl-8",
128
+ className
129
+ )}
130
+ {...props}
131
+ />
132
+ )
133
+ }
134
+
135
+ function DropdownMenuSeparator({
136
+ className,
137
+ ...props
138
+ }: React.ComponentProps<typeof Menu.Separator>) {
139
+ return (
140
+ <Menu.Separator
141
+ data-slot="dropdown-menu-separator"
142
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
143
+ {...props}
144
+ />
145
+ )
146
+ }
147
+
148
+ function DropdownMenuShortcut({
149
+ className,
150
+ ...props
151
+ }: React.HTMLAttributes<HTMLSpanElement>) {
152
+ return (
153
+ <span
154
+ data-slot="dropdown-menu-shortcut"
155
+ className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
156
+ {...props}
157
+ />
158
+ )
159
+ }
160
+
161
+ // Sub-menu support
162
+ function DropdownMenuSub(props: React.ComponentProps<typeof Menu.Root>) {
163
+ return <Menu.Root {...props} />
164
+ }
165
+
166
+ function DropdownMenuSubTrigger({
167
+ className,
168
+ inset,
169
+ children,
170
+ ...props
171
+ }: React.ComponentProps<typeof Menu.SubmenuTrigger> & { inset?: boolean }) {
172
+ return (
173
+ <Menu.SubmenuTrigger
174
+ data-slot="dropdown-menu-sub-trigger"
175
+ className={cn(
176
+ "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[highlighted]:bg-accent data-[open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
177
+ inset && "pl-8",
178
+ className
179
+ )}
180
+ {...props}
181
+ >
182
+ {children}
183
+ <ChevronRight className="ml-auto" />
184
+ </Menu.SubmenuTrigger>
185
+ )
186
+ }
187
+
188
+ function DropdownMenuSubContent({
189
+ className,
190
+ ...props
191
+ }: React.ComponentProps<typeof Menu.Popup>) {
192
+ return (
193
+ <Menu.Portal>
194
+ <Menu.Positioner>
195
+ <Menu.Popup
196
+ data-slot="dropdown-menu-sub-content"
197
+ className={cn(
198
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg transition-all data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
199
+ className
200
+ )}
201
+ {...props}
202
+ />
203
+ </Menu.Positioner>
204
+ </Menu.Portal>
205
+ )
206
+ }
207
+
208
+ const DropdownMenuPortal = Menu.Portal
209
+
210
+ export {
211
+ DropdownMenu,
212
+ DropdownMenuTrigger,
213
+ DropdownMenuContent,
214
+ DropdownMenuItem,
215
+ DropdownMenuCheckboxItem,
216
+ DropdownMenuRadioItem,
217
+ DropdownMenuLabel,
218
+ DropdownMenuSeparator,
219
+ DropdownMenuShortcut,
220
+ DropdownMenuGroup,
221
+ DropdownMenuPortal,
222
+ DropdownMenuSub,
223
+ DropdownMenuSubContent,
224
+ DropdownMenuSubTrigger,
225
+ DropdownMenuRadioGroup,
226
+ }
package/src/ui/index.ts CHANGED
@@ -14,3 +14,35 @@ export {
14
14
  TableCaption,
15
15
  } from './table'
16
16
  export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } from './tabs'
17
+ export {
18
+ Dialog,
19
+ DialogPortal,
20
+ DialogOverlay,
21
+ DialogTrigger,
22
+ DialogClose,
23
+ DialogContent,
24
+ DialogHeader,
25
+ DialogFooter,
26
+ DialogTitle,
27
+ DialogDescription,
28
+ } from './dialog'
29
+ export {
30
+ DropdownMenu,
31
+ DropdownMenuTrigger,
32
+ DropdownMenuContent,
33
+ DropdownMenuItem,
34
+ DropdownMenuCheckboxItem,
35
+ DropdownMenuRadioItem,
36
+ DropdownMenuLabel,
37
+ DropdownMenuSeparator,
38
+ DropdownMenuShortcut,
39
+ DropdownMenuGroup,
40
+ DropdownMenuPortal,
41
+ DropdownMenuSub,
42
+ DropdownMenuSubContent,
43
+ DropdownMenuSubTrigger,
44
+ DropdownMenuRadioGroup,
45
+ } from './dropdown-menu'
46
+ export { Tooltip, TooltipProvider } from './tooltip'
47
+ export { Toaster } from './sonner'
48
+ export { AutoResizeTextarea } from './AutoResizeTextarea'
@@ -0,0 +1,23 @@
1
+ import { Toaster as SonnerToaster } from "sonner"
2
+
3
+ import { cn } from "../lib/cn"
4
+
5
+ type ToasterProps = React.ComponentProps<typeof SonnerToaster>
6
+
7
+ export function Toaster({ className, ...props }: ToasterProps) {
8
+ return (
9
+ <SonnerToaster
10
+ className={cn(className)}
11
+ toastOptions={{
12
+ classNames: {
13
+ toast:
14
+ "group toast bg-card text-card-foreground border-border shadow-lg",
15
+ description: "text-muted-foreground",
16
+ actionButton: "bg-primary text-primary-foreground",
17
+ cancelButton: "bg-muted text-muted-foreground",
18
+ },
19
+ }}
20
+ {...props}
21
+ />
22
+ )
23
+ }
@@ -0,0 +1,55 @@
1
+ import * as React from "react";
2
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
3
+
4
+ import { cn } from "../lib/cn";
5
+
6
+ /**
7
+ * Thin wrapper around @base-ui/react's Tooltip primitive that matches
8
+ * the rest of the shadcn-ish ui/ collection. Use <Tooltip> for any
9
+ * icon-only button so sighted users get hover feedback that mirrors
10
+ * the aria-label exposed to AT.
11
+ *
12
+ * Usage:
13
+ * <Tooltip label="Delete this opp">
14
+ * <button aria-label="Delete foo">...</button>
15
+ * </Tooltip>
16
+ *
17
+ * The child is rendered via base-ui's `render` prop so the existing
18
+ * button element (with its aria-label, classes, handlers) is preserved.
19
+ */
20
+ const TooltipProvider = TooltipPrimitive.Provider;
21
+
22
+ interface TooltipProps {
23
+ /** Tooltip text. Should mirror the trigger's aria-label. */
24
+ label: React.ReactNode;
25
+ /** The interactive element to attach the tooltip to. */
26
+ children: React.ReactElement;
27
+ /** Open delay in ms. */
28
+ delay?: number;
29
+ /** Side of the trigger to place the popup. */
30
+ side?: "top" | "right" | "bottom" | "left";
31
+ /** Offset (px) from the trigger. */
32
+ sideOffset?: number;
33
+ }
34
+
35
+ function Tooltip({ label, children, delay = 300, side = "top", sideOffset = 6 }: TooltipProps) {
36
+ return (
37
+ <TooltipPrimitive.Root>
38
+ <TooltipPrimitive.Trigger delay={delay} render={children} />
39
+ <TooltipPrimitive.Portal>
40
+ <TooltipPrimitive.Positioner side={side} sideOffset={sideOffset}>
41
+ <TooltipPrimitive.Popup
42
+ className={cn(
43
+ "z-50 max-w-xs rounded-md border border-border bg-popover px-2 py-1 text-xs text-popover-foreground shadow-md",
44
+ "data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 transition-opacity",
45
+ )}
46
+ >
47
+ {label}
48
+ </TooltipPrimitive.Popup>
49
+ </TooltipPrimitive.Positioner>
50
+ </TooltipPrimitive.Portal>
51
+ </TooltipPrimitive.Root>
52
+ );
53
+ }
54
+
55
+ export { Tooltip, TooltipProvider };