@octanejs/shadcn 0.0.7 → 0.0.8
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/package.json +29 -7
- package/src/bases/base-ui/ui/accordion.tsrx +71 -0
- package/src/bases/base-ui/ui/alert-dialog.tsrx +149 -0
- package/src/bases/base-ui/ui/alert.tsrx +68 -0
- package/src/bases/base-ui/ui/aspect-ratio.tsrx +24 -0
- package/src/bases/base-ui/ui/button.tsrx +69 -0
- package/src/bases/base-ui/ui/card.tsrx +76 -0
- package/src/bases/base-ui/ui/checkbox.tsrx +43 -0
- package/src/bases/base-ui/ui/dialog.tsrx +137 -0
- package/src/bases/base-ui/ui/empty.tsrx +88 -0
- package/src/bases/base-ui/ui/input.tsrx +28 -0
- package/src/bases/base-ui/ui/kbd.tsrx +27 -0
- package/src/bases/base-ui/ui/label.tsrx +31 -0
- package/src/bases/base-ui/ui/native-select.tsrx +54 -0
- package/src/bases/base-ui/ui/popover.tsrx +85 -0
- package/src/bases/base-ui/ui/radio-group.tsrx +50 -0
- package/src/bases/base-ui/ui/separator.tsrx +34 -0
- package/src/bases/base-ui/ui/skeleton.tsrx +17 -0
- package/src/bases/base-ui/ui/spinner.tsrx +30 -0
- package/src/bases/base-ui/ui/switch.tsrx +43 -0
- package/src/bases/base-ui/ui/textarea.tsrx +22 -0
- package/src/bases/base-ui/ui/tooltip.tsrx +73 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/shadcn",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -104,6 +104,27 @@
|
|
|
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/Button": "./src/bases/base-ui/ui/button.tsrx",
|
|
112
|
+
"./base-ui/Card": "./src/bases/base-ui/ui/card.tsrx",
|
|
113
|
+
"./base-ui/Checkbox": "./src/bases/base-ui/ui/checkbox.tsrx",
|
|
114
|
+
"./base-ui/Dialog": "./src/bases/base-ui/ui/dialog.tsrx",
|
|
115
|
+
"./base-ui/Empty": "./src/bases/base-ui/ui/empty.tsrx",
|
|
116
|
+
"./base-ui/Input": "./src/bases/base-ui/ui/input.tsrx",
|
|
117
|
+
"./base-ui/Kbd": "./src/bases/base-ui/ui/kbd.tsrx",
|
|
118
|
+
"./base-ui/Label": "./src/bases/base-ui/ui/label.tsrx",
|
|
119
|
+
"./base-ui/NativeSelect": "./src/bases/base-ui/ui/native-select.tsrx",
|
|
120
|
+
"./base-ui/Popover": "./src/bases/base-ui/ui/popover.tsrx",
|
|
121
|
+
"./base-ui/RadioGroup": "./src/bases/base-ui/ui/radio-group.tsrx",
|
|
122
|
+
"./base-ui/Separator": "./src/bases/base-ui/ui/separator.tsrx",
|
|
123
|
+
"./base-ui/Skeleton": "./src/bases/base-ui/ui/skeleton.tsrx",
|
|
124
|
+
"./base-ui/Spinner": "./src/bases/base-ui/ui/spinner.tsrx",
|
|
125
|
+
"./base-ui/Switch": "./src/bases/base-ui/ui/switch.tsrx",
|
|
126
|
+
"./base-ui/Textarea": "./src/bases/base-ui/ui/textarea.tsrx",
|
|
127
|
+
"./base-ui/Tooltip": "./src/bases/base-ui/ui/tooltip.tsrx",
|
|
107
128
|
"./cn": "./src/lib/utils.ts",
|
|
108
129
|
"./types": "./src/lib/types.ts",
|
|
109
130
|
"./hooks/use-mobile": "./src/hooks/use-mobile.ts",
|
|
@@ -113,13 +134,14 @@
|
|
|
113
134
|
"class-variance-authority": "0.7.1",
|
|
114
135
|
"clsx": "^2.1.1",
|
|
115
136
|
"tailwind-merge": "3.6.0",
|
|
116
|
-
"@octanejs/aria": "0.0.
|
|
117
|
-
"@octanejs/
|
|
118
|
-
"@octanejs/
|
|
119
|
-
"@octanejs/
|
|
137
|
+
"@octanejs/aria": "0.0.17",
|
|
138
|
+
"@octanejs/base-ui": "0.1.21",
|
|
139
|
+
"@octanejs/lucide": "0.1.18",
|
|
140
|
+
"@octanejs/radix": "0.1.22",
|
|
141
|
+
"@octanejs/sonner": "0.1.18"
|
|
120
142
|
},
|
|
121
143
|
"peerDependencies": {
|
|
122
|
-
"octane": "0.1.
|
|
144
|
+
"octane": "0.1.23"
|
|
123
145
|
},
|
|
124
146
|
"devDependencies": {
|
|
125
147
|
"@tsrx/react": "^0.2.56",
|
|
@@ -129,7 +151,7 @@
|
|
|
129
151
|
"react": "^19.2.7",
|
|
130
152
|
"react-dom": "^19.2.7",
|
|
131
153
|
"vitest": "^4.1.10",
|
|
132
|
-
"octane": "0.1.
|
|
154
|
+
"octane": "0.1.23"
|
|
133
155
|
},
|
|
134
156
|
"scripts": {
|
|
135
157
|
"test": "vitest run",
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Base UI base button — runs on the @octanejs/base-ui `Button` primitive.
|
|
2
|
+
//
|
|
3
|
+
// TWO THINGS HERE ARE RECORDED FACTS, not guesses, both from the React Aria base's own
|
|
4
|
+
// provenance note:
|
|
5
|
+
// 1. Upstream's bases SHARE button's cva strings exactly, so the class strings below are the
|
|
6
|
+
// package's shipped utilities-inlined flavor and match the other bases.
|
|
7
|
+
// 2. The Base UI base OMITS the `data-variant`/`data-size` attributes the aria base sets. They
|
|
8
|
+
// are deliberately absent rather than forgotten — no selector in these strings keys off
|
|
9
|
+
// them, so nothing styles differently for their absence.
|
|
10
|
+
//
|
|
11
|
+
// NO LinkButton. The aria base has one because RAC ships a `Link` primitive slotted as a button;
|
|
12
|
+
// Base UI has no such primitive, and upstream's Base UI base composes links through the `render`
|
|
13
|
+
// prop instead. Inventing one here would be an API this base does not have — `pagination`, which
|
|
14
|
+
// consumes it in the aria base, therefore stays unported until the upstream source says how.
|
|
15
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
16
|
+
import { Button as ButtonPrimitive } from '@octanejs/base-ui/button';
|
|
17
|
+
|
|
18
|
+
import { cn } from '../../../lib/utils';
|
|
19
|
+
|
|
20
|
+
export const buttonVariants = cva(
|
|
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',
|
|
22
|
+
{
|
|
23
|
+
variants: {
|
|
24
|
+
variant: {
|
|
25
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/80',
|
|
26
|
+
outline: 'border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
|
|
27
|
+
secondary: 'bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
|
|
28
|
+
ghost: 'hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50',
|
|
29
|
+
destructive: 'bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40',
|
|
30
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
31
|
+
},
|
|
32
|
+
size: {
|
|
33
|
+
default: 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
|
|
34
|
+
xs: 'h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*=\'size-\'])]:size-3',
|
|
35
|
+
sm: 'h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*=\'size-\'])]:size-3.5',
|
|
36
|
+
lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
|
|
37
|
+
icon: 'size-8',
|
|
38
|
+
'icon-xs': 'size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*=\'size-\'])]:size-3',
|
|
39
|
+
'icon-sm': 'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
|
|
40
|
+
'icon-lg': 'size-9',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
variant: 'default',
|
|
45
|
+
size: 'default',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
type ButtonVariants = VariantProps<typeof buttonVariants>;
|
|
51
|
+
|
|
52
|
+
export interface ButtonProps extends Record<string, any> {
|
|
53
|
+
variant?: ButtonVariants['variant'];
|
|
54
|
+
size?: ButtonVariants['size'];
|
|
55
|
+
className?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function Button({
|
|
59
|
+
className,
|
|
60
|
+
variant = 'default',
|
|
61
|
+
size = 'default',
|
|
62
|
+
...props
|
|
63
|
+
}: ButtonProps) @{
|
|
64
|
+
<ButtonPrimitive
|
|
65
|
+
data-slot="button"
|
|
66
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Base UI base card — 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/card.tsx`, which was not available at port time.
|
|
5
|
+
// Diff the class strings against the upstream source before treating this as ported.
|
|
6
|
+
import { cn } from '../../../lib/utils';
|
|
7
|
+
import type { DivProps } from '../../../lib/types';
|
|
8
|
+
|
|
9
|
+
export interface CardProps extends DivProps {
|
|
10
|
+
size?: 'default' | 'sm';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Card({ className, size = 'default', ...props }: CardProps) @{
|
|
14
|
+
<div
|
|
15
|
+
data-slot="card"
|
|
16
|
+
data-size={size}
|
|
17
|
+
className={cn(
|
|
18
|
+
'group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl',
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function CardHeader({ className, ...props }: DivProps) @{
|
|
26
|
+
<div
|
|
27
|
+
data-slot="card-header"
|
|
28
|
+
className={cn(
|
|
29
|
+
'group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)',
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function CardTitle({ className, ...props }: DivProps) @{
|
|
37
|
+
<div
|
|
38
|
+
data-slot="card-title"
|
|
39
|
+
className={cn(
|
|
40
|
+
'text-base leading-snug font-medium group-data-[size=sm]/card:text-sm',
|
|
41
|
+
className,
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function CardDescription({ className, ...props }: DivProps) @{
|
|
48
|
+
<div
|
|
49
|
+
data-slot="card-description"
|
|
50
|
+
className={cn('text-sm text-muted-foreground', className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function CardAction({ className, ...props }: DivProps) @{
|
|
56
|
+
<div
|
|
57
|
+
data-slot="card-action"
|
|
58
|
+
className={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function CardContent({ className, ...props }: DivProps) @{
|
|
64
|
+
<div data-slot="card-content" className={cn('px-(--card-spacing)', className)} {...props} />
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function CardFooter({ className, ...props }: DivProps) @{
|
|
68
|
+
<div
|
|
69
|
+
data-slot="card-footer"
|
|
70
|
+
className={cn(
|
|
71
|
+
'flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)',
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Base UI base checkbox — runs on the @octanejs/base-ui `Checkbox` primitive.
|
|
2
|
+
//
|
|
3
|
+
// THE CONDITIONAL UTILITIES ARE ADAPTED, NOT COPIED, and the reason is structural rather than
|
|
4
|
+
// stylistic. Base UI's Checkbox.Root renders a `<span role="checkbox">` — verified by rendering
|
|
5
|
+
// it, not assumed — with a visually-hidden native `<input>` alongside it for form submission.
|
|
6
|
+
// A `<span>` is never `:disabled`, so Tailwind's `disabled:` variant (which compiles to the
|
|
7
|
+
// `:disabled` pseudo-class) can NEVER match. The primitive publishes `data-disabled` instead:
|
|
8
|
+
//
|
|
9
|
+
// radix base: `disabled:opacity-50` -> matches, Root is a real button
|
|
10
|
+
// base-ui base: `data-disabled:opacity-50` <- what this file uses
|
|
11
|
+
//
|
|
12
|
+
// `data-checked:` needs no change — the radix base already uses that exact form, and Base UI
|
|
13
|
+
// emits `data-checked`/`data-unchecked` too. `focus-visible:` also stays: the span carries
|
|
14
|
+
// tabindex, so the native pseudo-class applies.
|
|
15
|
+
//
|
|
16
|
+
// UNVERIFIED PROVENANCE: the non-conditional utilities are taken from this package's radix base
|
|
17
|
+
// rather than transcribed from upstream's Base UI source.
|
|
18
|
+
import { Checkbox as CheckboxPrimitive } from '@octanejs/base-ui/checkbox';
|
|
19
|
+
import { CheckIcon } from '@octanejs/lucide';
|
|
20
|
+
|
|
21
|
+
import { cn } from '../../../lib/utils';
|
|
22
|
+
|
|
23
|
+
export interface CheckboxProps extends Record<string, unknown> {
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function Checkbox({ className, ...props }: CheckboxProps) @{
|
|
28
|
+
<CheckboxPrimitive.Root
|
|
29
|
+
data-slot="checkbox"
|
|
30
|
+
className={cn(
|
|
31
|
+
'peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 data-disabled:cursor-not-allowed data-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary',
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
<CheckboxPrimitive.Indicator
|
|
37
|
+
data-slot="checkbox-indicator"
|
|
38
|
+
className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
|
|
39
|
+
>
|
|
40
|
+
<CheckIcon />
|
|
41
|
+
</CheckboxPrimitive.Indicator>
|
|
42
|
+
</CheckboxPrimitive.Root>
|
|
43
|
+
}
|