@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.
- package/package.json +109 -10
- package/src/bases/base-ui/ui/accordion.tsrx +71 -0
- package/src/bases/base-ui/ui/alert-dialog.tsrx +149 -0
- package/src/bases/base-ui/ui/alert.tsrx +68 -0
- package/src/bases/base-ui/ui/aspect-ratio.tsrx +24 -0
- package/src/bases/base-ui/ui/button.tsrx +69 -0
- package/src/bases/base-ui/ui/card.tsrx +76 -0
- package/src/bases/base-ui/ui/checkbox.tsrx +43 -0
- package/src/bases/base-ui/ui/dialog.tsrx +137 -0
- package/src/bases/base-ui/ui/empty.tsrx +88 -0
- package/src/bases/base-ui/ui/input.tsrx +28 -0
- package/src/bases/base-ui/ui/kbd.tsrx +27 -0
- package/src/bases/base-ui/ui/label.tsrx +31 -0
- package/src/bases/base-ui/ui/native-select.tsrx +54 -0
- package/src/bases/base-ui/ui/popover.tsrx +85 -0
- package/src/bases/base-ui/ui/radio-group.tsrx +50 -0
- package/src/bases/base-ui/ui/separator.tsrx +34 -0
- package/src/bases/base-ui/ui/skeleton.tsrx +17 -0
- package/src/bases/base-ui/ui/spinner.tsrx +30 -0
- package/src/bases/base-ui/ui/switch.tsrx +43 -0
- package/src/bases/base-ui/ui/textarea.tsrx +22 -0
- package/src/bases/base-ui/ui/tooltip.tsrx +73 -0
- package/src/{ui → bases/radix/ui}/accordion.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/alert-dialog.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/alert.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/avatar.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/badge.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/breadcrumb.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/button.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/card.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/checkbox.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/context-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/dialog.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/dropdown-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/empty.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/field.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/hover-card.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/input.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/item.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/kbd.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/label.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/menubar.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/native-select.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/navigation-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/pagination.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/popover.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/progress.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/radio-group.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/scroll-area.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/select.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/separator.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/sheet.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/sidebar.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/skeleton.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/slider.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/spinner.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/switch.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/table.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/tabs.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/textarea.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/toggle-group.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/toggle.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/tooltip.tsrx +1 -1
- package/src/bases/react-aria/ui/accordion.tsrx +73 -0
- package/src/bases/react-aria/ui/alert-dialog.tsrx +154 -0
- package/src/bases/react-aria/ui/alert.tsrx +60 -0
- package/src/bases/react-aria/ui/aspect-ratio.tsrx +13 -0
- package/src/bases/react-aria/ui/avatar.tsrx +83 -0
- package/src/bases/react-aria/ui/badge.tsrx +48 -0
- package/src/bases/react-aria/ui/breadcrumb.tsrx +100 -0
- package/src/bases/react-aria/ui/button.tsrx +96 -0
- package/src/bases/react-aria/ui/card.tsrx +73 -0
- package/src/bases/react-aria/ui/checkbox.tsrx +37 -0
- package/src/bases/react-aria/ui/collapsible.tsrx +20 -0
- package/src/bases/react-aria/ui/dialog.tsrx +131 -0
- package/src/bases/react-aria/ui/empty.tsrx +79 -0
- package/src/bases/react-aria/ui/field.tsrx +170 -0
- package/src/bases/react-aria/ui/input.tsrx +28 -0
- package/src/bases/react-aria/ui/item.tsrx +148 -0
- package/src/bases/react-aria/ui/kbd.tsrx +30 -0
- package/src/bases/react-aria/ui/label.tsrx +22 -0
- package/src/bases/react-aria/ui/native-select.tsrx +44 -0
- package/src/bases/react-aria/ui/pagination.tsrx +92 -0
- package/src/bases/react-aria/ui/popover.tsrx +57 -0
- package/src/bases/react-aria/ui/radio-group.tsrx +46 -0
- package/src/bases/react-aria/ui/scroll-area.tsrx +14 -0
- package/src/bases/react-aria/ui/separator.tsrx +28 -0
- package/src/bases/react-aria/ui/sheet.tsrx +143 -0
- package/src/bases/react-aria/ui/skeleton.tsrx +10 -0
- package/src/bases/react-aria/ui/slider.tsrx +56 -0
- package/src/bases/react-aria/ui/spinner.tsrx +28 -0
- package/src/bases/react-aria/ui/switch.tsrx +38 -0
- package/src/bases/react-aria/ui/table.tsrx +98 -0
- package/src/bases/react-aria/ui/tabs.tsrx +67 -0
- package/src/bases/react-aria/ui/textarea.tsrx +26 -0
- package/src/bases/react-aria/ui/toggle.tsrx +38 -0
- package/src/index.ts +0 -263
- /package/src/{ui → bases/radix/ui}/aspect-ratio.tsrx +0 -0
- /package/src/{ui → bases/radix/ui}/collapsible.tsrx +0 -0
- /package/src/{ui → bases/radix/ui}/sonner.tsrx +0 -0
|
@@ -0,0 +1,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
|
+
}
|