@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.
Files changed (79) hide show
  1. package/package.json +87 -10
  2. package/src/{ui → bases/radix/ui}/accordion.tsrx +1 -1
  3. package/src/{ui → bases/radix/ui}/alert-dialog.tsrx +1 -1
  4. package/src/{ui → bases/radix/ui}/alert.tsrx +2 -2
  5. package/src/{ui → bases/radix/ui}/avatar.tsrx +1 -1
  6. package/src/{ui → bases/radix/ui}/badge.tsrx +2 -2
  7. package/src/{ui → bases/radix/ui}/breadcrumb.tsrx +1 -1
  8. package/src/{ui → bases/radix/ui}/button.tsrx +2 -2
  9. package/src/{ui → bases/radix/ui}/card.tsrx +2 -2
  10. package/src/{ui → bases/radix/ui}/checkbox.tsrx +1 -1
  11. package/src/{ui → bases/radix/ui}/context-menu.tsrx +1 -1
  12. package/src/{ui → bases/radix/ui}/dialog.tsrx +1 -1
  13. package/src/{ui → bases/radix/ui}/dropdown-menu.tsrx +1 -1
  14. package/src/{ui → bases/radix/ui}/empty.tsrx +1 -1
  15. package/src/{ui → bases/radix/ui}/field.tsrx +1 -1
  16. package/src/{ui → bases/radix/ui}/hover-card.tsrx +1 -1
  17. package/src/{ui → bases/radix/ui}/input.tsrx +2 -2
  18. package/src/{ui → bases/radix/ui}/item.tsrx +1 -1
  19. package/src/{ui → bases/radix/ui}/kbd.tsrx +2 -2
  20. package/src/{ui → bases/radix/ui}/label.tsrx +1 -1
  21. package/src/{ui → bases/radix/ui}/menubar.tsrx +1 -1
  22. package/src/{ui → bases/radix/ui}/native-select.tsrx +1 -1
  23. package/src/{ui → bases/radix/ui}/navigation-menu.tsrx +1 -1
  24. package/src/{ui → bases/radix/ui}/pagination.tsrx +1 -1
  25. package/src/{ui → bases/radix/ui}/popover.tsrx +1 -1
  26. package/src/{ui → bases/radix/ui}/progress.tsrx +1 -1
  27. package/src/{ui → bases/radix/ui}/radio-group.tsrx +1 -1
  28. package/src/{ui → bases/radix/ui}/scroll-area.tsrx +1 -1
  29. package/src/{ui → bases/radix/ui}/select.tsrx +1 -1
  30. package/src/{ui → bases/radix/ui}/separator.tsrx +1 -1
  31. package/src/{ui → bases/radix/ui}/sheet.tsrx +1 -1
  32. package/src/{ui → bases/radix/ui}/sidebar.tsrx +2 -2
  33. package/src/{ui → bases/radix/ui}/skeleton.tsrx +2 -2
  34. package/src/{ui → bases/radix/ui}/slider.tsrx +1 -1
  35. package/src/{ui → bases/radix/ui}/spinner.tsrx +1 -1
  36. package/src/{ui → bases/radix/ui}/switch.tsrx +1 -1
  37. package/src/{ui → bases/radix/ui}/table.tsrx +1 -1
  38. package/src/{ui → bases/radix/ui}/tabs.tsrx +1 -1
  39. package/src/{ui → bases/radix/ui}/textarea.tsrx +2 -2
  40. package/src/{ui → bases/radix/ui}/toggle-group.tsrx +1 -1
  41. package/src/{ui → bases/radix/ui}/toggle.tsrx +1 -1
  42. package/src/{ui → bases/radix/ui}/tooltip.tsrx +1 -1
  43. package/src/bases/react-aria/ui/accordion.tsrx +73 -0
  44. package/src/bases/react-aria/ui/alert-dialog.tsrx +154 -0
  45. package/src/bases/react-aria/ui/alert.tsrx +60 -0
  46. package/src/bases/react-aria/ui/aspect-ratio.tsrx +13 -0
  47. package/src/bases/react-aria/ui/avatar.tsrx +83 -0
  48. package/src/bases/react-aria/ui/badge.tsrx +48 -0
  49. package/src/bases/react-aria/ui/breadcrumb.tsrx +100 -0
  50. package/src/bases/react-aria/ui/button.tsrx +96 -0
  51. package/src/bases/react-aria/ui/card.tsrx +73 -0
  52. package/src/bases/react-aria/ui/checkbox.tsrx +37 -0
  53. package/src/bases/react-aria/ui/collapsible.tsrx +20 -0
  54. package/src/bases/react-aria/ui/dialog.tsrx +131 -0
  55. package/src/bases/react-aria/ui/empty.tsrx +79 -0
  56. package/src/bases/react-aria/ui/field.tsrx +170 -0
  57. package/src/bases/react-aria/ui/input.tsrx +28 -0
  58. package/src/bases/react-aria/ui/item.tsrx +148 -0
  59. package/src/bases/react-aria/ui/kbd.tsrx +30 -0
  60. package/src/bases/react-aria/ui/label.tsrx +22 -0
  61. package/src/bases/react-aria/ui/native-select.tsrx +44 -0
  62. package/src/bases/react-aria/ui/pagination.tsrx +92 -0
  63. package/src/bases/react-aria/ui/popover.tsrx +57 -0
  64. package/src/bases/react-aria/ui/radio-group.tsrx +46 -0
  65. package/src/bases/react-aria/ui/scroll-area.tsrx +14 -0
  66. package/src/bases/react-aria/ui/separator.tsrx +28 -0
  67. package/src/bases/react-aria/ui/sheet.tsrx +143 -0
  68. package/src/bases/react-aria/ui/skeleton.tsrx +10 -0
  69. package/src/bases/react-aria/ui/slider.tsrx +56 -0
  70. package/src/bases/react-aria/ui/spinner.tsrx +28 -0
  71. package/src/bases/react-aria/ui/switch.tsrx +38 -0
  72. package/src/bases/react-aria/ui/table.tsrx +98 -0
  73. package/src/bases/react-aria/ui/tabs.tsrx +67 -0
  74. package/src/bases/react-aria/ui/textarea.tsrx +26 -0
  75. package/src/bases/react-aria/ui/toggle.tsrx +38 -0
  76. package/src/index.ts +0 -263
  77. /package/src/{ui → bases/radix/ui}/aspect-ratio.tsrx +0 -0
  78. /package/src/{ui → bases/radix/ui}/collapsible.tsrx +0 -0
  79. /package/src/{ui → bases/radix/ui}/sonner.tsrx +0 -0
@@ -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
+ }
@@ -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
+ }