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,654 @@
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 RadioCardType =
7
+ | "with-icon"
8
+ | "icon-radio-right"
9
+ | "payment"
10
+ | "with-avatar"
11
+ | "company-icon"
12
+ | "icon-radio-left"
13
+ | "radio-label"
14
+
15
+ type RadioCardState = "default" | "active" | "disabled"
16
+ type RadioCardDirection = "ltr" | "rtl"
17
+
18
+ const radioCardVariants = cva(
19
+ [
20
+ "group/radio-card relative inline-block w-[var(--bh-radio-card-width)] max-w-full shrink-0",
21
+ "rounded-[var(--bh-radius-xl-10)] p-[var(--bh-space-3xl-16)]",
22
+ "bg-[var(--bh-radio-card-bg)] text-start shadow-[var(--shadow-radio-card-current)] outline-none",
23
+ "transition-[background-color,box-shadow]",
24
+ "[--bh-radio-card-bg:var(--bh-bg-raised)]",
25
+ "[--bh-radio-card-border:var(--bh-border-default)]",
26
+ "[--shadow-radio-card-current:var(--shadow-radio-card)]",
27
+ "has-[:focus-visible]:[--bh-radio-card-border:var(--bh-border-brand-strong)]",
28
+ "has-[:disabled]:pointer-events-none",
29
+ "has-[:disabled]:[--bh-radio-card-border:var(--bh-border-disabled)]",
30
+ "has-[:disabled]:[--shadow-radio-card-current:var(--shadow-radio-card-disabled)]",
31
+ "has-[:disabled]:[--bh-radio-card-bg:var(--bh-bg-raised)]",
32
+ ],
33
+ {
34
+ variants: {
35
+ state: {
36
+ default:
37
+ "hover:[--bh-radio-card-bg:var(--bh-bg-raised-hover)]",
38
+ active:
39
+ "[--bh-radio-card-border:var(--bh-border-brand-strong)] [--bh-radio-card-border-width:var(--bh-radio-card-border-width-active)]",
40
+ disabled:
41
+ "pointer-events-none [--bh-radio-card-bg:var(--bh-bg-raised)] [--bh-radio-card-border:var(--bh-border-disabled)] [--shadow-radio-card-current:var(--shadow-radio-card-disabled)]",
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ state: "default",
46
+ },
47
+ }
48
+ )
49
+
50
+ type RadioCardProps = Omit<
51
+ React.ComponentProps<"label">,
52
+ "children" | "label"
53
+ > &
54
+ VariantProps<typeof radioCardVariants> & {
55
+ type?: RadioCardType
56
+ direction?: RadioCardDirection
57
+ checked?: boolean
58
+ disabled?: boolean
59
+ label?: React.ReactNode
60
+ description?: React.ReactNode
61
+ supportLabel?: React.ReactNode
62
+ supportDescription?: React.ReactNode
63
+ showDescription?: boolean
64
+ showSupportItems?: boolean
65
+ showSupportDescription?: boolean
66
+ icon?: React.ReactNode
67
+ media?: React.ReactNode
68
+ inputProps?: Omit<React.ComponentProps<"input">, "type">
69
+ }
70
+
71
+ function RadioCard({
72
+ className,
73
+ type = "with-icon",
74
+ state = "default",
75
+ direction = "ltr",
76
+ checked,
77
+ disabled,
78
+ label,
79
+ description,
80
+ supportLabel,
81
+ supportDescription,
82
+ showDescription = true,
83
+ showSupportItems = true,
84
+ showSupportDescription = true,
85
+ icon,
86
+ media,
87
+ inputProps,
88
+ ...props
89
+ }: RadioCardProps) {
90
+ const inputChecked =
91
+ checked ?? inputProps?.checked ?? Boolean(inputProps?.defaultChecked)
92
+ const isDisabled = disabled ?? inputProps?.disabled ?? state === "disabled"
93
+ const visualState: RadioCardState = isDisabled
94
+ ? "disabled"
95
+ : inputChecked || state === "active"
96
+ ? "active"
97
+ : state ?? "default"
98
+ const isRtl = direction === "rtl"
99
+ const textLabel = label
100
+ const textDescription = description
101
+
102
+ return (
103
+ <label
104
+ data-slot="radio-card"
105
+ data-state={visualState}
106
+ data-type={type}
107
+ data-direction={direction}
108
+ {...props}
109
+ dir="ltr"
110
+ className={cn(radioCardVariants({ state: visualState }), className)}
111
+ >
112
+ <input
113
+ aria-label={typeof textLabel === "string" ? textLabel : undefined}
114
+ {...inputProps}
115
+ checked={inputProps?.checked ?? checked}
116
+ className={cn("sr-only", inputProps?.className)}
117
+ disabled={isDisabled}
118
+ type="radio"
119
+ />
120
+ <span
121
+ data-slot="radio-card-layout"
122
+ className={cn(
123
+ "flex min-w-0 items-start [direction:ltr]",
124
+ type === "radio-label" && "items-center",
125
+ getCardGapClass(type, isRtl)
126
+ )}
127
+ >
128
+ {renderRadioCardContent({
129
+ type,
130
+ direction,
131
+ state: visualState,
132
+ label: textLabel,
133
+ description: textDescription,
134
+ supportLabel,
135
+ supportDescription,
136
+ showDescription,
137
+ showSupportItems,
138
+ showSupportDescription,
139
+ icon,
140
+ media,
141
+ })}
142
+ </span>
143
+ </label>
144
+ )
145
+ }
146
+
147
+ function renderRadioCardContent({
148
+ type,
149
+ direction,
150
+ state,
151
+ label,
152
+ description,
153
+ supportLabel,
154
+ supportDescription,
155
+ showDescription,
156
+ showSupportItems,
157
+ showSupportDescription,
158
+ icon,
159
+ media,
160
+ }: {
161
+ type: RadioCardType
162
+ direction: RadioCardDirection
163
+ state: RadioCardState
164
+ label?: React.ReactNode
165
+ description?: React.ReactNode
166
+ supportLabel?: React.ReactNode
167
+ supportDescription?: React.ReactNode
168
+ showDescription: boolean
169
+ showSupportItems: boolean
170
+ showSupportDescription: boolean
171
+ icon?: React.ReactNode
172
+ media?: React.ReactNode
173
+ }) {
174
+ const isRtl = direction === "rtl"
175
+ const checked = state === "active"
176
+ const disabled = state === "disabled"
177
+ const radio = <RadioCardIndicator checked={checked} disabled={disabled} />
178
+
179
+ if (type === "radio-label") {
180
+ return isRtl ? (
181
+ <>
182
+ <RadioCardLabelText label={label} rtl={isRtl} disabled={disabled} />
183
+ {radio}
184
+ </>
185
+ ) : (
186
+ <>
187
+ {radio}
188
+ <RadioCardLabelText label={label} rtl={isRtl} disabled={disabled} />
189
+ </>
190
+ )
191
+ }
192
+
193
+ if (type === "icon-radio-right") {
194
+ const text = (
195
+ <RadioCardText
196
+ description={description}
197
+ disabled={disabled}
198
+ gap="xxs"
199
+ label={label}
200
+ rtl={isRtl}
201
+ showDescription={showDescription}
202
+ />
203
+ )
204
+
205
+ return isRtl ? (
206
+ <>
207
+ {radio}
208
+ {text}
209
+ </>
210
+ ) : (
211
+ <>
212
+ {text}
213
+ {radio}
214
+ </>
215
+ )
216
+ }
217
+
218
+ if (type === "icon-radio-left") {
219
+ const text = (
220
+ <RadioCardText
221
+ description={description}
222
+ disabled={disabled}
223
+ gap="xxs"
224
+ label={label}
225
+ rtl={isRtl}
226
+ showDescription={showDescription}
227
+ />
228
+ )
229
+
230
+ if (isRtl) {
231
+ return (
232
+ <>
233
+ {text}
234
+ {radio}
235
+ </>
236
+ )
237
+ }
238
+
239
+ return (
240
+ <>
241
+ {radio}
242
+ {text}
243
+ {showSupportItems &&
244
+ (hasRenderableContent(supportLabel) ||
245
+ hasRenderableContent(supportDescription)) ? (
246
+ <RadioCardSupportText
247
+ disabled={disabled}
248
+ label={supportLabel}
249
+ description={supportDescription}
250
+ showDescription={showSupportDescription}
251
+ />
252
+ ) : null}
253
+ </>
254
+ )
255
+ }
256
+
257
+ const content = (
258
+ <RadioCardMediaContent
259
+ type={type}
260
+ direction={direction}
261
+ state={state}
262
+ icon={icon}
263
+ media={media}
264
+ label={label}
265
+ description={description}
266
+ showDescription={showDescription}
267
+ />
268
+ )
269
+
270
+ return isRtl ? (
271
+ <>
272
+ {radio}
273
+ {content}
274
+ </>
275
+ ) : (
276
+ <>
277
+ {content}
278
+ {radio}
279
+ </>
280
+ )
281
+ }
282
+
283
+ function RadioCardMediaContent({
284
+ type,
285
+ direction,
286
+ state,
287
+ icon,
288
+ media,
289
+ label,
290
+ description,
291
+ showDescription,
292
+ }: {
293
+ type: RadioCardType
294
+ direction: RadioCardDirection
295
+ state: RadioCardState
296
+ icon?: React.ReactNode
297
+ media?: React.ReactNode
298
+ label?: React.ReactNode
299
+ description?: React.ReactNode
300
+ showDescription: boolean
301
+ }) {
302
+ const isRtl = direction === "rtl"
303
+ const disabled = state === "disabled"
304
+ const text = (
305
+ <RadioCardText
306
+ description={description}
307
+ disabled={disabled}
308
+ gap={getTextGap(type, state, isRtl)}
309
+ label={label}
310
+ rtl={isRtl}
311
+ showDescription={showDescription}
312
+ />
313
+ )
314
+ const visual = (
315
+ <RadioCardMedia type={type} disabled={disabled} icon={icon} media={media} />
316
+ )
317
+
318
+ return (
319
+ <span
320
+ data-slot="radio-card-content"
321
+ className={cn(
322
+ "flex min-w-0 flex-1 items-start gap-[var(--bh-space-lg-10)]",
323
+ isRtl && "justify-end",
324
+ (type === "with-avatar" || type === "company-icon") &&
325
+ "group-hover/radio-card:items-center",
326
+ (type === "with-avatar" || type === "company-icon") &&
327
+ state !== "default" &&
328
+ "items-center",
329
+ type === "with-avatar" &&
330
+ isRtl &&
331
+ state === "active" &&
332
+ "gap-[var(--bh-space-xl-12)]"
333
+ )}
334
+ >
335
+ {isRtl ? (
336
+ <>
337
+ {text}
338
+ {visual}
339
+ </>
340
+ ) : (
341
+ <>
342
+ {visual}
343
+ {text}
344
+ </>
345
+ )}
346
+ </span>
347
+ )
348
+ }
349
+
350
+ function RadioCardText({
351
+ label,
352
+ description,
353
+ showDescription,
354
+ rtl,
355
+ disabled,
356
+ gap,
357
+ }: {
358
+ label?: React.ReactNode
359
+ description?: React.ReactNode
360
+ showDescription: boolean
361
+ rtl: boolean
362
+ disabled: boolean
363
+ gap: "xxs" | "xs"
364
+ }) {
365
+ if (!hasRenderableContent(label)) return null
366
+
367
+ return (
368
+ <span
369
+ data-slot="radio-card-text"
370
+ className={cn(
371
+ "flex min-w-0 flex-1 flex-col",
372
+ gap === "xxs"
373
+ ? "gap-[var(--bh-space-xxs-2)]"
374
+ : "gap-[var(--bh-space-xs-4)]",
375
+ rtl ? "items-end text-right" : "items-start text-left"
376
+ )}
377
+ >
378
+ {hasRenderableContent(label) ? (
379
+ <span
380
+ data-slot="radio-card-label"
381
+ dir={rtl ? "rtl" : "auto"}
382
+ className={cn(radioCardLabelClass, disabled && radioCardDisabledClass)}
383
+ >
384
+ {label}
385
+ </span>
386
+ ) : null}
387
+ {showDescription && hasRenderableContent(description) ? (
388
+ <span
389
+ data-slot="radio-card-description"
390
+ dir={rtl ? "rtl" : "auto"}
391
+ className={cn(
392
+ radioCardDescriptionClass,
393
+ disabled && radioCardDisabledClass
394
+ )}
395
+ >
396
+ {description}
397
+ </span>
398
+ ) : null}
399
+ </span>
400
+ )
401
+ }
402
+
403
+ function hasRenderableContent(content: React.ReactNode) {
404
+ return (
405
+ content !== undefined &&
406
+ content !== null &&
407
+ content !== false &&
408
+ content !== ""
409
+ )
410
+ }
411
+
412
+ function RadioCardSupportText({
413
+ label,
414
+ description,
415
+ showDescription,
416
+ disabled,
417
+ }: {
418
+ label?: React.ReactNode
419
+ description?: React.ReactNode
420
+ showDescription: boolean
421
+ disabled: boolean
422
+ }) {
423
+ return (
424
+ <span
425
+ data-slot="radio-card-support"
426
+ className="flex shrink-0 flex-col items-end gap-[var(--bh-space-xxs-2)] whitespace-nowrap text-right"
427
+ >
428
+ {hasRenderableContent(label) ? (
429
+ <span
430
+ data-slot="radio-card-support-label"
431
+ className={cn(radioCardLabelClass, disabled && radioCardDisabledClass)}
432
+ >
433
+ {label}
434
+ </span>
435
+ ) : null}
436
+ {showDescription && hasRenderableContent(description) ? (
437
+ <span
438
+ data-slot="radio-card-support-description"
439
+ className={cn(
440
+ radioCardDescriptionClass,
441
+ disabled && radioCardDisabledClass
442
+ )}
443
+ >
444
+ {description}
445
+ </span>
446
+ ) : null}
447
+ </span>
448
+ )
449
+ }
450
+
451
+ function RadioCardLabelText({
452
+ label,
453
+ rtl,
454
+ disabled,
455
+ }: {
456
+ label?: React.ReactNode
457
+ rtl: boolean
458
+ disabled: boolean
459
+ }) {
460
+ return (
461
+ <span
462
+ data-slot="radio-card-label"
463
+ dir={rtl ? "rtl" : "auto"}
464
+ className={cn(
465
+ radioCardLabelClass,
466
+ "min-w-0 flex-1",
467
+ rtl ? "text-right" : "text-left",
468
+ disabled && radioCardDisabledClass
469
+ )}
470
+ >
471
+ {label}
472
+ </span>
473
+ )
474
+ }
475
+
476
+ function RadioCardIndicator({
477
+ checked,
478
+ disabled,
479
+ }: {
480
+ checked: boolean
481
+ disabled: boolean
482
+ }) {
483
+ return (
484
+ <span
485
+ aria-hidden="true"
486
+ data-slot="radio-card-indicator"
487
+ className="flex size-[var(--bh-space-4xl-20)] shrink-0 items-center justify-center"
488
+ >
489
+ <span
490
+ className={cn(
491
+ "flex size-[var(--bh-space-3xl-16)] items-center justify-center rounded-[var(--bh-radius-full)] [--shadow-radio-control:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-radio-control-border,var(--bh-border-input)),var(--shadow-component-default)]",
492
+ checked
493
+ ? "bg-[var(--bh-interactive-radiobutton-default)] shadow-[var(--shadow-radio-control)] [--bh-radio-control-border:transparent]"
494
+ : disabled
495
+ ? "bg-[var(--bh-bg-raised)] shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]"
496
+ : "bg-[var(--bh-bg-raised)] shadow-[var(--shadow-radio-control)] [--bh-radio-control-border:var(--bh-border-input)]"
497
+ )}
498
+ >
499
+ {checked ? (
500
+ <span className="size-[var(--bh-space-sm-6)] rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-always-white)]" />
501
+ ) : null}
502
+ </span>
503
+ </span>
504
+ )
505
+ }
506
+
507
+ function RadioCardMedia({
508
+ type,
509
+ disabled,
510
+ icon,
511
+ media,
512
+ }: {
513
+ type: RadioCardType
514
+ disabled: boolean
515
+ icon?: React.ReactNode
516
+ media?: React.ReactNode
517
+ }) {
518
+ if (media) {
519
+ return (
520
+ <span
521
+ data-slot="radio-card-media"
522
+ className={cn(
523
+ "shrink-0",
524
+ (type === "with-avatar" || type === "company-icon") &&
525
+ disabled &&
526
+ "opacity-[var(--bh-opacity-disabled-media)]"
527
+ )}
528
+ >
529
+ {media}
530
+ </span>
531
+ )
532
+ }
533
+
534
+ if (type === "payment") return <RadioCardPaymentIcon />
535
+ if (type === "with-avatar") return <RadioCardAvatarPlaceholder disabled={disabled} />
536
+ if (type === "company-icon") return <RadioCardCompanyIcon disabled={disabled} />
537
+
538
+ return (
539
+ <span
540
+ data-slot="radio-card-icon"
541
+ className={cn(
542
+ "flex size-[var(--bh-space-4xl-20)] shrink-0 items-center justify-center text-[var(--bh-content-subtle)]",
543
+ disabled && "text-[var(--bh-content-disabled)]"
544
+ )}
545
+ >
546
+ {icon ?? <RadioCardUserIcon />}
547
+ </span>
548
+ )
549
+ }
550
+
551
+ function RadioCardUserIcon() {
552
+ return (
553
+ <svg
554
+ aria-hidden="true"
555
+ className="size-[var(--bh-space-4xl-20)]"
556
+ fill="none"
557
+ viewBox="0 0 20 20"
558
+ >
559
+ <path
560
+ d="M3.333 18.333c0-3.681 2.985-6.666 6.667-6.666s6.667 2.985 6.667 6.666H15c0-2.761-2.239-5-5-5s-5 2.239-5 5H3.333ZM10 10.833a5 5 0 1 1 0-10 5 5 0 0 1 0 10Zm0-1.666a3.333 3.333 0 1 0 0-6.667 3.333 3.333 0 0 0 0 6.667Z"
561
+ fill="currentColor"
562
+ />
563
+ </svg>
564
+ )
565
+ }
566
+
567
+ function RadioCardPaymentIcon() {
568
+ return (
569
+ <span
570
+ data-slot="radio-card-payment-icon"
571
+ className="flex h-[var(--bh-radio-card-payment-height)] w-[var(--bh-radio-card-payment-width)] shrink-0 items-center justify-center rounded-[var(--bh-radius-md-6)] bg-[var(--bh-bg-always-white)] shadow-[inset_0_0_0_var(--bh-border-width-default)_var(--bh-border-subtle)]"
572
+ >
573
+ <svg
574
+ aria-hidden="true"
575
+ className="h-[var(--bh-radio-card-payment-logo-height)] w-[var(--bh-radio-card-payment-logo-width)]"
576
+ fill="none"
577
+ viewBox="0 0 33 11"
578
+ >
579
+ <path
580
+ clipRule="evenodd"
581
+ d="M8.05 10.344H5.263L3.174 2.256c-.1-.372-.31-.7-.62-.856C1.78 1.011.852.701 0 .544V.233h4.489c.62 0 1.084.468 1.162 1.011l1.084 5.834L9.52.233h2.71L8.05 10.344Zm5.728 0h-2.632L13.313.233h2.632l-2.167 10.111Zm5.572-7.31c.078-.544.542-.855 1.085-.855.852-.078 1.78.078 2.554.467l.465-2.177A7.09 7.09 0 0 0 21.055 0c-2.554 0-4.413 1.401-4.413 3.344 0 1.479 1.317 2.255 2.246 2.723 1.006.467 1.393.778 1.315 1.244 0 .7-.774 1.011-1.547 1.011-.93 0-1.86-.233-2.71-.622l-.465 2.178c.93.388 1.935.545 2.864.545 2.864.076 4.644-1.323 4.644-3.423 0-2.645-3.639-2.8-3.639-3.966Zm12.85 7.31L30.11.233h-2.245c-.464 0-.929.311-1.084.778l-3.87 9.333h2.71l.541-1.477h3.329l.31 1.477H32.2ZM28.253 2.956l.773 3.811H26.86l1.394-3.811Z"
582
+ fill="var(--bh-radio-card-payment-logo-color)"
583
+ fillRule="evenodd"
584
+ />
585
+ </svg>
586
+ </span>
587
+ )
588
+ }
589
+
590
+ function RadioCardAvatarPlaceholder({ disabled }: { disabled: boolean }) {
591
+ return (
592
+ <span
593
+ data-slot="radio-card-avatar"
594
+ className={cn(
595
+ "flex size-[var(--bh-space-7xl-40)] shrink-0 items-center justify-center overflow-hidden rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-neutral-subtle)] shadow-[inset_0_0_0_var(--bh-border-width-default)_var(--bh-border-subtle)]",
596
+ disabled && "opacity-[var(--bh-opacity-disabled-media)]"
597
+ )}
598
+ >
599
+ <span className="size-full bg-[linear-gradient(135deg,var(--bh-bg-accent-blue-soft),var(--bh-bg-accent-purple-soft))]" />
600
+ </span>
601
+ )
602
+ }
603
+
604
+ function RadioCardCompanyIcon({ disabled }: { disabled: boolean }) {
605
+ return (
606
+ <span
607
+ data-slot="radio-card-company-icon"
608
+ className={cn(
609
+ "flex size-[var(--bh-space-7xl-40)] shrink-0 items-center justify-center overflow-hidden",
610
+ disabled && "opacity-[var(--bh-opacity-disabled-media)]"
611
+ )}
612
+ >
613
+ <svg aria-hidden="true" className="size-full" fill="none" viewBox="0 0 40 40">
614
+ <path
615
+ clipRule="evenodd"
616
+ d="M20.5 37.5c9.941 0 18-8.059 18-18s-8.059-18-18-18-18 8.059-18 18 8.059 18 18 18Zm2.778-22.093a3.929 3.929 0 0 0 0-5.556 3.929 3.929 0 0 0-5.556 0 3.929 3.929 0 0 0 0 5.556l2.778 2.778 2.778-2.778Zm1.315 6.871a3.929 3.929 0 0 0 5.556 0 3.929 3.929 0 0 0 0-5.556 3.929 3.929 0 0 0-5.556 0L21.815 19.5l2.778 2.778Zm-1.315 6.871a3.929 3.929 0 0 0 0-5.556L20.5 20.815l-2.778 2.778a3.929 3.929 0 0 0 0 5.556 3.929 3.929 0 0 0 5.556 0Zm-12.427-6.871a3.929 3.929 0 0 1 0-5.556 3.929 3.929 0 0 1 5.556 0L19.185 19.5l-2.778 2.778a3.929 3.929 0 0 1-5.556 0Z"
617
+ fill="var(--bh-radio-card-company-mark-color)"
618
+ fillRule="evenodd"
619
+ />
620
+ </svg>
621
+ </span>
622
+ )
623
+ }
624
+
625
+ function getCardGapClass(type: RadioCardType, rtl: boolean) {
626
+ if (rtl && (type === "company-icon" || type === "icon-radio-left")) {
627
+ return "gap-[var(--bh-space-xl-12)]"
628
+ }
629
+
630
+ return "gap-[var(--bh-space-lg-10)]"
631
+ }
632
+
633
+ function getTextGap(type: RadioCardType, state: RadioCardState, rtl: boolean) {
634
+ if (type === "with-icon" && state === "default" && !rtl) return "xxs"
635
+ if (type === "icon-radio-right" || type === "icon-radio-left") return "xxs"
636
+ return "xs"
637
+ }
638
+
639
+ const radioCardLabelClass =
640
+ "block min-w-0 break-words 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)] text-[var(--bh-content-default)]"
641
+
642
+ const radioCardDescriptionClass =
643
+ "block min-w-0 break-words text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-text-body-sm-regular-font-weight)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
644
+
645
+ const radioCardDisabledClass = "text-[var(--bh-content-disabled)]"
646
+
647
+ export {
648
+ RadioCard,
649
+ radioCardVariants,
650
+ type RadioCardDirection,
651
+ type RadioCardProps,
652
+ type RadioCardState,
653
+ type RadioCardType,
654
+ }