@bundle-stats/ui 4.13.0-beta.0 → 4.13.0-beta.10

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 (207) 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/components/bundle-assets/bundle-assets.js +12 -5
  5. package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
  6. package/lib/components/bundle-assets/bundle-assets.module.css +0 -5
  7. package/lib/components/bundle-assets/index.js +2 -2
  8. package/lib/components/bundle-assets/index.js.map +1 -1
  9. package/lib/components/bundle-assets-totals/bundle-assets-totals.js +22 -11
  10. package/lib/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  11. package/lib/components/bundle-modules/bundle-modules.js +22 -12
  12. package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
  13. package/lib/components/bundle-modules/bundle-modules.module.css +0 -5
  14. package/lib/components/bundle-modules/index.js +2 -2
  15. package/lib/components/bundle-modules/index.js.map +1 -1
  16. package/lib/components/bundle-packages/bundle-packages.js +10 -3
  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/bundle-packages/index.js +2 -2
  20. package/lib/components/bundle-packages/index.js.map +1 -1
  21. package/lib/components/index.js +1 -3
  22. package/lib/components/index.js.map +1 -1
  23. package/lib/components/jobs-header/jobs-header.module.css +0 -4
  24. package/lib/components/metrics-display-selector/metrics-display-selector.js +4 -10
  25. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  26. package/lib/components/metrics-table/metrics-table.i18n.js +1 -6
  27. package/lib/components/metrics-table/metrics-table.i18n.js.map +1 -1
  28. package/lib/components/metrics-table/metrics-table.js +4 -37
  29. package/lib/components/metrics-table/metrics-table.js.map +1 -1
  30. package/lib/components/metrics-table/metrics-table.module.css +1 -44
  31. package/lib/components/{total-size-type-title → metrics-table-header}/index.js +1 -1
  32. package/lib/components/metrics-table-header/index.js.map +1 -0
  33. package/lib/components/metrics-table-header/metrics-table-header.i18n.js +9 -0
  34. package/lib/components/metrics-table-header/metrics-table-header.i18n.js.map +1 -0
  35. package/lib/components/metrics-table-header/metrics-table-header.js +79 -0
  36. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -0
  37. package/lib/components/metrics-table-header/metrics-table-header.module.css +51 -0
  38. package/lib/components/metrics-table-options/metrics-table-options.js +2 -2
  39. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  40. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  41. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  42. package/lib/components/metrics-table-title/metrics-table-title.module.css +4 -4
  43. package/lib/components/metrics-treemap/metrics-treemap.js +66 -51
  44. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  45. package/lib/components/metrics-treemap/metrics-treemap.module.css +89 -56
  46. package/lib/components/sort-button/sort-button.module.css +1 -0
  47. package/lib/css/variables.css +11 -11
  48. package/lib/hooks/entry-info.js +12 -0
  49. package/lib/hooks/entry-info.js.map +1 -0
  50. package/lib/layout/flex-stack/flex-stack.js +3 -4
  51. package/lib/layout/flex-stack/flex-stack.js.map +1 -1
  52. package/lib/layout/flex-stack/flex-stack.module.css +19 -23
  53. package/lib/query-state.js +1 -1
  54. package/lib/query-state.js.map +1 -1
  55. package/lib/ui/button/button.module.css +1 -0
  56. package/lib/ui/dropdown/dropdown.js +10 -40
  57. package/lib/ui/dropdown/dropdown.js.map +1 -1
  58. package/lib/ui/dropdown/index.js.map +1 -1
  59. package/lib/ui/filters/filters.js +33 -35
  60. package/lib/ui/filters/filters.js.map +1 -1
  61. package/lib/ui/filters/filters.module.css +55 -67
  62. package/lib/ui/hover-card/hover-card.js +1 -1
  63. package/lib/ui/hover-card/hover-card.js.map +1 -1
  64. package/lib/ui/toolbar/toolbar.module.css +1 -3
  65. package/lib-esm/app/app.js +4 -8
  66. package/lib-esm/app/app.js.map +1 -1
  67. package/lib-esm/app/app.module.css +2 -2
  68. package/lib-esm/components/bundle-assets/bundle-assets.js +12 -5
  69. package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
  70. package/lib-esm/components/bundle-assets/bundle-assets.module.css +0 -5
  71. package/lib-esm/components/bundle-assets/index.js +3 -3
  72. package/lib-esm/components/bundle-assets/index.js.map +1 -1
  73. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js +23 -12
  74. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.js.map +1 -1
  75. package/lib-esm/components/bundle-modules/bundle-modules.js +22 -12
  76. package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
  77. package/lib-esm/components/bundle-modules/bundle-modules.module.css +0 -5
  78. package/lib-esm/components/bundle-modules/index.js +2 -2
  79. package/lib-esm/components/bundle-modules/index.js.map +1 -1
  80. package/lib-esm/components/bundle-packages/bundle-packages.js +10 -3
  81. package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
  82. package/lib-esm/components/bundle-packages/bundle-packages.module.css +0 -5
  83. package/lib-esm/components/bundle-packages/index.js +3 -3
  84. package/lib-esm/components/bundle-packages/index.js.map +1 -1
  85. package/lib-esm/components/index.js +0 -1
  86. package/lib-esm/components/index.js.map +1 -1
  87. package/lib-esm/components/jobs-header/jobs-header.module.css +0 -4
  88. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +4 -10
  89. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  90. package/lib-esm/components/metrics-table/metrics-table.i18n.js +0 -5
  91. package/lib-esm/components/metrics-table/metrics-table.i18n.js.map +1 -1
  92. package/lib-esm/components/metrics-table/metrics-table.js +4 -37
  93. package/lib-esm/components/metrics-table/metrics-table.js.map +1 -1
  94. package/lib-esm/components/metrics-table/metrics-table.module.css +1 -44
  95. package/lib-esm/components/metrics-table-header/index.js +2 -0
  96. package/lib-esm/components/metrics-table-header/index.js.map +1 -0
  97. package/lib-esm/components/metrics-table-header/metrics-table-header.i18n.js +6 -0
  98. package/lib-esm/components/metrics-table-header/metrics-table-header.i18n.js.map +1 -0
  99. package/lib-esm/components/metrics-table-header/metrics-table-header.js +49 -0
  100. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -0
  101. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +51 -0
  102. package/lib-esm/components/metrics-table-options/metrics-table-options.js +3 -3
  103. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  104. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  105. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  106. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +4 -4
  107. package/lib-esm/components/metrics-treemap/metrics-treemap.js +69 -53
  108. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  109. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +89 -56
  110. package/lib-esm/components/sort-button/sort-button.module.css +1 -0
  111. package/lib-esm/css/variables.css +11 -11
  112. package/lib-esm/hooks/entry-info.js +8 -0
  113. package/lib-esm/hooks/entry-info.js.map +1 -0
  114. package/lib-esm/layout/flex-stack/flex-stack.js +3 -4
  115. package/lib-esm/layout/flex-stack/flex-stack.js.map +1 -1
  116. package/lib-esm/layout/flex-stack/flex-stack.module.css +19 -23
  117. package/lib-esm/query-state.js +1 -1
  118. package/lib-esm/query-state.js.map +1 -1
  119. package/lib-esm/ui/button/button.module.css +1 -0
  120. package/lib-esm/ui/dropdown/dropdown.js +8 -39
  121. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  122. package/lib-esm/ui/dropdown/index.js.map +1 -1
  123. package/lib-esm/ui/filters/filters.js +34 -36
  124. package/lib-esm/ui/filters/filters.js.map +1 -1
  125. package/lib-esm/ui/filters/filters.module.css +55 -67
  126. package/lib-esm/ui/hover-card/hover-card.js +1 -1
  127. package/lib-esm/ui/hover-card/hover-card.js.map +1 -1
  128. package/lib-esm/ui/toolbar/toolbar.module.css +1 -3
  129. package/package.json +8 -10
  130. package/src/app/app.jsx +4 -12
  131. package/src/app/app.module.css +2 -2
  132. package/src/components/bundle-assets/bundle-assets.jsx +24 -7
  133. package/src/components/bundle-assets/bundle-assets.module.css +0 -5
  134. package/src/components/bundle-assets/index.jsx +8 -3
  135. package/src/components/bundle-assets-totals/bundle-assets-totals.jsx +55 -24
  136. package/src/components/bundle-modules/bundle-modules.module.css +0 -5
  137. package/src/components/bundle-modules/bundle-modules.tsx +60 -42
  138. package/src/components/bundle-modules/index.tsx +2 -2
  139. package/src/components/bundle-packages/bundle-packages.jsx +19 -5
  140. package/src/components/bundle-packages/bundle-packages.module.css +0 -5
  141. package/src/components/bundle-packages/index.jsx +8 -3
  142. package/src/components/index.js +0 -1
  143. package/src/components/jobs-header/jobs-header.module.css +0 -4
  144. package/src/components/metrics-display-selector/metrics-display-selector.tsx +8 -12
  145. package/src/components/metrics-table/metrics-table.i18n.ts +0 -7
  146. package/src/components/metrics-table/metrics-table.module.css +1 -44
  147. package/src/components/metrics-table/metrics-table.tsx +17 -153
  148. package/src/components/metrics-table-header/index.ts +1 -0
  149. package/src/components/metrics-table-header/metrics-table-header.i18n.ts +6 -0
  150. package/src/components/metrics-table-header/metrics-table-header.module.css +51 -0
  151. package/src/components/metrics-table-header/metrics-table-header.tsx +172 -0
  152. package/src/components/metrics-table-options/metrics-table-options.jsx +4 -10
  153. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  154. package/src/components/metrics-table-title/metrics-table-title.module.css +4 -4
  155. package/src/components/metrics-treemap/metrics-treemap.module.css +89 -56
  156. package/src/components/metrics-treemap/metrics-treemap.tsx +151 -118
  157. package/src/components/sort-button/sort-button.module.css +1 -0
  158. package/src/css/variables.css +11 -11
  159. package/src/hooks/entry-info.ts +19 -0
  160. package/src/layout/flex-stack/flex-stack.module.css +19 -23
  161. package/src/layout/flex-stack/flex-stack.tsx +2 -8
  162. package/src/query-state.tsx +1 -1
  163. package/src/ui/button/button.module.css +1 -0
  164. package/src/ui/dropdown/dropdown.stories.tsx +25 -0
  165. package/src/ui/dropdown/dropdown.tsx +66 -0
  166. package/src/ui/filters/filters.module.css +55 -67
  167. package/src/ui/filters/filters.stories.tsx +109 -0
  168. package/src/ui/filters/filters.tsx +128 -167
  169. package/src/ui/hover-card/hover-card.tsx +1 -1
  170. package/src/ui/toolbar/toolbar.module.css +1 -3
  171. package/types/components/bundle-assets-totals/bundle-assets-totals.d.ts +5 -1
  172. package/types/components/index.d.ts +0 -1
  173. package/types/components/metrics-display-selector/metrics-display-selector.d.ts +2 -2
  174. package/types/components/metrics-table/metrics-table.d.ts +4 -10
  175. package/types/components/metrics-table/metrics-table.i18n.d.ts +0 -5
  176. package/types/components/metrics-table-header/index.d.ts +1 -0
  177. package/types/components/metrics-table-header/metrics-table-header.d.ts +15 -0
  178. package/types/components/metrics-table-header/metrics-table-header.i18n.d.ts +5 -0
  179. package/types/hooks/entry-info.d.ts +8 -0
  180. package/types/ui/dropdown/dropdown.d.ts +12 -47
  181. package/types/ui/dropdown/index.d.ts +1 -1
  182. package/types/ui/filters/filters.d.ts +2 -7
  183. package/lib/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  184. package/lib/components/total-size-type-title/index.js.map +0 -1
  185. package/lib/components/total-size-type-title/total-size-type-title.js +0 -32
  186. package/lib/components/total-size-type-title/total-size-type-title.js.map +0 -1
  187. package/lib/components/total-size-type-title/total-size-type-title.module.css +0 -14
  188. package/lib/hooks/show-entry.js +0 -13
  189. package/lib/hooks/show-entry.js.map +0 -1
  190. package/lib-esm/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  191. package/lib-esm/components/total-size-type-title/index.js +0 -2
  192. package/lib-esm/components/total-size-type-title/index.js.map +0 -1
  193. package/lib-esm/components/total-size-type-title/total-size-type-title.js +0 -25
  194. package/lib-esm/components/total-size-type-title/total-size-type-title.js.map +0 -1
  195. package/lib-esm/components/total-size-type-title/total-size-type-title.module.css +0 -14
  196. package/lib-esm/hooks/show-entry.js +0 -9
  197. package/lib-esm/hooks/show-entry.js.map +0 -1
  198. package/src/components/bundle-assets-totals/bundle-assets-totals.module.css +0 -7
  199. package/src/components/total-size-type-title/index.js +0 -1
  200. package/src/components/total-size-type-title/total-size-type-title.jsx +0 -41
  201. package/src/components/total-size-type-title/total-size-type-title.module.css +0 -14
  202. package/src/hooks/show-entry.ts +0 -17
  203. package/src/ui/dropdown/dropdown.jsx +0 -88
  204. package/types/components/total-size-type-title/index.d.ts +0 -1
  205. package/types/components/total-size-type-title/total-size-type-title.d.ts +0 -14
  206. package/types/hooks/show-entry.d.ts +0 -10
  207. /package/src/ui/dropdown/{index.js → index.ts} +0 -0
@@ -1,16 +1,9 @@
1
- import React, { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
1
+ import React, { RefObject, forwardRef, useCallback, useMemo, useRef } from 'react';
2
+ import { useDebounce, useHoverDirty, useMeasure, useMouseHovered } from 'react-use';
2
3
  import cx from 'classnames';
3
- import { useMeasure } from 'react-use';
4
4
  import type { ReportMetricRow, MetricRunInfo, MetricRunInfoBaseline } from '@bundle-stats/utils';
5
5
  import { hierarchy, treemap, treemapSquarify } from 'd3';
6
- import {
7
- FloatingPortal,
8
- useClientPoint,
9
- useFloating,
10
- useHover,
11
- useInteractions,
12
- } from '@floating-ui/react';
13
- import { arrow, offset } from '@floating-ui/react-dom';
6
+ import { Tooltip, TooltipArrow, TooltipAnchor, useTooltipState } from 'ariakit/tooltip';
14
7
 
15
8
  import { Stack } from '../../layout/stack';
16
9
  import { FileName } from '../../ui/file-name';
@@ -18,9 +11,9 @@ import { Delta } from '../delta';
18
11
  import { RunInfo } from '../run-info';
19
12
  import css from './metrics-treemap.module.css';
20
13
 
21
- const SQUARIFY_RATIO = 1.78;
14
+ const SQUARIFY_RATIO = 1.33;
22
15
  const PADDING_OUTER = 0;
23
- const PADDING_INNER = 1;
16
+ const PADDING_INNER = 2;
24
17
 
25
18
  type TileSizeDisplay = 'minimal' | 'small' | 'default';
26
19
 
@@ -36,10 +29,10 @@ const VERTICAL_SPACING = 4;
36
29
  const LINE_HEIGHT = 16;
37
30
  const LINE_HEIGHT_SMALL = 13.3;
38
31
 
39
- function resolveTileSizeDisplay(width: number, height: number): TileSizeDisplay {
32
+ function resolveLeafSizeDisplay(width: number, height: number): TileSizeDisplay {
40
33
  if (
41
34
  height > PADDING_TOP + PADDING_BOTTOM + VERTICAL_SPACING + LINE_HEIGHT * 2 &&
42
- width > PADDING_LEFT + PADDING_RIGHT + 96
35
+ width > PADDING_LEFT + PADDING_RIGHT + 128
43
36
  ) {
44
37
  return 'default';
45
38
  }
@@ -54,19 +47,20 @@ function resolveTileSizeDisplay(width: number, height: number): TileSizeDisplay
54
47
  return 'minimal';
55
48
  }
56
49
 
57
- interface TooltipContentProps {
58
- data: ReportMetricRow;
50
+ interface LeafTooltipContentProps {
51
+ item: ReportMetricRow;
59
52
  }
60
53
 
61
- const TooltipContent = (props: TooltipContentProps) => {
62
- const { data } = props;
63
- const currentRun = data.runs[0] as MetricRunInfo;
64
- const baselineRun = data.runs[data.runs.length - 1] as MetricRunInfoBaseline;
54
+ const LeafTooltipContent = (props: LeafTooltipContentProps) => {
55
+ const { item } = props;
56
+
57
+ const currentRun = item.runs[0] as MetricRunInfo;
58
+ const baselineRun = item.runs[item.runs.length - 1] as MetricRunInfoBaseline;
65
59
 
66
60
  return (
67
- <Stack space="small" className={css.tooltipContent}>
68
- <h3 className={css.tooltipContentTitle}>
69
- <FileName as="code" name={data.label} />
61
+ <Stack space="small" className={css.leafTooltipContent}>
62
+ <h3 className={css.leafTooltipContentTitle}>
63
+ <FileName as="code" name={item.label} />
70
64
  </h3>
71
65
  <RunInfo
72
66
  current={currentRun.displayValue}
@@ -78,10 +72,75 @@ const TooltipContent = (props: TooltipContentProps) => {
78
72
  );
79
73
  };
80
74
 
81
- const TileButton = forwardRef((props: React.ComponentProps<'button'>, ref) => (
82
- // @ts-expect-error
83
- <button type="button" xmlns="http://www.w3.org/1999/xhtml" ref={ref} {...props} />
84
- ));
75
+ interface LeafContentProps {
76
+ sizeDisplay: 'minimal' | 'small' | 'default';
77
+ item: ReportMetricRow;
78
+ runInfo: MetricRunInfo;
79
+ }
80
+
81
+ const LeafContent = forwardRef((props: LeafContentProps, ref: React.Ref<HTMLDivElement>) => {
82
+ const { sizeDisplay, item, runInfo } = props;
83
+
84
+ if (sizeDisplay === 'minimal') {
85
+ return <div className={css.leafContent} ref={ref} />;
86
+ }
87
+
88
+ return (
89
+ <div className={css.leafContent} ref={ref}>
90
+ <FileName as="p" className={css.leadContentLabel} name={item.label} />
91
+ {sizeDisplay !== 'small' && (
92
+ <p className={css.leadContentValue}>
93
+ <span className={css.leadContentMetric}>{runInfo.displayValue}</span>
94
+ <Delta
95
+ className={css.leadContentDelta}
96
+ displayValue={runInfo.displayDeltaPercentage}
97
+ deltaType={runInfo.deltaType}
98
+ />
99
+ </p>
100
+ )}
101
+ </div>
102
+ );
103
+ });
104
+
105
+ const LeafContentWithTooltip = (props: LeafContentProps & { parentRef: RefObject<Element> }) => {
106
+ const { sizeDisplay, item, runInfo, parentRef } = props;
107
+
108
+ const pointer = useMouseHovered(parentRef, { whenHovered: true });
109
+
110
+ // Return custom rect based on the mouse position
111
+ const getAnchorRect = useCallback(() => {
112
+ // Skip custom component rect area if the pointer position is empty
113
+ if (pointer.docX === 0 && pointer.docY === 0 && pointer.elW === 0 && pointer.elH === 0) {
114
+ return null;
115
+ }
116
+
117
+ const newRect = {
118
+ x: pointer.docX - (document?.defaultView?.scrollX ?? 0),
119
+ y: pointer.docY - (document?.defaultView?.scrollY ?? 0),
120
+ w: 1,
121
+ h: 1,
122
+ };
123
+
124
+ return newRect;
125
+ }, [pointer.docX, pointer.docY]);
126
+
127
+ const tooltipState = useTooltipState({ gutter: 12, getAnchorRect, timeout: 120 });
128
+
129
+ // Update tooltip position when poiner values are changing
130
+ useDebounce(tooltipState.render, 5, [pointer.docX, pointer.docY]);
131
+
132
+ return (
133
+ <>
134
+ <TooltipAnchor state={tooltipState} className={css.leafContentTooltipAnchor}>
135
+ <LeafContent sizeDisplay={sizeDisplay} item={item} runInfo={runInfo} />
136
+ </TooltipAnchor>
137
+ <Tooltip state={tooltipState} className={css.tooltip}>
138
+ <TooltipArrow state={tooltipState} size={16} className={css.leafTooltipArrow} />
139
+ <LeafTooltipContent item={item} />
140
+ </Tooltip>
141
+ </>
142
+ );
143
+ };
85
144
 
86
145
  interface TreeNode {
87
146
  id: string;
@@ -97,106 +156,70 @@ interface RootTree {
97
156
  }
98
157
 
99
158
  interface LeafProps {
100
- x: number;
101
- y: number;
159
+ left: number;
160
+ top: number;
102
161
  width: number;
103
162
  height: number;
104
163
  data: TreeNode;
105
164
  onClick?: (id: string) => void;
106
165
  }
107
166
 
108
- const Tile = (props: LeafProps) => {
109
- const { x, y, width, height, data, onClick } = props;
167
+ const Leaf = (props: LeafProps) => {
168
+ const { left, top, width, height, data, onClick } = props;
169
+
110
170
  const { item } = data;
111
171
  const runInfo = item.runs?.[0] as MetricRunInfo;
112
172
 
113
- const [isOpen, setIsOpen] = useState(false);
114
- const arrowRef = useRef(null);
115
- const { refs, floatingStyles, context, middlewareData } = useFloating({
116
- placement: 'top',
117
- open: isOpen,
118
- onOpenChange: setIsOpen,
119
- middleware: [arrow({ element: arrowRef }), offset(16)],
120
- });
121
- const clientPoint = useClientPoint(context);
122
- const hover = useHover(context);
123
- const { getReferenceProps, getFloatingProps } = useInteractions([hover, clientPoint]);
124
-
125
- const tileSizeDisplay = useMemo(() => resolveTileSizeDisplay(width, height), [width, height]);
126
-
127
- const handleOnClick = useCallback(() => {
128
- if (onClick) {
129
- onClick(item.key);
130
- }
131
- }, [item.key]);
173
+ const sizeDisplay = useMemo(() => resolveLeafSizeDisplay(width, height), [width, height]);
174
+ const handleOnClick = useCallback(() => onClick?.(item.key), [item.key, onClick]);
175
+
176
+ const leafContentRef = useRef<HTMLButtonElement>(null);
177
+ const hover = useHoverDirty(leafContentRef);
178
+
179
+ const leafClassName = cx(
180
+ css.leaf,
181
+ css[`leaf-${runInfo.deltaType}`],
182
+ sizeDisplay === 'small' && css.leafSmall,
183
+ left === 0 && css.leafFirstCol,
184
+ );
132
185
 
133
186
  return (
134
- <g
135
- className={cx(
136
- css.tile,
137
- !item.changed && css['tile-NO_CHANGE'],
138
- css[`tile-${runInfo.deltaType}`],
139
- tileSizeDisplay === 'small' && css.tileSmall,
140
- )}
187
+ <button
188
+ type="button"
189
+ onClick={handleOnClick}
190
+ ref={leafContentRef}
191
+ style={{ left, top, width, height }}
192
+ className={leafClassName}
141
193
  >
142
- <rect x={x} y={y} width={width} height={height} className={css.tileOverlay} />
143
- <foreignObject height={height} width={width} x={x} y={y}>
144
- <TileButton
145
- onClick={handleOnClick}
146
- className={css.tileContent}
147
- ref={refs.setReference}
148
- {...getReferenceProps()}
149
- >
150
- {tileSizeDisplay !== 'minimal' && (
151
- <div className={css.tileContentWrapper}>
152
- <p className={css.tileContentLabel}>{item.label}</p>
153
- <p className={css.tileContentValue}>
154
- <span className={css.tileContentMetric}>{runInfo.displayValue}</span>
155
- <Delta
156
- className={css.tileContentDelta}
157
- displayValue={runInfo.displayDeltaPercentage}
158
- deltaType={runInfo.deltaType}
159
- />
160
- </p>
161
- </div>
162
- )}
163
- </TileButton>
164
- {isOpen && (
165
- <FloatingPortal>
166
- <div
167
- ref={refs.setFloating}
168
- style={floatingStyles}
169
- {...getFloatingProps()}
170
- className={css.tooltip}
171
- >
172
- <div
173
- ref={arrowRef}
174
- style={{ left: middlewareData.arrow?.x }}
175
- className={css.tooltipArrow}
176
- />
177
- <TooltipContent data={item} />
178
- </div>
179
- </FloatingPortal>
180
- )}
181
- </foreignObject>
182
- </g>
194
+ {hover ? (
195
+ <LeafContentWithTooltip
196
+ sizeDisplay={sizeDisplay}
197
+ item={item}
198
+ runInfo={runInfo}
199
+ parentRef={leafContentRef}
200
+ />
201
+ ) : (
202
+ <LeafContent sizeDisplay={sizeDisplay} item={item} runInfo={runInfo} />
203
+ )}
204
+ </button>
183
205
  );
184
206
  };
185
207
 
186
- interface MetricsTreemapProps {
208
+ interface UseMetricsTreemapHierarchyParams {
187
209
  items: Array<ReportMetricRow>;
188
- emptyMessage?: React.ReactNode;
189
- onItemClick?: (entryId: string) => void;
210
+ width: number;
211
+ height: number;
190
212
  }
191
213
 
192
- export const MetricsTreemap = (props: MetricsTreemapProps & React.ComponentProps<'div'>) => {
193
- const { className = '', items, onItemClick, emptyMessage = 'No data', ...restProps } = props;
194
-
195
- const [containerRef, { width, height }] = useMeasure<HTMLDivElement>();
214
+ function useMetricsTreemapHierarchy(params: UseMetricsTreemapHierarchyParams) {
215
+ const { items, width, height } = params;
196
216
 
197
217
  const treemapHierarchy = useMemo(() => {
198
218
  const children = items.map((item) => {
199
- // set tile value to the largest run value to display the impact of deleted items
219
+ /**
220
+ * Set leaf value to the largest run value to allow to
221
+ * display the impact of deleted / items
222
+ */
200
223
  const values = item.runs.map((run) => run?.value || 0);
201
224
  const value = Math.max(...values);
202
225
 
@@ -234,33 +257,43 @@ export const MetricsTreemap = (props: MetricsTreemapProps & React.ComponentProps
234
257
  return tremapLayout.leaves().filter((leaf) => Boolean(leaf.parent));
235
258
  }, [height, width, treemapHierarchy]);
236
259
 
260
+ return cells;
261
+ }
262
+
263
+ interface MetricsTreemapProps {
264
+ items: Array<ReportMetricRow>;
265
+ emptyMessage?: React.ReactNode;
266
+ onItemClick?: (entryId: string) => void;
267
+ }
268
+
269
+ export const MetricsTreemap = (props: MetricsTreemapProps & React.ComponentProps<'div'>) => {
270
+ const { className = '', items, onItemClick, emptyMessage = 'No data', ...restProps } = props;
271
+
272
+ const [containerRef, { width, height }] = useMeasure<HTMLDivElement>();
273
+ const cells = useMetricsTreemapHierarchy({ items, width, height });
274
+
237
275
  return (
238
276
  <div className={cx(css.root, className)} {...restProps} ref={containerRef}>
239
277
  {cells.length > 0 ? (
240
- <svg
241
- width={width}
242
- height={height}
243
- viewBox={`0 0 ${width} ${height}`}
244
- className={css.canvas}
245
- >
278
+ <div className={css.canvas}>
246
279
  {cells.map((leaf) => {
247
280
  if (!leaf.parent) {
248
281
  return null;
249
282
  }
250
283
 
251
284
  return (
252
- <Tile
253
- x={leaf.x0}
254
- y={leaf.y0}
285
+ <Leaf
286
+ data={leaf.data as TreeNode}
287
+ left={leaf.x0}
288
+ top={leaf.y0}
255
289
  width={leaf.x1 - leaf.x0}
256
290
  height={leaf.y1 - leaf.y0}
257
- data={leaf.data as TreeNode}
258
- key={leaf.id}
259
291
  onClick={onItemClick}
292
+ key={leaf.id}
260
293
  />
261
294
  );
262
295
  })}
263
- </svg>
296
+ </div>
264
297
  ) : (
265
298
  <div className={css.emptyMessage}>
266
299
  <div className={css.emptyMessageWrapper}>{emptyMessage}</div>
@@ -3,6 +3,7 @@
3
3
  display: inline-block;
4
4
  border-radius: var(--radius-small);
5
5
  position: relative;
6
+ color: var(--color-text-ultra-light);
6
7
  }
7
8
 
8
9
  .toggle {
@@ -29,20 +29,20 @@
29
29
  /*
30
30
  * Color scheme:
31
31
  * https://coolors.co/287acc-da444e-55b950-efcc1a-192339
32
- * http://mcg.mbitson.com/#!?mcgpalette0=%23da444e&mcgpalette1=%2355b950&mcgpalette2=%23efcc1a&mcgpalette3=%23353138&mcgpalette4=%23287acc&themename=mcgtheme
32
+ * http://mcg.mbitson.com/#!?mcgpalette0=%23da444e&mcgpalette1=%2355b950&mcgpalette2=%23efcc1a&mcgpalette3=%233e3c49&mcgpalette4=%23287acc&themename=bundle-stats
33
33
  * https://hihayk.github.io/scale/#4/5/50/90/-51/0/0/14/353138/53/49/56/white
34
34
  */
35
35
  --color-gray-25: #f9f9fa;
36
36
  --color-gray-50: #e8e8e9;
37
- --color-gray-100: #c6c5c7;
38
- --color-gray-200: #a2a0a4;
39
- --color-gray-300: #7e7b80;
40
- --color-gray-400: #59565c;
41
- --color-gray-500: #353138;
42
- --color-gray-600: #302b31;
43
- --color-gray-700: #2a252a;
44
- --color-gray-800: #231e22;
45
- --color-gray-900: #1c181b;
37
+ --color-gray-100: #c5c5c8;
38
+ --color-gray-200: #9f9ea4;
39
+ --color-gray-300: #787780;
40
+ --color-gray-400: #5b5964;
41
+ --color-gray-500: #3e3c49;
42
+ --color-gray-600: #383642;
43
+ --color-gray-700: #302e39;
44
+ --color-gray-800: #282731;
45
+ --color-gray-900: #1b1a21;
46
46
 
47
47
  --color-gray-ultra-light: var(--color-gray-200);
48
48
  --color-gray-light: var(--color-gray-300);
@@ -199,7 +199,7 @@
199
199
  --outline-hover: 0 0 0 3px var(--color-highlight);
200
200
 
201
201
  /** Layout */
202
- --max-width: 88rem;
202
+ --max-width: 82rem;
203
203
  --entry-info-width: 64rem;
204
204
  --entry-info-top: var(--header-height);
205
205
  }
@@ -0,0 +1,19 @@
1
+ import { useCallback } from 'react';
2
+
3
+ interface UseEntryInfoParams {
4
+ setState: (data: { entryId: string }) => void;
5
+ }
6
+
7
+ type UseEntryInfo = [() => void, (entryId: string) => void];
8
+
9
+ /**
10
+ * Show/hide side panel entry
11
+ */
12
+ export function useEntryInfo(params: UseEntryInfoParams): UseEntryInfo {
13
+ const { setState } = params;
14
+
15
+ const hideEntryInfo = useCallback(() => setState({ entryId: '' }), [setState]);
16
+ const showEntryInfo = useCallback((entryId: string) => setState({ entryId }), [setState]);
17
+
18
+ return [hideEntryInfo, showEntryInfo];
19
+ }
@@ -1,48 +1,44 @@
1
1
  .root {
2
2
  display: flex;
3
- align-items: stretch;
4
3
  justify-content: flex-start;
5
- }
6
-
7
- .root > .item:last-child {
8
- margin-right: 0;
4
+ gap: 0;
9
5
  }
10
6
 
11
7
  /** Space variation */
12
- .space--xxxsmall > .item {
13
- margin: 0 var(--space-xxxsmall) 0 0;
8
+ .space--xxxsmall {
9
+ gap: var(--space-xxxsmall);
14
10
  }
15
11
 
16
- .space--xxsmall > .item {
17
- margin: 0 var(--space-xxsmall) 0 0;
12
+ .space--xxsmall {
13
+ gap: var(--space-xxsmall);
18
14
  }
19
15
 
20
- .space--xsmall > .item {
21
- margin: 0 var(--space-xsmall) 0 0;
16
+ .space--xsmall {
17
+ gap: var(--space-xsmall);
22
18
  }
23
19
 
24
- .space--small > .item {
25
- margin: 0 var(--space-small) 0 0;
20
+ .space--small {
21
+ gap: var(--space-small);
26
22
  }
27
23
 
28
- .space--medium > .item {
29
- margin: 0 var(--space-medium) 0 0;
24
+ .space--medium {
25
+ gap: var(--space-medium);
30
26
  }
31
27
 
32
- .space--large > .item {
33
- margin: 0 var(--space-large) 0 0;
28
+ .space--large {
29
+ gap: var(--space-large);
34
30
  }
35
31
 
36
- .space--xlarge > .item {
37
- margin: 0 var(--space-xlarge) 0 0;
32
+ .space--xlarge {
33
+ gap: var(--space-xlarge);
38
34
  }
39
35
 
40
- .space--xxlarge > .item {
41
- margin: 0 var(--space-xxlarge) 0 0;
36
+ .space--xxlarge {
37
+ gap: var(--space-xxlarge);
42
38
  }
43
39
 
44
- .space--xxxlarge > .item {
45
- margin: 0 var(--space-xxxlarge) 0 0;
40
+ .space--xxxlarge {
41
+ gap: var(--space-xxxlarge);
46
42
  }
47
43
 
48
44
  /** Align items variation */
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import cx from 'classnames';
3
3
 
4
4
  import { NO_SPACE, SPACES } from '../../tokens';
5
- import { getRenderChildWithClassName } from '../../utils';
6
5
  import css from './flex-stack.module.css';
7
6
 
8
7
  type SpaceValue = (typeof SPACES)[number];
@@ -22,22 +21,17 @@ export const FlexStack = <T extends React.ElementType = 'div'>(
22
21
  className = '',
23
22
  space = NO_SPACE,
24
23
  inline = false,
25
- children = null,
26
24
  alignItems = '',
27
25
  ...restProps
28
26
  } = props;
29
27
 
30
28
  const rootClassName = cx(
31
- className,
32
29
  css.root,
33
30
  css[`space--${space}`],
34
31
  css[`align-items--${alignItems}`],
35
32
  inline && css.inline,
33
+ className,
36
34
  );
37
35
 
38
- return (
39
- <Component {...restProps} className={rootClassName}>
40
- {React.Children.map(children, getRenderChildWithClassName(css.item))}
41
- </Component>
42
- );
36
+ return <Component {...restProps} className={rootClassName} />;
43
37
  };
@@ -30,7 +30,7 @@ export const useComponentQueryState = (componentName: string) => {
30
30
 
31
31
  setQueryState({ [componentName]: newState });
32
32
  },
33
- [componentState],
33
+ [componentState, setQueryState],
34
34
  );
35
35
 
36
36
  return [componentState, setState];
@@ -256,6 +256,7 @@ a.root {
256
256
  .small .glyph {
257
257
  width: calc(var(--space-xsmall) + 2px);
258
258
  height: calc(var(--space-xsmall) + 2px);
259
+ margin: 1px; /* compensate to keep the same height when the text is not provided */
259
260
  }
260
261
 
261
262
  .medium {
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { Meta } from '@storybook/react';
3
+
4
+ import { getWrapperDecorator } from '../../stories';
5
+ import { Dropdown, DropdownItem } from '.';
6
+
7
+ export default {
8
+ title: 'UI/Dropdown',
9
+ component: Dropdown,
10
+ decorators: [getWrapperDecorator()],
11
+ } as Meta;
12
+
13
+ export const Defualt = () => (
14
+ <Dropdown glyph="filter" label="Toggle">
15
+ <DropdownItem>Option 1</DropdownItem>
16
+ <DropdownItem>Option 2</DropdownItem>
17
+ </Dropdown>
18
+ );
19
+
20
+ export const Disabled = () => (
21
+ <Dropdown glyph="filter" label="Toggle" disabled>
22
+ <DropdownItem>Option 1</DropdownItem>
23
+ <DropdownItem>Option 2</DropdownItem>
24
+ </Dropdown>
25
+ );
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import cx from 'classnames';
3
+ import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
4
+
5
+ import { Button, BUTTON_SIZE } from '../button';
6
+ import css from './dropdown.module.css';
7
+
8
+ interface DropdownItemProps {
9
+ isActive?: boolean;
10
+ }
11
+
12
+ export const DropdownItem = (props: DropdownItemProps & React.ComponentProps<typeof MenuItem>) => {
13
+ const { className = '', isActive = false, ...restProps } = props;
14
+
15
+ return (
16
+ <MenuItem className={cx(css.item, className, isActive && css.itemActive)} {...restProps} />
17
+ );
18
+ };
19
+
20
+ interface DropdownProps {
21
+ dropdownClassName?: string;
22
+ label?: React.ReactNode;
23
+ ariaLabel?: string;
24
+ glyph?: string;
25
+ disabled?: boolean;
26
+ }
27
+
28
+ export const Dropdown = (props: DropdownProps & React.ComponentProps<'div'>) => {
29
+ const {
30
+ className = '',
31
+ dropdownClassName = '',
32
+ label = null,
33
+ ariaLabel = '',
34
+ glyph = '',
35
+ disabled = false,
36
+ children,
37
+ } = props;
38
+
39
+ const dropdownAriaLabel = ariaLabel || (typeof label === 'string' ? label : '');
40
+ const menuState = useMenuState();
41
+
42
+ return (
43
+ <>
44
+ <MenuButton
45
+ as={Button}
46
+ outline
47
+ size={BUTTON_SIZE.SMALL}
48
+ glyph={glyph}
49
+ disabled={disabled}
50
+ state={menuState}
51
+ tabIndex={null}
52
+ className={cx(css.button, className)}
53
+ >
54
+ {label}
55
+ </MenuButton>
56
+ <Menu
57
+ portal
58
+ state={menuState}
59
+ aria-label={dropdownAriaLabel}
60
+ className={cx(css.dropdown, dropdownClassName)}
61
+ >
62
+ {children}
63
+ </Menu>
64
+ </>
65
+ );
66
+ };