@octanejs/shadcn 0.0.37 → 0.0.39

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 (119) hide show
  1. package/{LICENSE → LICENSE.upstream} +1 -1
  2. package/README.md +22 -19
  3. package/package.json +17 -11
  4. package/src/bases/base-ui/ui/accordion.tsrx +1 -1
  5. package/src/bases/base-ui/ui/alert-dialog.tsrx +1 -1
  6. package/src/bases/base-ui/ui/alert.tsrx +1 -1
  7. package/src/bases/base-ui/ui/aspect-ratio.tsrx +1 -1
  8. package/src/bases/base-ui/ui/avatar.tsrx +1 -1
  9. package/src/bases/base-ui/ui/badge.tsrx +1 -1
  10. package/src/bases/base-ui/ui/breadcrumb.tsrx +1 -1
  11. package/src/bases/base-ui/ui/button.tsrx +1 -1
  12. package/src/bases/base-ui/ui/card.tsrx +1 -1
  13. package/src/bases/base-ui/ui/checkbox.tsrx +1 -1
  14. package/src/bases/base-ui/ui/context-menu.tsrx +22 -17
  15. package/src/bases/base-ui/ui/dialog.tsrx +31 -40
  16. package/src/bases/base-ui/ui/dropdown-menu.tsrx +22 -17
  17. package/src/bases/base-ui/ui/empty.tsrx +1 -1
  18. package/src/bases/base-ui/ui/field.tsrx +1 -1
  19. package/src/bases/base-ui/ui/hover-card.tsrx +18 -16
  20. package/src/bases/base-ui/ui/input.tsrx +1 -1
  21. package/src/bases/base-ui/ui/item.tsrx +1 -1
  22. package/src/bases/base-ui/ui/kbd.tsrx +1 -1
  23. package/src/bases/base-ui/ui/label.tsrx +1 -1
  24. package/src/bases/base-ui/ui/menubar.tsrx +22 -17
  25. package/src/bases/base-ui/ui/native-select.tsrx +1 -1
  26. package/src/bases/base-ui/ui/navigation-menu.tsrx +137 -0
  27. package/src/bases/base-ui/ui/pagination.tsrx +1 -1
  28. package/src/bases/base-ui/ui/popover.tsrx +18 -16
  29. package/src/bases/base-ui/ui/progress.tsrx +1 -1
  30. package/src/bases/base-ui/ui/radio-group.tsrx +1 -1
  31. package/src/bases/base-ui/ui/scroll-area.tsrx +44 -0
  32. package/src/bases/base-ui/ui/select.tsrx +157 -0
  33. package/src/bases/base-ui/ui/separator.tsrx +1 -1
  34. package/src/bases/base-ui/ui/sheet.tsrx +1 -1
  35. package/src/bases/base-ui/ui/sidebar.tsrx +1 -1
  36. package/src/bases/base-ui/ui/skeleton.tsrx +1 -1
  37. package/src/bases/base-ui/ui/slider.tsrx +41 -52
  38. package/src/bases/base-ui/ui/spinner.tsrx +1 -1
  39. package/src/bases/base-ui/ui/switch.tsrx +1 -1
  40. package/src/bases/base-ui/ui/table.tsrx +1 -1
  41. package/src/bases/base-ui/ui/tabs.tsrx +8 -17
  42. package/src/bases/base-ui/ui/textarea.tsrx +1 -1
  43. package/src/bases/base-ui/ui/toggle-group.tsrx +22 -32
  44. package/src/bases/base-ui/ui/toggle.tsrx +1 -1
  45. package/src/bases/base-ui/ui/tooltip.tsrx +18 -16
  46. package/src/bases/radix/ui/accordion.tsrx +1 -1
  47. package/src/bases/radix/ui/alert-dialog.tsrx +1 -1
  48. package/src/bases/radix/ui/alert.tsrx +1 -1
  49. package/src/bases/radix/ui/avatar.tsrx +1 -1
  50. package/src/bases/radix/ui/badge.tsrx +1 -1
  51. package/src/bases/radix/ui/breadcrumb.tsrx +1 -1
  52. package/src/bases/radix/ui/button.tsrx +1 -1
  53. package/src/bases/radix/ui/card.tsrx +1 -1
  54. package/src/bases/radix/ui/checkbox.tsrx +1 -1
  55. package/src/bases/radix/ui/context-menu.tsrx +1 -1
  56. package/src/bases/radix/ui/dialog.tsrx +1 -1
  57. package/src/bases/radix/ui/dropdown-menu.tsrx +1 -1
  58. package/src/bases/radix/ui/empty.tsrx +1 -1
  59. package/src/bases/radix/ui/field.tsrx +1 -1
  60. package/src/bases/radix/ui/hover-card.tsrx +1 -1
  61. package/src/bases/radix/ui/input.tsrx +1 -1
  62. package/src/bases/radix/ui/item.tsrx +1 -1
  63. package/src/bases/radix/ui/kbd.tsrx +1 -1
  64. package/src/bases/radix/ui/label.tsrx +1 -1
  65. package/src/bases/radix/ui/menubar.tsrx +1 -1
  66. package/src/bases/radix/ui/native-select.tsrx +1 -1
  67. package/src/bases/radix/ui/navigation-menu.tsrx +1 -1
  68. package/src/bases/radix/ui/pagination.tsrx +1 -1
  69. package/src/bases/radix/ui/popover.tsrx +1 -1
  70. package/src/bases/radix/ui/progress.tsrx +1 -1
  71. package/src/bases/radix/ui/radio-group.tsrx +1 -1
  72. package/src/bases/radix/ui/scroll-area.tsrx +1 -1
  73. package/src/bases/radix/ui/select.tsrx +1 -1
  74. package/src/bases/radix/ui/separator.tsrx +1 -1
  75. package/src/bases/radix/ui/sheet.tsrx +1 -1
  76. package/src/bases/radix/ui/sidebar.tsrx +1 -1
  77. package/src/bases/radix/ui/skeleton.tsrx +1 -1
  78. package/src/bases/radix/ui/slider.tsrx +1 -1
  79. package/src/bases/radix/ui/spinner.tsrx +1 -1
  80. package/src/bases/radix/ui/switch.tsrx +1 -1
  81. package/src/bases/radix/ui/table.tsrx +1 -1
  82. package/src/bases/radix/ui/tabs.tsrx +1 -1
  83. package/src/bases/radix/ui/textarea.tsrx +1 -1
  84. package/src/bases/radix/ui/toggle-group.tsrx +1 -1
  85. package/src/bases/radix/ui/toggle.tsrx +1 -1
  86. package/src/bases/radix/ui/tooltip.tsrx +1 -1
  87. package/src/bases/react-aria/ui/accordion.tsrx +1 -1
  88. package/src/bases/react-aria/ui/alert-dialog.tsrx +1 -1
  89. package/src/bases/react-aria/ui/alert.tsrx +1 -1
  90. package/src/bases/react-aria/ui/aspect-ratio.tsrx +1 -1
  91. package/src/bases/react-aria/ui/avatar.tsrx +1 -1
  92. package/src/bases/react-aria/ui/badge.tsrx +1 -1
  93. package/src/bases/react-aria/ui/breadcrumb.tsrx +1 -1
  94. package/src/bases/react-aria/ui/button.tsrx +1 -1
  95. package/src/bases/react-aria/ui/card.tsrx +1 -1
  96. package/src/bases/react-aria/ui/checkbox.tsrx +1 -1
  97. package/src/bases/react-aria/ui/dialog.tsrx +1 -1
  98. package/src/bases/react-aria/ui/empty.tsrx +1 -1
  99. package/src/bases/react-aria/ui/field.tsrx +1 -1
  100. package/src/bases/react-aria/ui/input.tsrx +1 -1
  101. package/src/bases/react-aria/ui/item.tsrx +1 -1
  102. package/src/bases/react-aria/ui/kbd.tsrx +1 -1
  103. package/src/bases/react-aria/ui/label.tsrx +1 -1
  104. package/src/bases/react-aria/ui/native-select.tsrx +1 -1
  105. package/src/bases/react-aria/ui/pagination.tsrx +1 -1
  106. package/src/bases/react-aria/ui/popover.tsrx +1 -1
  107. package/src/bases/react-aria/ui/radio-group.tsrx +1 -1
  108. package/src/bases/react-aria/ui/scroll-area.tsrx +1 -1
  109. package/src/bases/react-aria/ui/separator.tsrx +1 -1
  110. package/src/bases/react-aria/ui/sheet.tsrx +1 -1
  111. package/src/bases/react-aria/ui/skeleton.tsrx +1 -1
  112. package/src/bases/react-aria/ui/slider.tsrx +1 -1
  113. package/src/bases/react-aria/ui/spinner.tsrx +1 -1
  114. package/src/bases/react-aria/ui/switch.tsrx +1 -1
  115. package/src/bases/react-aria/ui/table.tsrx +1 -1
  116. package/src/bases/react-aria/ui/tabs.tsrx +1 -1
  117. package/src/bases/react-aria/ui/textarea.tsrx +1 -1
  118. package/src/bases/react-aria/ui/toggle.tsrx +1 -1
  119. package/src/lib/utils.ts +1 -8
@@ -29,25 +29,27 @@ import { Menubar as MenubarPrimitive } from '@octanejs/base-ui/menubar';
29
29
  import { Menu as MenuPrimitive } from '@octanejs/base-ui/menu';
30
30
  import { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';
31
31
 
32
- import { cn } from '../../../lib/utils';
32
+ import { cn } from 'cn';
33
33
 
34
34
  type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
35
35
 
36
- export interface BaseUiPositioningProps {
37
- /** Forwarded to the Positioner; see the note at the top of this file. */
38
- side?: 'top' | 'right' | 'bottom' | 'left';
39
- sideOffset?: number;
40
- align?: 'start' | 'center' | 'end';
41
- alignOffset?: number;
42
- anchor?: unknown;
43
- positionMethod?: 'absolute' | 'fixed';
44
- collisionBoundary?: unknown;
45
- collisionPadding?: unknown;
46
- collisionAvoidance?: unknown;
47
- arrowPadding?: number;
48
- sticky?: boolean;
49
- disableAnchorTracking?: boolean;
50
- }
36
+ /** Props routed to the Positioner rather than the popup. */
37
+ export type BaseUiPositioningProps =
38
+ Pick<
39
+ MenuPrimitive.Positioner.Props,
40
+ | 'side'
41
+ | 'sideOffset'
42
+ | 'align'
43
+ | 'alignOffset'
44
+ | 'anchor'
45
+ | 'positionMethod'
46
+ | 'collisionBoundary'
47
+ | 'collisionPadding'
48
+ | 'collisionAvoidance'
49
+ | 'arrowPadding'
50
+ | 'sticky'
51
+ | 'disableAnchorTracking'
52
+ >;
51
53
 
52
54
  export function Menubar({ className, ...props }: Props) @{
53
55
  <MenubarPrimitive
@@ -170,7 +172,10 @@ export function MenubarCheckboxItem(props: Props & {
170
172
  </MenuPrimitive.CheckboxItem>
171
173
  }
172
174
 
173
- export function MenubarRadioItem(props: Props & { inset?: boolean }) @{
175
+ export function MenubarRadioItem(props: Omit<MenuPrimitive.RadioItem.Props, 'className'> & {
176
+ className?: string;
177
+ inset?: boolean;
178
+ }) @{
174
179
  const { className, children: _children, inset, ...rest } = props;
175
180
 
176
181
  <MenuPrimitive.RadioItem
@@ -9,7 +9,7 @@
9
9
  // `onChange` handlers are passed through unrenamed.
10
10
  import { ChevronDownIcon } from '@octanejs/lucide';
11
11
 
12
- import { cn } from '../../../lib/utils';
12
+ import { cn } from 'cn';
13
13
  import type { HostProps } from '../../../lib/types';
14
14
 
15
15
  export type NativeSelectProps =
@@ -0,0 +1,137 @@
1
+ // Adapted from shadcn/ui 4.21.0 (7c9eaba1), Base UI with the Nova style.
2
+ // MIT: see LICENSE.upstream. Octane uses native events and refs as props.
3
+ import * as React from 'octane';
4
+ import { ChevronDownIcon } from '@octanejs/lucide';
5
+ import { NavigationMenu as NavigationMenuPrimitive } from '@octanejs/base-ui/navigation-menu';
6
+ import { cva } from 'class-variance-authority';
7
+ import { cn } from 'cn';
8
+
9
+ function NavigationMenu({
10
+ align = 'start',
11
+ className,
12
+ children,
13
+ ...props
14
+ }: NavigationMenuPrimitive.Root.Props & Pick<NavigationMenuPrimitive.Positioner.Props, 'align'>) {
15
+ return <NavigationMenuPrimitive.Root
16
+ data-slot="navigation-menu"
17
+ className={cn(
18
+ 'group/navigation-menu relative flex max-w-max flex-1 items-center justify-center',
19
+ className,
20
+ )}
21
+ {...props}
22
+ >
23
+ {children}
24
+ <NavigationMenuPositioner align={align} />
25
+ </NavigationMenuPrimitive.Root>;
26
+ }
27
+
28
+ function NavigationMenuList({ className, ...props }: React.ComponentPropsWithRef<
29
+ typeof NavigationMenuPrimitive.List
30
+ >) {
31
+ return <NavigationMenuPrimitive.List
32
+ data-slot="navigation-menu-list"
33
+ className={cn('gap-0 group flex flex-1 list-none items-center justify-center', className)}
34
+ {...props}
35
+ />;
36
+ }
37
+
38
+ function NavigationMenuItem({ className, ...props }: React.ComponentPropsWithRef<
39
+ typeof NavigationMenuPrimitive.Item
40
+ >) {
41
+ return <NavigationMenuPrimitive.Item
42
+ data-slot="navigation-menu-item"
43
+ className={cn('relative', className)}
44
+ {...props}
45
+ />;
46
+ }
47
+
48
+ const navigationMenuTriggerStyle = cva(
49
+ 'hover:bg-muted focus:bg-muted data-open:hover:bg-muted data-open:focus:bg-muted data-open:bg-muted/50 focus-visible:ring-ring/50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted rounded-lg px-2.5 py-1.5 text-sm font-medium transition-all focus-visible:ring-3 focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center outline-none disabled:pointer-events-none',
50
+ );
51
+
52
+ function NavigationMenuTrigger({
53
+ className,
54
+ children,
55
+ ...props
56
+ }: NavigationMenuPrimitive.Trigger.Props) {
57
+ return <NavigationMenuPrimitive.Trigger
58
+ data-slot="navigation-menu-trigger"
59
+ className={cn(navigationMenuTriggerStyle(), 'group', className)}
60
+ {...props}
61
+ >
62
+ {children}
63
+ {' '}
64
+ <ChevronDownIcon
65
+ className="relative top-px ml-1 size-3 transition duration-300 group-data-open/navigation-menu-trigger:rotate-180 group-data-popup-open/navigation-menu-trigger:rotate-180"
66
+ aria-hidden="true"
67
+ />
68
+ </NavigationMenuPrimitive.Trigger>;
69
+ }
70
+
71
+ function NavigationMenuContent({ className, ...props }: NavigationMenuPrimitive.Content.Props) {
72
+ return <NavigationMenuPrimitive.Content
73
+ data-slot="navigation-menu-content"
74
+ className={cn(
75
+ 'data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:ring-foreground/10 p-1 ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:duration-300 data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] h-full w-auto transition-[opacity,transform,translate] duration-[0.35s] data-ending-style:opacity-0 data-starting-style:opacity-0 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none',
76
+ className,
77
+ )}
78
+ {...props}
79
+ />;
80
+ }
81
+
82
+ function NavigationMenuPositioner({
83
+ className,
84
+ side = 'bottom',
85
+ sideOffset = 8,
86
+ align = 'start',
87
+ alignOffset = 0,
88
+ ...props
89
+ }: NavigationMenuPrimitive.Positioner.Props) {
90
+ return <NavigationMenuPrimitive.Portal>
91
+ <NavigationMenuPrimitive.Positioner
92
+ side={side}
93
+ sideOffset={sideOffset}
94
+ align={align}
95
+ alignOffset={alignOffset}
96
+ className={cn(
97
+ 'ease-[cubic-bezier(0.22,1,0.36,1)] data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0 isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[0.35s] data-instant:transition-none',
98
+ className,
99
+ )}
100
+ {...props}
101
+ >
102
+ <NavigationMenuPrimitive.Popup
103
+ className="bg-popover text-popover-foreground ring-foreground/10 rounded-lg shadow ring-1 outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0 data-[ending-style]:easing-[ease] xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)]"
104
+ >
105
+ <NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />
106
+ </NavigationMenuPrimitive.Popup>
107
+ </NavigationMenuPrimitive.Positioner>
108
+ </NavigationMenuPrimitive.Portal>;
109
+ }
110
+
111
+ function NavigationMenuLink({ className, ...props }: NavigationMenuPrimitive.Link.Props) {
112
+ return <NavigationMenuPrimitive.Link
113
+ data-slot="navigation-menu-link"
114
+ className={cn(
115
+ 'data-active:focus:bg-muted data-active:hover:bg-muted data-active:bg-muted/50 focus-visible:ring-ring/50 hover:bg-muted focus:bg-muted flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none focus-visible:ring-3 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md [&_svg:not([class*=\'size-\'])]:size-4',
116
+ className,
117
+ )}
118
+ {...props}
119
+ />;
120
+ }
121
+
122
+ function NavigationMenuIndicator({ className, ...props }: React.ComponentPropsWithRef<
123
+ typeof NavigationMenuPrimitive.Icon
124
+ >) {
125
+ return <NavigationMenuPrimitive.Icon
126
+ data-slot="navigation-menu-indicator"
127
+ className={cn(
128
+ 'data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-1 flex h-1.5 items-end justify-center overflow-hidden',
129
+ className,
130
+ )}
131
+ {...props}
132
+ >
133
+ <div className="bg-border rounded-tl-sm shadow-md relative top-[60%] h-2 w-2 rotate-45" />
134
+ </NavigationMenuPrimitive.Icon>;
135
+ }
136
+
137
+ export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, navigationMenuTriggerStyle, NavigationMenuPositioner };
@@ -21,7 +21,7 @@
21
21
  import { createElement, type OctaneNode } from 'octane';
22
22
  import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from '@octanejs/lucide';
23
23
 
24
- import { cn } from '../../../lib/utils';
24
+ import { cn } from 'cn';
25
25
  import { Button, type ButtonProps } from './button.tsrx';
26
26
 
27
27
  type Props = { className?: string } & Record<string, unknown>;
@@ -29,7 +29,7 @@
29
29
  import { createElement } from 'octane';
30
30
  import { Popover as PopoverPrimitive } from '@octanejs/base-ui/popover';
31
31
 
32
- import { cn } from '../../../lib/utils';
32
+ import { cn } from 'cn';
33
33
 
34
34
  type Props = { className?: string } & Record<string, unknown>;
35
35
 
@@ -41,21 +41,23 @@ export function PopoverTrigger(props: Record<string, unknown>) @{
41
41
  <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
42
42
  }
43
43
 
44
- export interface BaseUiPositioningProps {
45
- /** Forwarded to the Positioner; see the note at the top of this file. */
46
- side?: 'top' | 'right' | 'bottom' | 'left';
47
- sideOffset?: number;
48
- align?: 'start' | 'center' | 'end';
49
- alignOffset?: number;
50
- anchor?: unknown;
51
- positionMethod?: 'absolute' | 'fixed';
52
- collisionBoundary?: unknown;
53
- collisionPadding?: unknown;
54
- collisionAvoidance?: unknown;
55
- arrowPadding?: number;
56
- sticky?: boolean;
57
- disableAnchorTracking?: boolean;
58
- }
44
+ /** Props routed to the Positioner rather than the popup. */
45
+ export type BaseUiPositioningProps =
46
+ Pick<
47
+ PopoverPrimitive.Positioner.Props,
48
+ | 'side'
49
+ | 'sideOffset'
50
+ | 'align'
51
+ | 'alignOffset'
52
+ | 'anchor'
53
+ | 'positionMethod'
54
+ | 'collisionBoundary'
55
+ | 'collisionPadding'
56
+ | 'collisionAvoidance'
57
+ | 'arrowPadding'
58
+ | 'sticky'
59
+ | 'disableAnchorTracking'
60
+ >;
59
61
 
60
62
  export interface PopoverContentProps extends Record<string, unknown>, BaseUiPositioningProps {
61
63
  className?: string;
@@ -21,7 +21,7 @@
21
21
  // Indicator to match the extra part.
22
22
  import { Progress as ProgressPrimitive } from '@octanejs/base-ui/progress';
23
23
 
24
- import { cn } from '../../../lib/utils';
24
+ import { cn } from 'cn';
25
25
 
26
26
  export interface ProgressProps extends Record<string, unknown> {
27
27
  className?: string;
@@ -28,7 +28,7 @@ import { RadioGroup as RadioGroupPrimitive } from '@octanejs/base-ui/radio-group
28
28
  import { Radio as RadioPrimitive } from '@octanejs/base-ui/radio';
29
29
  import { CircleIcon } from '@octanejs/lucide';
30
30
 
31
- import { cn } from '../../../lib/utils';
31
+ import { cn } from 'cn';
32
32
 
33
33
  type Props = { className?: string } & Record<string, unknown>;
34
34
 
@@ -0,0 +1,44 @@
1
+ // Adapted from shadcn/ui 4.21.0 (7c9eaba1), Base UI with the Nova style.
2
+ // MIT: see LICENSE.upstream. Octane uses native events and refs as props.
3
+ import * as React from 'octane';
4
+ import { ScrollArea as ScrollAreaPrimitive } from '@octanejs/base-ui/scroll-area';
5
+ import { cn } from 'cn';
6
+
7
+ function ScrollArea({ className, children, ...props }: ScrollAreaPrimitive.Root.Props) {
8
+ return <ScrollAreaPrimitive.Root
9
+ data-slot="scroll-area"
10
+ className={cn('relative', className)}
11
+ {...props}
12
+ >
13
+ <ScrollAreaPrimitive.Viewport
14
+ data-slot="scroll-area-viewport"
15
+ className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
16
+ >{children}</ScrollAreaPrimitive.Viewport>
17
+ <ScrollBar />
18
+ <ScrollAreaPrimitive.Corner />
19
+ </ScrollAreaPrimitive.Root>;
20
+ }
21
+
22
+ function ScrollBar({
23
+ className,
24
+ orientation = 'vertical',
25
+ ...props
26
+ }: ScrollAreaPrimitive.Scrollbar.Props) {
27
+ return <ScrollAreaPrimitive.Scrollbar
28
+ data-slot="scroll-area-scrollbar"
29
+ data-orientation={orientation}
30
+ orientation={orientation}
31
+ className={cn(
32
+ 'data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none',
33
+ className,
34
+ )}
35
+ {...props}
36
+ >
37
+ <ScrollAreaPrimitive.Thumb
38
+ data-slot="scroll-area-thumb"
39
+ className="rounded-full relative flex-1 bg-border"
40
+ />
41
+ </ScrollAreaPrimitive.Scrollbar>;
42
+ }
43
+
44
+ export { ScrollArea, ScrollBar };
@@ -0,0 +1,157 @@
1
+ // Adapted from shadcn/ui 4.21.0 (7c9eaba1), Base UI with the Nova style.
2
+ // MIT: see LICENSE.upstream. Octane uses native events and refs as props.
3
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from '@octanejs/lucide';
4
+ import * as React from 'octane';
5
+ import { Select as SelectPrimitive } from '@octanejs/base-ui/select';
6
+ import { cn } from 'cn';
7
+
8
+ const Select = SelectPrimitive.Root;
9
+
10
+ function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
11
+ return <SelectPrimitive.Group
12
+ data-slot="select-group"
13
+ className={cn('scroll-my-1 p-1', className)}
14
+ {...props}
15
+ />;
16
+ }
17
+
18
+ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
19
+ return <SelectPrimitive.Value
20
+ data-slot="select-value"
21
+ className={cn('flex flex-1 text-left', className)}
22
+ {...props}
23
+ />;
24
+ }
25
+
26
+ function SelectTrigger({
27
+ className,
28
+ size = 'default',
29
+ children,
30
+ ...props
31
+ }: SelectPrimitive.Trigger.Props & {
32
+ size?: 'sm' | 'default';
33
+ }) {
34
+ return <SelectPrimitive.Trigger
35
+ data-slot="select-trigger"
36
+ data-size={size}
37
+ className={cn(
38
+ 'border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm transition-colors select-none focus-visible:ring-3 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*=\'size-\'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <SelectPrimitive.Icon
45
+ render={<ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />}
46
+ />
47
+ </SelectPrimitive.Trigger>;
48
+ }
49
+
50
+ function SelectContent({
51
+ className,
52
+ children,
53
+ side = 'bottom',
54
+ sideOffset = 4,
55
+ align = 'center',
56
+ alignOffset = 0,
57
+ alignItemWithTrigger = true,
58
+ ...props
59
+ }: SelectPrimitive.Popup.Props & Pick<
60
+ SelectPrimitive.Positioner.Props,
61
+ 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'
62
+ >) {
63
+ return <SelectPrimitive.Portal>
64
+ <SelectPrimitive.Positioner
65
+ side={side}
66
+ sideOffset={sideOffset}
67
+ align={align}
68
+ alignOffset={alignOffset}
69
+ alignItemWithTrigger={alignItemWithTrigger}
70
+ className="isolate z-50"
71
+ >
72
+ <SelectPrimitive.Popup
73
+ data-slot="select-content"
74
+ data-align-trigger={alignItemWithTrigger}
75
+ className={cn(
76
+ 'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-lg shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 cn-menu-target cn-menu-translucent relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none',
77
+ className,
78
+ )}
79
+ {...props}
80
+ >
81
+ <SelectScrollUpButton />
82
+ <SelectPrimitive.List>{children}</SelectPrimitive.List>
83
+ <SelectScrollDownButton />
84
+ </SelectPrimitive.Popup>
85
+ </SelectPrimitive.Positioner>
86
+ </SelectPrimitive.Portal>;
87
+ }
88
+
89
+ function SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props) {
90
+ return <SelectPrimitive.GroupLabel
91
+ data-slot="select-label"
92
+ className={cn('text-muted-foreground px-1.5 py-1 text-xs', className)}
93
+ {...props}
94
+ />;
95
+ }
96
+
97
+ function SelectItem({ className, children, ...props }: SelectPrimitive.Item.Props) {
98
+ return <SelectPrimitive.Item
99
+ data-slot="select-item"
100
+ className={cn(
101
+ 'focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm [&_svg:not([class*=\'size-\'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
102
+ className,
103
+ )}
104
+ {...props}
105
+ >
106
+ <SelectPrimitive.ItemText
107
+ className="flex flex-1 gap-2 shrink-0 whitespace-nowrap"
108
+ >{children}</SelectPrimitive.ItemText>
109
+ <SelectPrimitive.ItemIndicator
110
+ render={<span
111
+ className="pointer-events-none absolute right-2 flex size-4 items-center justify-center"
112
+ />}
113
+ >
114
+ <CheckIcon className="pointer-events-none" />
115
+ </SelectPrimitive.ItemIndicator>
116
+ </SelectPrimitive.Item>;
117
+ }
118
+
119
+ function SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Props) {
120
+ return <SelectPrimitive.Separator
121
+ data-slot="select-separator"
122
+ className={cn('bg-border -mx-1 my-1 h-px pointer-events-none', className)}
123
+ {...props}
124
+ />;
125
+ }
126
+
127
+ function SelectScrollUpButton({ className, ...props }: React.ComponentProps<
128
+ typeof SelectPrimitive.ScrollUpArrow
129
+ >) {
130
+ return <SelectPrimitive.ScrollUpArrow
131
+ data-slot="select-scroll-up-button"
132
+ className={cn(
133
+ 'bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*=\'size-\'])]:size-4 top-0 w-full',
134
+ className,
135
+ )}
136
+ {...props}
137
+ >
138
+ <ChevronUpIcon />
139
+ </SelectPrimitive.ScrollUpArrow>;
140
+ }
141
+
142
+ function SelectScrollDownButton({ className, ...props }: React.ComponentProps<
143
+ typeof SelectPrimitive.ScrollDownArrow
144
+ >) {
145
+ return <SelectPrimitive.ScrollDownArrow
146
+ data-slot="select-scroll-down-button"
147
+ className={cn(
148
+ 'bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*=\'size-\'])]:size-4 bottom-0 w-full',
149
+ className,
150
+ )}
151
+ {...props}
152
+ >
153
+ <ChevronDownIcon />
154
+ </SelectPrimitive.ScrollDownArrow>;
155
+ }
156
+
157
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue };
@@ -14,7 +14,7 @@
14
14
  // exactly this reason. The non-conditional utilities stay in step with the other bases.
15
15
  import { Separator as SeparatorPrimitive } from '@octanejs/base-ui/separator';
16
16
 
17
- import { cn } from '../../../lib/utils';
17
+ import { cn } from 'cn';
18
18
 
19
19
  export interface SeparatorProps extends Record<string, unknown> {
20
20
  className?: string;
@@ -27,7 +27,7 @@ import { type OctaneNode } from 'octane';
27
27
  import { Dialog as SheetPrimitive } from '@octanejs/base-ui/dialog';
28
28
  import { XIcon } from '@octanejs/lucide';
29
29
 
30
- import { cn } from '../../../lib/utils';
30
+ import { cn } from 'cn';
31
31
  import { Button } from './button.tsrx';
32
32
 
33
33
  type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
@@ -35,7 +35,7 @@ import {
35
35
  import { cva, type VariantProps } from 'class-variance-authority';
36
36
  import { PanelLeftIcon } from '@octanejs/lucide';
37
37
 
38
- import { cn } from '../../../lib/utils';
38
+ import { cn } from 'cn';
39
39
  import { useIsMobile } from '../../../hooks/use-mobile';
40
40
  import { Button } from './button.tsrx';
41
41
  import { Input } from './input.tsrx';
@@ -5,7 +5,7 @@
5
5
  // Skeleton is the WORST case for this, and the reason the whole batch is labelled: the Radix
6
6
  // base renders `bg-accent` where the React Aria base renders `bg-muted`, so the one token that
7
7
  // matters here is known to differ ACROSS bases. Verify against the upstream source.
8
- import { cn } from '../../../lib/utils';
8
+ import { cn } from 'cn';
9
9
  import type { DivProps } from '../../../lib/types';
10
10
 
11
11
  export function Skeleton({ className, ...props }: DivProps) @{
@@ -23,29 +23,26 @@
23
23
  // `thumbAlignment="edge"` is forwarded as upstream does: it insets the thumb so the two extremes
24
24
  // sit flush with the ends of the track instead of overhanging them.
25
25
  //
26
- // Thumbs are composed with createElement for the same reason as the radix base: one per entry of
27
- // `_values`, and mapped descriptors are octane's channel for computed children.
28
- import { createElement, useMemo } from 'octane';
26
+ // One keyed thumb is rendered for each value.
27
+ import { useMemo } from 'octane';
29
28
  import { Slider as SliderPrimitive } from '@octanejs/base-ui/slider';
30
29
 
31
- import { cn } from '../../../lib/utils';
30
+ import { cn } from 'cn';
32
31
 
33
- export interface SliderProps extends Record<string, unknown> {
32
+ export interface SliderProps<
33
+ Value extends number | readonly number[] = number | readonly number[],
34
+ > extends Omit<SliderPrimitive.Root.Props<Value>, 'className'> {
34
35
  className?: string;
35
- defaultValue?: number | number[];
36
- value?: number | number[];
37
- min?: number;
38
- max?: number;
39
36
  }
40
37
 
41
- export function Slider({
38
+ export function Slider<Value extends number | readonly number[]>({
42
39
  className,
43
40
  defaultValue,
44
41
  value,
45
42
  min = 0,
46
43
  max = 100,
47
44
  ...props
48
- }: SliderProps) {
45
+ }: SliderProps<Value>) @{
49
46
  // This drives HOW MANY THUMBS get rendered, so a value shape it fails to recognise is a visible
50
47
  // bug rather than a type nicety.
51
48
  //
@@ -67,45 +64,37 @@ export function Slider({
67
64
  return [min, max];
68
65
  }, [value, defaultValue, min, max]);
69
66
 
70
- return createElement(
71
- SliderPrimitive.Root,
72
- {
73
- className: cn(
74
- 'data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full',
75
- className,
76
- ),
77
- 'data-slot': 'slider',
78
- defaultValue,
79
- value,
80
- min,
81
- max,
82
- thumbAlignment: 'edge',
83
- ...props,
84
- },
85
- createElement(
86
- SliderPrimitive.Control,
87
- {
88
- className: 'relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-40 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col',
89
- },
90
- createElement(
91
- SliderPrimitive.Track,
92
- {
93
- 'data-slot': 'slider-track',
94
- className: 'relative grow overflow-hidden rounded-full bg-muted select-none data-[orientation=horizontal]:h-1 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1',
95
- },
96
- createElement(SliderPrimitive.Indicator, {
97
- 'data-slot': 'slider-range',
98
- className: 'bg-primary select-none data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full',
99
- }),
100
- ),
101
- ...Array.from(
102
- { length: _values.length },
103
- (_, index) => createElement(SliderPrimitive.Thumb, {
104
- 'data-slot': 'slider-thumb',
105
- key: index,
106
- className: 'relative block size-3 shrink-0 rounded-full border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none 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',
107
- }),
108
- ),
109
- ),
110
- );
67
+ <SliderPrimitive.Root
68
+ className={cn(
69
+ 'data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full',
70
+ className,
71
+ )}
72
+ data-slot="slider"
73
+ defaultValue={defaultValue}
74
+ value={value}
75
+ min={min}
76
+ max={max}
77
+ thumbAlignment="edge"
78
+ {...props}
79
+ >
80
+ <SliderPrimitive.Control
81
+ className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-40 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col"
82
+ >
83
+ <SliderPrimitive.Track
84
+ data-slot="slider-track"
85
+ className="relative grow overflow-hidden rounded-full bg-muted select-none data-[orientation=horizontal]:h-1 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1"
86
+ >
87
+ <SliderPrimitive.Indicator
88
+ data-slot="slider-range"
89
+ className="bg-primary select-none data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
90
+ />
91
+ </SliderPrimitive.Track>
92
+ @for (const index of _values.keys(); key index) {
93
+ <SliderPrimitive.Thumb
94
+ data-slot="slider-thumb"
95
+ className="relative block size-3 shrink-0 rounded-full border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none 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"
96
+ />
97
+ }
98
+ </SliderPrimitive.Control>
99
+ </SliderPrimitive.Root>
111
100
  }
@@ -13,7 +13,7 @@
13
13
  // describe them.
14
14
  import { Loader2Icon } from '@octanejs/lucide';
15
15
 
16
- import { cn } from '../../../lib/utils';
16
+ import { cn } from 'cn';
17
17
 
18
18
  export interface SpinnerProps extends Record<string, unknown> {
19
19
  className?: string;