@octanejs/shadcn 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +87 -10
- package/src/{ui → bases/radix/ui}/accordion.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/alert-dialog.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/alert.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/avatar.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/badge.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/breadcrumb.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/button.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/card.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/checkbox.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/context-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/dialog.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/dropdown-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/empty.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/field.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/hover-card.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/input.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/item.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/kbd.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/label.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/menubar.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/native-select.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/navigation-menu.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/pagination.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/popover.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/progress.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/radio-group.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/scroll-area.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/select.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/separator.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/sheet.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/sidebar.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/skeleton.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/slider.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/spinner.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/switch.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/table.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/tabs.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/textarea.tsrx +2 -2
- package/src/{ui → bases/radix/ui}/toggle-group.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/toggle.tsrx +1 -1
- package/src/{ui → bases/radix/ui}/tooltip.tsrx +1 -1
- package/src/bases/react-aria/ui/accordion.tsrx +73 -0
- package/src/bases/react-aria/ui/alert-dialog.tsrx +154 -0
- package/src/bases/react-aria/ui/alert.tsrx +60 -0
- package/src/bases/react-aria/ui/aspect-ratio.tsrx +13 -0
- package/src/bases/react-aria/ui/avatar.tsrx +83 -0
- package/src/bases/react-aria/ui/badge.tsrx +48 -0
- package/src/bases/react-aria/ui/breadcrumb.tsrx +100 -0
- package/src/bases/react-aria/ui/button.tsrx +96 -0
- package/src/bases/react-aria/ui/card.tsrx +73 -0
- package/src/bases/react-aria/ui/checkbox.tsrx +37 -0
- package/src/bases/react-aria/ui/collapsible.tsrx +20 -0
- package/src/bases/react-aria/ui/dialog.tsrx +131 -0
- package/src/bases/react-aria/ui/empty.tsrx +79 -0
- package/src/bases/react-aria/ui/field.tsrx +170 -0
- package/src/bases/react-aria/ui/input.tsrx +28 -0
- package/src/bases/react-aria/ui/item.tsrx +148 -0
- package/src/bases/react-aria/ui/kbd.tsrx +30 -0
- package/src/bases/react-aria/ui/label.tsrx +22 -0
- package/src/bases/react-aria/ui/native-select.tsrx +44 -0
- package/src/bases/react-aria/ui/pagination.tsrx +92 -0
- package/src/bases/react-aria/ui/popover.tsrx +57 -0
- package/src/bases/react-aria/ui/radio-group.tsrx +46 -0
- package/src/bases/react-aria/ui/scroll-area.tsrx +14 -0
- package/src/bases/react-aria/ui/separator.tsrx +28 -0
- package/src/bases/react-aria/ui/sheet.tsrx +143 -0
- package/src/bases/react-aria/ui/skeleton.tsrx +10 -0
- package/src/bases/react-aria/ui/slider.tsrx +56 -0
- package/src/bases/react-aria/ui/spinner.tsrx +28 -0
- package/src/bases/react-aria/ui/switch.tsrx +38 -0
- package/src/bases/react-aria/ui/table.tsrx +98 -0
- package/src/bases/react-aria/ui/tabs.tsrx +67 -0
- package/src/bases/react-aria/ui/textarea.tsrx +26 -0
- package/src/bases/react-aria/ui/toggle.tsrx +38 -0
- package/src/index.ts +0 -263
- /package/src/{ui → bases/radix/ui}/aspect-ratio.tsrx +0 -0
- /package/src/{ui → bases/radix/ui}/collapsible.tsrx +0 -0
- /package/src/{ui → bases/radix/ui}/sonner.tsrx +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { 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
|
+
}
|