@dinachi/cli 0.4.0 → 0.5.1

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 (35) hide show
  1. package/README.md +27 -5
  2. package/dist/index.js +1037 -567
  3. package/package.json +6 -5
  4. package/templates/autocomplete/autocomplete.tsx +197 -0
  5. package/templates/autocomplete/index.ts +17 -0
  6. package/templates/combobox/combobox.tsx +202 -0
  7. package/templates/combobox/index.ts +17 -0
  8. package/templates/context-menu/context-menu.tsx +104 -39
  9. package/templates/drawer/drawer.tsx +109 -0
  10. package/templates/drawer/index.ts +12 -0
  11. package/templates/fieldset/fieldset.tsx +32 -0
  12. package/templates/fieldset/index.ts +1 -0
  13. package/templates/menu/index.ts +17 -0
  14. package/templates/menu/menu.tsx +282 -0
  15. package/templates/menubar/menubar.tsx +7 -7
  16. package/templates/meter/index.ts +1 -0
  17. package/templates/meter/meter.tsx +64 -0
  18. package/templates/number-field/index.ts +9 -0
  19. package/templates/number-field/number-field.tsx +114 -0
  20. package/templates/popover/index.ts +12 -0
  21. package/templates/popover/popover.tsx +137 -0
  22. package/templates/preview-card/preview-card.tsx +4 -5
  23. package/templates/progress/index.ts +7 -0
  24. package/templates/progress/progress.tsx +64 -0
  25. package/templates/radio/index.ts +1 -0
  26. package/templates/radio/radio.tsx +39 -0
  27. package/templates/scroll-area/index.ts +8 -0
  28. package/templates/scroll-area/scroll-area.tsx +94 -0
  29. package/templates/separator/index.ts +1 -0
  30. package/templates/separator/separator.tsx +25 -0
  31. package/templates/switch/index.ts +1 -0
  32. package/templates/switch/switch.tsx +42 -0
  33. package/templates/toggle-group/index.ts +1 -0
  34. package/templates/toggle-group/toggle-group.tsx +67 -0
  35. package/templates/tooltip/tooltip.tsx +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dinachi/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI for adding Dinachi UI components to your project",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -15,12 +15,12 @@
15
15
  "license": "MIT",
16
16
  "repository": {
17
17
  "type": "git",
18
- "url": "https://github.com/dinachi/ui"
18
+ "url": "git+https://github.com/dinachi/ui.git"
19
19
  },
20
20
  "homepage": "https://dinachi.dev",
21
21
  "bin": {
22
- "dinachi": "./dist/index.js",
23
- "dinachi-ui": "./dist/index.js"
22
+ "dinachi": "dist/index.js",
23
+ "dinachi-ui": "dist/index.js"
24
24
  },
25
25
  "files": [
26
26
  "dist",
@@ -51,6 +51,7 @@
51
51
  "scripts": {
52
52
  "build": "tsup src/index.ts --format esm --dts --clean",
53
53
  "dev": "tsup src/index.ts --format esm --dts --watch",
54
- "type-check": "tsc --noEmit"
54
+ "type-check": "tsc --noEmit",
55
+ "test": "pnpm run build && node --test test/**/*.test.mjs"
55
56
  }
56
57
  }
@@ -0,0 +1,197 @@
1
+ // @ts-nocheck
2
+ "use client"
3
+
4
+ import * as React from "react"
5
+ import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete"
6
+ import { ChevronDown, X } from "lucide-react"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const Autocomplete = AutocompletePrimitive.Root
10
+ const AutocompleteValue = AutocompletePrimitive.Value
11
+ const AutocompleteCollection = AutocompletePrimitive.Collection
12
+ const AutocompletePortal = AutocompletePrimitive.Portal
13
+
14
+ const AutocompleteInput = React.forwardRef<
15
+ React.ComponentRef<typeof AutocompletePrimitive.Input>,
16
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Input>
17
+ >(({ className, ...props }, ref) => (
18
+ <AutocompletePrimitive.Input
19
+ ref={ref}
20
+ className={cn(
21
+ "flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm",
22
+ "placeholder:text-muted-foreground",
23
+ "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
24
+ "disabled:cursor-not-allowed disabled:opacity-50",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ ))
30
+ AutocompleteInput.displayName = "AutocompleteInput"
31
+
32
+ const AutocompleteTrigger = React.forwardRef<
33
+ React.ComponentRef<typeof AutocompletePrimitive.Trigger>,
34
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Trigger>
35
+ >(({ className, children, ...props }, ref) => (
36
+ <AutocompletePrimitive.Trigger
37
+ ref={ref}
38
+ className={cn(
39
+ "inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-3",
40
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
41
+ "disabled:pointer-events-none disabled:opacity-50",
42
+ className
43
+ )}
44
+ {...props}
45
+ >
46
+ {children ?? (
47
+ <AutocompletePrimitive.Icon>
48
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
49
+ </AutocompletePrimitive.Icon>
50
+ )}
51
+ </AutocompletePrimitive.Trigger>
52
+ ))
53
+ AutocompleteTrigger.displayName = "AutocompleteTrigger"
54
+
55
+ const AutocompleteClear = React.forwardRef<
56
+ React.ComponentRef<typeof AutocompletePrimitive.Clear>,
57
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Clear>
58
+ >(({ className, children, ...props }, ref) => (
59
+ <AutocompletePrimitive.Clear
60
+ ref={ref}
61
+ className={cn(
62
+ "inline-flex h-8 w-8 items-center justify-center rounded-sm text-muted-foreground",
63
+ "hover:bg-accent hover:text-accent-foreground",
64
+ className
65
+ )}
66
+ {...props}
67
+ >
68
+ {children ?? <X className="h-4 w-4" />}
69
+ </AutocompletePrimitive.Clear>
70
+ ))
71
+ AutocompleteClear.displayName = "AutocompleteClear"
72
+
73
+ const AutocompleteContent = React.forwardRef<
74
+ React.ComponentRef<typeof AutocompletePrimitive.Popup>,
75
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Popup>
76
+ >(({ className, ...props }, ref) => (
77
+ <AutocompletePortal>
78
+ <AutocompletePrimitive.Positioner sideOffset={4}>
79
+ <AutocompletePrimitive.Popup
80
+ ref={ref}
81
+ className={cn(
82
+ "relative z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
83
+ "origin-[var(--transform-origin)] outline-none",
84
+ "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
85
+ "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
86
+ className
87
+ )}
88
+ {...props}
89
+ />
90
+ </AutocompletePrimitive.Positioner>
91
+ </AutocompletePortal>
92
+ ))
93
+ AutocompleteContent.displayName = "AutocompleteContent"
94
+
95
+ const AutocompleteList = React.forwardRef<
96
+ React.ComponentRef<typeof AutocompletePrimitive.List>,
97
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.List>
98
+ >(({ className, ...props }, ref) => (
99
+ <AutocompletePrimitive.List
100
+ ref={ref}
101
+ className={cn("max-h-80 overflow-y-auto p-1", className)}
102
+ {...props}
103
+ />
104
+ ))
105
+ AutocompleteList.displayName = "AutocompleteList"
106
+
107
+ const AutocompleteItem = React.forwardRef<
108
+ React.ComponentRef<typeof AutocompletePrimitive.Item>,
109
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Item> & {
110
+ inset?: boolean
111
+ }
112
+ >(({ className, children, inset, ...props }, ref) => (
113
+ <AutocompletePrimitive.Item
114
+ ref={ref}
115
+ className={cn(
116
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
117
+ "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
118
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
119
+ inset && "pl-8",
120
+ className
121
+ )}
122
+ {...props}
123
+ >
124
+ {children}
125
+ </AutocompletePrimitive.Item>
126
+ ))
127
+ AutocompleteItem.displayName = "AutocompleteItem"
128
+
129
+ const AutocompleteGroup = React.forwardRef<
130
+ React.ComponentRef<typeof AutocompletePrimitive.Group>,
131
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Group>
132
+ >(({ className, ...props }, ref) => (
133
+ <AutocompletePrimitive.Group ref={ref} className={cn(className)} {...props} />
134
+ ))
135
+ AutocompleteGroup.displayName = "AutocompleteGroup"
136
+
137
+ const AutocompleteGroupLabel = React.forwardRef<
138
+ React.ComponentRef<typeof AutocompletePrimitive.GroupLabel>,
139
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.GroupLabel>
140
+ >(({ className, ...props }, ref) => (
141
+ <AutocompletePrimitive.GroupLabel
142
+ ref={ref}
143
+ className={cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", className)}
144
+ {...props}
145
+ />
146
+ ))
147
+ AutocompleteGroupLabel.displayName = "AutocompleteGroupLabel"
148
+
149
+ const AutocompleteEmpty = React.forwardRef<
150
+ React.ComponentRef<typeof AutocompletePrimitive.Empty>,
151
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Empty>
152
+ >(({ className, ...props }, ref) => (
153
+ <AutocompletePrimitive.Empty
154
+ ref={ref}
155
+ className={cn("px-2 py-6 text-center text-sm text-muted-foreground", className)}
156
+ {...props}
157
+ />
158
+ ))
159
+ AutocompleteEmpty.displayName = "AutocompleteEmpty"
160
+
161
+ const AutocompleteStatus = React.forwardRef<
162
+ React.ComponentRef<typeof AutocompletePrimitive.Status>,
163
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Status>
164
+ >(({ className, ...props }, ref) => (
165
+ <AutocompletePrimitive.Status ref={ref} className={cn("sr-only", className)} {...props} />
166
+ ))
167
+ AutocompleteStatus.displayName = "AutocompleteStatus"
168
+
169
+ const AutocompleteSeparator = React.forwardRef<
170
+ React.ComponentRef<typeof AutocompletePrimitive.Separator>,
171
+ React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Separator>
172
+ >(({ className, ...props }, ref) => (
173
+ <AutocompletePrimitive.Separator
174
+ ref={ref}
175
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
176
+ {...props}
177
+ />
178
+ ))
179
+ AutocompleteSeparator.displayName = "AutocompleteSeparator"
180
+
181
+ export {
182
+ Autocomplete,
183
+ AutocompleteValue,
184
+ AutocompleteCollection,
185
+ AutocompletePortal,
186
+ AutocompleteInput,
187
+ AutocompleteTrigger,
188
+ AutocompleteClear,
189
+ AutocompleteContent,
190
+ AutocompleteList,
191
+ AutocompleteItem,
192
+ AutocompleteGroup,
193
+ AutocompleteGroupLabel,
194
+ AutocompleteEmpty,
195
+ AutocompleteStatus,
196
+ AutocompleteSeparator,
197
+ }
@@ -0,0 +1,17 @@
1
+ export {
2
+ Autocomplete,
3
+ AutocompleteValue,
4
+ AutocompleteCollection,
5
+ AutocompletePortal,
6
+ AutocompleteInput,
7
+ AutocompleteTrigger,
8
+ AutocompleteClear,
9
+ AutocompleteContent,
10
+ AutocompleteList,
11
+ AutocompleteItem,
12
+ AutocompleteGroup,
13
+ AutocompleteGroupLabel,
14
+ AutocompleteEmpty,
15
+ AutocompleteStatus,
16
+ AutocompleteSeparator,
17
+ } from "./autocomplete"
@@ -0,0 +1,202 @@
1
+ // @ts-nocheck
2
+ "use client"
3
+
4
+ import * as React from "react"
5
+ import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox"
6
+ import { Check, ChevronDown, X } from "lucide-react"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const Combobox = ComboboxPrimitive.Root
10
+ const ComboboxValue = ComboboxPrimitive.Value
11
+ const ComboboxCollection = ComboboxPrimitive.Collection
12
+ const ComboboxPortal = ComboboxPrimitive.Portal
13
+
14
+ const ComboboxInput = React.forwardRef<
15
+ React.ComponentRef<typeof ComboboxPrimitive.Input>,
16
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Input>
17
+ >(({ className, ...props }, ref) => (
18
+ <ComboboxPrimitive.Input
19
+ ref={ref}
20
+ className={cn(
21
+ "flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm",
22
+ "placeholder:text-muted-foreground",
23
+ "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
24
+ "disabled:cursor-not-allowed disabled:opacity-50",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ ))
30
+ ComboboxInput.displayName = "ComboboxInput"
31
+
32
+ const ComboboxTrigger = React.forwardRef<
33
+ React.ComponentRef<typeof ComboboxPrimitive.Trigger>,
34
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Trigger>
35
+ >(({ className, children, ...props }, ref) => (
36
+ <ComboboxPrimitive.Trigger
37
+ ref={ref}
38
+ className={cn(
39
+ "inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-3",
40
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
41
+ "disabled:pointer-events-none disabled:opacity-50",
42
+ className
43
+ )}
44
+ {...props}
45
+ >
46
+ {children ?? (
47
+ <ComboboxPrimitive.Icon>
48
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
49
+ </ComboboxPrimitive.Icon>
50
+ )}
51
+ </ComboboxPrimitive.Trigger>
52
+ ))
53
+ ComboboxTrigger.displayName = "ComboboxTrigger"
54
+
55
+ const ComboboxClear = React.forwardRef<
56
+ React.ComponentRef<typeof ComboboxPrimitive.Clear>,
57
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Clear>
58
+ >(({ className, children, ...props }, ref) => (
59
+ <ComboboxPrimitive.Clear
60
+ ref={ref}
61
+ className={cn(
62
+ "inline-flex h-8 w-8 items-center justify-center rounded-sm text-muted-foreground",
63
+ "hover:bg-accent hover:text-accent-foreground",
64
+ className
65
+ )}
66
+ {...props}
67
+ >
68
+ {children ?? <X className="h-4 w-4" />}
69
+ </ComboboxPrimitive.Clear>
70
+ ))
71
+ ComboboxClear.displayName = "ComboboxClear"
72
+
73
+ const ComboboxContent = React.forwardRef<
74
+ React.ComponentRef<typeof ComboboxPrimitive.Popup>,
75
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Popup>
76
+ >(({ className, ...props }, ref) => (
77
+ <ComboboxPortal>
78
+ <ComboboxPrimitive.Positioner sideOffset={4}>
79
+ <ComboboxPrimitive.Popup
80
+ ref={ref}
81
+ className={cn(
82
+ "relative z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
83
+ "origin-[var(--transform-origin)] outline-none",
84
+ "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
85
+ "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
86
+ className
87
+ )}
88
+ {...props}
89
+ />
90
+ </ComboboxPrimitive.Positioner>
91
+ </ComboboxPortal>
92
+ ))
93
+ ComboboxContent.displayName = "ComboboxContent"
94
+
95
+ const ComboboxList = React.forwardRef<
96
+ React.ComponentRef<typeof ComboboxPrimitive.List>,
97
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.List>
98
+ >(({ className, ...props }, ref) => (
99
+ <ComboboxPrimitive.List
100
+ ref={ref}
101
+ className={cn("max-h-80 overflow-y-auto p-1", className)}
102
+ {...props}
103
+ />
104
+ ))
105
+ ComboboxList.displayName = "ComboboxList"
106
+
107
+ const ComboboxItem = React.forwardRef<
108
+ React.ComponentRef<typeof ComboboxPrimitive.Item>,
109
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Item> & {
110
+ inset?: boolean
111
+ }
112
+ >(({ className, children, inset, ...props }, ref) => (
113
+ <ComboboxPrimitive.Item
114
+ ref={ref}
115
+ className={cn(
116
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
117
+ "data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
118
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
119
+ inset && "pl-10",
120
+ className
121
+ )}
122
+ {...props}
123
+ >
124
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
125
+ <ComboboxPrimitive.ItemIndicator>
126
+ <Check className="h-4 w-4" />
127
+ </ComboboxPrimitive.ItemIndicator>
128
+ </span>
129
+ {children}
130
+ </ComboboxPrimitive.Item>
131
+ ))
132
+ ComboboxItem.displayName = "ComboboxItem"
133
+
134
+ const ComboboxGroup = React.forwardRef<
135
+ React.ComponentRef<typeof ComboboxPrimitive.Group>,
136
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Group>
137
+ >(({ className, ...props }, ref) => (
138
+ <ComboboxPrimitive.Group ref={ref} className={cn(className)} {...props} />
139
+ ))
140
+ ComboboxGroup.displayName = "ComboboxGroup"
141
+
142
+ const ComboboxGroupLabel = React.forwardRef<
143
+ React.ComponentRef<typeof ComboboxPrimitive.GroupLabel>,
144
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.GroupLabel>
145
+ >(({ className, ...props }, ref) => (
146
+ <ComboboxPrimitive.GroupLabel
147
+ ref={ref}
148
+ className={cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", className)}
149
+ {...props}
150
+ />
151
+ ))
152
+ ComboboxGroupLabel.displayName = "ComboboxGroupLabel"
153
+
154
+ const ComboboxEmpty = React.forwardRef<
155
+ React.ComponentRef<typeof ComboboxPrimitive.Empty>,
156
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Empty>
157
+ >(({ className, ...props }, ref) => (
158
+ <ComboboxPrimitive.Empty
159
+ ref={ref}
160
+ className={cn("px-2 py-6 text-center text-sm text-muted-foreground", className)}
161
+ {...props}
162
+ />
163
+ ))
164
+ ComboboxEmpty.displayName = "ComboboxEmpty"
165
+
166
+ const ComboboxStatus = React.forwardRef<
167
+ React.ComponentRef<typeof ComboboxPrimitive.Status>,
168
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Status>
169
+ >(({ className, ...props }, ref) => (
170
+ <ComboboxPrimitive.Status ref={ref} className={cn("sr-only", className)} {...props} />
171
+ ))
172
+ ComboboxStatus.displayName = "ComboboxStatus"
173
+
174
+ const ComboboxSeparator = React.forwardRef<
175
+ React.ComponentRef<typeof ComboboxPrimitive.Separator>,
176
+ React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Separator>
177
+ >(({ className, ...props }, ref) => (
178
+ <ComboboxPrimitive.Separator
179
+ ref={ref}
180
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
181
+ {...props}
182
+ />
183
+ ))
184
+ ComboboxSeparator.displayName = "ComboboxSeparator"
185
+
186
+ export {
187
+ Combobox,
188
+ ComboboxValue,
189
+ ComboboxCollection,
190
+ ComboboxPortal,
191
+ ComboboxInput,
192
+ ComboboxTrigger,
193
+ ComboboxClear,
194
+ ComboboxContent,
195
+ ComboboxList,
196
+ ComboboxItem,
197
+ ComboboxGroup,
198
+ ComboboxGroupLabel,
199
+ ComboboxEmpty,
200
+ ComboboxStatus,
201
+ ComboboxSeparator,
202
+ }
@@ -0,0 +1,17 @@
1
+ export {
2
+ Combobox,
3
+ ComboboxValue,
4
+ ComboboxCollection,
5
+ ComboboxPortal,
6
+ ComboboxInput,
7
+ ComboboxTrigger,
8
+ ComboboxClear,
9
+ ComboboxContent,
10
+ ComboboxList,
11
+ ComboboxItem,
12
+ ComboboxGroup,
13
+ ComboboxGroupLabel,
14
+ ComboboxEmpty,
15
+ ComboboxStatus,
16
+ ComboboxSeparator,
17
+ } from "./combobox"
@@ -2,9 +2,9 @@
2
2
  import * as React from "react";
3
3
  import { ContextMenu as BaseContextMenu } from "@base-ui/react/context-menu";
4
4
  import { Menu } from "@base-ui/react/menu";
5
- import { cn } from "@/lib/utils"
6
- import { Check, ChevronRight, Circle } from "lucide-react";
7
5
  import { useRender } from "@base-ui/react/use-render";
6
+ import { cn } from "@/lib/utils";
7
+ import { Check, ChevronRight, Circle } from "lucide-react";
8
8
 
9
9
  const ContextMenu = React.forwardRef<
10
10
  React.ComponentRef<typeof BaseContextMenu.Root>,
@@ -58,21 +58,57 @@ ContextMenuPositioner.displayName = "ContextMenuPositioner";
58
58
 
59
59
  const ContextMenuContent = React.forwardRef<
60
60
  React.ComponentRef<typeof BaseContextMenu.Popup>,
61
- React.ComponentProps<typeof BaseContextMenu.Popup>
62
- >(({ className, ...props }, ref) => (
63
- <BaseContextMenu.Popup
64
- ref={ref}
65
- className={cn(
66
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
67
- "origin-[var(--transform-origin)]",
68
- "outline-none focus:outline-none focus-visible:outline-none",
69
- "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
70
- "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
71
- className
72
- )}
73
- {...props}
74
- />
75
- ));
61
+ React.ComponentProps<typeof BaseContextMenu.Popup> & {
62
+ container?: HTMLElement | React.RefObject<HTMLElement | null> | null;
63
+ }
64
+ >(({ className, container, ...props }, ref) => {
65
+ const [portalContainer, setPortalContainer] =
66
+ React.useState<HTMLElement | null>(null);
67
+
68
+ React.useEffect(() => {
69
+ // If container is provided, use it
70
+ if (container) {
71
+ if (container instanceof HTMLElement) {
72
+ setPortalContainer(container);
73
+ } else if (container.current) {
74
+ setPortalContainer(container.current);
75
+ }
76
+ return;
77
+ }
78
+
79
+ // Otherwise create/use the high-level portal root
80
+ let portalRoot = document.getElementById(
81
+ "context-menu-portal-root"
82
+ ) as HTMLElement;
83
+ if (!portalRoot) {
84
+ portalRoot = document.createElement("div");
85
+ portalRoot.id = "context-menu-portal-root";
86
+ portalRoot.style.cssText =
87
+ "position: fixed; top: 0; left: 0; z-index: 9999; pointer-events: none; isolation: isolate;";
88
+ document.body.appendChild(portalRoot);
89
+ }
90
+ setPortalContainer(portalRoot);
91
+ }, [container]);
92
+
93
+ return (
94
+ <ContextMenuPortal container={portalContainer}>
95
+ <ContextMenuPositioner>
96
+ <BaseContextMenu.Popup
97
+ ref={ref}
98
+ className={cn(
99
+ "z-[9999] min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg pointer-events-auto",
100
+ "origin-[var(--transform-origin)]",
101
+ "outline-none focus:outline-none focus-visible:outline-none",
102
+ "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
103
+ "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
104
+ className
105
+ )}
106
+ {...props}
107
+ />
108
+ </ContextMenuPositioner>
109
+ </ContextMenuPortal>
110
+ );
111
+ });
76
112
  ContextMenuContent.displayName = "ContextMenuContent";
77
113
 
78
114
  const ContextMenuItem = React.forwardRef<
@@ -151,6 +187,14 @@ const ContextMenuRadioItem = React.forwardRef<
151
187
  ));
152
188
  ContextMenuRadioItem.displayName = "ContextMenuRadioItem";
153
189
 
190
+ const ContextMenuGroup = React.forwardRef<
191
+ React.ComponentRef<typeof BaseContextMenu.Group>,
192
+ React.ComponentProps<typeof BaseContextMenu.Group>
193
+ >(({ className, ...props }, ref) => (
194
+ <BaseContextMenu.Group ref={ref} className={cn(className)} {...props} />
195
+ ));
196
+ ContextMenuGroup.displayName = "ContextMenuGroup";
197
+
154
198
  const ContextMenuLabel = React.forwardRef<
155
199
  React.ComponentRef<typeof BaseContextMenu.GroupLabel>,
156
200
  React.ComponentProps<typeof BaseContextMenu.GroupLabel> & {
@@ -199,11 +243,11 @@ ContextMenuShortcut.displayName = "ContextMenuShortcut";
199
243
 
200
244
  // Submenu components using Menu from @base-ui-components
201
245
  const ContextMenuSub = React.forwardRef<
202
- React.ComponentRef<typeof Menu.Root>,
203
- React.ComponentProps<typeof Menu.Root>
246
+ HTMLDivElement,
247
+ React.ComponentProps<typeof Menu.SubmenuRoot>
204
248
  >(({ children, ...props }, ref) => {
205
249
  const element = useRender({
206
- render: <Menu.Root>{children}</Menu.Root>,
250
+ render: <Menu.SubmenuRoot>{children}</Menu.SubmenuRoot>,
207
251
  props,
208
252
  ref,
209
253
  });
@@ -239,24 +283,44 @@ ContextMenuSubTrigger.displayName = "ContextMenuSubTrigger";
239
283
  const ContextMenuSubContent = React.forwardRef<
240
284
  React.ComponentRef<typeof Menu.Popup>,
241
285
  React.ComponentProps<typeof Menu.Popup>
242
- >(({ className, ...props }, ref) => (
243
- <Menu.Portal>
244
- <Menu.Positioner className="outline-none" alignOffset={-4} sideOffset={8}>
245
- <Menu.Popup
246
- ref={ref}
247
- className={cn(
248
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
249
- "origin-[var(--transform-origin)]",
250
- "outline-none focus:outline-none focus-visible:outline-none",
251
- "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
252
- "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
253
- className
254
- )}
255
- {...props}
256
- />
257
- </Menu.Positioner>
258
- </Menu.Portal>
259
- ));
286
+ >(({ className, ...props }, ref) => {
287
+ const [portalContainer, setPortalContainer] =
288
+ React.useState<HTMLElement | null>(null);
289
+
290
+ React.useEffect(() => {
291
+ // Use the same high-level portal root for submenus
292
+ let portalRoot = document.getElementById(
293
+ "context-menu-portal-root"
294
+ ) as HTMLElement;
295
+ if (!portalRoot) {
296
+ portalRoot = document.createElement("div");
297
+ portalRoot.id = "context-menu-portal-root";
298
+ portalRoot.style.cssText =
299
+ "position: fixed; top: 0; left: 0; z-index: 9999; pointer-events: none; isolation: isolate;";
300
+ document.body.appendChild(portalRoot);
301
+ }
302
+ setPortalContainer(portalRoot);
303
+ }, []);
304
+
305
+ return (
306
+ <Menu.Portal container={portalContainer}>
307
+ <Menu.Positioner className="outline-none" alignOffset={-4} sideOffset={8}>
308
+ <Menu.Popup
309
+ ref={ref}
310
+ className={cn(
311
+ "z-[9999] min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg pointer-events-auto",
312
+ "origin-[var(--transform-origin)]",
313
+ "outline-none focus:outline-none focus-visible:outline-none",
314
+ "data-[starting-style]:animate-in data-[starting-style]:fade-in-0 data-[starting-style]:zoom-in-95",
315
+ "data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95",
316
+ className
317
+ )}
318
+ {...props}
319
+ />
320
+ </Menu.Positioner>
321
+ </Menu.Portal>
322
+ );
323
+ });
260
324
  ContextMenuSubContent.displayName = "ContextMenuSubContent";
261
325
 
262
326
  export {
@@ -269,10 +333,11 @@ export {
269
333
  ContextMenuCheckboxItem,
270
334
  ContextMenuRadioGroup,
271
335
  ContextMenuRadioItem,
336
+ ContextMenuGroup,
272
337
  ContextMenuLabel,
273
338
  ContextMenuSeparator,
274
339
  ContextMenuShortcut,
275
340
  ContextMenuSub,
276
341
  ContextMenuSubTrigger,
277
342
  ContextMenuSubContent,
278
- };
343
+ };