@bundle-stats/ui 4.13.0-rc.0 → 4.13.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/bundle-assets/bundle-assets.js +24 -22
- package/lib/components/bundle-assets/bundle-assets.js.map +1 -1
- package/lib/components/bundle-assets/index.js +3 -1
- package/lib/components/bundle-assets/index.js.map +1 -1
- package/lib/components/bundle-modules/bundle-modules.js +11 -6
- package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib/components/bundle-modules/bundle-modules.utils.js +2 -13
- package/lib/components/bundle-modules/bundle-modules.utils.js.map +1 -1
- package/lib/components/bundle-modules/index.js +2 -1
- package/lib/components/bundle-modules/index.js.map +1 -1
- package/lib/components/bundle-packages/bundle-packages.js +12 -6
- package/lib/components/bundle-packages/bundle-packages.js.map +1 -1
- package/lib/components/metrics-treemap/metrics-treemap.constants.js +7 -0
- package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
- package/lib/components/metrics-treemap/metrics-treemap.js +69 -71
- package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
- package/lib/components/metrics-treemap/metrics-treemap.module.css +58 -26
- package/lib/components/metrics-treemap/metrics-treemap.utils.js +60 -1
- package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
- package/lib/utils/colors.js +22 -0
- package/lib/utils/colors.js.map +1 -0
- package/lib/{utils.js → utils/components.js} +2 -17
- package/lib/utils/components.js.map +1 -0
- package/lib/utils/index.js +20 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/jobs.js +18 -0
- package/lib/utils/jobs.js.map +1 -0
- package/lib-esm/components/bundle-assets/bundle-assets.js +24 -23
- package/lib-esm/components/bundle-assets/bundle-assets.js.map +1 -1
- package/lib-esm/components/bundle-assets/index.js +3 -1
- package/lib-esm/components/bundle-assets/index.js.map +1 -1
- package/lib-esm/components/bundle-modules/bundle-modules.js +11 -6
- package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib-esm/components/bundle-modules/bundle-modules.utils.js +1 -11
- package/lib-esm/components/bundle-modules/bundle-modules.utils.js.map +1 -1
- package/lib-esm/components/bundle-modules/index.js +3 -2
- package/lib-esm/components/bundle-modules/index.js.map +1 -1
- package/lib-esm/components/bundle-packages/bundle-packages.js +12 -6
- package/lib-esm/components/bundle-packages/bundle-packages.js.map +1 -1
- package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +6 -1
- package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
- package/lib-esm/components/metrics-treemap/metrics-treemap.js +65 -68
- package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
- package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +58 -26
- package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +57 -0
- package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
- package/lib-esm/utils/colors.js +15 -0
- package/lib-esm/utils/colors.js.map +1 -0
- package/lib-esm/utils/components.js +16 -0
- package/lib-esm/utils/components.js.map +1 -0
- package/lib-esm/utils/index.js +4 -0
- package/lib-esm/utils/index.js.map +1 -0
- package/lib-esm/utils/jobs.js +11 -0
- package/lib-esm/utils/jobs.js.map +1 -0
- package/package.json +3 -3
- package/src/components/bundle-assets/bundle-assets.jsx +74 -48
- package/src/components/bundle-assets/index.jsx +4 -0
- package/src/components/bundle-modules/bundle-modules.tsx +33 -11
- package/src/components/bundle-modules/bundle-modules.types.d.ts +2 -17
- package/src/components/bundle-modules/bundle-modules.utils.ts +7 -18
- package/src/components/bundle-modules/index.tsx +4 -3
- package/src/components/bundle-packages/bundle-packages.jsx +21 -7
- package/src/components/metrics-treemap/metrics-treemap.constants.ts +11 -0
- package/src/components/metrics-treemap/metrics-treemap.module.css +58 -26
- package/src/components/metrics-treemap/metrics-treemap.tsx +173 -110
- package/src/components/metrics-treemap/metrics-treemap.utils.ts +107 -1
- package/src/utils/colors.ts +19 -0
- package/src/utils/components.tsx +19 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/jobs.ts +22 -0
- package/types/components/bundle-assets/bundle-assets.d.ts +1 -5
- package/types/components/bundle-modules/bundle-modules.d.ts +4 -2
- package/types/components/bundle-modules/bundle-modules.utils.d.ts +3 -7
- package/types/components/bundle-modules/index.d.ts +2 -2
- package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +8 -0
- package/types/components/metrics-treemap/metrics-treemap.d.ts +1 -1
- package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +11 -1
- package/types/utils/colors.d.ts +1 -0
- package/types/utils/components.d.ts +2 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/jobs.d.ts +7 -0
- package/lib/utils.js.map +0 -1
- package/lib-esm/utils.js +0 -30
- package/lib-esm/utils.js.map +0 -1
- package/src/utils.jsx +0 -38
- package/types/utils.d.ts +0 -3
|
@@ -25,40 +25,72 @@
|
|
|
25
25
|
|
|
26
26
|
.tileGroupTitle {
|
|
27
27
|
width: 100%;
|
|
28
|
-
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.tileGroupTitleContent {
|
|
31
|
+
width: 100%;
|
|
32
|
+
padding: var(--space-xxxsmall) 2px var(--space-xxxsmall) var(--space-xxsmall);
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
29
35
|
color: var(--color-text);
|
|
30
36
|
font-family: var(--font-family-fixed);
|
|
31
37
|
font-size: 10px;
|
|
32
|
-
line-height:
|
|
38
|
+
line-height: var(--space-small);
|
|
33
39
|
overflow: hidden;
|
|
34
40
|
white-space: nowrap;
|
|
35
41
|
text-overflow: ellipsis;
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
.tileGroupTitleText,
|
|
38
45
|
.tileGroupTitleTotal {
|
|
39
|
-
|
|
46
|
+
flex: 0 0 auto;
|
|
47
|
+
min-width: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.tileGroupTitleTotal {
|
|
51
|
+
margin-left: var(--space-xxsmall);
|
|
52
|
+
color: var(--color-text-light);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.tileGroupTitle {
|
|
56
|
+
transition: var(--ui-transition-out);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.tileGroupTitle:hover .tileGroupTitleText {
|
|
60
|
+
text-decoration: underline;
|
|
61
|
+
transition: var(--ui-transition-in);
|
|
40
62
|
}
|
|
41
63
|
|
|
42
64
|
/** Tile group size variation */
|
|
43
|
-
.
|
|
65
|
+
.tileGroup {
|
|
44
66
|
transition: var(--ui-transition-in);
|
|
45
67
|
transition-property: left, top, width, height;
|
|
46
68
|
}
|
|
47
69
|
|
|
70
|
+
.tileGroup--minimal .tileGroupTitle,
|
|
71
|
+
.tileGroup--small .tileGroupTitle {
|
|
72
|
+
height: 100%; /* full height when title is missing or we do not render any children */
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.tileGroup--minimal .tileGroupTitleContent,
|
|
76
|
+
.tileGroup--small .tileGroupTitleContent {
|
|
77
|
+
min-height: 100%;
|
|
78
|
+
}
|
|
79
|
+
|
|
48
80
|
/** Tile group nested variation */
|
|
49
81
|
.nested .tileGroup {
|
|
50
|
-
background-color: rgba(27, 26, 33, 0.
|
|
51
|
-
outline: 1px solid rgba(255, 255, 255, 0.
|
|
82
|
+
background-color: rgba(27, 26, 33, 0.03);
|
|
83
|
+
outline: 1px solid rgba(255, 255, 255, 0.6);
|
|
52
84
|
}
|
|
53
85
|
|
|
54
86
|
.nested .tileGroup:has(> .tileGroupTitle:hover),
|
|
55
87
|
.nested .tileGroup:has(> .tile:hover) {
|
|
56
|
-
background-color: rgba(27, 26, 33, 0.
|
|
88
|
+
background-color: rgba(27, 26, 33, 0.06);
|
|
57
89
|
outline: 1px solid rgba(255, 255, 255, 1);
|
|
58
90
|
}
|
|
59
91
|
|
|
60
92
|
.nested .tileGroup--NEGATIVE {
|
|
61
|
-
background-color: rgba(194, 31, 37, 0.
|
|
93
|
+
background-color: rgba(194, 31, 37, 0.04);
|
|
62
94
|
}
|
|
63
95
|
|
|
64
96
|
.nested .tileGroup--NEGATIVE:has(> .tileGroupTitle:hover),
|
|
@@ -67,7 +99,7 @@
|
|
|
67
99
|
}
|
|
68
100
|
|
|
69
101
|
.nested .tileGroup--POSITIVE {
|
|
70
|
-
background-color: rgba(42, 147, 39, 0.
|
|
102
|
+
background-color: rgba(42, 147, 39, 0.04);
|
|
71
103
|
}
|
|
72
104
|
|
|
73
105
|
.nested .tileGroup--POSITIVE:has(> .tileGroupTitle:hover),
|
|
@@ -141,7 +173,13 @@
|
|
|
141
173
|
flex: 0 1 auto;
|
|
142
174
|
width: 100%;
|
|
143
175
|
min-width: 0;
|
|
144
|
-
max-height: calc(2em * var(--line-height-heading));
|
|
176
|
+
max-height: calc(2em * var(--line-height-heading)); /* render max 2 rows */
|
|
177
|
+
overflow-wrap: break-word;
|
|
178
|
+
word-wrap: break-word;
|
|
179
|
+
word-break: break-all;
|
|
180
|
+
word-break: break-word;
|
|
181
|
+
hyphens: auto;
|
|
182
|
+
white-space: normal;
|
|
145
183
|
overflow: hidden;
|
|
146
184
|
}
|
|
147
185
|
|
|
@@ -162,9 +200,8 @@
|
|
|
162
200
|
|
|
163
201
|
/** Tile size variation */
|
|
164
202
|
.tileSizeSmall .tileContent {
|
|
165
|
-
padding: var(--space-xxxsmall)
|
|
166
|
-
|
|
167
|
-
font-size: var(--size-xsmall);
|
|
203
|
+
padding: var(--space-xxxsmall);
|
|
204
|
+
font-size: 10px;
|
|
168
205
|
}
|
|
169
206
|
|
|
170
207
|
.tileSizeSmall .tileContentLabel {
|
|
@@ -173,7 +210,7 @@
|
|
|
173
210
|
|
|
174
211
|
.tileSizeDefault {
|
|
175
212
|
transition: var(--ui-transition-in);
|
|
176
|
-
transition-property: left top width height;
|
|
213
|
+
transition-property: left, top, width, height;
|
|
177
214
|
}
|
|
178
215
|
|
|
179
216
|
/** Tile diff variation */
|
|
@@ -187,7 +224,7 @@
|
|
|
187
224
|
color: var(--color-highlight);
|
|
188
225
|
transition: var(--ui-transition-out);
|
|
189
226
|
transition-property: opacity;
|
|
190
|
-
opacity: 0.
|
|
227
|
+
opacity: 0.6;
|
|
191
228
|
}
|
|
192
229
|
|
|
193
230
|
.tile:hover::before {
|
|
@@ -196,11 +233,16 @@
|
|
|
196
233
|
}
|
|
197
234
|
|
|
198
235
|
.tile-NO_CHANGE {
|
|
199
|
-
color: var(--color-text-
|
|
236
|
+
color: var(--color-text-light);
|
|
200
237
|
}
|
|
201
238
|
|
|
202
239
|
.tile-NO_CHANGE::before {
|
|
203
240
|
color: var(--color-gray-50);
|
|
241
|
+
opacity: 0.4;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.tile-NO_CHANGE:hover::before {
|
|
245
|
+
opacity: 0.2;
|
|
204
246
|
}
|
|
205
247
|
|
|
206
248
|
.tile-HIGH_NEGATIVE::before {
|
|
@@ -213,20 +255,10 @@
|
|
|
213
255
|
|
|
214
256
|
.tile-LOW_NEGATIVE::before {
|
|
215
257
|
color: var(--color-red-50);
|
|
216
|
-
opacity: 0.6;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.tile-LOW_NEGATIVE:hover::before {
|
|
220
|
-
opacity: 0.5;
|
|
221
258
|
}
|
|
222
259
|
|
|
223
260
|
.tile-LOW_POSITIVE::before {
|
|
224
261
|
color: var(--color-green-50);
|
|
225
|
-
opacity: 0.6;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.tile-LOW_POSITIVE:hover::before {
|
|
229
|
-
opacity: 0.5;
|
|
230
262
|
}
|
|
231
263
|
|
|
232
264
|
.tile-POSITIVE::before {
|
|
@@ -9,77 +9,43 @@ import React, {
|
|
|
9
9
|
useMemo,
|
|
10
10
|
useRef,
|
|
11
11
|
} from 'react';
|
|
12
|
-
import {
|
|
12
|
+
import { useHoverDirty, useMeasure } from 'react-use';
|
|
13
13
|
import cx from 'classnames';
|
|
14
14
|
import { HierarchyRectangularNode, hierarchy, treemap, treemapSquarify } from 'd3';
|
|
15
|
-
import { Tooltip, TooltipArrow, TooltipAnchor
|
|
15
|
+
import { Tooltip, TooltipArrow, TooltipAnchor } from 'ariakit/tooltip';
|
|
16
16
|
import {
|
|
17
17
|
type ReportMetricRow,
|
|
18
18
|
type MetricRunInfo,
|
|
19
19
|
type MetricRunInfoBaseline,
|
|
20
|
-
getMetricRunInfo,
|
|
21
20
|
METRIC_TYPE_CONFIGS,
|
|
21
|
+
formatFileSize,
|
|
22
|
+
getMetricRunInfo,
|
|
22
23
|
} from '@bundle-stats/utils';
|
|
23
24
|
|
|
24
25
|
import { Stack } from '../../layout/stack';
|
|
25
26
|
import { FileName } from '../../ui/file-name';
|
|
26
27
|
import { Delta } from '../delta';
|
|
27
28
|
import { RunInfo } from '../run-info';
|
|
28
|
-
import
|
|
29
|
+
import {
|
|
30
|
+
type TreeLeaf,
|
|
31
|
+
type TreeNode,
|
|
32
|
+
type Tree,
|
|
33
|
+
SQUARIFY_RATIO,
|
|
34
|
+
PADDING_OUTER,
|
|
35
|
+
PADDING_INNER,
|
|
36
|
+
NESTED_PADDING,
|
|
37
|
+
NESTED_PADDING_TOP,
|
|
38
|
+
NESTED_PADDING_LEFT,
|
|
39
|
+
TileSizeDisplay,
|
|
40
|
+
} from './metrics-treemap.constants';
|
|
29
41
|
import * as I18N from './metrics-treemap.i18n';
|
|
30
42
|
import css from './metrics-treemap.module.css';
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const NESTED_PADDING_TOP = 16 + NESTED_PADDING * 2;
|
|
38
|
-
const NESTED_PADDING_LEFT = 8;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Resolve the tile's size using predefined values to avoid
|
|
42
|
-
* computing the size of the content for every tile
|
|
43
|
-
*/
|
|
44
|
-
const PADDING_TOP = 4;
|
|
45
|
-
const PADDING_BOTTOM = 2; // substracted to allow longer texts to appear
|
|
46
|
-
const PADDING_LEFT = 2;
|
|
47
|
-
const PADDING_RIGHT = 4; // substracted to allow longer texts to not break
|
|
48
|
-
const VERTICAL_SPACING = 4;
|
|
49
|
-
const LINE_HEIGHT = 16;
|
|
50
|
-
const LINE_HEIGHT_SMALL = 13.3;
|
|
51
|
-
|
|
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 {
|
|
67
|
-
if (
|
|
68
|
-
height > PADDING_TOP + PADDING_BOTTOM + VERTICAL_SPACING + LINE_HEIGHT * 2 &&
|
|
69
|
-
width > PADDING_LEFT + PADDING_RIGHT + 128
|
|
70
|
-
) {
|
|
71
|
-
return 'default';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
height > PADDING_TOP + PADDING_BOTTOM + LINE_HEIGHT_SMALL * 2 &&
|
|
76
|
-
width > PADDING_LEFT + PADDING_BOTTOM + 48
|
|
77
|
-
) {
|
|
78
|
-
return 'small';
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return 'minimal';
|
|
82
|
-
}
|
|
43
|
+
import {
|
|
44
|
+
resolveGroupDeltaType,
|
|
45
|
+
resolveTileSizeDisplay,
|
|
46
|
+
resolveTileGroupSizeDisplay,
|
|
47
|
+
useTooltipStateWithMouseFollow,
|
|
48
|
+
} from './metrics-treemap.utils';
|
|
83
49
|
|
|
84
50
|
interface TileTooltipContentProps {
|
|
85
51
|
item: ReportMetricRow;
|
|
@@ -118,7 +84,7 @@ interface TileContentProps {
|
|
|
118
84
|
/**
|
|
119
85
|
* The estimated size of the tile
|
|
120
86
|
*/
|
|
121
|
-
sizeDisplay:
|
|
87
|
+
sizeDisplay: TileSizeDisplay;
|
|
122
88
|
/**
|
|
123
89
|
* Metric run info
|
|
124
90
|
*/
|
|
@@ -128,23 +94,33 @@ interface TileContentProps {
|
|
|
128
94
|
const TileContent = forwardRef((props: TileContentProps, ref: Ref<HTMLDivElement>) => {
|
|
129
95
|
const { label, sizeDisplay, item, runInfo } = props;
|
|
130
96
|
|
|
97
|
+
// Render only the container
|
|
131
98
|
if (sizeDisplay === 'minimal') {
|
|
132
99
|
return <div className={css.tileContent} ref={ref} />;
|
|
133
100
|
}
|
|
134
101
|
|
|
102
|
+
const resolvedLabel = label || item.label;
|
|
103
|
+
|
|
104
|
+
// Render only the label
|
|
105
|
+
if (sizeDisplay === 'small') {
|
|
106
|
+
return (
|
|
107
|
+
<div className={css.tileContent} ref={ref}>
|
|
108
|
+
<p className={css.tileContentLabel}>{resolvedLabel}</p>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
135
113
|
return (
|
|
136
114
|
<div className={css.tileContent} ref={ref}>
|
|
137
|
-
<
|
|
138
|
-
{
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</p>
|
|
147
|
-
)}
|
|
115
|
+
<p className={css.tileContentLabel}>{label || item.label}</p>
|
|
116
|
+
<p className={css.tileContentValue}>
|
|
117
|
+
<span className={css.tileContentMetric}>{runInfo.displayValue}</span>
|
|
118
|
+
<Delta
|
|
119
|
+
className={css.tileContentDelta}
|
|
120
|
+
displayValue={runInfo.displayDeltaPercentage}
|
|
121
|
+
deltaType={runInfo.deltaType}
|
|
122
|
+
/>
|
|
123
|
+
</p>
|
|
148
124
|
</div>
|
|
149
125
|
);
|
|
150
126
|
});
|
|
@@ -152,29 +128,7 @@ const TileContent = forwardRef((props: TileContentProps, ref: Ref<HTMLDivElement
|
|
|
152
128
|
const TileContentWithTooltip = (props: TileContentProps & { parentRef: RefObject<Element> }) => {
|
|
153
129
|
const { label, sizeDisplay, item, runInfo, parentRef } = props;
|
|
154
130
|
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
// Return custom rect based on the mouse position
|
|
158
|
-
const getAnchorRect = useCallback(() => {
|
|
159
|
-
// Skip custom component rect area if the pointer position is empty
|
|
160
|
-
if (pointer.docX === 0 && pointer.docY === 0 && pointer.elW === 0 && pointer.elH === 0) {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const newRect = {
|
|
165
|
-
x: pointer.docX - (document?.defaultView?.scrollX ?? 0),
|
|
166
|
-
y: pointer.docY - (document?.defaultView?.scrollY ?? 0),
|
|
167
|
-
w: 1,
|
|
168
|
-
h: 1,
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
return newRect;
|
|
172
|
-
}, [pointer.docX, pointer.docY]);
|
|
173
|
-
|
|
174
|
-
const tooltipState = useTooltipState({ gutter: 12, getAnchorRect, timeout: 120 });
|
|
175
|
-
|
|
176
|
-
// Update tooltip position when poiner values are changing
|
|
177
|
-
useDebounce(tooltipState.render, 5, [pointer.docX, pointer.docY]);
|
|
131
|
+
const tooltipState = useTooltipStateWithMouseFollow({ parentRef });
|
|
178
132
|
|
|
179
133
|
return (
|
|
180
134
|
<>
|
|
@@ -221,7 +175,7 @@ const Tile = (props: TileProps) => {
|
|
|
221
175
|
css[`tile-${runInfo.deltaType}`],
|
|
222
176
|
sizeDisplay === 'small' && css.tileSizeSmall,
|
|
223
177
|
sizeDisplay === 'default' && css.tileSizeDefault,
|
|
224
|
-
left ===
|
|
178
|
+
left === PADDING_INNER && css.tileFirstCol,
|
|
225
179
|
);
|
|
226
180
|
|
|
227
181
|
/* eslint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus */
|
|
@@ -249,6 +203,98 @@ const Tile = (props: TileProps) => {
|
|
|
249
203
|
);
|
|
250
204
|
};
|
|
251
205
|
|
|
206
|
+
interface TileGroupTitleTooltipContentProps {
|
|
207
|
+
title: string;
|
|
208
|
+
runInfo?: MetricRunInfo;
|
|
209
|
+
baselineDisplayValue?: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const TileGroupTitleTooltipContent = (props: TileGroupTitleTooltipContentProps) => {
|
|
213
|
+
const { title, runInfo, baselineDisplayValue } = props;
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<Stack space="small" className={css.tileTooltip}>
|
|
217
|
+
<h3 className={css.tileTooltipContentTitle}>
|
|
218
|
+
<FileName as="code" name={title} />
|
|
219
|
+
</h3>
|
|
220
|
+
{runInfo && (
|
|
221
|
+
<RunInfo
|
|
222
|
+
current={runInfo.displayValue}
|
|
223
|
+
baseline={baselineDisplayValue}
|
|
224
|
+
delta={runInfo.displayDeltaPercentage}
|
|
225
|
+
deltaType={runInfo.deltaType}
|
|
226
|
+
/>
|
|
227
|
+
)}
|
|
228
|
+
</Stack>
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
interface TileGroupTitleContentProps {
|
|
233
|
+
title?: string;
|
|
234
|
+
runInfo?: MetricRunInfo;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const TileGroupTitleContent = (props: TileGroupTitleContentProps) => {
|
|
238
|
+
const { title, runInfo } = props;
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<div className={css.tileGroupTitleContent}>
|
|
242
|
+
{title && <span className={css.tileGroupTitleText}>{title}</span>}
|
|
243
|
+
{runInfo && (
|
|
244
|
+
<span className={css.tileGroupTitleTotal}>
|
|
245
|
+
{runInfo.displayValue}
|
|
246
|
+
{'displayDelta' in runInfo && `(${runInfo.displayDeltaPercentage})`}
|
|
247
|
+
</span>
|
|
248
|
+
)}
|
|
249
|
+
</div>
|
|
250
|
+
);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
type TileGroupTitleContentWithTooltipProps = {
|
|
254
|
+
parentRef: RefObject<Element>;
|
|
255
|
+
tooltipContent: TileGroupTitleTooltipContentProps;
|
|
256
|
+
} & TileGroupTitleContentProps;
|
|
257
|
+
|
|
258
|
+
const TileGroupTitleContentWithTooltip = (props: TileGroupTitleContentWithTooltipProps) => {
|
|
259
|
+
const { parentRef, tooltipContent, ...restProps } = props;
|
|
260
|
+
|
|
261
|
+
const tooltipState = useTooltipStateWithMouseFollow({ parentRef });
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<>
|
|
265
|
+
<TooltipAnchor state={tooltipState} className={css.tileContentTooltipAnchor}>
|
|
266
|
+
<TileGroupTitleContent {...restProps} />
|
|
267
|
+
</TooltipAnchor>
|
|
268
|
+
<Tooltip state={tooltipState} className={css.tooltip}>
|
|
269
|
+
<TooltipArrow state={tooltipState} size={16} className={css.tileTooltipArrow} />
|
|
270
|
+
<TileGroupTitleTooltipContent {...tooltipContent} />
|
|
271
|
+
</Tooltip>
|
|
272
|
+
</>
|
|
273
|
+
);
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
type TileGroupTitleProps = Omit<TileGroupTitleContentWithTooltipProps, 'parentRef'>;
|
|
277
|
+
|
|
278
|
+
const TileGroupTitle = (props: TileGroupTitleProps) => {
|
|
279
|
+
const { tooltipContent, ...restProps } = props;
|
|
280
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
281
|
+
const hover = useHoverDirty(contentRef);
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<div className={css.tileGroupTitle} ref={contentRef}>
|
|
285
|
+
{hover ? (
|
|
286
|
+
<TileGroupTitleContentWithTooltip
|
|
287
|
+
parentRef={contentRef}
|
|
288
|
+
tooltipContent={tooltipContent}
|
|
289
|
+
{...restProps}
|
|
290
|
+
/>
|
|
291
|
+
) : (
|
|
292
|
+
<TileGroupTitleContent {...restProps} />
|
|
293
|
+
)}
|
|
294
|
+
</div>
|
|
295
|
+
);
|
|
296
|
+
};
|
|
297
|
+
|
|
252
298
|
interface TileGroupProps extends ComponentProps<'div'> {
|
|
253
299
|
/**
|
|
254
300
|
* Node title
|
|
@@ -323,11 +369,36 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
323
369
|
[width, height],
|
|
324
370
|
);
|
|
325
371
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
372
|
+
// Prepare data
|
|
373
|
+
const [runInfo, baselineDisplayValue, deltaType] = useMemo(() => {
|
|
374
|
+
if (!total) {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const resolvedRunInfo = getMetricRunInfo(
|
|
379
|
+
METRIC_TYPE_CONFIGS.METRIC_TYPE_FILE_SIZE,
|
|
380
|
+
total.current,
|
|
381
|
+
total.baseline,
|
|
382
|
+
) as MetricRunInfo;
|
|
383
|
+
const resolvedBaselineDisplayValue = formatFileSize(total.baseline);
|
|
384
|
+
const resolvedDeltaType = resolveGroupDeltaType(resolvedRunInfo);
|
|
385
|
+
|
|
386
|
+
return [resolvedRunInfo, resolvedBaselineDisplayValue, resolvedDeltaType];
|
|
387
|
+
}, [total]);
|
|
388
|
+
|
|
389
|
+
const rootClassName = cx(
|
|
390
|
+
css.tileGroup,
|
|
391
|
+
css[`tileGroup--${deltaType}`],
|
|
392
|
+
css[`tileGroup--${displaySize}`],
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
// Tooltip data
|
|
396
|
+
// - by default show the node id (full path) as title
|
|
397
|
+
const tooltipContent = {
|
|
398
|
+
title: id || title,
|
|
399
|
+
runInfo,
|
|
400
|
+
baselineDisplayValue,
|
|
401
|
+
} as TileGroupTitleTooltipContentProps;
|
|
331
402
|
|
|
332
403
|
if (title && displaySize === 'minimal') {
|
|
333
404
|
return (
|
|
@@ -337,7 +408,9 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
337
408
|
aria-label={I18N.TILE_GROUP_LABEL}
|
|
338
409
|
className={rootClassName}
|
|
339
410
|
style={{ left, top, width, height }}
|
|
340
|
-
|
|
411
|
+
>
|
|
412
|
+
<TileGroupTitle tooltipContent={tooltipContent} />
|
|
413
|
+
</div>
|
|
341
414
|
);
|
|
342
415
|
}
|
|
343
416
|
|
|
@@ -350,7 +423,7 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
350
423
|
style={{ left, top, width, height }}
|
|
351
424
|
className={rootClassName}
|
|
352
425
|
>
|
|
353
|
-
<
|
|
426
|
+
<TileGroupTitle title={title} tooltipContent={tooltipContent} />
|
|
354
427
|
</div>
|
|
355
428
|
);
|
|
356
429
|
}
|
|
@@ -363,17 +436,7 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
363
436
|
className={cx(rootClassName, css.tileGroupSizeDefault)}
|
|
364
437
|
style={{ left, top, width, height }}
|
|
365
438
|
>
|
|
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>
|
|
374
|
-
)}
|
|
375
|
-
</div>
|
|
376
|
-
)}
|
|
439
|
+
<TileGroupTitle title={title} runInfo={runInfo} tooltipContent={tooltipContent} />
|
|
377
440
|
{childNodes?.map((childNode) => {
|
|
378
441
|
if ('children' in childNode) {
|
|
379
442
|
const groupData = childNode.data as Tree;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type RefObject, useCallback } from 'react';
|
|
2
|
+
import { useDebounce, useMouseHovered } from 'react-use';
|
|
3
|
+
import { useTooltipState } from 'ariakit/tooltip';
|
|
1
4
|
import {
|
|
2
5
|
DeltaType,
|
|
3
6
|
type MetricRunInfoBaseline,
|
|
@@ -5,7 +8,15 @@ import {
|
|
|
5
8
|
type ReportMetricRow,
|
|
6
9
|
} from '@bundle-stats/utils';
|
|
7
10
|
|
|
8
|
-
import
|
|
11
|
+
import {
|
|
12
|
+
type TreeLeaf,
|
|
13
|
+
type TreeNodeChildren,
|
|
14
|
+
type Tree,
|
|
15
|
+
type TreeTotal,
|
|
16
|
+
type TileSizeDisplay,
|
|
17
|
+
type TileGroupSizeDisplay,
|
|
18
|
+
NESTED_PADDING_TOP,
|
|
19
|
+
} from './metrics-treemap.constants';
|
|
9
20
|
|
|
10
21
|
const ROOT_LABEL = '(root)';
|
|
11
22
|
|
|
@@ -160,3 +171,98 @@ export function resolveGroupDeltaType(
|
|
|
160
171
|
|
|
161
172
|
return DeltaType.NO_CHANGE;
|
|
162
173
|
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve the tile's size using predefined values to avoid
|
|
177
|
+
* computing the size of the content for every tile
|
|
178
|
+
*/
|
|
179
|
+
const PADDING_TOP = 8;
|
|
180
|
+
const PADDING_BOTTOM = 8;
|
|
181
|
+
const PADDING_LEFT = 8;
|
|
182
|
+
const PADDING_RIGHT = 8;
|
|
183
|
+
const LINE_HEIGHT = 16;
|
|
184
|
+
const LINE_HEIGHT_SMALL = 13.3;
|
|
185
|
+
|
|
186
|
+
export function resolveTileGroupSizeDisplay(width: number, height: number): TileGroupSizeDisplay {
|
|
187
|
+
if (height < NESTED_PADDING_TOP) {
|
|
188
|
+
return 'minimal';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (width < 24) {
|
|
192
|
+
return 'minimal';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (height < NESTED_PADDING_TOP + 8) {
|
|
196
|
+
return 'small';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return 'default';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function resolveTileSizeDisplay(width: number, height: number): TileSizeDisplay {
|
|
203
|
+
if (height < PADDING_TOP + PADDING_BOTTOM + LINE_HEIGHT_SMALL) {
|
|
204
|
+
return 'minimal';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (width < PADDING_LEFT + PADDING_RIGHT + 42) {
|
|
208
|
+
return 'minimal';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (height < PADDING_TOP + PADDING_TOP + LINE_HEIGHT * 2) {
|
|
212
|
+
return 'small';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (width < PADDING_LEFT + PADDING_RIGHT + 96) {
|
|
216
|
+
return 'small';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return 'default';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
interface UseTooltipStateWithMouseFollowOptions {
|
|
223
|
+
/**
|
|
224
|
+
* React ref to parent div
|
|
225
|
+
*/
|
|
226
|
+
parentRef: RefObject<Element>;
|
|
227
|
+
/**
|
|
228
|
+
* Tooltip gutter
|
|
229
|
+
*/
|
|
230
|
+
gutter?: number;
|
|
231
|
+
/**
|
|
232
|
+
* Tooltip timeout
|
|
233
|
+
*/
|
|
234
|
+
timeout?: number;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Ariakit tooltip state hook with mouse follow functionality
|
|
239
|
+
*/
|
|
240
|
+
export function useTooltipStateWithMouseFollow(options: UseTooltipStateWithMouseFollowOptions) {
|
|
241
|
+
const { parentRef, gutter = 16, timeout = 180 } = options;
|
|
242
|
+
|
|
243
|
+
const pointer = useMouseHovered(parentRef, { whenHovered: true });
|
|
244
|
+
|
|
245
|
+
// Return custom rect based on the mouse position
|
|
246
|
+
const getAnchorRect = useCallback(() => {
|
|
247
|
+
// Skip custom component rect area if the pointer position is empty
|
|
248
|
+
if (pointer.docX === 0 && pointer.docY === 0 && pointer.elW === 0 && pointer.elH === 0) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const newRect = {
|
|
253
|
+
x: pointer.docX - (document?.defaultView?.scrollX ?? 0),
|
|
254
|
+
y: pointer.docY - (document?.defaultView?.scrollY ?? 0),
|
|
255
|
+
w: 1,
|
|
256
|
+
h: 1,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return newRect;
|
|
260
|
+
}, [pointer.docX, pointer.docY]);
|
|
261
|
+
|
|
262
|
+
const tooltipState = useTooltipState({ gutter, getAnchorRect, timeout });
|
|
263
|
+
|
|
264
|
+
// Update tooltip position when pointer values change
|
|
265
|
+
useDebounce(tooltipState.render, 10, [pointer.docX, pointer.docY]);
|
|
266
|
+
|
|
267
|
+
return tooltipState;
|
|
268
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import chunk from 'lodash/chunk';
|
|
2
|
+
|
|
3
|
+
import COLORS from '../chart-colors.json';
|
|
4
|
+
|
|
5
|
+
export function getColors(count = 2) {
|
|
6
|
+
const chunks = chunk(COLORS, Math.round(COLORS.length / count));
|
|
7
|
+
|
|
8
|
+
return chunks.map((chunkColors, index) => {
|
|
9
|
+
if (index === 0) {
|
|
10
|
+
return chunkColors[0];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (index === chunks.length - 1) {
|
|
14
|
+
return chunkColors[chunkColors.length - 1];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return chunkColors[Math.floor(chunkColors.length / 2)];
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
|
|
4
|
+
export const getRenderChildWithClassName = (className: string) => (child: any) => {
|
|
5
|
+
if (child === null || child === '') {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { props } = child;
|
|
10
|
+
|
|
11
|
+
if (!props) {
|
|
12
|
+
return <div className={className}>{child}</div>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return React.cloneElement(child, {
|
|
16
|
+
...props,
|
|
17
|
+
className: cx(className, props.className), // eslint-disable-line react/prop-types
|
|
18
|
+
});
|
|
19
|
+
};
|