@speakeasy-api/moonshine 1.33.4 → 2.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +26 -10
  2. package/package.json +12 -23
  3. package/scripts/generate-utility-docs.js +324 -0
  4. package/src/assets/icons/external/github.svg +3 -0
  5. package/src/assets/icons/external/maven.svg +152 -0
  6. package/src/assets/icons/external/npm.svg +4 -0
  7. package/src/assets/icons/external/nuget.svg +5 -0
  8. package/src/assets/icons/external/packagist.svg +1 -0
  9. package/src/assets/icons/external/pypi.svg +182 -0
  10. package/src/assets/icons/external/rubygems.svg +14 -0
  11. package/src/assets/icons/external/terraform.svg +1 -0
  12. package/src/assets/icons/languages/csharp.svg +1 -0
  13. package/src/assets/icons/languages/go.svg +1 -0
  14. package/src/assets/icons/languages/java.svg +1 -0
  15. package/src/assets/icons/languages/json.svg +2 -0
  16. package/src/assets/icons/languages/php.svg +1 -0
  17. package/src/assets/icons/languages/postman.svg +3 -0
  18. package/src/assets/icons/languages/python.svg +1 -0
  19. package/src/assets/icons/languages/ruby.svg +1 -0
  20. package/src/assets/icons/languages/swift.svg +1 -0
  21. package/src/assets/icons/languages/terraform.svg +1 -0
  22. package/src/assets/icons/languages/typescript.svg +1 -0
  23. package/src/assets/icons/languages/unity.svg +1 -0
  24. package/src/components/AIChat/AIChatContainer.tsx +71 -0
  25. package/src/components/AIChat/AIChatMessage.tsx +135 -0
  26. package/src/components/AIChat/AIChatMessageComposer.tsx +175 -0
  27. package/src/components/AIChat/AIChatMessageList.tsx +34 -0
  28. package/src/components/AIChat/AIChatModelSelector.tsx +159 -0
  29. package/src/components/AIChat/componentsTypes.ts +36 -0
  30. package/src/components/AIChat/context.ts +15 -0
  31. package/src/components/AIChat/index.ts +12 -0
  32. package/src/components/AIChat/parts/AIChatMessageFilePart.tsx +129 -0
  33. package/src/components/AIChat/parts/AIChatMessageReasoningPart.tsx +23 -0
  34. package/src/components/AIChat/parts/AIChatMessageSourcePart.tsx +58 -0
  35. package/src/components/AIChat/parts/AIChatMessageTextPart.tsx +33 -0
  36. package/src/components/AIChat/parts/AIChatMessageToolInvocationPart.tsx +53 -0
  37. package/src/components/AIChat/parts/AIChatMessageToolPart.tsx +395 -0
  38. package/src/components/AIChat/parts/AIChatMessageToolResultPart.tsx +46 -0
  39. package/src/components/AIChat/toolCallApproval.ts +61 -0
  40. package/src/components/AIChat/types.ts +97 -0
  41. package/src/components/ActionBar/index.tsx +184 -0
  42. package/src/components/Alert/index.tsx +118 -0
  43. package/src/components/Alert/types.ts +12 -0
  44. package/src/components/AppLayout/context.tsx +31 -0
  45. package/src/components/AppLayout/index.tsx +550 -0
  46. package/src/components/AppLayout/provider.tsx +40 -0
  47. package/src/components/AppLayout/useAppLayoutKeys.ts +26 -0
  48. package/src/components/Badge/index.tsx +227 -0
  49. package/src/components/Button/index.tsx +531 -0
  50. package/src/components/Card/index.tsx +193 -0
  51. package/src/components/CodeEditorLayout/index.tsx +394 -0
  52. package/src/components/CodeEditorLayout/styles.module.css +8 -0
  53. package/src/components/CodeHighlight/Pre.tsx +63 -0
  54. package/src/components/CodePlayground/index.tsx +411 -0
  55. package/src/components/CodeSnippet/codeSnippet.css +97 -0
  56. package/src/components/CodeSnippet/index.tsx +224 -0
  57. package/src/components/Combobox/index.tsx +193 -0
  58. package/src/components/Command/index.tsx +152 -0
  59. package/src/components/Container/index.tsx +31 -0
  60. package/src/components/ContextDropdown/index.tsx +150 -0
  61. package/src/components/Dialog/index.tsx +123 -0
  62. package/src/components/DragNDrop/DragNDropArea.tsx +30 -0
  63. package/src/components/DragNDrop/DragOverlay.tsx +4 -0
  64. package/src/components/DragNDrop/Draggable.tsx +97 -0
  65. package/src/components/DragNDrop/Droppable.tsx +51 -0
  66. package/src/components/Dropdown/index.tsx +201 -0
  67. package/src/components/ExternalPill/index.tsx +58 -0
  68. package/src/components/Facepile/index.tsx +309 -0
  69. package/src/components/GradientCircle/gradientCircle.css +34 -0
  70. package/src/components/GradientCircle/index.tsx +143 -0
  71. package/src/components/Grid/index.tsx +150 -0
  72. package/src/components/Heading/index.tsx +54 -0
  73. package/src/components/HighlightedText/index.tsx +152 -0
  74. package/src/components/Icon/customIcons/createCustomLucideIcon.ts +25 -0
  75. package/src/components/Icon/customIcons/gems.ts +26 -0
  76. package/{dist/go-DsW1bFpz.mjs → src/components/Icon/customIcons/go.ts} +21 -19
  77. package/src/components/Icon/customIcons/index.ts +11 -0
  78. package/{dist/maven-Dub5liK1.mjs → src/components/Icon/customIcons/maven.ts} +17 -15
  79. package/src/components/Icon/customIcons/npm.ts +19 -0
  80. package/{dist/nuget-D86y5HDl.mjs → src/components/Icon/customIcons/nuget.ts} +17 -15
  81. package/src/components/Icon/customIcons/packagist.ts +124 -0
  82. package/{dist/pypi-2SUX-2OR.mjs → src/components/Icon/customIcons/pypi.ts} +16 -14
  83. package/src/components/Icon/index.tsx +83 -0
  84. package/src/components/Icon/isIconName.ts +10 -0
  85. package/src/components/Icon/names.ts +14 -0
  86. package/src/components/IconButton/index.tsx +51 -0
  87. package/src/components/Input/index.tsx +98 -0
  88. package/src/components/KeyHint/index.tsx +118 -0
  89. package/src/components/LanguageIndicator/index.tsx +68 -0
  90. package/src/components/Link/index.tsx +153 -0
  91. package/src/components/LoggedInUserMenu/index.tsx +116 -0
  92. package/src/components/Logo/Animated.tsx +191 -0
  93. package/src/components/Logo/index.tsx +17 -0
  94. package/src/components/Logo/speakeasy-logo.riv +0 -0
  95. package/src/components/Logo/svgs/index.tsx +126 -0
  96. package/src/components/Modal/index.tsx +104 -0
  97. package/src/components/PageHeader/index.tsx +227 -0
  98. package/src/components/PageHeader/styles.module.css +27 -0
  99. package/src/components/Popover/index.tsx +35 -0
  100. package/src/components/PromptInput/index.tsx +372 -0
  101. package/src/components/PullRequestLink/index.tsx +64 -0
  102. package/src/components/ResizablePanel/index.tsx +119 -0
  103. package/src/components/Score/index.module.css +32 -0
  104. package/src/components/Score/index.tsx +268 -0
  105. package/src/components/ScrollArea/index.tsx +48 -0
  106. package/src/components/SegmentedButton/index.module.css +19 -0
  107. package/src/components/SegmentedButton/index.tsx +101 -0
  108. package/src/components/Select/index.tsx +159 -0
  109. package/src/components/Separator/index.tsx +23 -0
  110. package/src/components/Skeleton/index.tsx +61 -0
  111. package/src/components/Skeleton/skeleton.css +52 -0
  112. package/src/components/Stack/index.tsx +137 -0
  113. package/src/components/Subnav/index.tsx +315 -0
  114. package/src/components/Switch/index.tsx +29 -0
  115. package/src/components/Table/context/context.tsx +19 -0
  116. package/src/components/Table/context/tableProvider.tsx +39 -0
  117. package/src/components/Table/index.tsx +707 -0
  118. package/src/components/Table/styles.module.css +25 -0
  119. package/src/components/Tabs/index.tsx +87 -0
  120. package/src/components/TargetLanguageIcon/index.tsx +84 -0
  121. package/src/components/Text/index.tsx +59 -0
  122. package/src/components/ThemeSwitcher/index.tsx +118 -0
  123. package/src/components/Timeline/index.tsx +290 -0
  124. package/src/components/Tooltip/index.tsx +41 -0
  125. package/src/components/UserAvatar/index.tsx +87 -0
  126. package/src/components/UserAvatar/sizeMap.ts +12 -0
  127. package/src/components/Wizard/index.tsx +208 -0
  128. package/src/components/Wizard/types.ts +17 -0
  129. package/src/components/WorkspaceSelector/CreateOrg.tsx +95 -0
  130. package/src/components/WorkspaceSelector/CreateWorkspace.tsx +196 -0
  131. package/src/components/WorkspaceSelector/OrgList.tsx +115 -0
  132. package/src/components/WorkspaceSelector/OrgSelector.tsx +207 -0
  133. package/src/components/WorkspaceSelector/RecentWorkspaces.tsx +83 -0
  134. package/src/components/WorkspaceSelector/ScrollingList.tsx +84 -0
  135. package/src/components/WorkspaceSelector/SearchBox.tsx +40 -0
  136. package/src/components/WorkspaceSelector/WorkspaceItem.tsx +37 -0
  137. package/src/components/WorkspaceSelector/WorkspaceList.tsx +107 -0
  138. package/src/components/WorkspaceSelector/index.tsx +400 -0
  139. package/src/components/WorkspaceSelector/styles.css +74 -0
  140. package/src/components/__beta__/CLIWizard/index.tsx +357 -0
  141. package/src/components/__beta__/CLIWizard/terminal-command.tsx +108 -0
  142. package/src/components/__beta__/CLIWizard/terminal.tsx +83 -0
  143. package/src/components/__beta__/README.md +3 -0
  144. package/src/components/index.mdx +38 -0
  145. package/src/context/ConfigContext.tsx +43 -0
  146. package/src/context/ModalContext.tsx +118 -0
  147. package/src/context/theme.ts +1 -0
  148. package/src/hooks/useAppLayout.ts +10 -0
  149. package/src/hooks/useConfig.ts +10 -0
  150. package/src/hooks/useIsMounted.ts +13 -0
  151. package/src/hooks/useModal.tsx +10 -0
  152. package/src/hooks/useTailwindBreakpoint.ts +47 -0
  153. package/src/hooks/useTheme.ts +13 -0
  154. package/src/index.ts +234 -0
  155. package/src/lib/assert.ts +9 -0
  156. package/src/lib/codeUtils.ts +177 -0
  157. package/src/lib/debounce.ts +9 -0
  158. package/src/lib/responsiveMappers.ts +69 -0
  159. package/src/lib/responsiveUtils.ts +23 -0
  160. package/src/lib/storybookUtils.tsx +26 -0
  161. package/src/lib/typeUtils.ts +109 -0
  162. package/src/lib/utils.ts +85 -0
  163. package/src/styles/codeSyntax.css +59 -0
  164. package/src/styles/globals.css +51 -0
  165. package/src/types.ts +200 -0
  166. package/src/vite-env.d.ts +6 -0
  167. package/types/utilities.d.ts +1 -1
  168. package/dist/components/AIChat/AIChatContainer.d.ts +0 -26
  169. package/dist/components/AIChat/AIChatMessage.d.ts +0 -19
  170. package/dist/components/AIChat/AIChatMessageComposer.d.ts +0 -22
  171. package/dist/components/AIChat/AIChatMessageList.d.ts +0 -6
  172. package/dist/components/AIChat/AIChatModelSelector.d.ts +0 -14
  173. package/dist/components/AIChat/componentsTypes.d.ts +0 -11
  174. package/dist/components/AIChat/context.d.ts +0 -3
  175. package/dist/components/AIChat/index.d.ts +0 -12
  176. package/dist/components/AIChat/parts/AIChatMessageFilePart.d.ts +0 -7
  177. package/dist/components/AIChat/parts/AIChatMessageReasoningPart.d.ts +0 -5
  178. package/dist/components/AIChat/parts/AIChatMessageSourcePart.d.ts +0 -9
  179. package/dist/components/AIChat/parts/AIChatMessageTextPart.d.ts +0 -5
  180. package/dist/components/AIChat/parts/AIChatMessageToolInvocationPart.d.ts +0 -6
  181. package/dist/components/AIChat/parts/AIChatMessageToolPart.d.ts +0 -33
  182. package/dist/components/AIChat/parts/AIChatMessageToolResultPart.d.ts +0 -5
  183. package/dist/components/AIChat/toolCallApproval.d.ts +0 -15
  184. package/dist/components/AIChat/types.d.ts +0 -79
  185. package/dist/components/ActionBar/index.d.ts +0 -36
  186. package/dist/components/Alert/index.d.ts +0 -18
  187. package/dist/components/Alert/types.d.ts +0 -4
  188. package/dist/components/AppLayout/context.d.ts +0 -16
  189. package/dist/components/AppLayout/index.d.ts +0 -131
  190. package/dist/components/AppLayout/provider.d.ts +0 -8
  191. package/dist/components/AppLayout/useAppLayoutKeys.d.ts +0 -1
  192. package/dist/components/Badge/index.d.ts +0 -21
  193. package/dist/components/Button/index.d.ts +0 -22
  194. package/dist/components/Card/index.d.ts +0 -47
  195. package/dist/components/CodeEditorLayout/index.d.ts +0 -101
  196. package/dist/components/CodeHighlight/Pre.d.ts +0 -8
  197. package/dist/components/CodePlayground/index.d.ts +0 -102
  198. package/dist/components/CodeSnippet/index.d.ts +0 -49
  199. package/dist/components/Combobox/index.d.ts +0 -35
  200. package/dist/components/Command/index.d.ts +0 -80
  201. package/dist/components/Container/index.d.ts +0 -9
  202. package/dist/components/ContextDropdown/index.d.ts +0 -7
  203. package/dist/components/Dialog/index.d.ts +0 -21
  204. package/dist/components/DragNDrop/DragNDropArea.d.ts +0 -8
  205. package/dist/components/DragNDrop/DragOverlay.d.ts +0 -1
  206. package/dist/components/DragNDrop/Draggable.d.ts +0 -29
  207. package/dist/components/DragNDrop/Droppable.d.ts +0 -28
  208. package/dist/components/Dropdown/index.d.ts +0 -27
  209. package/dist/components/ExternalPill/index.d.ts +0 -12
  210. package/dist/components/Facepile/index.d.ts +0 -16
  211. package/dist/components/GradientCircle/index.d.ts +0 -10
  212. package/dist/components/Grid/index.d.ts +0 -80
  213. package/dist/components/Heading/index.d.ts +0 -12
  214. package/dist/components/HighlightedText/index.d.ts +0 -19
  215. package/dist/components/Icon/customIcons/createCustomLucideIcon.d.ts +0 -3
  216. package/dist/components/Icon/customIcons/gems.d.ts +0 -2
  217. package/dist/components/Icon/customIcons/go.d.ts +0 -2
  218. package/dist/components/Icon/customIcons/index.d.ts +0 -10
  219. package/dist/components/Icon/customIcons/maven.d.ts +0 -2
  220. package/dist/components/Icon/customIcons/npm.d.ts +0 -2
  221. package/dist/components/Icon/customIcons/nuget.d.ts +0 -2
  222. package/dist/components/Icon/customIcons/packagist.d.ts +0 -2
  223. package/dist/components/Icon/customIcons/pypi.d.ts +0 -2
  224. package/dist/components/Icon/index.d.ts +0 -10
  225. package/dist/components/Icon/isIconName.d.ts +0 -2
  226. package/dist/components/Icon/names.d.ts +0 -6
  227. package/dist/components/IconButton/index.d.ts +0 -14
  228. package/dist/components/Input/index.d.ts +0 -8
  229. package/dist/components/KeyHint/index.d.ts +0 -16
  230. package/dist/components/LanguageIndicator/index.d.ts +0 -7
  231. package/dist/components/Link/index.d.ts +0 -15
  232. package/dist/components/LoggedInUserMenu/index.d.ts +0 -17
  233. package/dist/components/Logo/Animated.d.ts +0 -7
  234. package/dist/components/Logo/index.d.ts +0 -7
  235. package/dist/components/Logo/svgs/index.d.ts +0 -6
  236. package/dist/components/Modal/index.d.ts +0 -8
  237. package/dist/components/PageHeader/index.d.ts +0 -45
  238. package/dist/components/Popover/index.d.ts +0 -8
  239. package/dist/components/PromptInput/index.d.ts +0 -55
  240. package/dist/components/PullRequestLink/index.d.ts +0 -10
  241. package/dist/components/ResizablePanel/index.d.ts +0 -26
  242. package/dist/components/Score/index.d.ts +0 -37
  243. package/dist/components/ScrollArea/index.d.ts +0 -5
  244. package/dist/components/SegmentedButton/index.d.ts +0 -23
  245. package/dist/components/Select/index.d.ts +0 -13
  246. package/dist/components/Separator/index.d.ts +0 -6
  247. package/dist/components/Skeleton/index.d.ts +0 -27
  248. package/dist/components/Stack/index.d.ts +0 -33
  249. package/dist/components/Subnav/index.d.ts +0 -12
  250. package/dist/components/Switch/index.d.ts +0 -4
  251. package/dist/components/Table/context/context.d.ts +0 -8
  252. package/dist/components/Table/context/tableProvider.d.ts +0 -6
  253. package/dist/components/Table/index.d.ts +0 -94
  254. package/dist/components/Tabs/index.d.ts +0 -21
  255. package/dist/components/TargetLanguageIcon/index.d.ts +0 -7
  256. package/dist/components/Text/index.d.ts +0 -19
  257. package/dist/components/ThemeSwitcher/index.d.ts +0 -6
  258. package/dist/components/Timeline/index.d.ts +0 -49
  259. package/dist/components/Tooltip/index.d.ts +0 -9
  260. package/dist/components/UserAvatar/index.d.ts +0 -9
  261. package/dist/components/UserAvatar/sizeMap.d.ts +0 -3
  262. package/dist/components/Wizard/index.d.ts +0 -19
  263. package/dist/components/Wizard/types.d.ts +0 -15
  264. package/dist/components/WorkspaceSelector/CreateOrg.d.ts +0 -6
  265. package/dist/components/WorkspaceSelector/CreateWorkspace.d.ts +0 -17
  266. package/dist/components/WorkspaceSelector/OrgList.d.ts +0 -12
  267. package/dist/components/WorkspaceSelector/OrgSelector.d.ts +0 -13
  268. package/dist/components/WorkspaceSelector/RecentWorkspaces.d.ts +0 -11
  269. package/dist/components/WorkspaceSelector/ScrollingList.d.ts +0 -21
  270. package/dist/components/WorkspaceSelector/SearchBox.d.ts +0 -9
  271. package/dist/components/WorkspaceSelector/WorkspaceItem.d.ts +0 -9
  272. package/dist/components/WorkspaceSelector/WorkspaceList.d.ts +0 -11
  273. package/dist/components/WorkspaceSelector/index.d.ts +0 -36
  274. package/dist/components/__beta__/CLIWizard/index.d.ts +0 -19
  275. package/dist/components/__beta__/CLIWizard/terminal-command.d.ts +0 -19
  276. package/dist/components/__beta__/CLIWizard/terminal.d.ts +0 -26
  277. package/dist/context/ConfigContext.d.ts +0 -18
  278. package/dist/context/ModalContext.d.ts +0 -22
  279. package/dist/context/theme.d.ts +0 -1
  280. package/dist/createCustomLucideIcon-CatlpFc0.mjs +0 -19
  281. package/dist/createCustomLucideIcon-CatlpFc0.mjs.map +0 -1
  282. package/dist/gems-DQ7pOLLr.mjs +0 -24
  283. package/dist/gems-DQ7pOLLr.mjs.map +0 -1
  284. package/dist/github-kgjMtfE7.mjs +0 -11
  285. package/dist/github-kgjMtfE7.mjs.map +0 -1
  286. package/dist/go-DsW1bFpz.mjs.map +0 -1
  287. package/dist/hooks/useAppLayout.d.ts +0 -1
  288. package/dist/hooks/useConfig.d.ts +0 -2
  289. package/dist/hooks/useIsMounted.d.ts +0 -1
  290. package/dist/hooks/useModal.d.ts +0 -11
  291. package/dist/hooks/useTailwindBreakpoint.d.ts +0 -3
  292. package/dist/hooks/useTheme.d.ts +0 -6
  293. package/dist/index-C9bJtuJ5.mjs +0 -16347
  294. package/dist/index-C9bJtuJ5.mjs.map +0 -1
  295. package/dist/index.d.ts +0 -80
  296. package/dist/lib/assert.d.ts +0 -2
  297. package/dist/lib/codeUtils.d.ts +0 -36
  298. package/dist/lib/debounce.d.ts +0 -1
  299. package/dist/lib/responsiveMappers.d.ts +0 -10
  300. package/dist/lib/responsiveUtils.d.ts +0 -3
  301. package/dist/lib/storybookUtils.d.ts +0 -5
  302. package/dist/lib/typeUtils.d.ts +0 -24
  303. package/dist/lib/utils.d.ts +0 -24
  304. package/dist/maven-Dub5liK1.mjs.map +0 -1
  305. package/dist/maven-W_nkSDNW.mjs +0 -107
  306. package/dist/maven-W_nkSDNW.mjs.map +0 -1
  307. package/dist/moonshine.es.js +0 -125
  308. package/dist/moonshine.es.js.map +0 -1
  309. package/dist/npm-BWTcVvFH.mjs +0 -11
  310. package/dist/npm-BWTcVvFH.mjs.map +0 -1
  311. package/dist/npm-BYcG5_q9.mjs +0 -17
  312. package/dist/npm-BYcG5_q9.mjs.map +0 -1
  313. package/dist/nuget-CV5HU1JR.mjs +0 -11
  314. package/dist/nuget-CV5HU1JR.mjs.map +0 -1
  315. package/dist/nuget-D86y5HDl.mjs.map +0 -1
  316. package/dist/packagist-BFSSrw4p.mjs +0 -118
  317. package/dist/packagist-BFSSrw4p.mjs.map +0 -1
  318. package/dist/packagist-D01fn9N_.mjs +0 -11
  319. package/dist/packagist-D01fn9N_.mjs.map +0 -1
  320. package/dist/pypi-2SUX-2OR.mjs.map +0 -1
  321. package/dist/pypi-DLh6kIJe.mjs +0 -11
  322. package/dist/pypi-DLh6kIJe.mjs.map +0 -1
  323. package/dist/rubygems-DeiNjcDV.mjs +0 -11
  324. package/dist/rubygems-DeiNjcDV.mjs.map +0 -1
  325. package/dist/speakeasy-logo-ByBTXLWb.mjs +0 -5
  326. package/dist/speakeasy-logo-ByBTXLWb.mjs.map +0 -1
  327. package/dist/style.css +0 -1
  328. package/dist/terraform-C4aktQ0o.mjs +0 -11
  329. package/dist/terraform-C4aktQ0o.mjs.map +0 -1
  330. package/dist/types.d.ts +0 -85
@@ -0,0 +1,58 @@
1
+ // TODO: https://linear.app/speakeasy/issue/SXF-173/external-pill-component
2
+ import React, { useState, useEffect } from 'react'
3
+ import { Icon as FallbackIcon } from '../Icon'
4
+ import { cn } from '../../lib/utils'
5
+
6
+ type AllExternalIcons =
7
+ | 'github'
8
+ | 'npm'
9
+ | 'rubygems'
10
+ | 'nuget'
11
+ | 'maven'
12
+ | 'pypi'
13
+ | 'packagist'
14
+ | 'terraform'
15
+ const supportedExternals = ['github', 'npm', 'rubygems']
16
+
17
+ export interface ExternalPillProps {
18
+ href: string
19
+ icon: AllExternalIcons
20
+ text: React.ReactNode
21
+ title?: string
22
+ target?: '_blank' | '_self' | '_parent' | '_top'
23
+ className?: string
24
+ }
25
+
26
+ export function ExternalPill({
27
+ href,
28
+ icon,
29
+ text,
30
+ target = '_blank',
31
+ title,
32
+ className,
33
+ }: ExternalPillProps) {
34
+ const [Icon, setIcon] = useState<React.ComponentType | null>(null)
35
+
36
+ useEffect(() => {
37
+ if (supportedExternals.includes(icon)) {
38
+ import(`../../assets/icons/external/${icon}.svg?react`).then((module) =>
39
+ setIcon(module.default)
40
+ )
41
+ }
42
+ }, [icon])
43
+
44
+ return (
45
+ <a
46
+ href={href}
47
+ target={target}
48
+ title={title}
49
+ className={cn(
50
+ 'inline-flex flex-row items-center gap-1.5 rounded-xl border px-3.5 py-2 text-zinc-700 transition-colors duration-500 hover:border-zinc-300 hover:bg-zinc-50 hover:text-black dark:text-zinc-300 dark:hover:border-zinc-700 dark:hover:bg-zinc-900 hover:dark:text-white',
51
+ className
52
+ )}
53
+ >
54
+ {Icon ? <Icon /> : <FallbackIcon name="external-link" />}
55
+ <p className="text-xs font-normal">{text}</p>
56
+ </a>
57
+ )
58
+ }
@@ -0,0 +1,309 @@
1
+ import React, { useState, useRef, useEffect } from 'react'
2
+ import { motion, AnimatePresence } from 'motion/react'
3
+ import { ResponsiveValue, Size } from '../../types'
4
+ import { UserAvatar, UserAvatarProps } from '../UserAvatar'
5
+ import { userAvatarSizeMap, userAvatarSizeMapper } from '../UserAvatar/sizeMap'
6
+ import { cn, getResponsiveClasses } from '../../lib/utils'
7
+ import useTailwindBreakpoint from '../../hooks/useTailwindBreakpoint'
8
+ import { resolveSizeForBreakpoint } from '../../lib/responsiveUtils'
9
+
10
+ type FacepileVariant = 'interactive' | 'static'
11
+ type AvatarProps = Omit<UserAvatarProps, 'size'> & { href?: string }
12
+
13
+ export interface FacepileProps {
14
+ avatars: AvatarProps[]
15
+ maxFaces?: number
16
+ avatarSize?: ResponsiveValue<Size>
17
+ variant?: FacepileVariant
18
+ tooltips?: boolean
19
+ className?: string
20
+ }
21
+
22
+ export function Facepile({
23
+ avatars = [],
24
+ maxFaces = 3,
25
+ avatarSize = 'medium',
26
+ variant = 'interactive',
27
+ tooltips = true,
28
+ className,
29
+ }: FacepileProps) {
30
+ const [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
31
+ const [prevHoveredIndex, setPrevHoveredIndex] = useState<number | null>(null)
32
+ const [tooltipVisible, setTooltipVisible] = useState(false)
33
+ const [isExpanded, setIsExpanded] = useState(false)
34
+ const isHovered = useRef(false)
35
+
36
+ const breakpoint = useTailwindBreakpoint()
37
+ const resolvedSize = resolveSizeForBreakpoint(
38
+ breakpoint,
39
+ avatarSize,
40
+ 'medium'
41
+ )
42
+
43
+ const overlap = 0.6
44
+ const size = userAvatarSizeMap[resolvedSize] * 4 // *4 as avatarSizeMap is a record of tailwind sizes - possibly a better way to do this
45
+ const offsetX = size * overlap // How much each avatar overlaps
46
+
47
+ const visibleFaces = avatars.slice(0, maxFaces)
48
+ const hiddenFaces = avatars.slice(maxFaces)
49
+ const extraFaces = Math.max(0, avatars.length - maxFaces)
50
+
51
+ const containerRef = useRef<HTMLDivElement>(null)
52
+ const tooltipRef = useRef<HTMLDivElement>(null)
53
+ const [tooltipWidth, setTooltipWidth] = useState(0)
54
+
55
+ useEffect(() => {
56
+ if (tooltipRef.current) {
57
+ const updateTooltipWidth = () => {
58
+ if (tooltipRef.current?.offsetWidth) {
59
+ setTooltipWidth(tooltipRef.current!.offsetWidth)
60
+ }
61
+ }
62
+ updateTooltipWidth()
63
+ const resizeObserver = new ResizeObserver(updateTooltipWidth)
64
+ resizeObserver.observe(tooltipRef.current)
65
+ return () => resizeObserver.disconnect()
66
+ }
67
+ }, [hoveredIndex, visibleFaces])
68
+
69
+ useEffect(() => {
70
+ if (hoveredIndex !== null) {
71
+ setTooltipVisible(true)
72
+ setPrevHoveredIndex(hoveredIndex)
73
+ } else {
74
+ setTooltipVisible(false)
75
+ }
76
+ }, [hoveredIndex])
77
+
78
+ const getTooltipPosition = (index: number, tooltipWidth: number) => {
79
+ const avatarCenter = index * offsetX + size / 2
80
+ return avatarCenter - tooltipWidth / 2
81
+ }
82
+
83
+ const handleContainerMouseLeave = () => {
84
+ setHoveredIndex(null)
85
+ setIsExpanded(false)
86
+ isHovered.current = false
87
+ }
88
+
89
+ const handleContainerMouseEnter = () => {
90
+ isHovered.current = true
91
+ }
92
+
93
+ const handleMouseEnter = (index: number) => {
94
+ setHoveredIndex(index)
95
+ if (index < maxFaces) {
96
+ setIsExpanded(false)
97
+ } else {
98
+ setIsExpanded(true)
99
+ }
100
+ }
101
+
102
+ const handleHoverExpand = () => {
103
+ if (variant === 'interactive') {
104
+ setTimeout(() => {
105
+ if (isHovered.current) {
106
+ setIsExpanded(true)
107
+ }
108
+ }, 400)
109
+ }
110
+ }
111
+
112
+ const containerWidth = isExpanded
113
+ ? avatars.length * offsetX + size - offsetX
114
+ : visibleFaces.length * offsetX +
115
+ size -
116
+ offsetX +
117
+ (extraFaces > 0 ? offsetX : 0)
118
+
119
+ const getTooltipAnimation = (index: number | null) => {
120
+ if (prevHoveredIndex === null || index === null) return {}
121
+ const direction = index > prevHoveredIndex ? 1 : -1
122
+ return {
123
+ initial: { opacity: 0, x: -15 * direction },
124
+ animate: { opacity: 1, x: 0 },
125
+ exit: { opacity: 0, x: 20 * direction },
126
+ transition: { duration: 0.2 },
127
+ }
128
+ }
129
+
130
+ return (
131
+ <motion.div
132
+ ref={containerRef}
133
+ className={cn('relative', className)}
134
+ style={{ height: size }}
135
+ animate={{ width: containerWidth }}
136
+ transition={{ duration: 0.3, ease: 'easeInOut' }}
137
+ onMouseEnter={handleContainerMouseEnter}
138
+ onMouseLeave={handleContainerMouseLeave}
139
+ >
140
+ {visibleFaces.map((face, index) => (
141
+ <AvatarWrapper
142
+ key={face.name}
143
+ avatar={face}
144
+ avatarSize={avatarSize}
145
+ index={index}
146
+ size={size}
147
+ offsetX={offsetX}
148
+ hoveredIndex={variant === 'interactive' ? hoveredIndex : null}
149
+ handleMouseEnter={handleMouseEnter}
150
+ totalFaces={visibleFaces.length}
151
+ isExpanded={isExpanded}
152
+ maxFaces={maxFaces}
153
+ interactive={variant === 'static' ? false : true}
154
+ />
155
+ ))}
156
+
157
+ {variant === 'interactive' && (
158
+ <AnimatePresence>
159
+ {isExpanded &&
160
+ hiddenFaces.map((face, index) => (
161
+ <AvatarWrapper
162
+ key={face.name}
163
+ avatar={face}
164
+ avatarSize={avatarSize}
165
+ index={index + maxFaces}
166
+ size={size}
167
+ offsetX={offsetX}
168
+ hoveredIndex={hoveredIndex}
169
+ handleMouseEnter={handleMouseEnter}
170
+ totalFaces={avatars.length}
171
+ isExpanded={isExpanded}
172
+ maxFaces={maxFaces}
173
+ interactive={true}
174
+ initial={{ opacity: 0, scale: 0.5, x: -size / 2 }}
175
+ animate={{ opacity: 1, scale: 1, x: 0 }}
176
+ exit={{ opacity: 0, scale: 0.5, x: -size / 2 }}
177
+ transition={{ duration: 0.3, ease: 'easeInOut' }}
178
+ />
179
+ ))}
180
+ </AnimatePresence>
181
+ )}
182
+
183
+ <AnimatePresence>
184
+ {tooltips && tooltipVisible && hoveredIndex !== null && (
185
+ <motion.div
186
+ ref={tooltipRef}
187
+ layout
188
+ initial={{ opacity: 0 }}
189
+ animate={{
190
+ opacity: 1,
191
+ x: getTooltipPosition(hoveredIndex ?? 0, tooltipWidth),
192
+ }}
193
+ exit={{ opacity: 0 }}
194
+ transition={{ duration: 0.15, ease: 'easeOut' }}
195
+ className={`pointer-events-none absolute left-0 z-10 flex h-5 items-center justify-center rounded-full bg-black px-2 text-xs font-medium whitespace-nowrap text-white`}
196
+ style={{
197
+ top: size + 4,
198
+ }}
199
+ >
200
+ <AnimatePresence mode="wait">
201
+ <motion.span
202
+ key={avatars[hoveredIndex].name}
203
+ {...getTooltipAnimation(hoveredIndex)}
204
+ >
205
+ {avatars[hoveredIndex].name}
206
+ </motion.span>
207
+ </AnimatePresence>
208
+ </motion.div>
209
+ )}
210
+ </AnimatePresence>
211
+
212
+ {extraFaces > 0 && !isExpanded && (
213
+ <motion.div
214
+ className="absolute z-0"
215
+ style={{
216
+ left: visibleFaces.length * offsetX,
217
+ }}
218
+ onMouseEnter={handleHoverExpand}
219
+ whileHover={{ scale: variant === 'interactive' ? 1.1 : 1 }}
220
+ >
221
+ <div
222
+ className={cn(
223
+ 'border-background ml-1.5 flex items-center justify-center rounded-full border-2 bg-gray-200 text-sm font-medium text-gray-600',
224
+ getResponsiveClasses(avatarSize, userAvatarSizeMapper)
225
+ )}
226
+ >
227
+ +{extraFaces}
228
+ </div>
229
+ </motion.div>
230
+ )}
231
+ </motion.div>
232
+ )
233
+ }
234
+
235
+ function AvatarWrapper({
236
+ avatar,
237
+ avatarSize,
238
+ index,
239
+ size,
240
+ offsetX,
241
+ hoveredIndex,
242
+ handleMouseEnter,
243
+ totalFaces,
244
+ isExpanded,
245
+ maxFaces,
246
+ interactive,
247
+ ...motionProps
248
+ }: {
249
+ avatar: AvatarProps
250
+ avatarSize?: ResponsiveValue<Size>
251
+ index: number
252
+ size: number
253
+ offsetX: number
254
+ hoveredIndex: number | null
255
+ handleMouseEnter: (index: number) => void
256
+ totalFaces: number
257
+ isExpanded: boolean
258
+ maxFaces: number
259
+ interactive: boolean
260
+ } & React.ComponentProps<typeof motion.div>) {
261
+ return (
262
+ <motion.div
263
+ className="absolute"
264
+ style={{
265
+ left: index * offsetX,
266
+ zIndex:
267
+ interactive && hoveredIndex === index
268
+ ? totalFaces + 1
269
+ : isExpanded
270
+ ? totalFaces - index
271
+ : index < maxFaces
272
+ ? maxFaces - index
273
+ : 0,
274
+ }}
275
+ whileHover={{ scale: interactive ? 1.1 : 1 }}
276
+ animate={{
277
+ x:
278
+ hoveredIndex === null || !interactive
279
+ ? 0
280
+ : hoveredIndex === index
281
+ ? 0
282
+ : hoveredIndex < index
283
+ ? size * 0.2
284
+ : -size * 0.2,
285
+ }}
286
+ transition={{ type: 'spring', stiffness: 300, damping: 20 }}
287
+ onMouseEnter={() => handleMouseEnter(index)}
288
+ {...motionProps}
289
+ >
290
+ {avatar.href ? (
291
+ <a href={avatar.href}>
292
+ <UserAvatar
293
+ name={avatar.name}
294
+ imageUrl={avatar.imageUrl}
295
+ size={avatarSize}
296
+ border
297
+ />
298
+ </a>
299
+ ) : (
300
+ <UserAvatar
301
+ name={avatar.name}
302
+ imageUrl={avatar.imageUrl}
303
+ size={avatarSize}
304
+ border
305
+ />
306
+ )}
307
+ </motion.div>
308
+ )
309
+ }
@@ -0,0 +1,34 @@
1
+ @reference '../../global.css';
2
+
3
+ @property --from-color {
4
+ syntax: '<color>';
5
+ inherits: false;
6
+ initial-value: red;
7
+ }
8
+
9
+ @property --to-color {
10
+ syntax: '<color>';
11
+ inherits: false;
12
+ initial-value: blue;
13
+ }
14
+
15
+ .gradient-circle {
16
+ box-sizing: border-box;
17
+ background: linear-gradient(var(--from-color), var(--to-color));
18
+ }
19
+
20
+ .gradient-circle-initial {
21
+ @apply text-5xl;
22
+ position: absolute;
23
+ top: 50%;
24
+ left: 50%;
25
+ transform: translate(var(--translate));
26
+ font-weight: bold;
27
+ color: white;
28
+ line-height: 1em;
29
+ text-align: center;
30
+ display: flex;
31
+ align-items: center;
32
+ text-shadow: 1px 0 5px rgb(0, 0, 0, 0.2);
33
+ justify-content: center;
34
+ }
@@ -0,0 +1,143 @@
1
+ import { cn } from '../../lib/utils'
2
+ import { Size } from '../../types'
3
+ import './gradientCircle.css'
4
+ import { useMemo } from 'react'
5
+
6
+ export interface GradientCircleProps {
7
+ name: string
8
+ size?: Size
9
+ transition?: boolean
10
+ showInitial?: boolean
11
+ inactive?: boolean
12
+ className?: string
13
+ }
14
+
15
+ const initialSizeMap: Record<Size, number> = {
16
+ small: 16,
17
+ medium: 24,
18
+ large: 24,
19
+ xl: 36,
20
+ '2xl': 48,
21
+ }
22
+
23
+ const sizeClasses = {
24
+ small: 'h-6 w-6 border-2',
25
+ medium: 'h-8 w-8 border-2',
26
+ large: 'h-10 w-10 border-4',
27
+ xl: 'h-12 w-12 border-4',
28
+ '2xl': 'h-20 w-20 border-4',
29
+ }
30
+
31
+ export function GradientCircle({
32
+ name,
33
+ size = 'small',
34
+ transition = false,
35
+ showInitial = false,
36
+ inactive = false,
37
+ className,
38
+ }: GradientCircleProps) {
39
+ const initial = useMemo<string | undefined>(
40
+ () => (name.length > 0 ? name[0].toUpperCase() : undefined),
41
+ [name]
42
+ )
43
+ // Define vibrant base colors (in HSL)
44
+ const baseColors = [
45
+ 0, // Red
46
+ 30, // Orange
47
+ 60, // Yellow
48
+ 120, // Green
49
+ 180, // Cyan
50
+ 210, // Blue
51
+ 270, // Purple
52
+ 300, // Pink
53
+ 330, // Magenta
54
+ ]
55
+
56
+ // Generate hash for color selection
57
+ const hash = name.split('').reduce((acc, char, i) => {
58
+ const charCode = char.charCodeAt(0)
59
+ return (acc * 31 + charCode * (i + 1)) >>> 0
60
+ }, 5381)
61
+
62
+ // Select base color from hash
63
+ const baseColorIndex = hash % baseColors.length
64
+ const baseHue = baseColors[baseColorIndex]
65
+
66
+ // Create harmonious color pair using analogous colors (30° apart)
67
+ const hue1 = baseHue
68
+ const hue2 = (baseHue + 30) % 360 // Wrap around 360 degrees
69
+
70
+ // Adjust saturation and lightness for better blending
71
+ // First color is more saturated, second is slightly lighter
72
+ const fromColor = `hsl(${hue1}, 85%, 60%)`
73
+ const toColor = `hsl(${hue2}, 75%, 65%)`
74
+
75
+ return (
76
+ <div
77
+ className={cn(
78
+ 'gradient-circle relative min-h-6 min-w-6 rounded-full border-white',
79
+ size && `${sizeClasses[size]}`,
80
+ inactive && 'opacity-50',
81
+ className
82
+ )}
83
+ style={
84
+ {
85
+ '--from-color': fromColor,
86
+ '--to-color': toColor,
87
+ transition: transition
88
+ ? '--from-color 0.5s, --to-color 2s'
89
+ : undefined,
90
+ } as React.CSSProperties
91
+ }
92
+ >
93
+ {showInitial && name.length > 0 && (
94
+ <div
95
+ className="gradient-circle-initial"
96
+ style={
97
+ {
98
+ '--translate': initial
99
+ ? getInitialTranslateY(initial)
100
+ : undefined,
101
+ fontSize: initialSizeMap[size],
102
+ } as React.CSSProperties
103
+ }
104
+ >
105
+ {name[0].toUpperCase()}
106
+ </div>
107
+ )}
108
+ </div>
109
+ )
110
+ }
111
+
112
+ /**
113
+ * every letter has a different central focal point
114
+ * so may need some adjustment in order to center them
115
+ */
116
+ function getInitialTranslateY(letter: string): [string, string] {
117
+ switch (letter) {
118
+ case 'A':
119
+ return ['-50%', '-55%']
120
+ case 'B':
121
+ return ['-50%', '-55%']
122
+ case 'C':
123
+ return ['-54%', '-50%']
124
+ case 'D':
125
+ return ['-46%', '-50%']
126
+ case 'E':
127
+ return ['-52%', '-50%']
128
+ case 'F':
129
+ return ['-50%', '-52%']
130
+ case 'G':
131
+ return ['-52%', '-52%']
132
+ case 'J':
133
+ return ['-55%', '-52%']
134
+ case 'L':
135
+ return ['-47%', '-54%']
136
+ case 'Q':
137
+ return ['-53%', '-55%']
138
+ case 'R':
139
+ return ['-52%', '-55%']
140
+ default:
141
+ return ['-50%', '-50%']
142
+ }
143
+ }
@@ -0,0 +1,150 @@
1
+ import {
2
+ colSpanMapper,
3
+ gapMapper,
4
+ paddingMapper,
5
+ } from '../../lib/responsiveMappers'
6
+ import { cn, getResponsiveClasses } from '../../lib/utils'
7
+ import { Columns, Gap, Padding, ResponsiveValue } from '../../types'
8
+ import { isValidElement, ReactElement } from 'react'
9
+
10
+ export interface GridProps {
11
+ /**
12
+ * The number of columns in the grid.
13
+ * @default 1
14
+ * @example Default
15
+ * <Grid columns={2}>
16
+ * <Grid.Item>Item 1</Grid.Item>
17
+ * <Grid.Item>Item 2</Grid.Item>
18
+ * </Grid>
19
+ * @example With responsive columns
20
+ * <Grid columns={{ xs: 1, md: 2 }}>
21
+ * <Grid.Item>Item 1</Grid.Item>
22
+ * <Grid.Item>Item 2</Grid.Item>
23
+ * </Grid>
24
+ */
25
+ columns?: ResponsiveValue<Columns>
26
+
27
+ /**
28
+ * The gap between the grid items.
29
+ * @default 0
30
+ * @example Default
31
+ * <Grid gap={4}>
32
+ * <Grid.Item>Item 1</Grid.Item>
33
+ * <Grid.Item>Item 2</Grid.Item>
34
+ * </Grid>
35
+ * @example With responsive gap
36
+ * <Grid gap={{ xs: 2, md: 4 }}>
37
+ * <Grid.Item>Item 1</Grid.Item>
38
+ * <Grid.Item>Item 2</Grid.Item>
39
+ * </Grid>
40
+ */
41
+ gap?: ResponsiveValue<Gap>
42
+
43
+ /**
44
+ * The Grid.Item children of the grid.
45
+ * @example
46
+ * <Grid>
47
+ * <Grid.Item>Item 1</Grid.Item>
48
+ * <Grid.Item>Item 2</Grid.Item>
49
+ * </Grid>
50
+ */
51
+ children: Array<ReactElement<typeof GridItem>>
52
+
53
+ /**
54
+ * If true, the grid will wrap its children.
55
+ * @default false
56
+ */
57
+ wrap?: boolean
58
+
59
+ /**
60
+ * Can be an object of responsive Padding values, or just Padding values.
61
+ *
62
+ * @example Simple Padding
63
+ * padding: 10
64
+ *
65
+ * @example Responsive Padding
66
+ * padding: { xs: 10, md: 20, lg: 30, xl: 40 }
67
+ *
68
+ * @example Padding per side
69
+ * padding: { top: 10, right: 0, bottom: 10, left: 0 }
70
+ *
71
+ * @example Responsive Padding per side (just x and y axis)
72
+ * padding: { xs: 0, md: 0, lg: 0, xl: { x: 10, y: 12 } }
73
+ *
74
+ * @example Responsive Padding per side with different values for each side
75
+ * padding: { xs: 0, md: 0, lg: 0, xl: { top: 10, right: 0, bottom: 10, left: 0 } }
76
+ */
77
+ padding?: ResponsiveValue<Padding>
78
+
79
+ className?: string
80
+ }
81
+
82
+ const columnsMapper = (columns: Columns) => `grid-cols-${columns}`
83
+
84
+ const isValidGridChild = (child: ReactElement) =>
85
+ isValidElement(child) && child.type === GridItem
86
+
87
+ const Grid = ({
88
+ children,
89
+ columns = 1,
90
+ gap = 0,
91
+ wrap = true,
92
+ padding = 0,
93
+ className,
94
+ }: GridProps) => {
95
+ const validGridChildren = children.filter(isValidGridChild)
96
+ return (
97
+ <div
98
+ className={cn(
99
+ 'grid',
100
+ getResponsiveClasses(columns, columnsMapper),
101
+ getResponsiveClasses(gap, gapMapper),
102
+ !wrap && 'grid-flow-col',
103
+ getResponsiveClasses(padding, paddingMapper),
104
+ className
105
+ )}
106
+ >
107
+ {validGridChildren}
108
+ </div>
109
+ )
110
+ }
111
+
112
+ type PermittedHTMLAttributes = Omit<
113
+ React.HTMLAttributes<HTMLDivElement>,
114
+ 'className'
115
+ >
116
+
117
+ interface GridItemProps extends PermittedHTMLAttributes {
118
+ children: React.ReactNode
119
+ colSpan?: ResponsiveValue<number>
120
+ padding?: ResponsiveValue<Padding>
121
+ className?: string
122
+ }
123
+
124
+ const GridItem = ({
125
+ children,
126
+ colSpan,
127
+ padding,
128
+ className,
129
+ ...props
130
+ }: GridItemProps) => {
131
+ return (
132
+ <div
133
+ className={cn(
134
+ 'grid-item',
135
+ colSpan && getResponsiveClasses(colSpan, colSpanMapper),
136
+ padding && getResponsiveClasses(padding, paddingMapper),
137
+ className
138
+ )}
139
+ {...props}
140
+ >
141
+ {children}
142
+ </div>
143
+ )
144
+ }
145
+
146
+ const GridWithSubcomponents = Object.assign(Grid, {
147
+ Item: GridItem,
148
+ })
149
+
150
+ export { GridWithSubcomponents as Grid }