banhaten 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 (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -0
  3. package/banhaten.config.example.json +13 -0
  4. package/package.json +59 -0
  5. package/registry/assets/activity-feed-avatar.png +0 -0
  6. package/registry/assets/avatars/avatar-01.jpg +0 -0
  7. package/registry/assets/avatars/avatar-02.jpg +0 -0
  8. package/registry/assets/avatars/avatar-03.jpg +0 -0
  9. package/registry/assets/avatars/avatar-04.jpg +0 -0
  10. package/registry/assets/avatars/avatar-05.jpg +0 -0
  11. package/registry/assets/avatars/avatar-06.jpg +0 -0
  12. package/registry/assets/avatars/avatar-07.jpg +0 -0
  13. package/registry/assets/avatars/avatar-08.jpg +0 -0
  14. package/registry/assets/avatars/avatar-09.jpg +0 -0
  15. package/registry/assets/avatars/avatar-10.jpg +0 -0
  16. package/registry/assets/avatars/avatar-11.jpg +0 -0
  17. package/registry/assets/avatars/avatar-12.jpg +0 -0
  18. package/registry/assets/avatars/avatar-13.jpg +0 -0
  19. package/registry/assets/avatars/avatar-14.jpg +0 -0
  20. package/registry/assets/avatars/avatar-15.jpg +0 -0
  21. package/registry/assets/avatars/avatar-16.jpg +0 -0
  22. package/registry/assets/avatars/avatar-17.jpg +0 -0
  23. package/registry/assets/avatars/avatar-18.jpg +0 -0
  24. package/registry/assets/avatars/avatar-19.jpg +0 -0
  25. package/registry/assets/avatars/avatar-20.jpg +0 -0
  26. package/registry/assets/avatars/avatar-21.jpg +0 -0
  27. package/registry/assets/avatars/avatar-22.jpg +0 -0
  28. package/registry/assets/avatars/avatar-23.jpg +0 -0
  29. package/registry/assets/avatars/avatar-24.jpg +0 -0
  30. package/registry/assets/avatars/avatar-25.jpg +0 -0
  31. package/registry/assets/avatars/avatar-26.jpg +0 -0
  32. package/registry/assets/avatars/avatar-27.jpg +0 -0
  33. package/registry/assets/avatars/avatar-28.jpg +0 -0
  34. package/registry/assets/avatars/avatar-29.jpg +0 -0
  35. package/registry/assets/avatars/avatar-30.jpg +0 -0
  36. package/registry/assets/avatars/avatar-31.jpg +0 -0
  37. package/registry/assets/avatars/avatar-32.jpg +0 -0
  38. package/registry/assets/avatars/avatar-33.jpg +0 -0
  39. package/registry/assets/avatars/avatar-34.jpg +0 -0
  40. package/registry/assets/avatars/avatar-35.jpg +0 -0
  41. package/registry/assets/image-assets.json +744 -0
  42. package/registry/assets/images/art-01.jpg +0 -0
  43. package/registry/assets/images/art-02.jpg +0 -0
  44. package/registry/assets/images/art-03.jpg +0 -0
  45. package/registry/assets/images/art-04.jpg +0 -0
  46. package/registry/assets/images/art-05.jpg +0 -0
  47. package/registry/assets/images/art-06.jpg +0 -0
  48. package/registry/assets/images/art-07.jpg +0 -0
  49. package/registry/assets/images/art-08.jpg +0 -0
  50. package/registry/assets/images/art-09.jpg +0 -0
  51. package/registry/assets/images/art-10.jpg +0 -0
  52. package/registry/assets/images/art-11.jpg +0 -0
  53. package/registry/assets/images/art-12.jpg +0 -0
  54. package/registry/assets/images/art-13.jpg +0 -0
  55. package/registry/assets/images/art-14.jpg +0 -0
  56. package/registry/assets/images/art-15.jpg +0 -0
  57. package/registry/assets/images/art-16.jpg +0 -0
  58. package/registry/assets/images/art-17.jpg +0 -0
  59. package/registry/assets/images/art-18.jpg +0 -0
  60. package/registry/assets/images/art-19.jpg +0 -0
  61. package/registry/assets/images/art-20.jpg +0 -0
  62. package/registry/assets/images/art-21.jpg +0 -0
  63. package/registry/assets/images/art-22.jpg +0 -0
  64. package/registry/assets/images/art-23.jpg +0 -0
  65. package/registry/assets/images/art-24.jpg +0 -0
  66. package/registry/assets/images/art-25.jpg +0 -0
  67. package/registry/assets/images/art-26.jpg +0 -0
  68. package/registry/assets/images/art-27.jpg +0 -0
  69. package/registry/assets/images/nature-01.jpg +0 -0
  70. package/registry/assets/images/nature-02.jpg +0 -0
  71. package/registry/assets/images/nature-03.jpg +0 -0
  72. package/registry/assets/images/nature-04.jpg +0 -0
  73. package/registry/assets/images/nature-05.jpg +0 -0
  74. package/registry/assets/images/nature-06.jpg +0 -0
  75. package/registry/assets/images/nature-07.jpg +0 -0
  76. package/registry/assets/images/nature-08.jpg +0 -0
  77. package/registry/assets/images/nature-09.jpg +0 -0
  78. package/registry/assets/images/nature-10.jpg +0 -0
  79. package/registry/assets/images/nature-11.jpg +0 -0
  80. package/registry/assets/images/nature-12.jpg +0 -0
  81. package/registry/assets/images/nature-13.jpg +0 -0
  82. package/registry/assets/images/nature-14.jpg +0 -0
  83. package/registry/assets/images/nature-15.jpg +0 -0
  84. package/registry/assets/images/nature-16.jpg +0 -0
  85. package/registry/assets/images/nature-17.jpg +0 -0
  86. package/registry/assets/images/nature-18.jpg +0 -0
  87. package/registry/assets/images/nature-19.jpg +0 -0
  88. package/registry/assets/images/nature-20.jpg +0 -0
  89. package/registry/components/accordion.tsx +119 -0
  90. package/registry/components/alert.tsx +282 -0
  91. package/registry/components/attribute.tsx +452 -0
  92. package/registry/components/avatar.tsx +142 -0
  93. package/registry/components/badge.tsx +567 -0
  94. package/registry/components/button-group.tsx +246 -0
  95. package/registry/components/button.tsx +102 -0
  96. package/registry/components/card.tsx +613 -0
  97. package/registry/components/checkbox.tsx +244 -0
  98. package/registry/components/date-picker.tsx +1143 -0
  99. package/registry/components/divider.tsx +82 -0
  100. package/registry/components/expanded/ActivityFeed.tsx +226 -0
  101. package/registry/components/expanded/Banner.tsx +145 -0
  102. package/registry/components/expanded/BannerBoard.tsx +225 -0
  103. package/registry/components/expanded/Breadcrumbs.tsx +156 -0
  104. package/registry/components/expanded/CatalogComponentsShowcase.tsx +211 -0
  105. package/registry/components/expanded/CatalogDivider.tsx +48 -0
  106. package/registry/components/expanded/CatalogTag.tsx +92 -0
  107. package/registry/components/expanded/CommandBar.tsx +406 -0
  108. package/registry/components/expanded/FileUpload.tsx +231 -0
  109. package/registry/components/expanded/IconExplorer.tsx +612 -0
  110. package/registry/components/expanded/OnboardingStepListItem.tsx +67 -0
  111. package/registry/components/expanded/PageHeader.tsx +184 -0
  112. package/registry/components/expanded/Slideout.tsx +514 -0
  113. package/registry/components/expanded/Steps.tsx +266 -0
  114. package/registry/components/expanded/Table.tsx +1014 -0
  115. package/registry/components/expanded/Tabs.tsx +86 -0
  116. package/registry/components/expanded/Timeline.tsx +235 -0
  117. package/registry/components/expanded/TimelineShowcase.tsx +158 -0
  118. package/registry/components/expanded/activityFeed.css +292 -0
  119. package/registry/components/expanded/banner.css +312 -0
  120. package/registry/components/expanded/breadcrumbs.css +140 -0
  121. package/registry/components/expanded/catalogComponentsShowcase.css +87 -0
  122. package/registry/components/expanded/commandBar.css +473 -0
  123. package/registry/components/expanded/divider.css +75 -0
  124. package/registry/components/expanded/fileUpload.css +228 -0
  125. package/registry/components/expanded/iconExplorer.css +764 -0
  126. package/registry/components/expanded/iconPacks.ts +866 -0
  127. package/registry/components/expanded/onboardingStepListItem.css +126 -0
  128. package/registry/components/expanded/pageHeader.css +287 -0
  129. package/registry/components/expanded/slideout.css +955 -0
  130. package/registry/components/expanded/steps.css +329 -0
  131. package/registry/components/expanded/table.css +607 -0
  132. package/registry/components/expanded/tabs.css +197 -0
  133. package/registry/components/expanded/tag.css +148 -0
  134. package/registry/components/expanded/timeline.css +282 -0
  135. package/registry/components/input-content.ts +106 -0
  136. package/registry/components/input.tsx +866 -0
  137. package/registry/components/menu.tsx +758 -0
  138. package/registry/components/modal.tsx +799 -0
  139. package/registry/components/pagination.tsx +543 -0
  140. package/registry/components/progress-slider.tsx +216 -0
  141. package/registry/components/progress.tsx +367 -0
  142. package/registry/components/radio-card.tsx +654 -0
  143. package/registry/components/radio-group.tsx +570 -0
  144. package/registry/components/select-content.tsx +313 -0
  145. package/registry/components/select.tsx +871 -0
  146. package/registry/components/slider.tsx +380 -0
  147. package/registry/components/social-button.tsx +360 -0
  148. package/registry/components/spinner.tsx +31 -0
  149. package/registry/components/tag.tsx +423 -0
  150. package/registry/components/textarea.tsx +625 -0
  151. package/registry/components/toggle.tsx +272 -0
  152. package/registry/components/toolbar.tsx +467 -0
  153. package/registry/components/tooltip.tsx +427 -0
  154. package/registry/examples/accordion-demo.tsx +34 -0
  155. package/registry/examples/alert-demo.tsx +14 -0
  156. package/registry/examples/attribute-demo.tsx +65 -0
  157. package/registry/examples/avatar-demo.tsx +74 -0
  158. package/registry/examples/badge-demo.tsx +53 -0
  159. package/registry/examples/button-demo.tsx +83 -0
  160. package/registry/examples/button-group-demo.tsx +42 -0
  161. package/registry/examples/card-demo.tsx +48 -0
  162. package/registry/examples/checkbox-demo.tsx +67 -0
  163. package/registry/examples/date-picker-demo.tsx +74 -0
  164. package/registry/examples/divider-demo.tsx +17 -0
  165. package/registry/examples/expanded/activity-feed-demo.tsx +22 -0
  166. package/registry/examples/expanded/banner-demo.tsx +23 -0
  167. package/registry/examples/expanded/catalog-components-demo.tsx +5 -0
  168. package/registry/examples/expanded/command-bar-demo.tsx +10 -0
  169. package/registry/examples/expanded/icons-demo.tsx +5 -0
  170. package/registry/examples/expanded/onboarding-step-demo.tsx +11 -0
  171. package/registry/examples/expanded/page-header-demo.tsx +19 -0
  172. package/registry/examples/expanded/slideout-demo.tsx +15 -0
  173. package/registry/examples/expanded/steps-demo.tsx +18 -0
  174. package/registry/examples/expanded/tabs-demo.tsx +13 -0
  175. package/registry/examples/expanded/timeline-demo.tsx +18 -0
  176. package/registry/examples/input-demo.tsx +87 -0
  177. package/registry/examples/menu-demo.tsx +109 -0
  178. package/registry/examples/modal-demo.tsx +16 -0
  179. package/registry/examples/pagination-demo.tsx +17 -0
  180. package/registry/examples/progress-demo.tsx +37 -0
  181. package/registry/examples/progress-slider-demo.tsx +29 -0
  182. package/registry/examples/radio-card-demo.tsx +51 -0
  183. package/registry/examples/radio-group-demo.tsx +62 -0
  184. package/registry/examples/select-demo.tsx +73 -0
  185. package/registry/examples/slider-demo.tsx +31 -0
  186. package/registry/examples/social-button-demo.tsx +51 -0
  187. package/registry/examples/tag-demo.tsx +29 -0
  188. package/registry/examples/textarea-demo.tsx +79 -0
  189. package/registry/examples/toggle-demo.tsx +59 -0
  190. package/registry/examples/toolbar-demo.tsx +80 -0
  191. package/registry/examples/tooltip-demo.tsx +115 -0
  192. package/registry/hooks/use-direction.ts +27 -0
  193. package/registry/index.json +1213 -0
  194. package/registry/styles/globals.css +4600 -0
  195. package/registry/utils/cn.ts +6 -0
  196. package/src/cli/index.js +826 -0
  197. package/tokens/Color mode.zip +0 -0
  198. package/tokens/Numbers.zip +0 -0
  199. package/tokens/Radius.zip +0 -0
  200. package/tokens/Theme.zip +0 -0
  201. package/tokens/banhaten.tokens.json +5525 -0
@@ -0,0 +1,452 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ type AttributeLayout = "inline" | "end" | "stacked" | "two-column"
7
+ type AttributeDensity = "compact" | "default" | "comfortable"
8
+ type AttributeAlign = "start" | "end" | "stretch"
9
+ type AttributeValueDir = "auto" | "ltr" | "rtl"
10
+
11
+ type AttributeContextValue = {
12
+ density: AttributeDensity
13
+ dividers: boolean
14
+ labelWidth: string
15
+ layout: AttributeLayout
16
+ }
17
+
18
+ type AttributeStyle = React.CSSProperties & {
19
+ "--bh-attribute-label-width"?: string
20
+ }
21
+
22
+ const defaultAttributeContext: AttributeContextValue = {
23
+ density: "default",
24
+ dividers: false,
25
+ labelWidth: "var(--bh-attribute-label-width)",
26
+ layout: "inline",
27
+ }
28
+
29
+ const AttributeContext = React.createContext<AttributeContextValue>(
30
+ defaultAttributeContext
31
+ )
32
+
33
+ const attributeCardVariants = cva(
34
+ [
35
+ "min-w-0 overflow-hidden rounded-[var(--bh-radius-2xl-12)]",
36
+ "border border-[var(--bh-border-subtle)] bg-[var(--bh-bg-default)]",
37
+ "text-[var(--bh-content-default)]",
38
+ ],
39
+ {
40
+ variants: {
41
+ padding: {
42
+ compact:
43
+ "px-[var(--bh-space-4xl-20)] pb-[var(--bh-space-xl-12)] pt-[var(--bh-space-4xl-20)]",
44
+ default:
45
+ "px-[var(--bh-space-5xl-24)] pb-[var(--bh-space-3xl-16)] pt-[var(--bh-space-5xl-24)]",
46
+ },
47
+ },
48
+ defaultVariants: {
49
+ padding: "default",
50
+ },
51
+ }
52
+ )
53
+
54
+ const attributeHeaderVariants = cva(
55
+ "flex min-w-0 flex-col items-start gap-[var(--bh-space-md-8)] pb-[var(--bh-space-4xl-20)]",
56
+ {
57
+ variants: {
58
+ size: {
59
+ default: "",
60
+ compact: "gap-[var(--bh-space-sm-6)] pb-[var(--bh-space-3xl-16)]",
61
+ },
62
+ },
63
+ defaultVariants: {
64
+ size: "default",
65
+ },
66
+ }
67
+ )
68
+
69
+ const attributeListVariants = cva("grid min-w-0", {
70
+ variants: {
71
+ layout: {
72
+ inline: "grid-cols-1",
73
+ end: "grid-cols-1",
74
+ stacked: "grid-cols-1",
75
+ "two-column":
76
+ "grid-cols-1 gap-x-[var(--bh-space-6xl-32)] sm:grid-cols-2",
77
+ },
78
+ bordered: {
79
+ true: "overflow-hidden rounded-[var(--bh-radius-lg-8)] border border-[var(--bh-border-subtle)]",
80
+ false: "",
81
+ },
82
+ },
83
+ defaultVariants: {
84
+ layout: "inline",
85
+ bordered: false,
86
+ },
87
+ })
88
+
89
+ const attributeItemVariants = cva("min-w-0", {
90
+ variants: {
91
+ layout: {
92
+ inline:
93
+ "grid grid-cols-1 items-center gap-x-[var(--bh-space-xs-4)] sm:grid-cols-[var(--bh-attribute-label-width)_minmax(0,1fr)]",
94
+ end:
95
+ "grid grid-cols-1 items-center gap-x-[var(--bh-space-xs-4)] sm:grid-cols-[var(--bh-attribute-label-width)_minmax(0,1fr)]",
96
+ stacked:
97
+ "flex flex-col items-start justify-center gap-[var(--bh-space-xs-4)]",
98
+ "two-column":
99
+ "flex flex-col items-start justify-center gap-[var(--bh-space-xs-4)]",
100
+ },
101
+ density: {
102
+ compact: "py-[var(--bh-space-md-8)]",
103
+ default: "py-[var(--bh-space-xl-12)] sm:py-[var(--bh-space-3xl-16)]",
104
+ comfortable: "py-[var(--bh-space-3xl-16)]",
105
+ },
106
+ divided: {
107
+ true: "border-b border-[var(--bh-border-subtle)] last:border-b-0",
108
+ false: "",
109
+ },
110
+ fullSpan: {
111
+ true: "sm:col-span-2",
112
+ false: "",
113
+ },
114
+ },
115
+ defaultVariants: {
116
+ layout: "inline",
117
+ density: "default",
118
+ divided: false,
119
+ fullSpan: false,
120
+ },
121
+ })
122
+
123
+ const attributeLabelVariants = cva(
124
+ [
125
+ "min-w-0 break-words text-[length:var(--bh-text-body-md-regular-font-size)]",
126
+ "font-[var(--bh-text-body-md-regular-font-weight)]",
127
+ "leading-[var(--bh-text-body-md-regular-line-height)]",
128
+ "tracking-[var(--bh-text-body-md-regular-letter-spacing)]",
129
+ "text-[var(--bh-content-subtle)]",
130
+ ],
131
+ {
132
+ variants: {
133
+ truncate: {
134
+ true: "truncate",
135
+ false: "",
136
+ },
137
+ },
138
+ defaultVariants: {
139
+ truncate: false,
140
+ },
141
+ }
142
+ )
143
+
144
+ const attributeValueVariants = cva(
145
+ [
146
+ "flex min-w-0 flex-wrap items-center gap-[var(--bh-space-xs-4)]",
147
+ "break-words text-[length:var(--bh-text-body-md-medium-font-size)]",
148
+ "font-[var(--bh-text-body-md-medium-font-weight)]",
149
+ "leading-[var(--bh-text-body-md-medium-line-height)]",
150
+ "tracking-[var(--bh-text-body-md-medium-letter-spacing)]",
151
+ "text-[var(--bh-content-default)]",
152
+ ],
153
+ {
154
+ variants: {
155
+ align: {
156
+ start: "justify-self-start text-start",
157
+ end: "justify-self-end justify-end text-end rtl:justify-self-start rtl:justify-start rtl:text-right",
158
+ stretch: "w-full justify-self-stretch text-start",
159
+ },
160
+ truncate: {
161
+ true: "[&_[data-slot='attribute-value-text']]:truncate",
162
+ false: "",
163
+ },
164
+ },
165
+ defaultVariants: {
166
+ align: "start",
167
+ truncate: false,
168
+ },
169
+ }
170
+ )
171
+
172
+ type AttributeCardProps = React.ComponentProps<"section"> &
173
+ VariantProps<typeof attributeCardVariants>
174
+
175
+ const AttributeCard = React.forwardRef<HTMLElement, AttributeCardProps>(
176
+ function AttributeCard({ className, padding, ...props }, ref) {
177
+ return (
178
+ <section
179
+ data-slot="attribute-card"
180
+ ref={ref}
181
+ className={cn(attributeCardVariants({ padding, className }))}
182
+ {...props}
183
+ />
184
+ )
185
+ }
186
+ )
187
+
188
+ type AttributeHeaderProps = Omit<React.ComponentProps<"header">, "title"> &
189
+ VariantProps<typeof attributeHeaderVariants> & {
190
+ description?: React.ReactNode
191
+ icon?: React.ReactNode
192
+ title: React.ReactNode
193
+ }
194
+
195
+ const AttributeHeader = React.forwardRef<HTMLElement, AttributeHeaderProps>(
196
+ function AttributeHeader({
197
+ className,
198
+ description,
199
+ icon,
200
+ size,
201
+ title,
202
+ ...props
203
+ }, ref) {
204
+ return (
205
+ <header
206
+ data-slot="attribute-header"
207
+ ref={ref}
208
+ className={cn(attributeHeaderVariants({ size, className }))}
209
+ {...props}
210
+ >
211
+ {icon ? (
212
+ <span
213
+ data-slot="attribute-header-icon"
214
+ className="grid size-[var(--bh-attribute-header-icon-size)] place-items-center rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-soft)] text-[var(--bh-content-subtle)] sm:size-[var(--bh-attribute-header-icon-size-lg)] [&_svg]:size-[var(--bh-space-4xl-20)] sm:[&_svg]:size-[var(--bh-space-5xl-24)]"
215
+ >
216
+ {icon}
217
+ </span>
218
+ ) : null}
219
+ <span
220
+ data-slot="attribute-header-content"
221
+ className="flex min-w-0 flex-col gap-[var(--bh-space-xs-4)]"
222
+ >
223
+ <span
224
+ data-slot="attribute-header-title"
225
+ dir="auto"
226
+ className="break-words text-[length:var(--bh-text-heading-sm-medium-font-size)] font-[var(--bh-text-heading-sm-medium-font-weight)] leading-[var(--bh-text-heading-sm-medium-line-height)] tracking-[var(--bh-text-heading-sm-medium-letter-spacing)] text-[var(--bh-content-default)]"
227
+ >
228
+ {title}
229
+ </span>
230
+ {description ? (
231
+ <span
232
+ data-slot="attribute-header-description"
233
+ dir="auto"
234
+ className="break-words text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
235
+ >
236
+ {description}
237
+ </span>
238
+ ) : null}
239
+ </span>
240
+ </header>
241
+ )
242
+ }
243
+ )
244
+
245
+ type AttributeListProps = React.ComponentProps<"dl"> &
246
+ VariantProps<typeof attributeListVariants> & {
247
+ density?: AttributeDensity
248
+ dividers?: boolean
249
+ labelWidth?: string
250
+ }
251
+
252
+ const AttributeList = React.forwardRef<HTMLDListElement, AttributeListProps>(
253
+ function AttributeList({
254
+ bordered = false,
255
+ className,
256
+ density = "default",
257
+ dividers = false,
258
+ labelWidth = defaultAttributeContext.labelWidth,
259
+ layout = "inline",
260
+ style,
261
+ ...props
262
+ }, ref) {
263
+ const value = React.useMemo(
264
+ () => ({
265
+ density,
266
+ dividers,
267
+ labelWidth,
268
+ layout: (layout || "inline") as AttributeLayout,
269
+ }),
270
+ [density, dividers, labelWidth, layout]
271
+ )
272
+ const attributeStyle = {
273
+ "--bh-attribute-label-width": labelWidth,
274
+ ...style,
275
+ } as AttributeStyle
276
+
277
+ return (
278
+ <AttributeContext.Provider value={value}>
279
+ <dl
280
+ data-density={density}
281
+ data-layout={layout}
282
+ data-slot="attribute-list"
283
+ ref={ref}
284
+ style={attributeStyle}
285
+ className={cn(attributeListVariants({ layout, bordered, className }))}
286
+ {...props}
287
+ />
288
+ </AttributeContext.Provider>
289
+ )
290
+ }
291
+ )
292
+
293
+ type AttributeItemProps = Omit<React.ComponentProps<"div">, "children"> &
294
+ VariantProps<typeof attributeItemVariants> & {
295
+ children?: React.ReactNode
296
+ label: React.ReactNode
297
+ labelClassName?: string
298
+ truncate?: boolean
299
+ value?: React.ReactNode
300
+ valueAlign?: AttributeAlign
301
+ valueClassName?: string
302
+ valueDir?: AttributeValueDir
303
+ }
304
+
305
+ const AttributeItem = React.forwardRef<HTMLDivElement, AttributeItemProps>(
306
+ function AttributeItem({
307
+ children,
308
+ className,
309
+ density,
310
+ divided,
311
+ fullSpan = false,
312
+ label,
313
+ labelClassName,
314
+ layout,
315
+ truncate = false,
316
+ value,
317
+ valueAlign,
318
+ valueClassName,
319
+ valueDir = "auto",
320
+ ...props
321
+ }, ref) {
322
+ const context = React.useContext(AttributeContext)
323
+ const selectedLayout = layout || context.layout
324
+ const selectedDensity = density || context.density
325
+ const selectedDivided = divided ?? context.dividers
326
+ const selectedAlign =
327
+ valueAlign || (selectedLayout === "end" ? "end" : "start")
328
+
329
+ return (
330
+ <div
331
+ data-slot="attribute-item"
332
+ ref={ref}
333
+ className={cn(
334
+ attributeItemVariants({
335
+ layout: selectedLayout,
336
+ density: selectedDensity,
337
+ divided: selectedDivided,
338
+ fullSpan,
339
+ className,
340
+ })
341
+ )}
342
+ {...props}
343
+ >
344
+ <AttributeLabel className={labelClassName} truncate={truncate}>
345
+ {label}
346
+ </AttributeLabel>
347
+ <AttributeValue
348
+ align={selectedAlign}
349
+ className={valueClassName}
350
+ dir={valueDir}
351
+ truncate={truncate}
352
+ >
353
+ {children ?? value}
354
+ </AttributeValue>
355
+ </div>
356
+ )
357
+ }
358
+ )
359
+
360
+ type AttributeLabelProps = React.ComponentProps<"dt"> &
361
+ VariantProps<typeof attributeLabelVariants>
362
+
363
+ const AttributeLabel = React.forwardRef<HTMLElement, AttributeLabelProps>(
364
+ function AttributeLabel({ children, className, truncate, ...props }, ref) {
365
+ return (
366
+ <dt
367
+ data-slot="attribute-label"
368
+ ref={ref}
369
+ className={cn(attributeLabelVariants({ truncate, className }))}
370
+ {...props}
371
+ >
372
+ {renderAttributeText(children, "attribute-label-text")}
373
+ </dt>
374
+ )
375
+ }
376
+ )
377
+
378
+ type AttributeValueProps = React.ComponentProps<"dd"> &
379
+ VariantProps<typeof attributeValueVariants> & {
380
+ dir?: AttributeValueDir
381
+ }
382
+
383
+ const AttributeValue = React.forwardRef<HTMLElement, AttributeValueProps>(
384
+ function AttributeValue({
385
+ align,
386
+ children,
387
+ className,
388
+ dir = "auto",
389
+ truncate,
390
+ ...props
391
+ }, ref) {
392
+ return (
393
+ <dd
394
+ data-slot="attribute-value"
395
+ ref={ref}
396
+ className={cn(attributeValueVariants({ align, truncate, className }))}
397
+ {...props}
398
+ >
399
+ {renderAttributeText(children, "attribute-value-text", dir)}
400
+ </dd>
401
+ )
402
+ }
403
+ )
404
+
405
+ function renderAttributeText(
406
+ children: React.ReactNode,
407
+ slot: string,
408
+ dir: AttributeValueDir = "auto"
409
+ ): React.ReactNode {
410
+ return React.Children.map(children, (child) => {
411
+ if (
412
+ typeof child === "string" ||
413
+ typeof child === "number" ||
414
+ typeof child === "bigint"
415
+ ) {
416
+ return (
417
+ <span data-slot={slot} dir={dir} className="min-w-0">
418
+ {child}
419
+ </span>
420
+ )
421
+ }
422
+
423
+ return child
424
+ })
425
+ }
426
+
427
+ export {
428
+ AttributeCard,
429
+ AttributeHeader,
430
+ AttributeItem,
431
+ AttributeLabel,
432
+ AttributeList,
433
+ AttributeValue,
434
+ attributeCardVariants,
435
+ attributeHeaderVariants,
436
+ attributeItemVariants,
437
+ attributeLabelVariants,
438
+ attributeListVariants,
439
+ attributeValueVariants,
440
+ }
441
+ export type {
442
+ AttributeAlign,
443
+ AttributeCardProps,
444
+ AttributeDensity,
445
+ AttributeHeaderProps,
446
+ AttributeItemProps,
447
+ AttributeLabelProps,
448
+ AttributeLayout,
449
+ AttributeListProps,
450
+ AttributeValueDir,
451
+ AttributeValueProps,
452
+ }
@@ -0,0 +1,142 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AvatarPrimitive from "@radix-ui/react-avatar"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const avatarVariants = cva("relative flex shrink-0 rounded-[var(--bh-radius-full)]", {
10
+ variants: {
11
+ size: {
12
+ sm: "size-[var(--bh-space-6xl-32)]",
13
+ default: "size-[var(--bh-space-7xl-40)]",
14
+ lg: "size-[var(--bh-space-8xl-48)]",
15
+ xl: "size-[var(--bh-space-9xl-64)]",
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ size: "default",
20
+ },
21
+ })
22
+
23
+ const avatarFallbackVariants = cva(
24
+ "flex size-full items-center justify-center rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-subtle)] whitespace-nowrap text-[var(--bh-content-subtle)]",
25
+ {
26
+ variants: {
27
+ size: {
28
+ sm: "text-[length:var(--bh-text-body-xs-medium-font-size)] font-[var(--bh-text-body-xs-medium-font-weight)] leading-[var(--bh-text-body-xs-medium-line-height)] tracking-[var(--bh-text-body-xs-medium-letter-spacing)]",
29
+ default: "text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)]",
30
+ lg: "text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)]",
31
+ xl: "text-[length:var(--bh-text-body-md-medium-font-size)] font-[var(--bh-text-body-md-medium-font-weight)] leading-[var(--bh-text-body-md-medium-line-height)] tracking-[var(--bh-text-body-md-medium-letter-spacing)]",
32
+ },
33
+ },
34
+ defaultVariants: {
35
+ size: "default",
36
+ },
37
+ }
38
+ )
39
+
40
+ function Avatar({
41
+ className,
42
+ size,
43
+ ...props
44
+ }: React.ComponentProps<typeof AvatarPrimitive.Root> &
45
+ VariantProps<typeof avatarVariants>) {
46
+ return (
47
+ <AvatarPrimitive.Root
48
+ data-slot="avatar"
49
+ data-size={size ?? "default"}
50
+ className={cn(avatarVariants({ size, className }))}
51
+ {...props}
52
+ />
53
+ )
54
+ }
55
+
56
+ function AvatarImage({
57
+ className,
58
+ ...props
59
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
60
+ return (
61
+ <AvatarPrimitive.Image
62
+ data-slot="avatar-image"
63
+ className={cn("size-full rounded-[var(--bh-radius-full)] object-cover", className)}
64
+ {...props}
65
+ />
66
+ )
67
+ }
68
+
69
+ function AvatarFallback({
70
+ className,
71
+ size,
72
+ ...props
73
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback> &
74
+ VariantProps<typeof avatarFallbackVariants>) {
75
+ return (
76
+ <AvatarPrimitive.Fallback
77
+ data-slot="avatar-fallback"
78
+ className={cn(avatarFallbackVariants({ size, className }))}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ function AvatarBadge({
85
+ className,
86
+ ...props
87
+ }: React.ComponentProps<"span">) {
88
+ return (
89
+ <span
90
+ data-slot="avatar-badge"
91
+ className={cn(
92
+ "absolute end-0 bottom-0 flex size-[var(--bh-space-xl-12)] items-center justify-center rounded-[var(--bh-radius-full)] border-[var(--bh-space-xxs-2)] border-[var(--bh-bg-default)] bg-[var(--bh-interactive-brand-default)] text-[var(--bh-content-on-brand)] shadow-[var(--shadow-avatar-badge)]",
93
+ className
94
+ )}
95
+ {...props}
96
+ />
97
+ )
98
+ }
99
+
100
+ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
101
+ return (
102
+ <div
103
+ data-slot="avatar-group"
104
+ className={cn(
105
+ "flex items-center [&_[data-slot=avatar]]:-ms-[var(--bh-space-md-8)] [&_[data-slot=avatar]]:ring-[var(--bh-space-xxs-2)] [&_[data-slot=avatar]]:ring-[var(--bh-bg-default)] [&_[data-slot=avatar]:first-child]:ms-[var(--bh-space-none)]",
106
+ className
107
+ )}
108
+ {...props}
109
+ />
110
+ )
111
+ }
112
+
113
+ function AvatarGroupCount({
114
+ className,
115
+ children,
116
+ dir = "ltr",
117
+ ...props
118
+ }: React.ComponentProps<"span">) {
119
+ return (
120
+ <span
121
+ data-slot="avatar-group-count"
122
+ dir={dir}
123
+ className={cn(
124
+ "-ms-[var(--bh-space-md-8)] flex size-[var(--bh-space-7xl-40)] items-center justify-center rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-subtle)] text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-content-default)] ring-[var(--bh-space-xxs-2)] ring-[var(--bh-bg-default)]",
125
+ className
126
+ )}
127
+ {...props}
128
+ >
129
+ {children}
130
+ </span>
131
+ )
132
+ }
133
+
134
+ export {
135
+ Avatar,
136
+ AvatarImage,
137
+ AvatarFallback,
138
+ AvatarBadge,
139
+ AvatarGroup,
140
+ AvatarGroupCount,
141
+ avatarVariants,
142
+ }