azamat-ui-kit-cli 0.2.2

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 (213) hide show
  1. package/README.md +8 -0
  2. package/dist/index.js +432 -0
  3. package/package.json +34 -0
  4. package/vendor/package.json +4 -0
  5. package/vendor/src/components/actions/action-bar.tsx +35 -0
  6. package/vendor/src/components/actions/action-menu.tsx +120 -0
  7. package/vendor/src/components/actions/button-group.tsx +47 -0
  8. package/vendor/src/components/actions/copy-button.tsx +91 -0
  9. package/vendor/src/components/actions/copy-field.tsx +31 -0
  10. package/vendor/src/components/actions/floating-action-button.tsx +33 -0
  11. package/vendor/src/components/actions/index.ts +7 -0
  12. package/vendor/src/components/actions/public.ts +5 -0
  13. package/vendor/src/components/actions/quick-action-grid.tsx +162 -0
  14. package/vendor/src/components/calendar/calendar.tsx +328 -0
  15. package/vendor/src/components/calendar/date-picker.tsx +78 -0
  16. package/vendor/src/components/calendar/date-range-picker.tsx +96 -0
  17. package/vendor/src/components/calendar/date-utils.ts +89 -0
  18. package/vendor/src/components/calendar/index.ts +4 -0
  19. package/vendor/src/components/charts/charts.tsx +275 -0
  20. package/vendor/src/components/charts/horizontal-bar-chart.tsx +46 -0
  21. package/vendor/src/components/charts/index.ts +4 -0
  22. package/vendor/src/components/charts/kpi.tsx +68 -0
  23. package/vendor/src/components/charts/progress-ring.tsx +45 -0
  24. package/vendor/src/components/charts/public.ts +1 -0
  25. package/vendor/src/components/command/command-palette.tsx +375 -0
  26. package/vendor/src/components/command/index.ts +1 -0
  27. package/vendor/src/components/data-table/data-table-actions-column.tsx +58 -0
  28. package/vendor/src/components/data-table/data-table-bulk-actions.tsx +84 -0
  29. package/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +79 -0
  30. package/vendor/src/components/data-table/data-table-pagination.tsx +91 -0
  31. package/vendor/src/components/data-table/data-table-row-actions.tsx +48 -0
  32. package/vendor/src/components/data-table/data-table-select-column.tsx +59 -0
  33. package/vendor/src/components/data-table/data-table-sortable-header.tsx +45 -0
  34. package/vendor/src/components/data-table/data-table-toolbar.tsx +76 -0
  35. package/vendor/src/components/data-table/data-table-view-presets.tsx +128 -0
  36. package/vendor/src/components/data-table/data-table.tsx +507 -0
  37. package/vendor/src/components/data-table/index.ts +12 -0
  38. package/vendor/src/components/data-table/public.ts +10 -0
  39. package/vendor/src/components/data-table/table-export-menu.tsx +56 -0
  40. package/vendor/src/components/data-table/table-import-button.tsx +43 -0
  41. package/vendor/src/components/display/activity-feed.tsx +97 -0
  42. package/vendor/src/components/display/avatar.tsx +131 -0
  43. package/vendor/src/components/display/code-block.tsx +33 -0
  44. package/vendor/src/components/display/data-state.tsx +63 -0
  45. package/vendor/src/components/display/description-list.tsx +119 -0
  46. package/vendor/src/components/display/descriptions.tsx +83 -0
  47. package/vendor/src/components/display/entity-card.tsx +53 -0
  48. package/vendor/src/components/display/file-card.tsx +54 -0
  49. package/vendor/src/components/display/index.ts +30 -0
  50. package/vendor/src/components/display/kanban.tsx +104 -0
  51. package/vendor/src/components/display/keyboard-shortcut.tsx +31 -0
  52. package/vendor/src/components/display/list.tsx +100 -0
  53. package/vendor/src/components/display/metric-grid.tsx +86 -0
  54. package/vendor/src/components/display/progress.tsx +162 -0
  55. package/vendor/src/components/display/property-grid.tsx +54 -0
  56. package/vendor/src/components/display/result.tsx +90 -0
  57. package/vendor/src/components/display/smart-card.tsx +168 -0
  58. package/vendor/src/components/display/statistic.tsx +107 -0
  59. package/vendor/src/components/display/status-legend.tsx +108 -0
  60. package/vendor/src/components/display/tag-list.tsx +52 -0
  61. package/vendor/src/components/display/timeline.tsx +132 -0
  62. package/vendor/src/components/display/tree-view.tsx +116 -0
  63. package/vendor/src/components/feedback/alert.tsx +69 -0
  64. package/vendor/src/components/feedback/empty-state.tsx +56 -0
  65. package/vendor/src/components/feedback/index.ts +5 -0
  66. package/vendor/src/components/feedback/loading-state.tsx +39 -0
  67. package/vendor/src/components/feedback/page-state.tsx +69 -0
  68. package/vendor/src/components/feedback/status-badge.tsx +62 -0
  69. package/vendor/src/components/filters/filter-bar.tsx +89 -0
  70. package/vendor/src/components/filters/filter-chips.tsx +69 -0
  71. package/vendor/src/components/filters/index.ts +2 -0
  72. package/vendor/src/components/form/form-actions.tsx +53 -0
  73. package/vendor/src/components/form/form-async-select.tsx +26 -0
  74. package/vendor/src/components/form/form-date-input.tsx +19 -0
  75. package/vendor/src/components/form/form-date-picker.tsx +54 -0
  76. package/vendor/src/components/form/form-date-range-input.tsx +79 -0
  77. package/vendor/src/components/form/form-date-range-picker.tsx +57 -0
  78. package/vendor/src/components/form/form-field-shell.tsx +191 -0
  79. package/vendor/src/components/form/form-input.tsx +480 -0
  80. package/vendor/src/components/form/form-number-input.tsx +19 -0
  81. package/vendor/src/components/form/form-password-input.tsx +19 -0
  82. package/vendor/src/components/form/form-phone-input.tsx +22 -0
  83. package/vendor/src/components/form/form-search-input.tsx +19 -0
  84. package/vendor/src/components/form/form-section.tsx +29 -0
  85. package/vendor/src/components/form/form-select.tsx +194 -0
  86. package/vendor/src/components/form/form-switch.tsx +145 -0
  87. package/vendor/src/components/form/form-textarea.tsx +103 -0
  88. package/vendor/src/components/form/index.ts +17 -0
  89. package/vendor/src/components/form/public.ts +14 -0
  90. package/vendor/src/components/form/smart-form-shell.tsx +59 -0
  91. package/vendor/src/components/inputs/async-select.tsx +1143 -0
  92. package/vendor/src/components/inputs/clearable-input.tsx +78 -0
  93. package/vendor/src/components/inputs/color-input.tsx +47 -0
  94. package/vendor/src/components/inputs/combobox.tsx +89 -0
  95. package/vendor/src/components/inputs/date-input.tsx +32 -0
  96. package/vendor/src/components/inputs/date-range-input.tsx +67 -0
  97. package/vendor/src/components/inputs/index.ts +19 -0
  98. package/vendor/src/components/inputs/input-chrome.tsx +37 -0
  99. package/vendor/src/components/inputs/input-decorator.tsx +64 -0
  100. package/vendor/src/components/inputs/input-value.ts +42 -0
  101. package/vendor/src/components/inputs/masked-input.tsx +51 -0
  102. package/vendor/src/components/inputs/money-input.tsx +73 -0
  103. package/vendor/src/components/inputs/number-input.tsx +87 -0
  104. package/vendor/src/components/inputs/numeric-value.ts +39 -0
  105. package/vendor/src/components/inputs/otp-input.tsx +102 -0
  106. package/vendor/src/components/inputs/password-input.tsx +85 -0
  107. package/vendor/src/components/inputs/phone-input.tsx +46 -0
  108. package/vendor/src/components/inputs/quantity-input.tsx +116 -0
  109. package/vendor/src/components/inputs/quantity-stepper.tsx +49 -0
  110. package/vendor/src/components/inputs/rating.tsx +98 -0
  111. package/vendor/src/components/inputs/search-input.tsx +26 -0
  112. package/vendor/src/components/inputs/simple-select.tsx +72 -0
  113. package/vendor/src/components/inputs/slider.tsx +149 -0
  114. package/vendor/src/components/inputs/tag-input.tsx +104 -0
  115. package/vendor/src/components/layout/app-header.tsx +46 -0
  116. package/vendor/src/components/layout/app-shell.tsx +243 -0
  117. package/vendor/src/components/layout/app-sidebar.tsx +179 -0
  118. package/vendor/src/components/layout/breadcrumbs.tsx +72 -0
  119. package/vendor/src/components/layout/index.ts +11 -0
  120. package/vendor/src/components/layout/page-container.tsx +30 -0
  121. package/vendor/src/components/layout/page-header.tsx +60 -0
  122. package/vendor/src/components/layout/public.ts +10 -0
  123. package/vendor/src/components/layout/section.tsx +76 -0
  124. package/vendor/src/components/layout/sidebar-nav.tsx +147 -0
  125. package/vendor/src/components/layout/stat-card.tsx +88 -0
  126. package/vendor/src/components/layout/sticky-footer-bar.tsx +23 -0
  127. package/vendor/src/components/layout/workspace-shell.tsx +50 -0
  128. package/vendor/src/components/navigation/anchor-nav.tsx +44 -0
  129. package/vendor/src/components/navigation/index.ts +4 -0
  130. package/vendor/src/components/navigation/page-tabs.tsx +67 -0
  131. package/vendor/src/components/navigation/pagination.tsx +179 -0
  132. package/vendor/src/components/navigation/stepper-tabs.tsx +67 -0
  133. package/vendor/src/components/notifications/index.ts +1 -0
  134. package/vendor/src/components/notifications/toast.tsx +259 -0
  135. package/vendor/src/components/overlay/confirm-dialog.tsx +66 -0
  136. package/vendor/src/components/overlay/dialog-actions.tsx +68 -0
  137. package/vendor/src/components/overlay/index.ts +4 -0
  138. package/vendor/src/components/overlay/modal-shell.tsx +93 -0
  139. package/vendor/src/components/overlay/sheet-shell.tsx +212 -0
  140. package/vendor/src/components/patterns/action-system.tsx +116 -0
  141. package/vendor/src/components/patterns/crud-system.tsx +53 -0
  142. package/vendor/src/components/patterns/data-view.tsx +84 -0
  143. package/vendor/src/components/patterns/entity-details.tsx +66 -0
  144. package/vendor/src/components/patterns/filter-builder.tsx +113 -0
  145. package/vendor/src/components/patterns/form-builder-presets.ts +131 -0
  146. package/vendor/src/components/patterns/form-builder.tsx +334 -0
  147. package/vendor/src/components/patterns/index.ts +12 -0
  148. package/vendor/src/components/patterns/public.ts +4 -0
  149. package/vendor/src/components/patterns/resource-detail-page.tsx +160 -0
  150. package/vendor/src/components/patterns/resource-page.tsx +159 -0
  151. package/vendor/src/components/patterns/resource-system.tsx +61 -0
  152. package/vendor/src/components/patterns/settings-section.tsx +46 -0
  153. package/vendor/src/components/patterns/status-system.tsx +89 -0
  154. package/vendor/src/components/theme-provider.tsx +51 -0
  155. package/vendor/src/components/ui/badge.tsx +52 -0
  156. package/vendor/src/components/ui/button.tsx +61 -0
  157. package/vendor/src/components/ui/card.tsx +103 -0
  158. package/vendor/src/components/ui/checkbox.tsx +82 -0
  159. package/vendor/src/components/ui/collapse.tsx +126 -0
  160. package/vendor/src/components/ui/command.tsx +194 -0
  161. package/vendor/src/components/ui/dialog.tsx +160 -0
  162. package/vendor/src/components/ui/divider.tsx +46 -0
  163. package/vendor/src/components/ui/dropdown-menu.tsx +266 -0
  164. package/vendor/src/components/ui/input-group.tsx +158 -0
  165. package/vendor/src/components/ui/input.tsx +20 -0
  166. package/vendor/src/components/ui/popover.tsx +90 -0
  167. package/vendor/src/components/ui/segmented-control.tsx +78 -0
  168. package/vendor/src/components/ui/select.tsx +201 -0
  169. package/vendor/src/components/ui/skeleton.tsx +75 -0
  170. package/vendor/src/components/ui/spinner.tsx +50 -0
  171. package/vendor/src/components/ui/switch.tsx +71 -0
  172. package/vendor/src/components/ui/table.tsx +114 -0
  173. package/vendor/src/components/ui/tabs.tsx +55 -0
  174. package/vendor/src/components/ui/textarea.tsx +18 -0
  175. package/vendor/src/components/ui/tooltip.tsx +38 -0
  176. package/vendor/src/components/upload/file-upload.tsx +483 -0
  177. package/vendor/src/components/upload/image-upload.tsx +118 -0
  178. package/vendor/src/components/upload/index.ts +2 -0
  179. package/vendor/src/components/wizard/index.ts +2 -0
  180. package/vendor/src/components/wizard/stepper.tsx +53 -0
  181. package/vendor/src/components/wizard/wizard.tsx +60 -0
  182. package/vendor/src/families/card-family.ts +28 -0
  183. package/vendor/src/families/catalog.ts +96 -0
  184. package/vendor/src/families/data-table-family.ts +31 -0
  185. package/vendor/src/families/docs-adoption.ts +103 -0
  186. package/vendor/src/families/docs-groups.ts +209 -0
  187. package/vendor/src/families/docs-queries.ts +84 -0
  188. package/vendor/src/families/docs-routing.ts +89 -0
  189. package/vendor/src/families/form-family.ts +45 -0
  190. package/vendor/src/families/index.ts +17 -0
  191. package/vendor/src/families/input-family.ts +61 -0
  192. package/vendor/src/families/member-metadata.ts +466 -0
  193. package/vendor/src/families/member-queries.ts +28 -0
  194. package/vendor/src/families/member-snippet-queries.ts +54 -0
  195. package/vendor/src/families/member-snippets.ts +673 -0
  196. package/vendor/src/families/migration-map.ts +79 -0
  197. package/vendor/src/families/queries.ts +63 -0
  198. package/vendor/src/families/select-family.ts +33 -0
  199. package/vendor/src/families/views.ts +81 -0
  200. package/vendor/src/hooks/index.ts +6 -0
  201. package/vendor/src/hooks/use-before-unload-when-dirty.ts +21 -0
  202. package/vendor/src/hooks/use-data-table-view-state.ts +122 -0
  203. package/vendor/src/hooks/use-debounce.ts +52 -0
  204. package/vendor/src/hooks/use-disclosure.ts +38 -0
  205. package/vendor/src/hooks/use-is-mobile.ts +28 -0
  206. package/vendor/src/hooks/use-session-storage-state.ts +85 -0
  207. package/vendor/src/index.ts +38 -0
  208. package/vendor/src/lib/utils.ts +6 -0
  209. package/vendor/templates/components/button.tsx +0 -0
  210. package/vendor/templates/components/data-table.tsx +0 -0
  211. package/vendor/templates/components/input.tsx +0 -0
  212. package/vendor/templates/lib/utils.ts +0 -0
  213. package/vendor/templates/styles/globals.css +0 -0
@@ -0,0 +1,97 @@
1
+ import * as React from "react"
2
+
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ export type ActivityFeedItem = {
7
+ id: string
8
+ title: React.ReactNode
9
+ description?: React.ReactNode
10
+ time?: React.ReactNode
11
+ icon?: React.ReactNode
12
+ tone?: "default" | "success" | "warning" | "danger" | "info" | "muted"
13
+ actions?: React.ReactNode
14
+ hidden?: boolean
15
+ className?: string
16
+ }
17
+
18
+ export type ActivityFeedProps = React.ComponentProps<typeof Card> & {
19
+ title?: React.ReactNode
20
+ description?: React.ReactNode
21
+ actions?: React.ReactNode
22
+ items: ActivityFeedItem[]
23
+ empty?: React.ReactNode
24
+ compact?: boolean
25
+ contentClassName?: string
26
+ itemClassName?: string
27
+ }
28
+
29
+ const toneDotClassName: Record<NonNullable<ActivityFeedItem["tone"]>, string> = {
30
+ default: "bg-primary",
31
+ success: "bg-emerald-500",
32
+ warning: "bg-amber-500",
33
+ danger: "bg-destructive",
34
+ info: "bg-blue-500",
35
+ muted: "bg-muted-foreground",
36
+ }
37
+
38
+ function ActivityFeed({
39
+ className,
40
+ title,
41
+ description,
42
+ actions,
43
+ items,
44
+ empty = "No activity yet.",
45
+ compact = false,
46
+ contentClassName,
47
+ itemClassName,
48
+ ...props
49
+ }: ActivityFeedProps) {
50
+ const visibleItems = items.filter((item) => !item.hidden)
51
+ const hasHeader = Boolean(title || description || actions)
52
+
53
+ return (
54
+ <Card data-slot="activity-feed" className={cn("min-w-0", className)} {...props}>
55
+ {hasHeader && (
56
+ <CardHeader>
57
+ <div className="flex min-w-0 items-start justify-between gap-3">
58
+ <div className="min-w-0 space-y-1">
59
+ {title && <CardTitle>{title}</CardTitle>}
60
+ {description && <CardDescription>{description}</CardDescription>}
61
+ </div>
62
+ {actions && <div className="shrink-0">{actions}</div>}
63
+ </div>
64
+ </CardHeader>
65
+ )}
66
+
67
+ <CardContent className={cn("grid gap-0", contentClassName)}>
68
+ {visibleItems.length === 0 ? (
69
+ <div className="rounded-lg border border-dashed p-4 text-center text-sm text-muted-foreground">{empty}</div>
70
+ ) : (
71
+ visibleItems.map((item, index) => (
72
+ <div
73
+ key={item.id}
74
+ data-slot="activity-feed-item"
75
+ className={cn("relative flex gap-3 pb-4 last:pb-0", compact && "gap-2 pb-3", itemClassName, item.className)}
76
+ >
77
+ {index < visibleItems.length - 1 && <div className="absolute left-3 top-7 h-[calc(100%-1.75rem)] w-px bg-border" />}
78
+ <div className="relative z-10 mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full border bg-background">
79
+ {item.icon ?? <span className={cn("size-2 rounded-full", toneDotClassName[item.tone ?? "default"])} />}
80
+ </div>
81
+ <div className="min-w-0 flex-1 space-y-1">
82
+ <div className="flex min-w-0 items-start justify-between gap-2">
83
+ <div className="min-w-0 text-sm font-medium leading-5 text-foreground">{item.title}</div>
84
+ {item.time && <div className="shrink-0 text-xs text-muted-foreground">{item.time}</div>}
85
+ </div>
86
+ {item.description && <div className="text-sm leading-5 text-muted-foreground">{item.description}</div>}
87
+ {item.actions && <div className="pt-1">{item.actions}</div>}
88
+ </div>
89
+ </div>
90
+ ))
91
+ )}
92
+ </CardContent>
93
+ </Card>
94
+ )
95
+ }
96
+
97
+ export { ActivityFeed }
@@ -0,0 +1,131 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export type AvatarSize = "xs" | "sm" | "default" | "lg" | "xl"
6
+ export type AvatarShape = "circle" | "rounded" | "square"
7
+
8
+ export type AvatarProps = React.ComponentProps<"span"> & {
9
+ src?: string
10
+ alt?: string
11
+ name?: string
12
+ fallback?: React.ReactNode
13
+ size?: AvatarSize
14
+ shape?: AvatarShape
15
+ status?: "online" | "offline" | "busy" | "away"
16
+ }
17
+
18
+ export type AvatarGroupItem = AvatarProps & {
19
+ key: string
20
+ }
21
+
22
+ export type AvatarGroupProps = React.ComponentProps<"div"> & {
23
+ items: AvatarGroupItem[]
24
+ max?: number
25
+ size?: AvatarSize
26
+ shape?: AvatarShape
27
+ stacked?: boolean
28
+ overflowLabel?: (count: number) => React.ReactNode
29
+ }
30
+
31
+ const sizeClassName: Record<AvatarSize, string> = {
32
+ xs: "size-6 text-[10px]",
33
+ sm: "size-8 text-xs",
34
+ default: "size-10 text-sm",
35
+ lg: "size-12 text-base",
36
+ xl: "size-16 text-lg",
37
+ }
38
+
39
+ const shapeClassName: Record<AvatarShape, string> = {
40
+ circle: "rounded-full",
41
+ rounded: "rounded-xl",
42
+ square: "rounded-md",
43
+ }
44
+
45
+ const statusClassName = {
46
+ online: "bg-emerald-500",
47
+ offline: "bg-muted-foreground",
48
+ busy: "bg-destructive",
49
+ away: "bg-amber-500",
50
+ }
51
+
52
+ function getInitials(name?: string) {
53
+ if (!name) return "?"
54
+ const words = name.trim().split(/\s+/).filter(Boolean)
55
+ if (!words.length) return "?"
56
+ return words.slice(0, 2).map((word) => word[0]?.toUpperCase()).join("")
57
+ }
58
+
59
+ function Avatar({
60
+ src,
61
+ alt,
62
+ name,
63
+ fallback,
64
+ size = "default",
65
+ shape = "circle",
66
+ status,
67
+ className,
68
+ ...props
69
+ }: AvatarProps) {
70
+ const [imageError, setImageError] = React.useState(false)
71
+ const showImage = Boolean(src && !imageError)
72
+
73
+ return (
74
+ <span
75
+ data-slot="avatar"
76
+ data-size={size}
77
+ data-shape={shape}
78
+ className={cn("relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden border bg-muted font-medium text-muted-foreground", sizeClassName[size], shapeClassName[shape], className)}
79
+ {...props}
80
+ >
81
+ {showImage ? (
82
+ <img src={src} alt={alt ?? name ?? "Avatar"} className="size-full object-cover" onError={() => setImageError(true)} />
83
+ ) : (
84
+ <span data-slot="avatar-fallback">{fallback ?? getInitials(name)}</span>
85
+ )}
86
+ {status && (
87
+ <span
88
+ data-slot="avatar-status"
89
+ data-status={status}
90
+ className={cn("absolute bottom-0 right-0 size-2.5 rounded-full border-2 border-background", size === "xs" && "size-2", size === "xl" && "size-3.5", statusClassName[status])}
91
+ />
92
+ )}
93
+ </span>
94
+ )
95
+ }
96
+
97
+ function AvatarGroup({
98
+ items,
99
+ max = 5,
100
+ size = "default",
101
+ shape = "circle",
102
+ stacked = true,
103
+ overflowLabel = (count) => `+${count}`,
104
+ className,
105
+ ...props
106
+ }: AvatarGroupProps) {
107
+ const visibleItems = items.slice(0, max)
108
+ const overflowCount = Math.max(items.length - max, 0)
109
+
110
+ return (
111
+ <div data-slot="avatar-group" className={cn("flex items-center", stacked ? "-space-x-2" : "gap-2", className)} {...props}>
112
+ {visibleItems.map((item) => {
113
+ const { key: itemKey, ...avatarProps } = item
114
+ return (
115
+ <Avatar
116
+ key={itemKey}
117
+ size={item.size ?? size}
118
+ shape={item.shape ?? shape}
119
+ className={cn(stacked && "ring-2 ring-background", item.className)}
120
+ {...avatarProps}
121
+ />
122
+ )
123
+ })}
124
+ {overflowCount > 0 && (
125
+ <Avatar size={size} shape={shape} fallback={overflowLabel(overflowCount)} className={cn(stacked && "ring-2 ring-background")} />
126
+ )}
127
+ </div>
128
+ )
129
+ }
130
+
131
+ export { Avatar, AvatarGroup }
@@ -0,0 +1,33 @@
1
+ import * as React from "react"
2
+
3
+ import { CopyButton } from "@/components/actions/copy-button"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ export type CodeBlockProps = React.ComponentProps<"div"> & {
7
+ code: string
8
+ language?: string
9
+ title?: React.ReactNode
10
+ showCopy?: boolean
11
+ wrap?: boolean
12
+ }
13
+
14
+ function CodeBlock({ code, language, title, showCopy = true, wrap = false, className, ...props }: CodeBlockProps) {
15
+ return (
16
+ <div data-slot="code-block" className={cn("overflow-hidden rounded-lg border bg-card", className)} {...props}>
17
+ {(title || language || showCopy) && (
18
+ <div className="flex items-center justify-between gap-3 border-b px-3 py-2 text-xs text-muted-foreground">
19
+ <div className="flex min-w-0 items-center gap-2">
20
+ {title && <span className="truncate font-medium text-foreground">{title}</span>}
21
+ {language && <span className="rounded bg-muted px-1.5 py-0.5 font-mono">{language}</span>}
22
+ </div>
23
+ {showCopy && <CopyButton value={code} size="sm" variant="ghost" copyLabel="Copy" copiedLabel="Copied" />}
24
+ </div>
25
+ )}
26
+ <pre className={cn("overflow-x-auto p-3 text-sm", wrap && "whitespace-pre-wrap break-words")}>
27
+ <code>{code}</code>
28
+ </pre>
29
+ </div>
30
+ )
31
+ }
32
+
33
+ export { CodeBlock }
@@ -0,0 +1,63 @@
1
+ import * as React from "react"
2
+ import { AlertTriangleIcon, CheckCircle2Icon, Loader2Icon, SearchXIcon } from "lucide-react"
3
+
4
+ import { Button } from "@/components/ui/button"
5
+ import { Card, CardContent } from "@/components/ui/card"
6
+ import { cn } from "@/lib/utils"
7
+
8
+ export type DataStateStatus = "idle" | "loading" | "empty" | "error" | "success"
9
+
10
+ export type DataStateProps = React.ComponentProps<typeof Card> & {
11
+ status: DataStateStatus
12
+ title?: React.ReactNode
13
+ description?: React.ReactNode
14
+ actions?: React.ReactNode
15
+ icon?: React.ReactNode
16
+ compact?: boolean
17
+ children?: React.ReactNode
18
+ retryLabel?: React.ReactNode
19
+ onRetry?: () => void
20
+ }
21
+
22
+ const defaultContent: Record<DataStateStatus, { title: string; description: string; icon: React.ReactNode }> = {
23
+ idle: { title: "Ready", description: "No action has started yet.", icon: <CheckCircle2Icon /> },
24
+ loading: { title: "Loading", description: "Please wait while data is being prepared.", icon: <Loader2Icon className="animate-spin" /> },
25
+ empty: { title: "No data", description: "There is nothing to show yet.", icon: <SearchXIcon /> },
26
+ error: { title: "Something went wrong", description: "Try again or check the request.", icon: <AlertTriangleIcon /> },
27
+ success: { title: "Ready", description: "Data loaded successfully.", icon: <CheckCircle2Icon /> },
28
+ }
29
+
30
+ function DataState({
31
+ status,
32
+ title,
33
+ description,
34
+ actions,
35
+ icon,
36
+ compact = false,
37
+ children,
38
+ retryLabel = "Retry",
39
+ onRetry,
40
+ className,
41
+ ...props
42
+ }: DataStateProps) {
43
+ const content = defaultContent[status]
44
+
45
+ return (
46
+ <Card data-slot="data-state" data-status={status} className={cn("min-w-0", className)} {...props}>
47
+ <CardContent className={cn("flex flex-col items-center justify-center text-center", compact ? "p-4" : "min-h-52 p-8")}>
48
+ <div className={cn("mb-3 flex items-center justify-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5", compact ? "size-9" : "size-12")}>{icon ?? content.icon}</div>
49
+ <div className={cn("font-semibold", compact ? "text-sm" : "text-base")}>{title ?? content.title}</div>
50
+ <p className="mt-1 max-w-md text-sm text-muted-foreground">{description ?? content.description}</p>
51
+ {children && <div className="mt-4 w-full">{children}</div>}
52
+ {(actions || onRetry) && (
53
+ <div className="mt-4 flex flex-wrap justify-center gap-2">
54
+ {onRetry && <Button size="sm" variant={status === "error" ? "default" : "outline"} onClick={onRetry}>{retryLabel}</Button>}
55
+ {actions}
56
+ </div>
57
+ )}
58
+ </CardContent>
59
+ </Card>
60
+ )
61
+ }
62
+
63
+ export { DataState }
@@ -0,0 +1,119 @@
1
+ import * as React from "react"
2
+
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ export type DescriptionListColumn = 1 | 2 | 3 | 4
7
+
8
+ export type DescriptionListItem = {
9
+ key: string
10
+ label: React.ReactNode
11
+ value?: React.ReactNode
12
+ description?: React.ReactNode
13
+ icon?: React.ReactNode
14
+ span?: DescriptionListColumn
15
+ hidden?: boolean
16
+ className?: string
17
+ labelClassName?: string
18
+ valueClassName?: string
19
+ }
20
+
21
+ export type DescriptionListProps = React.ComponentProps<typeof Card> & {
22
+ title?: React.ReactNode
23
+ description?: React.ReactNode
24
+ actions?: React.ReactNode
25
+ items: DescriptionListItem[]
26
+ columns?: DescriptionListColumn
27
+ bordered?: boolean
28
+ compact?: boolean
29
+ emptyValue?: React.ReactNode
30
+ itemClassName?: string
31
+ labelClassName?: string
32
+ valueClassName?: string
33
+ contentClassName?: string
34
+ }
35
+
36
+ const columnsClassName: Record<DescriptionListColumn, string> = {
37
+ 1: "grid-cols-1",
38
+ 2: "grid-cols-1 md:grid-cols-2",
39
+ 3: "grid-cols-1 md:grid-cols-2 xl:grid-cols-3",
40
+ 4: "grid-cols-1 md:grid-cols-2 xl:grid-cols-4",
41
+ }
42
+
43
+ const spanClassName: Record<DescriptionListColumn, string> = {
44
+ 1: "col-span-1",
45
+ 2: "md:col-span-2",
46
+ 3: "xl:col-span-3",
47
+ 4: "xl:col-span-4",
48
+ }
49
+
50
+ function isEmptyValue(value: React.ReactNode) {
51
+ return value === null || value === undefined || value === ""
52
+ }
53
+
54
+ function DescriptionList({
55
+ title,
56
+ description,
57
+ actions,
58
+ items,
59
+ columns = 2,
60
+ bordered = true,
61
+ compact = false,
62
+ emptyValue = "-",
63
+ itemClassName,
64
+ labelClassName,
65
+ valueClassName,
66
+ contentClassName,
67
+ className,
68
+ ...props
69
+ }: DescriptionListProps) {
70
+ const visibleItems = items.filter((item) => !item.hidden)
71
+ const hasHeader = Boolean(title || description || actions)
72
+
73
+ return (
74
+ <Card data-slot="description-list" className={cn("min-w-0", className)} {...props}>
75
+ {hasHeader && (
76
+ <CardHeader>
77
+ <div className="flex min-w-0 items-start justify-between gap-3">
78
+ <div className="min-w-0 space-y-1">
79
+ {title && <CardTitle>{title}</CardTitle>}
80
+ {description && <CardDescription>{description}</CardDescription>}
81
+ </div>
82
+ {actions && <div className="shrink-0">{actions}</div>}
83
+ </div>
84
+ </CardHeader>
85
+ )}
86
+ <CardContent className={cn("grid gap-2", columnsClassName[columns], contentClassName)}>
87
+ {visibleItems.map((item) => (
88
+ <div
89
+ key={item.key}
90
+ data-slot="description-list-item"
91
+ className={cn(
92
+ "min-w-0 rounded-lg",
93
+ bordered && "border bg-muted/20",
94
+ compact ? "p-2" : "p-3",
95
+ item.span && item.span > 1 && spanClassName[item.span],
96
+ itemClassName,
97
+ item.className
98
+ )}
99
+ >
100
+ <div className="flex min-w-0 items-start gap-2">
101
+ {item.icon && <span className="mt-0.5 shrink-0 text-muted-foreground [&_svg]:size-4">{item.icon}</span>}
102
+ <div className="min-w-0 flex-1 space-y-1">
103
+ <div className={cn("text-xs font-medium uppercase tracking-wide text-muted-foreground", labelClassName, item.labelClassName)}>
104
+ {item.label}
105
+ </div>
106
+ <div className={cn("min-w-0 break-words text-sm font-medium text-foreground", valueClassName, item.valueClassName)}>
107
+ {isEmptyValue(item.value) ? emptyValue : item.value}
108
+ </div>
109
+ {item.description && <div className="text-xs leading-5 text-muted-foreground">{item.description}</div>}
110
+ </div>
111
+ </div>
112
+ </div>
113
+ ))}
114
+ </CardContent>
115
+ </Card>
116
+ )
117
+ }
118
+
119
+ export { DescriptionList }
@@ -0,0 +1,83 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export type DescriptionItem = {
6
+ key: string
7
+ label: React.ReactNode
8
+ value: React.ReactNode
9
+ span?: 1 | 2 | 3 | 4
10
+ }
11
+
12
+ export type DescriptionsProps = React.ComponentProps<"div"> & {
13
+ items: DescriptionItem[]
14
+ title?: React.ReactNode
15
+ extra?: React.ReactNode
16
+ bordered?: boolean
17
+ columns?: 1 | 2 | 3 | 4
18
+ size?: "sm" | "md" | "lg"
19
+ labelClassName?: string
20
+ valueClassName?: string
21
+ }
22
+
23
+ const gapClassName = {
24
+ sm: "gap-2 text-sm",
25
+ md: "gap-3 text-sm",
26
+ lg: "gap-4 text-base",
27
+ }
28
+
29
+ function Descriptions({
30
+ items,
31
+ title,
32
+ extra,
33
+ bordered = true,
34
+ columns = 3,
35
+ size = "md",
36
+ labelClassName,
37
+ valueClassName,
38
+ className,
39
+ ...props
40
+ }: DescriptionsProps) {
41
+ return (
42
+ <div data-slot="descriptions" className={cn("grid gap-3", className)} {...props}>
43
+ {(title || extra) && (
44
+ <div className="flex items-center justify-between gap-3">
45
+ {title && <div className="text-base font-semibold text-foreground">{title}</div>}
46
+ {extra}
47
+ </div>
48
+ )}
49
+ <div
50
+ className={cn(
51
+ "grid overflow-hidden",
52
+ bordered && "rounded-lg border bg-card",
53
+ !bordered && gapClassName[size],
54
+ columns === 1 && "grid-cols-1",
55
+ columns === 2 && "grid-cols-1 sm:grid-cols-2",
56
+ columns === 3 && "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
57
+ columns === 4 && "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
58
+ )}
59
+ >
60
+ {items.map((item) => (
61
+ <div
62
+ key={item.key}
63
+ data-slot="descriptions-item"
64
+ className={cn(
65
+ "min-w-0",
66
+ bordered ? "border-b border-r p-3 last:border-b-0" : "grid gap-1",
67
+ item.span === 2 && "sm:col-span-2",
68
+ item.span === 3 && "lg:col-span-3",
69
+ item.span === 4 && "lg:col-span-4"
70
+ )}
71
+ >
72
+ <div className={cn("text-xs font-medium uppercase tracking-wide text-muted-foreground", labelClassName)}>
73
+ {item.label}
74
+ </div>
75
+ <div className={cn("mt-1 min-w-0 break-words text-sm text-foreground", valueClassName)}>{item.value}</div>
76
+ </div>
77
+ ))}
78
+ </div>
79
+ </div>
80
+ )
81
+ }
82
+
83
+ export { Descriptions }
@@ -0,0 +1,53 @@
1
+ import * as React from "react"
2
+
3
+ import { Card } from "@/components/ui/card"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ export type EntityCardProps = React.ComponentProps<typeof Card> & {
7
+ title: React.ReactNode
8
+ description?: React.ReactNode
9
+ media?: React.ReactNode
10
+ icon?: React.ReactNode
11
+ status?: React.ReactNode
12
+ meta?: React.ReactNode
13
+ actions?: React.ReactNode
14
+ footer?: React.ReactNode
15
+ selected?: boolean
16
+ disabled?: boolean
17
+ orientation?: "vertical" | "horizontal"
18
+ onSelect?: () => void
19
+ }
20
+
21
+ function EntityCard({ title, description, media, icon, status, meta, actions, footer, selected = false, disabled = false, orientation = "vertical", onSelect, className, ...props }: EntityCardProps) {
22
+ return (
23
+ <Card
24
+ data-slot="entity-card"
25
+ data-selected={selected || undefined}
26
+ data-disabled={disabled || undefined}
27
+ className={cn("overflow-hidden transition-colors hover:bg-muted/35 data-[selected=true]:border-primary data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-55", onSelect && "cursor-pointer", orientation === "horizontal" && "flex", className)}
28
+ onClick={onSelect}
29
+ {...props}
30
+ >
31
+ {media && <div className={cn("bg-muted", orientation === "horizontal" ? "w-32 shrink-0" : "aspect-video")}>{media}</div>}
32
+ <div className="grid min-w-0 flex-1 gap-3 p-4">
33
+ <div className="flex items-start justify-between gap-3">
34
+ <div className="flex min-w-0 items-start gap-3">
35
+ {icon && <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">{icon}</div>}
36
+ <div className="grid min-w-0 gap-1">
37
+ <div className="flex flex-wrap items-center gap-2">
38
+ <div className="truncate text-sm font-medium text-foreground">{title}</div>
39
+ {status}
40
+ </div>
41
+ {description && <div className="line-clamp-2 text-sm text-muted-foreground">{description}</div>}
42
+ </div>
43
+ </div>
44
+ {actions && <div className="shrink-0" onClick={(event) => event.stopPropagation()}>{actions}</div>}
45
+ </div>
46
+ {meta && <div className="text-xs text-muted-foreground">{meta}</div>}
47
+ {footer && <div className="border-t pt-3 text-sm text-muted-foreground">{footer}</div>}
48
+ </div>
49
+ </Card>
50
+ )
51
+ }
52
+
53
+ export { EntityCard }
@@ -0,0 +1,54 @@
1
+ import * as React from "react"
2
+ import { FileIcon, MoreHorizontalIcon } from "lucide-react"
3
+
4
+ import { Button } from "@/components/ui/button"
5
+ import { cn } from "@/lib/utils"
6
+
7
+ export type FileCardProps = React.ComponentProps<"div"> & {
8
+ name: React.ReactNode
9
+ description?: React.ReactNode
10
+ meta?: React.ReactNode
11
+ icon?: React.ReactNode
12
+ preview?: React.ReactNode
13
+ actions?: React.ReactNode
14
+ selected?: boolean
15
+ disabled?: boolean
16
+ onOpen?: () => void
17
+ }
18
+
19
+ function FileCard({ name, description, meta, icon, preview, actions, selected = false, disabled = false, onOpen, className, ...props }: FileCardProps) {
20
+ return (
21
+ <div
22
+ data-slot="file-card"
23
+ data-selected={selected || undefined}
24
+ data-disabled={disabled || undefined}
25
+ className={cn(
26
+ "group grid gap-3 rounded-lg border bg-card p-3 transition-colors hover:bg-muted/35 data-[selected=true]:border-primary data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-55",
27
+ onOpen && "cursor-pointer",
28
+ className
29
+ )}
30
+ onClick={onOpen}
31
+ {...props}
32
+ >
33
+ {preview && <div className="overflow-hidden rounded-md bg-muted">{preview}</div>}
34
+ <div className="flex items-start gap-3">
35
+ <div className="flex size-9 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
36
+ {icon ?? <FileIcon className="size-4" />}
37
+ </div>
38
+ <div className="min-w-0 flex-1">
39
+ <div className="truncate text-sm font-medium text-foreground">{name}</div>
40
+ {description && <div className="mt-0.5 line-clamp-2 text-xs text-muted-foreground">{description}</div>}
41
+ {meta && <div className="mt-2 text-xs text-muted-foreground">{meta}</div>}
42
+ </div>
43
+ {actions ?? (
44
+ <Button type="button" variant="ghost" size="icon-xs" onClick={(event) => event.stopPropagation()}>
45
+ <MoreHorizontalIcon />
46
+ <span className="sr-only">File actions</span>
47
+ </Button>
48
+ )}
49
+ </div>
50
+ </div>
51
+ )
52
+ }
53
+
54
+ export { FileCard }
@@ -0,0 +1,30 @@
1
+ export * from "./description-list"
2
+ export * from "./progress"
3
+ export * from "./result"
4
+ export * from "./timeline"
5
+ export * from "./metric-grid"
6
+ export * from "./activity-feed"
7
+ export * from "./status-legend"
8
+ export * from "./avatar"
9
+ export * from "./data-state"
10
+ export * from "./statistic"
11
+ export * from "./list"
12
+ export * from "./descriptions"
13
+ export * from "./kanban"
14
+ export * from "./tag-list"
15
+ export * from "./tree-view"
16
+ export * from "./keyboard-shortcut"
17
+ export * from "./code-block"
18
+ export * from "./file-card"
19
+ export * from "./property-grid"
20
+ export * from "./entity-card"
21
+ export {
22
+ SmartCard as InfoCard,
23
+ type SmartCardClassNames as InfoCardClassNames,
24
+ type SmartCardDensity as InfoCardDensity,
25
+ type SmartCardOrientation as InfoCardOrientation,
26
+ type SmartCardProps as InfoCardProps,
27
+ type SmartCardRenderContext as InfoCardRenderContext,
28
+ type SmartCardSize as InfoCardSize,
29
+ type SmartCardVariant as InfoCardVariant,
30
+ } from "./smart-card"