@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,188 @@
1
+ // Field — maintainer-supplied default-Tailwind flavor, class strings verbatim.
2
+ // Octane adaptations: "use client" dropped; Label/Separator come from the
3
+ // package's own ports; FieldSeparator and FieldError compose their conditional
4
+ // children with createElement (a `children && …` branch beside a sibling
5
+ // descriptor), everything else is template JSX.
6
+ import { createElement, useMemo, type OctaneNode } from 'octane';
7
+ import { cva, type VariantProps } from 'class-variance-authority';
8
+
9
+ import { cn } from '../lib/utils';
10
+ import { Label } from './label.tsrx';
11
+ import { Separator } from './separator.tsrx';
12
+
13
+ type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
14
+
15
+ export function FieldSet({ className, ...props }: Props) @{
16
+ <fieldset
17
+ data-slot="field-set"
18
+ className={cn(
19
+ 'flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ }
25
+
26
+ export function FieldLegend({ className, variant = 'legend', ...props }: Props & {
27
+ variant?: 'legend' | 'label';
28
+ }) @{
29
+ <legend
30
+ data-slot="field-legend"
31
+ data-variant={variant}
32
+ className={cn(
33
+ 'mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ }
39
+
40
+ export function FieldGroup({ className, ...props }: Props) @{
41
+ <div
42
+ data-slot="field-group"
43
+ className={cn(
44
+ 'group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4',
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ }
50
+
51
+ export const fieldVariants = cva(
52
+ 'group/field flex w-full gap-2 data-[invalid=true]:text-destructive',
53
+ {
54
+ variants: {
55
+ orientation: {
56
+ vertical: 'flex-col *:w-full [&>.sr-only]:w-auto',
57
+ horizontal: 'flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
58
+ responsive: 'flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
59
+ },
60
+ },
61
+ defaultVariants: {
62
+ orientation: 'vertical',
63
+ },
64
+ },
65
+ );
66
+
67
+ export function Field({ className, orientation = 'vertical', ...props }: Props & {
68
+ orientation?: VariantProps<typeof fieldVariants>['orientation'];
69
+ }) @{
70
+ <div
71
+ role="group"
72
+ data-slot="field"
73
+ data-orientation={orientation}
74
+ className={cn(fieldVariants({ orientation }), className)}
75
+ {...props}
76
+ />
77
+ }
78
+
79
+ export function FieldContent({ className, ...props }: Props) @{
80
+ <div
81
+ data-slot="field-content"
82
+ className={cn('group/field-content flex flex-1 flex-col gap-0.5 leading-snug', className)}
83
+ {...props}
84
+ />
85
+ }
86
+
87
+ export function FieldLabel({ className, ...props }: Props) @{
88
+ <Label
89
+ data-slot="field-label"
90
+ className={cn(
91
+ 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10',
92
+ 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',
93
+ className,
94
+ )}
95
+ {...props}
96
+ />
97
+ }
98
+
99
+ export function FieldTitle({ className, ...props }: Props) @{
100
+ <div
101
+ data-slot="field-label"
102
+ className={cn(
103
+ 'flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50',
104
+ className,
105
+ )}
106
+ {...props}
107
+ />
108
+ }
109
+
110
+ export function FieldDescription({ className, ...props }: Props) @{
111
+ <p
112
+ data-slot="field-description"
113
+ className={cn(
114
+ 'text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5',
115
+ 'last:mt-0 nth-last-2:-mt-1',
116
+ '[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
117
+ className,
118
+ )}
119
+ {...props}
120
+ />
121
+ }
122
+
123
+ export function FieldSeparator({ children, className, ...props }: Props) {
124
+ return createElement(
125
+ 'div',
126
+ {
127
+ 'data-slot': 'field-separator',
128
+ 'data-content': !!children,
129
+ className: cn(
130
+ 'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',
131
+ className,
132
+ ),
133
+ ...props,
134
+ },
135
+ createElement(Separator, { key: 'rule', className: 'absolute inset-0 top-1/2' }),
136
+ children
137
+ ? createElement(
138
+ 'span',
139
+ {
140
+ key: 'content',
141
+ className: 'relative mx-auto block w-fit bg-background px-2 text-muted-foreground',
142
+ 'data-slot': 'field-separator-content',
143
+ },
144
+ children,
145
+ )
146
+ : null,
147
+ );
148
+ }
149
+
150
+ export function FieldError(props: Props & { errors?: Array<{ message?: string } | undefined> }) {
151
+ const { className, children: _children, errors: _errors, ...rest } = props;
152
+ // Read through `props` inside the callback: octane's destructured-parameter
153
+ // locals are not the value channel the compiler threads into a closure.
154
+ const content = useMemo(() => {
155
+ if (props.children) return props.children;
156
+ const errors = props.errors;
157
+ if (!errors?.length) return null;
158
+
159
+ const uniqueErrors = [
160
+ ...new Map(errors.map((error) => [error?.message, error])).values(),
161
+ ];
162
+
163
+ if (uniqueErrors?.length == 1) return uniqueErrors[0]?.message;
164
+
165
+ return createElement(
166
+ 'ul',
167
+ { className: 'ml-4 flex list-disc flex-col gap-1' },
168
+ uniqueErrors.map(
169
+ (error, index) => error?.message
170
+ ? createElement('li', { key: index }, error.message)
171
+ : null,
172
+ ),
173
+ );
174
+ });
175
+
176
+ if (!content) return null;
177
+
178
+ return createElement(
179
+ 'div',
180
+ {
181
+ role: 'alert',
182
+ 'data-slot': 'field-error',
183
+ className: cn('text-sm font-normal text-destructive', className),
184
+ ...rest,
185
+ },
186
+ content,
187
+ );
188
+ }
@@ -0,0 +1,48 @@
1
+ // Ported from shadcn-ui/ui@4baadbc6517070ae8f8feb2c97037adc2b305544
2
+ // apps/v4/registry/bases/radix/ui/hover-card.tsx (the `"use client"` directive
3
+ // is dropped — octane has no RSC axis).
4
+ //
5
+ // HoverCardContent 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 { HoverCard as HoverCardPrimitive } from '@octanejs/radix';
12
+
13
+ import { cn } from '../lib/utils';
14
+
15
+ export function HoverCard(props: Record<string, unknown>) @{
16
+ <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
17
+ }
18
+
19
+ export function HoverCardTrigger(props: Record<string, unknown>) @{
20
+ <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
21
+ }
22
+
23
+ export interface HoverCardContentProps extends Record<string, unknown> {
24
+ className?: string;
25
+ align?: 'start' | 'center' | 'end';
26
+ sideOffset?: number;
27
+ }
28
+
29
+ export function HoverCardContent({
30
+ className,
31
+ align = 'center',
32
+ sideOffset = 4,
33
+ ...props
34
+ }: HoverCardContentProps) {
35
+ return createElement(HoverCardPrimitive.Portal, {
36
+ 'data-slot': 'hover-card-portal',
37
+ children: createElement(HoverCardPrimitive.Content, {
38
+ 'data-slot': 'hover-card-content',
39
+ align,
40
+ sideOffset,
41
+ className: cn(
42
+ 'z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden 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-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
43
+ className,
44
+ ),
45
+ ...props,
46
+ }),
47
+ });
48
+ }
@@ -0,0 +1,18 @@
1
+ // Default-Tailwind (package canonical) input — user-supplied flavor, class string verbatim.
2
+ // OCTANE DIVERGENCE: per-keystroke handling is the native onInput event.
3
+ import { cn } from '../lib/utils';
4
+ import type { HostProps } from '../lib/types';
5
+
6
+ export interface InputProps extends HostProps<'input'> {}
7
+
8
+ export function Input({ className, type, ...props }: InputProps) @{
9
+ <input
10
+ type={type}
11
+ data-slot="input"
12
+ className={cn(
13
+ 'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ }
@@ -0,0 +1,171 @@
1
+ // Item — maintainer-supplied default-Tailwind flavor, class strings verbatim.
2
+ // Octane adaptations: radix-ui Slot → @octanejs/radix (asChild via the @if/Slot
3
+ // descriptor contract); Separator comes from the package's own port.
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+ import { Slot } from '@octanejs/radix';
6
+
7
+ import { cn } from '../lib/utils';
8
+ import { Separator } from './separator.tsrx';
9
+
10
+ type DivProps = { className?: string } & Record<string, unknown>;
11
+
12
+ export function ItemGroup({ className, ...props }: DivProps) @{
13
+ <div
14
+ role="list"
15
+ data-slot="item-group"
16
+ className={cn(
17
+ 'group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2',
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ }
23
+
24
+ export function ItemSeparator({ className, ...props }: DivProps) @{
25
+ <Separator
26
+ data-slot="item-separator"
27
+ orientation="horizontal"
28
+ className={cn('my-2', className)}
29
+ {...props}
30
+ />
31
+ }
32
+
33
+ export const itemVariants = cva(
34
+ 'group/item flex w-full flex-wrap items-center rounded-lg border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted',
35
+ {
36
+ variants: {
37
+ variant: {
38
+ default: 'border-transparent',
39
+ outline: 'border-border',
40
+ muted: 'border-transparent bg-muted/50',
41
+ },
42
+ size: {
43
+ default: 'gap-2.5 px-3 py-2.5',
44
+ sm: 'gap-2.5 px-3 py-2.5',
45
+ xs: 'gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0',
46
+ },
47
+ },
48
+ defaultVariants: {
49
+ variant: 'default',
50
+ size: 'default',
51
+ },
52
+ },
53
+ );
54
+
55
+ export interface ItemProps extends Record<string, unknown> {
56
+ className?: string;
57
+ variant?: VariantProps<typeof itemVariants>['variant'];
58
+ size?: VariantProps<typeof itemVariants>['size'];
59
+ // OCTANE DIVERGENCE: asChild composes element descriptors (createElement),
60
+ // not opaque compiled .tsrx children — the documented radix Slot contract.
61
+ asChild?: boolean;
62
+ }
63
+
64
+ export function Item({
65
+ className,
66
+ variant = 'default',
67
+ size = 'default',
68
+ asChild = false,
69
+ ...props
70
+ }: ItemProps) @{
71
+ @if (asChild) {
72
+ <Slot
73
+ data-slot="item"
74
+ data-variant={variant}
75
+ data-size={size}
76
+ className={cn(itemVariants({ variant, size, className }))}
77
+ {...props}
78
+ />
79
+ } @else {
80
+ <div
81
+ data-slot="item"
82
+ data-variant={variant}
83
+ data-size={size}
84
+ className={cn(itemVariants({ variant, size, className }))}
85
+ {...props}
86
+ />
87
+ }
88
+ }
89
+
90
+ export const itemMediaVariants = cva(
91
+ 'flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none',
92
+ {
93
+ variants: {
94
+ variant: {
95
+ default: 'bg-transparent',
96
+ icon: '[&_svg:not([class*=\'size-\'])]:size-4',
97
+ image: 'size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover',
98
+ },
99
+ },
100
+ defaultVariants: {
101
+ variant: 'default',
102
+ },
103
+ },
104
+ );
105
+
106
+ export interface ItemMediaProps extends Record<string, unknown> {
107
+ className?: string;
108
+ variant?: VariantProps<typeof itemMediaVariants>['variant'];
109
+ }
110
+
111
+ export function ItemMedia({ className, variant = 'default', ...props }: ItemMediaProps) @{
112
+ <div
113
+ data-slot="item-media"
114
+ data-variant={variant}
115
+ className={cn(itemMediaVariants({ variant, className }))}
116
+ {...props}
117
+ />
118
+ }
119
+
120
+ export function ItemContent({ className, ...props }: DivProps) @{
121
+ <div
122
+ data-slot="item-content"
123
+ className={cn(
124
+ 'flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none',
125
+ className,
126
+ )}
127
+ {...props}
128
+ />
129
+ }
130
+
131
+ export function ItemTitle({ className, ...props }: DivProps) @{
132
+ <div
133
+ data-slot="item-title"
134
+ className={cn(
135
+ 'line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4',
136
+ className,
137
+ )}
138
+ {...props}
139
+ />
140
+ }
141
+
142
+ export function ItemDescription({ className, ...props }: DivProps) @{
143
+ <p
144
+ data-slot="item-description"
145
+ className={cn(
146
+ 'line-clamp-2 text-left text-sm leading-normal font-normal text-muted-foreground group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary',
147
+ className,
148
+ )}
149
+ {...props}
150
+ />
151
+ }
152
+
153
+ export function ItemActions({ className, ...props }: DivProps) @{
154
+ <div data-slot="item-actions" className={cn('flex items-center gap-2', className)} {...props} />
155
+ }
156
+
157
+ export function ItemHeader({ className, ...props }: DivProps) @{
158
+ <div
159
+ data-slot="item-header"
160
+ className={cn('flex basis-full items-center justify-between gap-2', className)}
161
+ {...props}
162
+ />
163
+ }
164
+
165
+ export function ItemFooter({ className, ...props }: DivProps) @{
166
+ <div
167
+ data-slot="item-footer"
168
+ className={cn('flex basis-full items-center justify-between gap-2', className)}
169
+ {...props}
170
+ />
171
+ }
@@ -0,0 +1,26 @@
1
+ // Ported from shadcn-ui/ui@4baadbc6517070ae8f8feb2c97037adc2b305544
2
+ // apps/v4/registry/bases/radix/ui/kbd.tsx. Note: upstream renders KbdGroup as
3
+ // a <kbd> host too — preserved as-is.
4
+ import { cn } from '../lib/utils';
5
+ import type { HostProps } from '../lib/types';
6
+
7
+ export interface KbdProps extends HostProps<'kbd'> {}
8
+
9
+ export function Kbd({ className, ...props }: KbdProps) @{
10
+ <kbd
11
+ data-slot="kbd"
12
+ className={cn(
13
+ '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',
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ }
19
+
20
+ export function KbdGroup({ className, ...props }: KbdProps) @{
21
+ <kbd
22
+ data-slot="kbd-group"
23
+ className={cn('inline-flex items-center gap-1', className)}
24
+ {...props}
25
+ />
26
+ }
@@ -0,0 +1,21 @@
1
+ // Ported from shadcn-ui/ui@4baadbc6517070ae8f8feb2c97037adc2b305544
2
+ // apps/v4/registry/bases/radix/ui/label.tsx (the `"use client"` directive is
3
+ // dropped — octane has no RSC axis).
4
+ import { Label as LabelPrimitive } from '@octanejs/radix';
5
+
6
+ import { cn } from '../lib/utils';
7
+
8
+ export interface LabelProps extends Record<string, unknown> {
9
+ className?: string;
10
+ }
11
+
12
+ export function Label({ className, ...props }: LabelProps) @{
13
+ <LabelPrimitive.Root
14
+ data-slot="label"
15
+ className={cn(
16
+ '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',
17
+ className,
18
+ )}
19
+ {...props}
20
+ />
21
+ }
@@ -0,0 +1,198 @@
1
+ // Menubar — maintainer-supplied default-Tailwind flavor, class strings
2
+ // verbatim. Octane adaptations: "use client" dropped, lucide-react →
3
+ // @octanejs/lucide, radix-ui → @octanejs/radix. Content composes through the
4
+ // local MenubarPortal wrapper with createElement (radix's Portal slots its
5
+ // child); parts interleaving consumer children with sibling descriptors use
6
+ // the {props.children} lowering in template JSX.
7
+ import { createElement, type OctaneNode } from 'octane';
8
+ import { Menubar as MenubarPrimitive } from '@octanejs/radix';
9
+ import { CheckIcon, ChevronRightIcon } from '@octanejs/lucide';
10
+
11
+ import { cn } from '../lib/utils';
12
+
13
+ type Props = { className?: string; children?: OctaneNode } & Record<string, unknown>;
14
+
15
+ export function Menubar({ className, ...props }: Props) @{
16
+ <MenubarPrimitive.Root
17
+ data-slot="menubar"
18
+ className={cn('flex h-8 items-center gap-0.5 rounded-lg border p-[3px]', className)}
19
+ {...props}
20
+ />
21
+ }
22
+
23
+ export function MenubarMenu(props: Record<string, unknown>) @{
24
+ <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />
25
+ }
26
+
27
+ export function MenubarGroup(props: Record<string, unknown>) @{
28
+ <MenubarPrimitive.Group data-slot="menubar-group" {...props} />
29
+ }
30
+
31
+ export function MenubarPortal(props: Record<string, unknown>) @{
32
+ <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} />
33
+ }
34
+
35
+ export function MenubarRadioGroup(props: Record<string, unknown>) @{
36
+ <MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />
37
+ }
38
+
39
+ export function MenubarTrigger({ className, ...props }: Props) @{
40
+ <MenubarPrimitive.Trigger
41
+ data-slot="menubar-trigger"
42
+ className={cn(
43
+ 'flex items-center rounded-sm px-1.5 py-[2px] text-sm font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted',
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ }
49
+
50
+ export function MenubarContent({
51
+ className,
52
+ align = 'start',
53
+ alignOffset = -4,
54
+ sideOffset = 8,
55
+ ...props
56
+ }: Props & { align?: string; alignOffset?: number; sideOffset?: number }) {
57
+ return createElement(
58
+ MenubarPortal,
59
+ {},
60
+ createElement(MenubarPrimitive.Content, {
61
+ 'data-slot': 'menubar-content',
62
+ align,
63
+ alignOffset,
64
+ sideOffset,
65
+ className: cn(
66
+ 'z-50 min-w-36 origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 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',
67
+ className,
68
+ ),
69
+ ...props,
70
+ }),
71
+ );
72
+ }
73
+
74
+ export function MenubarItem({ className, inset, variant = 'default', ...props }: Props & {
75
+ inset?: boolean;
76
+ variant?: 'default' | 'destructive';
77
+ }) @{
78
+ <MenubarPrimitive.Item
79
+ data-slot="menubar-item"
80
+ data-inset={inset}
81
+ data-variant={variant}
82
+ className={cn(
83
+ 'group/menubar-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive!',
84
+ className,
85
+ )}
86
+ {...props}
87
+ />
88
+ }
89
+
90
+ export function MenubarCheckboxItem(props: Props & {
91
+ checked?: boolean | 'indeterminate';
92
+ inset?: boolean;
93
+ }) @{
94
+ const { className, children: _children, checked, inset, ...rest } = props;
95
+
96
+ <MenubarPrimitive.CheckboxItem
97
+ data-slot="menubar-checkbox-item"
98
+ data-inset={inset}
99
+ className={cn(
100
+ 'relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0',
101
+ className,
102
+ )}
103
+ checked={checked}
104
+ {...rest}
105
+ >
106
+ <span
107
+ className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"
108
+ >
109
+ <MenubarPrimitive.ItemIndicator>
110
+ <CheckIcon />
111
+ </MenubarPrimitive.ItemIndicator>
112
+ </span>
113
+ {props.children}
114
+ </MenubarPrimitive.CheckboxItem>
115
+ }
116
+
117
+ export function MenubarRadioItem(props: Props & { inset?: boolean }) @{
118
+ const { className, children: _children, inset, ...rest } = props;
119
+
120
+ <MenubarPrimitive.RadioItem
121
+ data-slot="menubar-radio-item"
122
+ data-inset={inset}
123
+ className={cn(
124
+ 'relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
125
+ className,
126
+ )}
127
+ {...rest}
128
+ >
129
+ <span
130
+ className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"
131
+ >
132
+ <MenubarPrimitive.ItemIndicator>
133
+ <CheckIcon />
134
+ </MenubarPrimitive.ItemIndicator>
135
+ </span>
136
+ {props.children}
137
+ </MenubarPrimitive.RadioItem>
138
+ }
139
+
140
+ export function MenubarLabel({ className, inset, ...props }: Props & { inset?: boolean }) @{
141
+ <MenubarPrimitive.Label
142
+ data-slot="menubar-label"
143
+ data-inset={inset}
144
+ className={cn('px-1.5 py-1 text-sm font-medium data-inset:pl-7', className)}
145
+ {...props}
146
+ />
147
+ }
148
+
149
+ export function MenubarSeparator({ className, ...props }: Props) @{
150
+ <MenubarPrimitive.Separator
151
+ data-slot="menubar-separator"
152
+ className={cn('-mx-1 my-1 h-px bg-border', className)}
153
+ {...props}
154
+ />
155
+ }
156
+
157
+ export function MenubarShortcut({ className, ...props }: Props) @{
158
+ <span
159
+ data-slot="menubar-shortcut"
160
+ className={cn(
161
+ 'ml-auto text-xs tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground',
162
+ className,
163
+ )}
164
+ {...props}
165
+ />
166
+ }
167
+
168
+ export function MenubarSub(props: Record<string, unknown>) @{
169
+ <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />
170
+ }
171
+
172
+ export function MenubarSubTrigger(props: Props & { inset?: boolean }) @{
173
+ const { className, children: _children, inset, ...rest } = props;
174
+
175
+ <MenubarPrimitive.SubTrigger
176
+ data-slot="menubar-sub-trigger"
177
+ data-inset={inset}
178
+ className={cn(
179
+ 'flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg:not([class*=\'size-\'])]:size-4',
180
+ className,
181
+ )}
182
+ {...rest}
183
+ >
184
+ {props.children}
185
+ <ChevronRightIcon className="cn-rtl-flip ml-auto size-4" />
186
+ </MenubarPrimitive.SubTrigger>
187
+ }
188
+
189
+ export function MenubarSubContent({ className, ...props }: Props) @{
190
+ <MenubarPrimitive.SubContent
191
+ data-slot="menubar-sub-content"
192
+ className={cn(
193
+ 'z-50 min-w-32 origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 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',
194
+ className,
195
+ )}
196
+ {...props}
197
+ />
198
+ }