@octanejs/shadcn 0.0.10 → 0.0.12
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 +6 -7
- package/package.json +13 -8
- package/src/bases/base-ui/ui/context-menu.tsrx +277 -0
- package/src/bases/base-ui/ui/hover-card.tsrx +100 -0
- package/src/bases/base-ui/ui/menubar.tsrx +288 -0
- package/src/bases/base-ui/ui/sidebar.tsrx +628 -0
- package/src/bases/base-ui/ui/tabs.tsrx +90 -0
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ component names stay identical.
|
|
|
33
33
|
|
|
34
34
|
<!-- BEGIN COVERAGE -->
|
|
35
35
|
|
|
36
|
-
**44 families** — Radix 44/44 · React Aria 33/44 · Base UI
|
|
36
|
+
**44 families** — Radix 44/44 · React Aria 33/44 · Base UI 40/44
|
|
37
37
|
|
|
38
38
|
✅ ported · — not ported yet (fair game) · ⛔ blocked, see notes below
|
|
39
39
|
|
|
@@ -50,17 +50,17 @@ component names stay identical.
|
|
|
50
50
|
| `card` | ✅ | ✅ | ✅ |
|
|
51
51
|
| `checkbox` | ✅ | ✅ | ✅ |
|
|
52
52
|
| `collapsible` | ✅ | ✅ | ✅ |
|
|
53
|
-
| `context-menu` | ✅ | ⛔ |
|
|
53
|
+
| `context-menu` | ✅ | ⛔ | ✅ |
|
|
54
54
|
| `dialog` | ✅ | ✅ | ✅ |
|
|
55
55
|
| `dropdown-menu` | ✅ | ⛔ | ✅ |
|
|
56
56
|
| `empty` | ✅ | ✅ | ✅ |
|
|
57
57
|
| `field` | ✅ | ✅ | ✅ |
|
|
58
|
-
| `hover-card` | ✅ | ⛔ |
|
|
58
|
+
| `hover-card` | ✅ | ⛔ | ✅ |
|
|
59
59
|
| `input` | ✅ | ✅ | ✅ |
|
|
60
60
|
| `item` | ✅ | ✅ | ✅ |
|
|
61
61
|
| `kbd` | ✅ | ✅ | ✅ |
|
|
62
62
|
| `label` | ✅ | ✅ | ✅ |
|
|
63
|
-
| `menubar` | ✅ | ⛔ |
|
|
63
|
+
| `menubar` | ✅ | ⛔ | ✅ |
|
|
64
64
|
| `native-select` | ✅ | ✅ | ✅ |
|
|
65
65
|
| `navigation-menu` | ✅ | ⛔ | ⛔ |
|
|
66
66
|
| `pagination` | ✅ | ✅ | ✅ |
|
|
@@ -71,14 +71,14 @@ component names stay identical.
|
|
|
71
71
|
| `select` | ✅ | ⛔ | ⛔ |
|
|
72
72
|
| `separator` | ✅ | ✅ | ✅ |
|
|
73
73
|
| `sheet` | ✅ | ✅ | ✅ |
|
|
74
|
-
| `sidebar` | ✅ | ⛔ |
|
|
74
|
+
| `sidebar` | ✅ | ⛔ | ✅ |
|
|
75
75
|
| `skeleton` | ✅ | ✅ | ✅ |
|
|
76
76
|
| `slider` | ✅ | ✅ | ✅ |
|
|
77
77
|
| `sonner` | ✅ | ⛔ | ⛔ |
|
|
78
78
|
| `spinner` | ✅ | ✅ | ✅ |
|
|
79
79
|
| `switch` | ✅ | ✅ | ✅ |
|
|
80
80
|
| `table` | ✅ | ✅ | ✅ |
|
|
81
|
-
| `tabs` | ✅ | ✅ |
|
|
81
|
+
| `tabs` | ✅ | ✅ | ✅ |
|
|
82
82
|
| `textarea` | ✅ | ✅ | ✅ |
|
|
83
83
|
| `toggle` | ✅ | ✅ | ✅ |
|
|
84
84
|
| `toggle-group` | ✅ | ⛔ | ✅ |
|
|
@@ -92,7 +92,6 @@ component names stay identical.
|
|
|
92
92
|
| Base UI | `scroll-area` | @octanejs/base-ui has no scroll-area primitive |
|
|
93
93
|
| Base UI | `select` | @octanejs/base-ui has no select primitive |
|
|
94
94
|
| Base UI | `sonner` | needs next-themes, which has no octane binding |
|
|
95
|
-
| Base UI | `tabs` | @octanejs/base-ui has no tabs primitive |
|
|
96
95
|
| React Aria | `context-menu` | no counterpart in upstream’s aria base |
|
|
97
96
|
| React Aria | `dropdown-menu` | no counterpart in upstream’s aria base |
|
|
98
97
|
| React Aria | `hover-card` | no counterpart in upstream’s aria base |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/shadcn",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -115,14 +115,17 @@
|
|
|
115
115
|
"./base-ui/Card": "./src/bases/base-ui/ui/card.tsrx",
|
|
116
116
|
"./base-ui/Checkbox": "./src/bases/base-ui/ui/checkbox.tsrx",
|
|
117
117
|
"./base-ui/Collapsible": "./src/bases/base-ui/ui/collapsible.tsrx",
|
|
118
|
+
"./base-ui/ContextMenu": "./src/bases/base-ui/ui/context-menu.tsrx",
|
|
118
119
|
"./base-ui/Dialog": "./src/bases/base-ui/ui/dialog.tsrx",
|
|
119
120
|
"./base-ui/DropdownMenu": "./src/bases/base-ui/ui/dropdown-menu.tsrx",
|
|
120
121
|
"./base-ui/Empty": "./src/bases/base-ui/ui/empty.tsrx",
|
|
121
122
|
"./base-ui/Field": "./src/bases/base-ui/ui/field.tsrx",
|
|
123
|
+
"./base-ui/HoverCard": "./src/bases/base-ui/ui/hover-card.tsrx",
|
|
122
124
|
"./base-ui/Input": "./src/bases/base-ui/ui/input.tsrx",
|
|
123
125
|
"./base-ui/Item": "./src/bases/base-ui/ui/item.tsrx",
|
|
124
126
|
"./base-ui/Kbd": "./src/bases/base-ui/ui/kbd.tsrx",
|
|
125
127
|
"./base-ui/Label": "./src/bases/base-ui/ui/label.tsrx",
|
|
128
|
+
"./base-ui/Menubar": "./src/bases/base-ui/ui/menubar.tsrx",
|
|
126
129
|
"./base-ui/NativeSelect": "./src/bases/base-ui/ui/native-select.tsrx",
|
|
127
130
|
"./base-ui/Pagination": "./src/bases/base-ui/ui/pagination.tsrx",
|
|
128
131
|
"./base-ui/Popover": "./src/bases/base-ui/ui/popover.tsrx",
|
|
@@ -130,11 +133,13 @@
|
|
|
130
133
|
"./base-ui/RadioGroup": "./src/bases/base-ui/ui/radio-group.tsrx",
|
|
131
134
|
"./base-ui/Separator": "./src/bases/base-ui/ui/separator.tsrx",
|
|
132
135
|
"./base-ui/Sheet": "./src/bases/base-ui/ui/sheet.tsrx",
|
|
136
|
+
"./base-ui/Sidebar": "./src/bases/base-ui/ui/sidebar.tsrx",
|
|
133
137
|
"./base-ui/Skeleton": "./src/bases/base-ui/ui/skeleton.tsrx",
|
|
134
138
|
"./base-ui/Slider": "./src/bases/base-ui/ui/slider.tsrx",
|
|
135
139
|
"./base-ui/Spinner": "./src/bases/base-ui/ui/spinner.tsrx",
|
|
136
140
|
"./base-ui/Switch": "./src/bases/base-ui/ui/switch.tsrx",
|
|
137
141
|
"./base-ui/Table": "./src/bases/base-ui/ui/table.tsrx",
|
|
142
|
+
"./base-ui/Tabs": "./src/bases/base-ui/ui/tabs.tsrx",
|
|
138
143
|
"./base-ui/Textarea": "./src/bases/base-ui/ui/textarea.tsrx",
|
|
139
144
|
"./base-ui/Toggle": "./src/bases/base-ui/ui/toggle.tsrx",
|
|
140
145
|
"./base-ui/ToggleGroup": "./src/bases/base-ui/ui/toggle-group.tsrx",
|
|
@@ -148,14 +153,14 @@
|
|
|
148
153
|
"class-variance-authority": "0.7.1",
|
|
149
154
|
"clsx": "^2.1.1",
|
|
150
155
|
"tailwind-merge": "3.6.0",
|
|
151
|
-
"@octanejs/aria": "0.0.
|
|
152
|
-
"@octanejs/base-ui": "0.1.
|
|
153
|
-
"@octanejs/lucide": "0.1.
|
|
154
|
-
"@octanejs/radix": "0.1.
|
|
155
|
-
"@octanejs/sonner": "0.1.
|
|
156
|
+
"@octanejs/aria": "0.0.21",
|
|
157
|
+
"@octanejs/base-ui": "0.1.25",
|
|
158
|
+
"@octanejs/lucide": "0.1.22",
|
|
159
|
+
"@octanejs/radix": "0.1.26",
|
|
160
|
+
"@octanejs/sonner": "0.1.22"
|
|
156
161
|
},
|
|
157
162
|
"peerDependencies": {
|
|
158
|
-
"octane": "0.1.
|
|
163
|
+
"octane": "0.1.27"
|
|
159
164
|
},
|
|
160
165
|
"devDependencies": {
|
|
161
166
|
"@tsrx/react": "^0.2.56",
|
|
@@ -165,7 +170,7 @@
|
|
|
165
170
|
"react": "^19.2.7",
|
|
166
171
|
"react-dom": "^19.2.7",
|
|
167
172
|
"vitest": "^4.1.10",
|
|
168
|
-
"octane": "0.1.
|
|
173
|
+
"octane": "0.1.27"
|
|
169
174
|
},
|
|
170
175
|
"scripts": {
|
|
171
176
|
"test": "vitest run",
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// Base UI base context-menu — runs on @octanejs/base-ui's `ContextMenu`. Class strings from the
|
|
2
|
+
// maintainer-supplied radix source; every difference below was checked against rendered DOM.
|
|
3
|
+
//
|
|
4
|
+
// This is dropdown-menu's sibling and inherits its findings, WITH ONE IMPROVEMENT: `ContextMenu`
|
|
5
|
+
// ships a real `Separator` part, which `Menu` does not, so the separator is the primitive here
|
|
6
|
+
// rather than a plain host element. It emits `role="separator"`, `aria-orientation` and
|
|
7
|
+
// `data-orientation`.
|
|
8
|
+
//
|
|
9
|
+
// CARRIED OVER FROM dropdown-menu, all re-verified against this primitive:
|
|
10
|
+
// - Two CSS variables are renamed. Radix publishes per-component names; Base UI's Positioner
|
|
11
|
+
// publishes generic ones, and a utility naming a variable nothing sets does nothing, silently:
|
|
12
|
+
// --radix-context-menu-content-available-height -> --available-height
|
|
13
|
+
// --radix-context-menu-content-transform-origin -> --transform-origin
|
|
14
|
+
// - The submenu trigger marks itself open with `data-popup-open`, not the `data-open` the popup
|
|
15
|
+
// carries, so radix's `data-open:bg-accent` would leave an open submenu's parent row unstyled.
|
|
16
|
+
// - `Label` stays a plain `<div>`: `ContextMenu.GroupLabel` requires a `ContextMenu.Group`
|
|
17
|
+
// ancestor and throws without one, while shadcn's label is used standalone.
|
|
18
|
+
// - Radix's `focus:` item utilities carry over unchanged, because Base UI moves real DOM focus
|
|
19
|
+
// onto the highlighted item as well as publishing `data-highlighted`.
|
|
20
|
+
//
|
|
21
|
+
// POSITIONING PROPS GO TO THE POSITIONER, NOT THE POPUP. Radix has one `Content` element that takes
|
|
22
|
+
// them all; Base UI splits positioning into its own layer, so anything swept onto `Popup` by a rest
|
|
23
|
+
// spread is inert — `side="top"` would silently leave the menu on its default side and also land on
|
|
24
|
+
// the DOM as an invalid attribute. They are destructured and forwarded explicitly.
|
|
25
|
+
import { type OctaneNode } from 'octane';
|
|
26
|
+
import { ContextMenu as ContextMenuPrimitive } from '@octanejs/base-ui/context-menu';
|
|
27
|
+
import { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';
|
|
28
|
+
|
|
29
|
+
import { cn } from '../../../lib/utils';
|
|
30
|
+
|
|
31
|
+
type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
|
|
32
|
+
|
|
33
|
+
export interface BaseUiPositioningProps {
|
|
34
|
+
/** Forwarded to the Positioner; see the note at the top of this file. */
|
|
35
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
36
|
+
sideOffset?: number;
|
|
37
|
+
align?: 'start' | 'center' | 'end';
|
|
38
|
+
alignOffset?: number;
|
|
39
|
+
anchor?: unknown;
|
|
40
|
+
positionMethod?: 'absolute' | 'fixed';
|
|
41
|
+
collisionBoundary?: unknown;
|
|
42
|
+
collisionPadding?: unknown;
|
|
43
|
+
collisionAvoidance?: unknown;
|
|
44
|
+
arrowPadding?: number;
|
|
45
|
+
sticky?: boolean;
|
|
46
|
+
disableAnchorTracking?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ContextMenu(props: Record<string, unknown>) @{
|
|
50
|
+
<ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function ContextMenuTrigger({ className, ...props }: Props) @{
|
|
54
|
+
<ContextMenuPrimitive.Trigger
|
|
55
|
+
data-slot="context-menu-trigger"
|
|
56
|
+
className={cn('select-none', className)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function ContextMenuGroup(props: Record<string, unknown>) @{
|
|
62
|
+
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function ContextMenuPortal(props: Record<string, unknown>) @{
|
|
66
|
+
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function ContextMenuSub(props: Record<string, unknown>) @{
|
|
70
|
+
<ContextMenuPrimitive.SubmenuRoot data-slot="context-menu-sub" {...props} />
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function ContextMenuRadioGroup(props: Record<string, unknown>) @{
|
|
74
|
+
<ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} />
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function ContextMenuContent({
|
|
78
|
+
className,
|
|
79
|
+
side,
|
|
80
|
+
sideOffset,
|
|
81
|
+
align,
|
|
82
|
+
alignOffset,
|
|
83
|
+
anchor,
|
|
84
|
+
positionMethod,
|
|
85
|
+
collisionBoundary,
|
|
86
|
+
collisionPadding,
|
|
87
|
+
collisionAvoidance,
|
|
88
|
+
arrowPadding,
|
|
89
|
+
sticky,
|
|
90
|
+
disableAnchorTracking,
|
|
91
|
+
...props
|
|
92
|
+
}: Props & BaseUiPositioningProps) @{
|
|
93
|
+
<ContextMenuPrimitive.Portal>
|
|
94
|
+
<ContextMenuPrimitive.Positioner
|
|
95
|
+
side={side}
|
|
96
|
+
sideOffset={sideOffset}
|
|
97
|
+
align={align}
|
|
98
|
+
alignOffset={alignOffset}
|
|
99
|
+
anchor={anchor}
|
|
100
|
+
positionMethod={positionMethod}
|
|
101
|
+
collisionBoundary={collisionBoundary}
|
|
102
|
+
collisionPadding={collisionPadding}
|
|
103
|
+
collisionAvoidance={collisionAvoidance}
|
|
104
|
+
arrowPadding={arrowPadding}
|
|
105
|
+
sticky={sticky}
|
|
106
|
+
disableAnchorTracking={disableAnchorTracking}
|
|
107
|
+
>
|
|
108
|
+
<ContextMenuPrimitive.Popup
|
|
109
|
+
data-slot="context-menu-content"
|
|
110
|
+
className={cn(
|
|
111
|
+
'z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
|
|
112
|
+
className,
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
</ContextMenuPrimitive.Positioner>
|
|
117
|
+
</ContextMenuPrimitive.Portal>
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function ContextMenuItem({ className, inset, variant = 'default', ...props }: Props & {
|
|
121
|
+
inset?: boolean;
|
|
122
|
+
variant?: 'default' | 'destructive';
|
|
123
|
+
}) @{
|
|
124
|
+
<ContextMenuPrimitive.Item
|
|
125
|
+
data-slot="context-menu-item"
|
|
126
|
+
data-inset={inset}
|
|
127
|
+
data-variant={variant}
|
|
128
|
+
className={cn(
|
|
129
|
+
'group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive',
|
|
130
|
+
className,
|
|
131
|
+
)}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function ContextMenuSubTrigger(props: Props & { inset?: boolean }) @{
|
|
137
|
+
const { className, children: _children, inset, ...rest } = props;
|
|
138
|
+
|
|
139
|
+
<ContextMenuPrimitive.SubmenuTrigger
|
|
140
|
+
data-slot="context-menu-sub-trigger"
|
|
141
|
+
data-inset={inset}
|
|
142
|
+
className={cn(
|
|
143
|
+
'flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
144
|
+
className,
|
|
145
|
+
)}
|
|
146
|
+
{...rest}
|
|
147
|
+
>
|
|
148
|
+
{props.children}
|
|
149
|
+
<ChevronRightIcon className="cn-rtl-flip ml-auto" />
|
|
150
|
+
</ContextMenuPrimitive.SubmenuTrigger>
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function ContextMenuSubContent({
|
|
154
|
+
className,
|
|
155
|
+
side,
|
|
156
|
+
sideOffset,
|
|
157
|
+
align,
|
|
158
|
+
alignOffset,
|
|
159
|
+
anchor,
|
|
160
|
+
positionMethod,
|
|
161
|
+
collisionBoundary,
|
|
162
|
+
collisionPadding,
|
|
163
|
+
collisionAvoidance,
|
|
164
|
+
arrowPadding,
|
|
165
|
+
sticky,
|
|
166
|
+
disableAnchorTracking,
|
|
167
|
+
...props
|
|
168
|
+
}: Props & BaseUiPositioningProps) @{
|
|
169
|
+
<ContextMenuPrimitive.Portal>
|
|
170
|
+
<ContextMenuPrimitive.Positioner
|
|
171
|
+
side={side}
|
|
172
|
+
sideOffset={sideOffset}
|
|
173
|
+
align={align}
|
|
174
|
+
alignOffset={alignOffset}
|
|
175
|
+
anchor={anchor}
|
|
176
|
+
positionMethod={positionMethod}
|
|
177
|
+
collisionBoundary={collisionBoundary}
|
|
178
|
+
collisionPadding={collisionPadding}
|
|
179
|
+
collisionAvoidance={collisionAvoidance}
|
|
180
|
+
arrowPadding={arrowPadding}
|
|
181
|
+
sticky={sticky}
|
|
182
|
+
disableAnchorTracking={disableAnchorTracking}
|
|
183
|
+
>
|
|
184
|
+
<ContextMenuPrimitive.Popup
|
|
185
|
+
data-slot="context-menu-sub-content"
|
|
186
|
+
className={cn(
|
|
187
|
+
'z-50 min-w-32 origin-(--transform-origin) overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
|
|
188
|
+
className,
|
|
189
|
+
)}
|
|
190
|
+
{...props}
|
|
191
|
+
/>
|
|
192
|
+
</ContextMenuPrimitive.Positioner>
|
|
193
|
+
</ContextMenuPrimitive.Portal>
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function ContextMenuCheckboxItem(props: Props & {
|
|
197
|
+
checked?: boolean;
|
|
198
|
+
inset?: boolean;
|
|
199
|
+
}) @{
|
|
200
|
+
const { className, children: _children, checked, inset, ...rest } = props;
|
|
201
|
+
|
|
202
|
+
<ContextMenuPrimitive.CheckboxItem
|
|
203
|
+
data-slot="context-menu-checkbox-item"
|
|
204
|
+
data-inset={inset}
|
|
205
|
+
className={cn(
|
|
206
|
+
'relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
207
|
+
className,
|
|
208
|
+
)}
|
|
209
|
+
checked={checked}
|
|
210
|
+
{...rest}
|
|
211
|
+
>
|
|
212
|
+
<span
|
|
213
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
214
|
+
data-slot="context-menu-checkbox-item-indicator"
|
|
215
|
+
>
|
|
216
|
+
<ContextMenuPrimitive.CheckboxItemIndicator>
|
|
217
|
+
<CheckIcon />
|
|
218
|
+
</ContextMenuPrimitive.CheckboxItemIndicator>
|
|
219
|
+
</span>
|
|
220
|
+
{props.children}
|
|
221
|
+
</ContextMenuPrimitive.CheckboxItem>
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function ContextMenuRadioItem(props: Props & { inset?: boolean }) @{
|
|
225
|
+
const { className, children: _children, inset, ...rest } = props;
|
|
226
|
+
|
|
227
|
+
<ContextMenuPrimitive.RadioItem
|
|
228
|
+
data-slot="context-menu-radio-item"
|
|
229
|
+
data-inset={inset}
|
|
230
|
+
className={cn(
|
|
231
|
+
'relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
|
232
|
+
className,
|
|
233
|
+
)}
|
|
234
|
+
{...rest}
|
|
235
|
+
>
|
|
236
|
+
<span
|
|
237
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
238
|
+
data-slot="context-menu-radio-item-indicator"
|
|
239
|
+
>
|
|
240
|
+
<ContextMenuPrimitive.RadioItemIndicator>
|
|
241
|
+
<CheckIcon />
|
|
242
|
+
</ContextMenuPrimitive.RadioItemIndicator>
|
|
243
|
+
</span>
|
|
244
|
+
{props.children}
|
|
245
|
+
</ContextMenuPrimitive.RadioItem>
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function ContextMenuLabel({ className, inset, ...props }: Props & { inset?: boolean }) @{
|
|
249
|
+
<div
|
|
250
|
+
data-slot="context-menu-label"
|
|
251
|
+
data-inset={inset}
|
|
252
|
+
className={cn(
|
|
253
|
+
'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7',
|
|
254
|
+
className,
|
|
255
|
+
)}
|
|
256
|
+
{...props}
|
|
257
|
+
/>
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function ContextMenuSeparator({ className, ...props }: Props) @{
|
|
261
|
+
<ContextMenuPrimitive.Separator
|
|
262
|
+
data-slot="context-menu-separator"
|
|
263
|
+
className={cn('-mx-1 my-1 h-px bg-border', className)}
|
|
264
|
+
{...props}
|
|
265
|
+
/>
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function ContextMenuShortcut({ className, ...props }: Props) @{
|
|
269
|
+
<span
|
|
270
|
+
data-slot="context-menu-shortcut"
|
|
271
|
+
className={cn(
|
|
272
|
+
'ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground',
|
|
273
|
+
className,
|
|
274
|
+
)}
|
|
275
|
+
{...props}
|
|
276
|
+
/>
|
|
277
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Base UI base hover-card — runs on @octanejs/base-ui's `PreviewCard`, which is Base UI's name for
|
|
2
|
+
// this family. Class strings from the maintainer-supplied radix source.
|
|
3
|
+
//
|
|
4
|
+
// THE OPEN/CLOSED DIALECT HAD TO BE REWRITTEN, and this is the one that would have failed loudest.
|
|
5
|
+
// The radix source for this family uses the OLDER spelling — `data-[state=open]:animate-in`,
|
|
6
|
+
// `data-[state=closed]:animate-out` — where the other menu families in this base use `data-open:`.
|
|
7
|
+
// Base UI publishes NO `data-state` attribute at all (verified by rendering an open card and
|
|
8
|
+
// querying for one: there is none). Copied across, every entry and exit utility would match nothing
|
|
9
|
+
// and the card would pop in and out with no animation whatsoever.
|
|
10
|
+
//
|
|
11
|
+
// data-[state=open]:… -> data-open:…
|
|
12
|
+
// data-[state=closed]:… -> data-closed:…
|
|
13
|
+
//
|
|
14
|
+
// The transform-origin variable is renamed for the same reason as every other overlay here:
|
|
15
|
+
// `--radix-hover-card-content-transform-origin` -> `--transform-origin`.
|
|
16
|
+
//
|
|
17
|
+
// PART TREE. Radix has Portal > Content; Base UI splits positioning into its own layer, the same
|
|
18
|
+
// shape this base's popover and dropdown-menu use:
|
|
19
|
+
//
|
|
20
|
+
// radix Root > Portal > Content
|
|
21
|
+
// Base UI Root > Portal > Positioner > Popup
|
|
22
|
+
//
|
|
23
|
+
// Positioning props are therefore destructured and forwarded to the Positioner. Swept onto `Popup`
|
|
24
|
+
// by a rest spread they are inert — `side="top"` would silently leave the card on its default side
|
|
25
|
+
// and also land on the DOM as an invalid attribute.
|
|
26
|
+
import { createElement } from 'octane';
|
|
27
|
+
import { PreviewCard as PreviewCardPrimitive } from '@octanejs/base-ui/preview-card';
|
|
28
|
+
|
|
29
|
+
import { cn } from '../../../lib/utils';
|
|
30
|
+
|
|
31
|
+
export interface BaseUiPositioningProps {
|
|
32
|
+
/** Forwarded to the Positioner; see the note at the top of this file. */
|
|
33
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
34
|
+
sideOffset?: number;
|
|
35
|
+
align?: 'start' | 'center' | 'end';
|
|
36
|
+
alignOffset?: number;
|
|
37
|
+
anchor?: unknown;
|
|
38
|
+
positionMethod?: 'absolute' | 'fixed';
|
|
39
|
+
collisionBoundary?: unknown;
|
|
40
|
+
collisionPadding?: unknown;
|
|
41
|
+
collisionAvoidance?: unknown;
|
|
42
|
+
arrowPadding?: number;
|
|
43
|
+
sticky?: boolean;
|
|
44
|
+
disableAnchorTracking?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function HoverCard(props: Record<string, unknown>) @{
|
|
48
|
+
<PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function HoverCardTrigger(props: Record<string, unknown>) @{
|
|
52
|
+
<PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface HoverCardContentProps extends Record<string, unknown>, BaseUiPositioningProps {
|
|
56
|
+
className?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function HoverCardContent({
|
|
60
|
+
className,
|
|
61
|
+
align = 'center',
|
|
62
|
+
sideOffset = 4,
|
|
63
|
+
side,
|
|
64
|
+
alignOffset,
|
|
65
|
+
anchor,
|
|
66
|
+
positionMethod,
|
|
67
|
+
collisionBoundary,
|
|
68
|
+
collisionPadding,
|
|
69
|
+
collisionAvoidance,
|
|
70
|
+
arrowPadding,
|
|
71
|
+
sticky,
|
|
72
|
+
disableAnchorTracking,
|
|
73
|
+
...props
|
|
74
|
+
}: HoverCardContentProps) {
|
|
75
|
+
return createElement(PreviewCardPrimitive.Portal, {
|
|
76
|
+
'data-slot': 'hover-card-portal',
|
|
77
|
+
children: createElement(PreviewCardPrimitive.Positioner, {
|
|
78
|
+
align,
|
|
79
|
+
sideOffset,
|
|
80
|
+
side,
|
|
81
|
+
alignOffset,
|
|
82
|
+
anchor,
|
|
83
|
+
positionMethod,
|
|
84
|
+
collisionBoundary,
|
|
85
|
+
collisionPadding,
|
|
86
|
+
collisionAvoidance,
|
|
87
|
+
arrowPadding,
|
|
88
|
+
sticky,
|
|
89
|
+
disableAnchorTracking,
|
|
90
|
+
children: createElement(PreviewCardPrimitive.Popup, {
|
|
91
|
+
'data-slot': 'hover-card-content',
|
|
92
|
+
className: cn(
|
|
93
|
+
'z-50 w-64 origin-(--transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden 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 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95',
|
|
94
|
+
className,
|
|
95
|
+
),
|
|
96
|
+
...props,
|
|
97
|
+
}),
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
}
|