@placeholderco/placeholder-ui 1.0.11 → 2.0.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 (289) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +134 -130
  3. package/dist/app.css +178 -0
  4. package/dist/bootstrap.min.css +3600 -0
  5. package/dist/cards/GitCard.svelte +115 -0
  6. package/dist/cards/GitCard.svelte.d.ts +22 -0
  7. package/dist/display/Avatar.svelte +29 -17
  8. package/dist/display/Avatar.svelte.d.ts +12 -2
  9. package/dist/display/LinkCollection.svelte +40 -18
  10. package/dist/display/LinkCollection.svelte.d.ts +9 -2
  11. package/dist/display/Paper.svelte +31 -19
  12. package/dist/display/Paper.svelte.d.ts +12 -3
  13. package/dist/display/alert/Alert.svelte +129 -0
  14. package/dist/display/alert/Alert.svelte.d.ts +15 -0
  15. package/dist/display/alert/AlertVariant.d.ts +1 -0
  16. package/dist/display/alert/AlertVariant.js +1 -0
  17. package/dist/display/filetree/FileTree.svelte +259 -0
  18. package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
  19. package/dist/display/filetree/FileTreeItem.svelte +396 -0
  20. package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
  21. package/dist/display/filetree/FileTreeNode.d.ts +46 -0
  22. package/dist/display/filetree/FileTreeNode.js +115 -0
  23. package/dist/display/filetree/index.d.ts +5 -0
  24. package/dist/display/filetree/index.js +3 -0
  25. package/dist/editors/CodeMirror.svelte +306 -0
  26. package/dist/editors/CodeMirror.svelte.d.ts +31 -0
  27. package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
  28. package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
  29. package/dist/editors/tiptap/TipTap.svelte +856 -0
  30. package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
  31. package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
  32. package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
  33. package/dist/editors/tiptap/collaboration.d.ts +55 -0
  34. package/dist/editors/tiptap/collaboration.js +1 -0
  35. package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
  36. package/dist/editors/tiptap/extensions/Alert.js +72 -0
  37. package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
  38. package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
  39. package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
  40. package/dist/editors/tiptap/extensions/Div.js +89 -0
  41. package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
  42. package/dist/editors/tiptap/extensions/Span.js +56 -0
  43. package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
  44. package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
  45. package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
  46. package/dist/editors/tiptap/extensions/index.d.ts +7 -0
  47. package/dist/editors/tiptap/extensions/index.js +5 -0
  48. package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
  49. package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
  50. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
  51. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
  52. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
  53. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
  54. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
  55. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
  56. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
  57. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
  58. package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
  59. package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
  60. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
  61. package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
  62. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
  63. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
  64. package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
  65. package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
  66. package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
  67. package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
  68. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
  69. package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
  70. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
  71. package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
  72. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
  73. package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
  74. package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
  75. package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
  76. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
  77. package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
  78. package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
  79. package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
  80. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
  81. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
  82. package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
  83. package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
  84. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
  85. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
  86. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
  87. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
  88. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
  89. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
  90. package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
  91. package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
  92. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
  93. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
  94. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
  95. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
  96. package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
  97. package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
  98. package/dist/form/Autocomplete.svelte +270 -137
  99. package/dist/form/Autocomplete.svelte.d.ts +58 -10
  100. package/dist/form/AutocompleteMulti.svelte +385 -227
  101. package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
  102. package/dist/form/Checkbox.svelte +124 -43
  103. package/dist/form/Checkbox.svelte.d.ts +26 -2
  104. package/dist/form/Chips.svelte +47 -17
  105. package/dist/form/Chips.svelte.d.ts +24 -3
  106. package/dist/form/ComboBox.svelte +30 -20
  107. package/dist/form/ComboBox.svelte.d.ts +1 -1
  108. package/dist/form/ComboBoxItemBuilder.svelte +161 -188
  109. package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
  110. package/dist/form/ComboBoxMulti.svelte +38 -24
  111. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  112. package/dist/form/CronBuilder.svelte +84 -67
  113. package/dist/form/CronBuilder.svelte.d.ts +16 -3
  114. package/dist/form/DatePicker.svelte +177 -191
  115. package/dist/form/DatePicker.svelte.d.ts +17 -2
  116. package/dist/form/DateRangePicker.svelte +775 -0
  117. package/dist/form/DateRangePicker.svelte.d.ts +23 -0
  118. package/dist/form/DateTimePicker.svelte +168 -209
  119. package/dist/form/DateTimePicker.svelte.d.ts +10 -2
  120. package/dist/form/FormGroup.svelte +33 -10
  121. package/dist/form/FormGroup.svelte.d.ts +12 -2
  122. package/dist/form/Number.svelte +71 -29
  123. package/dist/form/Number.svelte.d.ts +39 -6
  124. package/dist/form/Radio.svelte +174 -0
  125. package/dist/form/Radio.svelte.d.ts +21 -0
  126. package/dist/form/RadioGroup.svelte +52 -138
  127. package/dist/form/RadioGroup.svelte.d.ts +19 -2
  128. package/dist/form/SegmentedControl.svelte +103 -57
  129. package/dist/form/SegmentedControl.svelte.d.ts +12 -2
  130. package/dist/form/Select.svelte +160 -229
  131. package/dist/form/Select.svelte.d.ts +35 -6
  132. package/dist/form/SelectMulti.svelte +166 -277
  133. package/dist/form/SelectMulti.svelte.d.ts +37 -6
  134. package/dist/form/Slider.svelte +185 -268
  135. package/dist/form/Slider.svelte.d.ts +33 -16
  136. package/dist/form/StringArrayBuilder.svelte +249 -0
  137. package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
  138. package/dist/form/Switch.svelte +164 -106
  139. package/dist/form/Switch.svelte.d.ts +22 -3
  140. package/dist/form/TextArea.svelte +80 -45
  141. package/dist/form/TextArea.svelte.d.ts +35 -10
  142. package/dist/form/Textbox.svelte +80 -47
  143. package/dist/form/Textbox.svelte.d.ts +41 -8
  144. package/dist/form/TimePicker.svelte +42 -47
  145. package/dist/form/TimePicker.svelte.d.ts +12 -2
  146. package/dist/form/comboBoxCore.svelte.d.ts +99 -0
  147. package/dist/form/comboBoxCore.svelte.js +243 -0
  148. package/dist/icon/Icon.svelte +8 -3
  149. package/dist/icon/Icon.svelte.d.ts +8 -3
  150. package/dist/icon/cube-send.svg +1 -0
  151. package/dist/icon/file-type-bmp.svg +1 -0
  152. package/dist/icon/file-type-csv.svg +1 -0
  153. package/dist/icon/file-type-doc.svg +1 -0
  154. package/dist/icon/file-type-docx.svg +1 -0
  155. package/dist/icon/file-type-jpg.svg +1 -0
  156. package/dist/icon/file-type-pdf.svg +1 -0
  157. package/dist/icon/file-type-png.svg +1 -0
  158. package/dist/icon/file-type-ppt.svg +1 -0
  159. package/dist/icon/file-type-svg.svg +1 -0
  160. package/dist/icon/file-type-txt.svg +1 -0
  161. package/dist/icon/file-type-xls.svg +1 -0
  162. package/dist/icon/file-type-zip.svg +1 -0
  163. package/dist/icon/index.d.ts +166 -145
  164. package/dist/icon/index.js +166 -146
  165. package/dist/icon/layout-sidebar-left-collapse.svg +1 -0
  166. package/dist/icon/layout-sidebar-left-expand.svg +1 -0
  167. package/dist/icon/logo-placeholder.svg +6 -0
  168. package/dist/icon/paperclip.svg +1 -0
  169. package/dist/icon/source-code.svg +1 -0
  170. package/dist/icon/sun-moon.svg +1 -0
  171. package/dist/icon/table.svg +6 -0
  172. package/dist/icon/thumb-down.svg +1 -0
  173. package/dist/icon/thumb-up.svg +1 -0
  174. package/dist/index.d.ts +93 -22
  175. package/dist/index.js +28 -27
  176. package/dist/layout/AppShell.svelte +12 -162
  177. package/dist/layout/AppShell.svelte.d.ts +6 -27
  178. package/dist/layout/CustomNavbar.svelte +9 -7
  179. package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
  180. package/dist/layout/Navbar.svelte +158 -110
  181. package/dist/layout/Navbar.svelte.d.ts +26 -12
  182. package/dist/layout/NavbarItemDisplay.svelte +2 -2
  183. package/dist/layout/NavbarItemView.svelte +182 -0
  184. package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
  185. package/dist/layout/Sidenav.svelte +280 -341
  186. package/dist/layout/Sidenav.svelte.d.ts +36 -4
  187. package/dist/models/ComboBoxItem.d.ts +2 -0
  188. package/dist/models/Employee.d.ts +53 -0
  189. package/dist/models/Employee.js +23 -0
  190. package/dist/models/Hyperlink.d.ts +3 -1
  191. package/dist/models/LinkCollectionItem.d.ts +3 -1
  192. package/dist/models/NavbarItem.d.ts +6 -2
  193. package/dist/theme.svelte.d.ts +0 -1
  194. package/dist/theme.svelte.js +46 -58
  195. package/dist/ui/Accordion.svelte +81 -242
  196. package/dist/ui/Accordion.svelte.d.ts +37 -14
  197. package/dist/ui/AccordionItem.svelte +303 -0
  198. package/dist/ui/AccordionItem.svelte.d.ts +21 -0
  199. package/dist/ui/ActionIcon.svelte +25 -21
  200. package/dist/ui/ActionIcon.svelte.d.ts +15 -2
  201. package/dist/ui/Badge.svelte +170 -161
  202. package/dist/ui/Badge.svelte.d.ts +16 -4
  203. package/dist/ui/Button.svelte +178 -205
  204. package/dist/ui/Button.svelte.d.ts +19 -2
  205. package/dist/ui/ButtonVariant.d.ts +15 -1
  206. package/dist/ui/ContextMenu.svelte +146 -0
  207. package/dist/ui/ContextMenu.svelte.d.ts +37 -0
  208. package/dist/ui/ContextMenuList.svelte +188 -0
  209. package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
  210. package/dist/ui/Dialog.svelte +183 -104
  211. package/dist/ui/Dialog.svelte.d.ts +21 -2
  212. package/dist/ui/Drawer.svelte +237 -184
  213. package/dist/ui/Drawer.svelte.d.ts +20 -2
  214. package/dist/ui/Dropdown.svelte +24 -14
  215. package/dist/ui/Dropdown.svelte.d.ts +11 -2
  216. package/dist/ui/Dropzone.svelte +159 -57
  217. package/dist/ui/Dropzone.svelte.d.ts +29 -2
  218. package/dist/ui/Link.svelte +12 -4
  219. package/dist/ui/Link.svelte.d.ts +8 -2
  220. package/dist/ui/Loader.svelte +23 -18
  221. package/dist/ui/Loader.svelte.d.ts +5 -2
  222. package/dist/ui/Logo.svelte +37 -0
  223. package/dist/ui/Logo.svelte.d.ts +16 -0
  224. package/dist/ui/MultiSortable.svelte +287 -0
  225. package/dist/ui/MultiSortable.svelte.d.ts +50 -0
  226. package/dist/ui/Pagination.svelte +12 -6
  227. package/dist/ui/Pagination.svelte.d.ts +6 -2
  228. package/dist/ui/Popover.svelte +43 -65
  229. package/dist/ui/Popover.svelte.d.ts +14 -5
  230. package/dist/ui/ProgressBar.svelte +169 -0
  231. package/dist/ui/ProgressBar.svelte.d.ts +30 -0
  232. package/dist/ui/ProgressBarVariant.d.ts +7 -0
  233. package/dist/ui/ProgressBarVariant.js +1 -0
  234. package/dist/ui/SegmentedControl.svelte +0 -0
  235. package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
  236. package/dist/ui/Sortable.svelte +163 -0
  237. package/dist/ui/Sortable.svelte.d.ts +47 -0
  238. package/dist/ui/Table.svelte +497 -56
  239. package/dist/ui/Table.svelte.d.ts +64 -2
  240. package/dist/ui/Tabs.svelte +22 -18
  241. package/dist/ui/Tabs.svelte.d.ts +6 -2
  242. package/dist/ui/ThemeSwitcher.svelte +26 -7
  243. package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
  244. package/dist/ui/Tooltip.svelte +312 -180
  245. package/dist/ui/Tooltip.svelte.d.ts +4 -3
  246. package/dist/uiTheme.svelte.d.ts +86 -0
  247. package/dist/uiTheme.svelte.js +214 -0
  248. package/dist/util/DateFunctions.js +1 -1
  249. package/dist/util/DragDrop.d.ts +50 -0
  250. package/dist/util/DragDrop.js +154 -0
  251. package/dist/util/NavigateTo.js +2 -2
  252. package/dist/util/dayjs.d.ts +4 -0
  253. package/dist/util/dayjs.js +15 -0
  254. package/dist/util/interceptLinkClick.d.ts +13 -0
  255. package/dist/util/interceptLinkClick.js +33 -0
  256. package/package.json +66 -26
  257. package/dist/display/Alert.svelte +0 -179
  258. package/dist/display/Alert.svelte.d.ts +0 -13
  259. package/dist/form/FileInput.svelte +0 -235
  260. package/dist/form/FileInput.svelte.d.ts +0 -23
  261. package/dist/form/PasswordInput.svelte +0 -252
  262. package/dist/form/PasswordInput.svelte.d.ts +0 -33
  263. package/dist/form/Rating.svelte +0 -235
  264. package/dist/form/Rating.svelte.d.ts +0 -21
  265. package/dist/styles/components.css +0 -199
  266. package/dist/styles/dark.css +0 -146
  267. package/dist/styles/index.css +0 -116
  268. package/dist/styles/reset.css +0 -110
  269. package/dist/styles/semantic.css +0 -86
  270. package/dist/styles/tokens.css +0 -203
  271. package/dist/styles/utilities.css +0 -523
  272. package/dist/ui/Breadcrumbs.svelte +0 -131
  273. package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
  274. package/dist/ui/LoadingOverlay.svelte +0 -53
  275. package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
  276. package/dist/ui/Progress.svelte +0 -191
  277. package/dist/ui/Progress.svelte.d.ts +0 -15
  278. package/dist/ui/RingProgress.svelte +0 -141
  279. package/dist/ui/RingProgress.svelte.d.ts +0 -18
  280. package/dist/ui/Skeleton.svelte +0 -85
  281. package/dist/ui/Skeleton.svelte.d.ts +0 -12
  282. package/dist/ui/Stepper.svelte +0 -355
  283. package/dist/ui/Stepper.svelte.d.ts +0 -24
  284. package/dist/ui/Timeline.svelte +0 -225
  285. package/dist/ui/Timeline.svelte.d.ts +0 -23
  286. /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
  287. /package/dist/{actions → util}/ClickOutside.js +0 -0
  288. /package/dist/{transitions → util}/Transitions.d.ts +0 -0
  289. /package/dist/{transitions → util}/Transitions.js +0 -0
@@ -2,7 +2,23 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import { fade } from 'svelte/transition';
4
4
 
5
- interface Props {
5
+ export type TooltipLocation =
6
+ | 'top'
7
+ | 'right'
8
+ | 'bottom'
9
+ | 'left'
10
+ | 'top-start'
11
+ | 'top-end'
12
+ | 'right-start'
13
+ | 'right-end'
14
+ | 'bottom-start'
15
+ | 'bottom-end'
16
+ | 'left-start'
17
+ | 'left-end';
18
+
19
+ type Side = 'top' | 'right' | 'bottom' | 'left';
20
+
21
+ export interface TooltipProps {
6
22
  /** The element to attach the tooltip to */
7
23
  children: Snippet;
8
24
  /** Rich content using a Svelte snippet */
@@ -12,7 +28,7 @@
12
28
  /** HTML content (use with caution - sanitize user input) */
13
29
  html?: string;
14
30
  /** Preferred tooltip position */
15
- location?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
31
+ location?: TooltipLocation;
16
32
  /** Maximum width of tooltip */
17
33
  maxWidth?: string;
18
34
  /** Offset distance from target element (px) */
@@ -39,33 +55,50 @@
39
55
  delay = 0,
40
56
  onOpen,
41
57
  disabled = false
42
- }: Props = $props();
58
+ }: TooltipProps = $props();
59
+
60
+ /** Half the arrow's base and its full height — the triangle is 2x wide, 1x tall. */
61
+ const ARROW_SIZE = 6;
62
+ /** Matches .tooltip-v2-content border-radius so the arrow never sits on a rounded corner. */
63
+ const CORNER_RADIUS = 6;
64
+ /** Minimum gap between the tooltip and the viewport edge. */
65
+ const VIEWPORT_PADDING = 5;
66
+
67
+ const OPPOSITE_SIDE: Record<Side, Side> = {
68
+ top: 'bottom',
69
+ bottom: 'top',
70
+ left: 'right',
71
+ right: 'left'
72
+ };
43
73
 
44
74
  // State
45
75
  let open = $state(false);
76
+ /** False until the tooltip has been measured and placed; it stays hidden until then. */
77
+ let positioned = $state(false);
46
78
  let referenceElement: HTMLElement | null = $state(null);
47
79
  let tooltipElement: HTMLElement | null = $state(null);
48
- let arrowElement: HTMLElement | null = $state(null);
49
80
  let timeoutId: number | null = null;
81
+ let rafId: number | null = null;
82
+ /** Ancestor the tooltip's left/top actually resolve against, if not the viewport. */
83
+ let fixedContainer: HTMLElement | null = null;
50
84
 
51
85
  // Position state
52
- let tooltipStyle = $state('');
86
+ let tooltipStyle = $state('left: 0px; top: 0px;');
53
87
  let arrowStyle = $state('');
54
- let actualPlacement = $state<'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end'>('top');
55
-
56
- // Sync actualPlacement with location prop
57
- $effect(() => {
58
- actualPlacement = location;
59
- });
88
+ let actualPlacement = $state<TooltipLocation>('top');
60
89
 
61
90
  // Check if tooltip has any content
62
91
  const hasContent = $derived(!!(text || html || tooltipContent));
92
+ const visible = $derived(open && hasContent && !disabled);
63
93
 
64
94
  function handleMouseEnter() {
65
95
  if (disabled || !hasContent) return;
66
96
 
97
+ cancelPending();
98
+
67
99
  if (delay > 0) {
68
100
  timeoutId = window.setTimeout(() => {
101
+ timeoutId = null;
69
102
  showTooltip();
70
103
  }, delay);
71
104
  } else {
@@ -74,65 +107,175 @@
74
107
  }
75
108
 
76
109
  function handleMouseLeave() {
77
- if (timeoutId) {
110
+ cancelPending();
111
+ hideTooltip();
112
+ }
113
+
114
+ function cancelPending() {
115
+ if (timeoutId !== null) {
78
116
  clearTimeout(timeoutId);
79
117
  timeoutId = null;
80
118
  }
81
- hideTooltip();
82
119
  }
83
120
 
84
121
  function showTooltip() {
122
+ if (open) return;
123
+
124
+ // The floating element is remounted on every open, so it starts unmeasured.
125
+ // Keeping it hidden until the positioning effect has run stops the fade-in
126
+ // from playing at the coordinates left over from the previous open.
127
+ positioned = false;
85
128
  open = true;
86
129
  onOpen?.(true);
87
-
88
- // Position tooltip after it's rendered
89
- // Use multiple RAF calls and verify dimensions to ensure layout is complete
90
- requestAnimationFrame(() => {
91
- requestAnimationFrame(() => {
92
- if (referenceElement && tooltipElement) {
93
- positionTooltip();
94
-
95
- // Verify and reposition if dimensions changed
96
- requestAnimationFrame(() => {
97
- if (tooltipElement) {
98
- const rect = tooltipElement.getBoundingClientRect();
99
- if (rect.width > 0 && rect.height > 0) {
100
- positionTooltip();
101
- }
102
- }
103
- });
104
- }
105
- });
106
- });
107
130
  }
108
131
 
109
132
  function hideTooltip() {
133
+ if (!open) return;
134
+
110
135
  open = false;
111
136
  onOpen?.(false);
112
137
  }
113
138
 
139
+ function clamp(value: number, min: number, max: number): number {
140
+ return Math.max(min, Math.min(value, max));
141
+ }
142
+
143
+ /**
144
+ * A transform, filter, perspective, containment or container-type on an ancestor
145
+ * makes that ancestor the containing block for `position: fixed` descendants —
146
+ * left/top stop meaning "from the viewport" while getBoundingClientRect() keeps
147
+ * reporting viewport coordinates. Dialog hits this: its open animation uses a
148
+ * `forwards` fill, which leaves `transform: translateY(0)` applied while open.
149
+ */
150
+ function findFixedContainingBlock(element: HTMLElement): HTMLElement | null {
151
+ let node = element.parentElement;
152
+
153
+ while (node) {
154
+ const style = getComputedStyle(node);
155
+ // Newer properties go through getPropertyValue so an older DOM lib (or a
156
+ // browser without support) reports '' instead of failing to compile.
157
+ const containerType = style.getPropertyValue('container-type');
158
+ const backdropFilter =
159
+ style.getPropertyValue('backdrop-filter') ||
160
+ style.getPropertyValue('-webkit-backdrop-filter');
161
+
162
+ if (
163
+ style.transform !== 'none' ||
164
+ style.perspective !== 'none' ||
165
+ style.filter !== 'none' ||
166
+ (backdropFilter !== '' && backdropFilter !== 'none') ||
167
+ (containerType !== '' && containerType !== 'normal') ||
168
+ /\b(paint|layout|strict|content)\b/.test(style.contain) ||
169
+ /\b(transform|perspective|filter)\b/.test(style.willChange)
170
+ ) {
171
+ return node;
172
+ }
173
+
174
+ node = node.parentElement;
175
+ }
176
+
177
+ return null;
178
+ }
179
+
180
+ /** Viewport coordinates of the origin that left/top are measured from. */
181
+ function containingBlockOrigin(): { x: number; y: number } {
182
+ if (!fixedContainer) return { x: 0, y: 0 };
183
+
184
+ const rect = fixedContainer.getBoundingClientRect();
185
+ const style = getComputedStyle(fixedContainer);
186
+
187
+ // Fixed descendants resolve against the padding box, so drop the border.
188
+ return {
189
+ x: rect.left + (parseFloat(style.borderLeftWidth) || 0),
190
+ y: rect.top + (parseFloat(style.borderTopWidth) || 0)
191
+ };
192
+ }
193
+
114
194
  function positionTooltip() {
195
+ // Read the geometry props before any early return, so that calling this from
196
+ // an effect always registers them as dependencies and a prop changed while
197
+ // the tooltip is open re-anchors it.
198
+ const preferred = location;
199
+ const offset = offsetDistance + (showArrow ? ARROW_SIZE : 0);
200
+
115
201
  if (!referenceElement || !tooltipElement) return;
116
202
 
117
203
  const refRect = referenceElement.getBoundingClientRect();
118
204
  const tooltipRect = tooltipElement.getBoundingClientRect();
119
- const arrowSize = showArrow ? 12 : 0;
120
- const offset = offsetDistance + arrowSize;
121
205
 
122
- let position = calculatePosition(location, refRect, tooltipRect, offset);
206
+ // A zero-sized box means layout has not happened yet; anchoring to it would
207
+ // pin the tooltip to dimensions that are about to change.
208
+ if (tooltipRect.width === 0 || tooltipRect.height === 0) return;
209
+
210
+ const { position, placement } = resolvePlacement(preferred, refRect, tooltipRect, offset);
123
211
 
124
- // Check if tooltip would overflow viewport and flip if needed
125
- const flippedPosition = checkAndFlipPosition(position, tooltipRect, location, refRect, offset);
126
- position = flippedPosition.position;
127
- actualPlacement = flippedPosition.placement as typeof actualPlacement;
212
+ // `position` is in viewport space, which is what the arrow maths below wants
213
+ // too. Only the style needs translating into the containing block's space.
214
+ const origin = containingBlockOrigin();
215
+
216
+ actualPlacement = placement;
217
+ tooltipStyle = `left: ${Math.round(position.left - origin.x)}px; top: ${Math.round(position.top - origin.y)}px;`;
218
+ arrowStyle = showArrow ? calculateArrowPosition(placement, refRect, position, tooltipRect) : '';
219
+ positioned = true;
220
+ }
128
221
 
129
- // Apply position
130
- tooltipStyle = `left: ${position.left}px; top: ${position.top}px;`;
222
+ /** Coalesce bursts of scroll/resize events into one reposition per frame. */
223
+ function schedulePosition() {
224
+ if (rafId !== null) return;
131
225
 
132
- // Position arrow
133
- if (showArrow && arrowElement) {
134
- arrowStyle = calculateArrowPosition(actualPlacement, refRect, position);
226
+ rafId = requestAnimationFrame(() => {
227
+ rafId = null;
228
+ positionTooltip();
229
+ });
230
+ }
231
+
232
+ function resolvePlacement(
233
+ preferred: TooltipLocation,
234
+ refRect: DOMRect,
235
+ tooltipRect: DOMRect,
236
+ offset: number
237
+ ): { position: { left: number; top: number }; placement: TooltipLocation } {
238
+ // clientWidth/Height exclude the scrollbars, which window.innerWidth includes.
239
+ const viewportWidth = document.documentElement.clientWidth;
240
+ const viewportHeight = document.documentElement.clientHeight;
241
+
242
+ const [side, alignment] = preferred.split('-') as [Side, string | undefined];
243
+
244
+ const space = {
245
+ top: refRect.top - VIEWPORT_PADDING,
246
+ bottom: viewportHeight - refRect.bottom - VIEWPORT_PADDING,
247
+ left: refRect.left - VIEWPORT_PADDING,
248
+ right: viewportWidth - refRect.right - VIEWPORT_PADDING
249
+ };
250
+ const required =
251
+ side === 'top' || side === 'bottom'
252
+ ? tooltipRect.height + offset
253
+ : tooltipRect.width + offset;
254
+
255
+ // Flip only when the preferred side cannot fit *and* the opposite side is
256
+ // roomier — flipping into an equally cramped side just moves the problem.
257
+ let chosen = side;
258
+ if (space[side] < required && space[OPPOSITE_SIDE[side]] > space[side]) {
259
+ chosen = OPPOSITE_SIDE[side];
135
260
  }
261
+
262
+ const placement = (alignment ? `${chosen}-${alignment}` : chosen) as TooltipLocation;
263
+ const position = calculatePosition(placement, refRect, tooltipRect, offset);
264
+
265
+ // Shift back inside the viewport. The arrow is clamped separately so it keeps
266
+ // pointing at the trigger after the box slides away from it.
267
+ position.left = clamp(
268
+ position.left,
269
+ VIEWPORT_PADDING,
270
+ viewportWidth - tooltipRect.width - VIEWPORT_PADDING
271
+ );
272
+ position.top = clamp(
273
+ position.top,
274
+ VIEWPORT_PADDING,
275
+ viewportHeight - tooltipRect.height - VIEWPORT_PADDING
276
+ );
277
+
278
+ return { position, placement };
136
279
  }
137
280
 
138
281
  function calculatePosition(
@@ -179,174 +322,150 @@
179
322
  return { left, top };
180
323
  }
181
324
 
182
- function checkAndFlipPosition(
183
- position: { left: number; top: number },
184
- tooltipRect: DOMRect,
185
- placement: string,
186
- refRect: DOMRect,
187
- offset: number
188
- ): { position: { left: number; top: number }; placement: string } {
189
- const viewportWidth = window.innerWidth;
190
- const viewportHeight = window.innerHeight;
191
- const padding = 5;
192
-
193
- let newPosition = { ...position };
194
- let newPlacement = placement;
195
- const [side] = placement.split('-');
196
-
197
- // Check vertical overflow
198
- if (side === 'top' && position.top < padding) {
199
- // Flip to bottom
200
- newPlacement = placement.replace('top', 'bottom');
201
- newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
202
- } else if (side === 'bottom' && position.top + tooltipRect.height > viewportHeight - padding) {
203
- // Flip to top
204
- newPlacement = placement.replace('bottom', 'top');
205
- newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
206
- }
207
-
208
- // Check horizontal overflow
209
- if (side === 'left' && position.left < padding) {
210
- // Flip to right
211
- newPlacement = placement.replace('left', 'right');
212
- newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
213
- } else if (side === 'right' && position.left + tooltipRect.width > viewportWidth - padding) {
214
- // Flip to left
215
- newPlacement = placement.replace('right', 'left');
216
- newPosition = calculatePosition(newPlacement, refRect, tooltipRect, offset);
217
- }
218
-
219
- // Shift within bounds if still overflowing
220
- if (newPosition.left < padding) {
221
- newPosition.left = padding;
222
- } else if (newPosition.left + tooltipRect.width > viewportWidth - padding) {
223
- newPosition.left = viewportWidth - tooltipRect.width - padding;
224
- }
225
-
226
- if (newPosition.top < padding) {
227
- newPosition.top = padding;
228
- } else if (newPosition.top + tooltipRect.height > viewportHeight - padding) {
229
- newPosition.top = viewportHeight - tooltipRect.height - padding;
230
- }
231
-
232
- return { position: newPosition, placement: newPlacement };
233
- }
234
-
235
325
  function calculateArrowPosition(
236
- placement: string,
326
+ placement: TooltipLocation,
237
327
  refRect: DOMRect,
238
- tooltipPos: { left: number; top: number }
328
+ tooltipPos: { left: number; top: number },
329
+ tooltipRect: DOMRect
239
330
  ): string {
240
331
  const [side] = placement.split('-');
241
332
 
242
- const refCenter = {
243
- x: refRect.left + refRect.width / 2,
244
- y: refRect.top + refRect.height / 2
245
- };
333
+ const refCenterX = refRect.left + refRect.width / 2;
334
+ const refCenterY = refRect.top + refRect.height / 2;
246
335
 
247
- const tooltipHeight = tooltipElement?.offsetHeight || 0;
248
- const tooltipWidth = tooltipElement?.offsetWidth || 0;
336
+ // Aim at the trigger's centre, but never past the tooltip's rounded corners —
337
+ // an unclamped arrow floats off the box once the tooltip has been shifted.
338
+ const inset = CORNER_RADIUS + ARROW_SIZE;
339
+ const arrowX = clamp(refCenterX - tooltipPos.left, inset, tooltipRect.width - inset);
340
+ const arrowY = clamp(refCenterY - tooltipPos.top, inset, tooltipRect.height - inset);
249
341
 
250
- // Calculate arrow center position relative to tooltip
251
- const arrowX = refCenter.x - tooltipPos.left;
252
- const arrowY = refCenter.y - tooltipPos.top;
342
+ const transparent = `${ARROW_SIZE}px solid transparent`;
343
+ const filled = `${ARROW_SIZE}px solid var(--tooltip-bg)`;
344
+ // Negative margin centres the triangle on the aim point; the 100% offsets sit
345
+ // it flush against the edge so it spans the whole gap to the trigger.
346
+ const centreX = `margin-left: ${-ARROW_SIZE}px;`;
347
+ const centreY = `margin-top: ${-ARROW_SIZE}px;`;
253
348
 
254
349
  switch (side) {
255
350
  case 'top':
256
- // Arrow at bottom edge, pointing down
257
- return `
258
- left: ${arrowX}px;
259
- top: ${tooltipHeight}px;
260
- border-left: 12px solid transparent;
261
- border-right: 12px solid transparent;
262
- border-top: 12px solid var(--tooltip-bg);
263
- transform: translateX(-50%) translateY(-50%);
264
- `;
351
+ // Bottom edge, pointing down
352
+ return `left: ${Math.round(arrowX)}px; top: 100%; ${centreX} border-left: ${transparent}; border-right: ${transparent}; border-top: ${filled};`;
265
353
 
266
354
  case 'bottom':
267
- // Arrow at top edge, pointing up
268
- return `
269
- left: ${arrowX}px;
270
- top: 0px;
271
- border-left: 12px solid transparent;
272
- border-right: 12px solid transparent;
273
- border-bottom: 12px solid var(--tooltip-bg);
274
- transform: translateX(-50%) translateY(-50%);
275
- `;
355
+ // Top edge, pointing up
356
+ return `left: ${Math.round(arrowX)}px; bottom: 100%; ${centreX} border-left: ${transparent}; border-right: ${transparent}; border-bottom: ${filled};`;
276
357
 
277
358
  case 'left':
278
- // Arrow at right edge, pointing left
279
- return `
280
- left: ${tooltipWidth}px;
281
- top: ${arrowY}px;
282
- border-top: 12px solid transparent;
283
- border-bottom: 12px solid transparent;
284
- border-left: 12px solid var(--tooltip-bg);
285
- transform: translateX(-50%) translateY(-50%);
286
- `;
359
+ // Right edge, pointing right at the trigger
360
+ return `top: ${Math.round(arrowY)}px; left: 100%; ${centreY} border-top: ${transparent}; border-bottom: ${transparent}; border-left: ${filled};`;
287
361
 
288
362
  case 'right':
289
- // Arrow at left edge, pointing right
290
- return `
291
- left: 0px;
292
- top: ${arrowY}px;
293
- border-top: 12px solid transparent;
294
- border-bottom: 12px solid transparent;
295
- border-right: 12px solid var(--tooltip-bg);
296
- transform: translateX(-50%) translateY(-50%);
297
- `;
363
+ // Left edge, pointing left at the trigger
364
+ return `top: ${Math.round(arrowY)}px; right: 100%; ${centreY} border-top: ${transparent}; border-bottom: ${transparent}; border-right: ${filled};`;
298
365
 
299
366
  default:
300
367
  return '';
301
368
  }
302
369
  }
303
370
 
304
- function handleKeydown(event: KeyboardEvent) {
305
- if (event.key === 'Escape' && open) {
371
+ // Keep the tooltip anchored for as long as it is on screen. Runs before paint,
372
+ // so the first frame is already in the right place.
373
+ $effect(() => {
374
+ if (!visible || !tooltipElement || !referenceElement) return;
375
+
376
+ const tooltip = tooltipElement;
377
+ const reference = referenceElement;
378
+
379
+ // Resolved once per open rather than per reposition — walking the ancestor
380
+ // chain with getComputedStyle is too costly to repeat on every scroll frame.
381
+ fixedContainer = findFixedContainingBlock(tooltip);
382
+
383
+ // Reads location/offsetDistance/showArrow, so this effect re-runs when they
384
+ // change. maxWidth is covered by the observer below: it resizes the box.
385
+ positionTooltip();
386
+
387
+ // Content that grows, shrinks or reflows after the fact — async data, images,
388
+ // a font swap, a reactive snippet — invalidates the measurements above.
389
+ const observer = new ResizeObserver(() => positionTooltip());
390
+ observer.observe(tooltip);
391
+ observer.observe(reference);
392
+ // A centred container (a dialog) moves the trigger when it resizes without
393
+ // resizing the trigger itself, so neither observer above would notice.
394
+ if (fixedContainer) observer.observe(fixedContainer);
395
+
396
+ // Capture phase so scrolling any ancestor container counts, not just the page.
397
+ window.addEventListener('scroll', schedulePosition, true);
398
+ window.addEventListener('resize', schedulePosition);
399
+
400
+ return () => {
401
+ observer.disconnect();
402
+ window.removeEventListener('scroll', schedulePosition, true);
403
+ window.removeEventListener('resize', schedulePosition);
404
+ fixedContainer = null;
405
+
406
+ if (rafId !== null) {
407
+ cancelAnimationFrame(rafId);
408
+ rafId = null;
409
+ }
410
+ };
411
+ });
412
+
413
+ $effect(() => {
414
+ if (!visible) return;
415
+
416
+ const onKeydown = (event: KeyboardEvent) => {
417
+ if (event.key === 'Escape') {
418
+ cancelPending();
419
+ hideTooltip();
420
+ }
421
+ };
422
+
423
+ window.addEventListener('keydown', onKeydown);
424
+ return () => window.removeEventListener('keydown', onKeydown);
425
+ });
426
+
427
+ // Losing the content or being disabled mid-hover has to close the tooltip, not
428
+ // just hide it, or onOpen would be left reporting an open tooltip.
429
+ $effect(() => {
430
+ if (open && (disabled || !hasContent)) {
431
+ cancelPending();
306
432
  hideTooltip();
307
433
  }
308
- }
434
+ });
309
435
 
436
+ // Drop a pending delay timer if the component is destroyed mid-hover.
310
437
  $effect(() => {
311
- if (open) {
312
- window.addEventListener('keydown', handleKeydown);
313
- window.addEventListener('scroll', positionTooltip, true);
314
- window.addEventListener('resize', positionTooltip);
315
-
316
- return () => {
317
- window.removeEventListener('keydown', handleKeydown);
318
- window.removeEventListener('scroll', positionTooltip, true);
319
- window.removeEventListener('resize', positionTooltip);
320
- };
321
- }
438
+ return () => cancelPending();
322
439
  });
323
440
  </script>
324
441
 
325
442
  <div class="tooltip-v2-container">
326
- <!-- Reference Element -->
443
+ <!-- Reference Element: a passive wrapper — the interactive element (e.g. button) is
444
+ provided by children; focusin/focusout bubble up from it, unlike focus/blur -->
445
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
327
446
  <div
328
447
  bind:this={referenceElement}
329
448
  class="tooltip-v2-trigger"
330
449
  onmouseenter={handleMouseEnter}
331
450
  onmouseleave={handleMouseLeave}
332
- onfocus={handleMouseEnter}
333
- onblur={handleMouseLeave}
334
- role="button"
335
- tabindex="0"
451
+ onfocusin={handleMouseEnter}
452
+ onfocusout={handleMouseLeave}
336
453
  >
337
454
  {@render children()}
338
455
  </div>
339
456
 
340
457
  <!-- Floating Tooltip -->
341
- {#if open && hasContent && !disabled}
458
+ {#if visible}
342
459
  <div
343
460
  bind:this={tooltipElement}
344
461
  class="tooltip-v2-floating"
345
- style={tooltipStyle}
462
+ class:is-positioned={positioned}
463
+ style="{tooltipStyle} max-width: {maxWidth};"
464
+ data-placement={actualPlacement}
346
465
  role="tooltip"
347
466
  transition:fade={{ duration: 150 }}
348
467
  >
349
- <div class="tooltip-v2-content" style="max-width: {maxWidth}">
468
+ <div class="tooltip-v2-content">
350
469
  {#if text}
351
470
  <span>{text}</span>
352
471
  {:else if html}
@@ -357,7 +476,7 @@
357
476
  </div>
358
477
 
359
478
  {#if showArrow}
360
- <div bind:this={arrowElement} class="tooltip-v2-arrow" style={arrowStyle}></div>
479
+ <div class="tooltip-v2-arrow" style={arrowStyle}></div>
361
480
  {/if}
362
481
  </div>
363
482
  {/if}
@@ -375,29 +494,42 @@
375
494
 
376
495
  .tooltip-v2-floating {
377
496
  position: fixed;
378
- z-index: var(--pui-z-tooltip);
497
+ left: 0;
498
+ top: 0;
499
+ z-index: 9999;
379
500
  pointer-events: none;
380
- --tooltip-bg: var(--pui-color-gray-200);
381
- --tooltip-text: var(--pui-color-gray-800);
501
+ /* Size the box independently of where it sits. With auto width the available
502
+ space is viewportWidth - left, so measuring and then moving the tooltip
503
+ would change the size it was measured at and retrigger the observer. */
504
+ width: max-content;
505
+ /* Revealed by .is-positioned once measured, so it is never painted at a
506
+ stale position. */
507
+ visibility: hidden;
508
+ --tooltip-bg: #e0e0e0;
509
+ --tooltip-text: #333;
510
+ }
511
+
512
+ .tooltip-v2-floating.is-positioned {
513
+ visibility: visible;
382
514
  }
383
515
 
384
516
  :global(.dark) .tooltip-v2-floating {
385
- --tooltip-bg: var(--pui-color-gray-800);
386
- --tooltip-text: var(--pui-color-gray-200);
517
+ --tooltip-bg: rgb(51, 52, 57);
518
+ --tooltip-text: #e5e5e5;
387
519
  }
388
520
 
389
521
  .tooltip-v2-content {
390
522
  background: var(--tooltip-bg);
391
523
  color: var(--tooltip-text);
392
- padding: var(--pui-spacing-2) var(--pui-spacing-3);
393
- border-radius: var(--pui-radius-lg);
394
- font-size: var(--pui-font-size-sm);
395
- line-height: var(--pui-line-height-normal);
396
- position: relative;
524
+ padding: 0.5rem 0.75rem;
525
+ border-radius: 6px;
526
+ font-size: 0.875rem;
527
+ line-height: 1.4;
528
+ overflow-wrap: break-word;
397
529
  }
398
530
 
399
531
  :global(.dark) .tooltip-v2-content {
400
- box-shadow: var(--pui-shadow-md);
532
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
401
533
  }
402
534
 
403
535
  .tooltip-v2-arrow {
@@ -432,4 +564,4 @@
432
564
  :global(.dark) .tooltip-v2-content :global(code) {
433
565
  background: rgba(255, 255, 255, 0.05);
434
566
  }
435
- </style>
567
+ </style>
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
- interface Props {
2
+ export type TooltipLocation = 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
3
+ export interface TooltipProps {
3
4
  /** The element to attach the tooltip to */
4
5
  children: Snippet;
5
6
  /** Rich content using a Svelte snippet */
@@ -9,7 +10,7 @@ interface Props {
9
10
  /** HTML content (use with caution - sanitize user input) */
10
11
  html?: string;
11
12
  /** Preferred tooltip position */
12
- location?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
13
+ location?: TooltipLocation;
13
14
  /** Maximum width of tooltip */
14
15
  maxWidth?: string;
15
16
  /** Offset distance from target element (px) */
@@ -23,6 +24,6 @@ interface Props {
23
24
  /** Disable the tooltip */
24
25
  disabled?: boolean;
25
26
  }
26
- declare const Tooltip: import("svelte").Component<Props, {}, "">;
27
+ declare const Tooltip: import("svelte").Component<TooltipProps, {}, "">;
27
28
  type Tooltip = ReturnType<typeof Tooltip>;
28
29
  export default Tooltip;