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,427 @@
1
+ import * as React from "react"
2
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip"
3
+ import { XIcon } from "lucide-react"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ type TooltipVariant = "dark" | "default"
9
+ type TooltipSize = "sm" | "lg"
10
+ type TooltipPointerPosition =
11
+ | "top-left"
12
+ | "top-center"
13
+ | "top-right"
14
+ | "bottom-left"
15
+ | "bottom-center"
16
+ | "bottom-right"
17
+ | "center-right"
18
+ | "center-left"
19
+ | "none"
20
+
21
+ const defaultTooltipLabel = "More information"
22
+ const defaultTooltipSupportText =
23
+ "You may notice that we've made some updates to our look and feel."
24
+ // Radix sideOffset requires numbers; these mirror the tooltip spacing aliases.
25
+ const TOOLTIP_POINTER_SIDE_OFFSET_PX = 7
26
+ const TOOLTIP_POINTERLESS_SIDE_OFFSET_PX = 6
27
+
28
+ const tooltipContentVariants = cva(
29
+ [
30
+ "z-[var(--bh-z-overlay)] overflow-visible bg-[var(--bh-tooltip-bg)] text-[var(--bh-tooltip-fg)] shadow-[var(--bh-tooltip-shadow)] outline-none",
31
+ "font-[var(--bh-font-family)] tracking-[var(--bh-text-base-letter-spacing)]",
32
+ ],
33
+ {
34
+ variants: {
35
+ variant: {
36
+ dark:
37
+ "[--bh-tooltip-bg:var(--bh-bg-always-dark)] [--bh-tooltip-border:transparent] [--bh-tooltip-close-fg:var(--bh-content-on-color)] [--bh-tooltip-fg:var(--bh-content-on-color)] [--bh-tooltip-shadow:none] [--bh-tooltip-shortcut-bg:var(--bh-bg-neutral-soft)] [--bh-tooltip-shortcut-border:var(--bh-border-inverse-subtle)]",
38
+ default:
39
+ "[--bh-tooltip-bg:var(--bh-bg-raised)] [--bh-tooltip-border:var(--bh-border-subtle)] [--bh-tooltip-close-fg:var(--bh-content-subtle)] [--bh-tooltip-fg:var(--bh-content-default)] [--bh-tooltip-shadow:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-tooltip-border),var(--shadow-component-default)] [--bh-tooltip-shortcut-bg:var(--bh-bg-neutral-subtle)] [--bh-tooltip-shortcut-border:var(--bh-border-subtle)]",
40
+ },
41
+ size: {
42
+ sm:
43
+ "inline-flex max-w-[var(--bh-tooltip-max-width)] items-center gap-[var(--bh-space-xs-4)] rounded-[var(--bh-tooltip-sm-radius)] px-[var(--bh-tooltip-sm-padding-x)] py-[var(--bh-tooltip-sm-padding-y)]",
44
+ lg:
45
+ "flex w-[var(--bh-tooltip-lg-width)] max-w-[var(--bh-tooltip-max-width)] items-start gap-[var(--bh-space-md-8)] rounded-[var(--bh-tooltip-lg-radius)] p-[var(--bh-tooltip-lg-padding)]",
46
+ },
47
+ },
48
+ defaultVariants: {
49
+ variant: "dark",
50
+ size: "sm",
51
+ },
52
+ }
53
+ )
54
+
55
+ type TooltipContentProps = Omit<
56
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>,
57
+ "asChild"
58
+ > &
59
+ VariantProps<typeof tooltipContentVariants> & {
60
+ closeLabel?: string
61
+ onCloseClick?: React.MouseEventHandler<HTMLButtonElement>
62
+ pointerPosition?: TooltipPointerPosition
63
+ shortcut?: React.ReactNode
64
+ showCloseButton?: boolean
65
+ showPointer?: boolean
66
+ showShortcut?: boolean
67
+ supportText?: React.ReactNode
68
+ }
69
+
70
+ type TooltipShortcutProps = React.ComponentProps<"span">
71
+ type TooltipCloseButtonProps = React.ComponentProps<"button"> & {
72
+ label?: string
73
+ }
74
+
75
+ const TooltipProvider = TooltipPrimitive.Provider
76
+ const Tooltip = TooltipPrimitive.Root
77
+ const TooltipRoot = TooltipPrimitive.Root
78
+ const TooltipTrigger = TooltipPrimitive.Trigger
79
+ const TooltipPortal = TooltipPrimitive.Portal
80
+
81
+ const TooltipContent = React.forwardRef<
82
+ React.ElementRef<typeof TooltipPrimitive.Content>,
83
+ TooltipContentProps
84
+ >(function TooltipContent(
85
+ {
86
+ align,
87
+ children,
88
+ className,
89
+ closeLabel,
90
+ onCloseClick,
91
+ pointerPosition = "top-left",
92
+ shortcut,
93
+ showCloseButton,
94
+ showPointer = true,
95
+ showShortcut,
96
+ side,
97
+ sideOffset,
98
+ size = "sm",
99
+ supportText,
100
+ variant = "dark",
101
+ ...props
102
+ },
103
+ ref
104
+ ) {
105
+ const resolvedPointerPosition = pointerPosition ?? "top-left"
106
+ const resolvedSize = (size ?? "sm") as TooltipSize
107
+ const resolvedVariant = (variant ?? "dark") as TooltipVariant
108
+ const placement = getTooltipPlacement(resolvedPointerPosition)
109
+ const label = children ?? defaultTooltipLabel
110
+ const renderedSupportText =
111
+ supportText ??
112
+ (children === undefined && resolvedSize === "lg"
113
+ ? defaultTooltipSupportText
114
+ : undefined)
115
+ const shouldShowCloseButton = showCloseButton ?? resolvedSize === "lg"
116
+ const shouldShowShortcut = showShortcut ?? resolvedSize === "sm"
117
+ const shouldShowPointer =
118
+ showPointer && resolvedPointerPosition !== "none"
119
+
120
+ return (
121
+ <TooltipPrimitive.Portal>
122
+ <TooltipPrimitive.Content
123
+ data-pointer-position={resolvedPointerPosition}
124
+ data-size={resolvedSize}
125
+ data-slot="tooltip-content"
126
+ data-variant={resolvedVariant}
127
+ ref={ref}
128
+ side={side ?? placement.side}
129
+ align={align ?? placement.align}
130
+ sideOffset={
131
+ sideOffset ??
132
+ (shouldShowPointer
133
+ ? TOOLTIP_POINTER_SIDE_OFFSET_PX
134
+ : TOOLTIP_POINTERLESS_SIDE_OFFSET_PX)
135
+ }
136
+ className={cn(
137
+ tooltipContentVariants({
138
+ variant: resolvedVariant,
139
+ size: resolvedSize,
140
+ }),
141
+ className
142
+ )}
143
+ {...props}
144
+ >
145
+ {resolvedSize === "lg" ? (
146
+ <TooltipLargeContent
147
+ closeLabel={closeLabel}
148
+ onCloseClick={onCloseClick}
149
+ showCloseButton={shouldShowCloseButton}
150
+ supportText={renderedSupportText}
151
+ >
152
+ {label}
153
+ </TooltipLargeContent>
154
+ ) : (
155
+ <TooltipSmallContent
156
+ shortcut={shortcut}
157
+ showShortcut={shouldShowShortcut}
158
+ >
159
+ {label}
160
+ </TooltipSmallContent>
161
+ )}
162
+
163
+ {shouldShowPointer ? (
164
+ <TooltipPointer
165
+ pointerPosition={resolvedPointerPosition}
166
+ variant={resolvedVariant}
167
+ />
168
+ ) : null}
169
+ </TooltipPrimitive.Content>
170
+ </TooltipPrimitive.Portal>
171
+ )
172
+ })
173
+
174
+ function TooltipSmallContent({
175
+ children,
176
+ shortcut,
177
+ showShortcut,
178
+ }: {
179
+ children: React.ReactNode
180
+ shortcut?: React.ReactNode
181
+ showShortcut: boolean
182
+ }) {
183
+ return (
184
+ <>
185
+ <span
186
+ data-slot="tooltip-label"
187
+ dir="auto"
188
+ className="min-w-0 whitespace-nowrap text-start text-[length:var(--bh-text-body-2xs-medium-font-size)] font-[var(--bh-text-body-2xs-medium-font-weight)] leading-[var(--bh-text-body-2xs-medium-line-height)] tracking-[var(--bh-text-body-2xs-medium-letter-spacing)]"
189
+ >
190
+ {children}
191
+ </span>
192
+ {showShortcut ? <TooltipShortcut>{shortcut ?? "/"}</TooltipShortcut> : null}
193
+ </>
194
+ )
195
+ }
196
+
197
+ function TooltipLargeContent({
198
+ children,
199
+ closeLabel,
200
+ onCloseClick,
201
+ showCloseButton,
202
+ supportText,
203
+ }: {
204
+ children: React.ReactNode
205
+ closeLabel?: string
206
+ onCloseClick?: React.MouseEventHandler<HTMLButtonElement>
207
+ showCloseButton: boolean
208
+ supportText?: React.ReactNode
209
+ }) {
210
+ return (
211
+ <>
212
+ <span
213
+ data-slot="tooltip-text"
214
+ className="flex min-w-0 flex-1 flex-col items-start gap-[var(--bh-space-xs-4)] text-start rtl:items-end rtl:text-right"
215
+ >
216
+ <span
217
+ data-slot="tooltip-label"
218
+ dir="auto"
219
+ className="w-full min-w-0 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)]"
220
+ >
221
+ {children}
222
+ </span>
223
+ {hasRenderableContent(supportText) ? (
224
+ <span
225
+ data-slot="tooltip-support-text"
226
+ dir="auto"
227
+ className="w-full min-w-0 text-[length:var(--bh-text-body-2xs-regular-font-size)] font-[var(--bh-text-body-2xs-regular-font-weight)] leading-[var(--bh-text-body-2xs-regular-line-height)] tracking-[var(--bh-text-body-2xs-regular-letter-spacing)]"
228
+ >
229
+ {supportText}
230
+ </span>
231
+ ) : null}
232
+ </span>
233
+ {showCloseButton ? (
234
+ <TooltipCloseButton label={closeLabel} onClick={onCloseClick} />
235
+ ) : null}
236
+ </>
237
+ )
238
+ }
239
+
240
+ function TooltipShortcut({
241
+ children = "/",
242
+ className,
243
+ ...props
244
+ }: TooltipShortcutProps) {
245
+ return (
246
+ <span
247
+ data-slot="tooltip-shortcut"
248
+ className={cn(
249
+ "flex h-[var(--bh-tooltip-shortcut-height)] min-w-[var(--bh-tooltip-shortcut-min-width)] shrink-0 items-center justify-center rounded-[var(--bh-radius-sm-4)] border border-[var(--bh-tooltip-shortcut-border)] bg-[var(--bh-tooltip-shortcut-bg)] px-[var(--bh-space-xs-4)] text-center text-[length:var(--bh-text-body-2xs-medium-font-size)] font-[var(--bh-text-body-2xs-medium-font-weight)] leading-[var(--bh-text-body-2xs-medium-line-height)] tracking-[var(--bh-text-body-2xs-medium-letter-spacing)]",
250
+ className
251
+ )}
252
+ {...props}
253
+ >
254
+ {children}
255
+ </span>
256
+ )
257
+ }
258
+
259
+ function TooltipCloseButton({
260
+ className,
261
+ label = "Close tooltip",
262
+ type,
263
+ ...props
264
+ }: TooltipCloseButtonProps) {
265
+ return (
266
+ <button
267
+ aria-label={label}
268
+ data-slot="tooltip-close"
269
+ type={type || "button"}
270
+ className={cn(
271
+ "inline-flex size-[var(--bh-tooltip-close-size)] shrink-0 items-center justify-center rounded-[var(--bh-radius-full)] border-0 bg-transparent p-0 text-[var(--bh-tooltip-close-fg)] outline-none transition-[background-color,box-shadow] hover:bg-[var(--bh-interactive-ghost-hover)] focus-visible:shadow-[var(--shadow-button-focus)]",
272
+ className
273
+ )}
274
+ {...props}
275
+ >
276
+ <XIcon
277
+ aria-hidden="true"
278
+ className="size-[var(--bh-tooltip-close-icon-size)]"
279
+ strokeWidth={2}
280
+ />
281
+ </button>
282
+ )
283
+ }
284
+
285
+ function TooltipPointer({
286
+ pointerPosition,
287
+ variant,
288
+ }: {
289
+ pointerPosition: TooltipPointerPosition
290
+ variant: TooltipVariant
291
+ }) {
292
+ const side = getPointerSide(pointerPosition)
293
+ if (!side) return null
294
+
295
+ return (
296
+ <span
297
+ aria-hidden="true"
298
+ data-pointer-side={side}
299
+ data-slot="tooltip-pointer"
300
+ className={cn(
301
+ "pointer-events-none absolute z-[var(--bh-z-raised)]",
302
+ pointerPlacement[pointerPosition as Exclude<TooltipPointerPosition, "none">]
303
+ )}
304
+ >
305
+ {variant === "default" ? (
306
+ <span className={cn("absolute size-[var(--bh-space-none)]", pointerBorderShape[side])} />
307
+ ) : null}
308
+ <span className={cn("absolute size-[var(--bh-space-none)]", pointerFillShape[side])} />
309
+ </span>
310
+ )
311
+ }
312
+
313
+ function getTooltipPlacement(pointerPosition: TooltipPointerPosition): {
314
+ align: React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>["align"]
315
+ side: React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>["side"]
316
+ } {
317
+ if (pointerPosition.startsWith("bottom")) {
318
+ return {
319
+ side: "top",
320
+ align: pointerPosition.endsWith("left")
321
+ ? "start"
322
+ : pointerPosition.endsWith("right")
323
+ ? "end"
324
+ : "center",
325
+ }
326
+ }
327
+
328
+ if (pointerPosition === "center-left") {
329
+ return { side: "right", align: "center" }
330
+ }
331
+
332
+ if (pointerPosition === "center-right") {
333
+ return { side: "left", align: "center" }
334
+ }
335
+
336
+ if (pointerPosition === "none") {
337
+ return { side: "top", align: "center" }
338
+ }
339
+
340
+ return {
341
+ side: "bottom",
342
+ align: pointerPosition.endsWith("left")
343
+ ? "start"
344
+ : pointerPosition.endsWith("right")
345
+ ? "end"
346
+ : "center",
347
+ }
348
+ }
349
+
350
+ function getPointerSide(pointerPosition: TooltipPointerPosition) {
351
+ if (pointerPosition.startsWith("top")) return "top"
352
+ if (pointerPosition.startsWith("bottom")) return "bottom"
353
+ if (pointerPosition === "center-left") return "left"
354
+ if (pointerPosition === "center-right") return "right"
355
+
356
+ return null
357
+ }
358
+
359
+ function hasRenderableContent(content: React.ReactNode) {
360
+ return (
361
+ content !== undefined &&
362
+ content !== null &&
363
+ content !== false &&
364
+ content !== ""
365
+ )
366
+ }
367
+
368
+ const pointerPlacement: Record<Exclude<TooltipPointerPosition, "none">, string> = {
369
+ "top-left":
370
+ "top-[var(--bh-tooltip-pointer-offset)] h-[var(--bh-tooltip-pointer-depth)] w-[var(--bh-tooltip-pointer-width)] [inset-inline-start:var(--bh-tooltip-pointer-inline-offset)]",
371
+ "top-center":
372
+ "top-[var(--bh-tooltip-pointer-offset)] left-1/2 h-[var(--bh-tooltip-pointer-depth)] w-[var(--bh-tooltip-pointer-width)] -translate-x-1/2 rtl:translate-x-1/2",
373
+ "top-right":
374
+ "top-[var(--bh-tooltip-pointer-offset)] h-[var(--bh-tooltip-pointer-depth)] w-[var(--bh-tooltip-pointer-width)] [inset-inline-end:var(--bh-tooltip-pointer-inline-offset)]",
375
+ "bottom-left":
376
+ "bottom-[var(--bh-tooltip-pointer-offset)] h-[var(--bh-tooltip-pointer-depth)] w-[var(--bh-tooltip-pointer-width)] [inset-inline-start:var(--bh-tooltip-pointer-inline-offset)]",
377
+ "bottom-center":
378
+ "bottom-[var(--bh-tooltip-pointer-offset)] left-1/2 h-[var(--bh-tooltip-pointer-depth)] w-[var(--bh-tooltip-pointer-width)] -translate-x-1/2 rtl:translate-x-1/2",
379
+ "bottom-right":
380
+ "bottom-[var(--bh-tooltip-pointer-offset)] h-[var(--bh-tooltip-pointer-depth)] w-[var(--bh-tooltip-pointer-width)] [inset-inline-end:var(--bh-tooltip-pointer-inline-offset)]",
381
+ "center-right":
382
+ "right-[var(--bh-tooltip-pointer-offset)] top-1/2 h-[var(--bh-tooltip-pointer-width)] w-[var(--bh-tooltip-pointer-depth)] -translate-y-1/2",
383
+ "center-left":
384
+ "left-[var(--bh-tooltip-pointer-offset)] top-1/2 h-[var(--bh-tooltip-pointer-width)] w-[var(--bh-tooltip-pointer-depth)] -translate-y-1/2",
385
+ }
386
+
387
+ const pointerFillShape = {
388
+ top:
389
+ "left-0 top-0 border-x-[length:var(--bh-tooltip-pointer-half-width)] border-b-[length:var(--bh-tooltip-pointer-depth)] border-x-[color:transparent] border-b-[color:var(--bh-tooltip-bg)]",
390
+ bottom:
391
+ "bottom-0 left-0 border-x-[length:var(--bh-tooltip-pointer-half-width)] border-t-[length:var(--bh-tooltip-pointer-depth)] border-x-[color:transparent] border-t-[color:var(--bh-tooltip-bg)]",
392
+ right:
393
+ "left-0 top-0 border-y-[length:var(--bh-tooltip-pointer-half-width)] border-l-[length:var(--bh-tooltip-pointer-depth)] border-y-[color:transparent] border-l-[color:var(--bh-tooltip-bg)]",
394
+ left:
395
+ "right-0 top-0 border-y-[length:var(--bh-tooltip-pointer-half-width)] border-r-[length:var(--bh-tooltip-pointer-depth)] border-y-[color:transparent] border-r-[color:var(--bh-tooltip-bg)]",
396
+ } as const
397
+
398
+ const pointerBorderShape = {
399
+ top:
400
+ "left-[var(--bh-tooltip-pointer-border-offset)] top-[var(--bh-tooltip-pointer-border-offset)] border-x-[length:var(--bh-tooltip-pointer-border-half-width)] border-b-[length:var(--bh-tooltip-pointer-border-depth)] border-x-[color:transparent] border-b-[color:var(--bh-tooltip-border)]",
401
+ bottom:
402
+ "bottom-[var(--bh-tooltip-pointer-border-offset)] left-[var(--bh-tooltip-pointer-border-offset)] border-x-[length:var(--bh-tooltip-pointer-border-half-width)] border-t-[length:var(--bh-tooltip-pointer-border-depth)] border-x-[color:transparent] border-t-[color:var(--bh-tooltip-border)]",
403
+ right:
404
+ "top-[var(--bh-tooltip-pointer-border-offset)] left-0 border-y-[length:var(--bh-tooltip-pointer-border-half-width)] border-l-[length:var(--bh-tooltip-pointer-border-depth)] border-y-[color:transparent] border-l-[color:var(--bh-tooltip-border)]",
405
+ left:
406
+ "top-[var(--bh-tooltip-pointer-border-offset)] right-0 border-y-[length:var(--bh-tooltip-pointer-border-half-width)] border-r-[length:var(--bh-tooltip-pointer-border-depth)] border-y-[color:transparent] border-r-[color:var(--bh-tooltip-border)]",
407
+ } as const
408
+
409
+ export {
410
+ Tooltip,
411
+ TooltipCloseButton,
412
+ TooltipContent,
413
+ TooltipPortal,
414
+ TooltipProvider,
415
+ TooltipRoot,
416
+ TooltipShortcut,
417
+ TooltipTrigger,
418
+ tooltipContentVariants,
419
+ }
420
+ export type {
421
+ TooltipCloseButtonProps,
422
+ TooltipContentProps,
423
+ TooltipPointerPosition,
424
+ TooltipShortcutProps,
425
+ TooltipSize,
426
+ TooltipVariant,
427
+ }
@@ -0,0 +1,34 @@
1
+ import {
2
+ Accordion,
3
+ AccordionContent,
4
+ AccordionItem,
5
+ AccordionTrigger,
6
+ } from "@/components/ui/accordion"
7
+
8
+ export function AccordionDemo() {
9
+ return (
10
+ <Accordion type="single" collapsible defaultValue="support" variant="bordered">
11
+ <AccordionItem value="support">
12
+ <AccordionTrigger>What support channels are included?</AccordionTrigger>
13
+ <AccordionContent>
14
+ Email, chat, and priority escalation are available. Critical production
15
+ issues receive the fastest response window.
16
+ </AccordionContent>
17
+ </AccordionItem>
18
+ <AccordionItem value="billing">
19
+ <AccordionTrigger>Can billing details be changed?</AccordionTrigger>
20
+ <AccordionContent>
21
+ Yes. Admins can update billing profiles, tax details, and renewal
22
+ preferences from the account settings area.
23
+ </AccordionContent>
24
+ </AccordionItem>
25
+ <AccordionItem value="arabic">
26
+ <AccordionTrigger>هل يدعم المكون العربية؟</AccordionTrigger>
27
+ <AccordionContent>
28
+ نعم. يرث المكون اتجاه الصفحة، وتستخدم المسافات محاور منطقية لتعمل
29
+ بشكل صحيح في الواجهات العربية والإنجليزية.
30
+ </AccordionContent>
31
+ </AccordionItem>
32
+ </Accordion>
33
+ )
34
+ }
@@ -0,0 +1,14 @@
1
+ import { Alert } from "@/components/ui/alert"
2
+
3
+ export function AlertDemo() {
4
+ return (
5
+ <div className="grid gap-4">
6
+ <Alert status="danger" />
7
+ <Alert status="success" />
8
+ <Alert isExpandable status="warning" />
9
+ <div dir="rtl">
10
+ <Alert dir="rtl" isExpandable status="info" />
11
+ </div>
12
+ </div>
13
+ )
14
+ }
@@ -0,0 +1,65 @@
1
+ import { CopyIcon, ExternalLinkIcon, PaperclipIcon } from "lucide-react"
2
+
3
+ import {
4
+ AttributeCard,
5
+ AttributeHeader,
6
+ AttributeItem,
7
+ AttributeList,
8
+ } from "@/components/ui/attribute"
9
+ import { Badge } from "@/components/ui/badge"
10
+ import { Button } from "@/components/ui/button"
11
+
12
+ export function AttributeDemo() {
13
+ return (
14
+ <div className="grid max-w-5xl gap-4 lg:grid-cols-2">
15
+ <AttributeCard>
16
+ <AttributeHeader
17
+ title="Company profile"
18
+ description="Key-value information in a scannable card."
19
+ />
20
+ <AttributeList>
21
+ <AttributeItem label="Name">Banhaten</AttributeItem>
22
+ <AttributeItem label="Location">Remote</AttributeItem>
23
+ <AttributeItem label="Website">
24
+ <a
25
+ className="inline-flex items-center gap-[var(--bh-space-xxs-2)] text-[var(--bh-content-link)] hover:text-[var(--bh-content-link-hover)]"
26
+ href="https://banhaten.com"
27
+ >
28
+ banhaten.com
29
+ <ExternalLinkIcon className="size-[var(--bh-space-3xl-16)]" />
30
+ </a>
31
+ </AttributeItem>
32
+ <AttributeItem label="Industry">
33
+ <Badge>Design</Badge>
34
+ </AttributeItem>
35
+ </AttributeList>
36
+ </AttributeCard>
37
+
38
+ <AttributeCard>
39
+ <AttributeHeader
40
+ title="Order details"
41
+ description="End-aligned values, copy actions, files, and status chips."
42
+ />
43
+ <AttributeList layout="end" dividers>
44
+ <AttributeItem label="Subscription">
45
+ <Badge color="green" type="dot">
46
+ Active
47
+ </Badge>
48
+ </AttributeItem>
49
+ <AttributeItem label="API key" truncate>
50
+ sk_live_************1234
51
+ <Button aria-label="Copy API key" size="icon-xs" variant="outline">
52
+ <CopyIcon />
53
+ </Button>
54
+ </AttributeItem>
55
+ <AttributeItem label="Invoice">
56
+ <span className="inline-flex min-w-0 items-center gap-[var(--bh-space-md-8)]">
57
+ <PaperclipIcon className="size-[var(--bh-space-4xl-20)] shrink-0 text-[var(--bh-content-muted)]" />
58
+ INV-2024-00123.pdf
59
+ </span>
60
+ </AttributeItem>
61
+ </AttributeList>
62
+ </AttributeCard>
63
+ </div>
64
+ )
65
+ }
@@ -0,0 +1,74 @@
1
+ import { PlusIcon } from "lucide-react"
2
+
3
+ import {
4
+ Avatar,
5
+ AvatarBadge,
6
+ AvatarFallback,
7
+ AvatarGroup,
8
+ AvatarGroupCount,
9
+ AvatarImage,
10
+ } from "@/components/ui/avatar"
11
+
12
+ const avatarUrl = new URL("../assets/avatars/avatar-01.jpg", import.meta.url).href
13
+
14
+ export function AvatarDemo() {
15
+ return (
16
+ <div className="flex flex-col gap-6">
17
+ <div className="flex flex-wrap items-center gap-4">
18
+ <Avatar>
19
+ <AvatarImage src={avatarUrl} alt="Profile portrait" />
20
+ <AvatarFallback>CN</AvatarFallback>
21
+ </Avatar>
22
+ <Avatar>
23
+ <AvatarFallback>ER</AvatarFallback>
24
+ </Avatar>
25
+ <Avatar>
26
+ <AvatarImage src="/missing-avatar.png" alt="Missing image" />
27
+ <AvatarFallback>LR</AvatarFallback>
28
+ </Avatar>
29
+ </div>
30
+
31
+ <div className="flex flex-wrap items-center gap-4">
32
+ <Avatar>
33
+ <AvatarImage src={avatarUrl} alt="Profile portrait" />
34
+ <AvatarFallback>CN</AvatarFallback>
35
+ <AvatarBadge />
36
+ </Avatar>
37
+ <Avatar>
38
+ <AvatarFallback>PP</AvatarFallback>
39
+ <AvatarBadge className="size-5 bg-success">
40
+ <PlusIcon className="size-3" />
41
+ </AvatarBadge>
42
+ </Avatar>
43
+ </div>
44
+
45
+ <AvatarGroup>
46
+ <Avatar>
47
+ <AvatarFallback>CN</AvatarFallback>
48
+ </Avatar>
49
+ <Avatar>
50
+ <AvatarFallback>LR</AvatarFallback>
51
+ </Avatar>
52
+ <Avatar>
53
+ <AvatarFallback>ER</AvatarFallback>
54
+ </Avatar>
55
+ <AvatarGroupCount>+3</AvatarGroupCount>
56
+ </AvatarGroup>
57
+
58
+ <div dir="rtl">
59
+ <AvatarGroup>
60
+ <Avatar>
61
+ <AvatarFallback>ح</AvatarFallback>
62
+ </Avatar>
63
+ <Avatar>
64
+ <AvatarFallback>ن</AvatarFallback>
65
+ </Avatar>
66
+ <Avatar>
67
+ <AvatarFallback>م</AvatarFallback>
68
+ </Avatar>
69
+ <AvatarGroupCount>+٣</AvatarGroupCount>
70
+ </AvatarGroup>
71
+ </div>
72
+ </div>
73
+ )
74
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ ArrowUpRightIcon,
3
+ BadgeCheckIcon,
4
+ BookmarkIcon,
5
+ } from "lucide-react"
6
+
7
+ import { Badge } from "@/components/ui/badge"
8
+ import { Spinner } from "@/components/ui/spinner"
9
+
10
+ export function BadgeDemo() {
11
+ return (
12
+ <div className="flex flex-wrap items-center gap-2">
13
+ <Badge>Badge</Badge>
14
+ <Badge badgeStyle="outline">Outline</Badge>
15
+ <Badge badgeStyle="solid" color="blue">
16
+ Blue
17
+ </Badge>
18
+ <Badge badgeStyle="solid" color="green">
19
+ Approved
20
+ </Badge>
21
+ <Badge badgeStyle="solid" color="amber">
22
+ Warning
23
+ </Badge>
24
+ <Badge badgeStyle="solid" color="danger">
25
+ Danger
26
+ </Badge>
27
+ <Badge color="purple" type="dot">
28
+ Live
29
+ </Badge>
30
+ <Badge color="rose" showNumber>
31
+ 2
32
+ </Badge>
33
+ <Badge color="green" type="leading-icon">
34
+ <BadgeCheckIcon data-icon="inline-start" />
35
+ Verified
36
+ </Badge>
37
+ <Badge badgeStyle="outline" color="neutral" type="trailing-icon">
38
+ Bookmark
39
+ <BookmarkIcon data-icon="inline-end" />
40
+ </Badge>
41
+ <Badge color="sky" type="leading-icon">
42
+ <Spinner data-icon="inline-start" />
43
+ Syncing
44
+ </Badge>
45
+ <Badge asChild badgeStyle="outline" color="blue" type="trailing-icon">
46
+ <a href="#badge">
47
+ Open
48
+ <ArrowUpRightIcon data-icon="inline-end" data-rtl-flip="true" />
49
+ </a>
50
+ </Badge>
51
+ </div>
52
+ )
53
+ }