@moloodev/ui 0.0.0-stage → 0.2.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/package.json +139 -3
- package/registry/components.ts +135 -0
- package/registry/fonts.css +81 -0
- package/registry/suit-OFL.txt +94 -0
- package/registry/wanted-sans-OFL.txt +93 -0
- package/src/components/ui/accordion.tsx +74 -0
- package/src/components/ui/alert-dialog.tsx +187 -0
- package/src/components/ui/alert.tsx +83 -0
- package/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/components/ui/attachment.tsx +207 -0
- package/src/components/ui/avatar.tsx +109 -0
- package/src/components/ui/badge.tsx +54 -0
- package/src/components/ui/breadcrumb.tsx +126 -0
- package/src/components/ui/bubble.tsx +128 -0
- package/src/components/ui/button-group.tsx +103 -0
- package/src/components/ui/button.tsx +68 -0
- package/src/components/ui/calendar.tsx +222 -0
- package/src/components/ui/card.tsx +103 -0
- package/src/components/ui/carousel.tsx +242 -0
- package/src/components/ui/chart.tsx +398 -0
- package/src/components/ui/checkbox.tsx +28 -0
- package/src/components/ui/collapsible.tsx +40 -0
- package/src/components/ui/combobox.tsx +299 -0
- package/src/components/ui/command.tsx +218 -0
- package/src/components/ui/context-menu.tsx +271 -0
- package/src/components/ui/data-table.tsx +919 -0
- package/src/components/ui/date-picker.tsx +65 -0
- package/src/components/ui/dialog.tsx +224 -0
- package/src/components/ui/drawer.tsx +231 -0
- package/src/components/ui/dropdown-menu.tsx +268 -0
- package/src/components/ui/dropzone.tsx +186 -0
- package/src/components/ui/empty.tsx +101 -0
- package/src/components/ui/field.tsx +238 -0
- package/src/components/ui/form.tsx +83 -0
- package/src/components/ui/highlighted-source.tsx +200 -0
- package/src/components/ui/hover-card.tsx +51 -0
- package/src/components/ui/image-field.tsx +796 -0
- package/src/components/ui/input-group.tsx +149 -0
- package/src/components/ui/input-otp.tsx +86 -0
- package/src/components/ui/input-step.ts +19 -0
- package/src/components/ui/input.tsx +100 -0
- package/src/components/ui/item.tsx +201 -0
- package/src/components/ui/kbd.tsx +91 -0
- package/src/components/ui/kind-icon-glob.ts +24 -0
- package/src/components/ui/kind-icon-load.ts +52 -0
- package/src/components/ui/kind-icon-static.ts +46 -0
- package/src/components/ui/kind-icon.tsx +84 -0
- package/src/components/ui/kind-mark.tsx +65 -0
- package/src/components/ui/label.tsx +29 -0
- package/src/components/ui/marker.tsx +71 -0
- package/src/components/ui/menubar.tsx +277 -0
- package/src/components/ui/message-scroller.tsx +137 -0
- package/src/components/ui/message.tsx +92 -0
- package/src/components/ui/moloo-logo.tsx +45 -0
- package/src/components/ui/native-select.tsx +61 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/outline-list.tsx +73 -0
- package/src/components/ui/pagination.tsx +130 -0
- package/src/components/ui/panel.tsx +108 -0
- package/src/components/ui/popover.tsx +90 -0
- package/src/components/ui/progress.tsx +117 -0
- package/src/components/ui/radio-group.tsx +38 -0
- package/src/components/ui/resizable.tsx +52 -0
- package/src/components/ui/scroll-area.tsx +54 -0
- package/src/components/ui/select.tsx +205 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/sheet.tsx +140 -0
- package/src/components/ui/sidebar.tsx +746 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +52 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +83 -0
- package/src/components/ui/tag-list-input.tsx +194 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/timeline.tsx +265 -0
- package/src/components/ui/toast.tsx +237 -0
- package/src/components/ui/toggle-group.tsx +90 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/css.d.ts +1 -0
- package/src/hooks/use-clipboard.ts +82 -0
- package/src/hooks/use-mobile.ts +21 -0
- package/src/index.ts +106 -0
- package/src/kind-icon.ts +4 -0
- package/src/lib/format-relative-time.ts +41 -0
- package/src/lib/list-selection.ts +61 -0
- package/src/lib/utils.ts +6 -0
- package/src/moloo.css +360 -0
- package/src/styles.css +36 -0
- package/src/typeset.css +518 -0
- package/README.md +0 -3
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Icon } from "@tabler/icons-react"
|
|
2
|
+
|
|
3
|
+
type IconLoader = () => Promise<{ default: Icon }>
|
|
4
|
+
|
|
5
|
+
let loaderMap: Promise<Map<string, IconLoader>> | null = null
|
|
6
|
+
|
|
7
|
+
function viteLoaders(): Promise<Map<string, IconLoader>> {
|
|
8
|
+
if (!loaderMap) {
|
|
9
|
+
loaderMap = import("./kind-icon-glob")
|
|
10
|
+
.then((mod) => mod.tablerIconLoaders)
|
|
11
|
+
.catch((error: unknown) => {
|
|
12
|
+
loaderMap = null
|
|
13
|
+
throw error
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
return loaderMap
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isIcon(value: unknown): value is Icon {
|
|
20
|
+
if (typeof value === "function") return true
|
|
21
|
+
return (
|
|
22
|
+
typeof value === "object" &&
|
|
23
|
+
value !== null &&
|
|
24
|
+
"render" in value &&
|
|
25
|
+
typeof value.render === "function"
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isIconModule(value: unknown): value is { default: Icon } {
|
|
30
|
+
return typeof value === "object" && value !== null && "default" in value && isIcon(value.default)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function loadTablerIcon(component: string): Promise<Icon | null> {
|
|
34
|
+
let loaders: Map<string, IconLoader>
|
|
35
|
+
try {
|
|
36
|
+
loaders = await viteLoaders()
|
|
37
|
+
} catch {
|
|
38
|
+
loaders = new Map()
|
|
39
|
+
}
|
|
40
|
+
const loader = loaders.get(component)
|
|
41
|
+
try {
|
|
42
|
+
if (loader) return (await loader()).default ?? null
|
|
43
|
+
const mod: unknown = await import(
|
|
44
|
+
/* @vite-ignore */
|
|
45
|
+
`@tabler/icons-react/dist/esm/icons/${component}.mjs`
|
|
46
|
+
)
|
|
47
|
+
if (!isIconModule(mod)) return null
|
|
48
|
+
return mod.default
|
|
49
|
+
} catch {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IconArchive,
|
|
3
|
+
IconCode,
|
|
4
|
+
IconCube,
|
|
5
|
+
IconDatabase,
|
|
6
|
+
IconFile,
|
|
7
|
+
IconFileText,
|
|
8
|
+
IconFolder,
|
|
9
|
+
IconHelp,
|
|
10
|
+
IconHierarchy,
|
|
11
|
+
IconLayout,
|
|
12
|
+
IconMarkdown,
|
|
13
|
+
IconMovie,
|
|
14
|
+
IconMusic,
|
|
15
|
+
IconPackage,
|
|
16
|
+
IconPhoto,
|
|
17
|
+
IconTable,
|
|
18
|
+
IconTypography,
|
|
19
|
+
type Icon,
|
|
20
|
+
} from "@tabler/icons-react"
|
|
21
|
+
|
|
22
|
+
const icons: Record<string, Icon> = {
|
|
23
|
+
IconArchive,
|
|
24
|
+
IconCode,
|
|
25
|
+
IconCube,
|
|
26
|
+
IconDatabase,
|
|
27
|
+
IconFile,
|
|
28
|
+
IconFileText,
|
|
29
|
+
IconFolder,
|
|
30
|
+
IconHelp,
|
|
31
|
+
IconHierarchy,
|
|
32
|
+
IconLayout,
|
|
33
|
+
IconMarkdown,
|
|
34
|
+
IconMovie,
|
|
35
|
+
IconMusic,
|
|
36
|
+
IconPackage,
|
|
37
|
+
IconPhoto,
|
|
38
|
+
IconTable,
|
|
39
|
+
IconTypography,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The published editor is one script. A computed import() of an icon module
|
|
43
|
+
// cannot be checked, so the view build loads this map instead of kind-icon-load.
|
|
44
|
+
export function loadTablerIcon(component: string): Promise<Icon | null> {
|
|
45
|
+
return Promise.resolve(icons[component] ?? null)
|
|
46
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { IconFile, type Icon, type IconProps } from "@tabler/icons-react"
|
|
5
|
+
import { loadTablerIcon } from "./kind-icon-load"
|
|
6
|
+
|
|
7
|
+
const loaded = new Map<string, Icon>()
|
|
8
|
+
const inflight = new Map<string, Promise<Icon | null>>()
|
|
9
|
+
const listeners = new Map<string, Set<() => void>>()
|
|
10
|
+
|
|
11
|
+
function notify(name: string) {
|
|
12
|
+
const set = listeners.get(name)
|
|
13
|
+
if (!set) return
|
|
14
|
+
for (const listener of set) listener()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function componentName(kebab: string): string | null {
|
|
18
|
+
if (typeof kebab !== "string" || kebab.length === 0) return null
|
|
19
|
+
const raw = kebab.startsWith("tabler:") ? kebab.slice("tabler:".length) : kebab
|
|
20
|
+
let name = "Icon"
|
|
21
|
+
for (const part of raw.split("-")) {
|
|
22
|
+
if (part.length === 0) return null
|
|
23
|
+
name += /^\d+$/.test(part) ? part : part.charAt(0).toUpperCase() + part.slice(1)
|
|
24
|
+
}
|
|
25
|
+
return name
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadIcon(icon: string): Promise<Icon | null> {
|
|
29
|
+
const hit = loaded.get(icon)
|
|
30
|
+
if (hit) return Promise.resolve(hit)
|
|
31
|
+
const pending = inflight.get(icon)
|
|
32
|
+
if (pending) return pending
|
|
33
|
+
const component = componentName(icon)
|
|
34
|
+
if (!component) return Promise.resolve(null)
|
|
35
|
+
const request = loadTablerIcon(component).then((Comp) => {
|
|
36
|
+
if (Comp) {
|
|
37
|
+
loaded.set(icon, Comp)
|
|
38
|
+
notify(icon)
|
|
39
|
+
}
|
|
40
|
+
return Comp
|
|
41
|
+
})
|
|
42
|
+
inflight.set(icon, request)
|
|
43
|
+
void request.finally(() => {
|
|
44
|
+
if (inflight.get(icon) === request) inflight.delete(icon)
|
|
45
|
+
})
|
|
46
|
+
return request
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function subscribe(name: string, onStoreChange: () => void) {
|
|
50
|
+
let set = listeners.get(name)
|
|
51
|
+
if (!set) {
|
|
52
|
+
set = new Set()
|
|
53
|
+
listeners.set(name, set)
|
|
54
|
+
}
|
|
55
|
+
set.add(onStoreChange)
|
|
56
|
+
return () => {
|
|
57
|
+
set.delete(onStoreChange)
|
|
58
|
+
if (set.size === 0) listeners.delete(name)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function useKindIcon(icon: string): Icon {
|
|
63
|
+
const Comp = React.useSyncExternalStore(
|
|
64
|
+
(onStoreChange) => subscribe(icon, onStoreChange),
|
|
65
|
+
() => loaded.get(icon) ?? null,
|
|
66
|
+
() => null,
|
|
67
|
+
)
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
void loadIcon(icon)
|
|
70
|
+
}, [icon])
|
|
71
|
+
return Comp ?? IconFile
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type KindIconProps = IconProps & {
|
|
75
|
+
/** Tabler icon URI or kebab-case name (e.g. `tabler:table`, `file-text`). */
|
|
76
|
+
icon: string
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function KindIcon({ icon, ...props }: KindIconProps) {
|
|
80
|
+
const Comp = useKindIcon(icon)
|
|
81
|
+
return <Comp data-slot="kind-icon" aria-hidden {...props} />
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { KindIcon }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
import { KindIcon } from "./kind-icon"
|
|
6
|
+
|
|
7
|
+
const kindMarkVariants = cva(
|
|
8
|
+
"inline-flex shrink-0 items-center justify-center rounded-md border-2 border-border text-[#12141a]",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
default: "size-6 [&_svg:not([class*='size-'])]:size-3.5",
|
|
13
|
+
sm: "size-5 [&_svg:not([class*='size-'])]:size-3",
|
|
14
|
+
lg: "size-8 [&_svg:not([class*='size-'])]:size-4",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
size: "default",
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export type KindMarkProps = Omit<React.ComponentProps<"span">, "children"> &
|
|
24
|
+
VariantProps<typeof kindMarkVariants> & {
|
|
25
|
+
/** Kind colour (platform `kind.color`). */
|
|
26
|
+
color: string
|
|
27
|
+
/** Tabler icon URI or kebab-case name (platform `kind.icon`). */
|
|
28
|
+
icon: string
|
|
29
|
+
/** Kind label (platform `kind.label`). Used for `title`; also `aria-label` when not decorative. */
|
|
30
|
+
label: string
|
|
31
|
+
/**
|
|
32
|
+
* When true (default), the mark is decorative: `aria-hidden` with `title` for
|
|
33
|
+
* hover. Set false to expose `label` as `aria-label` (and keep `title`).
|
|
34
|
+
*/
|
|
35
|
+
decorative?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function KindMark({
|
|
39
|
+
color,
|
|
40
|
+
icon,
|
|
41
|
+
label,
|
|
42
|
+
className,
|
|
43
|
+
size = "default",
|
|
44
|
+
decorative = true,
|
|
45
|
+
style,
|
|
46
|
+
...props
|
|
47
|
+
}: KindMarkProps) {
|
|
48
|
+
return (
|
|
49
|
+
<span
|
|
50
|
+
data-slot="kind-mark"
|
|
51
|
+
data-size={size}
|
|
52
|
+
className={cn(kindMarkVariants({ size }), className)}
|
|
53
|
+
style={{ backgroundColor: color, ...style }}
|
|
54
|
+
title={label}
|
|
55
|
+
aria-hidden={decorative ? true : undefined}
|
|
56
|
+
role={decorative ? undefined : "img"}
|
|
57
|
+
aria-label={decorative ? undefined : label}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
<KindIcon icon={icon} />
|
|
61
|
+
</span>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { KindMark }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
type LabelProps = React.ComponentProps<"label"> & {
|
|
8
|
+
"data-slot"?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function Label({
|
|
12
|
+
className,
|
|
13
|
+
"data-slot": dataSlot = "label",
|
|
14
|
+
...props
|
|
15
|
+
}: LabelProps) {
|
|
16
|
+
return (
|
|
17
|
+
<label
|
|
18
|
+
data-slot={dataSlot}
|
|
19
|
+
className={cn(
|
|
20
|
+
dataSlot === "field-label" ? "text-sm font-semibold" : "label leading-none",
|
|
21
|
+
"flex items-center gap-2 select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { Label }
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
3
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
const markerVariants = cva(
|
|
9
|
+
"group/marker relative flex min-h-5 w-full items-center gap-2 text-left text-sm font-semibold text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [a]:underline [a]:underline-offset-3 [a]:hover:text-foreground",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "",
|
|
14
|
+
separator:
|
|
15
|
+
"before:mr-1 before:h-px before:min-w-0 before:flex-1 before:bg-border after:ml-1 after:h-px after:min-w-0 after:flex-1 after:bg-border",
|
|
16
|
+
border: "border-b border-border pb-2",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
function Marker({
|
|
23
|
+
className,
|
|
24
|
+
variant = "default",
|
|
25
|
+
render,
|
|
26
|
+
...props
|
|
27
|
+
}: useRender.ComponentProps<"div"> & VariantProps<typeof markerVariants>) {
|
|
28
|
+
return useRender({
|
|
29
|
+
defaultTagName: "div",
|
|
30
|
+
props: mergeProps<"div">(
|
|
31
|
+
{
|
|
32
|
+
className: cn(markerVariants({ variant, className })),
|
|
33
|
+
},
|
|
34
|
+
props
|
|
35
|
+
),
|
|
36
|
+
render,
|
|
37
|
+
state: {
|
|
38
|
+
slot: "marker",
|
|
39
|
+
variant,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function MarkerIcon({ className, ...props }: React.ComponentProps<"span">) {
|
|
45
|
+
return (
|
|
46
|
+
<span
|
|
47
|
+
data-slot="marker-icon"
|
|
48
|
+
aria-hidden="true"
|
|
49
|
+
className={cn(
|
|
50
|
+
"size-4 shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
51
|
+
className
|
|
52
|
+
)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function MarkerContent({ className, ...props }: React.ComponentProps<"span">) {
|
|
59
|
+
return (
|
|
60
|
+
<span
|
|
61
|
+
data-slot="marker-content"
|
|
62
|
+
className={cn(
|
|
63
|
+
"min-w-0 wrap-break-word group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { Marker, MarkerIcon, MarkerContent, markerVariants }
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Menu as MenuPrimitive } from "@base-ui/react/menu"
|
|
5
|
+
import { Menubar as MenubarPrimitive } from "@base-ui/react/menubar"
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../lib/utils"
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
DropdownMenuGroup,
|
|
12
|
+
DropdownMenuItem,
|
|
13
|
+
DropdownMenuLabel,
|
|
14
|
+
DropdownMenuPortal,
|
|
15
|
+
DropdownMenuRadioGroup,
|
|
16
|
+
DropdownMenuSeparator,
|
|
17
|
+
DropdownMenuShortcut,
|
|
18
|
+
DropdownMenuSub,
|
|
19
|
+
DropdownMenuSubContent,
|
|
20
|
+
DropdownMenuSubTrigger,
|
|
21
|
+
DropdownMenuTrigger,
|
|
22
|
+
} from "./dropdown-menu"
|
|
23
|
+
import { IconCheck } from "@tabler/icons-react"
|
|
24
|
+
|
|
25
|
+
function Menubar({ className, ...props }: MenubarPrimitive.Props) {
|
|
26
|
+
return (
|
|
27
|
+
<MenubarPrimitive
|
|
28
|
+
data-slot="menubar"
|
|
29
|
+
className={cn(
|
|
30
|
+
"flex h-8 items-center gap-0.5 rounded-md border-2 p-1",
|
|
31
|
+
className
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) {
|
|
39
|
+
return <DropdownMenu data-slot="menubar-menu" {...props} />
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function MenubarGroup({
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<typeof DropdownMenuGroup>) {
|
|
45
|
+
return <DropdownMenuGroup data-slot="menubar-group" {...props} />
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function MenubarPortal({
|
|
49
|
+
...props
|
|
50
|
+
}: React.ComponentProps<typeof DropdownMenuPortal>) {
|
|
51
|
+
return <DropdownMenuPortal data-slot="menubar-portal" {...props} />
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function MenubarTrigger({
|
|
55
|
+
className,
|
|
56
|
+
...props
|
|
57
|
+
}: React.ComponentProps<typeof DropdownMenuTrigger>) {
|
|
58
|
+
return (
|
|
59
|
+
<DropdownMenuTrigger
|
|
60
|
+
data-slot="menubar-trigger"
|
|
61
|
+
className={cn(
|
|
62
|
+
"moloo-focus flex cursor-pointer items-center rounded-md px-1.5 py-[calc(--spacing(0.8))] text-sm font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function MenubarContent({
|
|
71
|
+
className,
|
|
72
|
+
align = "start",
|
|
73
|
+
alignOffset = -4,
|
|
74
|
+
sideOffset = 8,
|
|
75
|
+
...props
|
|
76
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
77
|
+
return (
|
|
78
|
+
<DropdownMenuContent
|
|
79
|
+
data-slot="menubar-content"
|
|
80
|
+
align={align}
|
|
81
|
+
alignOffset={alignOffset}
|
|
82
|
+
sideOffset={sideOffset}
|
|
83
|
+
className={cn("min-w-36 rounded-md moloo-surface bg-popover text-popover-foreground shadow-moloo-lg duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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", className )}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function MenubarItem({
|
|
90
|
+
className,
|
|
91
|
+
inset,
|
|
92
|
+
variant = "default",
|
|
93
|
+
...props
|
|
94
|
+
}: React.ComponentProps<typeof DropdownMenuItem>) {
|
|
95
|
+
return (
|
|
96
|
+
<DropdownMenuItem
|
|
97
|
+
data-slot="menubar-item"
|
|
98
|
+
data-inset={inset}
|
|
99
|
+
data-variant={variant}
|
|
100
|
+
className={cn(
|
|
101
|
+
"group/menubar-item gap-2 rounded-md px-2 py-2 text-sm font-semibold focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 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:opacity-50 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive!",
|
|
102
|
+
className
|
|
103
|
+
)}
|
|
104
|
+
{...props}
|
|
105
|
+
/>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function MenubarCheckboxItem({
|
|
110
|
+
className,
|
|
111
|
+
children,
|
|
112
|
+
checked,
|
|
113
|
+
inset,
|
|
114
|
+
...props
|
|
115
|
+
}: MenuPrimitive.CheckboxItem.Props & {
|
|
116
|
+
inset?: boolean
|
|
117
|
+
}) {
|
|
118
|
+
return (
|
|
119
|
+
<MenuPrimitive.CheckboxItem
|
|
120
|
+
data-slot="menubar-checkbox-item"
|
|
121
|
+
data-inset={inset}
|
|
122
|
+
className={cn(
|
|
123
|
+
"relative flex cursor-pointer items-center gap-2 rounded-md py-2 pr-28 pl-8 text-sm font-semibold outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
124
|
+
className
|
|
125
|
+
)}
|
|
126
|
+
checked={checked}
|
|
127
|
+
{...props}
|
|
128
|
+
>
|
|
129
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
130
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
131
|
+
<IconCheck
|
|
132
|
+
/>
|
|
133
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
134
|
+
</span>
|
|
135
|
+
{children}
|
|
136
|
+
</MenuPrimitive.CheckboxItem>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function MenubarRadioGroup({
|
|
141
|
+
...props
|
|
142
|
+
}: React.ComponentProps<typeof DropdownMenuRadioGroup>) {
|
|
143
|
+
return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function MenubarRadioItem({
|
|
147
|
+
className,
|
|
148
|
+
children,
|
|
149
|
+
inset,
|
|
150
|
+
...props
|
|
151
|
+
}: MenuPrimitive.RadioItem.Props & {
|
|
152
|
+
inset?: boolean
|
|
153
|
+
}) {
|
|
154
|
+
return (
|
|
155
|
+
<MenuPrimitive.RadioItem
|
|
156
|
+
data-slot="menubar-radio-item"
|
|
157
|
+
data-inset={inset}
|
|
158
|
+
className={cn(
|
|
159
|
+
"relative flex cursor-pointer items-center gap-2 rounded-md py-2 pr-2 pl-8 text-sm font-semibold outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
160
|
+
className
|
|
161
|
+
)}
|
|
162
|
+
{...props}
|
|
163
|
+
>
|
|
164
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
165
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
166
|
+
<IconCheck
|
|
167
|
+
/>
|
|
168
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
169
|
+
</span>
|
|
170
|
+
{children}
|
|
171
|
+
</MenuPrimitive.RadioItem>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function MenubarLabel({
|
|
176
|
+
className,
|
|
177
|
+
inset,
|
|
178
|
+
...props
|
|
179
|
+
}: React.ComponentProps<typeof DropdownMenuLabel> & {
|
|
180
|
+
inset?: boolean
|
|
181
|
+
}) {
|
|
182
|
+
return (
|
|
183
|
+
<DropdownMenuLabel
|
|
184
|
+
data-slot="menubar-label"
|
|
185
|
+
data-inset={inset}
|
|
186
|
+
className={cn("px-2 py-2 text-sm data-inset:pl-8", className)}
|
|
187
|
+
{...props}
|
|
188
|
+
/>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function MenubarSeparator({
|
|
193
|
+
className,
|
|
194
|
+
...props
|
|
195
|
+
}: React.ComponentProps<typeof DropdownMenuSeparator>) {
|
|
196
|
+
return (
|
|
197
|
+
<DropdownMenuSeparator
|
|
198
|
+
data-slot="menubar-separator"
|
|
199
|
+
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
200
|
+
{...props}
|
|
201
|
+
/>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function MenubarShortcut({
|
|
206
|
+
className,
|
|
207
|
+
...props
|
|
208
|
+
}: React.ComponentProps<typeof DropdownMenuShortcut>) {
|
|
209
|
+
return (
|
|
210
|
+
<DropdownMenuShortcut
|
|
211
|
+
data-slot="menubar-shortcut"
|
|
212
|
+
className={cn(
|
|
213
|
+
"ml-auto text-sm tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground",
|
|
214
|
+
className
|
|
215
|
+
)}
|
|
216
|
+
{...props}
|
|
217
|
+
/>
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function MenubarSub({
|
|
222
|
+
...props
|
|
223
|
+
}: React.ComponentProps<typeof DropdownMenuSub>) {
|
|
224
|
+
return <DropdownMenuSub data-slot="menubar-sub" {...props} />
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function MenubarSubTrigger({
|
|
228
|
+
className,
|
|
229
|
+
inset,
|
|
230
|
+
...props
|
|
231
|
+
}: React.ComponentProps<typeof DropdownMenuSubTrigger> & {
|
|
232
|
+
inset?: boolean
|
|
233
|
+
}) {
|
|
234
|
+
return (
|
|
235
|
+
<DropdownMenuSubTrigger
|
|
236
|
+
data-slot="menubar-sub-trigger"
|
|
237
|
+
data-inset={inset}
|
|
238
|
+
className={cn(
|
|
239
|
+
"gap-2 rounded-md px-2 py-2 text-sm font-semibold focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-open:bg-accent data-open:text-accent-foreground [&_svg:not([class*='size-'])]:size-4",
|
|
240
|
+
className
|
|
241
|
+
)}
|
|
242
|
+
{...props}
|
|
243
|
+
/>
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function MenubarSubContent({
|
|
248
|
+
className,
|
|
249
|
+
...props
|
|
250
|
+
}: React.ComponentProps<typeof DropdownMenuSubContent>) {
|
|
251
|
+
return (
|
|
252
|
+
<DropdownMenuSubContent
|
|
253
|
+
data-slot="menubar-sub-content"
|
|
254
|
+
className={cn("min-w-32 rounded-md moloo-surface bg-popover text-popover-foreground shadow-moloo-lg 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", className )}
|
|
255
|
+
{...props}
|
|
256
|
+
/>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export {
|
|
261
|
+
Menubar,
|
|
262
|
+
MenubarPortal,
|
|
263
|
+
MenubarMenu,
|
|
264
|
+
MenubarTrigger,
|
|
265
|
+
MenubarContent,
|
|
266
|
+
MenubarGroup,
|
|
267
|
+
MenubarSeparator,
|
|
268
|
+
MenubarLabel,
|
|
269
|
+
MenubarItem,
|
|
270
|
+
MenubarShortcut,
|
|
271
|
+
MenubarCheckboxItem,
|
|
272
|
+
MenubarRadioGroup,
|
|
273
|
+
MenubarRadioItem,
|
|
274
|
+
MenubarSub,
|
|
275
|
+
MenubarSubTrigger,
|
|
276
|
+
MenubarSubContent,
|
|
277
|
+
}
|