@octanejs/shadcn 0.0.6 → 0.0.7
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 +87 -10
- package/src/{ui → bases/radix/ui}/accordion.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/alert-dialog.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/alert.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/avatar.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/badge.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/breadcrumb.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/button.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/card.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/checkbox.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/context-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/dialog.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/dropdown-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/empty.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/field.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/hover-card.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/input.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/item.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/kbd.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/label.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/menubar.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/native-select.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/navigation-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/pagination.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/popover.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/progress.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/radio-group.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/scroll-area.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/select.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/separator.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/sheet.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/sidebar.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/skeleton.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/slider.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/spinner.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/switch.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/table.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/tabs.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/textarea.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/toggle-group.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/toggle.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/tooltip.tsrx +1 -1
- package/src/bases/react-aria/ui/accordion.tsrx +73 -0
- package/src/bases/react-aria/ui/alert-dialog.tsrx +154 -0
- package/src/bases/react-aria/ui/alert.tsrx +60 -0
- package/src/bases/react-aria/ui/aspect-ratio.tsrx +13 -0
- package/src/bases/react-aria/ui/avatar.tsrx +83 -0
- package/src/bases/react-aria/ui/badge.tsrx +48 -0
- package/src/bases/react-aria/ui/breadcrumb.tsrx +100 -0
- package/src/bases/react-aria/ui/button.tsrx +96 -0
- package/src/bases/react-aria/ui/card.tsrx +73 -0
- package/src/bases/react-aria/ui/checkbox.tsrx +37 -0
- package/src/bases/react-aria/ui/collapsible.tsrx +20 -0
- package/src/bases/react-aria/ui/dialog.tsrx +131 -0
- package/src/bases/react-aria/ui/empty.tsrx +79 -0
- package/src/bases/react-aria/ui/field.tsrx +170 -0
- package/src/bases/react-aria/ui/input.tsrx +28 -0
- package/src/bases/react-aria/ui/item.tsrx +148 -0
- package/src/bases/react-aria/ui/kbd.tsrx +30 -0
- package/src/bases/react-aria/ui/label.tsrx +22 -0
- package/src/bases/react-aria/ui/native-select.tsrx +44 -0
- package/src/bases/react-aria/ui/pagination.tsrx +92 -0
- package/src/bases/react-aria/ui/popover.tsrx +57 -0
- package/src/bases/react-aria/ui/radio-group.tsrx +46 -0
- package/src/bases/react-aria/ui/scroll-area.tsrx +14 -0
- package/src/bases/react-aria/ui/separator.tsrx +28 -0
- package/src/bases/react-aria/ui/sheet.tsrx +143 -0
- package/src/bases/react-aria/ui/skeleton.tsrx +10 -0
- package/src/bases/react-aria/ui/slider.tsrx +56 -0
- package/src/bases/react-aria/ui/spinner.tsrx +28 -0
- package/src/bases/react-aria/ui/switch.tsrx +38 -0
- package/src/bases/react-aria/ui/table.tsrx +98 -0
- package/src/bases/react-aria/ui/tabs.tsrx +67 -0
- package/src/bases/react-aria/ui/textarea.tsrx +26 -0
- package/src/bases/react-aria/ui/toggle.tsrx +38 -0
- package/src/index.ts +0 -263
- /package/src/{ui → bases/radix/ui}/aspect-ratio.tsrx +0 -0
- /package/src/{ui → bases/radix/ui}/collapsible.tsrx +0 -0
- /package/src/{ui → bases/radix/ui}/sonner.tsrx +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type OctaneNode } from 'octane';
|
|
2
|
+
import {
|
|
3
|
+
DisclosurePanel as AccordionContentPrimitive,
|
|
4
|
+
Heading as AccordionHeaderPrimitive,
|
|
5
|
+
Disclosure as AccordionItemPrimitive,
|
|
6
|
+
DisclosureGroup as AccordionPrimitive,
|
|
7
|
+
Button as AccordionTriggerPrimitive,
|
|
8
|
+
type ButtonProps,
|
|
9
|
+
type DisclosureGroupProps,
|
|
10
|
+
type DisclosurePanelProps,
|
|
11
|
+
type DisclosureProps,
|
|
12
|
+
} from '@octanejs/aria/components';
|
|
13
|
+
|
|
14
|
+
import { cn } from '../../../lib/utils';
|
|
15
|
+
import { ChevronDownIcon, ChevronUpIcon } from '@octanejs/lucide';
|
|
16
|
+
|
|
17
|
+
export function Accordion({ className, ...props }: DisclosureGroupProps) {
|
|
18
|
+
return <AccordionPrimitive
|
|
19
|
+
data-slot="accordion"
|
|
20
|
+
className={cn('flex w-full flex-col', className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function AccordionItem({ className, ...props }: DisclosureProps) {
|
|
26
|
+
return <AccordionItemPrimitive
|
|
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 }: Omit<
|
|
34
|
+
ButtonProps,
|
|
35
|
+
'children'
|
|
36
|
+
> & { children: OctaneNode }) {
|
|
37
|
+
return <AccordionHeaderPrimitive className="flex">
|
|
38
|
+
<AccordionTriggerPrimitive
|
|
39
|
+
slot="trigger"
|
|
40
|
+
data-slot="accordion-trigger"
|
|
41
|
+
className={cn(
|
|
42
|
+
'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 disabled:pointer-events-none 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',
|
|
43
|
+
className,
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
<ChevronDownIcon
|
|
49
|
+
data-slot="accordion-trigger-icon"
|
|
50
|
+
className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
|
|
51
|
+
/>
|
|
52
|
+
<ChevronUpIcon
|
|
53
|
+
data-slot="accordion-trigger-icon"
|
|
54
|
+
className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
|
|
55
|
+
/>
|
|
56
|
+
</AccordionTriggerPrimitive>
|
|
57
|
+
</AccordionHeaderPrimitive>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function AccordionContent({ className, children, ...props }: DisclosurePanelProps) {
|
|
61
|
+
return <AccordionContentPrimitive
|
|
62
|
+
data-slot="accordion-content"
|
|
63
|
+
className="h-(--disclosure-panel-height) overflow-clip text-sm transition-[height] data-open:animate-accordion-down data-closed:animate-accordion-up"
|
|
64
|
+
{...props}
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
'pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
|
|
69
|
+
className,
|
|
70
|
+
)}
|
|
71
|
+
>{children}</div>
|
|
72
|
+
</AccordionContentPrimitive>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// OCTANE ADAPTATION: RAC does not publish a `ModalProps` type (upstream writes
|
|
2
|
+
// `React.ComponentProps<typeof Modal>` for exactly that reason), so the one
|
|
3
|
+
// prop borrowed from Modal is spelled out instead of picked.
|
|
4
|
+
import { type OctaneNode } from 'octane';
|
|
5
|
+
import {
|
|
6
|
+
Dialog as AlertDialogPrimitive,
|
|
7
|
+
DialogTrigger as AlertDialogTriggerPrimitive,
|
|
8
|
+
Heading,
|
|
9
|
+
ModalOverlay as ModalOverlayPrimitive,
|
|
10
|
+
Modal as ModalPrimitive,
|
|
11
|
+
type DialogProps as AlertDialogPrimitiveProps,
|
|
12
|
+
type DialogTriggerProps as AlertDialogTriggerPrimitiveProps,
|
|
13
|
+
type ModalOverlayProps as ModalOverlayPrimitiveProps,
|
|
14
|
+
} from '@octanejs/aria/components';
|
|
15
|
+
import type { HeadingProps } from '@octanejs/aria/components';
|
|
16
|
+
|
|
17
|
+
import { cn } from '../../../lib/utils';
|
|
18
|
+
import type { HostProps } from '../../../lib/types';
|
|
19
|
+
import { Button } from './button.tsrx';
|
|
20
|
+
|
|
21
|
+
export function AlertDialogTrigger({ ...props }: AlertDialogTriggerPrimitiveProps) {
|
|
22
|
+
return <AlertDialogTriggerPrimitive data-slot="alert-dialog-trigger" {...props} />;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function AlertDialogOverlay({ className, children, ...props }: Omit<
|
|
26
|
+
ModalOverlayPrimitiveProps,
|
|
27
|
+
'className' | 'children'
|
|
28
|
+
> & {
|
|
29
|
+
className?: string;
|
|
30
|
+
children: OctaneNode;
|
|
31
|
+
}) {
|
|
32
|
+
return <ModalOverlayPrimitive
|
|
33
|
+
data-slot="alert-dialog-overlay"
|
|
34
|
+
className={cn(
|
|
35
|
+
'fixed inset-0 isolate z-50 bg-black/10 duration-100 data-entering:animate-in data-entering:fade-in-0 data-exiting:animate-out data-exiting:fade-out-0 supports-backdrop-filter:backdrop-blur-xs',
|
|
36
|
+
className,
|
|
37
|
+
)}
|
|
38
|
+
{...props}
|
|
39
|
+
>{children}</ModalOverlayPrimitive>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function AlertDialog({ className, size = 'default', children, ...props }: Omit<
|
|
43
|
+
ModalOverlayPrimitiveProps,
|
|
44
|
+
'className' | 'children'
|
|
45
|
+
> & { isDismissable?: boolean } & {
|
|
46
|
+
className?: string;
|
|
47
|
+
size?: 'default' | 'sm';
|
|
48
|
+
children: OctaneNode;
|
|
49
|
+
}) {
|
|
50
|
+
return <AlertDialogOverlay {...props}>
|
|
51
|
+
<ModalPrimitive
|
|
52
|
+
data-slot="alert-dialog-content"
|
|
53
|
+
data-size={size}
|
|
54
|
+
className={cn(
|
|
55
|
+
'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-entering:animate-in data-entering:fade-in-0 data-entering:zoom-in-95 data-exiting:animate-out data-exiting:fade-out-0 data-exiting:zoom-out-95 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm',
|
|
56
|
+
className,
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
<AlertDialogPrimitive
|
|
60
|
+
data-slot="alert-dialog"
|
|
61
|
+
role="alertdialog"
|
|
62
|
+
className="[display:inherit] [gap:inherit] outline-none"
|
|
63
|
+
>{children}</AlertDialogPrimitive>
|
|
64
|
+
</ModalPrimitive>
|
|
65
|
+
</AlertDialogOverlay>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function AlertDialogContent({ className, size = 'default', children, ...props }: Record<
|
|
69
|
+
string,
|
|
70
|
+
any
|
|
71
|
+
>) {
|
|
72
|
+
return <AlertDialog className={className} size={size} {...props}>{children}</AlertDialog>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function AlertDialogHeader({ className, ...props }: HostProps<'div'>) {
|
|
76
|
+
return <div
|
|
77
|
+
data-slot="alert-dialog-header"
|
|
78
|
+
className={cn(
|
|
79
|
+
'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]',
|
|
80
|
+
className,
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
/>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function AlertDialogFooter({ className, ...props }: HostProps<'div'>) {
|
|
87
|
+
return <div
|
|
88
|
+
data-slot="alert-dialog-footer"
|
|
89
|
+
className={cn(
|
|
90
|
+
'-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',
|
|
91
|
+
className,
|
|
92
|
+
)}
|
|
93
|
+
{...props}
|
|
94
|
+
/>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function AlertDialogMedia({ className, ...props }: HostProps<'div'>) {
|
|
98
|
+
return <div
|
|
99
|
+
data-slot="alert-dialog-media"
|
|
100
|
+
className={cn(
|
|
101
|
+
'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',
|
|
102
|
+
className,
|
|
103
|
+
)}
|
|
104
|
+
{...props}
|
|
105
|
+
/>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function AlertDialogTitle({ className, ...props }: Omit<HeadingProps, 'slot'>) {
|
|
109
|
+
return <Heading
|
|
110
|
+
slot="title"
|
|
111
|
+
data-slot="alert-dialog-title"
|
|
112
|
+
className={cn(
|
|
113
|
+
'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',
|
|
114
|
+
className,
|
|
115
|
+
)}
|
|
116
|
+
{...props}
|
|
117
|
+
/>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function AlertDialogDescription({ className, ...props }: Omit<HostProps<'div'>, 'slot'>) {
|
|
121
|
+
return <div
|
|
122
|
+
data-slot="alert-dialog-description"
|
|
123
|
+
className={cn(
|
|
124
|
+
'text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
|
|
125
|
+
className,
|
|
126
|
+
)}
|
|
127
|
+
{...props}
|
|
128
|
+
/>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function AlertDialogAction({ className, ...props }: Record<string, any>) {
|
|
132
|
+
return <Button
|
|
133
|
+
slot="close"
|
|
134
|
+
data-slot="alert-dialog-action"
|
|
135
|
+
className={cn(className)}
|
|
136
|
+
{...props}
|
|
137
|
+
/>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function AlertDialogCancel({
|
|
141
|
+
className,
|
|
142
|
+
variant = 'outline',
|
|
143
|
+
size = 'default',
|
|
144
|
+
...props
|
|
145
|
+
}: Record<string, any>) {
|
|
146
|
+
return <Button
|
|
147
|
+
slot="close"
|
|
148
|
+
data-slot="alert-dialog-cancel"
|
|
149
|
+
className={cn(className)}
|
|
150
|
+
variant={variant}
|
|
151
|
+
size={size}
|
|
152
|
+
{...props}
|
|
153
|
+
/>;
|
|
154
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../../lib/utils';
|
|
4
|
+
import type { HostProps } from '../../../lib/types';
|
|
5
|
+
|
|
6
|
+
const alertVariants = cva(
|
|
7
|
+
'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',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'bg-card text-card-foreground',
|
|
12
|
+
destructive: 'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
variant: 'default',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export function Alert({ className, variant, ...props }: HostProps<'div'> & VariantProps<
|
|
22
|
+
typeof alertVariants
|
|
23
|
+
>) {
|
|
24
|
+
return <div
|
|
25
|
+
data-slot="alert"
|
|
26
|
+
role="alert"
|
|
27
|
+
className={cn(alertVariants({ variant }), className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function AlertTitle({ className, ...props }: HostProps<'div'>) {
|
|
33
|
+
return <div
|
|
34
|
+
data-slot="alert-title"
|
|
35
|
+
className={cn(
|
|
36
|
+
'font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground',
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function AlertDescription({ className, ...props }: HostProps<'div'>) {
|
|
44
|
+
return <div
|
|
45
|
+
data-slot="alert-description"
|
|
46
|
+
className={cn(
|
|
47
|
+
'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',
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function AlertAction({ className, ...props }: HostProps<'div'>) {
|
|
55
|
+
return <div
|
|
56
|
+
data-slot="alert-action"
|
|
57
|
+
className={cn('absolute top-2 right-2', className)}
|
|
58
|
+
{...props}
|
|
59
|
+
/>;
|
|
60
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn } from '../../../lib/utils';
|
|
2
|
+
import type { HostProps } from '../../../lib/types';
|
|
3
|
+
|
|
4
|
+
export function AspectRatio({ ratio, className, ...props }: HostProps<'div'> & { ratio: number }) {
|
|
5
|
+
return <div
|
|
6
|
+
data-slot="aspect-ratio"
|
|
7
|
+
style={{
|
|
8
|
+
'--ratio': ratio,
|
|
9
|
+
} as Record<string, string | number>}
|
|
10
|
+
className={cn('relative aspect-(--ratio)', className)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useState } from 'octane';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../../lib/utils';
|
|
4
|
+
import type { HostProps } from '../../../lib/types';
|
|
5
|
+
|
|
6
|
+
export function Avatar({ className, size = 'default', ...props }: HostProps<'div'> & {
|
|
7
|
+
size?: 'default' | 'sm' | 'lg';
|
|
8
|
+
}) {
|
|
9
|
+
return <div
|
|
10
|
+
data-slot="avatar"
|
|
11
|
+
data-size={size}
|
|
12
|
+
className={cn(
|
|
13
|
+
'group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten',
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ImageState = 'loading' | 'loaded' | 'error';
|
|
21
|
+
|
|
22
|
+
export function AvatarImage({ className, ...props }: HostProps<'img'>) {
|
|
23
|
+
const [state, setState] = useState<ImageState>(props.src ? 'loading' : 'error');
|
|
24
|
+
return <img
|
|
25
|
+
data-slot="avatar-image"
|
|
26
|
+
alt={props.alt || ''}
|
|
27
|
+
data-state={state}
|
|
28
|
+
onLoad={() => setState('loaded')}
|
|
29
|
+
onError={() => setState('error')}
|
|
30
|
+
className={cn(
|
|
31
|
+
'peer aspect-square size-full rounded-full object-cover data-[state=error]:hidden',
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function AvatarFallback({ className, ...props }: HostProps<'div'>) {
|
|
39
|
+
return <div
|
|
40
|
+
data-slot="avatar-fallback"
|
|
41
|
+
className={cn(
|
|
42
|
+
'flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs peer-data-[state=error]:flex peer-[*]:hidden',
|
|
43
|
+
className,
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function AvatarBadge({ className, ...props }: HostProps<'span'>) {
|
|
50
|
+
return <span
|
|
51
|
+
data-slot="avatar-badge"
|
|
52
|
+
className={cn(
|
|
53
|
+
'absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none',
|
|
54
|
+
'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden',
|
|
55
|
+
'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2',
|
|
56
|
+
'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2',
|
|
57
|
+
className,
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function AvatarGroup({ className, ...props }: HostProps<'div'>) {
|
|
64
|
+
return <div
|
|
65
|
+
data-slot="avatar-group"
|
|
66
|
+
className={cn(
|
|
67
|
+
'group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background',
|
|
68
|
+
className,
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function AvatarGroupCount({ className, ...props }: HostProps<'div'>) {
|
|
75
|
+
return <div
|
|
76
|
+
data-slot="avatar-group-count"
|
|
77
|
+
className={cn(
|
|
78
|
+
'relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3',
|
|
79
|
+
className,
|
|
80
|
+
)}
|
|
81
|
+
{...props}
|
|
82
|
+
/>;
|
|
83
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type OctaneNode } from 'octane';
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../../lib/utils';
|
|
5
|
+
import type { HostProps } from '../../../lib/types';
|
|
6
|
+
|
|
7
|
+
export const badgeVariants = cva(
|
|
8
|
+
'group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
|
|
13
|
+
secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
|
|
14
|
+
destructive: 'bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20',
|
|
15
|
+
outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
|
|
16
|
+
ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
|
|
17
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
variant: 'default',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export function Badge({ className, variant = 'default', render, ...props }: HostProps<
|
|
27
|
+
'span'
|
|
28
|
+
> & VariantProps<typeof badgeVariants> & {
|
|
29
|
+
render?: (props: Record<string, unknown>) => OctaneNode;
|
|
30
|
+
}) {
|
|
31
|
+
if (render) {
|
|
32
|
+
const renderProps = {
|
|
33
|
+
'data-slot': 'badge',
|
|
34
|
+
'data-variant': variant,
|
|
35
|
+
className: cn(badgeVariants({ variant }), className),
|
|
36
|
+
...props,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return render(renderProps);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return <span
|
|
43
|
+
data-slot="badge"
|
|
44
|
+
data-variant={variant}
|
|
45
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>;
|
|
48
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// OCTANE ADAPTATION: octane's renderable type is `OctaneNode = unknown`, and a
|
|
2
|
+
// union containing `unknown` collapses to `unknown` — so RAC's
|
|
3
|
+
// `ChildrenOrFunction` cannot keep its render-prop arm distinguishable the way
|
|
4
|
+
// React's precise `ReactNode` union does, and composeRenderProps has nothing to
|
|
5
|
+
// infer its render values from. Annotating them here restores the upstream
|
|
6
|
+
// parameter types without weakening the binding.
|
|
7
|
+
import {
|
|
8
|
+
Breadcrumb as BreadcrumbPrimitive,
|
|
9
|
+
Breadcrumbs as BreadcrumbsPrimitive,
|
|
10
|
+
composeRenderProps,
|
|
11
|
+
Link as LinkPrimitive,
|
|
12
|
+
type BreadcrumbProps,
|
|
13
|
+
type BreadcrumbRenderProps,
|
|
14
|
+
type BreadcrumbsProps,
|
|
15
|
+
type LinkProps,
|
|
16
|
+
} from '@octanejs/aria/components';
|
|
17
|
+
|
|
18
|
+
import { cn } from '../../../lib/utils';
|
|
19
|
+
import type { HostProps } from '../../../lib/types';
|
|
20
|
+
import { ChevronRightIcon, MoreHorizontalIcon } from '@octanejs/lucide';
|
|
21
|
+
|
|
22
|
+
export function Breadcrumb({ className, ...props }: HostProps<'nav'>) {
|
|
23
|
+
return <nav
|
|
24
|
+
aria-label="breadcrumb"
|
|
25
|
+
data-slot="breadcrumb"
|
|
26
|
+
className={cn(className)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function BreadcrumbList<T extends object>({ className, ...props }: BreadcrumbsProps<T>) {
|
|
32
|
+
return <BreadcrumbsPrimitive
|
|
33
|
+
data-slot="breadcrumb-list"
|
|
34
|
+
className={cn(
|
|
35
|
+
'flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground',
|
|
36
|
+
className,
|
|
37
|
+
)}
|
|
38
|
+
{...props}
|
|
39
|
+
/>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function BreadcrumbItem({
|
|
43
|
+
className,
|
|
44
|
+
children,
|
|
45
|
+
separatorClassName,
|
|
46
|
+
...props
|
|
47
|
+
}: BreadcrumbProps & { separatorClassName?: string }) {
|
|
48
|
+
return <BreadcrumbPrimitive
|
|
49
|
+
data-slot="breadcrumb-item"
|
|
50
|
+
className={cn('inline-flex items-center gap-1', className)}
|
|
51
|
+
{...props}
|
|
52
|
+
>{composeRenderProps(
|
|
53
|
+
children,
|
|
54
|
+
(children, { isCurrent }: BreadcrumbRenderProps) => <>
|
|
55
|
+
{children}
|
|
56
|
+
{!isCurrent &&
|
|
57
|
+
<span
|
|
58
|
+
data-slot="breadcrumb-separator"
|
|
59
|
+
role="presentation"
|
|
60
|
+
aria-hidden="true"
|
|
61
|
+
className={cn('[&>svg]:size-3.5', separatorClassName)}
|
|
62
|
+
>
|
|
63
|
+
<ChevronRightIcon />
|
|
64
|
+
</span>}
|
|
65
|
+
</>,
|
|
66
|
+
)}</BreadcrumbPrimitive>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function BreadcrumbLink({ className, render, ...props }: LinkProps) {
|
|
70
|
+
return <LinkPrimitive
|
|
71
|
+
data-slot="breadcrumb-link"
|
|
72
|
+
className={cn('transition-colors hover:text-foreground', className)}
|
|
73
|
+
render={render}
|
|
74
|
+
{...props}
|
|
75
|
+
/>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function BreadcrumbPage({ className, ...props }: HostProps<'span'>) {
|
|
79
|
+
return <span
|
|
80
|
+
data-slot="breadcrumb-page"
|
|
81
|
+
role="link"
|
|
82
|
+
aria-disabled="true"
|
|
83
|
+
aria-current="page"
|
|
84
|
+
className={cn('font-normal text-foreground', className)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function BreadcrumbEllipsis({ className, ...props }: HostProps<'span'>) {
|
|
90
|
+
return <span
|
|
91
|
+
data-slot="breadcrumb-ellipsis"
|
|
92
|
+
role="presentation"
|
|
93
|
+
aria-hidden="true"
|
|
94
|
+
className={cn('flex size-5 items-center justify-center [&>svg]:size-4', className)}
|
|
95
|
+
{...props}
|
|
96
|
+
>
|
|
97
|
+
<MoreHorizontalIcon />
|
|
98
|
+
<span className="sr-only">More</span>
|
|
99
|
+
</span>;
|
|
100
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// React Aria base button — structure from upstream `bases/aria/ui/button.tsx`
|
|
2
|
+
// (RAC Button + Link primitives, both slotted as `button`, and the aria base's
|
|
3
|
+
// data-variant/data-size attributes, which the Base UI base omits).
|
|
4
|
+
//
|
|
5
|
+
// STYLING: the cva strings are this package's Radix-base (default-Tailwind,
|
|
6
|
+
// utilities-inlined) flavor rather than upstream's `cn-*` semantic hooks, so
|
|
7
|
+
// this component styles against a plain Tailwind v4 build like the migrated
|
|
8
|
+
// Radix families do. Upstream's own bases share button's class strings exactly,
|
|
9
|
+
// so nothing is lost here — but that is NOT a general rule: where a selector
|
|
10
|
+
// keys off a primitive-emitted attribute the bases legitimately diverge (the
|
|
11
|
+
// Radix separator styles `data-horizontal:`, the aria one
|
|
12
|
+
// `aria-[orientation=horizontal]:`). `tests/cross-base.test.ts` therefore pins
|
|
13
|
+
// the structural contract — data-slot names and exported components — not class
|
|
14
|
+
// equality.
|
|
15
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
16
|
+
import { Button as ButtonPrimitive, Link as LinkPrimitive } from '@octanejs/aria/components';
|
|
17
|
+
import type {
|
|
18
|
+
ButtonProps as ButtonPrimitiveProps,
|
|
19
|
+
LinkProps as LinkPrimitiveProps,
|
|
20
|
+
} from '@octanejs/aria/components';
|
|
21
|
+
|
|
22
|
+
import { cn } from '../../../lib/utils';
|
|
23
|
+
|
|
24
|
+
export const buttonVariants = cva(
|
|
25
|
+
'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',
|
|
26
|
+
{
|
|
27
|
+
variants: {
|
|
28
|
+
variant: {
|
|
29
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/80',
|
|
30
|
+
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',
|
|
31
|
+
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',
|
|
32
|
+
ghost: 'hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50',
|
|
33
|
+
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',
|
|
34
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
35
|
+
},
|
|
36
|
+
size: {
|
|
37
|
+
default: 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
|
|
38
|
+
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',
|
|
39
|
+
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',
|
|
40
|
+
lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
|
|
41
|
+
icon: 'size-8',
|
|
42
|
+
'icon-xs': 'size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*=\'size-\'])]:size-3',
|
|
43
|
+
'icon-sm': 'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
|
|
44
|
+
'icon-lg': 'size-9',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
defaultVariants: {
|
|
48
|
+
variant: 'default',
|
|
49
|
+
size: 'default',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
type ButtonVariants = VariantProps<typeof buttonVariants>;
|
|
55
|
+
|
|
56
|
+
export interface ButtonProps extends Omit<ButtonPrimitiveProps, 'className'> {
|
|
57
|
+
variant?: ButtonVariants['variant'];
|
|
58
|
+
size?: ButtonVariants['size'];
|
|
59
|
+
className?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface LinkButtonProps extends Omit<LinkPrimitiveProps, 'className'> {
|
|
63
|
+
variant?: ButtonVariants['variant'];
|
|
64
|
+
size?: ButtonVariants['size'];
|
|
65
|
+
className?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function Button({
|
|
69
|
+
className,
|
|
70
|
+
variant = 'default',
|
|
71
|
+
size = 'default',
|
|
72
|
+
...props
|
|
73
|
+
}: ButtonProps) @{
|
|
74
|
+
<ButtonPrimitive
|
|
75
|
+
data-slot="button"
|
|
76
|
+
data-variant={variant}
|
|
77
|
+
data-size={size}
|
|
78
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function LinkButton({
|
|
84
|
+
className,
|
|
85
|
+
variant = 'default',
|
|
86
|
+
size = 'default',
|
|
87
|
+
...props
|
|
88
|
+
}: LinkButtonProps) @{
|
|
89
|
+
<LinkPrimitive
|
|
90
|
+
data-slot="button"
|
|
91
|
+
data-variant={variant}
|
|
92
|
+
data-size={size}
|
|
93
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
}
|