@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 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 35/44
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.10",
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.19",
152
- "@octanejs/base-ui": "0.1.23",
153
- "@octanejs/lucide": "0.1.20",
154
- "@octanejs/radix": "0.1.24",
155
- "@octanejs/sonner": "0.1.20"
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.25"
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.25"
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
+ }