@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,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
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { cn } from '../../../lib/utils';
|
|
2
|
+
import type { HostProps } from '../../../lib/types';
|
|
3
|
+
import { LinkButton } from './button.tsrx';
|
|
4
|
+
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from '@octanejs/lucide';
|
|
5
|
+
|
|
6
|
+
export function Pagination({ className, ...props }: HostProps<'nav'>) {
|
|
7
|
+
return <nav
|
|
8
|
+
role="navigation"
|
|
9
|
+
aria-label="pagination"
|
|
10
|
+
data-slot="pagination"
|
|
11
|
+
className={cn('mx-auto flex w-full justify-center', className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function PaginationContent({ className, ...props }: HostProps<'ul'>) {
|
|
17
|
+
return <ul
|
|
18
|
+
data-slot="pagination-content"
|
|
19
|
+
className={cn('flex items-center gap-0.5', className)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function PaginationItem({ ...props }: HostProps<'li'>) {
|
|
25
|
+
return <li data-slot="pagination-item" {...props} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type PaginationLinkProps =
|
|
29
|
+
{
|
|
30
|
+
isActive?: boolean;
|
|
31
|
+
} & Omit<Record<string, any>, 'variant'>;
|
|
32
|
+
|
|
33
|
+
export function PaginationLink({
|
|
34
|
+
className,
|
|
35
|
+
isActive,
|
|
36
|
+
size = 'icon',
|
|
37
|
+
...props
|
|
38
|
+
}: PaginationLinkProps) {
|
|
39
|
+
return <LinkButton
|
|
40
|
+
variant={isActive ? 'outline' : 'ghost'}
|
|
41
|
+
size={size}
|
|
42
|
+
className={cn(className)}
|
|
43
|
+
aria-current={isActive ? 'page' : undefined}
|
|
44
|
+
data-slot="pagination-link"
|
|
45
|
+
data-active={isActive}
|
|
46
|
+
{...props}
|
|
47
|
+
/>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function PaginationPrevious({ className, text = 'Previous', ...props }: Record<
|
|
51
|
+
string,
|
|
52
|
+
any
|
|
53
|
+
> & { text?: string }) {
|
|
54
|
+
return <PaginationLink
|
|
55
|
+
aria-label="Go to previous page"
|
|
56
|
+
size="default"
|
|
57
|
+
className={cn('pl-1.5!', className)}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
<ChevronLeftIcon data-icon="inline-start" />
|
|
61
|
+
<span className="hidden sm:block">{text}</span>
|
|
62
|
+
</PaginationLink>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function PaginationNext({ className, text = 'Next', ...props }: Record<string, any> & {
|
|
66
|
+
text?: string;
|
|
67
|
+
}) {
|
|
68
|
+
return <PaginationLink
|
|
69
|
+
aria-label="Go to next page"
|
|
70
|
+
size="default"
|
|
71
|
+
className={cn('pr-1.5!', className)}
|
|
72
|
+
{...props}
|
|
73
|
+
>
|
|
74
|
+
<span className="hidden sm:block">{text}</span>
|
|
75
|
+
<ChevronRightIcon data-icon="inline-end" />
|
|
76
|
+
</PaginationLink>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function PaginationEllipsis({ className, ...props }: HostProps<'span'>) {
|
|
80
|
+
return <span
|
|
81
|
+
aria-hidden
|
|
82
|
+
data-slot="pagination-ellipsis"
|
|
83
|
+
className={cn(
|
|
84
|
+
'flex size-8 items-center justify-center [&_svg:not([class*=\'size-\'])]:size-4',
|
|
85
|
+
className,
|
|
86
|
+
)}
|
|
87
|
+
{...props}
|
|
88
|
+
>
|
|
89
|
+
<MoreHorizontalIcon />
|
|
90
|
+
<span className="sr-only">More pages</span>
|
|
91
|
+
</span>;
|
|
92
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DialogTrigger,
|
|
3
|
+
Heading,
|
|
4
|
+
Popover as PopoverPrimitive,
|
|
5
|
+
type DialogTriggerProps,
|
|
6
|
+
type PopoverProps as PopoverPrimitiveProps,
|
|
7
|
+
} from '@octanejs/aria/components';
|
|
8
|
+
import type { HeadingProps } from '@octanejs/aria/components';
|
|
9
|
+
|
|
10
|
+
import { cn } from '../../../lib/utils';
|
|
11
|
+
import type { HostProps } from '../../../lib/types';
|
|
12
|
+
|
|
13
|
+
export function PopoverTrigger({ children, ...props }: DialogTriggerProps) {
|
|
14
|
+
return <DialogTrigger data-slot="popover-trigger" {...props}>{children}</DialogTrigger>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function Popover({
|
|
18
|
+
className,
|
|
19
|
+
placement = 'bottom',
|
|
20
|
+
offset = 4,
|
|
21
|
+
crossOffset = 0,
|
|
22
|
+
...props
|
|
23
|
+
}: Omit<PopoverPrimitiveProps, 'className'> & {
|
|
24
|
+
className?: string;
|
|
25
|
+
}) {
|
|
26
|
+
return <PopoverPrimitive
|
|
27
|
+
data-slot="popover-content"
|
|
28
|
+
placement={placement}
|
|
29
|
+
offset={offset}
|
|
30
|
+
crossOffset={crossOffset}
|
|
31
|
+
className={cn(
|
|
32
|
+
'z-50 flex w-72 origin-(--trigger-anchor-point) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-entering:animate-in data-entering:fade-in-0 data-entering:zoom-in-95 data-exiting:animate-out data-exiting:fade-out-0 data-exiting:zoom-out-95 data-[placement=bottom]:slide-in-from-top-2 data-[placement=left]:slide-in-from-right-2 data-[placement=right]:slide-in-from-left-2 data-[placement=top]:slide-in-from-bottom-2',
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function PopoverHeader({ className, ...props }: HostProps<'div'>) {
|
|
40
|
+
return <div
|
|
41
|
+
data-slot="popover-header"
|
|
42
|
+
className={cn('flex flex-col gap-0.5 text-sm', className)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function PopoverTitle({ className, ...props }: HeadingProps) {
|
|
48
|
+
return <Heading data-slot="popover-title" className={cn('font-medium', className)} {...props} />;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function PopoverDescription({ className, ...props }: HostProps<'div'>) {
|
|
52
|
+
return <div
|
|
53
|
+
data-slot="popover-description"
|
|
54
|
+
className={cn('text-muted-foreground', className)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>;
|
|
57
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
composeRenderProps,
|
|
9
|
+
RadioGroup as RadioGroupPrimitive,
|
|
10
|
+
Radio as RadioPrimitive,
|
|
11
|
+
type RadioGroupProps,
|
|
12
|
+
type RadioRenderProps,
|
|
13
|
+
type RadioProps,
|
|
14
|
+
} from '@octanejs/aria/components';
|
|
15
|
+
|
|
16
|
+
import { cn } from '../../../lib/utils';
|
|
17
|
+
|
|
18
|
+
export function RadioGroup({ className, ...props }: RadioGroupProps) {
|
|
19
|
+
return <RadioGroupPrimitive
|
|
20
|
+
data-slot="radio-group"
|
|
21
|
+
className={cn('grid w-full gap-2', className)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function RadioGroupItem({ className, children, ...props }: RadioProps) {
|
|
27
|
+
return <RadioPrimitive
|
|
28
|
+
data-slot="radio-group-item"
|
|
29
|
+
className={cn(
|
|
30
|
+
'group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none 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',
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
>{composeRenderProps(
|
|
35
|
+
children,
|
|
36
|
+
(children, { isSelected }: RadioRenderProps) => <>
|
|
37
|
+
<span data-slot="radio-group-indicator" className="flex size-4 items-center justify-center">
|
|
38
|
+
{isSelected &&
|
|
39
|
+
<span
|
|
40
|
+
className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground"
|
|
41
|
+
/>}
|
|
42
|
+
</span>
|
|
43
|
+
{children}
|
|
44
|
+
</>,
|
|
45
|
+
)}</RadioPrimitive>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cn } from '../../../lib/utils';
|
|
2
|
+
import type { HostProps } from '../../../lib/types';
|
|
3
|
+
|
|
4
|
+
export function ScrollArea({ className, children, ...props }: HostProps<'div'>) {
|
|
5
|
+
// Use native scrollbar-width and scrollbar-color to customize the scrollbar.
|
|
6
|
+
return <div
|
|
7
|
+
data-slot="scroll-area"
|
|
8
|
+
className={cn(
|
|
9
|
+
'relative [scrollbar-width:thin] [scrollbar-color:var(--color-border)_transparent] overflow-auto outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1',
|
|
10
|
+
className,
|
|
11
|
+
)}
|
|
12
|
+
{...props}
|
|
13
|
+
>{children}</div>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// React Aria base separator — structure from upstream `bases/aria/ui/separator.tsx`
|
|
2
|
+
// (RAC Separator).
|
|
3
|
+
//
|
|
4
|
+
// The class string is NOT the Radix base's. RAC's Separator publishes
|
|
5
|
+
// orientation as `aria-orientation`, where Radix publishes `data-orientation`,
|
|
6
|
+
// so the orientation-conditional utilities must key off the attribute this
|
|
7
|
+
// primitive actually emits — upstream's own bases diverge here for the same
|
|
8
|
+
// reason. The non-conditional utilities stay in step with the Radix base.
|
|
9
|
+
import { Separator as SeparatorPrimitive } from '@octanejs/aria/components';
|
|
10
|
+
|
|
11
|
+
import { cn } from '../../../lib/utils';
|
|
12
|
+
|
|
13
|
+
export interface SeparatorProps extends Record<string, unknown> {
|
|
14
|
+
className?: string;
|
|
15
|
+
orientation?: 'horizontal' | 'vertical';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function Separator({ className, orientation = 'horizontal', ...props }: SeparatorProps) @{
|
|
19
|
+
<SeparatorPrimitive
|
|
20
|
+
data-slot="separator"
|
|
21
|
+
orientation={orientation}
|
|
22
|
+
className={cn(
|
|
23
|
+
'block shrink-0 border-0 bg-border aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=vertical]:w-px aria-[orientation=vertical]:self-stretch [:is(hr)]:h-px [:is(hr)]:w-full',
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// OCTANE ADAPTATION: RAC does not publish a `ModalProps` type (upstream writes
|
|
2
|
+
// `React.ComponentProps<typeof Modal>` for exactly that reason), so the one
|
|
3
|
+
// prop borrowed from Modal is spelled out instead of picked.
|
|
4
|
+
import { type OctaneNode } from 'octane';
|
|
5
|
+
import {
|
|
6
|
+
Heading,
|
|
7
|
+
ModalOverlay as ModalOverlayPrimitive,
|
|
8
|
+
Modal as ModalPrimitive,
|
|
9
|
+
Dialog as SheetPrimitive,
|
|
10
|
+
DialogTrigger as SheetTriggerPrimitive,
|
|
11
|
+
type ModalOverlayProps as ModalOverlayPrimitiveProps,
|
|
12
|
+
type DialogProps as SheetPrimitiveProps,
|
|
13
|
+
type DialogTriggerProps as SheetTriggerPrimitiveProps,
|
|
14
|
+
} from '@octanejs/aria/components';
|
|
15
|
+
import type { HeadingProps } from '@octanejs/aria/components';
|
|
16
|
+
|
|
17
|
+
import { cn } from '../../../lib/utils';
|
|
18
|
+
import type { HostProps } from '../../../lib/types';
|
|
19
|
+
import { Button } from './button.tsrx';
|
|
20
|
+
import { XIcon } from '@octanejs/lucide';
|
|
21
|
+
|
|
22
|
+
export function SheetTrigger({ ...props }: SheetTriggerPrimitiveProps) {
|
|
23
|
+
return <SheetTriggerPrimitive data-slot="sheet-trigger" {...props} />;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function SheetClose({ className, variant = 'outline', size = 'default', ...props }: Record<
|
|
27
|
+
string,
|
|
28
|
+
any
|
|
29
|
+
>) {
|
|
30
|
+
return <Button
|
|
31
|
+
slot="close"
|
|
32
|
+
data-slot="sheet-close"
|
|
33
|
+
variant={variant}
|
|
34
|
+
size={size}
|
|
35
|
+
className={cn(className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function SheetOverlay({ className, children, ...props }: Omit<
|
|
41
|
+
ModalOverlayPrimitiveProps,
|
|
42
|
+
'className' | 'children'
|
|
43
|
+
> & {
|
|
44
|
+
className?: string;
|
|
45
|
+
children: OctaneNode;
|
|
46
|
+
}) {
|
|
47
|
+
return <ModalOverlayPrimitive
|
|
48
|
+
data-slot="sheet-overlay"
|
|
49
|
+
isDismissable
|
|
50
|
+
className={cn(
|
|
51
|
+
'fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-entering:opacity-0 data-exiting:opacity-0 supports-backdrop-filter:backdrop-blur-xs',
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
>{children}</ModalOverlayPrimitive>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function Sheet({
|
|
59
|
+
className,
|
|
60
|
+
children,
|
|
61
|
+
side = 'right',
|
|
62
|
+
showCloseButton = true,
|
|
63
|
+
...props
|
|
64
|
+
}: Omit<ModalOverlayPrimitiveProps, 'className' | 'children'> & { isDismissable?: boolean } & {
|
|
65
|
+
className?: string;
|
|
66
|
+
children: OctaneNode;
|
|
67
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
68
|
+
showCloseButton?: boolean;
|
|
69
|
+
}) {
|
|
70
|
+
return <SheetOverlay {...props}>
|
|
71
|
+
<ModalPrimitive
|
|
72
|
+
data-slot="sheet-content"
|
|
73
|
+
data-side={side}
|
|
74
|
+
className={cn(
|
|
75
|
+
'fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-entering:opacity-0 data-exiting:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-entering:translate-y-[2.5rem] data-[side=bottom]:data-exiting:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-entering:translate-x-[-2.5rem] data-[side=left]:data-exiting:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-entering:translate-x-[2.5rem] data-[side=right]:data-exiting:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-entering:translate-y-[-2.5rem] data-[side=top]:data-exiting:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm',
|
|
76
|
+
className,
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
<SheetPrimitive
|
|
80
|
+
data-slot="sheet"
|
|
81
|
+
className="[display:inherit] h-full max-h-[inherit] [flex-direction:inherit] [gap:inherit] outline-none"
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
{showCloseButton &&
|
|
85
|
+
<SheetClose variant="ghost" className="absolute top-3 right-3" size="icon-sm">
|
|
86
|
+
<XIcon />
|
|
87
|
+
<span className="sr-only">Close</span>
|
|
88
|
+
</SheetClose>}
|
|
89
|
+
</SheetPrimitive>
|
|
90
|
+
</ModalPrimitive>
|
|
91
|
+
</SheetOverlay>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function SheetContent({
|
|
95
|
+
className,
|
|
96
|
+
children,
|
|
97
|
+
side = 'right',
|
|
98
|
+
showCloseButton = true,
|
|
99
|
+
...props
|
|
100
|
+
}: Record<string, any> & {
|
|
101
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
102
|
+
showCloseButton?: boolean;
|
|
103
|
+
}) {
|
|
104
|
+
return <Sheet
|
|
105
|
+
className={className}
|
|
106
|
+
side={side}
|
|
107
|
+
showCloseButton={showCloseButton}
|
|
108
|
+
{...props}
|
|
109
|
+
>{children}</Sheet>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function SheetHeader({ className, ...props }: HostProps<'div'>) {
|
|
113
|
+
return <div
|
|
114
|
+
data-slot="sheet-header"
|
|
115
|
+
className={cn('flex flex-col gap-0.5 p-4', className)}
|
|
116
|
+
{...props}
|
|
117
|
+
/>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function SheetFooter({ className, ...props }: HostProps<'div'>) {
|
|
121
|
+
return <div
|
|
122
|
+
data-slot="sheet-footer"
|
|
123
|
+
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
|
124
|
+
{...props}
|
|
125
|
+
/>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function SheetTitle({ className, ...props }: Omit<HeadingProps, 'slot'>) {
|
|
129
|
+
return <Heading
|
|
130
|
+
slot="title"
|
|
131
|
+
data-slot="sheet-title"
|
|
132
|
+
className={cn('text-base font-medium text-foreground', className)}
|
|
133
|
+
{...props}
|
|
134
|
+
/>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function SheetDescription({ className, ...props }: Omit<HostProps<'div'>, 'slot'>) {
|
|
138
|
+
return <div
|
|
139
|
+
data-slot="sheet-description"
|
|
140
|
+
className={cn('text-sm text-muted-foreground', className)}
|
|
141
|
+
{...props}
|
|
142
|
+
/>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { cn } from '../../../lib/utils';
|
|
2
|
+
import type { HostProps } from '../../../lib/types';
|
|
3
|
+
|
|
4
|
+
export function Skeleton({ className, ...props }: HostProps<'div'>) {
|
|
5
|
+
return <div
|
|
6
|
+
data-slot="skeleton"
|
|
7
|
+
className={cn('animate-pulse rounded-md bg-muted', className)}
|
|
8
|
+
{...props}
|
|
9
|
+
/>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// OCTANE ADAPTATION: octane's renderable type is `OctaneNode = unknown`, and a
|
|
2
|
+
// union containing `unknown` collapses to `unknown`, so RAC's children render
|
|
3
|
+
// prop carries no inferable render values here. Annotating them restores the
|
|
4
|
+
// upstream parameter types without weakening the binding.
|
|
5
|
+
import {
|
|
6
|
+
SliderFill,
|
|
7
|
+
Slider as SliderPrimitive,
|
|
8
|
+
SliderThumb,
|
|
9
|
+
SliderTrack,
|
|
10
|
+
type SliderProps as SliderPrimitiveProps,
|
|
11
|
+
type SliderRenderProps,
|
|
12
|
+
} from '@octanejs/aria/components';
|
|
13
|
+
|
|
14
|
+
import { cn } from '../../../lib/utils';
|
|
15
|
+
|
|
16
|
+
type SliderValue = number | number[];
|
|
17
|
+
type SliderProps<T extends SliderValue = SliderValue> =
|
|
18
|
+
Omit<SliderPrimitiveProps<T>, 'className'> & {
|
|
19
|
+
className?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function Slider<T extends SliderValue = SliderValue>({ className, ...props }: SliderProps<
|
|
23
|
+
T
|
|
24
|
+
>) {
|
|
25
|
+
return <SliderPrimitive
|
|
26
|
+
className={cn(
|
|
27
|
+
'group relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col',
|
|
28
|
+
className,
|
|
29
|
+
)}
|
|
30
|
+
data-slot="slider"
|
|
31
|
+
{...props}
|
|
32
|
+
>
|
|
33
|
+
{({ state }: SliderRenderProps) => {
|
|
34
|
+
return <>
|
|
35
|
+
<SliderTrack
|
|
36
|
+
data-slot="slider-track"
|
|
37
|
+
className="relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1"
|
|
38
|
+
>
|
|
39
|
+
<SliderFill
|
|
40
|
+
data-slot="slider-range"
|
|
41
|
+
className="absolute bg-primary select-none data-horizontal:h-full data-vertical:w-full"
|
|
42
|
+
/>
|
|
43
|
+
</SliderTrack>
|
|
44
|
+
{state.values.map(
|
|
45
|
+
(_: number, index: number) =>
|
|
46
|
+
<SliderThumb
|
|
47
|
+
data-slot="slider-thumb"
|
|
48
|
+
key={index}
|
|
49
|
+
index={index}
|
|
50
|
+
className="relative block size-3 shrink-0 rounded-full border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none group-data-horizontal:top-[50%] group-data-vertical:left-[50%] after:absolute after:-inset-2 hover:ring-3 focus-visible:ring-3 focus-visible:outline-hidden active:ring-3 disabled:pointer-events-none disabled:opacity-50"
|
|
51
|
+
/>,
|
|
52
|
+
)}
|
|
53
|
+
</>;
|
|
54
|
+
}}
|
|
55
|
+
</SliderPrimitive>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// React Aria base spinner. Upstream's aria spinner is presentational — it uses
|
|
2
|
+
// no RAC primitive — so this matches the Radix base apart from its own class
|
|
3
|
+
// string.
|
|
4
|
+
//
|
|
5
|
+
// OCTANE DIVERGENCE: upstream's IconPlaceholder (the CLI-resolved iconLibrary
|
|
6
|
+
// axis) is resolved at port time to the default library, lucide, via
|
|
7
|
+
// @octanejs/lucide. Other icon libraries are a registry-emit concern.
|
|
8
|
+
//
|
|
9
|
+
// The props are a permissive record rather than HostProps<'svg'>: these are
|
|
10
|
+
// forwarded to a lucide component, not applied to a host element, so the host
|
|
11
|
+
// SVG attribute types do not describe them.
|
|
12
|
+
import { Loader2Icon } from '@octanejs/lucide';
|
|
13
|
+
|
|
14
|
+
import { cn } from '../../../lib/utils';
|
|
15
|
+
|
|
16
|
+
export interface SpinnerProps extends Record<string, unknown> {
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function Spinner({ className, ...props }: SpinnerProps) @{
|
|
21
|
+
<Loader2Icon
|
|
22
|
+
data-slot="spinner"
|
|
23
|
+
role="status"
|
|
24
|
+
aria-label="Loading"
|
|
25
|
+
className={cn('size-4 animate-spin', className)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
composeRenderProps,
|
|
9
|
+
Switch as SwitchPrimitive,
|
|
10
|
+
type SwitchProps as SwitchPrimitiveProps,
|
|
11
|
+
type SwitchRenderProps,
|
|
12
|
+
} from '@octanejs/aria/components';
|
|
13
|
+
|
|
14
|
+
import { cn } from '../../../lib/utils';
|
|
15
|
+
|
|
16
|
+
export function Switch({ className, size = 'default', children, ...props }: SwitchPrimitiveProps & {
|
|
17
|
+
size?: 'sm' | 'default';
|
|
18
|
+
}) {
|
|
19
|
+
return <SwitchPrimitive
|
|
20
|
+
data-slot="switch"
|
|
21
|
+
data-size={size}
|
|
22
|
+
className={cn(
|
|
23
|
+
'peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none not-data-selected:bg-input 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 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-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:not-data-selected:bg-input/80 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:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-selected:bg-primary data-disabled:cursor-not-allowed data-disabled:opacity-50',
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
>{composeRenderProps(
|
|
28
|
+
children,
|
|
29
|
+
(children, { isSelected }: SwitchRenderProps) => <>
|
|
30
|
+
<span
|
|
31
|
+
data-slot="switch-thumb"
|
|
32
|
+
data-selected={isSelected || undefined}
|
|
33
|
+
className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=default]/switch:not-data-selected:translate-x-0 group-data-[size=sm]/switch:size-3 group-data-[size=sm]/switch:not-data-selected:translate-x-0 dark:not-data-selected:bg-foreground group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground group-data-[size=default]/switch:data-selected:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-selected:translate-x-[calc(100%-2px)] dark:data-selected:bg-primary-foreground"
|
|
34
|
+
/>
|
|
35
|
+
{children}
|
|
36
|
+
</>,
|
|
37
|
+
)}</SwitchPrimitive>;
|
|
38
|
+
}
|