@kylincloud/flamegraph 0.35.6

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 (267) hide show
  1. package/CHANGELOG.md +1211 -0
  2. package/LICENSE +202 -0
  3. package/README.md +251 -0
  4. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts +2 -0
  5. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts.map +1 -0
  6. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts +17 -0
  7. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts.map +1 -0
  8. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts +14 -0
  9. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts.map +1 -0
  10. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts +9 -0
  11. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts.map +1 -0
  12. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts +8 -0
  13. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts.map +1 -0
  14. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts +96 -0
  15. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts.map +1 -0
  16. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts +27 -0
  17. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts.map +1 -0
  18. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts +7 -0
  19. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts.map +1 -0
  20. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts +12 -0
  21. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts.map +1 -0
  22. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts +18 -0
  23. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts.map +1 -0
  24. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts +2 -0
  25. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts.map +1 -0
  26. package/dist/FlameGraph/FlameGraphComponent/color.d.ts +20 -0
  27. package/dist/FlameGraph/FlameGraphComponent/color.d.ts.map +1 -0
  28. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts +11 -0
  29. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts.map +1 -0
  30. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts +6 -0
  31. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts.map +1 -0
  32. package/dist/FlameGraph/FlameGraphComponent/index.d.ts +37 -0
  33. package/dist/FlameGraph/FlameGraphComponent/index.d.ts.map +1 -0
  34. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts +2 -0
  35. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts.map +1 -0
  36. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts +53 -0
  37. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts.map +1 -0
  38. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts +6 -0
  39. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts.map +1 -0
  40. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts +2 -0
  41. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts.map +1 -0
  42. package/dist/FlameGraph/FlameGraphRenderer.d.ts +86 -0
  43. package/dist/FlameGraph/FlameGraphRenderer.d.ts.map +1 -0
  44. package/dist/FlameGraph/decode.d.ts +27 -0
  45. package/dist/FlameGraph/decode.d.ts.map +1 -0
  46. package/dist/FlameGraph/normalize.d.ts +6 -0
  47. package/dist/FlameGraph/normalize.d.ts.map +1 -0
  48. package/dist/FlameGraph/uniqueness.d.ts +3 -0
  49. package/dist/FlameGraph/uniqueness.d.ts.map +1 -0
  50. package/dist/FlamegraphRenderer.d.ts +19 -0
  51. package/dist/FlamegraphRenderer.d.ts.map +1 -0
  52. package/dist/Icons.d.ts +9 -0
  53. package/dist/Icons.d.ts.map +1 -0
  54. package/dist/ProfilerTable.d.ts +21 -0
  55. package/dist/ProfilerTable.d.ts.map +1 -0
  56. package/dist/SharedQueryInput.d.ts +10 -0
  57. package/dist/SharedQueryInput.d.ts.map +1 -0
  58. package/dist/Toolbar.d.ts +31 -0
  59. package/dist/Toolbar.d.ts.map +1 -0
  60. package/dist/Tooltip/FlamegraphTooltip.d.ts +59 -0
  61. package/dist/Tooltip/FlamegraphTooltip.d.ts.map +1 -0
  62. package/dist/Tooltip/LeftClickIcon.d.ts +2 -0
  63. package/dist/Tooltip/LeftClickIcon.d.ts.map +1 -0
  64. package/dist/Tooltip/RightClickIcon.d.ts +2 -0
  65. package/dist/Tooltip/RightClickIcon.d.ts.map +1 -0
  66. package/dist/Tooltip/TableTooltip.d.ts +12 -0
  67. package/dist/Tooltip/TableTooltip.d.ts.map +1 -0
  68. package/dist/Tooltip/Tooltip.d.ts +29 -0
  69. package/dist/Tooltip/Tooltip.d.ts.map +1 -0
  70. package/dist/convert/convertJaegerTraceToProfile.d.ts +3 -0
  71. package/dist/convert/convertJaegerTraceToProfile.d.ts.map +1 -0
  72. package/dist/convert/diffTwoProfiles.d.ts +3 -0
  73. package/dist/convert/diffTwoProfiles.d.ts.map +1 -0
  74. package/dist/convert/flamebearersToTree.d.ts +11 -0
  75. package/dist/convert/flamebearersToTree.d.ts.map +1 -0
  76. package/dist/convert/sandwichViewProfiles.d.ts +14 -0
  77. package/dist/convert/sandwichViewProfiles.d.ts.map +1 -0
  78. package/dist/convert/subtract.d.ts +3 -0
  79. package/dist/convert/subtract.d.ts.map +1 -0
  80. package/dist/convert/testData.d.ts +50 -0
  81. package/dist/convert/testData.d.ts.map +1 -0
  82. package/dist/convert/toGraphviz.d.ts +3 -0
  83. package/dist/convert/toGraphviz.d.ts.map +1 -0
  84. package/dist/fitMode/fitMode.d.ts +42 -0
  85. package/dist/fitMode/fitMode.d.ts.map +1 -0
  86. package/dist/format/format.d.ts +42 -0
  87. package/dist/format/format.d.ts.map +1 -0
  88. package/dist/i18n.d.ts +55 -0
  89. package/dist/i18n.d.ts.map +1 -0
  90. package/dist/index.cjs.css +792 -0
  91. package/dist/index.cjs.js +5087 -0
  92. package/dist/index.d.ts +4 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.esm.css +792 -0
  95. package/dist/index.esm.js +5079 -0
  96. package/dist/index.node.d.ts +9 -0
  97. package/dist/index.node.d.ts.map +1 -0
  98. package/dist/logo-v3-small-T5VXIMRR.svg +32 -0
  99. package/dist/models/decode.d.ts +3 -0
  100. package/dist/models/decode.d.ts.map +1 -0
  101. package/dist/models/flamebearer.d.ts +63 -0
  102. package/dist/models/flamebearer.d.ts.map +1 -0
  103. package/dist/models/groups.d.ts +37 -0
  104. package/dist/models/groups.d.ts.map +1 -0
  105. package/dist/models/index.d.ts +8 -0
  106. package/dist/models/index.d.ts.map +1 -0
  107. package/dist/models/profile.d.ts +152 -0
  108. package/dist/models/profile.d.ts.map +1 -0
  109. package/dist/models/spyName.d.ts +8 -0
  110. package/dist/models/spyName.d.ts.map +1 -0
  111. package/dist/models/trace.d.ts +357 -0
  112. package/dist/models/trace.d.ts.map +1 -0
  113. package/dist/models/units.d.ts +6 -0
  114. package/dist/models/units.d.ts.map +1 -0
  115. package/dist/search.d.ts +2 -0
  116. package/dist/search.d.ts.map +1 -0
  117. package/dist/shims/Box.d.ts +38 -0
  118. package/dist/shims/Box.d.ts.map +1 -0
  119. package/dist/shims/Button.d.ts +26 -0
  120. package/dist/shims/Button.d.ts.map +1 -0
  121. package/dist/shims/Dropdown.d.ts +30 -0
  122. package/dist/shims/Dropdown.d.ts.map +1 -0
  123. package/dist/shims/Input.d.ts +19 -0
  124. package/dist/shims/Input.d.ts.map +1 -0
  125. package/dist/shims/LoadingSpinner.d.ts +7 -0
  126. package/dist/shims/LoadingSpinner.d.ts.map +1 -0
  127. package/dist/shims/Menu.d.ts +4 -0
  128. package/dist/shims/Menu.d.ts.map +1 -0
  129. package/dist/shims/NoData.d.ts +2 -0
  130. package/dist/shims/NoData.d.ts.map +1 -0
  131. package/dist/shims/Table.d.ts +52 -0
  132. package/dist/shims/Table.d.ts.map +1 -0
  133. package/dist/shims/Tooltip.d.ts +9 -0
  134. package/dist/shims/Tooltip.d.ts.map +1 -0
  135. package/package.json +84 -0
  136. package/src/FlameGraph/FlameGraphComponent/CheckIcon.tsx +27 -0
  137. package/src/FlameGraph/FlameGraphComponent/ContextMenu.module.scss +10 -0
  138. package/src/FlameGraph/FlameGraphComponent/ContextMenu.spec.tsx +84 -0
  139. package/src/FlameGraph/FlameGraphComponent/ContextMenu.tsx +86 -0
  140. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.module.css +8 -0
  141. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.tsx +47 -0
  142. package/src/FlameGraph/FlameGraphComponent/DiffLegend.module.css +21 -0
  143. package/src/FlameGraph/FlameGraphComponent/DiffLegend.tsx +52 -0
  144. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.module.css +40 -0
  145. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.tsx +129 -0
  146. package/src/FlameGraph/FlameGraphComponent/Flamegraph.spec.ts +552 -0
  147. package/src/FlameGraph/FlameGraphComponent/Flamegraph.ts +446 -0
  148. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.spec.tsx +233 -0
  149. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.ts +478 -0
  150. package/src/FlameGraph/FlameGraphComponent/GraphVizPane.tsx +56 -0
  151. package/src/FlameGraph/FlameGraphComponent/GraphVizPanel.module.scss +55 -0
  152. package/src/FlameGraph/FlameGraphComponent/Header.module.css +27 -0
  153. package/src/FlameGraph/FlameGraphComponent/Header.tsx +71 -0
  154. package/src/FlameGraph/FlameGraphComponent/Highlight.module.css +7 -0
  155. package/src/FlameGraph/FlameGraphComponent/Highlight.spec.tsx +53 -0
  156. package/src/FlameGraph/FlameGraphComponent/Highlight.tsx +94 -0
  157. package/src/FlameGraph/FlameGraphComponent/LogoLink.module.scss +10 -0
  158. package/src/FlameGraph/FlameGraphComponent/LogoLink.tsx +101 -0
  159. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-collapses-small-blocks-into-one-1-snap.png +0 -0
  160. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-diff-mode-1-snap.png +0 -0
  161. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-highlighted-flamegraph-1-snap.png +0 -0
  162. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-normal-flamegraph-1-snap.png +0 -0
  163. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-selected-node-1-snap.png +0 -0
  164. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-also-zooms-1-snap.png +0 -0
  165. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-in-the-beginning-1-snap.png +0 -0
  166. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-when-node-is-not-in-the-beginning-1-snap.png +0 -0
  167. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-zoom-top-level-1-snap.png +0 -0
  168. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-complex-flamegraph-1-snap.png +0 -0
  169. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-double-diff-flamegraph-1-snap.png +0 -0
  170. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-double-flamegraph-1-snap.png +0 -0
  171. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-flamegraph-1-snap.png +0 -0
  172. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-flamegraph-1-snap.png +0 -0
  173. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-tree-1-snap.png +0 -0
  174. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-flamegraph-1-snap.png +0 -0
  175. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-with-fit-mode-tail-1-snap.png +0 -0
  176. package/src/FlameGraph/FlameGraphComponent/canvas.module.css +6 -0
  177. package/src/FlameGraph/FlameGraphComponent/color.spec.ts +308 -0
  178. package/src/FlameGraph/FlameGraphComponent/color.ts +167 -0
  179. package/src/FlameGraph/FlameGraphComponent/colorPalette.ts +58 -0
  180. package/src/FlameGraph/FlameGraphComponent/constants.ts +5 -0
  181. package/src/FlameGraph/FlameGraphComponent/index.spec.tsx +291 -0
  182. package/src/FlameGraph/FlameGraphComponent/index.tsx +411 -0
  183. package/src/FlameGraph/FlameGraphComponent/murmur3.ts +97 -0
  184. package/src/FlameGraph/FlameGraphComponent/styles.module.scss +10 -0
  185. package/src/FlameGraph/FlameGraphComponent/testData.ts +427 -0
  186. package/src/FlameGraph/FlameGraphComponent/utils.ts +31 -0
  187. package/src/FlameGraph/FlameGraphComponent/viewTypes.ts +6 -0
  188. package/src/FlameGraph/FlameGraphRenderer.tsx +603 -0
  189. package/src/FlameGraph/FlamegraphRenderer.module.scss +93 -0
  190. package/src/FlameGraph/decode.ts +78 -0
  191. package/src/FlameGraph/normalize.spec.ts +76 -0
  192. package/src/FlameGraph/normalize.ts +60 -0
  193. package/src/FlameGraph/testData.json +423 -0
  194. package/src/FlameGraph/uniqueness.spec.ts +16 -0
  195. package/src/FlameGraph/uniqueness.ts +84 -0
  196. package/src/FlamegraphRenderer.tsx +61 -0
  197. package/src/Icons.tsx +74 -0
  198. package/src/ProfilerTable.tsx +527 -0
  199. package/src/SharedQueryInput.module.scss +82 -0
  200. package/src/SharedQueryInput.tsx +127 -0
  201. package/src/Toolbar.module.scss +117 -0
  202. package/src/Toolbar.spec.tsx +217 -0
  203. package/src/Toolbar.tsx +471 -0
  204. package/src/Tooltip/FlamegraphTooltip.spec.tsx +81 -0
  205. package/src/Tooltip/FlamegraphTooltip.tsx +257 -0
  206. package/src/Tooltip/LeftClickIcon.tsx +18 -0
  207. package/src/Tooltip/RightClickIcon.tsx +18 -0
  208. package/src/Tooltip/TableTooltip.spec.tsx +44 -0
  209. package/src/Tooltip/TableTooltip.tsx +145 -0
  210. package/src/Tooltip/Tooltip.module.scss +71 -0
  211. package/src/Tooltip/Tooltip.spec.tsx +395 -0
  212. package/src/Tooltip/Tooltip.tsx +336 -0
  213. package/src/__snapshots__/Toolbar.spec.tsx.snap +297 -0
  214. package/src/convert/convertJaegerTraceToProfile.ts +97 -0
  215. package/src/convert/diffTwoProfiles.ts +81 -0
  216. package/src/convert/flamebearersToTree.ts +78 -0
  217. package/src/convert/sandwichViewProfiles.spec.ts +65 -0
  218. package/src/convert/sandwichViewProfiles.ts +191 -0
  219. package/src/convert/subtract.ts +87 -0
  220. package/src/convert/testData.ts +145 -0
  221. package/src/convert/toGraphviz.ts +485 -0
  222. package/src/fitMode/fitMode.spec.ts +93 -0
  223. package/src/fitMode/fitMode.ts +122 -0
  224. package/src/format/format.spec.ts +291 -0
  225. package/src/format/format.ts +303 -0
  226. package/src/globals.d.ts +13 -0
  227. package/src/i18n.tsx +293 -0
  228. package/src/index.node.ts +19 -0
  229. package/src/index.spec.tsx +383 -0
  230. package/src/index.tsx +10 -0
  231. package/src/logo-v3-small.svg +32 -0
  232. package/src/models/decode.ts +45 -0
  233. package/src/models/flamebearer.ts +86 -0
  234. package/src/models/groups.ts +14 -0
  235. package/src/models/index.ts +7 -0
  236. package/src/models/profile.spec.ts +32 -0
  237. package/src/models/profile.ts +48 -0
  238. package/src/models/spyName.spec.ts +18 -0
  239. package/src/models/spyName.ts +32 -0
  240. package/src/models/trace.ts +45 -0
  241. package/src/models/units.spec.ts +21 -0
  242. package/src/models/units.ts +24 -0
  243. package/src/sass/_common.scss +206 -0
  244. package/src/sass/_css-variables.scss +201 -0
  245. package/src/sass/_mixins.scss +15 -0
  246. package/src/sass/_sanitize.scss +407 -0
  247. package/src/sass/_variables.scss +53 -0
  248. package/src/sass/flamegraph.scss +18 -0
  249. package/src/search.spec.ts +11 -0
  250. package/src/search.ts +4 -0
  251. package/src/shameful-any.d.ts +2 -0
  252. package/src/shims/Box.module.scss +57 -0
  253. package/src/shims/Box.tsx +105 -0
  254. package/src/shims/Button.module.scss +129 -0
  255. package/src/shims/Button.tsx +128 -0
  256. package/src/shims/Dropdown.module.scss +63 -0
  257. package/src/shims/Dropdown.tsx +96 -0
  258. package/src/shims/Input.module.scss +15 -0
  259. package/src/shims/Input.tsx +55 -0
  260. package/src/shims/LoadingSpinner.tsx +19 -0
  261. package/src/shims/Menu.tsx +9 -0
  262. package/src/shims/NoData.module.scss +6 -0
  263. package/src/shims/NoData.tsx +11 -0
  264. package/src/shims/Table.module.scss +82 -0
  265. package/src/shims/Table.spec.tsx +121 -0
  266. package/src/shims/Table.tsx +252 -0
  267. package/src/shims/Tooltip.tsx +51 -0
@@ -0,0 +1,471 @@
1
+ // src/Toolbar.tsx
2
+ import React, {
3
+ ReactNode,
4
+ RefObject,
5
+ useState,
6
+ useRef,
7
+ useLayoutEffect,
8
+ isValidElement,
9
+ memo,
10
+ } from 'react';
11
+ import classNames from 'classnames/bind';
12
+ import { faUndo } from '@fortawesome/free-solid-svg-icons/faUndo';
13
+ import { faCompressAlt } from '@fortawesome/free-solid-svg-icons/faCompressAlt';
14
+ import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons/faProjectDiagram';
15
+ import { faEllipsisV } from '@fortawesome/free-solid-svg-icons/faEllipsisV';
16
+ import { Maybe } from 'true-myth';
17
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
18
+ import useResizeObserver from '@react-hook/resize-observer';
19
+ // until ui is moved to its own package this should do it
20
+ // eslint-disable-next-line import/no-extraneous-dependencies
21
+ import Button from './shims/Button';
22
+ // eslint-disable-next-line import/no-extraneous-dependencies
23
+ import { Tooltip } from './shims/Tooltip';
24
+ import { FitModes } from './fitMode/fitMode';
25
+ import SharedQueryInput from './SharedQueryInput';
26
+ import type { ViewTypes } from './FlameGraph/FlameGraphComponent/viewTypes';
27
+ import type { FlamegraphRendererProps } from './FlameGraph/FlameGraphRenderer';
28
+ import {
29
+ TableIcon,
30
+ TablePlusFlamegraphIcon,
31
+ FlamegraphIcon,
32
+ SandwichIcon,
33
+ HeadFirstIcon,
34
+ TailFirstIcon,
35
+ } from './Icons';
36
+
37
+ import styles from './Toolbar.module.scss';
38
+ import {
39
+ useFlamegraphI18n,
40
+ type FlamegraphMessages,
41
+ } from './i18n';
42
+
43
+ const cx = classNames.bind(styles);
44
+
45
+ const DIVIDER_WIDTH = 5;
46
+ const QUERY_INPUT_WIDTH = 175;
47
+ const LEFT_MARGIN = 2;
48
+ const RIGHT_MARGIN = 2;
49
+ const TOOLBAR_SQUARE_WIDTH = 40 + LEFT_MARGIN + RIGHT_MARGIN;
50
+ const MORE_BUTTON_WIDTH = 16;
51
+
52
+ const calculateCollapsedItems = (
53
+ clientWidth: number,
54
+ collapsedItemsNumber: number,
55
+ itemsW: number[]
56
+ ) => {
57
+ const availableToolbarItemsWidth =
58
+ collapsedItemsNumber === 0
59
+ ? clientWidth - QUERY_INPUT_WIDTH - 5
60
+ : clientWidth - QUERY_INPUT_WIDTH - MORE_BUTTON_WIDTH - 5;
61
+
62
+ let collapsedItems = 0;
63
+ let visibleItemsWidth = 0;
64
+ itemsW.reverse().forEach((v) => {
65
+ visibleItemsWidth += v;
66
+ if (availableToolbarItemsWidth <= visibleItemsWidth) {
67
+ collapsedItems += 1;
68
+ }
69
+ });
70
+
71
+ return collapsedItems;
72
+ };
73
+
74
+ const useMoreButton = (
75
+ target: RefObject<HTMLDivElement>,
76
+ toolbarItemsWidth: number[]
77
+ ) => {
78
+ const [isCollapsed, setCollapsedStatus] = useState(true);
79
+ const [collapsedItemsNumber, setCollapsedItemsNumber] = useState(0);
80
+
81
+ useLayoutEffect(() => {
82
+ if (target.current) {
83
+ const { width } = target.current.getBoundingClientRect();
84
+ const collapsedItems = calculateCollapsedItems(
85
+ width,
86
+ collapsedItemsNumber,
87
+ toolbarItemsWidth
88
+ );
89
+ setCollapsedItemsNumber(collapsedItems);
90
+ }
91
+ }, [target.current, toolbarItemsWidth]);
92
+
93
+ const handleMoreClick = () => {
94
+ setCollapsedStatus((v) => !v);
95
+ };
96
+
97
+ useResizeObserver(target, (entry: ResizeObserverEntry) => {
98
+ const { width } = entry.target.getBoundingClientRect();
99
+ const collapsedItems = calculateCollapsedItems(
100
+ width,
101
+ collapsedItemsNumber,
102
+ toolbarItemsWidth
103
+ );
104
+
105
+ setCollapsedItemsNumber(collapsedItems);
106
+ setCollapsedStatus(true);
107
+ });
108
+
109
+ return {
110
+ isCollapsed,
111
+ handleMoreClick,
112
+ collapsedItemsNumber,
113
+ };
114
+ };
115
+
116
+ export interface ProfileHeaderProps {
117
+ view: ViewTypes;
118
+ enableChangingDisplay?: boolean;
119
+ flamegraphType: 'single' | 'double';
120
+ handleSearchChange: (s: string) => void;
121
+ highlightQuery: string;
122
+ ExportData?: ReactNode;
123
+
124
+ /** Whether the flamegraph is different from its original state */
125
+ isFlamegraphDirty: boolean;
126
+ reset: () => void;
127
+
128
+ updateFitMode: (f: FitModes) => void;
129
+ fitMode: FitModes;
130
+ updateView: (s: ViewTypes) => void;
131
+
132
+ /**
133
+ * Refers to the node that has been selected in the flamegraph
134
+ */
135
+ selectedNode: Maybe<{ i: number; j: number }>;
136
+ onFocusOnSubtree: (i: number, j: number) => void;
137
+ sharedQuery?: FlamegraphRendererProps['sharedQuery'];
138
+ }
139
+
140
+ const Divider = () => <div className={styles.divider} />;
141
+
142
+ type ToolbarItemType = {
143
+ width: number;
144
+ el: ReactNode;
145
+ };
146
+
147
+ const Toolbar = memo(
148
+ ({
149
+ view,
150
+ handleSearchChange,
151
+ highlightQuery,
152
+ isFlamegraphDirty,
153
+ reset,
154
+ updateFitMode,
155
+ fitMode,
156
+ updateView,
157
+ selectedNode,
158
+ onFocusOnSubtree,
159
+ flamegraphType,
160
+ enableChangingDisplay = true,
161
+ sharedQuery,
162
+ ExportData,
163
+ }: ProfileHeaderProps) => {
164
+ const toolbarRef = useRef<HTMLDivElement>(null);
165
+ const i18n = useFlamegraphI18n();
166
+
167
+ const fitModeItem = {
168
+ el: (
169
+ <>
170
+ <FitMode fitMode={fitMode} updateFitMode={updateFitMode} />
171
+ <Divider />
172
+ </>
173
+ ),
174
+ width: TOOLBAR_SQUARE_WIDTH * 2 + DIVIDER_WIDTH,
175
+ };
176
+ const resetItem = {
177
+ el: <ResetView isFlamegraphDirty={isFlamegraphDirty} reset={reset} />,
178
+ width: TOOLBAR_SQUARE_WIDTH,
179
+ };
180
+ const focusOnSubtree = {
181
+ el: (
182
+ <>
183
+ <FocusOnSubtree
184
+ selectedNode={selectedNode}
185
+ onFocusOnSubtree={onFocusOnSubtree}
186
+ />
187
+ <Divider />
188
+ </>
189
+ ),
190
+ width: TOOLBAR_SQUARE_WIDTH + DIVIDER_WIDTH,
191
+ };
192
+
193
+ const viewSectionItem = enableChangingDisplay
194
+ ? {
195
+ el: (
196
+ <ViewSection
197
+ flamegraphType={flamegraphType}
198
+ view={view}
199
+ updateView={updateView}
200
+ />
201
+ ),
202
+ width: TOOLBAR_SQUARE_WIDTH * (flamegraphType === 'single' ? 5 : 3),
203
+ }
204
+ : null;
205
+ const exportDataItem = isValidElement(ExportData)
206
+ ? {
207
+ el: (
208
+ <>
209
+ <Divider />
210
+ {ExportData}
211
+ </>
212
+ ),
213
+ width: TOOLBAR_SQUARE_WIDTH + DIVIDER_WIDTH,
214
+ }
215
+ : null;
216
+
217
+ const filteredToolbarItems = [
218
+ fitModeItem,
219
+ resetItem,
220
+ focusOnSubtree,
221
+ viewSectionItem,
222
+ exportDataItem,
223
+ ].filter((v) => v !== null) as ToolbarItemType[];
224
+ const toolbarItemsWidth = filteredToolbarItems.reduce(
225
+ (acc, v) => [...acc, v.width],
226
+ [] as number[]
227
+ );
228
+
229
+ const { isCollapsed, collapsedItemsNumber, handleMoreClick } =
230
+ useMoreButton(toolbarRef, toolbarItemsWidth);
231
+
232
+ const toolbarFilteredItems = filteredToolbarItems.reduce(
233
+ (acc, v, i) => {
234
+ const isHiddenItem = i < collapsedItemsNumber;
235
+
236
+ if (isHiddenItem) {
237
+ acc.hidden.push(v);
238
+ } else {
239
+ acc.visible.push(v);
240
+ }
241
+
242
+ return acc;
243
+ },
244
+ { visible: [] as ToolbarItemType[], hidden: [] as ToolbarItemType[] }
245
+ );
246
+
247
+ return (
248
+ <div role="toolbar" ref={toolbarRef}>
249
+ <div className={styles.navbar}>
250
+ <div>
251
+ <SharedQueryInput
252
+ width={QUERY_INPUT_WIDTH}
253
+ onHighlightChange={handleSearchChange}
254
+ highlightQuery={highlightQuery}
255
+ sharedQuery={sharedQuery}
256
+ />
257
+ </div>
258
+ <div>
259
+ <div className={styles.itemsContainer}>
260
+ {toolbarFilteredItems.visible.map((v, i) => (
261
+ // eslint-disable-next-line react/no-array-index-key
262
+ <div key={i} className={styles.item} style={{ width: v.width }}>
263
+ {v.el}
264
+ </div>
265
+ ))}
266
+ {collapsedItemsNumber !== 0 && (
267
+ <Tooltip placement="top" title={i18n.more}>
268
+ <button
269
+ onClick={handleMoreClick}
270
+ className={cx({
271
+ [styles.moreButton]: true,
272
+ [styles.active]: !isCollapsed,
273
+ })}
274
+ >
275
+ <FontAwesomeIcon icon={faEllipsisV} />
276
+ </button>
277
+ </Tooltip>
278
+ )}
279
+ </div>
280
+ </div>
281
+ {!isCollapsed && (
282
+ <div className={styles.navbarCollapsedItems}>
283
+ {toolbarFilteredItems.hidden.map((v, i) => (
284
+ <div
285
+ // eslint-disable-next-line react/no-array-index-key
286
+ key={i}
287
+ className={styles.item}
288
+ style={{ width: v.width }}
289
+ >
290
+ {v.el}
291
+ </div>
292
+ ))}
293
+ </div>
294
+ )}
295
+ </div>
296
+ </div>
297
+ );
298
+ }
299
+ );
300
+
301
+ function FocusOnSubtree({
302
+ onFocusOnSubtree,
303
+ selectedNode,
304
+ }: {
305
+ selectedNode: ProfileHeaderProps['selectedNode'];
306
+ onFocusOnSubtree: ProfileHeaderProps['onFocusOnSubtree'];
307
+ }) {
308
+ const i18n = useFlamegraphI18n();
309
+
310
+ const onClick = selectedNode.mapOr(
311
+ () => {},
312
+ (f) => {
313
+ return () => onFocusOnSubtree(f.i, f.j);
314
+ }
315
+ );
316
+
317
+ return (
318
+ <Tooltip placement="top" title={i18n.collapseNodesAbove}>
319
+ <div>
320
+ <Button
321
+ disabled={!selectedNode.isJust}
322
+ onClick={onClick}
323
+ className={styles.collapseNodeButton}
324
+ aria-label={i18n.collapseNodesAbove}
325
+ >
326
+ <FontAwesomeIcon icon={faCompressAlt} />
327
+ </Button>
328
+ </div>
329
+ </Tooltip>
330
+ );
331
+ }
332
+
333
+ function ResetView({
334
+ isFlamegraphDirty,
335
+ reset,
336
+ }: {
337
+ isFlamegraphDirty: ProfileHeaderProps['isFlamegraphDirty'];
338
+ reset: ProfileHeaderProps['reset'];
339
+ }) {
340
+ const i18n = useFlamegraphI18n();
341
+
342
+ return (
343
+ <Tooltip placement="top" title={i18n.resetView}>
344
+ <span>
345
+ <Button
346
+ id="reset"
347
+ disabled={!isFlamegraphDirty}
348
+ onClick={reset}
349
+ className={styles.resetViewButton}
350
+ aria-label={i18n.resetView}
351
+ >
352
+ <FontAwesomeIcon icon={faUndo} />
353
+ </Button>
354
+ </span>
355
+ </Tooltip>
356
+ );
357
+ }
358
+
359
+ function FitMode({
360
+ fitMode,
361
+ updateFitMode,
362
+ }: {
363
+ fitMode: ProfileHeaderProps['fitMode'];
364
+ updateFitMode: ProfileHeaderProps['updateFitMode'];
365
+ }) {
366
+ const i18n = useFlamegraphI18n();
367
+ const isSelected = (a: FitModes) => fitMode === a;
368
+
369
+ return (
370
+ <>
371
+ <Tooltip placement="top" title={i18n.headFirst}>
372
+ <Button
373
+ onClick={() => updateFitMode('HEAD')}
374
+ className={cx({
375
+ [styles.fitModeButton]: true,
376
+ [styles.selected]: isSelected('HEAD'),
377
+ })}
378
+ >
379
+ <HeadFirstIcon />
380
+ </Button>
381
+ </Tooltip>
382
+ <Tooltip placement="top" title={i18n.tailFirst}>
383
+ <Button
384
+ onClick={() => updateFitMode('TAIL')}
385
+ className={cx({
386
+ [styles.fitModeButton]: true,
387
+ [styles.selected]: isSelected('TAIL'),
388
+ })}
389
+ >
390
+ <TailFirstIcon />
391
+ </Button>
392
+ </Tooltip>
393
+ </>
394
+ );
395
+ }
396
+
397
+ const getViewOptions = (
398
+ flamegraphType: ProfileHeaderProps['flamegraphType'],
399
+ messages: FlamegraphMessages
400
+ ): Array<{
401
+ label: string;
402
+ value: ViewTypes;
403
+ Icon: (props: { fill?: string | undefined }) => JSX.Element;
404
+ }> =>
405
+ flamegraphType === 'single'
406
+ ? [
407
+ { label: messages.viewTable, value: 'table', Icon: TableIcon },
408
+ {
409
+ label: messages.viewTableAndFlamegraph,
410
+ value: 'both',
411
+ Icon: TablePlusFlamegraphIcon,
412
+ },
413
+ {
414
+ label: messages.viewFlamegraph,
415
+ value: 'flamegraph',
416
+ Icon: FlamegraphIcon,
417
+ },
418
+ { label: messages.viewSandwich, value: 'sandwich', Icon: SandwichIcon },
419
+ {
420
+ label: messages.viewGraphviz,
421
+ value: 'graphviz',
422
+ Icon: () => <FontAwesomeIcon icon={faProjectDiagram} />,
423
+ },
424
+ ]
425
+ : [
426
+ { label: messages.viewTable, value: 'table', Icon: TableIcon },
427
+ {
428
+ label: messages.viewTableAndFlamegraph,
429
+ value: 'both',
430
+ Icon: TablePlusFlamegraphIcon,
431
+ },
432
+ {
433
+ label: messages.viewFlamegraph,
434
+ value: 'flamegraph',
435
+ Icon: FlamegraphIcon,
436
+ },
437
+ ];
438
+
439
+ function ViewSection({
440
+ view,
441
+ updateView,
442
+ flamegraphType,
443
+ }: {
444
+ updateView: ProfileHeaderProps['updateView'];
445
+ view: ProfileHeaderProps['view'];
446
+ flamegraphType: ProfileHeaderProps['flamegraphType'];
447
+ }) {
448
+ const i18n = useFlamegraphI18n();
449
+ const options = getViewOptions(flamegraphType, i18n);
450
+
451
+ return (
452
+ <div className={styles.viewType}>
453
+ {options.map(({ label, value, Icon }) => (
454
+ <Tooltip key={value} placement="top" title={label}>
455
+ <Button
456
+ data-testid={value}
457
+ onClick={() => updateView(value)}
458
+ className={cx({
459
+ [styles.toggleViewButton]: true,
460
+ selected: view === value,
461
+ })}
462
+ >
463
+ <Icon />
464
+ </Button>
465
+ </Tooltip>
466
+ ))}
467
+ </div>
468
+ );
469
+ }
470
+
471
+ export default Toolbar;
@@ -0,0 +1,81 @@
1
+ /* eslint-disable react/jsx-props-no-spreading */
2
+ import React, { useRef } from 'react';
3
+ import { render, screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { Maybe } from 'true-myth';
6
+ import type { Units } from './models';
7
+
8
+ import FlamegraphTooltip, { FlamegraphTooltipProps } from './FlamegraphTooltip';
9
+ import { DefaultPalette } from '../';
10
+
11
+ function TestCanvas(props: Omit<FlamegraphTooltipProps, 'canvasRef'>) {
12
+ const canvasRef = useRef<HTMLCanvasElement>(null);
13
+
14
+ return (
15
+ <>
16
+ <canvas data-testid="canvas" ref={canvasRef} />
17
+ <FlamegraphTooltip
18
+ {...(props as FlamegraphTooltipProps)}
19
+ canvasRef={canvasRef}
20
+ />
21
+ </>
22
+ );
23
+ }
24
+
25
+ describe('FlamegraphTooltip', () => {
26
+ const renderCanvas = (
27
+ props: Omit<FlamegraphTooltipProps, 'canvasRef' | 'palette'>
28
+ ) => render(<TestCanvas {...props} palette={DefaultPalette} />);
29
+
30
+ it('should render FlamegraphTooltip with single format', () => {
31
+ const xyToData = (x: number, y: number) =>
32
+ Maybe.of({
33
+ format: 'single' as const,
34
+ name: 'function_title',
35
+ total: 10,
36
+ });
37
+
38
+ const props = {
39
+ numTicks: 100,
40
+ sampleRate: 100,
41
+ xyToData,
42
+ leftTicks: 100,
43
+ rightTicks: 100,
44
+ format: 'single' as const,
45
+ units: 'samples' as Units,
46
+ };
47
+
48
+ renderCanvas(props);
49
+
50
+ userEvent.hover(screen.getByTestId('canvas'));
51
+
52
+ expect(screen.getByTestId('tooltip')).toBeInTheDocument();
53
+ });
54
+
55
+ it('should render FlamegraphTooltip with double format', () => {
56
+ const xyToData = (x: number, y: number) =>
57
+ Maybe.of({
58
+ format: 'double' as const,
59
+ name: 'my_function',
60
+ totalLeft: 100,
61
+ totalRight: 0,
62
+ barTotal: 100,
63
+ });
64
+
65
+ const props = {
66
+ numTicks: 100,
67
+ sampleRate: 100,
68
+ xyToData,
69
+ leftTicks: 1000,
70
+ rightTicks: 1000,
71
+ format: 'double' as const,
72
+ units: 'samples' as Units,
73
+ };
74
+
75
+ renderCanvas(props);
76
+
77
+ userEvent.hover(screen.getByTestId('canvas'));
78
+
79
+ expect(screen.getByTestId('tooltip')).toBeInTheDocument();
80
+ });
81
+ });