@octanejs/shadcn 0.0.37 → 0.0.39

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 (119) hide show
  1. package/{LICENSE → LICENSE.upstream} +1 -1
  2. package/README.md +22 -19
  3. package/package.json +17 -11
  4. package/src/bases/base-ui/ui/accordion.tsrx +1 -1
  5. package/src/bases/base-ui/ui/alert-dialog.tsrx +1 -1
  6. package/src/bases/base-ui/ui/alert.tsrx +1 -1
  7. package/src/bases/base-ui/ui/aspect-ratio.tsrx +1 -1
  8. package/src/bases/base-ui/ui/avatar.tsrx +1 -1
  9. package/src/bases/base-ui/ui/badge.tsrx +1 -1
  10. package/src/bases/base-ui/ui/breadcrumb.tsrx +1 -1
  11. package/src/bases/base-ui/ui/button.tsrx +1 -1
  12. package/src/bases/base-ui/ui/card.tsrx +1 -1
  13. package/src/bases/base-ui/ui/checkbox.tsrx +1 -1
  14. package/src/bases/base-ui/ui/context-menu.tsrx +22 -17
  15. package/src/bases/base-ui/ui/dialog.tsrx +31 -40
  16. package/src/bases/base-ui/ui/dropdown-menu.tsrx +22 -17
  17. package/src/bases/base-ui/ui/empty.tsrx +1 -1
  18. package/src/bases/base-ui/ui/field.tsrx +1 -1
  19. package/src/bases/base-ui/ui/hover-card.tsrx +18 -16
  20. package/src/bases/base-ui/ui/input.tsrx +1 -1
  21. package/src/bases/base-ui/ui/item.tsrx +1 -1
  22. package/src/bases/base-ui/ui/kbd.tsrx +1 -1
  23. package/src/bases/base-ui/ui/label.tsrx +1 -1
  24. package/src/bases/base-ui/ui/menubar.tsrx +22 -17
  25. package/src/bases/base-ui/ui/native-select.tsrx +1 -1
  26. package/src/bases/base-ui/ui/navigation-menu.tsrx +137 -0
  27. package/src/bases/base-ui/ui/pagination.tsrx +1 -1
  28. package/src/bases/base-ui/ui/popover.tsrx +18 -16
  29. package/src/bases/base-ui/ui/progress.tsrx +1 -1
  30. package/src/bases/base-ui/ui/radio-group.tsrx +1 -1
  31. package/src/bases/base-ui/ui/scroll-area.tsrx +44 -0
  32. package/src/bases/base-ui/ui/select.tsrx +157 -0
  33. package/src/bases/base-ui/ui/separator.tsrx +1 -1
  34. package/src/bases/base-ui/ui/sheet.tsrx +1 -1
  35. package/src/bases/base-ui/ui/sidebar.tsrx +1 -1
  36. package/src/bases/base-ui/ui/skeleton.tsrx +1 -1
  37. package/src/bases/base-ui/ui/slider.tsrx +41 -52
  38. package/src/bases/base-ui/ui/spinner.tsrx +1 -1
  39. package/src/bases/base-ui/ui/switch.tsrx +1 -1
  40. package/src/bases/base-ui/ui/table.tsrx +1 -1
  41. package/src/bases/base-ui/ui/tabs.tsrx +8 -17
  42. package/src/bases/base-ui/ui/textarea.tsrx +1 -1
  43. package/src/bases/base-ui/ui/toggle-group.tsrx +22 -32
  44. package/src/bases/base-ui/ui/toggle.tsrx +1 -1
  45. package/src/bases/base-ui/ui/tooltip.tsrx +18 -16
  46. package/src/bases/radix/ui/accordion.tsrx +1 -1
  47. package/src/bases/radix/ui/alert-dialog.tsrx +1 -1
  48. package/src/bases/radix/ui/alert.tsrx +1 -1
  49. package/src/bases/radix/ui/avatar.tsrx +1 -1
  50. package/src/bases/radix/ui/badge.tsrx +1 -1
  51. package/src/bases/radix/ui/breadcrumb.tsrx +1 -1
  52. package/src/bases/radix/ui/button.tsrx +1 -1
  53. package/src/bases/radix/ui/card.tsrx +1 -1
  54. package/src/bases/radix/ui/checkbox.tsrx +1 -1
  55. package/src/bases/radix/ui/context-menu.tsrx +1 -1
  56. package/src/bases/radix/ui/dialog.tsrx +1 -1
  57. package/src/bases/radix/ui/dropdown-menu.tsrx +1 -1
  58. package/src/bases/radix/ui/empty.tsrx +1 -1
  59. package/src/bases/radix/ui/field.tsrx +1 -1
  60. package/src/bases/radix/ui/hover-card.tsrx +1 -1
  61. package/src/bases/radix/ui/input.tsrx +1 -1
  62. package/src/bases/radix/ui/item.tsrx +1 -1
  63. package/src/bases/radix/ui/kbd.tsrx +1 -1
  64. package/src/bases/radix/ui/label.tsrx +1 -1
  65. package/src/bases/radix/ui/menubar.tsrx +1 -1
  66. package/src/bases/radix/ui/native-select.tsrx +1 -1
  67. package/src/bases/radix/ui/navigation-menu.tsrx +1 -1
  68. package/src/bases/radix/ui/pagination.tsrx +1 -1
  69. package/src/bases/radix/ui/popover.tsrx +1 -1
  70. package/src/bases/radix/ui/progress.tsrx +1 -1
  71. package/src/bases/radix/ui/radio-group.tsrx +1 -1
  72. package/src/bases/radix/ui/scroll-area.tsrx +1 -1
  73. package/src/bases/radix/ui/select.tsrx +1 -1
  74. package/src/bases/radix/ui/separator.tsrx +1 -1
  75. package/src/bases/radix/ui/sheet.tsrx +1 -1
  76. package/src/bases/radix/ui/sidebar.tsrx +1 -1
  77. package/src/bases/radix/ui/skeleton.tsrx +1 -1
  78. package/src/bases/radix/ui/slider.tsrx +1 -1
  79. package/src/bases/radix/ui/spinner.tsrx +1 -1
  80. package/src/bases/radix/ui/switch.tsrx +1 -1
  81. package/src/bases/radix/ui/table.tsrx +1 -1
  82. package/src/bases/radix/ui/tabs.tsrx +1 -1
  83. package/src/bases/radix/ui/textarea.tsrx +1 -1
  84. package/src/bases/radix/ui/toggle-group.tsrx +1 -1
  85. package/src/bases/radix/ui/toggle.tsrx +1 -1
  86. package/src/bases/radix/ui/tooltip.tsrx +1 -1
  87. package/src/bases/react-aria/ui/accordion.tsrx +1 -1
  88. package/src/bases/react-aria/ui/alert-dialog.tsrx +1 -1
  89. package/src/bases/react-aria/ui/alert.tsrx +1 -1
  90. package/src/bases/react-aria/ui/aspect-ratio.tsrx +1 -1
  91. package/src/bases/react-aria/ui/avatar.tsrx +1 -1
  92. package/src/bases/react-aria/ui/badge.tsrx +1 -1
  93. package/src/bases/react-aria/ui/breadcrumb.tsrx +1 -1
  94. package/src/bases/react-aria/ui/button.tsrx +1 -1
  95. package/src/bases/react-aria/ui/card.tsrx +1 -1
  96. package/src/bases/react-aria/ui/checkbox.tsrx +1 -1
  97. package/src/bases/react-aria/ui/dialog.tsrx +1 -1
  98. package/src/bases/react-aria/ui/empty.tsrx +1 -1
  99. package/src/bases/react-aria/ui/field.tsrx +1 -1
  100. package/src/bases/react-aria/ui/input.tsrx +1 -1
  101. package/src/bases/react-aria/ui/item.tsrx +1 -1
  102. package/src/bases/react-aria/ui/kbd.tsrx +1 -1
  103. package/src/bases/react-aria/ui/label.tsrx +1 -1
  104. package/src/bases/react-aria/ui/native-select.tsrx +1 -1
  105. package/src/bases/react-aria/ui/pagination.tsrx +1 -1
  106. package/src/bases/react-aria/ui/popover.tsrx +1 -1
  107. package/src/bases/react-aria/ui/radio-group.tsrx +1 -1
  108. package/src/bases/react-aria/ui/scroll-area.tsrx +1 -1
  109. package/src/bases/react-aria/ui/separator.tsrx +1 -1
  110. package/src/bases/react-aria/ui/sheet.tsrx +1 -1
  111. package/src/bases/react-aria/ui/skeleton.tsrx +1 -1
  112. package/src/bases/react-aria/ui/slider.tsrx +1 -1
  113. package/src/bases/react-aria/ui/spinner.tsrx +1 -1
  114. package/src/bases/react-aria/ui/switch.tsrx +1 -1
  115. package/src/bases/react-aria/ui/table.tsrx +1 -1
  116. package/src/bases/react-aria/ui/tabs.tsrx +1 -1
  117. package/src/bases/react-aria/ui/textarea.tsrx +1 -1
  118. package/src/bases/react-aria/ui/toggle.tsrx +1 -1
  119. package/src/lib/utils.ts +1 -8
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Dominic Gannaway
3
+ Copyright (c) 2023 shadcn
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  # @octanejs/shadcn
2
2
 
3
- shadcn/ui for the [octane](https://github.com/octanejs/octane) renderer — a port
4
- of the **Radix base** of [shadcn-ui/ui](https://github.com/shadcn-ui/ui) onto
5
- octane's hooks and native events, built on
6
- [`@octanejs/radix`](../radix).
3
+ shadcn/ui for the [octane](https://github.com/octanejs/octane) renderer, with
4
+ Base UI, Radix, and React Aria bases adapted to octane's hooks and native events.
7
5
 
8
6
  ## Installation
9
7
 
@@ -12,11 +10,11 @@ npm install @octanejs/shadcn
12
10
  pnpm add @octanejs/shadcn
13
11
  ```
14
12
 
15
- Upstream pin: `shadcn-ui/ui@4baadbc6` + CLI `shadcn@4.14.1`.
13
+ Upstream pin: `shadcn-ui/ui@7c9eaba1` + CLI `shadcn@4.21.0`.
16
14
 
17
15
  ## What ships
18
16
 
19
- - **40 component families (~185 exports)**: the full Tier-1 static set, all 24
17
+ - **44 component families across three bases**: the full Tier-1 static set, all 24
20
18
  radix-backed Tier-2 components, and the first Tier-3 composites (Sidebar with
21
19
  `useSidebar`/`useIsMobile`, Field), plus `cn()` and the default neutral theme
22
20
  tokens (`@octanejs/shadcn/theme.css`).
@@ -34,13 +32,21 @@ Upstream pin: `shadcn-ui/ui@4baadbc6` + CLI `shadcn@4.14.1`.
34
32
 
35
33
  ## Component coverage
36
34
 
37
- Each primitive base ships the families its primitives support, exactly as upstream's do.
38
- Switching `style` changes which primitive a component is built on the `data-slot` contract and
39
- component names stay identical.
35
+ The coverage table tracks the families ported for each primitive base.
36
+ Switching `style` changes which primitive a component uses. Each base retains its
37
+ upstream API; for example, Base UI Navigation Menu exposes a Positioner that owns
38
+ its viewport, and Base UI Select aliases its hostless Root.
39
+
40
+ The Base UI wrappers target `@octanejs/base-ui`'s Base UI 1.8.0 API, including
41
+ Select, Navigation Menu, and Scroll Area from the 4.21.0 registry with Nova
42
+ utilities. Combobox is outside this package's current family inventory.
43
+
44
+ All three bases use `cn@0.2.6`, matching the upstream update. Existing Octane
45
+ adaptations and styling remain intact; `@octanejs/shadcn/cn` re-exports the same helper.
40
46
 
41
47
  <!-- BEGIN COVERAGE -->
42
48
 
43
- **44 families** — Radix 44/44 · React Aria 33/44 · Base UI 40/44
49
+ **44 families** — Radix 44/44 · React Aria 33/44 · Base UI 43/44
44
50
 
45
51
  ✅ ported · — not ported yet (fair game) · ⛔ blocked, see notes below
46
52
 
@@ -69,13 +75,13 @@ component names stay identical.
69
75
  | `label` | ✅ | ✅ | ✅ |
70
76
  | `menubar` | ✅ | ⛔ | ✅ |
71
77
  | `native-select` | ✅ | ✅ | ✅ |
72
- | `navigation-menu` | ✅ | ⛔ | |
78
+ | `navigation-menu` | ✅ | ⛔ | |
73
79
  | `pagination` | ✅ | ✅ | ✅ |
74
80
  | `popover` | ✅ | ✅ | ✅ |
75
81
  | `progress` | ✅ | ⛔ | ✅ |
76
82
  | `radio-group` | ✅ | ✅ | ✅ |
77
- | `scroll-area` | ✅ | ✅ | |
78
- | `select` | ✅ | ⛔ | |
83
+ | `scroll-area` | ✅ | ✅ | |
84
+ | `select` | ✅ | ⛔ | |
79
85
  | `separator` | ✅ | ✅ | ✅ |
80
86
  | `sheet` | ✅ | ✅ | ✅ |
81
87
  | `sidebar` | ✅ | ⛔ | ✅ |
@@ -95,9 +101,6 @@ component names stay identical.
95
101
 
96
102
  | Base | Family | Reason |
97
103
  | --- | --- | --- |
98
- | Base UI | `navigation-menu` | @octanejs/base-ui has no navigation-menu primitive |
99
- | Base UI | `scroll-area` | @octanejs/base-ui has no scroll-area primitive |
100
- | Base UI | `select` | @octanejs/base-ui has no select primitive |
101
104
  | Base UI | `sonner` | needs next-themes, which has no octane binding |
102
105
  | React Aria | `context-menu` | no counterpart in upstream’s aria base |
103
106
  | React Aria | `dropdown-menu` | no counterpart in upstream’s aria base |
@@ -143,12 +146,12 @@ same mechanism shadcn uses upstream, where its own registry is
143
146
 
144
147
  | `style` | Primitives | Families |
145
148
  | --- | --- | --- |
146
- | `base-nova` *(default)* | `@octanejs/base-ui` | 21 |
149
+ | `base-nova` *(default)* | `@octanejs/base-ui` | 43 |
147
150
  | `radix-nova` | `@octanejs/radix` | 44 |
148
151
  | `aria-nova` | `@octanejs/aria` | 33 |
149
152
 
150
- The bases deliberately ship different family counts, exactly as upstream's do a base only
151
- has the families its primitives support. Switching `style` changes which primitive a component
153
+ The bases currently ship different family counts; see the coverage table for remaining ports.
154
+ Switching `style` changes which primitive a component
152
155
  is built on; the `data-slot` contract and component names stay identical, so your markup and
153
156
  styling do not change.
154
157
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@octanejs/shadcn",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22.22.2"
8
8
  },
9
- "description": "shadcn/ui for the octane renderer the Radix-base shadcn component distribution ported onto octane: cva-variant components preserving the upstream data-slot/class contract, built on @octanejs/radix, published as importable source with a shadcn-CLI-compatible registry planned.",
9
+ "description": "shadcn/ui components for Octane, with Base UI, Radix, and React Aria implementations and an installable source registry.",
10
10
  "author": {
11
11
  "name": "Dominic Gannaway",
12
12
  "email": "dg@domgan.com"
@@ -21,7 +21,8 @@
21
21
  },
22
22
  "files": [
23
23
  "src",
24
- "README.md"
24
+ "README.md",
25
+ "LICENSE.upstream"
25
26
  ],
26
27
  "sideEffects": [
27
28
  "./src/styles/theme.css"
@@ -127,10 +128,13 @@
127
128
  "./base-ui/Label": "./src/bases/base-ui/ui/label.tsrx",
128
129
  "./base-ui/Menubar": "./src/bases/base-ui/ui/menubar.tsrx",
129
130
  "./base-ui/NativeSelect": "./src/bases/base-ui/ui/native-select.tsrx",
131
+ "./base-ui/NavigationMenu": "./src/bases/base-ui/ui/navigation-menu.tsrx",
130
132
  "./base-ui/Pagination": "./src/bases/base-ui/ui/pagination.tsrx",
131
133
  "./base-ui/Popover": "./src/bases/base-ui/ui/popover.tsrx",
132
134
  "./base-ui/Progress": "./src/bases/base-ui/ui/progress.tsrx",
133
135
  "./base-ui/RadioGroup": "./src/bases/base-ui/ui/radio-group.tsrx",
136
+ "./base-ui/ScrollArea": "./src/bases/base-ui/ui/scroll-area.tsrx",
137
+ "./base-ui/Select": "./src/bases/base-ui/ui/select.tsrx",
134
138
  "./base-ui/Separator": "./src/bases/base-ui/ui/separator.tsrx",
135
139
  "./base-ui/Sheet": "./src/bases/base-ui/ui/sheet.tsrx",
136
140
  "./base-ui/Sidebar": "./src/bases/base-ui/ui/sidebar.tsrx",
@@ -152,25 +156,27 @@
152
156
  "dependencies": {
153
157
  "class-variance-authority": "0.7.1",
154
158
  "clsx": "^2.1.1",
159
+ "cn": "0.2.6",
155
160
  "tailwind-merge": "3.6.0",
156
- "@octanejs/aria": "0.0.46",
157
- "@octanejs/base-ui": "0.1.50",
161
+ "@octanejs/aria": "0.0.47",
162
+ "@octanejs/base-ui": "0.1.52",
158
163
  "@octanejs/lucide": "0.1.47",
159
- "@octanejs/radix": "0.1.51",
164
+ "@octanejs/radix": "0.1.52",
160
165
  "@octanejs/sonner": "0.1.47"
161
166
  },
162
167
  "peerDependencies": {
163
- "octane": "^0.1.51 || ^0.2.0"
168
+ "octane": "^0.2.5"
164
169
  },
165
170
  "devDependencies": {
166
- "@tsrx/react": "^0.2.63",
171
+ "@base-ui/react": "1.8.0",
172
+ "@tsrx/react": "^0.2.69",
167
173
  "esbuild": "^0.28.1",
168
174
  "lucide-react": "1.24.0",
169
175
  "radix-ui": "^1.6.4",
170
- "react": "^19.2.7",
171
- "react-dom": "^19.2.7",
176
+ "react": "19.2.7",
177
+ "react-dom": "19.2.7",
172
178
  "vitest": "^4.1.10",
173
- "octane": "0.2.0"
179
+ "octane": "0.2.7"
174
180
  },
175
181
  "scripts": {
176
182
  "test": "vitest run",
@@ -12,7 +12,7 @@
12
12
  import { Accordion as AccordionPrimitive } from '@octanejs/base-ui/accordion';
13
13
  import { ChevronDownIcon, ChevronUpIcon } from '@octanejs/lucide';
14
14
 
15
- import { cn } from '../../../lib/utils';
15
+ import { cn } from 'cn';
16
16
 
17
17
  export function Accordion({ className, ...props }: Record<string, any>) @{
18
18
  <AccordionPrimitive.Root
@@ -17,7 +17,7 @@
17
17
  // props.children channel.
18
18
  import { AlertDialog as AlertDialogPrimitive } from '@octanejs/base-ui/alert-dialog';
19
19
 
20
- import { cn } from '../../../lib/utils';
20
+ import { cn } from 'cn';
21
21
  import type { DivProps } from '../../../lib/types';
22
22
  import { Button } from './button.tsrx';
23
23
 
@@ -10,7 +10,7 @@
10
10
  // interleave, so every part uses the `@{ … }` shorthand.
11
11
  import { cva, type VariantProps } from 'class-variance-authority';
12
12
 
13
- import { cn } from '../../../lib/utils';
13
+ import { cn } from 'cn';
14
14
  import type { DivProps } from '../../../lib/types';
15
15
 
16
16
  export const alertVariants = cva(
@@ -5,7 +5,7 @@
5
5
  // `alert` was confirmed byte-identical between the two bases, which is why this is plausible —
6
6
  // but it is not proof, and the bases DO diverge elsewhere. Diff the class strings against the
7
7
  // upstream source before treating this as ported.
8
- import { cn } from '../../../lib/utils';
8
+ import { cn } from 'cn';
9
9
  import type { DivProps } from '../../../lib/types';
10
10
 
11
11
  export interface AspectRatioProps extends DivProps {
@@ -13,7 +13,7 @@
13
13
  // transcribed from upstream's Base UI source.
14
14
  import { Avatar as AvatarPrimitive } from '@octanejs/base-ui/avatar';
15
15
 
16
- import { cn } from '../../../lib/utils';
16
+ import { cn } from 'cn';
17
17
 
18
18
  type Props = { className?: string } & Record<string, unknown>;
19
19
 
@@ -18,7 +18,7 @@
18
18
  // spread `badgeVariants({ variant })` onto it directly, which is what the class export is for.
19
19
  import { cva, type VariantProps } from 'class-variance-authority';
20
20
 
21
- import { cn } from '../../../lib/utils';
21
+ import { cn } from 'cn';
22
22
  import type { HostProps } from '../../../lib/types';
23
23
 
24
24
  export const badgeVariants = cva(
@@ -26,7 +26,7 @@
26
26
  import { ChevronRightIcon, MoreHorizontalIcon } from '@octanejs/lucide';
27
27
  import { type OctaneNode } from 'octane';
28
28
 
29
- import { cn } from '../../../lib/utils';
29
+ import { cn } from 'cn';
30
30
 
31
31
  type Props = { className?: string } & Record<string, unknown>;
32
32
 
@@ -15,7 +15,7 @@
15
15
  import { cva, type VariantProps } from 'class-variance-authority';
16
16
  import { Button as ButtonPrimitive } from '@octanejs/base-ui/button';
17
17
 
18
- import { cn } from '../../../lib/utils';
18
+ import { cn } from 'cn';
19
19
 
20
20
  export const buttonVariants = cva(
21
21
  'group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
@@ -3,7 +3,7 @@
3
3
  // UNVERIFIED PROVENANCE: derived from this package's React Aria base rather than transcribed
4
4
  // from upstream's `bases/base-ui/ui/card.tsx`, which was not available at port time.
5
5
  // Diff the class strings against the upstream source before treating this as ported.
6
- import { cn } from '../../../lib/utils';
6
+ import { cn } from 'cn';
7
7
  import type { DivProps } from '../../../lib/types';
8
8
 
9
9
  export interface CardProps extends DivProps {
@@ -18,7 +18,7 @@
18
18
  import { Checkbox as CheckboxPrimitive } from '@octanejs/base-ui/checkbox';
19
19
  import { CheckIcon } from '@octanejs/lucide';
20
20
 
21
- import { cn } from '../../../lib/utils';
21
+ import { cn } from 'cn';
22
22
 
23
23
  export interface CheckboxProps extends Record<string, unknown> {
24
24
  className?: string;
@@ -26,25 +26,27 @@ import { type OctaneNode } from 'octane';
26
26
  import { ContextMenu as ContextMenuPrimitive } from '@octanejs/base-ui/context-menu';
27
27
  import { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';
28
28
 
29
- import { cn } from '../../../lib/utils';
29
+ import { cn } from 'cn';
30
30
 
31
31
  type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
32
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
- }
33
+ /** Props routed to the Positioner rather than the popup. */
34
+ export type BaseUiPositioningProps =
35
+ Pick<
36
+ ContextMenuPrimitive.Positioner.Props,
37
+ | 'side'
38
+ | 'sideOffset'
39
+ | 'align'
40
+ | 'alignOffset'
41
+ | 'anchor'
42
+ | 'positionMethod'
43
+ | 'collisionBoundary'
44
+ | 'collisionPadding'
45
+ | 'collisionAvoidance'
46
+ | 'arrowPadding'
47
+ | 'sticky'
48
+ | 'disableAnchorTracking'
49
+ >;
48
50
 
49
51
  export function ContextMenu(props: Record<string, unknown>) @{
50
52
  <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
@@ -221,7 +223,10 @@ export function ContextMenuCheckboxItem(props: Props & {
221
223
  </ContextMenuPrimitive.CheckboxItem>
222
224
  }
223
225
 
224
- export function ContextMenuRadioItem(props: Props & { inset?: boolean }) @{
226
+ export function ContextMenuRadioItem(props: Omit<
227
+ ContextMenuPrimitive.RadioItem.Props,
228
+ 'className'
229
+ > & { className?: string; inset?: boolean }) @{
225
230
  const { className, children: _children, inset, ...rest } = props;
226
231
 
227
232
  <ContextMenuPrimitive.RadioItem
@@ -10,11 +10,11 @@
10
10
  //
11
11
  // UNVERIFIED PROVENANCE: class strings come from this package's radix base rather than
12
12
  // transcribed from upstream's Base UI source.
13
- import { createElement, type OctaneNode } from 'octane';
13
+ import { type OctaneNode } from 'octane';
14
14
  import { Dialog as DialogPrimitive } from '@octanejs/base-ui/dialog';
15
15
  import { XIcon } from '@octanejs/lucide';
16
16
 
17
- import { cn } from '../../../lib/utils';
17
+ import { cn } from 'cn';
18
18
  import { Button } from './button.tsrx';
19
19
 
20
20
  type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
@@ -46,7 +46,8 @@ export function DialogOverlay({ className, ...props }: Props) @{
46
46
  />
47
47
  }
48
48
 
49
- export interface DialogContentProps extends Props {
49
+ export interface DialogContentProps extends Omit<DialogPrimitive.Popup.Props, 'className'> {
50
+ className?: string;
50
51
  showCloseButton?: boolean;
51
52
  }
52
53
 
@@ -55,38 +56,30 @@ export function DialogContent({
55
56
  children,
56
57
  showCloseButton = true,
57
58
  ...props
58
- }: DialogContentProps) {
59
- return createElement(
60
- DialogPrimitive.Portal,
61
- { 'data-slot': 'dialog-portal' },
62
- createElement(DialogOverlay, { key: 'overlay' }),
63
- createElement(
64
- DialogPrimitive.Popup,
65
- {
66
- key: 'content',
67
- 'data-slot': 'dialog-content',
68
- className: cn(
69
- 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm 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',
70
- className,
71
- ),
72
- ...props,
73
- },
74
- children,
75
- showCloseButton
76
- ? createElement(DialogPrimitive.Close, {
77
- key: 'close',
78
- 'data-slot': 'dialog-close',
79
- className: 'absolute top-2 right-2',
80
- render: createElement(
81
- Button,
82
- { variant: 'ghost', size: 'icon-sm' },
83
- createElement(XIcon, { key: 'icon' }),
84
- createElement('span', { key: 'sr', className: 'sr-only' }, 'Close'),
85
- ),
86
- })
87
- : null,
88
- ),
89
- );
59
+ }: DialogContentProps) @{
60
+ <DialogPrimitive.Portal data-slot="dialog-portal">
61
+ <DialogOverlay />
62
+ <DialogPrimitive.Popup
63
+ data-slot="dialog-content"
64
+ className={cn(
65
+ 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm 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',
66
+ className,
67
+ )}
68
+ {...props}
69
+ >
70
+ {children}
71
+ @if (showCloseButton) {
72
+ <DialogPrimitive.Close
73
+ data-slot="dialog-close"
74
+ className="absolute top-2 right-2"
75
+ render={<Button variant="ghost" size="icon-sm">
76
+ <XIcon />
77
+ <span className="sr-only">Close</span>
78
+ </Button>}
79
+ />
80
+ }
81
+ </DialogPrimitive.Popup>
82
+ </DialogPrimitive.Portal>
90
83
  }
91
84
 
92
85
  export function DialogHeader({ className, ...props }: Props) @{
@@ -109,11 +102,9 @@ export function DialogFooter(props: DialogFooterProps) @{
109
102
  {...rest}
110
103
  >
111
104
  {props.children}
112
- {showCloseButton
113
- ? createElement(DialogPrimitive.Close, {
114
- render: createElement(Button, { variant: 'outline' }, 'Close'),
115
- })
116
- : null}
105
+ @if (showCloseButton) {
106
+ <DialogPrimitive.Close render={<Button variant="outline">Close</Button>} />
107
+ }
117
108
  </div>
118
109
  }
119
110
 
@@ -41,7 +41,7 @@ import { type OctaneNode } from 'octane';
41
41
  import { Menu as MenuPrimitive } from '@octanejs/base-ui/menu';
42
42
  import { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';
43
43
 
44
- import { cn } from '../../../lib/utils';
44
+ import { cn } from 'cn';
45
45
 
46
46
  type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
47
47
 
@@ -57,21 +57,23 @@ export function DropdownMenuTrigger(props: Record<string, unknown>) @{
57
57
  <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
58
58
  }
59
59
 
60
- export interface BaseUiPositioningProps {
61
- /** Forwarded to the Positioner; see the note at the top of this file. */
62
- side?: 'top' | 'right' | 'bottom' | 'left';
63
- sideOffset?: number;
64
- align?: 'start' | 'center' | 'end';
65
- alignOffset?: number;
66
- anchor?: unknown;
67
- positionMethod?: 'absolute' | 'fixed';
68
- collisionBoundary?: unknown;
69
- collisionPadding?: unknown;
70
- collisionAvoidance?: unknown;
71
- arrowPadding?: number;
72
- sticky?: boolean;
73
- disableAnchorTracking?: boolean;
74
- }
60
+ /** Props routed to the Positioner rather than the popup. */
61
+ export type BaseUiPositioningProps =
62
+ Pick<
63
+ MenuPrimitive.Positioner.Props,
64
+ | 'side'
65
+ | 'sideOffset'
66
+ | 'align'
67
+ | 'alignOffset'
68
+ | 'anchor'
69
+ | 'positionMethod'
70
+ | 'collisionBoundary'
71
+ | 'collisionPadding'
72
+ | 'collisionAvoidance'
73
+ | 'arrowPadding'
74
+ | 'sticky'
75
+ | 'disableAnchorTracking'
76
+ >;
75
77
 
76
78
  export interface DropdownMenuContentProps extends Props, BaseUiPositioningProps {}
77
79
 
@@ -170,7 +172,10 @@ export function DropdownMenuRadioGroup(props: Record<string, unknown>) @{
170
172
  <MenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
171
173
  }
172
174
 
173
- export function DropdownMenuRadioItem(props: Props & { inset?: boolean }) @{
175
+ export function DropdownMenuRadioItem(props: Omit<MenuPrimitive.RadioItem.Props, 'className'> & {
176
+ className?: string;
177
+ inset?: boolean;
178
+ }) @{
174
179
  const { className, children: _children, inset, ...rest } = props;
175
180
 
176
181
  <MenuPrimitive.RadioItem
@@ -5,7 +5,7 @@
5
5
  // Diff the class strings against the upstream source before treating this as ported.
6
6
  import { cva, type VariantProps } from 'class-variance-authority';
7
7
 
8
- import { cn } from '../../../lib/utils';
8
+ import { cn } from 'cn';
9
9
  import type { DivProps, HostProps } from '../../../lib/types';
10
10
 
11
11
  export function Empty({ className, ...props }: DivProps) @{
@@ -19,7 +19,7 @@
19
19
  import { createElement, useMemo, type OctaneNode } from 'octane';
20
20
  import { cva, type VariantProps } from 'class-variance-authority';
21
21
 
22
- import { cn } from '../../../lib/utils';
22
+ import { cn } from 'cn';
23
23
  import { Label } from './label.tsrx';
24
24
  import { Separator } from './separator.tsrx';
25
25
 
@@ -26,23 +26,25 @@
26
26
  import { createElement } from 'octane';
27
27
  import { PreviewCard as PreviewCardPrimitive } from '@octanejs/base-ui/preview-card';
28
28
 
29
- import { cn } from '../../../lib/utils';
29
+ import { cn } from 'cn';
30
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
- }
31
+ /** Props routed to the Positioner rather than the popup. */
32
+ export type BaseUiPositioningProps =
33
+ Pick<
34
+ PreviewCardPrimitive.Positioner.Props,
35
+ | 'side'
36
+ | 'sideOffset'
37
+ | 'align'
38
+ | 'alignOffset'
39
+ | 'anchor'
40
+ | 'positionMethod'
41
+ | 'collisionBoundary'
42
+ | 'collisionPadding'
43
+ | 'collisionAvoidance'
44
+ | 'arrowPadding'
45
+ | 'sticky'
46
+ | 'disableAnchorTracking'
47
+ >;
46
48
 
47
49
  export function HoverCard(props: Record<string, unknown>) @{
48
50
  <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
@@ -13,7 +13,7 @@
13
13
  // keeps its commit-on-blur meaning.
14
14
  import { Input as InputPrimitive } from '@octanejs/base-ui/input';
15
15
 
16
- import { cn } from '../../../lib/utils';
16
+ import { cn } from 'cn';
17
17
 
18
18
  export function Input({ className, type, ...props }: Record<string, any>) @{
19
19
  <InputPrimitive
@@ -13,7 +13,7 @@
13
13
  // apply `itemVariants({ variant, size })` directly, which is what the class export is for.
14
14
  import { cva, type VariantProps } from 'class-variance-authority';
15
15
 
16
- import { cn } from '../../../lib/utils';
16
+ import { cn } from 'cn';
17
17
  import { Separator } from './separator.tsrx';
18
18
 
19
19
  type DivProps = { className?: string } & Record<string, unknown>;
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // UNVERIFIED PROVENANCE: class strings taken from this package's React Aria base rather than
6
6
  // transcribed from upstream's Base UI source.
7
- import { cn } from '../../../lib/utils';
7
+ import { cn } from 'cn';
8
8
  import type { HostProps } from '../../../lib/types';
9
9
 
10
10
  export function Kbd({ className, ...props }: HostProps<'kbd'>) @{
@@ -16,7 +16,7 @@
16
16
  // UNVERIFIED PROVENANCE: the class string is taken from this package's React Aria base rather
17
17
  // than transcribed from upstream's Base UI source. It carries one utility the Radix base omits
18
18
  // (`peer-data-disabled:opacity-50`).
19
- import { cn } from '../../../lib/utils';
19
+ import { cn } from 'cn';
20
20
  import type { HostProps } from '../../../lib/types';
21
21
 
22
22
  export function Label({ className, ...props }: HostProps<'label'>) @{