@moloodev/ui 0.0.0-stage → 0.1.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,398 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as RechartsPrimitive from "recharts"
5
+ import type { TooltipValueType } from "recharts"
6
+
7
+ import { cn } from "../../lib/utils"
8
+
9
+ // Format: { THEME_NAME: CSS_SELECTOR }
10
+ const THEMES = { light: "", dark: ".dark" } as const
11
+
12
+ const INITIAL_DIMENSION = { width: 320, height: 200 } as const
13
+ type TooltipNameType = number | string
14
+
15
+ /**
16
+ * Recharts chrome. v3 puts tick labels under `.recharts-cartesian-axis-tick-label`
17
+ * (not `.recharts-cartesian-axis-tick`); default tick fill is stroke `#666`.
18
+ */
19
+ const CHART_CHROME =
20
+ "[&_.recharts-responsive-container]:size-full " +
21
+ "[&_.recharts-cartesian-axis-tick-label_text]:fill-chart-axis " +
22
+ "[&_.recharts-cartesian-axis-tick-label_text]:font-semibold " +
23
+ "[&_.recharts-label]:fill-chart-axis " +
24
+ "[&_.recharts-cartesian-axis-line]:stroke-chart-grid " +
25
+ "[&_.recharts-cartesian-axis-tick-line]:stroke-chart-grid " +
26
+ "[&_.recharts-cartesian-grid_line]:stroke-chart-grid " +
27
+ "[&_.recharts-curve.recharts-tooltip-cursor]:stroke-chart-grid " +
28
+ "[&_.recharts-polar-grid_[stroke='#ccc']]:stroke-chart-grid " +
29
+ "[&_.recharts-reference-line_[stroke='#ccc']]:stroke-chart-axis " +
30
+ "[&_.recharts-radial-bar-background-sector]:fill-muted " +
31
+ "[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-chart-cursor " +
32
+ "[&_.recharts-dot[stroke='#fff']]:stroke-transparent " +
33
+ "[&_.recharts-sector[stroke='#fff']]:stroke-transparent " +
34
+ "[&_.recharts-layer]:outline-hidden " +
35
+ "[&_.recharts-sector]:outline-hidden " +
36
+ "[&_.recharts-surface]:outline-hidden"
37
+
38
+ export type ChartConfig = Record<
39
+ string,
40
+ {
41
+ label?: React.ReactNode
42
+ icon?: React.ComponentType
43
+ } & (
44
+ | { color?: string; theme?: never }
45
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
46
+ )
47
+ >
48
+
49
+ type ChartContextProps = {
50
+ config: ChartConfig
51
+ }
52
+
53
+ const ChartContext = React.createContext<ChartContextProps | null>(null)
54
+
55
+ function useChart() {
56
+ const context = React.useContext(ChartContext)
57
+
58
+ if (!context) {
59
+ throw new Error("useChart must be used within a <ChartContainer />")
60
+ }
61
+
62
+ return context
63
+ }
64
+
65
+ function ChartContainer({
66
+ id,
67
+ className,
68
+ children,
69
+ config,
70
+ initialDimension = INITIAL_DIMENSION,
71
+ ...props
72
+ }: React.ComponentProps<"div"> & {
73
+ config: ChartConfig
74
+ children: React.ComponentProps<
75
+ typeof RechartsPrimitive.ResponsiveContainer
76
+ >["children"]
77
+ initialDimension?: {
78
+ width: number
79
+ height: number
80
+ }
81
+ }) {
82
+ const uniqueId = React.useId()
83
+ const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`
84
+
85
+ return (
86
+ <ChartContext.Provider value={{ config }}>
87
+ <div
88
+ data-slot="chart"
89
+ data-chart={chartId}
90
+ className={cn(
91
+ "flex aspect-video w-full justify-center text-xs text-foreground",
92
+ CHART_CHROME,
93
+ className
94
+ )}
95
+ {...props}
96
+ >
97
+ <ChartStyle id={chartId} config={config} />
98
+ <RechartsPrimitive.ResponsiveContainer
99
+ initialDimension={initialDimension}
100
+ className="min-h-0 min-w-0 flex-1"
101
+ >
102
+ {children}
103
+ </RechartsPrimitive.ResponsiveContainer>
104
+ </div>
105
+ </ChartContext.Provider>
106
+ )
107
+ }
108
+
109
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
110
+ const colorConfig = Object.entries(config).filter(
111
+ ([, config]) => config.theme ?? config.color
112
+ )
113
+
114
+ if (!colorConfig.length) {
115
+ return null
116
+ }
117
+
118
+ return (
119
+ <style
120
+ dangerouslySetInnerHTML={{
121
+ __html: Object.entries(THEMES)
122
+ .map(
123
+ ([theme, prefix]) => `
124
+ ${prefix} [data-chart=${id}] {
125
+ ${colorConfig
126
+ .map(([key, itemConfig]) => {
127
+ const color =
128
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??
129
+ itemConfig.color
130
+ return color ? ` --color-${key}: ${color};` : null
131
+ })
132
+ .join("\n")}
133
+ }
134
+ `
135
+ )
136
+ .join("\n"),
137
+ }}
138
+ />
139
+ )
140
+ }
141
+
142
+ const ChartTooltip = RechartsPrimitive.Tooltip
143
+
144
+ function ChartTooltipContent({
145
+ active,
146
+ payload,
147
+ className,
148
+ indicator = "dot",
149
+ hideLabel = false,
150
+ hideIndicator = false,
151
+ label,
152
+ labelFormatter,
153
+ labelClassName,
154
+ formatter,
155
+ color,
156
+ nameKey,
157
+ labelKey,
158
+ }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
159
+ React.ComponentProps<"div"> & {
160
+ hideLabel?: boolean
161
+ hideIndicator?: boolean
162
+ indicator?: "line" | "dot" | "dashed"
163
+ nameKey?: string
164
+ labelKey?: string
165
+ } & Omit<
166
+ RechartsPrimitive.DefaultTooltipContentProps<
167
+ TooltipValueType,
168
+ TooltipNameType
169
+ >,
170
+ "accessibilityLayer"
171
+ >) {
172
+ const { config } = useChart()
173
+
174
+ const tooltipLabel = React.useMemo(() => {
175
+ if (hideLabel || !payload?.length) {
176
+ return null
177
+ }
178
+
179
+ const [item] = payload
180
+ const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`
181
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
182
+ const value =
183
+ !labelKey && typeof label === "string"
184
+ ? (config[label]?.label ?? label)
185
+ : itemConfig?.label
186
+
187
+ if (labelFormatter) {
188
+ return (
189
+ <div className={cn("font-medium", labelClassName)}>
190
+ {labelFormatter(value, payload)}
191
+ </div>
192
+ )
193
+ }
194
+
195
+ if (!value) {
196
+ return null
197
+ }
198
+
199
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>
200
+ }, [
201
+ label,
202
+ labelFormatter,
203
+ payload,
204
+ hideLabel,
205
+ labelClassName,
206
+ config,
207
+ labelKey,
208
+ ])
209
+
210
+ if (!active || !payload?.length) {
211
+ return null
212
+ }
213
+
214
+ const nestLabel = payload.length === 1 && indicator !== "dot"
215
+
216
+ return (
217
+ <div
218
+ className={cn(
219
+ "grid min-w-32 items-start gap-1.5 rounded-md border-2 border-border bg-background px-2.5 py-1.5 text-xs shadow-xl",
220
+ className
221
+ )}
222
+ >
223
+ {!nestLabel ? tooltipLabel : null}
224
+ <div className="grid gap-1.5">
225
+ {payload
226
+ .filter((item) => item.type !== "none")
227
+ .map((item, index) => {
228
+ const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`
229
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
230
+ const indicatorColor = color ?? item.payload?.fill ?? item.color
231
+
232
+ return (
233
+ <div
234
+ key={index}
235
+ className={cn(
236
+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-chart-axis",
237
+ indicator === "dot" && "items-center"
238
+ )}
239
+ >
240
+ {formatter && item?.value !== undefined && item.name ? (
241
+ formatter(item.value, item.name, item, index, item.payload)
242
+ ) : (
243
+ <>
244
+ {itemConfig?.icon ? (
245
+ <itemConfig.icon />
246
+ ) : (
247
+ !hideIndicator && (
248
+ <div
249
+ className={cn(
250
+ "shrink-0 rounded-sm border-(--color-border) bg-(--color-bg)",
251
+ {
252
+ "h-2.5 w-2.5": indicator === "dot",
253
+ "w-1": indicator === "line",
254
+ "w-0 border-[1.5px] border-dashed bg-transparent":
255
+ indicator === "dashed",
256
+ "my-0.5": nestLabel && indicator === "dashed",
257
+ }
258
+ )}
259
+ style={
260
+ {
261
+ "--color-bg": indicatorColor,
262
+ "--color-border": indicatorColor,
263
+ } as React.CSSProperties
264
+ }
265
+ />
266
+ )
267
+ )}
268
+ <div
269
+ className={cn(
270
+ "flex flex-1 justify-between leading-none",
271
+ nestLabel ? "items-end" : "items-center"
272
+ )}
273
+ >
274
+ <div className="grid gap-1.5">
275
+ {nestLabel ? tooltipLabel : null}
276
+ <span className="font-semibold text-chart-axis">
277
+ {itemConfig?.label ?? item.name}
278
+ </span>
279
+ </div>
280
+ {item.value != null && (
281
+ <span className="font-mono font-medium text-foreground tabular-nums">
282
+ {typeof item.value === "number"
283
+ ? item.value.toLocaleString()
284
+ : String(item.value)}
285
+ </span>
286
+ )}
287
+ </div>
288
+ </>
289
+ )}
290
+ </div>
291
+ )
292
+ })}
293
+ </div>
294
+ </div>
295
+ )
296
+ }
297
+
298
+ const ChartLegend = RechartsPrimitive.Legend
299
+
300
+ function ChartLegendContent({
301
+ className,
302
+ hideIcon = false,
303
+ payload,
304
+ verticalAlign = "bottom",
305
+ nameKey,
306
+ }: React.ComponentProps<"div"> & {
307
+ hideIcon?: boolean
308
+ nameKey?: string
309
+ } & RechartsPrimitive.DefaultLegendContentProps) {
310
+ const { config } = useChart()
311
+
312
+ if (!payload?.length) {
313
+ return null
314
+ }
315
+
316
+ return (
317
+ <div
318
+ className={cn(
319
+ "flex items-center justify-center gap-4 text-sm font-semibold text-chart-axis",
320
+ verticalAlign === "top" ? "pb-3" : "pt-3",
321
+ className
322
+ )}
323
+ >
324
+ {payload
325
+ .filter((item) => item.type !== "none")
326
+ .map((item, index) => {
327
+ const key = `${nameKey ?? item.dataKey ?? "value"}`
328
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
329
+
330
+ return (
331
+ <div
332
+ key={index}
333
+ className={cn(
334
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-chart-axis"
335
+ )}
336
+ >
337
+ {itemConfig?.icon && !hideIcon ? (
338
+ <itemConfig.icon />
339
+ ) : (
340
+ <div
341
+ className="h-2 w-2 shrink-0 rounded-sm"
342
+ style={{
343
+ backgroundColor: item.color,
344
+ }}
345
+ />
346
+ )}
347
+ {itemConfig?.label}
348
+ </div>
349
+ )
350
+ })}
351
+ </div>
352
+ )
353
+ }
354
+
355
+ function getPayloadConfigFromPayload(
356
+ config: ChartConfig,
357
+ payload: unknown,
358
+ key: string
359
+ ) {
360
+ if (typeof payload !== "object" || payload === null) {
361
+ return undefined
362
+ }
363
+
364
+ const payloadPayload =
365
+ "payload" in payload &&
366
+ typeof payload.payload === "object" &&
367
+ payload.payload !== null
368
+ ? payload.payload
369
+ : undefined
370
+
371
+ let configLabelKey: string = key
372
+
373
+ if (
374
+ key in payload &&
375
+ typeof payload[key as keyof typeof payload] === "string"
376
+ ) {
377
+ configLabelKey = payload[key as keyof typeof payload] as string
378
+ } else if (
379
+ payloadPayload &&
380
+ key in payloadPayload &&
381
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
382
+ ) {
383
+ configLabelKey = payloadPayload[
384
+ key as keyof typeof payloadPayload
385
+ ] as string
386
+ }
387
+
388
+ return configLabelKey in config ? config[configLabelKey] : config[key]
389
+ }
390
+
391
+ export {
392
+ ChartContainer,
393
+ ChartTooltip,
394
+ ChartTooltipContent,
395
+ ChartLegend,
396
+ ChartLegendContent,
397
+ ChartStyle,
398
+ }
@@ -0,0 +1,28 @@
1
+ "use client"
2
+
3
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
4
+
5
+ import { cn } from "../../lib/utils"
6
+ import { IconCheck } from "@tabler/icons-react"
7
+
8
+ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
9
+ return (
10
+ <CheckboxPrimitive.Root
11
+ data-slot="checkbox"
12
+ className={cn(
13
+ "moloo-focus peer relative flex size-4 shrink-0 items-center justify-center rounded-md border-2 border-input bg-[var(--field-bg)] transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:aria-checked:border-primary dark:bg-input/30 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
14
+ className
15
+ )}
16
+ {...props}
17
+ >
18
+ <CheckboxPrimitive.Indicator
19
+ data-slot="checkbox-indicator"
20
+ className="grid place-content-center text-current animate-in zoom-in-50 duration-150 [&>svg]:size-3.5"
21
+ >
22
+ <IconCheck />
23
+ </CheckboxPrimitive.Indicator>
24
+ </CheckboxPrimitive.Root>
25
+ )
26
+ }
27
+
28
+ export { Checkbox }
@@ -0,0 +1,40 @@
1
+ "use client"
2
+
3
+ import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
8
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
9
+ }
10
+
11
+ function CollapsibleTrigger({
12
+ className,
13
+ ...props
14
+ }: CollapsiblePrimitive.Trigger.Props) {
15
+ return (
16
+ <CollapsiblePrimitive.Trigger
17
+ data-slot="collapsible-trigger"
18
+ className={cn("moloo-focus outline-none", className)}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ function CollapsibleContent({
25
+ className,
26
+ ...props
27
+ }: CollapsiblePrimitive.Panel.Props) {
28
+ return (
29
+ <CollapsiblePrimitive.Panel
30
+ data-slot="collapsible-content"
31
+ className={cn(
32
+ "h-(--collapsible-panel-height) overflow-hidden text-sm transition-[height] duration-200 ease-out data-ending-style:h-0 data-starting-style:h-0",
33
+ className
34
+ )}
35
+ {...props}
36
+ />
37
+ )
38
+ }
39
+
40
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent }