@bundle-stats/ui 4.13.0-beta.2 → 4.13.0-beta.21

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 (279) hide show
  1. package/lib/app/app.js +4 -8
  2. package/lib/app/app.js.map +1 -1
  3. package/lib/app/app.module.css +2 -2
  4. package/lib/assets/icons.svg +70 -67
  5. package/lib/assets/icons.svg.js +14 -12
  6. package/lib/assets/icons.svg.js.map +1 -1
  7. package/lib/components/bundle-assets/bundle-assets.js +47 -8
  8. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  9. package/lib/components/bundle-assets/bundle-assets.module.css +0 -5
  10. package/lib/components/bundle-assets-totals/bundle-assets-totals.js +25 -14
  11. package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  12. package/lib/components/bundle-modules/bundle-modules.i18n.js +1 -1
  13. package/lib/components/bundle-modules/bundle-modules.js +45 -14
  14. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  15. package/lib/components/bundle-modules/bundle-modules.module.css +0 -5
  16. package/lib/components/bundle-packages/bundle-packages.js +45 -6
  17. package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
  18. package/lib/components/bundle-packages/bundle-packages.module.css +0 -5
  19. package/lib/components/delta/delta.js +1 -1
  20. package/lib/components/delta/delta.js.map +1 -1
  21. package/lib/components/delta/delta.module.css +10 -1
  22. package/lib/components/index.js +1 -3
  23. package/lib/components/index.js.map +1 -1
  24. package/lib/components/jobs-header/jobs-header.module.css +0 -4
  25. package/lib/components/metrics-display-selector/metrics-display-selector.js +42 -5
  26. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  27. package/lib/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  28. package/lib/components/metrics-table/metrics-table.i18n.js +1 -6
  29. package/lib/components/metrics-table/metrics-table.i18n.js.map +1 -1
  30. package/lib/components/metrics-table/metrics-table.js +4 -37
  31. package/lib/components/metrics-table/metrics-table.js.map +1 -1
  32. package/lib/components/metrics-table/metrics-table.module.css +1 -44
  33. package/lib/components/{total-size-type-title → metrics-table-header}/index.js +1 -1
  34. package/lib/components/metrics-table-header/index.js.map +1 -0
  35. package/lib/components/metrics-table-header/metrics-table-header.i18n.js +9 -0
  36. package/lib/components/metrics-table-header/metrics-table-header.i18n.js.map +1 -0
  37. package/lib/components/metrics-table-header/metrics-table-header.js +79 -0
  38. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -0
  39. package/lib/components/metrics-table-header/metrics-table-header.module.css +51 -0
  40. package/lib/components/metrics-table-options/metrics-table-options.js +2 -2
  41. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  42. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  43. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  44. package/lib/components/metrics-table-title/metrics-table-title.module.css +4 -4
  45. package/lib/components/metrics-treemap/index.js +4 -0
  46. package/lib/components/metrics-treemap/index.js.map +1 -1
  47. package/lib/components/metrics-treemap/metrics-treemap.constants.js +3 -0
  48. package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
  49. package/lib/components/metrics-treemap/metrics-treemap.i18n.js +6 -0
  50. package/lib/components/metrics-treemap/metrics-treemap.i18n.js.map +1 -0
  51. package/lib/components/metrics-treemap/metrics-treemap.js +106 -73
  52. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  53. package/lib/components/metrics-treemap/metrics-treemap.module.css +156 -51
  54. package/lib/components/metrics-treemap/metrics-treemap.utils.js +109 -0
  55. package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
  56. package/lib/components/module-info/module-info.js +1 -2
  57. package/lib/components/module-info/module-info.js.map +1 -1
  58. package/lib/components/run-info/run-info.module.css +7 -7
  59. package/lib/components/sort-button/sort-button.module.css +1 -0
  60. package/lib/css/variables.css +14 -13
  61. package/lib/hooks/metrics-display-type.js +45 -24
  62. package/lib/hooks/metrics-display-type.js.map +1 -1
  63. package/lib/hooks/search-params.js +1 -0
  64. package/lib/hooks/search-params.js.map +1 -1
  65. package/lib/layout/box/box.module.css +4 -2
  66. package/lib/layout/flex-stack/flex-stack.js +3 -4
  67. package/lib/layout/flex-stack/flex-stack.js.map +1 -1
  68. package/lib/layout/flex-stack/flex-stack.module.css +19 -23
  69. package/lib/ui/button/button.js +5 -4
  70. package/lib/ui/button/button.js.map +1 -1
  71. package/lib/ui/button/button.module.css +55 -53
  72. package/lib/ui/dropdown/dropdown.js +10 -40
  73. package/lib/ui/dropdown/dropdown.js.map +1 -1
  74. package/lib/ui/dropdown/index.js.map +1 -1
  75. package/lib/ui/filters/filters.js +33 -35
  76. package/lib/ui/filters/filters.js.map +1 -1
  77. package/lib/ui/filters/filters.module.css +55 -67
  78. package/lib/ui/hover-card/hover-card.js +1 -1
  79. package/lib/ui/hover-card/hover-card.js.map +1 -1
  80. package/lib/ui/icon/icon.js +2 -1
  81. package/lib/ui/icon/icon.js.map +1 -1
  82. package/lib/ui/input-search/input-search.js +1 -1
  83. package/lib/ui/input-search/input-search.js.map +1 -1
  84. package/lib/ui/loader/loader.js +1 -1
  85. package/lib/ui/loader/loader.js.map +1 -1
  86. package/lib/ui/table/table.module.css +30 -0
  87. package/lib/ui/tag/tag.module.css +3 -3
  88. package/lib/ui/toolbar/toolbar.module.css +1 -3
  89. package/lib-esm/app/app.js +4 -8
  90. package/lib-esm/app/app.js.map +1 -1
  91. package/lib-esm/app/app.module.css +2 -2
  92. package/lib-esm/assets/icons.svg +70 -67
  93. package/lib-esm/assets/icons.svg.js +14 -12
  94. package/lib-esm/assets/icons.svg.js.map +1 -1
  95. package/lib-esm/components/bundle-assets/bundle-assets.js +48 -9
  96. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  97. package/lib-esm/components/bundle-assets/bundle-assets.module.css +0 -5
  98. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +26 -15
  99. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  100. package/lib-esm/components/bundle-modules/bundle-modules.i18n.js +1 -1
  101. package/lib-esm/components/bundle-modules/bundle-modules.js +46 -15
  102. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  103. package/lib-esm/components/bundle-modules/bundle-modules.module.css +0 -5
  104. package/lib-esm/components/bundle-packages/bundle-packages.js +46 -7
  105. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  106. package/lib-esm/components/bundle-packages/bundle-packages.module.css +0 -5
  107. package/lib-esm/components/delta/delta.js +1 -1
  108. package/lib-esm/components/delta/delta.js.map +1 -1
  109. package/lib-esm/components/delta/delta.module.css +10 -1
  110. package/lib-esm/components/index.js +0 -1
  111. package/lib-esm/components/index.js.map +1 -1
  112. package/lib-esm/components/jobs-header/jobs-header.module.css +0 -4
  113. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +42 -5
  114. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  115. package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  116. package/lib-esm/components/metrics-table/metrics-table.i18n.js +0 -5
  117. package/lib-esm/components/metrics-table/metrics-table.i18n.js.map +1 -1
  118. package/lib-esm/components/metrics-table/metrics-table.js +4 -37
  119. package/lib-esm/components/metrics-table/metrics-table.js.map +1 -1
  120. package/lib-esm/components/metrics-table/metrics-table.module.css +1 -44
  121. package/lib-esm/components/metrics-table-header/index.js +2 -0
  122. package/lib-esm/components/metrics-table-header/index.js.map +1 -0
  123. package/lib-esm/components/metrics-table-header/metrics-table-header.i18n.js +6 -0
  124. package/lib-esm/components/metrics-table-header/metrics-table-header.i18n.js.map +1 -0
  125. package/lib-esm/components/metrics-table-header/metrics-table-header.js +49 -0
  126. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -0
  127. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +51 -0
  128. package/lib-esm/components/metrics-table-options/metrics-table-options.js +3 -3
  129. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  130. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  131. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  132. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +4 -4
  133. package/lib-esm/components/metrics-treemap/index.js +1 -0
  134. package/lib-esm/components/metrics-treemap/index.js.map +1 -1
  135. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +2 -0
  136. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -0
  137. package/lib-esm/components/metrics-treemap/metrics-treemap.i18n.js +3 -0
  138. package/lib-esm/components/metrics-treemap/metrics-treemap.i18n.js.map +1 -0
  139. package/lib-esm/components/metrics-treemap/metrics-treemap.js +108 -74
  140. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  141. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +156 -51
  142. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +105 -0
  143. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -0
  144. package/lib-esm/components/module-info/module-info.js +1 -2
  145. package/lib-esm/components/module-info/module-info.js.map +1 -1
  146. package/lib-esm/components/run-info/run-info.module.css +7 -7
  147. package/lib-esm/components/sort-button/sort-button.module.css +1 -0
  148. package/lib-esm/css/variables.css +14 -13
  149. package/lib-esm/hooks/metrics-display-type.js +46 -24
  150. package/lib-esm/hooks/metrics-display-type.js.map +1 -1
  151. package/lib-esm/hooks/search-params.js +1 -0
  152. package/lib-esm/hooks/search-params.js.map +1 -1
  153. package/lib-esm/layout/box/box.module.css +4 -2
  154. package/lib-esm/layout/flex-stack/flex-stack.js +3 -4
  155. package/lib-esm/layout/flex-stack/flex-stack.js.map +1 -1
  156. package/lib-esm/layout/flex-stack/flex-stack.module.css +19 -23
  157. package/lib-esm/ui/button/button.js +5 -4
  158. package/lib-esm/ui/button/button.js.map +1 -1
  159. package/lib-esm/ui/button/button.module.css +55 -53
  160. package/lib-esm/ui/dropdown/dropdown.js +8 -39
  161. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  162. package/lib-esm/ui/dropdown/index.js.map +1 -1
  163. package/lib-esm/ui/filters/filters.js +34 -36
  164. package/lib-esm/ui/filters/filters.js.map +1 -1
  165. package/lib-esm/ui/filters/filters.module.css +55 -67
  166. package/lib-esm/ui/hover-card/hover-card.js +1 -1
  167. package/lib-esm/ui/hover-card/hover-card.js.map +1 -1
  168. package/lib-esm/ui/icon/icon.js +2 -1
  169. package/lib-esm/ui/icon/icon.js.map +1 -1
  170. package/lib-esm/ui/input-search/input-search.js +1 -1
  171. package/lib-esm/ui/input-search/input-search.js.map +1 -1
  172. package/lib-esm/ui/loader/loader.js +1 -1
  173. package/lib-esm/ui/loader/loader.js.map +1 -1
  174. package/lib-esm/ui/table/table.module.css +30 -0
  175. package/lib-esm/ui/tag/tag.module.css +3 -3
  176. package/lib-esm/ui/toolbar/toolbar.module.css +1 -3
  177. package/package.json +10 -10
  178. package/src/app/app.jsx +4 -12
  179. package/src/app/app.module.css +2 -2
  180. package/src/assets/icons/check.svg +4 -0
  181. package/src/assets/icons.svg +70 -67
  182. package/src/assets/icons.svg.jsx +12 -12
  183. package/src/components/bundle-assets/bundle-assets.jsx +85 -12
  184. package/src/components/bundle-assets/bundle-assets.module.css +0 -5
  185. package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +71 -35
  186. package/src/components/bundle-modules/bundle-modules.i18n.ts +1 -1
  187. package/src/components/bundle-modules/bundle-modules.module.css +0 -5
  188. package/src/components/bundle-modules/bundle-modules.tsx +123 -45
  189. package/src/components/bundle-packages/bundle-packages.jsx +79 -10
  190. package/src/components/bundle-packages/bundle-packages.module.css +0 -5
  191. package/src/components/delta/delta.jsx +1 -1
  192. package/src/components/delta/delta.module.css +10 -1
  193. package/src/components/index.js +0 -1
  194. package/src/components/jobs-header/jobs-header.module.css +0 -4
  195. package/src/components/metrics-display-selector/metrics-display-selector.module.css +15 -7
  196. package/src/components/metrics-display-selector/metrics-display-selector.stories.tsx +33 -0
  197. package/src/components/metrics-display-selector/metrics-display-selector.tsx +86 -28
  198. package/src/components/metrics-table/metrics-table.i18n.ts +0 -7
  199. package/src/components/metrics-table/metrics-table.module.css +1 -44
  200. package/src/components/metrics-table/metrics-table.tsx +17 -153
  201. package/src/components/metrics-table-header/index.ts +1 -0
  202. package/src/components/metrics-table-header/metrics-table-header.i18n.ts +6 -0
  203. package/src/components/metrics-table-header/metrics-table-header.module.css +51 -0
  204. package/src/components/metrics-table-header/metrics-table-header.tsx +172 -0
  205. package/src/components/metrics-table-options/metrics-table-options.jsx +4 -10
  206. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  207. package/src/components/metrics-table-title/metrics-table-title.module.css +4 -4
  208. package/src/components/metrics-treemap/__tests__/metrics-treemap.utils.ts +188 -0
  209. package/src/components/metrics-treemap/index.ts +1 -0
  210. package/src/components/metrics-treemap/metrics-treemap.constants.ts +52 -0
  211. package/src/components/metrics-treemap/metrics-treemap.i18n.ts +2 -0
  212. package/src/components/metrics-treemap/metrics-treemap.module.css +156 -51
  213. package/src/components/metrics-treemap/metrics-treemap.stories.tsx +44 -10
  214. package/src/components/metrics-treemap/metrics-treemap.tsx +349 -145
  215. package/src/components/metrics-treemap/metrics-treemap.utils.ts +162 -0
  216. package/src/components/module-info/module-info.tsx +1 -1
  217. package/src/components/run-info/run-info.module.css +7 -7
  218. package/src/components/sort-button/sort-button.module.css +1 -0
  219. package/src/css/variables.css +14 -13
  220. package/src/hooks/metrics-display-type.ts +79 -29
  221. package/src/hooks/search-params.js +1 -0
  222. package/src/layout/box/box.module.css +4 -2
  223. package/src/layout/flex-stack/flex-stack.module.css +19 -23
  224. package/src/layout/flex-stack/flex-stack.tsx +2 -8
  225. package/src/ui/button/button.module.css +55 -53
  226. package/src/ui/button/button.stories.tsx +16 -9
  227. package/src/ui/button/button.tsx +8 -7
  228. package/src/ui/dropdown/dropdown.stories.tsx +25 -0
  229. package/src/ui/dropdown/dropdown.tsx +66 -0
  230. package/src/ui/filters/filters.module.css +55 -67
  231. package/src/ui/filters/filters.stories.tsx +109 -0
  232. package/src/ui/filters/filters.tsx +128 -167
  233. package/src/ui/hover-card/hover-card.tsx +1 -1
  234. package/src/ui/icon/icon.tsx +2 -1
  235. package/src/ui/input-search/input-search.tsx +1 -0
  236. package/src/ui/loader/loader.jsx +1 -1
  237. package/src/ui/table/table.module.css +30 -0
  238. package/src/ui/tag/tag.module.css +3 -3
  239. package/src/ui/toolbar/toolbar.module.css +1 -3
  240. package/types/components/bundle-assets-totals/bundle-assets-totals.d.ts +5 -1
  241. package/types/components/bundle-modules/bundle-modules.i18n.d.ts +1 -1
  242. package/types/components/delta/delta.d.ts +2 -2
  243. package/types/components/index.d.ts +0 -1
  244. package/types/components/metrics-display-selector/metrics-display-selector.d.ts +5 -3
  245. package/types/components/metrics-table/metrics-table.d.ts +4 -10
  246. package/types/components/metrics-table/metrics-table.i18n.d.ts +0 -5
  247. package/types/components/metrics-table-header/index.d.ts +1 -0
  248. package/types/components/metrics-table-header/metrics-table-header.d.ts +15 -0
  249. package/types/components/metrics-table-header/metrics-table-header.i18n.d.ts +5 -0
  250. package/types/components/metrics-treemap/index.d.ts +1 -0
  251. package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +20 -0
  252. package/types/components/metrics-treemap/metrics-treemap.d.ts +7 -5
  253. package/types/components/metrics-treemap/metrics-treemap.i18n.d.ts +2 -0
  254. package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +5 -0
  255. package/types/hooks/metrics-display-type.d.ts +8 -2
  256. package/types/ui/dropdown/dropdown.d.ts +12 -47
  257. package/types/ui/dropdown/index.d.ts +1 -1
  258. package/types/ui/filters/filters.d.ts +2 -7
  259. package/types/ui/icon/icon.d.ts +4 -2
  260. package/types/ui/loader/loader.d.ts +2 -2
  261. package/lib/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  262. package/lib/components/total-size-type-title/index.js.map +0 -1
  263. package/lib/components/total-size-type-title/total-size-type-title.js +0 -32
  264. package/lib/components/total-size-type-title/total-size-type-title.js.map +0 -1
  265. package/lib/components/total-size-type-title/total-size-type-title.module.css +0 -14
  266. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  267. package/lib-esm/components/total-size-type-title/index.js +0 -2
  268. package/lib-esm/components/total-size-type-title/index.js.map +0 -1
  269. package/lib-esm/components/total-size-type-title/total-size-type-title.js +0 -25
  270. package/lib-esm/components/total-size-type-title/total-size-type-title.js.map +0 -1
  271. package/lib-esm/components/total-size-type-title/total-size-type-title.module.css +0 -14
  272. package/src/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  273. package/src/components/total-size-type-title/index.js +0 -1
  274. package/src/components/total-size-type-title/total-size-type-title.jsx +0 -41
  275. package/src/components/total-size-type-title/total-size-type-title.module.css +0 -14
  276. package/src/ui/dropdown/dropdown.jsx +0 -88
  277. package/types/components/total-size-type-title/index.d.ts +0 -1
  278. package/types/components/total-size-type-title/total-size-type-title.d.ts +0 -14
  279. /package/src/ui/dropdown/{index.js → index.ts} +0 -0
@@ -1,35 +1,69 @@
1
- import React, { RefObject, forwardRef, useCallback, useMemo, useRef } from 'react';
1
+ import React, {
2
+ type ComponentProps,
3
+ type MouseEventHandler,
4
+ type ReactNode,
5
+ type Ref,
6
+ type RefObject,
7
+ forwardRef,
8
+ useCallback,
9
+ useMemo,
10
+ useRef,
11
+ } from 'react';
2
12
  import { useDebounce, useHoverDirty, useMeasure, useMouseHovered } from 'react-use';
3
13
  import cx from 'classnames';
4
- import type { ReportMetricRow, MetricRunInfo, MetricRunInfoBaseline } from '@bundle-stats/utils';
5
- import { hierarchy, treemap, treemapSquarify, window } from 'd3';
6
- import { Tooltip, TooltipArrow, TooltipAnchor, useTooltipState, useTooltip } from 'ariakit/tooltip';
14
+ import { HierarchyRectangularNode, hierarchy, treemap, treemapSquarify } from 'd3';
15
+ import { Tooltip, TooltipArrow, TooltipAnchor, useTooltipState } from 'ariakit/tooltip';
16
+ import {
17
+ type ReportMetricRow,
18
+ type MetricRunInfo,
19
+ type MetricRunInfoBaseline,
20
+ getMetricRunInfo,
21
+ METRIC_TYPE_CONFIGS,
22
+ } from '@bundle-stats/utils';
7
23
 
8
24
  import { Stack } from '../../layout/stack';
9
25
  import { FileName } from '../../ui/file-name';
10
26
  import { Delta } from '../delta';
11
27
  import { RunInfo } from '../run-info';
28
+ import type { TreeLeaf, TreeNode, Tree } from './metrics-treemap.constants';
29
+ import * as I18N from './metrics-treemap.i18n';
12
30
  import css from './metrics-treemap.module.css';
31
+ import { resolveGroupDeltaType } from './metrics-treemap.utils';
13
32
 
14
- const SQUARIFY_RATIO = 1.78;
15
- const PADDING_OUTER = 0;
16
- const PADDING_INNER = 1;
17
-
18
- type TileSizeDisplay = 'minimal' | 'small' | 'default';
33
+ const SQUARIFY_RATIO = 1.66;
34
+ const PADDING_OUTER = 1;
35
+ const PADDING_INNER = 2;
36
+ const NESTED_PADDING = 4;
37
+ const NESTED_PADDING_TOP = 16 + NESTED_PADDING * 2;
38
+ const NESTED_PADDING_LEFT = 8;
19
39
 
20
40
  /**
21
- * Resolve the tile size using predefined values to avoid
41
+ * Resolve the tile's size using predefined values to avoid
22
42
  * computing the size of the content for every tile
23
43
  */
24
44
  const PADDING_TOP = 4;
25
45
  const PADDING_BOTTOM = 2; // substracted to allow longer texts to appear
26
- const PADDING_LEFT = 16;
46
+ const PADDING_LEFT = 2;
27
47
  const PADDING_RIGHT = 4; // substracted to allow longer texts to not break
28
48
  const VERTICAL_SPACING = 4;
29
49
  const LINE_HEIGHT = 16;
30
50
  const LINE_HEIGHT_SMALL = 13.3;
31
51
 
32
- function resolveLeafSizeDisplay(width: number, height: number): TileSizeDisplay {
52
+ function resolveTileGroupSizeDisplay(width: number, height: number): TileSizeDisplay {
53
+ if (height < NESTED_PADDING_TOP || width < 24) {
54
+ return 'minimal';
55
+ }
56
+
57
+ if (height < NESTED_PADDING_TOP + 8) {
58
+ return 'small';
59
+ }
60
+
61
+ return 'default';
62
+ }
63
+
64
+ type TileSizeDisplay = 'minimal' | 'small' | 'default';
65
+
66
+ function resolveTileSizeDisplay(width: number, height: number): TileSizeDisplay {
33
67
  if (
34
68
  height > PADDING_TOP + PADDING_BOTTOM + VERTICAL_SPACING + LINE_HEIGHT * 2 &&
35
69
  width > PADDING_LEFT + PADDING_RIGHT + 128
@@ -47,19 +81,19 @@ function resolveLeafSizeDisplay(width: number, height: number): TileSizeDisplay
47
81
  return 'minimal';
48
82
  }
49
83
 
50
- interface LeafTooltipContentProps {
84
+ interface TileTooltipContentProps {
51
85
  item: ReportMetricRow;
52
86
  }
53
87
 
54
- const LeafTooltipContent = (props: LeafTooltipContentProps) => {
88
+ const TileTooltipContent = (props: TileTooltipContentProps) => {
55
89
  const { item } = props;
56
90
 
57
91
  const currentRun = item.runs[0] as MetricRunInfo;
58
92
  const baselineRun = item.runs[item.runs.length - 1] as MetricRunInfoBaseline;
59
93
 
60
94
  return (
61
- <Stack space="small" className={css.leafTooltipContent}>
62
- <h3 className={css.leafTooltipContentTitle}>
95
+ <Stack space="small" className={css.tileTooltipContent}>
96
+ <h3 className={css.tileTooltipContentTitle}>
63
97
  <FileName as="code" name={item.label} />
64
98
  </h3>
65
99
  <RunInfo
@@ -72,27 +106,40 @@ const LeafTooltipContent = (props: LeafTooltipContentProps) => {
72
106
  );
73
107
  };
74
108
 
75
- interface LeafContentProps {
76
- sizeDisplay: 'minimal' | 'small' | 'default';
109
+ interface TileContentProps {
110
+ /**
111
+ * Node label
112
+ */
113
+ label: string;
114
+ /**
115
+ * Tile id - metric label or basename for grouped tiles
116
+ */
77
117
  item: ReportMetricRow;
118
+ /**
119
+ * The estimated size of the tile
120
+ */
121
+ sizeDisplay: 'minimal' | 'small' | 'default';
122
+ /**
123
+ * Metric run info
124
+ */
78
125
  runInfo: MetricRunInfo;
79
126
  }
80
127
 
81
- const LeafContent = forwardRef((props: LeafContentProps, ref: React.Ref<HTMLDivElement>) => {
82
- const { sizeDisplay, item, runInfo } = props;
128
+ const TileContent = forwardRef((props: TileContentProps, ref: Ref<HTMLDivElement>) => {
129
+ const { label, sizeDisplay, item, runInfo } = props;
83
130
 
84
131
  if (sizeDisplay === 'minimal') {
85
- return <div className={css.leafContent} ref={ref} />;
132
+ return <div className={css.tileContent} ref={ref} />;
86
133
  }
87
134
 
88
135
  return (
89
- <div className={css.leafContent} ref={ref}>
90
- <p className={css.leadContentLabel}>{item.label}</p>
136
+ <div className={css.tileContent} ref={ref}>
137
+ <FileName as="p" className={css.tileContentLabel} name={label || item.label} />
91
138
  {sizeDisplay !== 'small' && (
92
- <p className={css.leadContentValue}>
93
- <span className={css.leadContentMetric}>{runInfo.displayValue}</span>
139
+ <p className={css.tileContentValue}>
140
+ <span className={css.tileContentMetric}>{runInfo.displayValue}</span>
94
141
  <Delta
95
- className={css.leadContentDelta}
142
+ className={css.tileContentDelta}
96
143
  displayValue={runInfo.displayDeltaPercentage}
97
144
  deltaType={runInfo.deltaType}
98
145
  />
@@ -102,8 +149,8 @@ const LeafContent = forwardRef((props: LeafContentProps, ref: React.Ref<HTMLDivE
102
149
  );
103
150
  });
104
151
 
105
- const LeafContentWithTooltip = (props: LeafContentProps & { parentRef: RefObject<Element> }) => {
106
- const { sizeDisplay, item, runInfo, parentRef } = props;
152
+ const TileContentWithTooltip = (props: TileContentProps & { parentRef: RefObject<Element> }) => {
153
+ const { label, sizeDisplay, item, runInfo, parentRef } = props;
107
154
 
108
155
  const pointer = useMouseHovered(parentRef, { whenHovered: true });
109
156
 
@@ -115,8 +162,8 @@ const LeafContentWithTooltip = (props: LeafContentProps & { parentRef: RefObject
115
162
  }
116
163
 
117
164
  const newRect = {
118
- x: pointer.docX - (document?.defaultView?.pageXOffset ?? 0),
119
- y: pointer.docY - (document?.defaultView?.pageYOffset ?? 0),
165
+ x: pointer.docX - (document?.defaultView?.scrollX ?? 0),
166
+ y: pointer.docY - (document?.defaultView?.scrollY ?? 0),
120
167
  w: 1,
121
168
  h: 1,
122
169
  };
@@ -124,180 +171,337 @@ const LeafContentWithTooltip = (props: LeafContentProps & { parentRef: RefObject
124
171
  return newRect;
125
172
  }, [pointer.docX, pointer.docY]);
126
173
 
127
- const tooltipState = useTooltipState({ gutter: 8, getAnchorRect, timeout: 100 });
174
+ const tooltipState = useTooltipState({ gutter: 12, getAnchorRect, timeout: 120 });
128
175
 
129
176
  // Update tooltip position when poiner values are changing
130
- useDebounce(tooltipState.render, 10, [pointer.docX, pointer.docY]);
177
+ useDebounce(tooltipState.render, 5, [pointer.docX, pointer.docY]);
131
178
 
132
179
  return (
133
180
  <>
134
- <TooltipAnchor state={tooltipState} className={css.leafContentTooltipAnchor}>
135
- <LeafContent sizeDisplay={sizeDisplay} item={item} runInfo={runInfo} />
181
+ <TooltipAnchor state={tooltipState} className={css.tileContentTooltipAnchor}>
182
+ <TileContent label={label} sizeDisplay={sizeDisplay} item={item} runInfo={runInfo} />
136
183
  </TooltipAnchor>
137
184
  <Tooltip state={tooltipState} className={css.tooltip}>
138
- <TooltipArrow state={tooltipState} size={16} className={css.leafTooltipArrow} />
139
- <LeafTooltipContent item={item} />
185
+ <TooltipArrow state={tooltipState} size={16} className={css.tileTooltipArrow} />
186
+ <TileTooltipContent item={item} />
140
187
  </Tooltip>
141
188
  </>
142
189
  );
143
190
  };
144
191
 
145
- const LeafAction = forwardRef((props: React.ComponentProps<'button'>, ref) => (
146
- // @ts-expect-error
147
- <button type="button" xmlns="http://www.w3.org/1999/xhtml" ref={ref} {...props} />
148
- ));
149
-
150
- interface TreeNode {
151
- id: string;
152
- value: number;
153
- item: ReportMetricRow;
154
- children?: Array<TreeNode>;
155
- }
156
-
157
- interface RootTree {
158
- id: string;
159
- value: number;
160
- children: Array<TreeNode>;
161
- }
162
-
163
- interface LeafProps {
164
- x: number;
165
- y: number;
192
+ interface TileProps {
193
+ left: number;
194
+ top: number;
166
195
  width: number;
167
196
  height: number;
168
- data: TreeNode;
197
+ data: TreeLeaf;
169
198
  onClick?: (id: string) => void;
170
199
  }
171
200
 
172
- const Leaf = (props: LeafProps) => {
173
- const { x, y, width, height, data, onClick } = props;
201
+ const Tile = (props: TileProps) => {
202
+ const { left, top, width, height, data, onClick } = props;
174
203
 
175
- const { item } = data;
204
+ const { item, label } = data;
176
205
  const runInfo = item.runs?.[0] as MetricRunInfo;
177
206
 
178
- const sizeDisplay = useMemo(() => resolveLeafSizeDisplay(width, height), [width, height]);
179
- const handleOnClick = useCallback(() => onClick?.(item.key), [item.key]);
207
+ const sizeDisplay = useMemo(() => resolveTileSizeDisplay(width, height), [width, height]);
208
+ const handleOnClick: MouseEventHandler<HTMLDivElement> = useCallback(
209
+ (event) => {
210
+ event.stopPropagation();
211
+ onClick?.(item.key);
212
+ },
213
+ [item.key, onClick],
214
+ );
180
215
 
181
- const leafContentRef = useRef<HTMLElement>(null);
182
- const hover = useHoverDirty(leafContentRef);
216
+ const contentRef = useRef<HTMLDivElement>(null);
217
+ const hover = useHoverDirty(contentRef);
183
218
 
184
- const leafClassName = cx(
185
- css.leaf,
186
- css[`leaf-${runInfo.deltaType}`],
187
- sizeDisplay === 'small' && css.leafSmall,
219
+ const className = cx(
220
+ css.tile,
221
+ css[`tile-${runInfo.deltaType}`],
222
+ sizeDisplay === 'small' && css.tileSizeSmall,
223
+ sizeDisplay === 'default' && css.tileSizeDefault,
224
+ left === 0 && css.tileFirstCol,
188
225
  );
189
226
 
227
+ /* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus */
190
228
  return (
191
- <g className={leafClassName}>
192
- <rect x={x} y={y} width={width} height={height} className={css.leafBackground} />
193
- <rect x={x} y={y} width={width} height={height} className={css.leafBackdrop} />
194
- <foreignObject height={height} width={width} x={x} y={y} className={css.leafWrapper}>
195
- <LeafAction onClick={handleOnClick} ref={leafContentRef} className={css.leafAction}>
196
- {hover ? (
197
- <LeafContentWithTooltip
198
- sizeDisplay={sizeDisplay}
199
- item={item}
200
- runInfo={runInfo}
201
- parentRef={leafContentRef}
202
- />
203
- ) : (
204
- <LeafContent sizeDisplay={sizeDisplay} item={item} runInfo={runInfo} />
229
+ <div
230
+ onClick={handleOnClick}
231
+ role="button"
232
+ aria-label={I18N.TILE_LABEL}
233
+ ref={contentRef}
234
+ style={{ left, top, width, height }}
235
+ className={className}
236
+ >
237
+ {hover ? (
238
+ <TileContentWithTooltip
239
+ label={label}
240
+ sizeDisplay={sizeDisplay}
241
+ item={item}
242
+ runInfo={runInfo}
243
+ parentRef={contentRef}
244
+ />
245
+ ) : (
246
+ <TileContent label={label} sizeDisplay={sizeDisplay} item={item} runInfo={runInfo} />
247
+ )}
248
+ </div>
249
+ );
250
+ };
251
+
252
+ interface TileGroupProps extends ComponentProps<'div'> {
253
+ /**
254
+ * Node title
255
+ */
256
+ title?: string;
257
+ /**
258
+ * Group sum
259
+ */
260
+ total?: Tree['total'];
261
+ /**
262
+ * Node id
263
+ */
264
+ id: string;
265
+ /**
266
+ * Node children
267
+ */
268
+ childNodes: HierarchyRectangularNode<TreeNode>['children'];
269
+ /**
270
+ * On click tile handler
271
+ */
272
+ onItemClick: TileProps['onClick'];
273
+ /**
274
+ * On click group title
275
+ */
276
+ onGroupClick?: (groupId: string) => void;
277
+
278
+ /**
279
+ * Node rectangle dimensions
280
+ */
281
+ width?: number;
282
+ height?: number;
283
+
284
+ /**
285
+ * Node rectangle relative coordinates (relative to parent)
286
+ */
287
+ left: number;
288
+ top: number;
289
+
290
+ /**
291
+ * Node rectangle absolute coordinates (relative to canvas)
292
+ */
293
+ absoluteLeft: number;
294
+ absoluteTop: number;
295
+ }
296
+
297
+ const TileGroup = (props: TileGroupProps) => {
298
+ const {
299
+ title = '',
300
+ total,
301
+ id,
302
+ childNodes,
303
+ onItemClick,
304
+ onGroupClick,
305
+ left,
306
+ top,
307
+ width,
308
+ height,
309
+ absoluteLeft,
310
+ absoluteTop,
311
+ } = props;
312
+
313
+ const onClick: MouseEventHandler<HTMLDivElement> = useCallback(
314
+ (event) => {
315
+ onGroupClick?.(id);
316
+ event.stopPropagation();
317
+ },
318
+ [onGroupClick, id],
319
+ );
320
+
321
+ const displaySize = useMemo(
322
+ () => resolveTileGroupSizeDisplay(width || 0, height || 0),
323
+ [width, height],
324
+ );
325
+
326
+ const metricRunInfo =
327
+ total &&
328
+ getMetricRunInfo(METRIC_TYPE_CONFIGS.METRIC_TYPE_FILE_SIZE, total.current, total.baseline);
329
+ const groupDeltaType = resolveGroupDeltaType(metricRunInfo);
330
+ const rootClassName = cx(css.tileGroup, css[`tileGroup--${groupDeltaType}`]);
331
+
332
+ if (title && displaySize === 'minimal') {
333
+ return (
334
+ <div
335
+ onClick={onClick}
336
+ role="button"
337
+ aria-label={I18N.TILE_GROUP_LABEL}
338
+ className={rootClassName}
339
+ style={{ left, top, width, height }}
340
+ />
341
+ );
342
+ }
343
+
344
+ if (title && displaySize === 'small') {
345
+ return (
346
+ <div
347
+ onClick={onClick}
348
+ role="button"
349
+ aria-label={I18N.TILE_GROUP_LABEL}
350
+ style={{ left, top, width, height }}
351
+ className={rootClassName}
352
+ >
353
+ <div className={css.tileGroupTitle}>{title}</div>
354
+ </div>
355
+ );
356
+ }
357
+
358
+ return (
359
+ <div
360
+ onClick={onClick}
361
+ role="button"
362
+ aria-label={I18N.TILE_GROUP_LABEL}
363
+ className={cx(rootClassName, css.tileGroupSizeDefault)}
364
+ style={{ left, top, width, height }}
365
+ >
366
+ {title && (
367
+ <div className={css.tileGroupTitle}>
368
+ {title}
369
+ {metricRunInfo && (
370
+ <span className={css.tileGroupTitleTotal}>
371
+ {`${metricRunInfo.displayValue}`}
372
+ {'displayDelta' in metricRunInfo && ` (${metricRunInfo.displayDeltaPercentage})`}
373
+ </span>
205
374
  )}
206
- </LeafAction>
207
- </foreignObject>
208
- </g>
375
+ </div>
376
+ )}
377
+ {childNodes?.map((childNode) => {
378
+ if ('children' in childNode) {
379
+ const groupData = childNode.data as Tree;
380
+
381
+ return (
382
+ <TileGroup
383
+ title={groupData.label}
384
+ total={groupData.total}
385
+ id={groupData.id}
386
+ childNodes={childNode.children}
387
+ left={childNode.x0 - absoluteLeft}
388
+ top={childNode.y0 - absoluteTop}
389
+ absoluteLeft={childNode.x0}
390
+ absoluteTop={childNode.y0}
391
+ width={childNode.x1 - childNode.x0}
392
+ height={childNode.y1 - childNode.y0}
393
+ onItemClick={onItemClick}
394
+ onGroupClick={onGroupClick}
395
+ key={groupData.id}
396
+ />
397
+ );
398
+ }
399
+
400
+ return (
401
+ <Tile
402
+ data={childNode.data as TreeLeaf}
403
+ left={childNode.x0 - absoluteLeft}
404
+ top={childNode.y0 - absoluteTop}
405
+ width={childNode.x1 - childNode.x0}
406
+ height={childNode.y1 - childNode.y0}
407
+ onClick={onItemClick}
408
+ key={childNode.data.id}
409
+ />
410
+ );
411
+ })}
412
+ </div>
209
413
  );
210
414
  };
211
415
 
212
416
  interface UseMetricsTreemapHierarchyParams {
213
- items: Array<ReportMetricRow>;
417
+ treeNodes: Tree;
214
418
  width: number;
215
419
  height: number;
420
+ nested: boolean;
216
421
  }
217
422
 
218
423
  function useMetricsTreemapHierarchy(params: UseMetricsTreemapHierarchyParams) {
219
- const { items, width, height } = params;
424
+ const { treeNodes, width, height, nested } = params;
220
425
 
221
426
  const treemapHierarchy = useMemo(() => {
222
- const children = items.map((item) => {
223
- /**
224
- * Set leaf value to the largest run value to allow to
225
- * display the impact of deleted / items
226
- */
227
- const values = item.runs.map((run) => run?.value || 0);
228
- const value = Math.max(...values);
229
-
230
- return {
231
- id: item.key,
232
- value,
233
- item,
234
- };
235
- });
236
-
237
- const treemapData = {
238
- id: 'root',
239
- name: 'root',
240
- value: 0,
241
- children,
242
- };
427
+ const customHierarchy = hierarchy<Tree>(treeNodes);
243
428
 
244
- const customHierarchy = hierarchy<RootTree>(treemapData);
245
429
  customHierarchy.sum((node) => node.value);
430
+ // sort by value descending for all cases
246
431
  // @ts-expect-error
247
432
  customHierarchy.sort((a, b) => b.value - a.value);
248
433
 
249
434
  return customHierarchy;
250
- }, [items]);
435
+ }, [treeNodes]);
251
436
 
252
- const cells = useMemo(() => {
253
- const createTremapLayout = treemap()
437
+ const treemapNodes = useMemo(() => {
438
+ let createTremapLayout = treemap()
254
439
  .size([width, height])
255
440
  .tile(treemapSquarify.ratio(SQUARIFY_RATIO))
256
- .paddingOuter(PADDING_OUTER)
257
- .paddingInner(PADDING_INNER);
258
- // @ts-expect-error
259
- const tremapLayout = createTremapLayout(treemapHierarchy);
441
+ .paddingOuter(PADDING_OUTER);
442
+
443
+ // Add padding top for group title
444
+ if (nested) {
445
+ createTremapLayout = createTremapLayout
446
+ .padding(NESTED_PADDING)
447
+ .paddingLeft(NESTED_PADDING_LEFT)
448
+ .paddingTop(NESTED_PADDING_TOP);
449
+ } else {
450
+ createTremapLayout = createTremapLayout.paddingInner(PADDING_INNER);
451
+ }
260
452
 
261
- return tremapLayout.leaves().filter((leaf) => Boolean(leaf.parent));
453
+ // @ts-expect-error
454
+ return createTremapLayout(treemapHierarchy) as HierarchyRectangularNode<TreeNode>;
262
455
  }, [height, width, treemapHierarchy]);
263
456
 
264
- return cells;
457
+ return treemapNodes;
265
458
  }
266
459
 
267
460
  interface MetricsTreemapProps {
268
- items: Array<ReportMetricRow>;
269
- emptyMessage?: React.ReactNode;
461
+ treeNodes: Tree;
462
+ emptyMessage?: ReactNode;
463
+ nested?: boolean;
270
464
  onItemClick?: (entryId: string) => void;
465
+ onGroupClick?: (entryId: string) => void;
271
466
  }
272
467
 
273
- export const MetricsTreemap = (props: MetricsTreemapProps & React.ComponentProps<'div'>) => {
274
- const { className = '', items, onItemClick, emptyMessage = 'No data', ...restProps } = props;
468
+ export const MetricsTreemap = (props: MetricsTreemapProps & ComponentProps<'div'>) => {
469
+ const {
470
+ className = '',
471
+ treeNodes,
472
+ onItemClick,
473
+ onGroupClick,
474
+ emptyMessage = 'No data',
475
+ nested = false,
476
+ ...restProps
477
+ } = props;
275
478
 
276
479
  const [containerRef, { width, height }] = useMeasure<HTMLDivElement>();
277
- const cells = useMetricsTreemapHierarchy({ items, width, height });
480
+ const rootNode = useMetricsTreemapHierarchy({ treeNodes, width, height, nested });
278
481
 
279
482
  return (
280
- <div className={cx(css.root, className)} {...restProps} ref={containerRef}>
281
- {cells.length > 0 ? (
282
- <svg viewBox={`0 0 ${width} ${height}`} className={css.canvas}>
283
- {cells.map((leaf) => {
284
- if (!leaf.parent) {
285
- return null;
286
- }
287
-
288
- return (
289
- <Leaf
290
- x={leaf.x0}
291
- y={leaf.y0}
292
- width={leaf.x1 - leaf.x0}
293
- height={leaf.y1 - leaf.y0}
294
- data={leaf.data as TreeNode}
295
- onClick={onItemClick}
296
- key={leaf.id}
297
- />
298
- );
299
- })}
300
- </svg>
483
+ <div
484
+ className={cx(css.root, nested && css.nested, className)}
485
+ {...restProps}
486
+ ref={containerRef}
487
+ >
488
+ {rootNode.children && rootNode.children?.length > 0 ? (
489
+ <div className={css.canvas}>
490
+ <TileGroup
491
+ title={nested ? rootNode.data.label : undefined}
492
+ total={(rootNode.data as Tree).total}
493
+ id={rootNode.data.id}
494
+ childNodes={rootNode.children}
495
+ onItemClick={onItemClick}
496
+ onGroupClick={onGroupClick}
497
+ left={rootNode.x0}
498
+ top={rootNode.y0}
499
+ absoluteLeft={rootNode.x0}
500
+ absoluteTop={rootNode.y0}
501
+ width={rootNode.x1 - rootNode.x0}
502
+ height={rootNode.y1 - rootNode.y0}
503
+ />
504
+ </div>
301
505
  ) : (
302
506
  <div className={css.emptyMessage}>
303
507
  <div className={css.emptyMessageWrapper}>{emptyMessage}</div>