@monkey-mini-app/ui 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 (149) hide show
  1. package/components.json +23 -0
  2. package/dist/globals.css +2 -0
  3. package/dist/index.js +138 -0
  4. package/dist/manifest.json +11 -0
  5. package/dist/src/blocks/activity-feed.tsx +9 -0
  6. package/dist/src/blocks/app-shell.tsx +37 -0
  7. package/dist/src/blocks/attachment-gallery.tsx +30 -0
  8. package/dist/src/blocks/bar-chart.tsx +33 -0
  9. package/dist/src/blocks/comment-thread.tsx +30 -0
  10. package/dist/src/blocks/description-list.tsx +16 -0
  11. package/dist/src/blocks/detail-panel.tsx +43 -0
  12. package/dist/src/blocks/donut-chart.tsx +38 -0
  13. package/dist/src/blocks/env-badge.tsx +23 -0
  14. package/dist/src/blocks/file-tree.tsx +5 -0
  15. package/dist/src/blocks/filter-bar.tsx +21 -0
  16. package/dist/src/blocks/gauge.tsx +13 -0
  17. package/dist/src/blocks/notification-center.tsx +22 -0
  18. package/dist/src/blocks/page-header.tsx +30 -0
  19. package/dist/src/blocks/progress-ring.tsx +25 -0
  20. package/dist/src/blocks/radar-chart.tsx +31 -0
  21. package/dist/src/blocks/request-inspector.tsx +34 -0
  22. package/dist/src/blocks/run-timeline.tsx +5 -0
  23. package/dist/src/blocks/scrollspy.tsx +42 -0
  24. package/dist/src/blocks/severity-chip.tsx +27 -0
  25. package/dist/src/blocks/sparkline.tsx +19 -0
  26. package/dist/src/blocks/stat-card.tsx +49 -0
  27. package/dist/src/blocks/status-badge.tsx +39 -0
  28. package/dist/src/blocks/terminal.tsx +10 -0
  29. package/dist/src/blocks/test-step-list.tsx +19 -0
  30. package/dist/src/blocks/trend-card.tsx +51 -0
  31. package/dist/src/components/accordion.tsx +72 -0
  32. package/dist/src/components/alert-dialog.tsx +185 -0
  33. package/dist/src/components/alert.tsx +76 -0
  34. package/dist/src/components/aspect-ratio.tsx +22 -0
  35. package/dist/src/components/attachment.tsx +207 -0
  36. package/dist/src/components/avatar.tsx +107 -0
  37. package/dist/src/components/badge.tsx +52 -0
  38. package/dist/src/components/breadcrumb.tsx +125 -0
  39. package/dist/src/components/bubble.tsx +128 -0
  40. package/dist/src/components/button-group.tsx +87 -0
  41. package/dist/src/components/button.tsx +58 -0
  42. package/dist/src/components/calendar.tsx +221 -0
  43. package/dist/src/components/card.tsx +103 -0
  44. package/dist/src/components/chart.tsx +371 -0
  45. package/dist/src/components/checkbox.tsx +29 -0
  46. package/dist/src/components/collapsible.tsx +19 -0
  47. package/dist/src/components/combobox.tsx +295 -0
  48. package/dist/src/components/command.tsx +196 -0
  49. package/dist/src/components/context-menu.tsx +271 -0
  50. package/dist/src/components/dialog.tsx +158 -0
  51. package/dist/src/components/drawer.tsx +226 -0
  52. package/dist/src/components/dropdown-menu.tsx +268 -0
  53. package/dist/src/components/empty.tsx +123 -0
  54. package/dist/src/components/field.tsx +236 -0
  55. package/dist/src/components/hover-card.tsx +49 -0
  56. package/dist/src/components/input-group.tsx +158 -0
  57. package/dist/src/components/input.tsx +20 -0
  58. package/dist/src/components/item.tsx +201 -0
  59. package/dist/src/components/kbd.tsx +26 -0
  60. package/dist/src/components/label.tsx +20 -0
  61. package/dist/src/components/marker.tsx +71 -0
  62. package/dist/src/components/menubar.tsx +280 -0
  63. package/dist/src/components/message-scroller.tsx +129 -0
  64. package/dist/src/components/message.tsx +92 -0
  65. package/dist/src/components/native-select.tsx +61 -0
  66. package/dist/src/components/navigation-menu.tsx +168 -0
  67. package/dist/src/components/pagination.tsx +135 -0
  68. package/dist/src/components/popover.tsx +88 -0
  69. package/dist/src/components/progress.tsx +83 -0
  70. package/dist/src/components/questionnaire.tsx +329 -0
  71. package/dist/src/components/radio-group.tsx +36 -0
  72. package/dist/src/components/resizable.tsx +50 -0
  73. package/dist/src/components/scroll-area.tsx +52 -0
  74. package/dist/src/components/select.tsx +201 -0
  75. package/dist/src/components/separator.tsx +23 -0
  76. package/dist/src/components/sheet.tsx +136 -0
  77. package/dist/src/components/sidebar.tsx +721 -0
  78. package/dist/src/components/skeleton.tsx +13 -0
  79. package/dist/src/components/slider.tsx +52 -0
  80. package/dist/src/components/spinner.tsx +10 -0
  81. package/dist/src/components/switch.tsx +32 -0
  82. package/dist/src/components/table.tsx +114 -0
  83. package/dist/src/components/tabs.tsx +82 -0
  84. package/dist/src/components/textarea.tsx +18 -0
  85. package/dist/src/components/toast.tsx +234 -0
  86. package/dist/src/components/toggle-group.tsx +89 -0
  87. package/dist/src/components/toggle.tsx +43 -0
  88. package/dist/src/components/tooltip.tsx +66 -0
  89. package/dist/src/composites/autocomplete.tsx +55 -0
  90. package/dist/src/composites/cascader.tsx +46 -0
  91. package/dist/src/composites/color-picker.tsx +19 -0
  92. package/dist/src/composites/confirm-dialog.tsx +56 -0
  93. package/dist/src/composites/copyable.tsx +37 -0
  94. package/dist/src/composites/currency-input.tsx +31 -0
  95. package/dist/src/composites/date-chrome.tsx +157 -0
  96. package/dist/src/composites/date-picker.tsx +91 -0
  97. package/dist/src/composites/date-range-picker.tsx +96 -0
  98. package/dist/src/composites/date-time-picker.tsx +115 -0
  99. package/dist/src/composites/date-time-range-picker.tsx +51 -0
  100. package/dist/src/composites/duration-input.tsx +122 -0
  101. package/dist/src/composites/mini-calendar.tsx +24 -0
  102. package/dist/src/composites/number-field.tsx +53 -0
  103. package/dist/src/composites/password-field.tsx +44 -0
  104. package/dist/src/composites/phone-input.tsx +22 -0
  105. package/dist/src/composites/rating.tsx +39 -0
  106. package/dist/src/composites/relative-date-picker.tsx +62 -0
  107. package/dist/src/composites/search-input.tsx +49 -0
  108. package/dist/src/composites/slider-range.tsx +30 -0
  109. package/dist/src/composites/tag-input.tsx +56 -0
  110. package/dist/src/composites/time-picker.tsx +199 -0
  111. package/dist/src/composites/time-range-picker.tsx +25 -0
  112. package/dist/src/composites/timezone-select.tsx +251 -0
  113. package/dist/src/composites/transfer.tsx +52 -0
  114. package/dist/src/composites/user-picker.tsx +40 -0
  115. package/dist/src/hooks/use-mobile.ts +19 -0
  116. package/dist/src/i18n/context.tsx +72 -0
  117. package/dist/src/i18n/en.ts +168 -0
  118. package/dist/src/i18n/zh.ts +160 -0
  119. package/dist/src/index.ts +134 -0
  120. package/dist/src/lib/icons.tsx +16 -0
  121. package/dist/src/lib/illustrations.tsx +573 -0
  122. package/dist/src/lib/utils.ts +6 -0
  123. package/dist/src/products/cells.tsx +18 -0
  124. package/dist/src/products/code-block.tsx +75 -0
  125. package/dist/src/products/code-editor.tsx +49 -0
  126. package/dist/src/products/column-meta.d.ts +9 -0
  127. package/dist/src/products/data-grid.tsx +689 -0
  128. package/dist/src/products/diff-viewer.tsx +129 -0
  129. package/dist/src/products/event-calendar.tsx +845 -0
  130. package/dist/src/products/export-csv.ts +25 -0
  131. package/dist/src/products/file-dropzone.tsx +41 -0
  132. package/dist/src/products/gantt.tsx +41 -0
  133. package/dist/src/products/jira-wiki-parse.ts +197 -0
  134. package/dist/src/products/jira-wiki.tsx +285 -0
  135. package/dist/src/products/jql-input.tsx +62 -0
  136. package/dist/src/products/jql-language.ts +70 -0
  137. package/dist/src/products/json-viewer.tsx +37 -0
  138. package/dist/src/products/kanban-issue-panel.tsx +98 -0
  139. package/dist/src/products/kanban.tsx +245 -0
  140. package/dist/src/products/log-viewer.tsx +33 -0
  141. package/dist/src/products/markdown-editor.tsx +63 -0
  142. package/dist/src/products/markdown.tsx +26 -0
  143. package/dist/src/products/rich-text-editor.tsx +84 -0
  144. package/dist/src/products/sortable-list.tsx +61 -0
  145. package/dist/src/products/stepper.tsx +89 -0
  146. package/dist/src/products/timeline.tsx +32 -0
  147. package/dist/src/products/tree-view.tsx +56 -0
  148. package/dist/src/styles/globals.css +352 -0
  149. package/package.json +100 -0
@@ -0,0 +1,268 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu"
5
+
6
+ import { cn } from "@monkey-mini-app/ui/lib/utils"
7
+ import { ChevronRightIcon, CheckIcon } from "lucide-react"
8
+
9
+ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
10
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
11
+ }
12
+
13
+ function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
14
+ return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
15
+ }
16
+
17
+ function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
18
+ return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
19
+ }
20
+
21
+ function DropdownMenuContent({
22
+ align = "start",
23
+ alignOffset = 0,
24
+ side = "bottom",
25
+ sideOffset = 4,
26
+ className,
27
+ ...props
28
+ }: MenuPrimitive.Popup.Props &
29
+ Pick<
30
+ MenuPrimitive.Positioner.Props,
31
+ "align" | "alignOffset" | "side" | "sideOffset"
32
+ >) {
33
+ return (
34
+ <MenuPrimitive.Portal>
35
+ <MenuPrimitive.Positioner
36
+ className="isolate z-50 outline-none"
37
+ align={align}
38
+ alignOffset={alignOffset}
39
+ side={side}
40
+ sideOffset={sideOffset}
41
+ >
42
+ <MenuPrimitive.Popup
43
+ data-slot="dropdown-menu-content"
44
+ className={cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none 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 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className )}
45
+ {...props}
46
+ />
47
+ </MenuPrimitive.Positioner>
48
+ </MenuPrimitive.Portal>
49
+ )
50
+ }
51
+
52
+ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
53
+ return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
54
+ }
55
+
56
+ function DropdownMenuLabel({
57
+ className,
58
+ inset,
59
+ ...props
60
+ }: MenuPrimitive.GroupLabel.Props & {
61
+ inset?: boolean
62
+ }) {
63
+ return (
64
+ <MenuPrimitive.GroupLabel
65
+ data-slot="dropdown-menu-label"
66
+ data-inset={inset}
67
+ className={cn(
68
+ "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
69
+ className
70
+ )}
71
+ {...props}
72
+ />
73
+ )
74
+ }
75
+
76
+ function DropdownMenuItem({
77
+ className,
78
+ inset,
79
+ variant = "default",
80
+ ...props
81
+ }: MenuPrimitive.Item.Props & {
82
+ inset?: boolean
83
+ variant?: "default" | "destructive"
84
+ }) {
85
+ return (
86
+ <MenuPrimitive.Item
87
+ data-slot="dropdown-menu-item"
88
+ data-inset={inset}
89
+ data-variant={variant}
90
+ className={cn(
91
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 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:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
92
+ className
93
+ )}
94
+ {...props}
95
+ />
96
+ )
97
+ }
98
+
99
+ function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
100
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
101
+ }
102
+
103
+ function DropdownMenuSubTrigger({
104
+ className,
105
+ inset,
106
+ children,
107
+ ...props
108
+ }: MenuPrimitive.SubmenuTrigger.Props & {
109
+ inset?: boolean
110
+ }) {
111
+ return (
112
+ <MenuPrimitive.SubmenuTrigger
113
+ data-slot="dropdown-menu-sub-trigger"
114
+ data-inset={inset}
115
+ className={cn(
116
+ "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
117
+ className
118
+ )}
119
+ {...props}
120
+ >
121
+ {children}
122
+ <ChevronRightIcon className="ml-auto" />
123
+ </MenuPrimitive.SubmenuTrigger>
124
+ )
125
+ }
126
+
127
+ function DropdownMenuSubContent({
128
+ align = "start",
129
+ alignOffset = -3,
130
+ side = "right",
131
+ sideOffset = 0,
132
+ className,
133
+ ...props
134
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
135
+ return (
136
+ <DropdownMenuContent
137
+ data-slot="dropdown-menu-sub-content"
138
+ className={cn("w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 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 )}
139
+ align={align}
140
+ alignOffset={alignOffset}
141
+ side={side}
142
+ sideOffset={sideOffset}
143
+ {...props}
144
+ />
145
+ )
146
+ }
147
+
148
+ function DropdownMenuCheckboxItem({
149
+ className,
150
+ children,
151
+ checked,
152
+ inset,
153
+ ...props
154
+ }: MenuPrimitive.CheckboxItem.Props & {
155
+ inset?: boolean
156
+ }) {
157
+ return (
158
+ <MenuPrimitive.CheckboxItem
159
+ data-slot="dropdown-menu-checkbox-item"
160
+ data-inset={inset}
161
+ className={cn(
162
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
163
+ className
164
+ )}
165
+ checked={checked}
166
+ {...props}
167
+ >
168
+ <span
169
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
170
+ data-slot="dropdown-menu-checkbox-item-indicator"
171
+ >
172
+ <MenuPrimitive.CheckboxItemIndicator>
173
+ <CheckIcon
174
+ />
175
+ </MenuPrimitive.CheckboxItemIndicator>
176
+ </span>
177
+ {children}
178
+ </MenuPrimitive.CheckboxItem>
179
+ )
180
+ }
181
+
182
+ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
183
+ return (
184
+ <MenuPrimitive.RadioGroup
185
+ data-slot="dropdown-menu-radio-group"
186
+ {...props}
187
+ />
188
+ )
189
+ }
190
+
191
+ function DropdownMenuRadioItem({
192
+ className,
193
+ children,
194
+ inset,
195
+ ...props
196
+ }: MenuPrimitive.RadioItem.Props & {
197
+ inset?: boolean
198
+ }) {
199
+ return (
200
+ <MenuPrimitive.RadioItem
201
+ data-slot="dropdown-menu-radio-item"
202
+ data-inset={inset}
203
+ className={cn(
204
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
205
+ className
206
+ )}
207
+ {...props}
208
+ >
209
+ <span
210
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
211
+ data-slot="dropdown-menu-radio-item-indicator"
212
+ >
213
+ <MenuPrimitive.RadioItemIndicator>
214
+ <CheckIcon
215
+ />
216
+ </MenuPrimitive.RadioItemIndicator>
217
+ </span>
218
+ {children}
219
+ </MenuPrimitive.RadioItem>
220
+ )
221
+ }
222
+
223
+ function DropdownMenuSeparator({
224
+ className,
225
+ ...props
226
+ }: MenuPrimitive.Separator.Props) {
227
+ return (
228
+ <MenuPrimitive.Separator
229
+ data-slot="dropdown-menu-separator"
230
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
231
+ {...props}
232
+ />
233
+ )
234
+ }
235
+
236
+ function DropdownMenuShortcut({
237
+ className,
238
+ ...props
239
+ }: React.ComponentProps<"span">) {
240
+ return (
241
+ <span
242
+ data-slot="dropdown-menu-shortcut"
243
+ className={cn(
244
+ "ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
245
+ className
246
+ )}
247
+ {...props}
248
+ />
249
+ )
250
+ }
251
+
252
+ export {
253
+ DropdownMenu,
254
+ DropdownMenuPortal,
255
+ DropdownMenuTrigger,
256
+ DropdownMenuContent,
257
+ DropdownMenuGroup,
258
+ DropdownMenuLabel,
259
+ DropdownMenuItem,
260
+ DropdownMenuCheckboxItem,
261
+ DropdownMenuRadioGroup,
262
+ DropdownMenuRadioItem,
263
+ DropdownMenuSeparator,
264
+ DropdownMenuShortcut,
265
+ DropdownMenuSub,
266
+ DropdownMenuSubTrigger,
267
+ DropdownMenuSubContent,
268
+ }
@@ -0,0 +1,123 @@
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+
3
+ import { cn } from "@monkey-mini-app/ui/lib/utils"
4
+
5
+ function Empty({
6
+ className,
7
+ title,
8
+ description,
9
+ children,
10
+ ...props
11
+ }: React.ComponentProps<"div"> & {
12
+ /** Convenience: renders an EmptyTitle as the empty-state heading. */
13
+ title?: React.ReactNode
14
+ /** Convenience: renders an EmptyDescription under the title. */
15
+ description?: React.ReactNode
16
+ }) {
17
+ return (
18
+ <div
19
+ data-slot="empty"
20
+ className={cn(
21
+ "flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance",
22
+ className
23
+ )}
24
+ {...props}
25
+ >
26
+ {title != null || description != null ? (
27
+ <EmptyHeader>
28
+ {title != null ? <EmptyTitle>{title}</EmptyTitle> : null}
29
+ {description != null ? <EmptyDescription>{description}</EmptyDescription> : null}
30
+ </EmptyHeader>
31
+ ) : null}
32
+ {children}
33
+ </div>
34
+ )
35
+ }
36
+
37
+ function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
38
+ return (
39
+ <div
40
+ data-slot="empty-header"
41
+ className={cn("flex max-w-sm flex-col items-center gap-2", className)}
42
+ {...props}
43
+ />
44
+ )
45
+ }
46
+
47
+ const emptyMediaVariants = cva(
48
+ "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
49
+ {
50
+ variants: {
51
+ variant: {
52
+ default: "bg-transparent",
53
+ icon: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-4",
54
+ },
55
+ },
56
+ defaultVariants: {
57
+ variant: "default",
58
+ },
59
+ }
60
+ )
61
+
62
+ function EmptyMedia({
63
+ className,
64
+ variant = "default",
65
+ ...props
66
+ }: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
67
+ return (
68
+ <div
69
+ data-slot="empty-icon"
70
+ data-variant={variant}
71
+ className={cn(emptyMediaVariants({ variant, className }))}
72
+ {...props}
73
+ />
74
+ )
75
+ }
76
+
77
+ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
78
+ return (
79
+ <div
80
+ data-slot="empty-title"
81
+ className={cn(
82
+ "font-heading text-sm font-medium tracking-tight",
83
+ className
84
+ )}
85
+ {...props}
86
+ />
87
+ )
88
+ }
89
+
90
+ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
91
+ return (
92
+ <div
93
+ data-slot="empty-description"
94
+ className={cn(
95
+ "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
96
+ className
97
+ )}
98
+ {...props}
99
+ />
100
+ )
101
+ }
102
+
103
+ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
104
+ return (
105
+ <div
106
+ data-slot="empty-content"
107
+ className={cn(
108
+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-2.5 text-sm text-balance",
109
+ className
110
+ )}
111
+ {...props}
112
+ />
113
+ )
114
+ }
115
+
116
+ export {
117
+ Empty,
118
+ EmptyHeader,
119
+ EmptyTitle,
120
+ EmptyDescription,
121
+ EmptyContent,
122
+ EmptyMedia,
123
+ }
@@ -0,0 +1,236 @@
1
+ import { useMemo } from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@monkey-mini-app/ui/lib/utils"
5
+ import { Label } from "@monkey-mini-app/ui/components/label"
6
+ import { Separator } from "@monkey-mini-app/ui/components/separator"
7
+
8
+ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
9
+ return (
10
+ <fieldset
11
+ data-slot="field-set"
12
+ className={cn(
13
+ "flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function FieldLegend({
22
+ className,
23
+ variant = "legend",
24
+ ...props
25
+ }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
26
+ return (
27
+ <legend
28
+ data-slot="field-legend"
29
+ data-variant={variant}
30
+ className={cn(
31
+ "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
32
+ className
33
+ )}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
40
+ return (
41
+ <div
42
+ data-slot="field-group"
43
+ className={cn(
44
+ "group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
45
+ className
46
+ )}
47
+ {...props}
48
+ />
49
+ )
50
+ }
51
+
52
+ const fieldVariants = cva(
53
+ "group/field flex w-full gap-2 data-[invalid=true]:text-destructive",
54
+ {
55
+ variants: {
56
+ orientation: {
57
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
58
+ horizontal:
59
+ "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
60
+ responsive:
61
+ "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
62
+ },
63
+ },
64
+ defaultVariants: {
65
+ orientation: "vertical",
66
+ },
67
+ }
68
+ )
69
+
70
+ function Field({
71
+ className,
72
+ orientation = "vertical",
73
+ ...props
74
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
75
+ return (
76
+ <div
77
+ role="group"
78
+ data-slot="field"
79
+ data-orientation={orientation}
80
+ className={cn(fieldVariants({ orientation }), className)}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
87
+ return (
88
+ <div
89
+ data-slot="field-content"
90
+ className={cn(
91
+ "group/field-content flex flex-1 flex-col gap-0.5 leading-snug",
92
+ className
93
+ )}
94
+ {...props}
95
+ />
96
+ )
97
+ }
98
+
99
+ function FieldLabel({
100
+ className,
101
+ ...props
102
+ }: React.ComponentProps<typeof Label>) {
103
+ return (
104
+ <Label
105
+ data-slot="field-label"
106
+ className={cn(
107
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
108
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
109
+ className
110
+ )}
111
+ {...props}
112
+ />
113
+ )
114
+ }
115
+
116
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
117
+ return (
118
+ <div
119
+ data-slot="field-label"
120
+ className={cn(
121
+ "flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
122
+ className
123
+ )}
124
+ {...props}
125
+ />
126
+ )
127
+ }
128
+
129
+ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
130
+ return (
131
+ <p
132
+ data-slot="field-description"
133
+ className={cn(
134
+ "text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
135
+ "last:mt-0 nth-last-2:-mt-1",
136
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
137
+ className
138
+ )}
139
+ {...props}
140
+ />
141
+ )
142
+ }
143
+
144
+ function FieldSeparator({
145
+ children,
146
+ className,
147
+ ...props
148
+ }: React.ComponentProps<"div"> & {
149
+ children?: React.ReactNode
150
+ }) {
151
+ return (
152
+ <div
153
+ data-slot="field-separator"
154
+ data-content={!!children}
155
+ className={cn(
156
+ "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
157
+ className
158
+ )}
159
+ {...props}
160
+ >
161
+ <Separator className="absolute inset-0 top-1/2" />
162
+ {children && (
163
+ <span
164
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
165
+ data-slot="field-separator-content"
166
+ >
167
+ {children}
168
+ </span>
169
+ )}
170
+ </div>
171
+ )
172
+ }
173
+
174
+ function FieldError({
175
+ className,
176
+ children,
177
+ errors,
178
+ ...props
179
+ }: React.ComponentProps<"div"> & {
180
+ errors?: Array<{ message?: string } | undefined>
181
+ }) {
182
+ const content = useMemo(() => {
183
+ if (children) {
184
+ return children
185
+ }
186
+
187
+ if (!errors?.length) {
188
+ return null
189
+ }
190
+
191
+ const uniqueErrors = [
192
+ ...new Map(errors.map((error) => [error?.message, error])).values(),
193
+ ]
194
+
195
+ if (uniqueErrors?.length == 1) {
196
+ return uniqueErrors[0]?.message
197
+ }
198
+
199
+ return (
200
+ <ul className="ml-4 flex list-disc flex-col gap-1">
201
+ {uniqueErrors.map(
202
+ (error, index) =>
203
+ error?.message && <li key={index}>{error.message}</li>
204
+ )}
205
+ </ul>
206
+ )
207
+ }, [children, errors])
208
+
209
+ if (!content) {
210
+ return null
211
+ }
212
+
213
+ return (
214
+ <div
215
+ role="alert"
216
+ data-slot="field-error"
217
+ className={cn("text-sm font-normal text-destructive", className)}
218
+ {...props}
219
+ >
220
+ {content}
221
+ </div>
222
+ )
223
+ }
224
+
225
+ export {
226
+ Field,
227
+ FieldLabel,
228
+ FieldDescription,
229
+ FieldError,
230
+ FieldGroup,
231
+ FieldLegend,
232
+ FieldSeparator,
233
+ FieldSet,
234
+ FieldContent,
235
+ FieldTitle,
236
+ }
@@ -0,0 +1,49 @@
1
+ import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
2
+
3
+ import { cn } from "@monkey-mini-app/ui/lib/utils"
4
+
5
+ function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
6
+ return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
7
+ }
8
+
9
+ function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
10
+ return (
11
+ <PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
12
+ )
13
+ }
14
+
15
+ function HoverCardContent({
16
+ className,
17
+ side = "bottom",
18
+ sideOffset = 4,
19
+ align = "center",
20
+ alignOffset = 4,
21
+ ...props
22
+ }: PreviewCardPrimitive.Popup.Props &
23
+ Pick<
24
+ PreviewCardPrimitive.Positioner.Props,
25
+ "align" | "alignOffset" | "side" | "sideOffset"
26
+ >) {
27
+ return (
28
+ <PreviewCardPrimitive.Portal data-slot="hover-card-portal">
29
+ <PreviewCardPrimitive.Positioner
30
+ align={align}
31
+ alignOffset={alignOffset}
32
+ side={side}
33
+ sideOffset={sideOffset}
34
+ className="isolate z-50"
35
+ >
36
+ <PreviewCardPrimitive.Popup
37
+ data-slot="hover-card-content"
38
+ className={cn(
39
+ "z-50 w-64 origin-(--transform-origin) rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden 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 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ </PreviewCardPrimitive.Positioner>
45
+ </PreviewCardPrimitive.Portal>
46
+ )
47
+ }
48
+
49
+ export { HoverCard, HoverCardTrigger, HoverCardContent }