@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,20 @@
1
+ import {
2
+ DisclosurePanel as CollapsibleContentPrimitive,
3
+ Disclosure as CollapsiblePrimitive,
4
+ Button as CollapsibleTriggerPrimitive,
5
+ type ButtonProps,
6
+ type DisclosurePanelProps,
7
+ type DisclosureProps,
8
+ } from '@octanejs/aria/components';
9
+
10
+ export function Collapsible({ ...props }: DisclosureProps) {
11
+ return <CollapsiblePrimitive data-slot="collapsible" {...props} />;
12
+ }
13
+
14
+ export function CollapsibleTrigger({ ...props }: ButtonProps) {
15
+ return <CollapsibleTriggerPrimitive slot="trigger" data-slot="collapsible-trigger" {...props} />;
16
+ }
17
+
18
+ export function CollapsibleContent({ ...props }: DisclosurePanelProps) {
19
+ return <CollapsibleContentPrimitive data-slot="collapsible-content" {...props} />;
20
+ }
@@ -0,0 +1,131 @@
1
+ import { type OctaneNode } from 'octane';
2
+ import {
3
+ Dialog as DialogPrimitive,
4
+ DialogTrigger as DialogTriggerPrimitive,
5
+ Heading,
6
+ ModalOverlay as ModalOverlayPrimitive,
7
+ Modal as ModalPrimitive,
8
+ type DialogProps as DialogPrimitiveProps,
9
+ type DialogTriggerProps as DialogTriggerPrimitiveProps,
10
+ type ModalOverlayProps as ModalOverlayPrimitiveProps,
11
+ } from '@octanejs/aria/components';
12
+
13
+ import { cn } from '../../../lib/utils';
14
+ import type { HostProps } from '../../../lib/types';
15
+ import { Button } from './button.tsrx';
16
+ import { XIcon } from '@octanejs/lucide';
17
+
18
+ export function DialogTrigger({ ...props }: DialogTriggerPrimitiveProps) {
19
+ return <DialogTriggerPrimitive data-slot="dialog-trigger" {...props} />;
20
+ }
21
+
22
+ export function DialogClose({ className, variant = 'outline', size = 'default', ...props }: Record<
23
+ string,
24
+ any
25
+ >) {
26
+ return <Button
27
+ slot="close"
28
+ data-slot="dialog-close"
29
+ variant={variant}
30
+ size={size}
31
+ className={cn(className)}
32
+ {...props}
33
+ />;
34
+ }
35
+
36
+ export function DialogOverlay({ className, children, ...props }: Omit<
37
+ ModalOverlayPrimitiveProps,
38
+ 'className' | 'children'
39
+ > & {
40
+ className?: string;
41
+ children: OctaneNode;
42
+ }) {
43
+ return <ModalOverlayPrimitive
44
+ data-slot="dialog-overlay"
45
+ className={cn(
46
+ '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',
47
+ className,
48
+ )}
49
+ {...props}
50
+ >{children}</ModalOverlayPrimitive>;
51
+ }
52
+
53
+ export function Dialog({
54
+ className,
55
+ children,
56
+ showCloseButton = true,
57
+ isDismissable = true,
58
+ ...props
59
+ }: Omit<ModalOverlayPrimitiveProps, 'className' | 'children'> & Pick<
60
+ Record<string, any>,
61
+ 'isDismissable'
62
+ > & {
63
+ className?: string;
64
+ children: OctaneNode;
65
+ showCloseButton?: boolean;
66
+ }) {
67
+ return <DialogOverlay isDismissable={isDismissable} {...props}>
68
+ <ModalPrimitive
69
+ data-slot="dialog-content"
70
+ className={cn(
71
+ 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none 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 sm:max-w-sm',
72
+ className,
73
+ )}
74
+ >
75
+ <DialogPrimitive data-slot="dialog" className="[display:inherit] [gap:inherit] outline-none">
76
+ {children}
77
+ {showCloseButton &&
78
+ <DialogClose variant="ghost" className="absolute top-2 right-2" size="icon-sm">
79
+ <XIcon />
80
+ <span className="sr-only">Close</span>
81
+ </DialogClose>}
82
+ </DialogPrimitive>
83
+ </ModalPrimitive>
84
+ </DialogOverlay>;
85
+ }
86
+
87
+ export function DialogHeader({ className, ...props }: HostProps<'div'>) {
88
+ return <div
89
+ data-slot="dialog-header"
90
+ className={cn('flex flex-col gap-2', className)}
91
+ {...props}
92
+ />;
93
+ }
94
+
95
+ export function DialogFooter({ className, showCloseButton = false, children, ...props }: HostProps<
96
+ 'div'
97
+ > & {
98
+ showCloseButton?: boolean;
99
+ }) {
100
+ return <div
101
+ data-slot="dialog-footer"
102
+ className={cn(
103
+ '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end',
104
+ className,
105
+ )}
106
+ {...props}
107
+ >
108
+ {children}
109
+ {showCloseButton && <DialogClose variant="outline">Close</DialogClose>}
110
+ </div>;
111
+ }
112
+
113
+ export function DialogTitle({ className, ...props }: Omit<Record<string, any>, 'slot'>) {
114
+ return <Heading
115
+ slot="title"
116
+ data-slot="dialog-title"
117
+ className={cn('text-base leading-none font-medium', className)}
118
+ {...props}
119
+ />;
120
+ }
121
+
122
+ export function DialogDescription({ className, ...props }: Omit<HostProps<'div'>, 'slot'>) {
123
+ return <div
124
+ data-slot="dialog-description"
125
+ className={cn(
126
+ 'text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
127
+ className,
128
+ )}
129
+ {...props}
130
+ />;
131
+ }
@@ -0,0 +1,79 @@
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
+ export function Empty({ className, ...props }: HostProps<'div'>) {
7
+ return <div
8
+ data-slot="empty"
9
+ className={cn(
10
+ 'flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance',
11
+ className,
12
+ )}
13
+ {...props}
14
+ />;
15
+ }
16
+
17
+ export function EmptyHeader({ className, ...props }: HostProps<'div'>) {
18
+ return <div
19
+ data-slot="empty-header"
20
+ className={cn('flex max-w-sm flex-col items-center gap-2', className)}
21
+ {...props}
22
+ />;
23
+ }
24
+
25
+ const emptyMediaVariants = cva(
26
+ 'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
27
+ {
28
+ variants: {
29
+ variant: {
30
+ default: 'bg-transparent',
31
+ icon: 'flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*=\'size-\'])]:size-4',
32
+ },
33
+ },
34
+ defaultVariants: {
35
+ variant: 'default',
36
+ },
37
+ },
38
+ );
39
+
40
+ export function EmptyMedia({ className, variant = 'default', ...props }: HostProps<
41
+ 'div'
42
+ > & VariantProps<typeof emptyMediaVariants>) {
43
+ return <div
44
+ data-slot="empty-icon"
45
+ data-variant={variant}
46
+ className={cn(emptyMediaVariants({ variant, className }))}
47
+ {...props}
48
+ />;
49
+ }
50
+
51
+ export function EmptyTitle({ className, ...props }: HostProps<'div'>) {
52
+ return <div
53
+ data-slot="empty-title"
54
+ className={cn('text-sm font-medium tracking-tight', className)}
55
+ {...props}
56
+ />;
57
+ }
58
+
59
+ export function EmptyDescription({ className, ...props }: HostProps<'p'>) {
60
+ return <div
61
+ data-slot="empty-description"
62
+ className={cn(
63
+ 'text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
64
+ className,
65
+ )}
66
+ {...props}
67
+ />;
68
+ }
69
+
70
+ export function EmptyContent({ className, ...props }: HostProps<'div'>) {
71
+ return <div
72
+ data-slot="empty-content"
73
+ className={cn(
74
+ 'flex w-full max-w-sm min-w-0 flex-col items-center gap-2.5 text-sm text-balance',
75
+ className,
76
+ )}
77
+ {...props}
78
+ />;
79
+ }
@@ -0,0 +1,170 @@
1
+ import { type OctaneNode, useMemo } 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
+ import { Label } from './label.tsrx';
7
+ import { Separator } from './separator.tsrx';
8
+
9
+ export function FieldSet({ className, ...props }: HostProps<'fieldset'>) {
10
+ return <fieldset
11
+ data-slot="field-set"
12
+ className={cn(
13
+ 'flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
14
+ className,
15
+ )}
16
+ {...props}
17
+ />;
18
+ }
19
+
20
+ export function FieldLegend({ className, variant = 'legend', ...props }: HostProps<'legend'> & {
21
+ variant?: 'legend' | 'label';
22
+ }) {
23
+ return <legend
24
+ data-slot="field-legend"
25
+ data-variant={variant}
26
+ className={cn(
27
+ 'mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',
28
+ className,
29
+ )}
30
+ {...props}
31
+ />;
32
+ }
33
+
34
+ export function FieldGroup({ className, ...props }: HostProps<'div'>) {
35
+ return <div
36
+ data-slot="field-group"
37
+ className={cn(
38
+ 'group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4',
39
+ className,
40
+ )}
41
+ {...props}
42
+ />;
43
+ }
44
+
45
+ const fieldVariants = cva('group/field flex w-full gap-2 data-[invalid=true]:text-destructive', {
46
+ variants: {
47
+ orientation: {
48
+ vertical: 'flex-col *:w-full [&>.sr-only]:w-auto',
49
+ horizontal: 'flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
50
+ responsive: 'flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
51
+ },
52
+ },
53
+ defaultVariants: {
54
+ orientation: 'vertical',
55
+ },
56
+ });
57
+
58
+ export function Field({ className, orientation = 'vertical', ...props }: HostProps<
59
+ 'div'
60
+ > & VariantProps<typeof fieldVariants>) {
61
+ return <div
62
+ role="group"
63
+ data-slot="field"
64
+ data-orientation={orientation}
65
+ className={cn(fieldVariants({ orientation }), className)}
66
+ {...props}
67
+ />;
68
+ }
69
+
70
+ export function FieldContent({ className, ...props }: HostProps<'div'>) {
71
+ return <div
72
+ data-slot="field-content"
73
+ className={cn('group/field-content flex flex-1 flex-col gap-0.5 leading-snug', className)}
74
+ {...props}
75
+ />;
76
+ }
77
+
78
+ export function FieldLabel({ className, ...props }: Record<string, any>) {
79
+ return <Label
80
+ data-slot="field-label"
81
+ className={cn(
82
+ 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-data-selected:border-primary/30 has-data-selected:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 dark:has-data-selected:border-primary/20 dark:has-data-selected:bg-primary/10',
83
+ 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',
84
+ className,
85
+ )}
86
+ {...props}
87
+ />;
88
+ }
89
+
90
+ export function FieldTitle({ className, ...props }: HostProps<'div'>) {
91
+ return <div
92
+ data-slot="field-label"
93
+ className={cn(
94
+ 'flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50',
95
+ className,
96
+ )}
97
+ {...props}
98
+ />;
99
+ }
100
+
101
+ export function FieldDescription({ className, ...props }: HostProps<'p'>) {
102
+ return <p
103
+ data-slot="field-description"
104
+ className={cn(
105
+ 'text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5',
106
+ 'last:mt-0 nth-last-2:-mt-1',
107
+ '[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
108
+ className,
109
+ )}
110
+ {...props}
111
+ />;
112
+ }
113
+
114
+ export function FieldSeparator({ children, className, ...props }: HostProps<'div'> & {
115
+ children?: OctaneNode;
116
+ }) {
117
+ return <div
118
+ data-slot="field-separator"
119
+ data-content={!!children}
120
+ className={cn(
121
+ 'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',
122
+ className,
123
+ )}
124
+ {...props}
125
+ >
126
+ <Separator className="absolute inset-0 top-1/2" />
127
+ {children &&
128
+ <span
129
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
130
+ data-slot="field-separator-content"
131
+ >{children}</span>}
132
+ </div>;
133
+ }
134
+
135
+ export function FieldError({ className, children, errors, ...props }: HostProps<'div'> & {
136
+ errors?: Array<{ message?: string } | undefined>;
137
+ }) {
138
+ const content = useMemo(() => {
139
+ if (children) {
140
+ return children;
141
+ }
142
+
143
+ if (!errors?.length) {
144
+ return null;
145
+ }
146
+
147
+ const uniqueErrors = [
148
+ ...new Map(errors.map((error) => [error?.message, error])).values(),
149
+ ];
150
+
151
+ if (uniqueErrors?.length == 1) {
152
+ return uniqueErrors[0]?.message;
153
+ }
154
+
155
+ return <ul className="ml-4 flex list-disc flex-col gap-1">{uniqueErrors.map(
156
+ (error, index) => error?.message && <li key={index}>{error.message}</li>,
157
+ )}</ul>;
158
+ }, [children, errors]);
159
+
160
+ if (!content) {
161
+ return null;
162
+ }
163
+
164
+ return <div
165
+ role="alert"
166
+ data-slot="field-error"
167
+ className={cn('text-sm font-normal text-destructive', className)}
168
+ {...props}
169
+ >{content}</div>;
170
+ }
@@ -0,0 +1,28 @@
1
+ // React Aria base input — structure from upstream `bases/aria/ui/input.tsx`
2
+ // (RAC Input, className routed through composeRenderProps so a caller's render
3
+ // prop still receives its render values). Class strings are the package's
4
+ // shipped utilities-inlined flavor, matching the Radix base.
5
+ //
6
+ // OCTANE DIVERGENCE: per-keystroke handling is the native `onInput` event;
7
+ // native `change` keeps its commit-on-blur meaning.
8
+ import { composeRenderProps, Input as InputPrimitive } from '@octanejs/aria/components';
9
+ import type { InputProps as InputPrimitiveProps } from '@octanejs/aria/components';
10
+
11
+ import { cn } from '../../../lib/utils';
12
+
13
+ export interface InputProps extends InputPrimitiveProps {}
14
+
15
+ export function Input({ className, type, ...props }: InputProps) @{
16
+ <InputPrimitive
17
+ type={type}
18
+ data-slot="input"
19
+ className={composeRenderProps(
20
+ className,
21
+ (resolved: string | undefined) => cn(
22
+ 'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
23
+ resolved,
24
+ ),
25
+ )}
26
+ {...props}
27
+ />
28
+ }
@@ -0,0 +1,148 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import { Link as LinkPrimitive, type LinkProps } from '@octanejs/aria/components';
3
+
4
+ import { cn } from '../../../lib/utils';
5
+ import type { HostProps } from '../../../lib/types';
6
+ import { Separator } from './separator.tsrx';
7
+
8
+ export function ItemGroup({ className, ...props }: HostProps<'div'>) {
9
+ return <div
10
+ role="list"
11
+ data-slot="item-group"
12
+ className={cn(
13
+ 'group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2',
14
+ className,
15
+ )}
16
+ {...props}
17
+ />;
18
+ }
19
+
20
+ export function ItemSeparator({ className, ...props }: Record<string, any>) {
21
+ return <Separator
22
+ data-slot="item-separator"
23
+ orientation="horizontal"
24
+ className={cn('my-2', className)}
25
+ {...props}
26
+ />;
27
+ }
28
+
29
+ const itemVariants = cva(
30
+ 'group/item flex w-full flex-wrap items-center rounded-lg border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted',
31
+ {
32
+ variants: {
33
+ variant: {
34
+ default: 'border-transparent',
35
+ outline: 'border-border',
36
+ muted: 'border-transparent bg-muted/50',
37
+ },
38
+ size: {
39
+ default: 'gap-2.5 px-3 py-2.5',
40
+ sm: 'gap-2.5 px-3 py-2.5',
41
+ xs: 'gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0',
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ variant: 'default',
46
+ size: 'default',
47
+ },
48
+ },
49
+ );
50
+
51
+ export function Item({ className, variant = 'default', size = 'default', ...props }: Omit<
52
+ LinkProps,
53
+ 'children'
54
+ > & Record<string, unknown> & VariantProps<typeof itemVariants>) {
55
+ const Element =
56
+ 'href' in props ? LinkPrimitive : 'div';
57
+ return <Element
58
+ data-slot="item"
59
+ data-variant={variant}
60
+ data-size={size}
61
+ className={cn(itemVariants({ variant, size, className }))}
62
+ {...props}
63
+ />;
64
+ }
65
+
66
+ const itemMediaVariants = cva(
67
+ 'flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none',
68
+ {
69
+ variants: {
70
+ variant: {
71
+ default: 'bg-transparent',
72
+ icon: '[&_svg:not([class*=\'size-\'])]:size-4',
73
+ image: 'size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover',
74
+ },
75
+ },
76
+ defaultVariants: {
77
+ variant: 'default',
78
+ },
79
+ },
80
+ );
81
+
82
+ export function ItemMedia({ className, variant = 'default', ...props }: HostProps<
83
+ 'div'
84
+ > & VariantProps<typeof itemMediaVariants>) {
85
+ return <div
86
+ data-slot="item-media"
87
+ data-variant={variant}
88
+ className={cn(itemMediaVariants({ variant, className }))}
89
+ {...props}
90
+ />;
91
+ }
92
+
93
+ export function ItemContent({ className, ...props }: HostProps<'div'>) {
94
+ return <div
95
+ data-slot="item-content"
96
+ className={cn(
97
+ 'flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none',
98
+ className,
99
+ )}
100
+ {...props}
101
+ />;
102
+ }
103
+
104
+ export function ItemTitle({ className, ...props }: HostProps<'div'>) {
105
+ return <div
106
+ data-slot="item-title"
107
+ className={cn(
108
+ 'line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4',
109
+ className,
110
+ )}
111
+ {...props}
112
+ />;
113
+ }
114
+
115
+ export function ItemDescription({ className, ...props }: HostProps<'p'>) {
116
+ return <p
117
+ data-slot="item-description"
118
+ className={cn(
119
+ 'line-clamp-2 text-left text-sm leading-normal font-normal text-muted-foreground group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
120
+ className,
121
+ )}
122
+ {...props}
123
+ />;
124
+ }
125
+
126
+ export function ItemActions({ className, ...props }: HostProps<'div'>) {
127
+ return <div
128
+ data-slot="item-actions"
129
+ className={cn('flex items-center gap-2', className)}
130
+ {...props}
131
+ />;
132
+ }
133
+
134
+ export function ItemHeader({ className, ...props }: HostProps<'div'>) {
135
+ return <div
136
+ data-slot="item-header"
137
+ className={cn('flex basis-full items-center justify-between gap-2', className)}
138
+ {...props}
139
+ />;
140
+ }
141
+
142
+ export function ItemFooter({ className, ...props }: HostProps<'div'>) {
143
+ return <div
144
+ data-slot="item-footer"
145
+ className={cn('flex basis-full items-center justify-between gap-2', className)}
146
+ {...props}
147
+ />;
148
+ }
@@ -0,0 +1,30 @@
1
+ // React Aria base kbd — structure from upstream `bases/aria/ui/kbd.tsx`, which
2
+ // renders BOTH parts through RAC's Keyboard primitive (the Radix base uses a
3
+ // plain <kbd> host for each; upstream likewise renders KbdGroup as a keyboard
4
+ // element rather than a <div>). Class strings are the package's shipped
5
+ // utilities-inlined flavor, matching the Radix base.
6
+ import { Keyboard as KbdPrimitive } from '@octanejs/aria/components';
7
+
8
+ import { cn } from '../../../lib/utils';
9
+ import type { HostProps } from '../../../lib/types';
10
+
11
+ export interface KbdProps extends HostProps<'kbd'> {}
12
+
13
+ export function Kbd({ className, ...props }: KbdProps) @{
14
+ <KbdPrimitive
15
+ data-slot="kbd"
16
+ className={cn(
17
+ 'pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*=\'size-\'])]:size-3',
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ }
23
+
24
+ export function KbdGroup({ className, ...props }: KbdProps) @{
25
+ <KbdPrimitive
26
+ data-slot="kbd-group"
27
+ className={cn('inline-flex items-center gap-1', className)}
28
+ {...props}
29
+ />
30
+ }
@@ -0,0 +1,22 @@
1
+ import { LabelContext, Label as LabelPrimitive, type LabelProps } from '@octanejs/aria/components';
2
+
3
+ import { cn } from '../../../lib/utils';
4
+
5
+ export function Label({ className, htmlFor, slot, ...props }: LabelProps) {
6
+ const label = <LabelPrimitive
7
+ data-slot="label"
8
+ className={cn(
9
+ 'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 peer-data-disabled:opacity-50',
10
+ className,
11
+ )}
12
+ {...props}
13
+ htmlFor={htmlFor}
14
+ slot={slot}
15
+ />;
16
+
17
+ if (htmlFor && slot === undefined) {
18
+ return <LabelContext.Provider value={null}>{label}</LabelContext.Provider>;
19
+ }
20
+
21
+ return label;
22
+ }
@@ -0,0 +1,44 @@
1
+ import { cn } from '../../../lib/utils';
2
+ import type { HostProps } from '../../../lib/types';
3
+ import { ChevronDownIcon } from '@octanejs/lucide';
4
+
5
+ type NativeSelectProps =
6
+ Omit<HostProps<'select'>, 'size'> & {
7
+ size?: 'sm' | 'default';
8
+ };
9
+
10
+ export function NativeSelect({ className, size = 'default', ...props }: NativeSelectProps) {
11
+ return <div
12
+ className={cn('group/native-select relative w-fit has-[select:disabled]:opacity-50', className)}
13
+ data-slot="native-select-wrapper"
14
+ data-size={size}
15
+ >
16
+ <select
17
+ data-slot="native-select"
18
+ data-size={size}
19
+ className="h-8 w-full min-w-0 appearance-none rounded-lg border border-input bg-transparent py-1 pr-8 pl-2.5 text-sm transition-colors outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-[size=sm]:py-0.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40"
20
+ {...props}
21
+ />
22
+ <ChevronDownIcon
23
+ className="pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none"
24
+ aria-hidden="true"
25
+ data-slot="native-select-icon"
26
+ />
27
+ </div>;
28
+ }
29
+
30
+ export function NativeSelectOption({ className, ...props }: HostProps<'option'>) {
31
+ return <option
32
+ data-slot="native-select-option"
33
+ className={cn('bg-[Canvas] text-[CanvasText]', className)}
34
+ {...props}
35
+ />;
36
+ }
37
+
38
+ export function NativeSelectOptGroup({ className, ...props }: HostProps<'optgroup'>) {
39
+ return <optgroup
40
+ data-slot="native-select-optgroup"
41
+ className={cn('bg-[Canvas] text-[CanvasText]', className)}
42
+ {...props}
43
+ />;
44
+ }