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