@octanejs/shadcn 0.0.1

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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -0
  3. package/package.json +63 -0
  4. package/src/hooks/use-mobile.ts +23 -0
  5. package/src/index.ts +263 -0
  6. package/src/lib/types.ts +10 -0
  7. package/src/lib/utils.ts +8 -0
  8. package/src/styles/theme.css +104 -0
  9. package/src/ui/accordion.tsrx +72 -0
  10. package/src/ui/alert-dialog.tsrx +161 -0
  11. package/src/ui/alert.tsrx +69 -0
  12. package/src/ui/aspect-ratio.tsrx +10 -0
  13. package/src/ui/avatar.tsrx +79 -0
  14. package/src/ui/badge.tsrx +50 -0
  15. package/src/ui/breadcrumb.tsrx +89 -0
  16. package/src/ui/button.tsrx +71 -0
  17. package/src/ui/card.tsrx +75 -0
  18. package/src/ui/checkbox.tsrx +31 -0
  19. package/src/ui/collapsible.tsrx +21 -0
  20. package/src/ui/context-menu.tsrx +179 -0
  21. package/src/ui/dialog.tsrx +133 -0
  22. package/src/ui/dropdown-menu.tsrx +189 -0
  23. package/src/ui/empty.tsrx +86 -0
  24. package/src/ui/field.tsrx +188 -0
  25. package/src/ui/hover-card.tsrx +48 -0
  26. package/src/ui/input.tsrx +18 -0
  27. package/src/ui/item.tsrx +171 -0
  28. package/src/ui/kbd.tsrx +26 -0
  29. package/src/ui/label.tsrx +21 -0
  30. package/src/ui/menubar.tsrx +198 -0
  31. package/src/ui/native-select.tsrx +55 -0
  32. package/src/ui/navigation-menu.tsrx +116 -0
  33. package/src/ui/pagination.tsrx +113 -0
  34. package/src/ui/popover.tsrx +73 -0
  35. package/src/ui/progress.tsrx +27 -0
  36. package/src/ui/radio-group.tsrx +37 -0
  37. package/src/ui/scroll-area.tsrx +43 -0
  38. package/src/ui/select.tsrx +165 -0
  39. package/src/ui/separator.tsrx +30 -0
  40. package/src/ui/sheet.tsrx +114 -0
  41. package/src/ui/sidebar.tsrx +661 -0
  42. package/src/ui/skeleton.tsrx +14 -0
  43. package/src/ui/slider.tsrx +69 -0
  44. package/src/ui/sonner.tsrx +45 -0
  45. package/src/ui/spinner.tsrx +23 -0
  46. package/src/ui/switch.tsrx +30 -0
  47. package/src/ui/table.tsrx +80 -0
  48. package/src/ui/tabs.tsrx +69 -0
  49. package/src/ui/textarea.tsrx +20 -0
  50. package/src/ui/toggle-group.tsrx +100 -0
  51. package/src/ui/toggle.tsrx +47 -0
  52. package/src/ui/tooltip.tsrx +65 -0
@@ -0,0 +1,55 @@
1
+ // NativeSelect — maintainer-supplied default-Tailwind flavor, class strings
2
+ // verbatim. Octane adaptations: lucide-react → @octanejs/lucide.
3
+ //
4
+ // The consumer className lands on the WRAPPER div (upstream behavior); the
5
+ // <select> and the chevron keep their fixed classes.
6
+ import { ChevronDownIcon } from '@octanejs/lucide';
7
+
8
+ import { cn } from '../lib/utils';
9
+
10
+ export interface NativeSelectProps extends Record<string, unknown> {
11
+ className?: string;
12
+ size?: 'sm' | 'default';
13
+ }
14
+
15
+ export function NativeSelect({ className, size = 'default', ...props }: NativeSelectProps) @{
16
+ <div
17
+ className={cn('group/native-select relative w-fit has-[select:disabled]:opacity-50', className)}
18
+ data-slot="native-select-wrapper"
19
+ data-size={size}
20
+ >
21
+ <select
22
+ data-slot="native-select"
23
+ data-size={size}
24
+ 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"
25
+ {...props}
26
+ />
27
+ <ChevronDownIcon
28
+ className="pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none"
29
+ aria-hidden="true"
30
+ data-slot="native-select-icon"
31
+ />
32
+ </div>
33
+ }
34
+
35
+ export function NativeSelectOption({ className, ...props }: { className?: string } & Record<
36
+ string,
37
+ unknown
38
+ >) @{
39
+ <option
40
+ data-slot="native-select-option"
41
+ className={cn('bg-[Canvas] text-[CanvasText]', className)}
42
+ {...props}
43
+ />
44
+ }
45
+
46
+ export function NativeSelectOptGroup({ className, ...props }: { className?: string } & Record<
47
+ string,
48
+ unknown
49
+ >) @{
50
+ <optgroup
51
+ data-slot="native-select-optgroup"
52
+ className={cn('bg-[Canvas] text-[CanvasText]', className)}
53
+ {...props}
54
+ />
55
+ }
@@ -0,0 +1,116 @@
1
+ // NavigationMenu — maintainer-supplied default-Tailwind flavor, class strings
2
+ // verbatim. Octane adaptations: lucide-react → @octanejs/lucide, radix-ui →
3
+ // @octanejs/radix. Root and Trigger interleave consumer children with sibling
4
+ // descriptors, so they use the {props.children} lowering in template JSX.
5
+ import type { OctaneNode } from 'octane';
6
+ import { cva } from 'class-variance-authority';
7
+ import { NavigationMenu as NavigationMenuPrimitive } from '@octanejs/radix';
8
+ import { ChevronDownIcon } from '@octanejs/lucide';
9
+
10
+ import { cn } from '../lib/utils';
11
+
12
+ type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
13
+
14
+ export function NavigationMenu(props: Props & { viewport?: boolean }) @{
15
+ const { className, children: _children, viewport = true, ...rest } = props;
16
+
17
+ <NavigationMenuPrimitive.Root
18
+ data-slot="navigation-menu"
19
+ data-viewport={viewport}
20
+ className={cn(
21
+ 'group/navigation-menu relative flex max-w-max flex-1 items-center justify-center',
22
+ className,
23
+ )}
24
+ {...rest}
25
+ >
26
+ {props.children}
27
+ @if (viewport) {
28
+ <NavigationMenuViewport />
29
+ }
30
+ </NavigationMenuPrimitive.Root>
31
+ }
32
+
33
+ export function NavigationMenuList({ className, ...props }: Props) @{
34
+ <NavigationMenuPrimitive.List
35
+ data-slot="navigation-menu-list"
36
+ className={cn('group flex flex-1 list-none items-center justify-center gap-0', className)}
37
+ {...props}
38
+ />
39
+ }
40
+
41
+ export function NavigationMenuItem({ className, ...props }: Props) @{
42
+ <NavigationMenuPrimitive.Item
43
+ data-slot="navigation-menu-item"
44
+ className={cn('relative', className)}
45
+ {...props}
46
+ />
47
+ }
48
+
49
+ export const navigationMenuTriggerStyle = cva(
50
+ 'group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center rounded-lg px-2.5 py-1.5 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted data-open:bg-muted/50 data-open:hover:bg-muted data-open:focus:bg-muted',
51
+ );
52
+
53
+ export function NavigationMenuTrigger(props: Props) @{
54
+ const { className, children: _children, ...rest } = props;
55
+
56
+ <NavigationMenuPrimitive.Trigger
57
+ data-slot="navigation-menu-trigger"
58
+ className={cn(navigationMenuTriggerStyle(), 'group', className)}
59
+ {...rest}
60
+ >
61
+ {props.children}
62
+ {' '}
63
+ <ChevronDownIcon
64
+ className="relative top-px ml-1 size-3 transition duration-300 group-data-popup-open/navigation-menu-trigger:rotate-180 group-data-open/navigation-menu-trigger:rotate-180"
65
+ aria-hidden="true"
66
+ />
67
+ </NavigationMenuPrimitive.Trigger>
68
+ }
69
+
70
+ export function NavigationMenuContent({ className, ...props }: Props) @{
71
+ <NavigationMenuPrimitive.Content
72
+ data-slot="navigation-menu-content"
73
+ className={cn(
74
+ 'top-0 left-0 w-full p-1 ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 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 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none md:absolute md:w-auto group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95',
75
+ className,
76
+ )}
77
+ {...props}
78
+ />
79
+ }
80
+
81
+ export function NavigationMenuViewport({ className, ...props }: Props) @{
82
+ <div className={cn('absolute top-full left-0 isolate z-50 flex justify-center')}>
83
+ <NavigationMenuPrimitive.Viewport
84
+ data-slot="navigation-menu-viewport"
85
+ className={cn(
86
+ 'origin-top-center relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-lg bg-popover text-popover-foreground shadow ring-1 ring-foreground/10 duration-100 md:w-(--radix-navigation-menu-viewport-width) data-open:animate-in data-open:zoom-in-90 data-closed:animate-out data-closed:zoom-out-90',
87
+ className,
88
+ )}
89
+ {...props}
90
+ />
91
+ </div>
92
+ }
93
+
94
+ export function NavigationMenuLink({ className, ...props }: Props) @{
95
+ <NavigationMenuPrimitive.Link
96
+ data-slot="navigation-menu-link"
97
+ className={cn(
98
+ 'flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md data-active:bg-muted/50 data-active:hover:bg-muted data-active:focus:bg-muted [&_svg:not([class*=\'size-\'])]:size-4',
99
+ className,
100
+ )}
101
+ {...props}
102
+ />
103
+ }
104
+
105
+ export function NavigationMenuIndicator({ className, ...props }: Props) @{
106
+ <NavigationMenuPrimitive.Indicator
107
+ data-slot="navigation-menu-indicator"
108
+ className={cn(
109
+ 'top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in',
110
+ className,
111
+ )}
112
+ {...props}
113
+ >
114
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
115
+ </NavigationMenuPrimitive.Indicator>
116
+ }
@@ -0,0 +1,113 @@
1
+ // Pagination — maintainer-supplied default-Tailwind flavor, class strings
2
+ // verbatim. Octane adaptations: lucide-react → @octanejs/lucide; PaginationLink
3
+ // is upstream's `<Button asChild><a …>` — Slot needs a real element descriptor
4
+ // as its child, so the Button/anchor pair composes with createElement and the
5
+ // link's children flow through the anchor's children prop.
6
+ import { createElement, type OctaneNode } from 'octane';
7
+ import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from '@octanejs/lucide';
8
+
9
+ import { cn } from '../lib/utils';
10
+ import { Button, type ButtonProps } from './button.tsrx';
11
+
12
+ type Props = { className?: string } & Record<string, unknown>;
13
+
14
+ export function Pagination({ className, ...props }: Props) @{
15
+ <nav
16
+ role="navigation"
17
+ aria-label="pagination"
18
+ data-slot="pagination"
19
+ className={cn('mx-auto flex w-full justify-center', className)}
20
+ {...props}
21
+ />
22
+ }
23
+
24
+ export function PaginationContent({ className, ...props }: Props) @{
25
+ <ul
26
+ data-slot="pagination-content"
27
+ className={cn('flex items-center gap-0.5', className)}
28
+ {...props}
29
+ />
30
+ }
31
+
32
+ export function PaginationItem(props: Record<string, unknown>) @{
33
+ <li data-slot="pagination-item" {...props} />
34
+ }
35
+
36
+ export interface PaginationLinkProps extends Record<string, unknown> {
37
+ className?: string;
38
+ isActive?: boolean;
39
+ size?: ButtonProps['size'];
40
+ children?: OctaneNode;
41
+ }
42
+
43
+ export function PaginationLink({
44
+ className,
45
+ isActive,
46
+ size = 'icon',
47
+ children,
48
+ ...props
49
+ }: PaginationLinkProps) {
50
+ return createElement(
51
+ Button,
52
+ {
53
+ asChild: true,
54
+ variant: isActive ? 'outline' : 'ghost',
55
+ size,
56
+ className: cn(className),
57
+ },
58
+ createElement('a', {
59
+ 'aria-current': isActive ? 'page' : undefined,
60
+ 'data-slot': 'pagination-link',
61
+ 'data-active': isActive,
62
+ ...props,
63
+ children,
64
+ }),
65
+ );
66
+ }
67
+
68
+ export function PaginationPrevious({
69
+ className,
70
+ text = 'Previous',
71
+ ...props
72
+ }: PaginationLinkProps & {
73
+ text?: string;
74
+ }) @{
75
+ <PaginationLink
76
+ aria-label="Go to previous page"
77
+ size="default"
78
+ className={cn('pl-1.5!', className)}
79
+ {...props}
80
+ >
81
+ <ChevronLeftIcon data-icon="inline-start" className="cn-rtl-flip" />
82
+ <span className="hidden sm:block">{text as string}</span>
83
+ </PaginationLink>
84
+ }
85
+
86
+ export function PaginationNext({ className, text = 'Next', ...props }: PaginationLinkProps & {
87
+ text?: string;
88
+ }) @{
89
+ <PaginationLink
90
+ aria-label="Go to next page"
91
+ size="default"
92
+ className={cn('pr-1.5!', className)}
93
+ {...props}
94
+ >
95
+ <span className="hidden sm:block">{text as string}</span>
96
+ <ChevronRightIcon data-icon="inline-end" className="cn-rtl-flip" />
97
+ </PaginationLink>
98
+ }
99
+
100
+ export function PaginationEllipsis({ className, ...props }: Props) @{
101
+ <span
102
+ aria-hidden="true"
103
+ data-slot="pagination-ellipsis"
104
+ className={cn(
105
+ 'flex size-8 items-center justify-center [&_svg:not([class*=\'size-\'])]:size-4',
106
+ className,
107
+ )}
108
+ {...props}
109
+ >
110
+ <MoreHorizontalIcon />
111
+ <span className="sr-only">More pages</span>
112
+ </span>
113
+ }
@@ -0,0 +1,73 @@
1
+ // Popover — maintainer-supplied default-Tailwind flavor, class strings
2
+ // verbatim. Octane adaptations: "use client" dropped, radix-ui →
3
+ // @octanejs/radix.
4
+ //
5
+ // PopoverContent renders Portal > Content: the Portal's child must be a real
6
+ // element DESCRIPTOR (radix portals cannot enumerate opaque compiled .tsrx
7
+ // children), so the pair is composed with createElement; the consumer's
8
+ // children flow into the Content primitive through the ordinary props.children
9
+ // channel.
10
+ import { createElement } from 'octane';
11
+ import { Popover as PopoverPrimitive } from '@octanejs/radix';
12
+
13
+ import { cn } from '../lib/utils';
14
+
15
+ type Props = { className?: string } & Record<string, unknown>;
16
+
17
+ export function Popover(props: Record<string, unknown>) @{
18
+ <PopoverPrimitive.Root data-slot="popover" {...props} />
19
+ }
20
+
21
+ export function PopoverTrigger(props: Record<string, unknown>) @{
22
+ <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
23
+ }
24
+
25
+ export interface PopoverContentProps extends Record<string, unknown> {
26
+ className?: string;
27
+ align?: 'start' | 'center' | 'end';
28
+ sideOffset?: number;
29
+ }
30
+
31
+ export function PopoverContent({
32
+ className,
33
+ align = 'center',
34
+ sideOffset = 4,
35
+ ...props
36
+ }: PopoverContentProps) {
37
+ return createElement(PopoverPrimitive.Portal, {
38
+ children: createElement(PopoverPrimitive.Content, {
39
+ 'data-slot': 'popover-content',
40
+ align,
41
+ sideOffset,
42
+ className: cn(
43
+ 'z-50 flex w-72 origin-(--radix-popover-content-transform-origin) 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-[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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
44
+ className,
45
+ ),
46
+ ...props,
47
+ }),
48
+ });
49
+ }
50
+
51
+ export function PopoverAnchor(props: Record<string, unknown>) @{
52
+ <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
53
+ }
54
+
55
+ export function PopoverHeader({ className, ...props }: Props) @{
56
+ <div
57
+ data-slot="popover-header"
58
+ className={cn('flex flex-col gap-0.5 text-sm', className)}
59
+ {...props}
60
+ />
61
+ }
62
+
63
+ export function PopoverTitle({ className, ...props }: Props) @{
64
+ <div data-slot="popover-title" className={cn('font-medium', className)} {...props} />
65
+ }
66
+
67
+ export function PopoverDescription({ className, ...props }: Props) @{
68
+ <p
69
+ data-slot="popover-description"
70
+ className={cn('text-muted-foreground', className)}
71
+ {...props}
72
+ />
73
+ }
@@ -0,0 +1,27 @@
1
+ // Ported from shadcn-ui/ui@4baadbc6517070ae8f8feb2c97037adc2b305544
2
+ // apps/v4/registry/bases/radix/ui/progress.tsx (the `"use client"` directive
3
+ // is dropped — octane has no RSC axis). As upstream, `value` drives only the
4
+ // indicator transform — it is destructured off and NOT forwarded to the radix
5
+ // Root, whose role/aria-valuemax/data-state contract comes from its defaults.
6
+ import { Progress as ProgressPrimitive } from '@octanejs/radix';
7
+
8
+ import { cn } from '../lib/utils';
9
+
10
+ export interface ProgressProps extends Record<string, unknown> {
11
+ className?: string;
12
+ value?: number | null;
13
+ }
14
+
15
+ export function Progress({ className, value, ...props }: ProgressProps) @{
16
+ <ProgressPrimitive.Root
17
+ data-slot="progress"
18
+ className={cn('relative h-2 w-full overflow-hidden rounded-full bg-primary/20', className)}
19
+ {...props}
20
+ >
21
+ <ProgressPrimitive.Indicator
22
+ data-slot="progress-indicator"
23
+ className="h-full w-full flex-1 bg-primary transition-all"
24
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
25
+ />
26
+ </ProgressPrimitive.Root>
27
+ }
@@ -0,0 +1,37 @@
1
+ // Default-Tailwind radio-group flavor (package canonical) — utilities-inlined
2
+ // upstream styling. Octane adaptations: "use client" dropped, lucide-react →
3
+ // @octanejs/lucide, radix-ui → @octanejs/radix; pure template JSX.
4
+ import { RadioGroup as RadioGroupPrimitive } from '@octanejs/radix';
5
+ import { CircleIcon } from '@octanejs/lucide';
6
+
7
+ import { cn } from '../lib/utils';
8
+
9
+ type Props = { className?: string } & Record<string, unknown>;
10
+
11
+ export function RadioGroup({ className, ...props }: Props) @{
12
+ <RadioGroupPrimitive.Root
13
+ data-slot="radio-group"
14
+ className={cn('grid gap-3', className)}
15
+ {...props}
16
+ />
17
+ }
18
+
19
+ export function RadioGroupItem({ className, ...props }: Props) @{
20
+ <RadioGroupPrimitive.Item
21
+ data-slot="radio-group-item"
22
+ className={cn(
23
+ 'aspect-square size-4 shrink-0 rounded-full border border-input text-primary shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:ring-destructive/40',
24
+ className,
25
+ )}
26
+ {...props}
27
+ >
28
+ <RadioGroupPrimitive.Indicator
29
+ data-slot="radio-group-indicator"
30
+ className="relative flex items-center justify-center"
31
+ >
32
+ <CircleIcon
33
+ className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-primary"
34
+ />
35
+ </RadioGroupPrimitive.Indicator>
36
+ </RadioGroupPrimitive.Item>
37
+ }
@@ -0,0 +1,43 @@
1
+ // ScrollArea — maintainer-supplied default-Tailwind flavor, class strings
2
+ // verbatim. Octane adaptations: "use client" dropped, radix-ui →
3
+ // @octanejs/radix; ScrollArea interleaves consumer children with sibling
4
+ // descriptors (ScrollBar, Corner), so it uses the {props.children} lowering.
5
+ import type { OctaneNode } from 'octane';
6
+ import { ScrollArea as ScrollAreaPrimitive } from '@octanejs/radix';
7
+
8
+ import { cn } from '../lib/utils';
9
+
10
+ type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
11
+
12
+ export function ScrollArea(props: Props) @{
13
+ const { className, children: _children, ...rest } = props;
14
+
15
+ <ScrollAreaPrimitive.Root data-slot="scroll-area" className={cn('relative', className)} {...rest}>
16
+ <ScrollAreaPrimitive.Viewport
17
+ data-slot="scroll-area-viewport"
18
+ className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
19
+ >{props.children}</ScrollAreaPrimitive.Viewport>
20
+ <ScrollBar />
21
+ <ScrollAreaPrimitive.Corner />
22
+ </ScrollAreaPrimitive.Root>
23
+ }
24
+
25
+ export function ScrollBar({ className, orientation = 'vertical', ...props }: Props & {
26
+ orientation?: 'vertical' | 'horizontal';
27
+ }) @{
28
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
29
+ data-slot="scroll-area-scrollbar"
30
+ data-orientation={orientation}
31
+ orientation={orientation}
32
+ className={cn(
33
+ 'flex touch-none p-px transition-colors select-none 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',
34
+ className,
35
+ )}
36
+ {...props}
37
+ >
38
+ <ScrollAreaPrimitive.ScrollAreaThumb
39
+ data-slot="scroll-area-thumb"
40
+ className="relative flex-1 rounded-full bg-border"
41
+ />
42
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
43
+ }
@@ -0,0 +1,165 @@
1
+ // Select — maintainer-supplied default-Tailwind flavor, class strings verbatim.
2
+ // Octane adaptations: "use client" dropped, lucide-react → @octanejs/lucide,
3
+ // radix-ui → @octanejs/radix. Trigger/Content/Item compose with createElement
4
+ // where a Portal child or an `asChild` slot target must be a real element
5
+ // descriptor; consumer children flow through the props.children channel.
6
+ import { createElement, type OctaneNode } from 'octane';
7
+ import { Select as SelectPrimitive } from '@octanejs/radix';
8
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from '@octanejs/lucide';
9
+
10
+ import { cn } from '../lib/utils';
11
+
12
+ type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
13
+
14
+ export function Select(props: Record<string, unknown>) @{
15
+ <SelectPrimitive.Root data-slot="select" {...props} />
16
+ }
17
+
18
+ export function SelectGroup({ className, ...props }: Props) @{
19
+ <SelectPrimitive.Group
20
+ data-slot="select-group"
21
+ className={cn('scroll-my-1 p-1', className)}
22
+ {...props}
23
+ />
24
+ }
25
+
26
+ export function SelectValue(props: Record<string, unknown>) @{
27
+ <SelectPrimitive.Value data-slot="select-value" {...props} />
28
+ }
29
+
30
+ export function SelectTrigger({ className, size = 'default', children, ...props }: Props & {
31
+ size?: 'sm' | 'default';
32
+ }) {
33
+ return createElement(
34
+ SelectPrimitive.Trigger,
35
+ {
36
+ 'data-slot': 'select-trigger',
37
+ 'data-size': size,
38
+ className: cn(
39
+ 'flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
40
+ className,
41
+ ),
42
+ ...props,
43
+ },
44
+ children,
45
+ createElement(
46
+ SelectPrimitive.Icon,
47
+ { key: 'icon', asChild: true },
48
+ createElement(ChevronDownIcon, {
49
+ className: 'pointer-events-none size-4 text-muted-foreground',
50
+ }),
51
+ ),
52
+ );
53
+ }
54
+
55
+ export function SelectContent({
56
+ className,
57
+ children,
58
+ position = 'item-aligned',
59
+ align = 'center',
60
+ ...props
61
+ }: Props & { position?: string; align?: string }) {
62
+ return createElement(
63
+ SelectPrimitive.Portal,
64
+ null,
65
+ createElement(
66
+ SelectPrimitive.Content,
67
+ {
68
+ 'data-slot': 'select-content',
69
+ 'data-align-trigger': position === 'item-aligned',
70
+ className: cn(
71
+ 'relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
72
+ position === 'popper' &&
73
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
74
+ className,
75
+ ),
76
+ position,
77
+ align,
78
+ ...props,
79
+ },
80
+ createElement(SelectScrollUpButton, { key: 'up' }),
81
+ createElement(
82
+ SelectPrimitive.Viewport,
83
+ {
84
+ key: 'viewport',
85
+ 'data-position': position,
86
+ className: cn(
87
+ 'data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)',
88
+ position === 'popper' && '',
89
+ ),
90
+ },
91
+ children,
92
+ ),
93
+ createElement(SelectScrollDownButton, { key: 'down' }),
94
+ ),
95
+ );
96
+ }
97
+
98
+ export function SelectLabel({ className, ...props }: Props) @{
99
+ <SelectPrimitive.Label
100
+ data-slot="select-label"
101
+ className={cn('px-1.5 py-1 text-xs text-muted-foreground', className)}
102
+ {...props}
103
+ />
104
+ }
105
+
106
+ export function SelectItem({ className, children, ...props }: Props) {
107
+ return createElement(
108
+ SelectPrimitive.Item,
109
+ {
110
+ 'data-slot': 'select-item',
111
+ className: cn(
112
+ 'relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',
113
+ className,
114
+ ),
115
+ ...props,
116
+ },
117
+ createElement(
118
+ 'span',
119
+ {
120
+ key: 'indicator',
121
+ className: 'pointer-events-none absolute right-2 flex size-4 items-center justify-center',
122
+ },
123
+ createElement(
124
+ SelectPrimitive.ItemIndicator,
125
+ null,
126
+ createElement(CheckIcon, { className: 'pointer-events-none' }),
127
+ ),
128
+ ),
129
+ createElement(SelectPrimitive.ItemText, { key: 'text' }, children),
130
+ );
131
+ }
132
+
133
+ export function SelectSeparator({ className, ...props }: Props) @{
134
+ <SelectPrimitive.Separator
135
+ data-slot="select-separator"
136
+ className={cn('pointer-events-none -mx-1 my-1 h-px bg-border', className)}
137
+ {...props}
138
+ />
139
+ }
140
+
141
+ export function SelectScrollUpButton({ className, ...props }: Props) @{
142
+ <SelectPrimitive.ScrollUpButton
143
+ data-slot="select-scroll-up-button"
144
+ className={cn(
145
+ 'z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*=\'size-\'])]:size-4',
146
+ className,
147
+ )}
148
+ {...props}
149
+ >
150
+ <ChevronUpIcon />
151
+ </SelectPrimitive.ScrollUpButton>
152
+ }
153
+
154
+ export function SelectScrollDownButton({ className, ...props }: Props) @{
155
+ <SelectPrimitive.ScrollDownButton
156
+ data-slot="select-scroll-down-button"
157
+ className={cn(
158
+ 'z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*=\'size-\'])]:size-4',
159
+ className,
160
+ )}
161
+ {...props}
162
+ >
163
+ <ChevronDownIcon />
164
+ </SelectPrimitive.ScrollDownButton>
165
+ }
@@ -0,0 +1,30 @@
1
+ // Ported from shadcn-ui/ui@4baadbc6517070ae8f8feb2c97037adc2b305544
2
+ // apps/v4/registry/bases/radix/ui/separator.tsx (the `"use client"` directive
3
+ // is dropped — octane has no RSC axis).
4
+ import { Separator as SeparatorPrimitive } from '@octanejs/radix';
5
+
6
+ import { cn } from '../lib/utils';
7
+
8
+ export interface SeparatorProps extends Record<string, unknown> {
9
+ className?: string;
10
+ orientation?: 'horizontal' | 'vertical';
11
+ decorative?: boolean;
12
+ }
13
+
14
+ export function Separator({
15
+ className,
16
+ orientation = 'horizontal',
17
+ decorative = true,
18
+ ...props
19
+ }: SeparatorProps) @{
20
+ <SeparatorPrimitive.Root
21
+ data-slot="separator"
22
+ decorative={decorative}
23
+ orientation={orientation}
24
+ className={cn(
25
+ 'shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
26
+ className,
27
+ )}
28
+ {...props}
29
+ />
30
+ }