@borisj74/bv-ds 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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/dist/index.cjs +33885 -0
  4. package/dist/index.d.cts +2715 -0
  5. package/dist/index.d.ts +2715 -0
  6. package/dist/index.js +33717 -0
  7. package/package.json +67 -0
  8. package/src/components/ActivityFeed/ActivityFeed.tsx +48 -0
  9. package/src/components/ActivityFeed/index.ts +2 -0
  10. package/src/components/ActivityGauge/ActivityGauge.tsx +155 -0
  11. package/src/components/ActivityGauge/index.ts +7 -0
  12. package/src/components/AdvancedFilterBar/AdvancedFilterBar.tsx +80 -0
  13. package/src/components/AdvancedFilterBar/index.ts +2 -0
  14. package/src/components/Alert/Alert.tsx +210 -0
  15. package/src/components/Alert/index.ts +2 -0
  16. package/src/components/Avatar/Avatar.tsx +111 -0
  17. package/src/components/Avatar/index.ts +2 -0
  18. package/src/components/AvatarAddButton/AvatarAddButton.tsx +65 -0
  19. package/src/components/AvatarAddButton/index.ts +5 -0
  20. package/src/components/AvatarGroup/AvatarGroup.tsx +79 -0
  21. package/src/components/AvatarGroup/index.ts +6 -0
  22. package/src/components/AvatarLabelGroup/AvatarLabelGroup.tsx +62 -0
  23. package/src/components/AvatarLabelGroup/index.ts +5 -0
  24. package/src/components/AvatarProfilePhoto/AvatarProfilePhoto.tsx +117 -0
  25. package/src/components/AvatarProfilePhoto/index.ts +5 -0
  26. package/src/components/Badge/ColorBadge.tsx +36 -0
  27. package/src/components/Badge/ModernBadge.tsx +38 -0
  28. package/src/components/Badge/PillBadge.tsx +36 -0
  29. package/src/components/Badge/badgeShared.tsx +139 -0
  30. package/src/components/Badge/index.ts +7 -0
  31. package/src/components/BadgeCloseX/BadgeCloseX.tsx +64 -0
  32. package/src/components/BadgeCloseX/index.ts +2 -0
  33. package/src/components/BadgeGroup/BadgeGroup.tsx +61 -0
  34. package/src/components/BadgeGroup/index.ts +7 -0
  35. package/src/components/BreadcrumbButtonBase/BreadcrumbButtonBase.tsx +75 -0
  36. package/src/components/BreadcrumbButtonBase/index.ts +5 -0
  37. package/src/components/Breadcrumbs/Breadcrumbs.tsx +62 -0
  38. package/src/components/Breadcrumbs/index.ts +2 -0
  39. package/src/components/Button/Button.tsx +71 -0
  40. package/src/components/Button/index.ts +2 -0
  41. package/src/components/ButtonCloseX/ButtonCloseX.tsx +54 -0
  42. package/src/components/ButtonCloseX/index.ts +2 -0
  43. package/src/components/ButtonDestructive/ButtonDestructive.tsx +67 -0
  44. package/src/components/ButtonDestructive/index.ts +6 -0
  45. package/src/components/ButtonGroup/ButtonGroup.tsx +28 -0
  46. package/src/components/ButtonGroup/index.ts +2 -0
  47. package/src/components/ButtonGroupSegment/ButtonGroupSegment.tsx +54 -0
  48. package/src/components/ButtonGroupSegment/index.ts +5 -0
  49. package/src/components/ButtonUtility/ButtonUtility.tsx +67 -0
  50. package/src/components/ButtonUtility/index.ts +6 -0
  51. package/src/components/CalendarCell/CalendarCell.tsx +82 -0
  52. package/src/components/CalendarCell/index.ts +2 -0
  53. package/src/components/CalendarCellDayWeekView/CalendarCellDayWeekView.tsx +56 -0
  54. package/src/components/CalendarCellDayWeekView/index.ts +2 -0
  55. package/src/components/CalendarColumnHeader/CalendarColumnHeader.tsx +45 -0
  56. package/src/components/CalendarColumnHeader/index.ts +5 -0
  57. package/src/components/CalendarDateIcon/CalendarDateIcon.tsx +25 -0
  58. package/src/components/CalendarDateIcon/index.ts +2 -0
  59. package/src/components/CalendarEvent/CalendarEvent.tsx +76 -0
  60. package/src/components/CalendarEvent/index.ts +2 -0
  61. package/src/components/CalendarEventDayWeekView/CalendarEventDayWeekView.tsx +76 -0
  62. package/src/components/CalendarEventDayWeekView/index.ts +2 -0
  63. package/src/components/CalendarHeader/CalendarHeader.tsx +47 -0
  64. package/src/components/CalendarHeader/index.ts +2 -0
  65. package/src/components/CalendarRowLabel/CalendarRowLabel.tsx +21 -0
  66. package/src/components/CalendarRowLabel/index.ts +2 -0
  67. package/src/components/CalendarTimemarker/CalendarTimemarker.tsx +46 -0
  68. package/src/components/CalendarTimemarker/index.ts +5 -0
  69. package/src/components/CalendarViewDropdown/CalendarViewDropdown.tsx +101 -0
  70. package/src/components/CalendarViewDropdown/index.ts +6 -0
  71. package/src/components/CardHeader/CardHeader.tsx +57 -0
  72. package/src/components/CardHeader/index.ts +2 -0
  73. package/src/components/CarouselArrow/CarouselArrow.tsx +47 -0
  74. package/src/components/CarouselArrow/index.ts +6 -0
  75. package/src/components/CarouselImage/CarouselImage.tsx +60 -0
  76. package/src/components/CarouselImage/index.ts +2 -0
  77. package/src/components/Change/Change.tsx +73 -0
  78. package/src/components/Change/index.ts +2 -0
  79. package/src/components/ChartLegend/ChartLegend.tsx +38 -0
  80. package/src/components/ChartLegend/index.ts +2 -0
  81. package/src/components/ChartMarker/ChartMarker.tsx +54 -0
  82. package/src/components/ChartMarker/index.ts +2 -0
  83. package/src/components/ChartMini/ChartMini.tsx +86 -0
  84. package/src/components/ChartMini/index.ts +2 -0
  85. package/src/components/ChartTooltip/ChartTooltip.tsx +44 -0
  86. package/src/components/ChartTooltip/index.ts +2 -0
  87. package/src/components/Checkbox/Checkbox.tsx +65 -0
  88. package/src/components/Checkbox/checkboxBase.tsx +81 -0
  89. package/src/components/Checkbox/index.ts +3 -0
  90. package/src/components/CodeSnippet/CodeSnippet.tsx +94 -0
  91. package/src/components/CodeSnippet/index.ts +2 -0
  92. package/src/components/CodeSnippetTabs/CodeSnippetTabs.tsx +44 -0
  93. package/src/components/CodeSnippetTabs/index.ts +2 -0
  94. package/src/components/CommandBar/CommandBar.tsx +80 -0
  95. package/src/components/CommandBar/index.ts +2 -0
  96. package/src/components/CommandBarFooter/CommandBarFooter.tsx +125 -0
  97. package/src/components/CommandBarFooter/index.ts +5 -0
  98. package/src/components/CommandBarMenuSection/CommandBarMenuSection.tsx +28 -0
  99. package/src/components/CommandBarMenuSection/index.ts +2 -0
  100. package/src/components/CommandBarNavigationIcon/CommandBarNavigationIcon.tsx +47 -0
  101. package/src/components/CommandBarNavigationIcon/index.ts +2 -0
  102. package/src/components/CommandDropdownMenuItem/CommandDropdownMenuItem.tsx +51 -0
  103. package/src/components/CommandDropdownMenuItem/index.ts +2 -0
  104. package/src/components/CommandInput/CommandInput.tsx +74 -0
  105. package/src/components/CommandInput/index.ts +2 -0
  106. package/src/components/CommandShortcut/CommandShortcut.tsx +26 -0
  107. package/src/components/CommandShortcut/index.ts +2 -0
  108. package/src/components/ContentDivider/ContentDivider.tsx +80 -0
  109. package/src/components/ContentDivider/index.ts +6 -0
  110. package/src/components/ContentFeatureText/ContentFeatureText.tsx +60 -0
  111. package/src/components/ContentFeatureText/index.ts +5 -0
  112. package/src/components/ContentHeading/ContentHeading.tsx +43 -0
  113. package/src/components/ContentHeading/index.ts +5 -0
  114. package/src/components/ContentParagraph/ContentParagraph.tsx +39 -0
  115. package/src/components/ContentParagraph/index.ts +5 -0
  116. package/src/components/ContentQuote/ContentQuote.tsx +114 -0
  117. package/src/components/ContentQuote/index.ts +6 -0
  118. package/src/components/ContentRule/ContentRule.tsx +31 -0
  119. package/src/components/ContentRule/index.ts +2 -0
  120. package/src/components/ContextMenu/ContextMenu.tsx +67 -0
  121. package/src/components/ContextMenu/index.ts +7 -0
  122. package/src/components/ContextMenu/useContextMenu.ts +41 -0
  123. package/src/components/DatePickerCell/DatePickerCell.tsx +77 -0
  124. package/src/components/DatePickerCell/index.ts +2 -0
  125. package/src/components/DatePickerListItem/DatePickerListItem.tsx +39 -0
  126. package/src/components/DatePickerListItem/index.ts +2 -0
  127. package/src/components/DatePickerMenu/DatePickerMenu.tsx +131 -0
  128. package/src/components/DatePickerMenu/index.ts +2 -0
  129. package/src/components/DropdownAccountListItem/DropdownAccountListItem.tsx +69 -0
  130. package/src/components/DropdownAccountListItem/index.ts +2 -0
  131. package/src/components/DropdownMenuFooter/DropdownMenuFooter.tsx +50 -0
  132. package/src/components/DropdownMenuFooter/index.ts +5 -0
  133. package/src/components/DropdownMenuHeader/DropdownMenuHeader.tsx +93 -0
  134. package/src/components/DropdownMenuHeader/index.ts +5 -0
  135. package/src/components/DropdownMenuItemInsetIcon/DropdownMenuItemInsetIcon.tsx +89 -0
  136. package/src/components/DropdownMenuItemInsetIcon/index.ts +5 -0
  137. package/src/components/DropdownMenuListItem/DropdownMenuListItem.tsx +84 -0
  138. package/src/components/DropdownMenuListItem/index.ts +2 -0
  139. package/src/components/EmptyState/EmptyState.tsx +65 -0
  140. package/src/components/EmptyState/index.ts +2 -0
  141. package/src/components/FeedItemBase/FeedItemBase.tsx +135 -0
  142. package/src/components/FeedItemBase/index.ts +2 -0
  143. package/src/components/FileUpload/FileUpload.tsx +112 -0
  144. package/src/components/FileUpload/index.ts +2 -0
  145. package/src/components/FileUploadBase/FileUploadBase.tsx +69 -0
  146. package/src/components/FileUploadBase/index.ts +2 -0
  147. package/src/components/FileUploadItemBase/FileUploadItemBase.tsx +190 -0
  148. package/src/components/FileUploadItemBase/index.ts +7 -0
  149. package/src/components/FilterBar/FilterBar.tsx +62 -0
  150. package/src/components/FilterBar/index.ts +2 -0
  151. package/src/components/FilterTabs/FilterTabs.tsx +41 -0
  152. package/src/components/FilterTabs/index.ts +2 -0
  153. package/src/components/FiltersDropdownMenu/FiltersDropdownMenu.tsx +104 -0
  154. package/src/components/FiltersDropdownMenu/index.ts +2 -0
  155. package/src/components/FiltersSlideoutMenu/FiltersSlideoutMenu.tsx +71 -0
  156. package/src/components/FiltersSlideoutMenu/index.ts +2 -0
  157. package/src/components/HeaderNavigation/HeaderNavigation.tsx +178 -0
  158. package/src/components/HeaderNavigation/index.ts +6 -0
  159. package/src/components/HelpIcon/HelpIcon.tsx +49 -0
  160. package/src/components/HelpIcon/index.ts +2 -0
  161. package/src/components/InputField/InputField.tsx +108 -0
  162. package/src/components/InputField/index.ts +3 -0
  163. package/src/components/InputField/inputFieldShared.tsx +68 -0
  164. package/src/components/LeadingInputField/LeadingInputField.tsx +60 -0
  165. package/src/components/LeadingInputField/index.ts +2 -0
  166. package/src/components/LineAndBarChart/LineAndBarChart.tsx +96 -0
  167. package/src/components/LineAndBarChart/index.ts +2 -0
  168. package/src/components/LinkMessage/LinkMessage.tsx +52 -0
  169. package/src/components/LinkMessage/index.ts +2 -0
  170. package/src/components/LoadingIndicator/LoadingIndicator.tsx +108 -0
  171. package/src/components/LoadingIndicator/index.ts +6 -0
  172. package/src/components/MediaMessage/MediaMessage.tsx +109 -0
  173. package/src/components/MediaMessage/index.ts +2 -0
  174. package/src/components/MegaInputFieldBase/MegaInputFieldBase.tsx +49 -0
  175. package/src/components/MegaInputFieldBase/index.ts +5 -0
  176. package/src/components/Message/Message.tsx +85 -0
  177. package/src/components/Message/index.ts +3 -0
  178. package/src/components/Message/messageShared.tsx +73 -0
  179. package/src/components/MessageAction/MessageAction.tsx +221 -0
  180. package/src/components/MessageAction/index.ts +2 -0
  181. package/src/components/MessageActionButton/MessageActionButton.tsx +36 -0
  182. package/src/components/MessageActionButton/index.ts +2 -0
  183. package/src/components/MessageActionPanel/MessageActionPanel.tsx +36 -0
  184. package/src/components/MessageActionPanel/index.ts +2 -0
  185. package/src/components/MessageReaction/MessageReaction.tsx +37 -0
  186. package/src/components/MessageReaction/index.ts +2 -0
  187. package/src/components/MessageStatusIcon/MessageStatusIcon.tsx +54 -0
  188. package/src/components/MessageStatusIcon/index.ts +2 -0
  189. package/src/components/MetricItem/MetricItem.tsx +147 -0
  190. package/src/components/MetricItem/index.ts +2 -0
  191. package/src/components/ModalActions/ModalActions.tsx +57 -0
  192. package/src/components/ModalActions/index.ts +2 -0
  193. package/src/components/ModalHeader/ModalHeader.tsx +99 -0
  194. package/src/components/ModalHeader/index.ts +2 -0
  195. package/src/components/MultiSelect/MultiSelect.tsx +118 -0
  196. package/src/components/MultiSelect/index.ts +2 -0
  197. package/src/components/NavAccountCard/NavAccountCard.tsx +124 -0
  198. package/src/components/NavAccountCard/index.ts +2 -0
  199. package/src/components/NavAccountCardMenuItem/NavAccountCardMenuItem.tsx +101 -0
  200. package/src/components/NavAccountCardMenuItem/index.ts +5 -0
  201. package/src/components/NavButton/NavButton.tsx +50 -0
  202. package/src/components/NavButton/index.ts +2 -0
  203. package/src/components/NavFeaturedCard/NavFeaturedCard.tsx +82 -0
  204. package/src/components/NavFeaturedCard/index.ts +2 -0
  205. package/src/components/NavItemBase/NavItemBase.tsx +79 -0
  206. package/src/components/NavItemBase/index.ts +2 -0
  207. package/src/components/NavItemDropdownBase/NavItemDropdownBase.tsx +74 -0
  208. package/src/components/NavItemDropdownBase/index.ts +2 -0
  209. package/src/components/NavMenuButton/NavMenuButton.tsx +47 -0
  210. package/src/components/NavMenuButton/index.ts +2 -0
  211. package/src/components/Notification/Notification.tsx +102 -0
  212. package/src/components/Notification/index.ts +2 -0
  213. package/src/components/NumberInput/NumberInput.tsx +114 -0
  214. package/src/components/NumberInput/index.ts +2 -0
  215. package/src/components/PageHeader/PageHeader.tsx +88 -0
  216. package/src/components/PageHeader/index.ts +2 -0
  217. package/src/components/Pagination/Pagination.tsx +124 -0
  218. package/src/components/Pagination/index.ts +2 -0
  219. package/src/components/PaginationButtonGroupBase/PaginationButtonGroupBase.tsx +69 -0
  220. package/src/components/PaginationButtonGroupBase/index.ts +5 -0
  221. package/src/components/PaginationCards/PaginationCards.tsx +72 -0
  222. package/src/components/PaginationCards/index.ts +2 -0
  223. package/src/components/PaginationDotGroup/PaginationDotGroup.tsx +66 -0
  224. package/src/components/PaginationDotGroup/index.ts +2 -0
  225. package/src/components/PaginationDotIndicator/PaginationDotIndicator.tsx +39 -0
  226. package/src/components/PaginationDotIndicator/index.ts +6 -0
  227. package/src/components/PaginationNumberBase/PaginationNumberBase.tsx +42 -0
  228. package/src/components/PaginationNumberBase/index.ts +5 -0
  229. package/src/components/PieChart/PieChart.tsx +73 -0
  230. package/src/components/PieChart/index.ts +2 -0
  231. package/src/components/ProgressBar/ProgressBar.tsx +75 -0
  232. package/src/components/ProgressBar/index.ts +2 -0
  233. package/src/components/ProgressCircle/ProgressCircle.tsx +89 -0
  234. package/src/components/ProgressCircle/index.ts +6 -0
  235. package/src/components/RadarChart/RadarChart.tsx +62 -0
  236. package/src/components/RadarChart/index.ts +2 -0
  237. package/src/components/Radio/Radio.tsx +55 -0
  238. package/src/components/Radio/index.ts +2 -0
  239. package/src/components/RadioGroup/RadioGroup.tsx +54 -0
  240. package/src/components/RadioGroup/index.ts +2 -0
  241. package/src/components/RadioGroupItem/RadioGroupItem.tsx +118 -0
  242. package/src/components/RadioGroupItem/index.ts +6 -0
  243. package/src/components/SectionFooter/SectionFooter.tsx +40 -0
  244. package/src/components/SectionFooter/index.ts +2 -0
  245. package/src/components/SectionHeader/SectionHeader.tsx +44 -0
  246. package/src/components/SectionHeader/index.ts +2 -0
  247. package/src/components/SectionLabel/SectionLabel.tsx +51 -0
  248. package/src/components/SectionLabel/index.ts +2 -0
  249. package/src/components/Select/Select.tsx +121 -0
  250. package/src/components/Select/index.ts +2 -0
  251. package/src/components/SelectMenuItem/SelectMenuItem.tsx +85 -0
  252. package/src/components/SelectMenuItem/index.ts +2 -0
  253. package/src/components/SidebarNavigation/SidebarNavigation.tsx +100 -0
  254. package/src/components/SidebarNavigation/index.ts +2 -0
  255. package/src/components/SlideOutMenuHeader/SlideOutMenuHeader.tsx +56 -0
  256. package/src/components/SlideOutMenuHeader/index.ts +2 -0
  257. package/src/components/Slider/Slider.tsx +125 -0
  258. package/src/components/Slider/index.ts +2 -0
  259. package/src/components/SocialButton/SocialButton.tsx +88 -0
  260. package/src/components/SocialButton/index.ts +2 -0
  261. package/src/components/StatusIcon/StatusIcon.tsx +75 -0
  262. package/src/components/StatusIcon/index.ts +2 -0
  263. package/src/components/StepBase/StepBase.tsx +90 -0
  264. package/src/components/StepBase/index.ts +2 -0
  265. package/src/components/StepIconBase/StepIconBase.tsx +65 -0
  266. package/src/components/StepIconBase/index.ts +7 -0
  267. package/src/components/TabButtonBase/TabButtonBase.tsx +88 -0
  268. package/src/components/TabButtonBase/index.ts +2 -0
  269. package/src/components/TableCell/TableCell.tsx +44 -0
  270. package/src/components/TableCell/index.ts +2 -0
  271. package/src/components/TableHeaderCell/TableHeaderCell.tsx +34 -0
  272. package/src/components/TableHeaderCell/index.ts +2 -0
  273. package/src/components/TableHeaderLabel/TableHeaderLabel.tsx +37 -0
  274. package/src/components/TableHeaderLabel/index.ts +2 -0
  275. package/src/components/Tabs/Tabs.tsx +80 -0
  276. package/src/components/Tabs/index.ts +2 -0
  277. package/src/components/Tag/Tag.tsx +91 -0
  278. package/src/components/Tag/index.ts +2 -0
  279. package/src/components/TagsInputField/TagsInputField.tsx +90 -0
  280. package/src/components/TagsInputField/index.ts +2 -0
  281. package/src/components/TextEditorToolbar/TextEditorToolbar.tsx +33 -0
  282. package/src/components/TextEditorToolbar/index.ts +2 -0
  283. package/src/components/TextEditorTooltip/TextEditorTooltip.tsx +28 -0
  284. package/src/components/TextEditorTooltip/index.ts +2 -0
  285. package/src/components/TextareaInputField/TextareaInputField.tsx +45 -0
  286. package/src/components/TextareaInputField/index.ts +2 -0
  287. package/src/components/Toggle/Toggle.tsx +87 -0
  288. package/src/components/Toggle/index.ts +2 -0
  289. package/src/components/Tooltip/Tooltip.tsx +59 -0
  290. package/src/components/Tooltip/index.ts +2 -0
  291. package/src/components/TrailingInputField/TrailingInputField.tsx +62 -0
  292. package/src/components/TrailingInputField/index.ts +2 -0
  293. package/src/components/TreeView/TreeView.tsx +86 -0
  294. package/src/components/TreeView/index.ts +2 -0
  295. package/src/components/TreeViewConnector/TreeViewConnector.tsx +36 -0
  296. package/src/components/TreeViewConnector/index.ts +2 -0
  297. package/src/components/TreeViewItem/TreeViewItem.tsx +111 -0
  298. package/src/components/TreeViewItem/index.ts +2 -0
  299. package/src/components/VerificationCodeInput/VerificationCodeInput.tsx +114 -0
  300. package/src/components/VerificationCodeInput/index.ts +2 -0
  301. package/src/illustrations/BoxIllustration.tsx +13 -0
  302. package/src/illustrations/CloudIllustration.tsx +18 -0
  303. package/src/illustrations/CreditCardIllustration.tsx +13 -0
  304. package/src/illustrations/DocumentsIllustration.tsx +13 -0
  305. package/src/illustrations/index.ts +4 -0
  306. package/src/index.ts +147 -0
  307. package/src/internal/chartTheme.ts +30 -0
  308. package/src/internal/ringBase.tsx +82 -0
  309. package/src/styles.css +3 -0
  310. package/tailwind-preset.js +295 -0
@@ -0,0 +1,178 @@
1
+ import { useState } from "react";
2
+ import type { ReactNode } from "react";
3
+ import clsx from "clsx";
4
+
5
+ export type HeaderNavigationType = "Simple" | "DualTier" | "Centered" | "Tabs";
6
+
7
+ export interface HeaderNavItem {
8
+ label: ReactNode;
9
+ href?: string;
10
+ active?: boolean;
11
+ onClick?: () => void;
12
+ }
13
+
14
+ export interface HeaderNavigationProps {
15
+ /** Layout variant. */
16
+ type?: HeaderNavigationType;
17
+ /** Brand mark (logo). Defaults to a text wordmark. */
18
+ logo?: ReactNode;
19
+ /** Primary navigation items. */
20
+ items?: HeaderNavItem[];
21
+ /** Right-aligned actions on desktop (search/settings/notifications/account). */
22
+ actions?: ReactNode;
23
+ /** Content shown at the bottom of the open mobile drawer (e.g. an account card). */
24
+ mobileFooter?: ReactNode;
25
+ className?: string;
26
+ }
27
+
28
+ function MenuIcon() {
29
+ return (
30
+ <svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
31
+ <path d="M2.5 10h15M2.5 5h15M2.5 15h15" stroke="currentColor" strokeWidth="1.667" strokeLinecap="round" strokeLinejoin="round" />
32
+ </svg>
33
+ );
34
+ }
35
+
36
+ function XIcon() {
37
+ return (
38
+ <svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
39
+ <path d="M15 5 5 15M5 5l10 10" stroke="currentColor" strokeWidth="1.667" strokeLinecap="round" strokeLinejoin="round" />
40
+ </svg>
41
+ );
42
+ }
43
+
44
+ function NavLink({ item, tab }: { item: HeaderNavItem; tab?: boolean }) {
45
+ const className = tab
46
+ ? clsx(
47
+ "border-b-2 px-xs pb-lg pt-px text-sm font-semibold",
48
+ item.active
49
+ ? "border-fg-brand-primary text-text-brand-secondary"
50
+ : "border-transparent text-text-quaternary hover:text-text-secondary",
51
+ )
52
+ : clsx(
53
+ "rounded-sm px-md py-sm text-sm font-semibold",
54
+ item.active
55
+ ? "bg-bg-secondary text-text-secondary-hover"
56
+ : "text-text-secondary hover:bg-bg-secondary",
57
+ );
58
+ if (item.href) {
59
+ return (
60
+ <a href={item.href} onClick={item.onClick} className={className} aria-current={item.active ? "page" : undefined}>
61
+ {item.label}
62
+ </a>
63
+ );
64
+ }
65
+ return (
66
+ <button type="button" onClick={item.onClick} className={className}>
67
+ {item.label}
68
+ </button>
69
+ );
70
+ }
71
+
72
+ /**
73
+ * Application header navigation. One component with a `type` prop covering the
74
+ * Simple / DualTier / Centered / Tabs layouts. Below `md` it collapses to a
75
+ * logo + hamburger that opens a real (stateful) drawer.
76
+ */
77
+ export function HeaderNavigation({
78
+ type = "Simple",
79
+ logo = <span className="text-md font-semibold text-text-primary">Untitled UI</span>,
80
+ items = [],
81
+ actions,
82
+ mobileFooter,
83
+ className,
84
+ }: HeaderNavigationProps) {
85
+ const [mobileOpen, setMobileOpen] = useState(false);
86
+
87
+ const navRow = (tabs?: boolean, centered?: boolean) => (
88
+ <nav
89
+ className={clsx(
90
+ "hidden items-center gap-xxs md:flex",
91
+ centered && "flex-1 justify-center",
92
+ )}
93
+ >
94
+ {items.map((item, i) => (
95
+ <NavLink key={i} item={item} tab={tabs} />
96
+ ))}
97
+ </nav>
98
+ );
99
+
100
+ const desktopActions = actions ? (
101
+ <div className="hidden shrink-0 items-center gap-lg md:flex">{actions}</div>
102
+ ) : null;
103
+
104
+ // Top bar — always holds logo (left) + mobile hamburger / desktop actions (right).
105
+ const single = type === "Simple";
106
+ return (
107
+ <header
108
+ className={clsx(
109
+ "w-full border-b border-border-secondary bg-bg-primary font-body",
110
+ className,
111
+ )}
112
+ >
113
+ <div className="mx-auto flex min-h-16 max-w-screen-xl items-center justify-between gap-xl px-container-padding-desktop max-md:px-container-padding-mobile">
114
+ <div className="flex items-center gap-xl">
115
+ <span className="shrink-0">{logo}</span>
116
+ {single ? navRow(false) : null}
117
+ {type === "Centered" ? null : null}
118
+ </div>
119
+
120
+ {/* Centered/DualTier/Tabs put nav on its own row; Simple keeps it inline. */}
121
+ {type === "Centered" ? navRow(false, true) : null}
122
+
123
+ <div className="flex items-center gap-lg">
124
+ {desktopActions}
125
+ <button
126
+ type="button"
127
+ onClick={() => setMobileOpen(true)}
128
+ aria-label="Open menu"
129
+ className="rounded-md p-md text-text-secondary md:hidden"
130
+ >
131
+ <MenuIcon />
132
+ </button>
133
+ </div>
134
+ </div>
135
+
136
+ {/* Second tier for non-Simple desktop layouts. */}
137
+ {type === "DualTier" || type === "Tabs" ? (
138
+ <div className="hidden border-t border-border-secondary md:block">
139
+ <div className="mx-auto flex max-w-screen-xl items-center px-container-padding-desktop">
140
+ {navRow(type === "Tabs")}
141
+ </div>
142
+ </div>
143
+ ) : null}
144
+
145
+ {/* Mobile drawer — real open/close state. */}
146
+ {mobileOpen ? (
147
+ <div className="fixed inset-0 z-50 flex md:hidden">
148
+ <div
149
+ className="absolute inset-0 bg-bg-overlay/70 backdrop-blur-md"
150
+ onClick={() => setMobileOpen(false)}
151
+ aria-hidden
152
+ />
153
+ <div className="relative flex h-full w-[296px] max-w-[80%] flex-col justify-between bg-bg-primary shadow-xl">
154
+ <div className="flex flex-col gap-2xl px-xl pb-2xl pt-xl">
155
+ <div className="flex items-center justify-between">
156
+ <span className="shrink-0">{logo}</span>
157
+ <button
158
+ type="button"
159
+ onClick={() => setMobileOpen(false)}
160
+ aria-label="Close menu"
161
+ className="rounded-md p-md text-text-secondary"
162
+ >
163
+ <XIcon />
164
+ </button>
165
+ </div>
166
+ <nav className="flex flex-col gap-px">
167
+ {items.map((item, i) => (
168
+ <NavLink key={i} item={item} />
169
+ ))}
170
+ </nav>
171
+ </div>
172
+ {mobileFooter ? <div className="px-xl pb-xl">{mobileFooter}</div> : null}
173
+ </div>
174
+ </div>
175
+ ) : null}
176
+ </header>
177
+ );
178
+ }
@@ -0,0 +1,6 @@
1
+ export { HeaderNavigation } from "./HeaderNavigation";
2
+ export type {
3
+ HeaderNavigationProps,
4
+ HeaderNavigationType,
5
+ HeaderNavItem,
6
+ } from "./HeaderNavigation";
@@ -0,0 +1,49 @@
1
+ import { type ReactNode } from "react";
2
+ import clsx from "clsx";
3
+ import { Tooltip, type TooltipArrow } from "../Tooltip";
4
+
5
+ export interface HelpIconProps {
6
+ /** Tooltip heading text. */
7
+ text?: ReactNode;
8
+ /** Optional second tooltip line. */
9
+ supportingText?: ReactNode;
10
+ /** Tooltip arrow position (default bottomCenter — tooltip sits above). */
11
+ arrow?: TooltipArrow;
12
+ /** Icon box size in px (default 16). */
13
+ size?: number;
14
+ className?: string;
15
+ "aria-label"?: string;
16
+ }
17
+
18
+ /**
19
+ * Help "?" icon that reveals a `Tooltip` on hover/focus. A composition (icon
20
+ * button + `Tooltip`), not a new primitive — the tooltip shows via CSS on
21
+ * `group-hover`/`focus-within`, positioned above the icon.
22
+ */
23
+ export function HelpIcon({
24
+ text = "This is a tooltip",
25
+ supportingText,
26
+ arrow = "bottomCenter",
27
+ size = 16,
28
+ className,
29
+ "aria-label": ariaLabel = "Help",
30
+ }: HelpIconProps) {
31
+ return (
32
+ <span className={clsx("group relative inline-flex", className)}>
33
+ <button
34
+ type="button"
35
+ aria-label={ariaLabel}
36
+ className="inline-flex items-center justify-center rounded-full text-fg-quaternary outline-none hover:text-fg-secondary focus-visible:ring-2 focus-visible:ring-utility-brand-500"
37
+ style={{ width: size, height: size }}
38
+ >
39
+ <svg viewBox="0 0 16 16" fill="none" className="size-full" aria-hidden>
40
+ <circle cx="8" cy="8" r="6.5" stroke="currentColor" strokeWidth="1.2" />
41
+ <path d="M6.4 6.2a1.6 1.6 0 1 1 2.2 1.5c-.4.2-.6.5-.6.9M8 11h.01" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
42
+ </svg>
43
+ </button>
44
+ <span className="pointer-events-none absolute bottom-[calc(100%+8px)] left-1/2 -translate-x-1/2 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
45
+ <Tooltip text={text} supportingText={supportingText} arrow={arrow} />
46
+ </span>
47
+ </span>
48
+ );
49
+ }
@@ -0,0 +1,2 @@
1
+ export { HelpIcon } from "./HelpIcon";
2
+ export type { HelpIconProps } from "./HelpIcon";
@@ -0,0 +1,108 @@
1
+ import { useState } from "react";
2
+ import type { InputHTMLAttributes, ReactNode } from "react";
3
+ import clsx from "clsx";
4
+ import {
5
+ FieldWrapper,
6
+ boxClasses,
7
+ inputSizeClasses,
8
+ type InputFieldSize,
9
+ } from "./inputFieldShared";
10
+
11
+ export type InputFieldType = "default" | "password" | "date-time" | "payment";
12
+
13
+ export interface InputFieldProps
14
+ extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
15
+ /** Field variant — drives the native input type and default affordances. */
16
+ type?: InputFieldType;
17
+ size?: InputFieldSize;
18
+ label?: ReactNode;
19
+ hint?: ReactNode;
20
+ required?: boolean;
21
+ destructive?: boolean;
22
+ /** Leading icon (overrides the per-type default, e.g. payment card). */
23
+ leadingIcon?: ReactNode;
24
+ /** Trailing icon (overrides the per-type default). */
25
+ trailingIcon?: ReactNode;
26
+ }
27
+
28
+ function CalendarIcon() {
29
+ return (
30
+ <svg viewBox="0 0 20 20" fill="none" className="size-5 text-fg-quaternary" aria-hidden>
31
+ <path d="M17.5 8.333H2.5M13.333 1.667V5M6.667 1.667V5M6.5 18.333h7c1.4 0 2.1 0 2.635-.272a2.5 2.5 0 0 0 1.093-1.093C17.5 16.433 17.5 15.733 17.5 14.333v-7c0-1.4 0-2.1-.272-2.635a2.5 2.5 0 0 0-1.093-1.093C15.6 3.333 14.9 3.333 13.5 3.333h-7c-1.4 0-2.1 0-2.635.272A2.5 2.5 0 0 0 2.772 4.698C2.5 5.233 2.5 5.933 2.5 7.333v7c0 1.4 0 2.1.272 2.635a2.5 2.5 0 0 0 1.093 1.093c.535.272 1.235.272 2.635.272Z" stroke="currentColor" strokeWidth="1.667" strokeLinecap="round" strokeLinejoin="round" />
32
+ </svg>
33
+ );
34
+ }
35
+
36
+ function CardIcon() {
37
+ return (
38
+ <svg viewBox="0 0 20 20" fill="none" className="size-5 text-fg-quaternary" aria-hidden>
39
+ <path d="M18.333 8.333H1.667M1.667 7.167l0-.834c0-1.4 0-2.1.272-2.635a2.5 2.5 0 0 1 1.093-1.093C3.567 2.333 4.267 2.333 5.667 2.333h8.666c1.4 0 2.1 0 2.635.272a2.5 2.5 0 0 1 1.093 1.093c.272.535.272 1.235.272 2.635v7.334c0 1.4 0 2.1-.272 2.635a2.5 2.5 0 0 1-1.093 1.092c-.535.273-1.235.273-2.635.273H5.667c-1.4 0-2.1 0-2.635-.273a2.5 2.5 0 0 1-1.093-1.092C1.667 15.767 1.667 15.067 1.667 13.667v-.834" stroke="currentColor" strokeWidth="1.667" strokeLinecap="round" strokeLinejoin="round" />
40
+ </svg>
41
+ );
42
+ }
43
+
44
+ function EyeToggle({ shown, onClick }: { shown: boolean; onClick: () => void }) {
45
+ return (
46
+ <button type="button" onClick={onClick} aria-label={shown ? "Hide" : "Show"} className="shrink-0 text-fg-quaternary hover:text-fg-quaternary-hover">
47
+ <svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
48
+ <path d="M1.667 10S4.667 4.167 10 4.167 18.333 10 18.333 10 15.333 15.833 10 15.833 1.667 10 1.667 10Z" stroke="currentColor" strokeWidth="1.667" strokeLinecap="round" strokeLinejoin="round" />
49
+ <circle cx="10" cy="10" r="2.5" stroke="currentColor" strokeWidth="1.667" />
50
+ {shown ? <path d="M3 3l14 14" stroke="currentColor" strokeWidth="1.667" strokeLinecap="round" /> : null}
51
+ </svg>
52
+ </button>
53
+ );
54
+ }
55
+
56
+ const nativeType: Record<InputFieldType, string> = {
57
+ default: "text",
58
+ password: "password",
59
+ "date-time": "datetime-local",
60
+ payment: "text",
61
+ };
62
+
63
+ /**
64
+ * Standard text input. `type` covers Default / Password / Date-time / Payment —
65
+ * same structural markup, differing only in the native input type and default
66
+ * leading/trailing affordance (password eye toggle, date-time calendar, payment
67
+ * card). Leading/trailing icons can be overridden via slots.
68
+ */
69
+ export function InputField({
70
+ type = "default",
71
+ size = "md",
72
+ label,
73
+ hint,
74
+ required,
75
+ destructive,
76
+ leadingIcon,
77
+ trailingIcon,
78
+ className,
79
+ ...rest
80
+ }: InputFieldProps) {
81
+ const [showPassword, setShowPassword] = useState(false);
82
+
83
+ const leading =
84
+ leadingIcon ?? (type === "payment" ? <CardIcon /> : null);
85
+
86
+ let trailing = trailingIcon ?? null;
87
+ if (!trailingIcon) {
88
+ if (type === "password")
89
+ trailing = <EyeToggle shown={showPassword} onClick={() => setShowPassword((s) => !s)} />;
90
+ else if (type === "date-time") trailing = <CalendarIcon />;
91
+ }
92
+
93
+ const resolvedType = type === "password" && showPassword ? "text" : nativeType[type];
94
+
95
+ return (
96
+ <FieldWrapper label={label} required={required} hint={hint} destructive={destructive} className={className}>
97
+ <div className={clsx(boxClasses(destructive), inputSizeClasses[size])}>
98
+ {leading ? <span className="flex shrink-0 items-center">{leading}</span> : null}
99
+ <input
100
+ type={resolvedType}
101
+ className="min-w-0 flex-1 bg-transparent text-text-primary outline-none placeholder:text-text-placeholder"
102
+ {...rest}
103
+ />
104
+ {trailing ? <span className="flex shrink-0 items-center">{trailing}</span> : null}
105
+ </div>
106
+ </FieldWrapper>
107
+ );
108
+ }
@@ -0,0 +1,3 @@
1
+ export { InputField } from "./InputField";
2
+ export type { InputFieldProps, InputFieldType } from "./InputField";
3
+ export type { InputFieldSize } from "./inputFieldShared";
@@ -0,0 +1,68 @@
1
+ import type { ReactNode } from "react";
2
+ import clsx from "clsx";
3
+
4
+ // Shared internals for the Input field family (InputField / LeadingInputField /
5
+ // TrailingInputField / TagsInputField). PRIVATE — not exported from the barrel;
6
+ // only the size type is re-exported by the public components.
7
+
8
+ export type InputFieldSize = "sm" | "md" | "lg";
9
+
10
+ /** Inner padding + text size per field size. */
11
+ export const inputSizeClasses: Record<InputFieldSize, string> = {
12
+ sm: "px-lg py-md text-sm",
13
+ md: "px-lg py-md text-md",
14
+ lg: "px-[14px] py-2.5 text-md",
15
+ };
16
+
17
+ /** Border/focus classes for the bordered input box. */
18
+ export function boxClasses(destructive?: boolean) {
19
+ return clsx(
20
+ "flex w-full items-center gap-md rounded-md border bg-bg-primary shadow-xs",
21
+ "transition-shadow focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-offset-bg-primary",
22
+ destructive
23
+ ? "border-border-error focus-within:ring-border-error"
24
+ : "border-border-primary focus-within:border-border-brand focus-within:ring-border-brand",
25
+ );
26
+ }
27
+
28
+ export interface FieldWrapperProps {
29
+ label?: ReactNode;
30
+ required?: boolean;
31
+ hint?: ReactNode;
32
+ destructive?: boolean;
33
+ /** The control (bordered box) goes here. */
34
+ children: ReactNode;
35
+ className?: string;
36
+ }
37
+
38
+ /** Label + control + hint scaffold shared by every Input field variant. */
39
+ export function FieldWrapper({
40
+ label,
41
+ required,
42
+ hint,
43
+ destructive,
44
+ children,
45
+ className,
46
+ }: FieldWrapperProps) {
47
+ return (
48
+ <div className={clsx("flex w-full flex-col gap-sm font-body", className)}>
49
+ {label ? (
50
+ <span className="flex gap-xxs text-sm font-medium text-text-secondary">
51
+ {label}
52
+ {required ? <span className="text-text-brand-tertiary">*</span> : null}
53
+ </span>
54
+ ) : null}
55
+ {children}
56
+ {hint ? (
57
+ <span
58
+ className={clsx(
59
+ "text-sm font-normal",
60
+ destructive ? "text-text-error-primary" : "text-text-tertiary",
61
+ )}
62
+ >
63
+ {hint}
64
+ </span>
65
+ ) : null}
66
+ </div>
67
+ );
68
+ }
@@ -0,0 +1,60 @@
1
+ import type { InputHTMLAttributes, ReactNode } from "react";
2
+ import clsx from "clsx";
3
+ import { FieldWrapper, type InputFieldSize } from "../InputField/inputFieldShared";
4
+
5
+ export interface LeadingInputFieldProps
6
+ extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "prefix"> {
7
+ /**
8
+ * Left add-on — a text prefix (e.g. "https://") or a leading dropdown.
9
+ * Covers the Leading text / Leading dropdown types.
10
+ */
11
+ prefix?: ReactNode;
12
+ size?: InputFieldSize;
13
+ label?: ReactNode;
14
+ hint?: ReactNode;
15
+ required?: boolean;
16
+ destructive?: boolean;
17
+ }
18
+
19
+ const pad: Record<InputFieldSize, string> = {
20
+ sm: "px-lg py-md text-sm",
21
+ md: "px-lg py-md text-md",
22
+ lg: "px-[14px] py-2.5 text-md",
23
+ };
24
+
25
+ /** Input with a left add-on (text prefix or dropdown). */
26
+ export function LeadingInputField({
27
+ prefix,
28
+ size = "md",
29
+ label,
30
+ hint,
31
+ required,
32
+ destructive,
33
+ className,
34
+ ...rest
35
+ }: LeadingInputFieldProps) {
36
+ return (
37
+ <FieldWrapper label={label} required={required} hint={hint} destructive={destructive} className={className}>
38
+ <div
39
+ className={clsx(
40
+ "flex w-full items-stretch overflow-hidden rounded-md border bg-bg-primary shadow-xs",
41
+ "focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-offset-bg-primary",
42
+ destructive
43
+ ? "border-border-error focus-within:ring-border-error"
44
+ : "border-border-primary focus-within:border-border-brand focus-within:ring-border-brand",
45
+ )}
46
+ >
47
+ <span className="flex shrink-0 items-center border-r border-border-primary px-lg text-md text-text-tertiary">
48
+ {prefix}
49
+ </span>
50
+ <input
51
+ className={clsx(
52
+ "min-w-0 flex-1 bg-transparent text-text-primary outline-none placeholder:text-text-placeholder",
53
+ pad[size],
54
+ )}
55
+ {...rest}
56
+ />
57
+ </div>
58
+ </FieldWrapper>
59
+ );
60
+ }
@@ -0,0 +1,2 @@
1
+ export { LeadingInputField } from "./LeadingInputField";
2
+ export type { LeadingInputFieldProps } from "./LeadingInputField";
@@ -0,0 +1,96 @@
1
+ import { type ReactNode } from "react";
2
+ import {
3
+ ResponsiveContainer,
4
+ LineChart,
5
+ BarChart,
6
+ Line,
7
+ Bar,
8
+ XAxis,
9
+ YAxis,
10
+ CartesianGrid,
11
+ Tooltip,
12
+ } from "recharts";
13
+ import { ChartTooltip } from "../ChartTooltip";
14
+ import { ChartLegend } from "../ChartLegend";
15
+ import { chartColor, CHART_GRID, tickStyle } from "../../internal/chartTheme";
16
+
17
+ export type ChartStyle = "line" | "bar";
18
+ export type ChartLegendPosition = "none" | "top" | "right";
19
+
20
+ export interface ChartSeries {
21
+ /** Key into each data row. */
22
+ dataKey: string;
23
+ /** Display name (legend/tooltip). Defaults to `dataKey`. */
24
+ name?: string;
25
+ /** Override color; defaults to the palette by index. */
26
+ color?: string;
27
+ }
28
+
29
+ export interface LineAndBarChartProps {
30
+ data: Record<string, number | string>[];
31
+ series: ChartSeries[];
32
+ /** Key for the x-axis category. */
33
+ xKey: string;
34
+ type?: ChartStyle;
35
+ legend?: ChartLegendPosition;
36
+ axisLabels?: boolean;
37
+ height?: number;
38
+ className?: string;
39
+ /** Tooltip value formatter. */
40
+ formatValue?: (value: number | string | undefined) => ReactNode;
41
+ }
42
+
43
+ /**
44
+ * Line or bar chart — a themed Recharts wrapper (not a from-scratch renderer).
45
+ * `series` map to `<Line>`/`<Bar>` colored from the shared palette; `legend`
46
+ * positions a `ChartLegend` top/right; `axisLabels` toggles the X/Y ticks.
47
+ * Recharts is an optional peer dependency.
48
+ */
49
+ export function LineAndBarChart({
50
+ data,
51
+ series,
52
+ xKey,
53
+ type = "line",
54
+ legend = "top",
55
+ axisLabels = true,
56
+ height = 240,
57
+ className,
58
+ formatValue,
59
+ }: LineAndBarChartProps) {
60
+ const colored = series.map((s, i) => ({ ...s, color: s.color ?? chartColor(i) }));
61
+ const Chart = type === "bar" ? BarChart : LineChart;
62
+
63
+ const legendEl =
64
+ legend !== "none" ? (
65
+ <ChartLegend
66
+ orientation={legend === "right" ? "vertical" : "horizontal"}
67
+ items={colored.map((s) => ({ label: s.name ?? s.dataKey, color: s.color! }))}
68
+ />
69
+ ) : null;
70
+
71
+ return (
72
+ <div className={className}>
73
+ {legend === "top" && <div className="mb-lg">{legendEl}</div>}
74
+ <div className={legend === "right" ? "flex items-center gap-2xl" : undefined}>
75
+ <div className="min-w-0 flex-1">
76
+ <ResponsiveContainer width="100%" height={height}>
77
+ <Chart data={data} margin={{ top: 8, right: 8, bottom: 8, left: 8 }}>
78
+ <CartesianGrid stroke={CHART_GRID} vertical={false} />
79
+ {axisLabels && <XAxis dataKey={xKey} tick={tickStyle} tickLine={false} axisLine={{ stroke: CHART_GRID }} />}
80
+ {axisLabels && <YAxis tick={tickStyle} tickLine={false} axisLine={false} width={36} />}
81
+ <Tooltip cursor={{ fill: CHART_GRID }} content={<ChartTooltip formatValue={formatValue} />} />
82
+ {colored.map((s) =>
83
+ type === "bar" ? (
84
+ <Bar key={s.dataKey} dataKey={s.dataKey} name={s.name ?? s.dataKey} fill={s.color} radius={[4, 4, 0, 0]} />
85
+ ) : (
86
+ <Line key={s.dataKey} dataKey={s.dataKey} name={s.name ?? s.dataKey} stroke={s.color} strokeWidth={2} dot={false} />
87
+ ),
88
+ )}
89
+ </Chart>
90
+ </ResponsiveContainer>
91
+ </div>
92
+ {legend === "right" && legendEl}
93
+ </div>
94
+ </div>
95
+ );
96
+ }
@@ -0,0 +1,2 @@
1
+ export { LineAndBarChart } from "./LineAndBarChart";
2
+ export type { LineAndBarChartProps, ChartSeries, ChartStyle, ChartLegendPosition } from "./LineAndBarChart";
@@ -0,0 +1,52 @@
1
+ import type { ReactNode } from "react";
2
+ import clsx from "clsx";
3
+ import { MessageShell, bubbleClasses } from "../Message/messageShared";
4
+
5
+ export type LinkMessageType = "minimal" | "preview";
6
+
7
+ export interface LinkMessageProps {
8
+ /** `minimal` = link only; `preview` = open-graph image + link. */
9
+ type?: LinkMessageType;
10
+ /** The URL (rendered as an underlined link). */
11
+ url?: string;
12
+ /** Open-graph image source (preview). */
13
+ image?: string;
14
+ /** Optional body text shown above the link. */
15
+ children?: ReactNode;
16
+ author?: ReactNode;
17
+ time?: ReactNode;
18
+ avatar?: ReactNode;
19
+ sent?: boolean;
20
+ reactions?: ReactNode;
21
+ className?: string;
22
+ }
23
+
24
+ /** A chat message that unfurls a URL — minimal (link only) or preview (with OG image). */
25
+ export function LinkMessage({
26
+ type = "preview",
27
+ url = "",
28
+ image,
29
+ children,
30
+ author,
31
+ time,
32
+ avatar,
33
+ sent = false,
34
+ reactions,
35
+ className,
36
+ }: LinkMessageProps) {
37
+ return (
38
+ <MessageShell author={author} time={time} avatar={avatar} sent={sent} reactions={reactions} className={className}>
39
+ <div className={clsx(bubbleClasses(sent, "secondary"), "flex flex-col gap-sm p-lg")}>
40
+ {children ? <span className="text-md font-normal">{children}</span> : null}
41
+ {type === "preview" && image ? (
42
+ <span className="block aspect-[1200/630] w-full overflow-hidden rounded-md border-[0.5px] border-black/10">
43
+ <img src={image} alt="" className="size-full object-cover" />
44
+ </span>
45
+ ) : null}
46
+ <a href={url} className="truncate text-md text-text-brand-secondary underline">
47
+ {url}
48
+ </a>
49
+ </div>
50
+ </MessageShell>
51
+ );
52
+ }
@@ -0,0 +1,2 @@
1
+ export { LinkMessage } from "./LinkMessage";
2
+ export type { LinkMessageProps, LinkMessageType } from "./LinkMessage";