@octanejs/shadcn 0.0.6 → 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.
Files changed (100) hide show
  1. package/package.json +109 -10
  2. package/src/bases/base-ui/ui/accordion.tsrx +71 -0
  3. package/src/bases/base-ui/ui/alert-dialog.tsrx +149 -0
  4. package/src/bases/base-ui/ui/alert.tsrx +68 -0
  5. package/src/bases/base-ui/ui/aspect-ratio.tsrx +24 -0
  6. package/src/bases/base-ui/ui/button.tsrx +69 -0
  7. package/src/bases/base-ui/ui/card.tsrx +76 -0
  8. package/src/bases/base-ui/ui/checkbox.tsrx +43 -0
  9. package/src/bases/base-ui/ui/dialog.tsrx +137 -0
  10. package/src/bases/base-ui/ui/empty.tsrx +88 -0
  11. package/src/bases/base-ui/ui/input.tsrx +28 -0
  12. package/src/bases/base-ui/ui/kbd.tsrx +27 -0
  13. package/src/bases/base-ui/ui/label.tsrx +31 -0
  14. package/src/bases/base-ui/ui/native-select.tsrx +54 -0
  15. package/src/bases/base-ui/ui/popover.tsrx +85 -0
  16. package/src/bases/base-ui/ui/radio-group.tsrx +50 -0
  17. package/src/bases/base-ui/ui/separator.tsrx +34 -0
  18. package/src/bases/base-ui/ui/skeleton.tsrx +17 -0
  19. package/src/bases/base-ui/ui/spinner.tsrx +30 -0
  20. package/src/bases/base-ui/ui/switch.tsrx +43 -0
  21. package/src/bases/base-ui/ui/textarea.tsrx +22 -0
  22. package/src/bases/base-ui/ui/tooltip.tsrx +73 -0
  23. package/src/{ui → bases/radix/ui}/accordion.tsrx +1 -1
  24. package/src/{ui → bases/radix/ui}/alert-dialog.tsrx +1 -1
  25. package/src/{ui → bases/radix/ui}/alert.tsrx +2 -2
  26. package/src/{ui → bases/radix/ui}/avatar.tsrx +1 -1
  27. package/src/{ui → bases/radix/ui}/badge.tsrx +2 -2
  28. package/src/{ui → bases/radix/ui}/breadcrumb.tsrx +1 -1
  29. package/src/{ui → bases/radix/ui}/button.tsrx +2 -2
  30. package/src/{ui → bases/radix/ui}/card.tsrx +2 -2
  31. package/src/{ui → bases/radix/ui}/checkbox.tsrx +1 -1
  32. package/src/{ui → bases/radix/ui}/context-menu.tsrx +1 -1
  33. package/src/{ui → bases/radix/ui}/dialog.tsrx +1 -1
  34. package/src/{ui → bases/radix/ui}/dropdown-menu.tsrx +1 -1
  35. package/src/{ui → bases/radix/ui}/empty.tsrx +1 -1
  36. package/src/{ui → bases/radix/ui}/field.tsrx +1 -1
  37. package/src/{ui → bases/radix/ui}/hover-card.tsrx +1 -1
  38. package/src/{ui → bases/radix/ui}/input.tsrx +2 -2
  39. package/src/{ui → bases/radix/ui}/item.tsrx +1 -1
  40. package/src/{ui → bases/radix/ui}/kbd.tsrx +2 -2
  41. package/src/{ui → bases/radix/ui}/label.tsrx +1 -1
  42. package/src/{ui → bases/radix/ui}/menubar.tsrx +1 -1
  43. package/src/{ui → bases/radix/ui}/native-select.tsrx +1 -1
  44. package/src/{ui → bases/radix/ui}/navigation-menu.tsrx +1 -1
  45. package/src/{ui → bases/radix/ui}/pagination.tsrx +1 -1
  46. package/src/{ui → bases/radix/ui}/popover.tsrx +1 -1
  47. package/src/{ui → bases/radix/ui}/progress.tsrx +1 -1
  48. package/src/{ui → bases/radix/ui}/radio-group.tsrx +1 -1
  49. package/src/{ui → bases/radix/ui}/scroll-area.tsrx +1 -1
  50. package/src/{ui → bases/radix/ui}/select.tsrx +1 -1
  51. package/src/{ui → bases/radix/ui}/separator.tsrx +1 -1
  52. package/src/{ui → bases/radix/ui}/sheet.tsrx +1 -1
  53. package/src/{ui → bases/radix/ui}/sidebar.tsrx +2 -2
  54. package/src/{ui → bases/radix/ui}/skeleton.tsrx +2 -2
  55. package/src/{ui → bases/radix/ui}/slider.tsrx +1 -1
  56. package/src/{ui → bases/radix/ui}/spinner.tsrx +1 -1
  57. package/src/{ui → bases/radix/ui}/switch.tsrx +1 -1
  58. package/src/{ui → bases/radix/ui}/table.tsrx +1 -1
  59. package/src/{ui → bases/radix/ui}/tabs.tsrx +1 -1
  60. package/src/{ui → bases/radix/ui}/textarea.tsrx +2 -2
  61. package/src/{ui → bases/radix/ui}/toggle-group.tsrx +1 -1
  62. package/src/{ui → bases/radix/ui}/toggle.tsrx +1 -1
  63. package/src/{ui → bases/radix/ui}/tooltip.tsrx +1 -1
  64. package/src/bases/react-aria/ui/accordion.tsrx +73 -0
  65. package/src/bases/react-aria/ui/alert-dialog.tsrx +154 -0
  66. package/src/bases/react-aria/ui/alert.tsrx +60 -0
  67. package/src/bases/react-aria/ui/aspect-ratio.tsrx +13 -0
  68. package/src/bases/react-aria/ui/avatar.tsrx +83 -0
  69. package/src/bases/react-aria/ui/badge.tsrx +48 -0
  70. package/src/bases/react-aria/ui/breadcrumb.tsrx +100 -0
  71. package/src/bases/react-aria/ui/button.tsrx +96 -0
  72. package/src/bases/react-aria/ui/card.tsrx +73 -0
  73. package/src/bases/react-aria/ui/checkbox.tsrx +37 -0
  74. package/src/bases/react-aria/ui/collapsible.tsrx +20 -0
  75. package/src/bases/react-aria/ui/dialog.tsrx +131 -0
  76. package/src/bases/react-aria/ui/empty.tsrx +79 -0
  77. package/src/bases/react-aria/ui/field.tsrx +170 -0
  78. package/src/bases/react-aria/ui/input.tsrx +28 -0
  79. package/src/bases/react-aria/ui/item.tsrx +148 -0
  80. package/src/bases/react-aria/ui/kbd.tsrx +30 -0
  81. package/src/bases/react-aria/ui/label.tsrx +22 -0
  82. package/src/bases/react-aria/ui/native-select.tsrx +44 -0
  83. package/src/bases/react-aria/ui/pagination.tsrx +92 -0
  84. package/src/bases/react-aria/ui/popover.tsrx +57 -0
  85. package/src/bases/react-aria/ui/radio-group.tsrx +46 -0
  86. package/src/bases/react-aria/ui/scroll-area.tsrx +14 -0
  87. package/src/bases/react-aria/ui/separator.tsrx +28 -0
  88. package/src/bases/react-aria/ui/sheet.tsrx +143 -0
  89. package/src/bases/react-aria/ui/skeleton.tsrx +10 -0
  90. package/src/bases/react-aria/ui/slider.tsrx +56 -0
  91. package/src/bases/react-aria/ui/spinner.tsrx +28 -0
  92. package/src/bases/react-aria/ui/switch.tsrx +38 -0
  93. package/src/bases/react-aria/ui/table.tsrx +98 -0
  94. package/src/bases/react-aria/ui/tabs.tsrx +67 -0
  95. package/src/bases/react-aria/ui/textarea.tsrx +26 -0
  96. package/src/bases/react-aria/ui/toggle.tsrx +38 -0
  97. package/src/index.ts +0 -263
  98. /package/src/{ui → bases/radix/ui}/aspect-ratio.tsrx +0 -0
  99. /package/src/{ui → bases/radix/ui}/collapsible.tsrx +0 -0
  100. /package/src/{ui → bases/radix/ui}/sonner.tsrx +0 -0
@@ -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
+ }
@@ -0,0 +1,73 @@
1
+ import { cn } from '../../../lib/utils';
2
+ import type { HostProps } from '../../../lib/types';
3
+
4
+ export function Card({ className, size = 'default', ...props }: HostProps<'div'> & {
5
+ size?: 'default' | 'sm';
6
+ }) {
7
+ return <div
8
+ data-slot="card"
9
+ data-size={size}
10
+ className={cn(
11
+ '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',
12
+ className,
13
+ )}
14
+ {...props}
15
+ />;
16
+ }
17
+
18
+ export function CardHeader({ className, ...props }: HostProps<'div'>) {
19
+ return <div
20
+ data-slot="card-header"
21
+ className={cn(
22
+ '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)',
23
+ className,
24
+ )}
25
+ {...props}
26
+ />;
27
+ }
28
+
29
+ export function CardTitle({ className, ...props }: HostProps<'div'>) {
30
+ return <div
31
+ data-slot="card-title"
32
+ className={cn(
33
+ 'text-base leading-snug font-medium group-data-[size=sm]/card:text-sm',
34
+ className,
35
+ )}
36
+ {...props}
37
+ />;
38
+ }
39
+
40
+ export function CardDescription({ className, ...props }: HostProps<'div'>) {
41
+ return <div
42
+ data-slot="card-description"
43
+ className={cn('text-sm text-muted-foreground', className)}
44
+ {...props}
45
+ />;
46
+ }
47
+
48
+ export function CardAction({ className, ...props }: HostProps<'div'>) {
49
+ return <div
50
+ data-slot="card-action"
51
+ className={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
52
+ {...props}
53
+ />;
54
+ }
55
+
56
+ export function CardContent({ className, ...props }: HostProps<'div'>) {
57
+ return <div
58
+ data-slot="card-content"
59
+ className={cn('px-(--card-spacing)', className)}
60
+ {...props}
61
+ />;
62
+ }
63
+
64
+ export function CardFooter({ className, ...props }: HostProps<'div'>) {
65
+ return <div
66
+ data-slot="card-footer"
67
+ className={cn(
68
+ 'flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)',
69
+ className,
70
+ )}
71
+ {...props}
72
+ />;
73
+ }
@@ -0,0 +1,37 @@
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
+ Checkbox as CheckboxPrimitive,
9
+ composeRenderProps,
10
+ type CheckboxProps,
11
+ type CheckboxRenderProps,
12
+ } from '@octanejs/aria/components';
13
+
14
+ import { cn } from '../../../lib/utils';
15
+ import { CheckIcon } from '@octanejs/lucide';
16
+
17
+ export function Checkbox({ className, children, ...props }: CheckboxProps) {
18
+ return <CheckboxPrimitive
19
+ data-slot="checkbox"
20
+ className={cn(
21
+ '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 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary data-focus-visible:border-ring data-focus-visible:ring-3 data-focus-visible:ring-ring/50 data-invalid:border-destructive data-invalid:ring-3 data-invalid:ring-destructive/20 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 dark:data-invalid:border-destructive/50 dark:data-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-selected:border-primary data-selected:bg-primary data-selected:text-primary-foreground data-invalid:data-selected:border-primary dark:data-selected:bg-primary',
22
+ className,
23
+ )}
24
+ {...props}
25
+ >{composeRenderProps(
26
+ children,
27
+ (children, { isSelected, isIndeterminate }: CheckboxRenderProps) => <>
28
+ <span
29
+ data-slot="checkbox-indicator"
30
+ className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
31
+ >
32
+ {(isSelected || isIndeterminate) && <CheckIcon />}
33
+ </span>
34
+ {children}
35
+ </>,
36
+ )}</CheckboxPrimitive>;
37
+ }