@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.
Files changed (93) hide show
  1. package/package.json +139 -3
  2. package/registry/components.ts +135 -0
  3. package/registry/fonts.css +81 -0
  4. package/registry/suit-OFL.txt +94 -0
  5. package/registry/wanted-sans-OFL.txt +93 -0
  6. package/src/components/ui/accordion.tsx +74 -0
  7. package/src/components/ui/alert-dialog.tsx +187 -0
  8. package/src/components/ui/alert.tsx +83 -0
  9. package/src/components/ui/aspect-ratio.tsx +22 -0
  10. package/src/components/ui/attachment.tsx +207 -0
  11. package/src/components/ui/avatar.tsx +109 -0
  12. package/src/components/ui/badge.tsx +54 -0
  13. package/src/components/ui/breadcrumb.tsx +126 -0
  14. package/src/components/ui/bubble.tsx +128 -0
  15. package/src/components/ui/button-group.tsx +103 -0
  16. package/src/components/ui/button.tsx +68 -0
  17. package/src/components/ui/calendar.tsx +222 -0
  18. package/src/components/ui/card.tsx +103 -0
  19. package/src/components/ui/carousel.tsx +242 -0
  20. package/src/components/ui/chart.tsx +398 -0
  21. package/src/components/ui/checkbox.tsx +28 -0
  22. package/src/components/ui/collapsible.tsx +40 -0
  23. package/src/components/ui/combobox.tsx +299 -0
  24. package/src/components/ui/command.tsx +218 -0
  25. package/src/components/ui/context-menu.tsx +271 -0
  26. package/src/components/ui/data-table.tsx +919 -0
  27. package/src/components/ui/date-picker.tsx +65 -0
  28. package/src/components/ui/dialog.tsx +224 -0
  29. package/src/components/ui/drawer.tsx +231 -0
  30. package/src/components/ui/dropdown-menu.tsx +268 -0
  31. package/src/components/ui/dropzone.tsx +186 -0
  32. package/src/components/ui/empty.tsx +101 -0
  33. package/src/components/ui/field.tsx +238 -0
  34. package/src/components/ui/form.tsx +83 -0
  35. package/src/components/ui/highlighted-source.tsx +200 -0
  36. package/src/components/ui/hover-card.tsx +51 -0
  37. package/src/components/ui/image-field.tsx +796 -0
  38. package/src/components/ui/input-group.tsx +149 -0
  39. package/src/components/ui/input-otp.tsx +86 -0
  40. package/src/components/ui/input-step.ts +19 -0
  41. package/src/components/ui/input.tsx +100 -0
  42. package/src/components/ui/item.tsx +201 -0
  43. package/src/components/ui/kbd.tsx +91 -0
  44. package/src/components/ui/kind-icon-glob.ts +24 -0
  45. package/src/components/ui/kind-icon-load.ts +52 -0
  46. package/src/components/ui/kind-icon-static.ts +46 -0
  47. package/src/components/ui/kind-icon.tsx +84 -0
  48. package/src/components/ui/kind-mark.tsx +65 -0
  49. package/src/components/ui/label.tsx +29 -0
  50. package/src/components/ui/marker.tsx +71 -0
  51. package/src/components/ui/menubar.tsx +277 -0
  52. package/src/components/ui/message-scroller.tsx +137 -0
  53. package/src/components/ui/message.tsx +92 -0
  54. package/src/components/ui/moloo-logo.tsx +45 -0
  55. package/src/components/ui/native-select.tsx +61 -0
  56. package/src/components/ui/navigation-menu.tsx +168 -0
  57. package/src/components/ui/outline-list.tsx +73 -0
  58. package/src/components/ui/pagination.tsx +130 -0
  59. package/src/components/ui/panel.tsx +108 -0
  60. package/src/components/ui/popover.tsx +90 -0
  61. package/src/components/ui/progress.tsx +117 -0
  62. package/src/components/ui/radio-group.tsx +38 -0
  63. package/src/components/ui/resizable.tsx +52 -0
  64. package/src/components/ui/scroll-area.tsx +54 -0
  65. package/src/components/ui/select.tsx +205 -0
  66. package/src/components/ui/separator.tsx +25 -0
  67. package/src/components/ui/sheet.tsx +140 -0
  68. package/src/components/ui/sidebar.tsx +746 -0
  69. package/src/components/ui/skeleton.tsx +13 -0
  70. package/src/components/ui/slider.tsx +52 -0
  71. package/src/components/ui/spinner.tsx +18 -0
  72. package/src/components/ui/switch.tsx +32 -0
  73. package/src/components/ui/table.tsx +116 -0
  74. package/src/components/ui/tabs.tsx +83 -0
  75. package/src/components/ui/tag-list-input.tsx +194 -0
  76. package/src/components/ui/textarea.tsx +18 -0
  77. package/src/components/ui/timeline.tsx +265 -0
  78. package/src/components/ui/toast.tsx +237 -0
  79. package/src/components/ui/toggle-group.tsx +90 -0
  80. package/src/components/ui/toggle.tsx +45 -0
  81. package/src/components/ui/tooltip.tsx +66 -0
  82. package/src/css.d.ts +1 -0
  83. package/src/hooks/use-clipboard.ts +82 -0
  84. package/src/hooks/use-mobile.ts +21 -0
  85. package/src/index.ts +106 -0
  86. package/src/kind-icon.ts +4 -0
  87. package/src/lib/format-relative-time.ts +41 -0
  88. package/src/lib/list-selection.ts +61 -0
  89. package/src/lib/utils.ts +6 -0
  90. package/src/moloo.css +360 -0
  91. package/src/styles.css +36 -0
  92. package/src/typeset.css +518 -0
  93. 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
+ }