@octanejs/shadcn 0.0.7 → 0.0.9

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 (37) hide show
  1. package/README.md +170 -3
  2. package/package.json +46 -7
  3. package/src/bases/base-ui/ui/accordion.tsrx +71 -0
  4. package/src/bases/base-ui/ui/alert-dialog.tsrx +149 -0
  5. package/src/bases/base-ui/ui/alert.tsrx +68 -0
  6. package/src/bases/base-ui/ui/aspect-ratio.tsrx +24 -0
  7. package/src/bases/base-ui/ui/avatar.tsrx +89 -0
  8. package/src/bases/base-ui/ui/badge.tsrx +56 -0
  9. package/src/bases/base-ui/ui/breadcrumb.tsrx +109 -0
  10. package/src/bases/base-ui/ui/button.tsrx +69 -0
  11. package/src/bases/base-ui/ui/card.tsrx +76 -0
  12. package/src/bases/base-ui/ui/checkbox.tsrx +43 -0
  13. package/src/bases/base-ui/ui/collapsible.tsrx +25 -0
  14. package/src/bases/base-ui/ui/dialog.tsrx +137 -0
  15. package/src/bases/base-ui/ui/dropdown-menu.tsrx +292 -0
  16. package/src/bases/base-ui/ui/empty.tsrx +88 -0
  17. package/src/bases/base-ui/ui/field.tsrx +201 -0
  18. package/src/bases/base-ui/ui/input.tsrx +28 -0
  19. package/src/bases/base-ui/ui/item.tsrx +163 -0
  20. package/src/bases/base-ui/ui/kbd.tsrx +27 -0
  21. package/src/bases/base-ui/ui/label.tsrx +31 -0
  22. package/src/bases/base-ui/ui/native-select.tsrx +54 -0
  23. package/src/bases/base-ui/ui/pagination.tsrx +125 -0
  24. package/src/bases/base-ui/ui/popover.tsrx +124 -0
  25. package/src/bases/base-ui/ui/progress.tsrx +46 -0
  26. package/src/bases/base-ui/ui/radio-group.tsrx +62 -0
  27. package/src/bases/base-ui/ui/separator.tsrx +34 -0
  28. package/src/bases/base-ui/ui/sheet.tsrx +121 -0
  29. package/src/bases/base-ui/ui/skeleton.tsrx +17 -0
  30. package/src/bases/base-ui/ui/slider.tsrx +111 -0
  31. package/src/bases/base-ui/ui/spinner.tsrx +30 -0
  32. package/src/bases/base-ui/ui/switch.tsrx +43 -0
  33. package/src/bases/base-ui/ui/table.tsrx +85 -0
  34. package/src/bases/base-ui/ui/textarea.tsrx +22 -0
  35. package/src/bases/base-ui/ui/toggle-group.tsrx +124 -0
  36. package/src/bases/base-ui/ui/toggle.tsrx +57 -0
  37. package/src/bases/base-ui/ui/tooltip.tsrx +111 -0
package/README.md CHANGED
@@ -13,20 +13,187 @@ Upstream pin: `shadcn-ui/ui@4baadbc6` + CLI `shadcn@4.14.1`.
13
13
  radix-backed Tier-2 components, and the first Tier-3 composites (Sidebar with
14
14
  `useSidebar`/`useIsMobile`, Field), plus `cn()` and the default neutral theme
15
15
  tokens (`@octanejs/shadcn/theme.css`).
16
- - **Two consumption modes**: import from the package entry, or install
16
+ - **Two consumption modes**: import from the package subpaths, or install
17
17
  components AS SOURCE with the upstream shadcn CLI against the generated
18
- registry (`registry/`, 47 items — `pnpm registry:build` / `registry:check`).
18
+ registry (`registry/` — `pnpm registry:build` / `registry:check`).
19
+ - **Three primitive bases in one registry**, selected the way shadcn selects its
20
+ own: `components.json`'s `style` field, substituted into the registry URL. See
21
+ [Installing with the shadcn CLI](#installing-with-the-shadcn-cli).
19
22
  - **Styling flavors**: components are migrating to the default-Tailwind
20
23
  (utilities-inlined) flavor — class strings work against any Tailwind v4 build
21
24
  with the shadcn theme tokens. The not-yet-migrated families still carry the
22
25
  upstream `cn-*` semantic hooks and need a shadcn style sheet (e.g.
23
26
  `style-nova`). `status.json` lists exactly which families are which.
24
27
 
28
+ ## Component coverage
29
+
30
+ Each primitive base ships the families its primitives support, exactly as upstream's do.
31
+ Switching `style` changes which primitive a component is built on — the `data-slot` contract and
32
+ component names stay identical.
33
+
34
+ <!-- BEGIN COVERAGE -->
35
+
36
+ **44 families** — Radix 44/44 · React Aria 33/44 · Base UI 35/44
37
+
38
+ ✅ ported · — not ported yet (fair game) · ⛔ blocked, see notes below
39
+
40
+ | Family | Radix | React Aria | Base UI |
41
+ | --- | :---: | :---: | :---: |
42
+ | `accordion` | ✅ | ✅ | ✅ |
43
+ | `alert` | ✅ | ✅ | ✅ |
44
+ | `alert-dialog` | ✅ | ✅ | ✅ |
45
+ | `aspect-ratio` | ✅ | ✅ | ✅ |
46
+ | `avatar` | ✅ | ✅ | ✅ |
47
+ | `badge` | ✅ | ✅ | ✅ |
48
+ | `breadcrumb` | ✅ | ✅ | ✅ |
49
+ | `button` | ✅ | ✅ | ✅ |
50
+ | `card` | ✅ | ✅ | ✅ |
51
+ | `checkbox` | ✅ | ✅ | ✅ |
52
+ | `collapsible` | ✅ | ✅ | ✅ |
53
+ | `context-menu` | ✅ | ⛔ | — |
54
+ | `dialog` | ✅ | ✅ | ✅ |
55
+ | `dropdown-menu` | ✅ | ⛔ | ✅ |
56
+ | `empty` | ✅ | ✅ | ✅ |
57
+ | `field` | ✅ | ✅ | ✅ |
58
+ | `hover-card` | ✅ | ⛔ | — |
59
+ | `input` | ✅ | ✅ | ✅ |
60
+ | `item` | ✅ | ✅ | ✅ |
61
+ | `kbd` | ✅ | ✅ | ✅ |
62
+ | `label` | ✅ | ✅ | ✅ |
63
+ | `menubar` | ✅ | ⛔ | — |
64
+ | `native-select` | ✅ | ✅ | ✅ |
65
+ | `navigation-menu` | ✅ | ⛔ | ⛔ |
66
+ | `pagination` | ✅ | ✅ | ✅ |
67
+ | `popover` | ✅ | ✅ | ✅ |
68
+ | `progress` | ✅ | ⛔ | ✅ |
69
+ | `radio-group` | ✅ | ✅ | ✅ |
70
+ | `scroll-area` | ✅ | ✅ | ⛔ |
71
+ | `select` | ✅ | ⛔ | ⛔ |
72
+ | `separator` | ✅ | ✅ | ✅ |
73
+ | `sheet` | ✅ | ✅ | ✅ |
74
+ | `sidebar` | ✅ | ⛔ | — |
75
+ | `skeleton` | ✅ | ✅ | ✅ |
76
+ | `slider` | ✅ | ✅ | ✅ |
77
+ | `sonner` | ✅ | ⛔ | ⛔ |
78
+ | `spinner` | ✅ | ✅ | ✅ |
79
+ | `switch` | ✅ | ✅ | ✅ |
80
+ | `table` | ✅ | ✅ | ✅ |
81
+ | `tabs` | ✅ | ✅ | ⛔ |
82
+ | `textarea` | ✅ | ✅ | ✅ |
83
+ | `toggle` | ✅ | ✅ | ✅ |
84
+ | `toggle-group` | ✅ | ⛔ | ✅ |
85
+ | `tooltip` | ✅ | ⛔ | ✅ |
86
+
87
+ **Blocked**
88
+
89
+ | Base | Family | Reason |
90
+ | --- | --- | --- |
91
+ | Base UI | `navigation-menu` | @octanejs/base-ui has no navigation-menu primitive |
92
+ | Base UI | `scroll-area` | @octanejs/base-ui has no scroll-area primitive |
93
+ | Base UI | `select` | @octanejs/base-ui has no select primitive |
94
+ | Base UI | `sonner` | needs next-themes, which has no octane binding |
95
+ | Base UI | `tabs` | @octanejs/base-ui has no tabs primitive |
96
+ | React Aria | `context-menu` | no counterpart in upstream’s aria base |
97
+ | React Aria | `dropdown-menu` | no counterpart in upstream’s aria base |
98
+ | React Aria | `hover-card` | no counterpart in upstream’s aria base |
99
+ | React Aria | `menubar` | no counterpart in upstream’s aria base |
100
+ | React Aria | `navigation-menu` | no counterpart in upstream’s aria base |
101
+ | React Aria | `progress` | no counterpart in upstream’s aria base |
102
+ | React Aria | `select` | needs input-group |
103
+ | React Aria | `sidebar` | no counterpart in upstream’s aria base |
104
+ | React Aria | `sonner` | needs next-themes, which has no octane binding |
105
+ | React Aria | `toggle-group` | no counterpart in upstream’s aria base |
106
+ | React Aria | `tooltip` | no counterpart in upstream’s aria base |
107
+
108
+ <!-- END COVERAGE -->
109
+
110
+ The table is generated from the sources by `pnpm coverage:build` and gated by `coverage:check`,
111
+ so it cannot drift as families land.
112
+
113
+ ## Installing with the shadcn CLI
114
+
115
+ The registry is consumed by the **upstream** `shadcn` CLI — nothing octane-specific is
116
+ required on the consumer side.
117
+
118
+ Add the registry to your `components.json` and pick a base with `style`:
119
+
120
+ ```jsonc
121
+ {
122
+ "style": "base-nova", // base-nova | radix-nova | aria-nova
123
+ "registries": {
124
+ "@octane": "https://octanejs.dev/r/styles/{style}/{name}.json"
125
+ }
126
+ }
127
+ ```
128
+
129
+ ```bash
130
+ npx shadcn add @octane/button
131
+ ```
132
+
133
+ `{style}` and `{name}` are the only placeholders the CLI substitutes, and it never parses the
134
+ style string — so the base is chosen entirely by which tree the registry serves. This is the
135
+ same mechanism shadcn uses upstream, where its own registry is
136
+ `<host>/styles/{style}/{name}.json`.
137
+
138
+ | `style` | Primitives | Families |
139
+ | --- | --- | --- |
140
+ | `base-nova` *(default)* | `@octanejs/base-ui` | 21 |
141
+ | `radix-nova` | `@octanejs/radix` | 44 |
142
+ | `aria-nova` | `@octanejs/aria` | 33 |
143
+
144
+ The bases deliberately ship different family counts, exactly as upstream's do — a base only
145
+ has the families its primitives support. Switching `style` changes which primitive a component
146
+ is built on; the `data-slot` contract and component names stay identical, so your markup and
147
+ styling do not change.
148
+
149
+ A registry URL without the `{style}` segment resolves to the default style, so
150
+ `https://octanejs.dev/r/{name}.json` serves the Base UI variant.
151
+
152
+ ### Serving it locally
153
+
154
+ ```bash
155
+ pnpm --dir packages/shadcn registry:build # regenerate from src/
156
+ pnpm --dir packages/shadcn registry:serve # http://localhost:4517
157
+ ```
158
+
159
+ Then point `components.json` at `http://localhost:4517/styles/{style}/{name}.json`, which is
160
+ what `playground/octane` does.
161
+
162
+ ## Importing from the package
163
+
164
+ There is no barrel entry. Every family has its own subpath, so an app pulls in only what it
165
+ uses — the Radix base at the bare name, the others under their base:
166
+
25
167
  ```tsx
26
- import { Button, Dialog, Tabs } from '@octanejs/shadcn';
168
+ // Radix base (the bare subpaths)
169
+ import { Button } from '@octanejs/shadcn/Button';
170
+ import { Dialog, DialogContent, DialogTitle } from '@octanejs/shadcn/Dialog';
171
+
172
+ // React Aria base
173
+ import { Button } from '@octanejs/shadcn/react-aria/Button';
174
+
175
+ // Base UI base
176
+ import {
177
+ AlertDialog,
178
+ AlertDialogAction,
179
+ AlertDialogCancel,
180
+ AlertDialogContent,
181
+ AlertDialogDescription,
182
+ AlertDialogFooter,
183
+ AlertDialogHeader,
184
+ AlertDialogMedia,
185
+ AlertDialogTitle,
186
+ AlertDialogTrigger,
187
+ } from '@octanejs/shadcn/base-ui/AlertDialog';
188
+
27
189
  import '@octanejs/shadcn/theme.css';
28
190
  ```
29
191
 
192
+ Subpaths are `PascalCase` and map to the family, not the export: `AlertDialogTrigger` and its
193
+ siblings all come from `.../AlertDialog`. `cn` lives at `@octanejs/shadcn/cn`, shared types at
194
+ `@octanejs/shadcn/types`, and `useIsMobile` at `@octanejs/shadcn/hooks/use-mobile`.
195
+
196
+
30
197
  Octane adaptations are uniform and documented per file: no `"use client"`,
31
198
  refs as props, native events (`onInput` per keystroke), `asChild` and Portal
32
199
  children composed as element descriptors, and children forwarding through the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/shadcn",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -104,6 +104,41 @@
104
104
  "./react-aria/Tabs": "./src/bases/react-aria/ui/tabs.tsrx",
105
105
  "./react-aria/Textarea": "./src/bases/react-aria/ui/textarea.tsrx",
106
106
  "./react-aria/Toggle": "./src/bases/react-aria/ui/toggle.tsrx",
107
+ "./base-ui/Accordion": "./src/bases/base-ui/ui/accordion.tsrx",
108
+ "./base-ui/Alert": "./src/bases/base-ui/ui/alert.tsrx",
109
+ "./base-ui/AlertDialog": "./src/bases/base-ui/ui/alert-dialog.tsrx",
110
+ "./base-ui/AspectRatio": "./src/bases/base-ui/ui/aspect-ratio.tsrx",
111
+ "./base-ui/Avatar": "./src/bases/base-ui/ui/avatar.tsrx",
112
+ "./base-ui/Badge": "./src/bases/base-ui/ui/badge.tsrx",
113
+ "./base-ui/Breadcrumb": "./src/bases/base-ui/ui/breadcrumb.tsrx",
114
+ "./base-ui/Button": "./src/bases/base-ui/ui/button.tsrx",
115
+ "./base-ui/Card": "./src/bases/base-ui/ui/card.tsrx",
116
+ "./base-ui/Checkbox": "./src/bases/base-ui/ui/checkbox.tsrx",
117
+ "./base-ui/Collapsible": "./src/bases/base-ui/ui/collapsible.tsrx",
118
+ "./base-ui/Dialog": "./src/bases/base-ui/ui/dialog.tsrx",
119
+ "./base-ui/DropdownMenu": "./src/bases/base-ui/ui/dropdown-menu.tsrx",
120
+ "./base-ui/Empty": "./src/bases/base-ui/ui/empty.tsrx",
121
+ "./base-ui/Field": "./src/bases/base-ui/ui/field.tsrx",
122
+ "./base-ui/Input": "./src/bases/base-ui/ui/input.tsrx",
123
+ "./base-ui/Item": "./src/bases/base-ui/ui/item.tsrx",
124
+ "./base-ui/Kbd": "./src/bases/base-ui/ui/kbd.tsrx",
125
+ "./base-ui/Label": "./src/bases/base-ui/ui/label.tsrx",
126
+ "./base-ui/NativeSelect": "./src/bases/base-ui/ui/native-select.tsrx",
127
+ "./base-ui/Pagination": "./src/bases/base-ui/ui/pagination.tsrx",
128
+ "./base-ui/Popover": "./src/bases/base-ui/ui/popover.tsrx",
129
+ "./base-ui/Progress": "./src/bases/base-ui/ui/progress.tsrx",
130
+ "./base-ui/RadioGroup": "./src/bases/base-ui/ui/radio-group.tsrx",
131
+ "./base-ui/Separator": "./src/bases/base-ui/ui/separator.tsrx",
132
+ "./base-ui/Sheet": "./src/bases/base-ui/ui/sheet.tsrx",
133
+ "./base-ui/Skeleton": "./src/bases/base-ui/ui/skeleton.tsrx",
134
+ "./base-ui/Slider": "./src/bases/base-ui/ui/slider.tsrx",
135
+ "./base-ui/Spinner": "./src/bases/base-ui/ui/spinner.tsrx",
136
+ "./base-ui/Switch": "./src/bases/base-ui/ui/switch.tsrx",
137
+ "./base-ui/Table": "./src/bases/base-ui/ui/table.tsrx",
138
+ "./base-ui/Textarea": "./src/bases/base-ui/ui/textarea.tsrx",
139
+ "./base-ui/Toggle": "./src/bases/base-ui/ui/toggle.tsrx",
140
+ "./base-ui/ToggleGroup": "./src/bases/base-ui/ui/toggle-group.tsrx",
141
+ "./base-ui/Tooltip": "./src/bases/base-ui/ui/tooltip.tsrx",
107
142
  "./cn": "./src/lib/utils.ts",
108
143
  "./types": "./src/lib/types.ts",
109
144
  "./hooks/use-mobile": "./src/hooks/use-mobile.ts",
@@ -113,13 +148,14 @@
113
148
  "class-variance-authority": "0.7.1",
114
149
  "clsx": "^2.1.1",
115
150
  "tailwind-merge": "3.6.0",
116
- "@octanejs/aria": "0.0.16",
117
- "@octanejs/lucide": "0.1.17",
118
- "@octanejs/radix": "0.1.21",
119
- "@octanejs/sonner": "0.1.17"
151
+ "@octanejs/aria": "0.0.18",
152
+ "@octanejs/base-ui": "0.1.22",
153
+ "@octanejs/lucide": "0.1.19",
154
+ "@octanejs/radix": "0.1.23",
155
+ "@octanejs/sonner": "0.1.19"
120
156
  },
121
157
  "peerDependencies": {
122
- "octane": "0.1.22"
158
+ "octane": "0.1.24"
123
159
  },
124
160
  "devDependencies": {
125
161
  "@tsrx/react": "^0.2.56",
@@ -129,12 +165,15 @@
129
165
  "react": "^19.2.7",
130
166
  "react-dom": "^19.2.7",
131
167
  "vitest": "^4.1.10",
132
- "octane": "0.1.22"
168
+ "octane": "0.1.24"
133
169
  },
134
170
  "scripts": {
135
171
  "test": "vitest run",
136
172
  "registry:build": "node scripts/build-registry.mjs",
137
173
  "registry:check": "node scripts/build-registry.mjs --check",
174
+ "registry:serve": "node scripts/serve-registry.mjs",
175
+ "coverage:build": "node scripts/build-coverage-table.mjs",
176
+ "coverage:check": "node scripts/build-coverage-table.mjs --check",
138
177
  "typecheck": "node scripts/typecheck.mjs"
139
178
  }
140
179
  }
@@ -0,0 +1,71 @@
1
+ // Base UI base accordion — transcribed from upstream's `bases/base-ui/ui/accordion.tsx`
2
+ // (maintainer-supplied), class strings and data-slot names verbatim. Runs on
3
+ // @octanejs/base-ui/accordion.
4
+ //
5
+ // Note the part mapping: upstream shadcn names the content part `AccordionContent` while the
6
+ // underlying Base UI part is `Accordion.Panel`, and `AccordionTrigger` renders the
7
+ // `Accordion.Header` wrapper itself rather than exposing it. Both are upstream's shape, kept
8
+ // as-is so consumers following the shadcn docs get the components they expect.
9
+ //
10
+ // Octane adaptations: no `"use client"` (octane has no Server Components); upstream's
11
+ // IconPlaceholder resolves at port time to lucide via @octanejs/lucide.
12
+ import { Accordion as AccordionPrimitive } from '@octanejs/base-ui/accordion';
13
+ import { ChevronDownIcon, ChevronUpIcon } from '@octanejs/lucide';
14
+
15
+ import { cn } from '../../../lib/utils';
16
+
17
+ export function Accordion({ className, ...props }: Record<string, any>) @{
18
+ <AccordionPrimitive.Root
19
+ data-slot="accordion"
20
+ className={cn('flex w-full flex-col', className)}
21
+ {...props}
22
+ />
23
+ }
24
+
25
+ export function AccordionItem({ className, ...props }: Record<string, any>) @{
26
+ <AccordionPrimitive.Item
27
+ data-slot="accordion-item"
28
+ className={cn('not-last:border-b', className)}
29
+ {...props}
30
+ />
31
+ }
32
+
33
+ export function AccordionTrigger({ className, children, ...props }: Record<string, any>) @{
34
+ <AccordionPrimitive.Header className="flex">
35
+ <AccordionPrimitive.Trigger
36
+ data-slot="accordion-trigger"
37
+ className={cn(
38
+ 'group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground',
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <ChevronDownIcon
45
+ data-slot="accordion-trigger-icon"
46
+ className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
47
+ />
48
+ <ChevronUpIcon
49
+ data-slot="accordion-trigger-icon"
50
+ className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
51
+ />
52
+ </AccordionPrimitive.Trigger>
53
+ </AccordionPrimitive.Header>
54
+ }
55
+
56
+ // `className` styles the INNER div, not the panel — upstream keeps the panel's own class string
57
+ // fixed so the open/close animation hooks cannot be overridden away by a consumer class.
58
+ export function AccordionContent({ className, children, ...props }: Record<string, any>) @{
59
+ <AccordionPrimitive.Panel
60
+ data-slot="accordion-content"
61
+ className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
62
+ {...props}
63
+ >
64
+ <div
65
+ className={cn(
66
+ 'h-(--accordion-panel-height) pt-0 pb-2.5 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
67
+ className,
68
+ )}
69
+ >{children}</div>
70
+ </AccordionPrimitive.Panel>
71
+ }
@@ -0,0 +1,149 @@
1
+ // Base UI base alert-dialog — transcribed from upstream's `bases/base-ui/ui/alert-dialog.tsx`
2
+ // (maintainer-supplied), running on @octanejs/base-ui's AlertDialog. Upstream part mapping:
3
+ // Overlay -> Backdrop, Content -> Popup, Cancel -> Close. This port also composes Action via Close.
4
+ //
5
+ // TWO DELIBERATE DEPARTURES FROM THE SOURCE:
6
+ // - Upstream's AlertDialogTitle carries `cn-font-heading`, a semantic hook from the pinned cn-*
7
+ // system. This package ships the default-Tailwind utilities-inlined flavor instead, the
8
+ // react-aria base already drops this exact class for that reason, and nothing in the playground
9
+ // defines it (0 occurrences in the built CSS), so keeping it would ship a class that resolves
10
+ // to nothing. Every other class string is verbatim.
11
+ // - Upstream renders AlertDialogAction as a plain Button, so it does not dismiss the dialog.
12
+ // Composing it through Close preserves the public Button-like API and supplies the expected
13
+ // confirm-and-dismiss behavior shown by Base UI's own alert-dialog examples.
14
+ //
15
+ // Octane adaptations: no `"use client"`; refs are props. AlertDialogContent composes its
16
+ // Portal/Overlay/Popup tree internally, so consumer children still flow through the ordinary
17
+ // props.children channel.
18
+ import { AlertDialog as AlertDialogPrimitive } from '@octanejs/base-ui/alert-dialog';
19
+
20
+ import { cn } from '../../../lib/utils';
21
+ import type { DivProps } from '../../../lib/types';
22
+ import { Button } from './button.tsrx';
23
+
24
+ export function AlertDialog({ ...props }: Record<string, any>) @{
25
+ <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
26
+ }
27
+
28
+ export function AlertDialogTrigger({ ...props }: Record<string, any>) @{
29
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
30
+ }
31
+
32
+ export function AlertDialogPortal({ ...props }: Record<string, any>) @{
33
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
34
+ }
35
+
36
+ export function AlertDialogOverlay({ className, ...props }: Record<string, any>) @{
37
+ <AlertDialogPrimitive.Backdrop
38
+ data-slot="alert-dialog-overlay"
39
+ className={cn(
40
+ 'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
41
+ className,
42
+ )}
43
+ {...props}
44
+ />
45
+ }
46
+
47
+ export function AlertDialogContent({ className, size = 'default', ...props }: Record<
48
+ string,
49
+ any
50
+ >) @{
51
+ <AlertDialogPortal>
52
+ <AlertDialogOverlay key="overlay" />
53
+ <AlertDialogPrimitive.Popup
54
+ key="content"
55
+ data-slot="alert-dialog-content"
56
+ data-size={size}
57
+ className={cn(
58
+ 'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]: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',
59
+ className,
60
+ )}
61
+ {...props}
62
+ />
63
+ </AlertDialogPortal>
64
+ }
65
+
66
+ export function AlertDialogHeader({ className, ...props }: DivProps) @{
67
+ <div
68
+ data-slot="alert-dialog-header"
69
+ className={cn(
70
+ 'grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]',
71
+ className,
72
+ )}
73
+ {...props}
74
+ />
75
+ }
76
+
77
+ export function AlertDialogFooter({ className, ...props }: DivProps) @{
78
+ <div
79
+ data-slot="alert-dialog-footer"
80
+ className={cn(
81
+ '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',
82
+ className,
83
+ )}
84
+ {...props}
85
+ />
86
+ }
87
+
88
+ export function AlertDialogMedia({ className, ...props }: DivProps) @{
89
+ <div
90
+ data-slot="alert-dialog-media"
91
+ className={cn(
92
+ 'mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*=\'size-\'])]:size-6',
93
+ className,
94
+ )}
95
+ {...props}
96
+ />
97
+ }
98
+
99
+ export function AlertDialogTitle({ className, ...props }: Record<string, any>) @{
100
+ <AlertDialogPrimitive.Title
101
+ data-slot="alert-dialog-title"
102
+ className={cn(
103
+ 'text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2',
104
+ className,
105
+ )}
106
+ {...props}
107
+ />
108
+ }
109
+
110
+ export function AlertDialogDescription({ className, ...props }: Record<string, any>) @{
111
+ <AlertDialogPrimitive.Description
112
+ data-slot="alert-dialog-description"
113
+ className={cn(
114
+ 'text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
115
+ className,
116
+ )}
117
+ {...props}
118
+ />
119
+ }
120
+
121
+ export function AlertDialogAction({
122
+ className,
123
+ variant = 'default',
124
+ size = 'default',
125
+ ...props
126
+ }: Record<string, any>) @{
127
+ <AlertDialogPrimitive.Close
128
+ data-slot="alert-dialog-action"
129
+ className={cn(className)}
130
+ render={<Button variant={variant} size={size} />}
131
+ {...props}
132
+ />
133
+ }
134
+
135
+ // `render` takes an ELEMENT here, not a function — that is Base UI's render-prop contract, and
136
+ // it is what lets each action keep Close's dismiss behavior while looking like a Button.
137
+ export function AlertDialogCancel({
138
+ className,
139
+ variant = 'outline',
140
+ size = 'default',
141
+ ...props
142
+ }: Record<string, any>) @{
143
+ <AlertDialogPrimitive.Close
144
+ data-slot="alert-dialog-cancel"
145
+ className={cn(className)}
146
+ render={<Button variant={variant} size={size} />}
147
+ {...props}
148
+ />
149
+ }
@@ -0,0 +1,68 @@
1
+ // Base UI base alert — structure and cva strings from upstream `bases/base-ui/ui/alert.tsx`.
2
+ //
3
+ // Alert uses no headless primitive in any base, and this file's nine string literals were
4
+ // verified byte-identical to the React Aria base's `alert.tsrx`. That is a finding, not an
5
+ // assumption: the two bases DO diverge elsewhere (the Radix base's Skeleton is `bg-accent`
6
+ // where aria's is `bg-muted`), so identity is checked per component rather than inherited.
7
+ //
8
+ // Octane adaptations: `React.ComponentProps<'div'>` → the package's `DivProps`; no
9
+ // `"use client"` (octane has no Server Components). Pure template JSX, no consumer-children
10
+ // interleave, so every part uses the `@{ … }` shorthand.
11
+ import { cva, type VariantProps } from 'class-variance-authority';
12
+
13
+ import { cn } from '../../../lib/utils';
14
+ import type { DivProps } from '../../../lib/types';
15
+
16
+ export const alertVariants = cva(
17
+ 'group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*=\'size-\'])]:size-4',
18
+ {
19
+ variants: {
20
+ variant: {
21
+ default: 'bg-card text-card-foreground',
22
+ destructive: 'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current',
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: 'default',
27
+ },
28
+ },
29
+ );
30
+
31
+ export interface AlertProps extends DivProps {
32
+ variant?: VariantProps<typeof alertVariants>['variant'];
33
+ }
34
+
35
+ export function Alert({ className, variant, ...props }: AlertProps) @{
36
+ <div
37
+ data-slot="alert"
38
+ role="alert"
39
+ className={cn(alertVariants({ variant }), className)}
40
+ {...props}
41
+ />
42
+ }
43
+
44
+ export function AlertTitle({ className, ...props }: DivProps) @{
45
+ <div
46
+ data-slot="alert-title"
47
+ className={cn(
48
+ 'font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground',
49
+ className,
50
+ )}
51
+ {...props}
52
+ />
53
+ }
54
+
55
+ export function AlertDescription({ className, ...props }: DivProps) @{
56
+ <div
57
+ data-slot="alert-description"
58
+ className={cn(
59
+ 'text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
60
+ className,
61
+ )}
62
+ {...props}
63
+ />
64
+ }
65
+
66
+ export function AlertAction({ className, ...props }: DivProps) @{
67
+ <div data-slot="alert-action" className={cn('absolute top-2 right-2', className)} {...props} />
68
+ }
@@ -0,0 +1,24 @@
1
+ // Base UI base aspect-ratio — presentational, no headless primitive in any base.
2
+ //
3
+ // UNVERIFIED PROVENANCE: derived from this package's React Aria base rather than transcribed
4
+ // from upstream's `bases/base-ui/ui/aspect-ratio.tsx`, which was not available at port time.
5
+ // `alert` was confirmed byte-identical between the two bases, which is why this is plausible —
6
+ // but it is not proof, and the bases DO diverge elsewhere. Diff the class strings against the
7
+ // upstream source before treating this as ported.
8
+ import { cn } from '../../../lib/utils';
9
+ import type { DivProps } from '../../../lib/types';
10
+
11
+ export interface AspectRatioProps extends DivProps {
12
+ ratio: number;
13
+ }
14
+
15
+ export function AspectRatio({ ratio, className, ...props }: AspectRatioProps) @{
16
+ <div
17
+ data-slot="aspect-ratio"
18
+ style={{
19
+ '--ratio': ratio,
20
+ } as Record<string, string | number>}
21
+ className={cn('relative aspect-(--ratio)', className)}
22
+ {...props}
23
+ />
24
+ }