@momentumworld/ui-react 0.17.0

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 (138) hide show
  1. package/dist/chunk-2HQPCV2L.js +895 -0
  2. package/dist/chunk-2HQPCV2L.js.map +1 -0
  3. package/dist/form.d.ts +175 -0
  4. package/dist/form.js +356 -0
  5. package/dist/form.js.map +1 -0
  6. package/dist/index.d.ts +2162 -0
  7. package/dist/index.js +11640 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/input-JCZs61MO.d.ts +24 -0
  10. package/package.json +90 -0
  11. package/src/components/form/checkbox-group-field.tsx +101 -0
  12. package/src/components/form/date-field.tsx +79 -0
  13. package/src/components/form/date-range-field.tsx +106 -0
  14. package/src/components/form/form-context.ts +10 -0
  15. package/src/components/form/form.tsx +54 -0
  16. package/src/components/form/number-field.tsx +69 -0
  17. package/src/components/form/select-field.tsx +76 -0
  18. package/src/components/form/submit-button.tsx +28 -0
  19. package/src/components/form/text-field.tsx +107 -0
  20. package/src/components/layout/dashboard-header.tsx +54 -0
  21. package/src/components/layout/dashboard-panel.tsx +34 -0
  22. package/src/components/layout/dashboard-topbar.tsx +221 -0
  23. package/src/components/theme-provider.tsx +403 -0
  24. package/src/components/ui/accordion.tsx +69 -0
  25. package/src/components/ui/alert-dialog.tsx +169 -0
  26. package/src/components/ui/alert.tsx +80 -0
  27. package/src/components/ui/animated-counter.tsx +109 -0
  28. package/src/components/ui/animated-theme-toggler.tsx +265 -0
  29. package/src/components/ui/app-store-buttons.tsx +182 -0
  30. package/src/components/ui/aspect-ratio.tsx +23 -0
  31. package/src/components/ui/aurora-orb.tsx +163 -0
  32. package/src/components/ui/aurora-text.tsx +76 -0
  33. package/src/components/ui/autocomplete.tsx +296 -0
  34. package/src/components/ui/avatar-group.tsx +150 -0
  35. package/src/components/ui/avatar.tsx +285 -0
  36. package/src/components/ui/badge-group.tsx +160 -0
  37. package/src/components/ui/badge.tsx +172 -0
  38. package/src/components/ui/breadcrumb.tsx +112 -0
  39. package/src/components/ui/button.tsx +77 -0
  40. package/src/components/ui/calendar.tsx +137 -0
  41. package/src/components/ui/card.tsx +244 -0
  42. package/src/components/ui/carousel.tsx +258 -0
  43. package/src/components/ui/chart.tsx +379 -0
  44. package/src/components/ui/chat-divider.tsx +67 -0
  45. package/src/components/ui/chat.tsx +494 -0
  46. package/src/components/ui/checkbox-group.tsx +16 -0
  47. package/src/components/ui/checkbox.tsx +82 -0
  48. package/src/components/ui/collapsible.tsx +45 -0
  49. package/src/components/ui/color-picker.tsx +389 -0
  50. package/src/components/ui/combobox.tsx +411 -0
  51. package/src/components/ui/command.tsx +264 -0
  52. package/src/components/ui/confetti.tsx +196 -0
  53. package/src/components/ui/context-menu.tsx +271 -0
  54. package/src/components/ui/context-meter.tsx +134 -0
  55. package/src/components/ui/credit-card.tsx +214 -0
  56. package/src/components/ui/data-table-paged.tsx +163 -0
  57. package/src/components/ui/date-picker.tsx +154 -0
  58. package/src/components/ui/dialog.tsx +196 -0
  59. package/src/components/ui/drawer.tsx +135 -0
  60. package/src/components/ui/empty.tsx +127 -0
  61. package/src/components/ui/fab.tsx +45 -0
  62. package/src/components/ui/featured-icon.tsx +149 -0
  63. package/src/components/ui/field.tsx +88 -0
  64. package/src/components/ui/fieldset.tsx +29 -0
  65. package/src/components/ui/form.tsx +17 -0
  66. package/src/components/ui/frame.tsx +82 -0
  67. package/src/components/ui/generic-empty.tsx +142 -0
  68. package/src/components/ui/group.tsx +97 -0
  69. package/src/components/ui/help-tip.tsx +94 -0
  70. package/src/components/ui/horizontal-scroll-fader.tsx +228 -0
  71. package/src/components/ui/input-group.tsx +102 -0
  72. package/src/components/ui/input-otp.tsx +96 -0
  73. package/src/components/ui/input.tsx +66 -0
  74. package/src/components/ui/item.tsx +198 -0
  75. package/src/components/ui/kbd.tsx +30 -0
  76. package/src/components/ui/label.tsx +28 -0
  77. package/src/components/ui/markdown.tsx +188 -0
  78. package/src/components/ui/menu.tsx +312 -0
  79. package/src/components/ui/menubar.tsx +93 -0
  80. package/src/components/ui/meter.tsx +67 -0
  81. package/src/components/ui/multi-select.tsx +308 -0
  82. package/src/components/ui/navigation-menu.tsx +143 -0
  83. package/src/components/ui/number-field.tsx +160 -0
  84. package/src/components/ui/pagination-controls.tsx +74 -0
  85. package/src/components/ui/pagination.tsx +149 -0
  86. package/src/components/ui/pipeline.tsx +102 -0
  87. package/src/components/ui/popover.tsx +119 -0
  88. package/src/components/ui/preview-card.tsx +55 -0
  89. package/src/components/ui/progress.tsx +289 -0
  90. package/src/components/ui/qr-code.tsx +150 -0
  91. package/src/components/ui/radio-group.tsx +103 -0
  92. package/src/components/ui/resizable.tsx +56 -0
  93. package/src/components/ui/scroll-area.tsx +90 -0
  94. package/src/components/ui/scroller.tsx +38 -0
  95. package/src/components/ui/section-header.tsx +118 -0
  96. package/src/components/ui/segmented-control.tsx +83 -0
  97. package/src/components/ui/select.tsx +181 -0
  98. package/src/components/ui/separator.tsx +23 -0
  99. package/src/components/ui/sheet.tsx +224 -0
  100. package/src/components/ui/sidebar.tsx +774 -0
  101. package/src/components/ui/skeleton.tsx +16 -0
  102. package/src/components/ui/slider.tsx +108 -0
  103. package/src/components/ui/smooth-scroll.tsx +206 -0
  104. package/src/components/ui/social-button.tsx +247 -0
  105. package/src/components/ui/spinner-on-demand.tsx +32 -0
  106. package/src/components/ui/spinner.tsx +18 -0
  107. package/src/components/ui/stat.tsx +187 -0
  108. package/src/components/ui/stepper.tsx +167 -0
  109. package/src/components/ui/switch.tsx +56 -0
  110. package/src/components/ui/table.tsx +126 -0
  111. package/src/components/ui/tabs.tsx +90 -0
  112. package/src/components/ui/tag.tsx +242 -0
  113. package/src/components/ui/target-countdown.tsx +46 -0
  114. package/src/components/ui/text-editor.tsx +313 -0
  115. package/src/components/ui/textarea.tsx +51 -0
  116. package/src/components/ui/thinking-orb.tsx +6 -0
  117. package/src/components/ui/time-picker.tsx +308 -0
  118. package/src/components/ui/timeline.tsx +116 -0
  119. package/src/components/ui/toast.tsx +268 -0
  120. package/src/components/ui/toggle-group.tsx +101 -0
  121. package/src/components/ui/toggle.tsx +45 -0
  122. package/src/components/ui/toolbar.tsx +89 -0
  123. package/src/components/ui/tooltip.tsx +102 -0
  124. package/src/components/ui/vertical-scroll-fader.tsx +250 -0
  125. package/src/components/ui/video-player.tsx +275 -0
  126. package/src/components/upload/avatar-upload-base.tsx +131 -0
  127. package/src/components/upload/image-upload-base.tsx +112 -0
  128. package/src/form.ts +17 -0
  129. package/src/index.ts +143 -0
  130. package/src/lib/hooks/use-callback-ref.ts +15 -0
  131. package/src/lib/hooks/use-first-render.ts +11 -0
  132. package/src/lib/hooks/use-hover.ts +53 -0
  133. package/src/lib/hooks/use-is-tab-active.ts +17 -0
  134. package/src/lib/hooks/use-media-query.ts +164 -0
  135. package/src/lib/utils/css.ts +6 -0
  136. package/src/styles.css +329 -0
  137. package/src/types/helpers.ts +24 -0
  138. package/src/types/react.d.ts +7 -0
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ import type * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils/css";
6
+
7
+ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
8
+ return (
9
+ <kbd
10
+ className={cn(
11
+ "pointer-events-none inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded bg-muted px-1 font-medium font-sans text-muted-foreground text-xs [&_svg:not([class*='size-'])]:size-3",
12
+ className,
13
+ )}
14
+ data-slot="kbd"
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ function KbdGroup({ className, ...props }: React.ComponentProps<"kbd">) {
21
+ return (
22
+ <kbd
23
+ className={cn("inline-flex items-center gap-1", className)}
24
+ data-slot="kbd-group"
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ export { Kbd, KbdGroup };
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import { mergeProps } from "@base-ui/react/merge-props";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+
6
+ import { cn } from "../../lib/utils/css";
7
+
8
+ function Label({
9
+ className,
10
+ render,
11
+ ...props
12
+ }: useRender.ComponentProps<"label">) {
13
+ const defaultProps = {
14
+ className: cn(
15
+ "inline-flex items-center gap-2 text-base/4.5 sm:text-sm/4 font-medium text-foreground",
16
+ className,
17
+ ),
18
+ "data-slot": "label",
19
+ };
20
+
21
+ return useRender({
22
+ defaultTagName: "label",
23
+ props: mergeProps<"label">(defaultProps, props),
24
+ render,
25
+ });
26
+ }
27
+
28
+ export { Label };
@@ -0,0 +1,188 @@
1
+ "use client";
2
+
3
+ import type * as React from "react";
4
+ import ReactMarkdown from "react-markdown";
5
+ import remarkGfm from "remark-gfm";
6
+
7
+ import { cn } from "../../lib/utils/css";
8
+
9
+ /** Common LaTeX command → readable-text replacements. */
10
+ const LATEX_SYMBOLS: Record<string, string> = {
11
+ times: "×",
12
+ cdot: "·",
13
+ div: "÷",
14
+ approx: "≈",
15
+ neq: "≠",
16
+ ne: "≠",
17
+ leq: "≤",
18
+ le: "≤",
19
+ geq: "≥",
20
+ ge: "≥",
21
+ pm: "±",
22
+ to: "→",
23
+ rightarrow: "→",
24
+ Rightarrow: "⇒",
25
+ infty: "∞",
26
+ cdots: "⋯",
27
+ ldots: "…",
28
+ deg: "°",
29
+ };
30
+
31
+ /** Wrap a fraction side in parens only when it holds an operator, so
32
+ * `\frac{116}{22}` → `116/22` but `\frac{a+b}{2}` → `(a+b)/2`. */
33
+ const wrapFracSide = (x: string): string => {
34
+ const t = x.trim();
35
+ return /[+\-*/×÷·\s]/.test(t) ? `(${t})` : t;
36
+ };
37
+
38
+ /**
39
+ * LLMs sometimes emit LaTeX math (`\( \frac{116}{22} \times 100 \approx 527\% \)`),
40
+ * which react-markdown renders as raw gibberish. We don't ship a full math
41
+ * engine (KaTeX + fonts) — assistant chat rarely needs typeset math — so this
42
+ * downgrades the common LaTeX constructs to readable plain text instead. Plain
43
+ * text with no backslash / `$` is returned untouched (fast path), and lone `$`
44
+ * amounts like "$100" are left alone (only `$…$` containing a command unwraps).
45
+ */
46
+ function normalizeLatex(src: string): string {
47
+ if (!src.includes("\\") && !src.includes("$")) return src;
48
+
49
+ let s = src;
50
+
51
+ // Unwrap math delimiters. `$$…$$` display + `\(…\)`/`\[…\]` inline always;
52
+ // single `$…$` only when the body looks like math (has a backslash).
53
+ s = s.replace(/\$\$([\s\S]*?)\$\$/g, " $1 ");
54
+ s = s.replace(/\$([^$\n]*\\[^$\n]*)\$/g, " $1 ");
55
+ s = s.replace(/\\[()[\]]/g, " ");
56
+
57
+ // \frac{a}{b} → a/b (a few passes for nesting), \sqrt{x} → √(x).
58
+ for (let i = 0; i < 3; i++) {
59
+ const next = s.replace(
60
+ /\\[dt]?frac\s*\{([^{}]*)\}\s*\{([^{}]*)\}/g,
61
+ (_m, a: string, b: string) => `${wrapFracSide(a)}/${wrapFracSide(b)}`,
62
+ );
63
+ if (next === s) break;
64
+ s = next;
65
+ }
66
+ s = s.replace(/\\sqrt\s*\{([^{}]*)\}/g, "√($1)");
67
+ s = s.replace(/\\(?:text|mathrm|mathbf|mathit|operatorname)\s*\{([^{}]*)\}/g, "$1");
68
+
69
+ // Known symbols → unicode; \left/\right dropped.
70
+ s = s.replace(/\\([a-zA-Z]+)/g, (m, name: string) => LATEX_SYMBOLS[name] ?? m);
71
+ s = s.replace(/\\(left|right)\b/g, "");
72
+
73
+ // Escaped chars (\% \$ \_ \{ …) and thin spaces (\, \; \!).
74
+ s = s.replace(/\\([%$&#_{}])/g, "$1");
75
+ s = s.replace(/\\[,;:!]/g, "");
76
+ s = s.replace(/\\ /g, " ");
77
+
78
+ // Any leftover unknown \command → keep the word, drop the backslash.
79
+ s = s.replace(/\\([a-zA-Z]+)/g, "$1");
80
+
81
+ // Collapse grouping braces ({x} → x) and runs of spaces.
82
+ for (let i = 0; i < 3; i++) {
83
+ const next = s.replace(/\{([^{}]*)\}/g, "$1");
84
+ if (next === s) break;
85
+ s = next;
86
+ }
87
+ return s.replace(/[ \t]{2,}/g, " ");
88
+ }
89
+
90
+ export interface MarkdownProps {
91
+ /** The markdown source to render. */
92
+ children: string;
93
+ className?: string;
94
+ }
95
+
96
+ /**
97
+ * Markdown renderer for assistant/chat content (GitHub-flavored markdown).
98
+ * Raw HTML in the source is NOT rendered — output is limited to markdown
99
+ * elements, which keeps untrusted model output safe to display.
100
+ */
101
+ function Markdown({ children, className }: MarkdownProps) {
102
+ return (
103
+ <div
104
+ className={cn(
105
+ "space-y-2 break-words text-foreground text-sm leading-relaxed",
106
+ className,
107
+ )}
108
+ data-slot="markdown"
109
+ >
110
+ <ReactMarkdown
111
+ components={{
112
+ a: ({ node: _node, ...props }) => (
113
+ <a
114
+ className="font-medium text-primary underline underline-offset-2 hover:opacity-80"
115
+ rel="noreferrer noopener"
116
+ target="_blank"
117
+ {...props}
118
+ />
119
+ ),
120
+ blockquote: ({ node: _node, ...props }) => (
121
+ <blockquote
122
+ className="border-border border-l-2 pl-3 text-muted-foreground italic"
123
+ {...props}
124
+ />
125
+ ),
126
+ code: ({ node: _node, className: codeClassName, ...props }) => {
127
+ const isBlock = /language-/.test(codeClassName ?? "");
128
+ return (
129
+ <code
130
+ className={cn(
131
+ "rounded bg-muted font-mono text-[0.85em]",
132
+ isBlock ? "block overflow-x-auto p-3" : "px-1 py-0.5",
133
+ codeClassName,
134
+ )}
135
+ {...props}
136
+ />
137
+ );
138
+ },
139
+ h1: ({ node: _node, ...props }) => (
140
+ <h1 className="font-semibold text-base" {...props} />
141
+ ),
142
+ h2: ({ node: _node, ...props }) => (
143
+ <h2 className="font-semibold text-base" {...props} />
144
+ ),
145
+ h3: ({ node: _node, ...props }) => (
146
+ <h3 className="font-semibold text-sm" {...props} />
147
+ ),
148
+ hr: ({ node: _node, ...props }) => (
149
+ <hr className="border-border" {...props} />
150
+ ),
151
+ li: ({ node: _node, ...props }) => <li className="my-0.5" {...props} />,
152
+ ol: ({ node: _node, ...props }) => (
153
+ <ol className="list-decimal space-y-1 pl-5" {...props} />
154
+ ),
155
+ pre: ({ node: _node, ...props }) => (
156
+ <pre
157
+ className="overflow-x-auto rounded-lg bg-muted p-0 text-[0.85em]"
158
+ {...props}
159
+ />
160
+ ),
161
+ table: ({ node: _node, ...props }) => (
162
+ <div className="overflow-x-auto rounded-lg border border-border">
163
+ <table className="w-full text-left text-sm" {...props} />
164
+ </div>
165
+ ),
166
+ td: ({ node: _node, ...props }) => (
167
+ <td className="border-border border-t px-3 py-1.5 align-top" {...props} />
168
+ ),
169
+ th: ({ node: _node, ...props }) => (
170
+ <th
171
+ className="bg-muted px-3 py-1.5 font-medium text-muted-foreground"
172
+ {...props}
173
+ />
174
+ ),
175
+ ul: ({ node: _node, ...props }) => (
176
+ <ul className="list-disc space-y-1 pl-5" {...props} />
177
+ ),
178
+ }}
179
+ remarkPlugins={[remarkGfm]}
180
+ skipHtml
181
+ >
182
+ {normalizeLatex(children)}
183
+ </ReactMarkdown>
184
+ </div>
185
+ );
186
+ }
187
+
188
+ export { Markdown };
@@ -0,0 +1,312 @@
1
+ "use client";
2
+
3
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
4
+ import { ChevronRightIcon } from "lucide-react";
5
+ import type * as React from "react";
6
+ import { cn } from "../../lib/utils/css";
7
+
8
+ const MenuCreateHandle = MenuPrimitive.createHandle;
9
+
10
+ const Menu = MenuPrimitive.Root;
11
+
12
+ const MenuPortal = MenuPrimitive.Portal;
13
+
14
+ function MenuTrigger(props: MenuPrimitive.Trigger.Props) {
15
+ return <MenuPrimitive.Trigger data-slot="menu-trigger" {...props} />;
16
+ }
17
+
18
+ function MenuPopup({
19
+ children,
20
+ className,
21
+ sideOffset = 4,
22
+ align = "center",
23
+ alignOffset,
24
+ side = "bottom",
25
+ ...props
26
+ }: MenuPrimitive.Popup.Props & {
27
+ align?: MenuPrimitive.Positioner.Props["align"];
28
+ sideOffset?: MenuPrimitive.Positioner.Props["sideOffset"];
29
+ alignOffset?: MenuPrimitive.Positioner.Props["alignOffset"];
30
+ side?: MenuPrimitive.Positioner.Props["side"];
31
+ }) {
32
+ return (
33
+ <MenuPrimitive.Portal>
34
+ <MenuPrimitive.Positioner
35
+ align={align}
36
+ alignOffset={alignOffset}
37
+ className="z-50"
38
+ data-slot="menu-positioner"
39
+ side={side}
40
+ sideOffset={sideOffset}
41
+ >
42
+ <MenuPrimitive.Popup
43
+ className={cn(
44
+ "relative flex not-[class*='w-']:min-w-32 origin-(--transform-origin) rounded-lg border bg-popover not-dark:bg-clip-padding shadow-lg/5 outline-none transition-[transform,scale,opacity] duration-150 ease-out before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] focus:outline-none data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
45
+ className,
46
+ )}
47
+ data-slot="menu-popup"
48
+ {...props}
49
+ >
50
+ <div className="max-h-(--available-height) w-full overflow-y-auto p-1">
51
+ {children}
52
+ </div>
53
+ </MenuPrimitive.Popup>
54
+ </MenuPrimitive.Positioner>
55
+ </MenuPrimitive.Portal>
56
+ );
57
+ }
58
+
59
+ function MenuGroup(props: MenuPrimitive.Group.Props) {
60
+ return <MenuPrimitive.Group data-slot="menu-group" {...props} />;
61
+ }
62
+
63
+ function MenuItem({
64
+ className,
65
+ inset,
66
+ variant = "default",
67
+ ...props
68
+ }: MenuPrimitive.Item.Props & {
69
+ inset?: boolean;
70
+ variant?: "default" | "destructive";
71
+ }) {
72
+ return (
73
+ <MenuPrimitive.Item
74
+ className={cn(
75
+ "[&>svg]:-mx-0.5 flex min-h-8 cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-inset:ps-8 data-[variant=destructive]:text-destructive-foreground data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&>svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4 [&>svg]:pointer-events-none [&>svg]:shrink-0",
76
+ className,
77
+ )}
78
+ data-inset={inset}
79
+ data-slot="menu-item"
80
+ data-variant={variant}
81
+ {...props}
82
+ />
83
+ );
84
+ }
85
+
86
+ function MenuCheckboxItem({
87
+ className,
88
+ children,
89
+ checked,
90
+ variant = "default",
91
+ ...props
92
+ }: MenuPrimitive.CheckboxItem.Props & {
93
+ variant?: "default" | "switch";
94
+ }) {
95
+ return (
96
+ <MenuPrimitive.CheckboxItem
97
+ checked={checked}
98
+ className={cn(
99
+ "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default items-center gap-2 rounded-sm py-1 ps-2 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
100
+ variant === "switch"
101
+ ? "grid-cols-[1fr_auto] gap-4 pe-1.5"
102
+ : "grid-cols-[1rem_1fr] pe-4",
103
+ className,
104
+ )}
105
+ data-slot="menu-checkbox-item"
106
+ {...props}
107
+ >
108
+ {variant === "switch" ? (
109
+ <>
110
+ <span className="col-start-1">{children}</span>
111
+ <MenuPrimitive.CheckboxItemIndicator
112
+ className="inset-shadow-[0_1px_--theme(--color-black/6%)] inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px outline-none transition-[background-color,box-shadow] duration-200 [--thumb-size:--spacing(4)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-64 sm:[--thumb-size:--spacing(3)]"
113
+ keepMounted
114
+ >
115
+ <span className="pointer-events-none block aspect-square h-full in-[[data-slot=menu-checkbox-item][data-checked]]:origin-[var(--thumb-size)_50%] origin-left in-[[data-slot=menu-checkbox-item][data-checked]]:translate-x-[calc(var(--thumb-size)-4px)] in-[[data-slot=menu-checkbox-item]:active]:not-data-disabled:scale-x-110 in-[[data-slot=menu-checkbox-item]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.10)] rounded-(--thumb-size) bg-background shadow-sm/5 will-change-transform [transition:translate_.15s,border-radius_.15s,scale_.1s_.1s,transform-origin_.15s]" />
116
+ </MenuPrimitive.CheckboxItemIndicator>
117
+ </>
118
+ ) : (
119
+ <>
120
+ <MenuPrimitive.CheckboxItemIndicator className="col-start-1">
121
+ <svg
122
+ fill="none"
123
+ height="24"
124
+ stroke="currentColor"
125
+ strokeLinecap="round"
126
+ strokeLinejoin="round"
127
+ strokeWidth="2"
128
+ viewBox="0 0 24 24"
129
+ width="24"
130
+ xmlns="http://www.w3.org/2000/svg"
131
+ >
132
+ <title>Check</title>
133
+ <path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
134
+ </svg>
135
+ </MenuPrimitive.CheckboxItemIndicator>
136
+ <span className="col-start-2">{children}</span>
137
+ </>
138
+ )}
139
+ </MenuPrimitive.CheckboxItem>
140
+ );
141
+ }
142
+
143
+ function MenuRadioGroup(props: MenuPrimitive.RadioGroup.Props) {
144
+ return <MenuPrimitive.RadioGroup data-slot="menu-radio-group" {...props} />;
145
+ }
146
+
147
+ function MenuRadioItem({
148
+ className,
149
+ children,
150
+ ...props
151
+ }: MenuPrimitive.RadioItem.Props) {
152
+ return (
153
+ <MenuPrimitive.RadioItem
154
+ className={cn(
155
+ "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[1rem_1fr] items-center gap-2 rounded-sm py-1 ps-2 pe-4 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
156
+ className,
157
+ )}
158
+ data-slot="menu-radio-item"
159
+ {...props}
160
+ >
161
+ <MenuPrimitive.RadioItemIndicator className="col-start-1">
162
+ <svg
163
+ fill="none"
164
+ height="24"
165
+ stroke="currentColor"
166
+ strokeLinecap="round"
167
+ strokeLinejoin="round"
168
+ strokeWidth="2"
169
+ viewBox="0 0 24 24"
170
+ width="24"
171
+ xmlns="http://www.w3.org/2000/svg"
172
+ >
173
+ <title>Check</title>
174
+ <path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
175
+ </svg>
176
+ </MenuPrimitive.RadioItemIndicator>
177
+ <span className="col-start-2">{children}</span>
178
+ </MenuPrimitive.RadioItem>
179
+ );
180
+ }
181
+
182
+ function MenuGroupLabel({
183
+ className,
184
+ inset,
185
+ ...props
186
+ }: MenuPrimitive.GroupLabel.Props & {
187
+ inset?: boolean;
188
+ }) {
189
+ return (
190
+ <MenuPrimitive.GroupLabel
191
+ className={cn(
192
+ "px-2 py-1.5 font-medium text-muted-foreground text-xs data-inset:ps-9 sm:data-inset:ps-8",
193
+ className,
194
+ )}
195
+ data-inset={inset}
196
+ data-slot="menu-label"
197
+ {...props}
198
+ />
199
+ );
200
+ }
201
+
202
+ function MenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {
203
+ return (
204
+ <MenuPrimitive.Separator
205
+ className={cn("mx-2 my-1 h-px bg-border", className)}
206
+ data-slot="menu-separator"
207
+ {...props}
208
+ />
209
+ );
210
+ }
211
+
212
+ function MenuShortcut({ className, ...props }: React.ComponentProps<"kbd">) {
213
+ return (
214
+ <kbd
215
+ className={cn(
216
+ "ms-auto font-medium font-sans text-muted-foreground/72 text-xs tracking-widest",
217
+ className,
218
+ )}
219
+ data-slot="menu-shortcut"
220
+ {...props}
221
+ />
222
+ );
223
+ }
224
+
225
+ function MenuSub(props: MenuPrimitive.SubmenuRoot.Props) {
226
+ return <MenuPrimitive.SubmenuRoot data-slot="menu-sub" {...props} />;
227
+ }
228
+
229
+ function MenuSubTrigger({
230
+ className,
231
+ inset,
232
+ children,
233
+ ...props
234
+ }: MenuPrimitive.SubmenuTrigger.Props & {
235
+ inset?: boolean;
236
+ }) {
237
+ return (
238
+ <MenuPrimitive.SubmenuTrigger
239
+ className={cn(
240
+ "flex min-h-8 items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-popup-open:bg-accent data-inset:ps-8 data-highlighted:text-accent-foreground data-popup-open:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
241
+ className,
242
+ )}
243
+ data-inset={inset}
244
+ data-slot="menu-sub-trigger"
245
+ {...props}
246
+ >
247
+ {children}
248
+ <ChevronRightIcon className="-me-0.5 ms-auto opacity-80" />
249
+ </MenuPrimitive.SubmenuTrigger>
250
+ );
251
+ }
252
+
253
+ function MenuSubPopup({
254
+ className,
255
+ sideOffset = 0,
256
+ alignOffset,
257
+ align = "start",
258
+ ...props
259
+ }: MenuPrimitive.Popup.Props & {
260
+ align?: MenuPrimitive.Positioner.Props["align"];
261
+ sideOffset?: MenuPrimitive.Positioner.Props["sideOffset"];
262
+ alignOffset?: MenuPrimitive.Positioner.Props["alignOffset"];
263
+ }) {
264
+ const defaultAlignOffset = align !== "center" ? -5 : undefined;
265
+
266
+ return (
267
+ <MenuPopup
268
+ align={align}
269
+ alignOffset={alignOffset ?? defaultAlignOffset}
270
+ className={className}
271
+ data-slot="menu-sub-content"
272
+ side="inline-end"
273
+ sideOffset={sideOffset}
274
+ {...props}
275
+ />
276
+ );
277
+ }
278
+
279
+ export {
280
+ MenuCreateHandle,
281
+ MenuCreateHandle as DropdownMenuCreateHandle,
282
+ Menu,
283
+ Menu as DropdownMenu,
284
+ MenuPortal,
285
+ MenuPortal as DropdownMenuPortal,
286
+ MenuTrigger,
287
+ MenuTrigger as DropdownMenuTrigger,
288
+ MenuPopup,
289
+ MenuPopup as DropdownMenuContent,
290
+ MenuGroup,
291
+ MenuGroup as DropdownMenuGroup,
292
+ MenuItem,
293
+ MenuItem as DropdownMenuItem,
294
+ MenuCheckboxItem,
295
+ MenuCheckboxItem as DropdownMenuCheckboxItem,
296
+ MenuRadioGroup,
297
+ MenuRadioGroup as DropdownMenuRadioGroup,
298
+ MenuRadioItem,
299
+ MenuRadioItem as DropdownMenuRadioItem,
300
+ MenuGroupLabel,
301
+ MenuGroupLabel as DropdownMenuLabel,
302
+ MenuSeparator,
303
+ MenuSeparator as DropdownMenuSeparator,
304
+ MenuShortcut,
305
+ MenuShortcut as DropdownMenuShortcut,
306
+ MenuSub,
307
+ MenuSub as DropdownMenuSub,
308
+ MenuSubTrigger,
309
+ MenuSubTrigger as DropdownMenuSubTrigger,
310
+ MenuSubPopup,
311
+ MenuSubPopup as DropdownMenuSubContent,
312
+ };
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
4
+ import {
5
+ Menubar as MenubarPrimitive,
6
+ type MenubarProps,
7
+ } from "@base-ui/react/menubar";
8
+ import type * as React from "react";
9
+
10
+ import { cn } from "../../lib/utils/css";
11
+ import {
12
+ Menu,
13
+ MenuCheckboxItem,
14
+ MenuGroup,
15
+ MenuGroupLabel,
16
+ MenuItem,
17
+ MenuPopup,
18
+ MenuPortal,
19
+ MenuRadioGroup,
20
+ MenuRadioItem,
21
+ MenuSeparator,
22
+ MenuShortcut,
23
+ MenuSub,
24
+ MenuSubPopup,
25
+ MenuSubTrigger,
26
+ } from "./menu";
27
+
28
+ function Menubar({ className, ...props }: MenubarProps) {
29
+ return (
30
+ <MenubarPrimitive
31
+ className={cn(
32
+ "flex h-9 w-fit items-center gap-1 rounded-lg border bg-background p-1 shadow-xs/5 sm:h-8 dark:bg-input/32",
33
+ className,
34
+ )}
35
+ data-slot="menubar"
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function MenubarMenu(props: MenuPrimitive.Root.Props) {
42
+ return <Menu data-slot="menubar-menu" {...props} />;
43
+ }
44
+
45
+ function MenubarTrigger({
46
+ className,
47
+ ...props
48
+ }: MenuPrimitive.Trigger.Props) {
49
+ return (
50
+ <MenuPrimitive.Trigger
51
+ className={cn(
52
+ "flex select-none items-center rounded-md px-2 py-1 font-medium text-base outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-popup-open:bg-accent data-popup-open:text-accent-foreground sm:text-sm",
53
+ className,
54
+ )}
55
+ data-slot="menubar-trigger"
56
+ {...props}
57
+ />
58
+ );
59
+ }
60
+
61
+ function MenubarContent({
62
+ align = "start",
63
+ sideOffset = 8,
64
+ ...props
65
+ }: React.ComponentProps<typeof MenuPopup>) {
66
+ return (
67
+ <MenuPopup
68
+ align={align}
69
+ data-slot="menubar-content"
70
+ sideOffset={sideOffset}
71
+ {...props}
72
+ />
73
+ );
74
+ }
75
+
76
+ export {
77
+ Menubar,
78
+ MenubarMenu,
79
+ MenubarTrigger,
80
+ MenubarContent,
81
+ MenuPortal as MenubarPortal,
82
+ MenuGroup as MenubarGroup,
83
+ MenuItem as MenubarItem,
84
+ MenuCheckboxItem as MenubarCheckboxItem,
85
+ MenuRadioGroup as MenubarRadioGroup,
86
+ MenuRadioItem as MenubarRadioItem,
87
+ MenuGroupLabel as MenubarLabel,
88
+ MenuSeparator as MenubarSeparator,
89
+ MenuShortcut as MenubarShortcut,
90
+ MenuSub as MenubarSub,
91
+ MenuSubTrigger as MenubarSubTrigger,
92
+ MenuSubPopup as MenubarSubContent,
93
+ };