@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.
- package/dist/chunk-2HQPCV2L.js +895 -0
- package/dist/chunk-2HQPCV2L.js.map +1 -0
- package/dist/form.d.ts +175 -0
- package/dist/form.js +356 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +2162 -0
- package/dist/index.js +11640 -0
- package/dist/index.js.map +1 -0
- package/dist/input-JCZs61MO.d.ts +24 -0
- package/package.json +90 -0
- package/src/components/form/checkbox-group-field.tsx +101 -0
- package/src/components/form/date-field.tsx +79 -0
- package/src/components/form/date-range-field.tsx +106 -0
- package/src/components/form/form-context.ts +10 -0
- package/src/components/form/form.tsx +54 -0
- package/src/components/form/number-field.tsx +69 -0
- package/src/components/form/select-field.tsx +76 -0
- package/src/components/form/submit-button.tsx +28 -0
- package/src/components/form/text-field.tsx +107 -0
- package/src/components/layout/dashboard-header.tsx +54 -0
- package/src/components/layout/dashboard-panel.tsx +34 -0
- package/src/components/layout/dashboard-topbar.tsx +221 -0
- package/src/components/theme-provider.tsx +403 -0
- package/src/components/ui/accordion.tsx +69 -0
- package/src/components/ui/alert-dialog.tsx +169 -0
- package/src/components/ui/alert.tsx +80 -0
- package/src/components/ui/animated-counter.tsx +109 -0
- package/src/components/ui/animated-theme-toggler.tsx +265 -0
- package/src/components/ui/app-store-buttons.tsx +182 -0
- package/src/components/ui/aspect-ratio.tsx +23 -0
- package/src/components/ui/aurora-orb.tsx +163 -0
- package/src/components/ui/aurora-text.tsx +76 -0
- package/src/components/ui/autocomplete.tsx +296 -0
- package/src/components/ui/avatar-group.tsx +150 -0
- package/src/components/ui/avatar.tsx +285 -0
- package/src/components/ui/badge-group.tsx +160 -0
- package/src/components/ui/badge.tsx +172 -0
- package/src/components/ui/breadcrumb.tsx +112 -0
- package/src/components/ui/button.tsx +77 -0
- package/src/components/ui/calendar.tsx +137 -0
- package/src/components/ui/card.tsx +244 -0
- package/src/components/ui/carousel.tsx +258 -0
- package/src/components/ui/chart.tsx +379 -0
- package/src/components/ui/chat-divider.tsx +67 -0
- package/src/components/ui/chat.tsx +494 -0
- package/src/components/ui/checkbox-group.tsx +16 -0
- package/src/components/ui/checkbox.tsx +82 -0
- package/src/components/ui/collapsible.tsx +45 -0
- package/src/components/ui/color-picker.tsx +389 -0
- package/src/components/ui/combobox.tsx +411 -0
- package/src/components/ui/command.tsx +264 -0
- package/src/components/ui/confetti.tsx +196 -0
- package/src/components/ui/context-menu.tsx +271 -0
- package/src/components/ui/context-meter.tsx +134 -0
- package/src/components/ui/credit-card.tsx +214 -0
- package/src/components/ui/data-table-paged.tsx +163 -0
- package/src/components/ui/date-picker.tsx +154 -0
- package/src/components/ui/dialog.tsx +196 -0
- package/src/components/ui/drawer.tsx +135 -0
- package/src/components/ui/empty.tsx +127 -0
- package/src/components/ui/fab.tsx +45 -0
- package/src/components/ui/featured-icon.tsx +149 -0
- package/src/components/ui/field.tsx +88 -0
- package/src/components/ui/fieldset.tsx +29 -0
- package/src/components/ui/form.tsx +17 -0
- package/src/components/ui/frame.tsx +82 -0
- package/src/components/ui/generic-empty.tsx +142 -0
- package/src/components/ui/group.tsx +97 -0
- package/src/components/ui/help-tip.tsx +94 -0
- package/src/components/ui/horizontal-scroll-fader.tsx +228 -0
- package/src/components/ui/input-group.tsx +102 -0
- package/src/components/ui/input-otp.tsx +96 -0
- package/src/components/ui/input.tsx +66 -0
- package/src/components/ui/item.tsx +198 -0
- package/src/components/ui/kbd.tsx +30 -0
- package/src/components/ui/label.tsx +28 -0
- package/src/components/ui/markdown.tsx +188 -0
- package/src/components/ui/menu.tsx +312 -0
- package/src/components/ui/menubar.tsx +93 -0
- package/src/components/ui/meter.tsx +67 -0
- package/src/components/ui/multi-select.tsx +308 -0
- package/src/components/ui/navigation-menu.tsx +143 -0
- package/src/components/ui/number-field.tsx +160 -0
- package/src/components/ui/pagination-controls.tsx +74 -0
- package/src/components/ui/pagination.tsx +149 -0
- package/src/components/ui/pipeline.tsx +102 -0
- package/src/components/ui/popover.tsx +119 -0
- package/src/components/ui/preview-card.tsx +55 -0
- package/src/components/ui/progress.tsx +289 -0
- package/src/components/ui/qr-code.tsx +150 -0
- package/src/components/ui/radio-group.tsx +103 -0
- package/src/components/ui/resizable.tsx +56 -0
- package/src/components/ui/scroll-area.tsx +90 -0
- package/src/components/ui/scroller.tsx +38 -0
- package/src/components/ui/section-header.tsx +118 -0
- package/src/components/ui/segmented-control.tsx +83 -0
- package/src/components/ui/select.tsx +181 -0
- package/src/components/ui/separator.tsx +23 -0
- package/src/components/ui/sheet.tsx +224 -0
- package/src/components/ui/sidebar.tsx +774 -0
- package/src/components/ui/skeleton.tsx +16 -0
- package/src/components/ui/slider.tsx +108 -0
- package/src/components/ui/smooth-scroll.tsx +206 -0
- package/src/components/ui/social-button.tsx +247 -0
- package/src/components/ui/spinner-on-demand.tsx +32 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/stat.tsx +187 -0
- package/src/components/ui/stepper.tsx +167 -0
- package/src/components/ui/switch.tsx +56 -0
- package/src/components/ui/table.tsx +126 -0
- package/src/components/ui/tabs.tsx +90 -0
- package/src/components/ui/tag.tsx +242 -0
- package/src/components/ui/target-countdown.tsx +46 -0
- package/src/components/ui/text-editor.tsx +313 -0
- package/src/components/ui/textarea.tsx +51 -0
- package/src/components/ui/thinking-orb.tsx +6 -0
- package/src/components/ui/time-picker.tsx +308 -0
- package/src/components/ui/timeline.tsx +116 -0
- package/src/components/ui/toast.tsx +268 -0
- package/src/components/ui/toggle-group.tsx +101 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/toolbar.tsx +89 -0
- package/src/components/ui/tooltip.tsx +102 -0
- package/src/components/ui/vertical-scroll-fader.tsx +250 -0
- package/src/components/ui/video-player.tsx +275 -0
- package/src/components/upload/avatar-upload-base.tsx +131 -0
- package/src/components/upload/image-upload-base.tsx +112 -0
- package/src/form.ts +17 -0
- package/src/index.ts +143 -0
- package/src/lib/hooks/use-callback-ref.ts +15 -0
- package/src/lib/hooks/use-first-render.ts +11 -0
- package/src/lib/hooks/use-hover.ts +53 -0
- package/src/lib/hooks/use-is-tab-active.ts +17 -0
- package/src/lib/hooks/use-media-query.ts +164 -0
- package/src/lib/utils/css.ts +6 -0
- package/src/styles.css +329 -0
- package/src/types/helpers.ts +24 -0
- 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
|
+
};
|