@bundle-stats/ui 4.13.0-rc.0 → 4.13.0-rc.1
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 +23 -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 +10 -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 +11 -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 +47 -10
- 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 +23 -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 +10 -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 +11 -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 +47 -10
- 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 +72 -48
- package/src/components/bundle-assets/index.jsx +4 -0
- package/src/components/bundle-modules/bundle-modules.tsx +32 -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 +19 -7
- package/src/components/metrics-treemap/metrics-treemap.constants.ts +11 -0
- package/src/components/metrics-treemap/metrics-treemap.module.css +47 -10
- package/src/components/metrics-treemap/metrics-treemap.tsx +171 -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
|
@@ -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,96 @@ 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
|
+
current={runInfo.displayValue}
|
|
222
|
+
baseline={baselineDisplayValue}
|
|
223
|
+
delta={runInfo.displayDeltaPercentage}
|
|
224
|
+
deltaType={runInfo.deltaType}
|
|
225
|
+
/>
|
|
226
|
+
</Stack>
|
|
227
|
+
);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
interface TileGroupTitleContentProps {
|
|
231
|
+
title?: string;
|
|
232
|
+
runInfo?: MetricRunInfo;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const TileGroupTitleContent = (props: TileGroupTitleContentProps) => {
|
|
236
|
+
const { title, runInfo } = props;
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<div className={css.tileGroupTitleContent}>
|
|
240
|
+
{title && <span className={css.tileGroupTitleText}>{title}</span>}
|
|
241
|
+
{runInfo && (
|
|
242
|
+
<span className={css.tileGroupTitleTotal}>
|
|
243
|
+
{runInfo.displayValue}
|
|
244
|
+
{'displayDelta' in runInfo && `(${runInfo.displayDeltaPercentage})`}
|
|
245
|
+
</span>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
type TileGroupTitleContentWithTooltipProps = {
|
|
252
|
+
parentRef: RefObject<Element>;
|
|
253
|
+
tooltipContent: TileGroupTitleTooltipContentProps;
|
|
254
|
+
} & TileGroupTitleContentProps;
|
|
255
|
+
|
|
256
|
+
const TileGroupTitleContentWithTooltip = (props: TileGroupTitleContentWithTooltipProps) => {
|
|
257
|
+
const { parentRef, tooltipContent, ...restProps } = props;
|
|
258
|
+
|
|
259
|
+
const tooltipState = useTooltipStateWithMouseFollow({ parentRef });
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<>
|
|
263
|
+
<TooltipAnchor state={tooltipState} className={css.tileContentTooltipAnchor}>
|
|
264
|
+
<TileGroupTitleContent {...restProps} />
|
|
265
|
+
</TooltipAnchor>
|
|
266
|
+
<Tooltip state={tooltipState} className={css.tooltip}>
|
|
267
|
+
<TooltipArrow state={tooltipState} size={16} className={css.tileTooltipArrow} />
|
|
268
|
+
<TileGroupTitleTooltipContent {...tooltipContent} />
|
|
269
|
+
</Tooltip>
|
|
270
|
+
</>
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
type TileGroupTitleProps = Omit<TileGroupTitleContentWithTooltipProps, 'parentRef'>;
|
|
275
|
+
|
|
276
|
+
const TileGroupTitle = (props: TileGroupTitleProps) => {
|
|
277
|
+
const { tooltipContent, ...restProps } = props;
|
|
278
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
279
|
+
const hover = useHoverDirty(contentRef);
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<div className={css.tileGroupTitle} ref={contentRef}>
|
|
283
|
+
{hover ? (
|
|
284
|
+
<TileGroupTitleContentWithTooltip
|
|
285
|
+
parentRef={contentRef}
|
|
286
|
+
tooltipContent={tooltipContent}
|
|
287
|
+
{...restProps}
|
|
288
|
+
/>
|
|
289
|
+
) : (
|
|
290
|
+
<TileGroupTitleContent {...restProps} />
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
);
|
|
294
|
+
};
|
|
295
|
+
|
|
252
296
|
interface TileGroupProps extends ComponentProps<'div'> {
|
|
253
297
|
/**
|
|
254
298
|
* Node title
|
|
@@ -323,11 +367,36 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
323
367
|
[width, height],
|
|
324
368
|
);
|
|
325
369
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
370
|
+
// Prepare data
|
|
371
|
+
const [runInfo, baselineDisplayValue, deltaType] = useMemo(() => {
|
|
372
|
+
if (!total) {
|
|
373
|
+
return [];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const resolvedRunInfo = getMetricRunInfo(
|
|
377
|
+
METRIC_TYPE_CONFIGS.METRIC_TYPE_FILE_SIZE,
|
|
378
|
+
total.current,
|
|
379
|
+
total.baseline,
|
|
380
|
+
) as MetricRunInfo;
|
|
381
|
+
const resolvedBaselineDisplayValue = formatFileSize(total.baseline);
|
|
382
|
+
const resolvedDeltaType = resolveGroupDeltaType(resolvedRunInfo);
|
|
383
|
+
|
|
384
|
+
return [resolvedRunInfo, resolvedBaselineDisplayValue, resolvedDeltaType];
|
|
385
|
+
}, [total]);
|
|
386
|
+
|
|
387
|
+
const rootClassName = cx(
|
|
388
|
+
css.tileGroup,
|
|
389
|
+
css[`tileGroup--${deltaType}`],
|
|
390
|
+
css[`tileGroup--${displaySize}`],
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
// Tooltip data
|
|
394
|
+
// - by default show the node id (full path) as title
|
|
395
|
+
const tooltipContent = {
|
|
396
|
+
title: id || title,
|
|
397
|
+
runInfo,
|
|
398
|
+
baselineDisplayValue,
|
|
399
|
+
} as TileGroupTitleTooltipContentProps;
|
|
331
400
|
|
|
332
401
|
if (title && displaySize === 'minimal') {
|
|
333
402
|
return (
|
|
@@ -337,7 +406,9 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
337
406
|
aria-label={I18N.TILE_GROUP_LABEL}
|
|
338
407
|
className={rootClassName}
|
|
339
408
|
style={{ left, top, width, height }}
|
|
340
|
-
|
|
409
|
+
>
|
|
410
|
+
<TileGroupTitle tooltipContent={tooltipContent} />
|
|
411
|
+
</div>
|
|
341
412
|
);
|
|
342
413
|
}
|
|
343
414
|
|
|
@@ -350,7 +421,7 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
350
421
|
style={{ left, top, width, height }}
|
|
351
422
|
className={rootClassName}
|
|
352
423
|
>
|
|
353
|
-
<
|
|
424
|
+
<TileGroupTitle title={title} tooltipContent={tooltipContent} />
|
|
354
425
|
</div>
|
|
355
426
|
);
|
|
356
427
|
}
|
|
@@ -363,17 +434,7 @@ const TileGroup = (props: TileGroupProps) => {
|
|
|
363
434
|
className={cx(rootClassName, css.tileGroupSizeDefault)}
|
|
364
435
|
style={{ left, top, width, height }}
|
|
365
436
|
>
|
|
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
|
-
)}
|
|
437
|
+
<TileGroupTitle title={title} runInfo={runInfo} tooltipContent={tooltipContent} />
|
|
377
438
|
{childNodes?.map((childNode) => {
|
|
378
439
|
if ('children' in childNode) {
|
|
379
440
|
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
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import uniqBy from 'lodash/uniqBy';
|
|
2
|
+
|
|
3
|
+
import type { Job, WebpackChunk } from '@bundle-stats/utils';
|
|
4
|
+
|
|
5
|
+
interface GetJobsChunksData {
|
|
6
|
+
chunks: Array<WebpackChunk>;
|
|
7
|
+
chunkIds: Array<string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get chunks data from all jobs
|
|
12
|
+
*/
|
|
13
|
+
export function getJobsChunksData(jobs: Array<Job>): GetJobsChunksData {
|
|
14
|
+
const jobChunks = jobs.map((job) => job?.meta?.webpack?.chunks || []);
|
|
15
|
+
const chunks = uniqBy(jobChunks.flat(), ({ id }) => id);
|
|
16
|
+
const chunkIds = chunks?.map(({ id }) => id);
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
chunks,
|
|
20
|
+
chunkIds,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -13,12 +13,8 @@ export namespace BundleAssets {
|
|
|
13
13
|
export let jobs: PropTypes.Validator<(PropTypes.InferProps<{
|
|
14
14
|
internalBuildNumber: PropTypes.Requireable<number>;
|
|
15
15
|
label: PropTypes.Requireable<string>;
|
|
16
|
-
meta: PropTypes.Requireable<PropTypes.InferProps<{
|
|
17
|
-
webpack: PropTypes.Requireable<PropTypes.InferProps<{
|
|
18
|
-
chunks: PropTypes.Requireable<any[]>;
|
|
19
|
-
}>>;
|
|
20
|
-
}>>;
|
|
21
16
|
}> | null | undefined)[]>;
|
|
17
|
+
export let chunks: PropTypes.Validator<any[]>;
|
|
22
18
|
export let items: PropTypes.Validator<(PropTypes.InferProps<{
|
|
23
19
|
key: PropTypes.Requireable<string>;
|
|
24
20
|
label: PropTypes.Requireable<string>;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { type Job } from '@bundle-stats/utils';
|
|
3
|
+
import { WebpackChunk } from '@bundle-stats/utils';
|
|
2
4
|
import { SortAction } from '../../types';
|
|
3
5
|
import { ModuleMetric } from './bundle-modules.constants';
|
|
4
|
-
import type {
|
|
6
|
+
import type { ReportMetricModuleRow } from './bundle-modules.types';
|
|
5
7
|
interface BundleModulesProps extends React.ComponentProps<'div'> {
|
|
6
8
|
jobs?: Array<Job>;
|
|
7
9
|
items?: Array<ReportMetricModuleRow>;
|
|
8
10
|
allItems?: Array<ReportMetricModuleRow>;
|
|
9
|
-
chunks?: Array<
|
|
11
|
+
chunks?: Array<WebpackChunk>;
|
|
10
12
|
totalRowCount: number;
|
|
11
13
|
updateFilters: () => void;
|
|
12
14
|
resetFilters: () => void;
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type WebpackChunk, type ReportMetricRow } from '@bundle-stats/utils';
|
|
2
2
|
import { type Module } from '@bundle-stats/utils/lib-esm/webpack';
|
|
3
3
|
import type { FilterFieldsData } from '../../types';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ReportMetricModuleRow } from './bundle-modules.types';
|
|
5
5
|
export declare const addRowFlags: (row: Module & ReportMetricRow) => ReportMetricModuleRow;
|
|
6
6
|
export declare const getCustomSort: (item: ReportMetricRow) => (string | boolean)[];
|
|
7
7
|
export declare const generateGetRowFilter: ({ chunkIds }: {
|
|
8
8
|
chunkIds: Array<string>;
|
|
9
9
|
}) => (filters: Record<string, unknown>) => (row: ReportMetricModuleRow) => boolean;
|
|
10
|
-
export declare const extractChunkData: (jobs: Array<Job>) => {
|
|
11
|
-
chunks: Chunk[];
|
|
12
|
-
chunkIds: string[];
|
|
13
|
-
};
|
|
14
10
|
export declare const generateFilters: (chunkIds: Array<string>, multipleJobs: boolean) => {
|
|
15
11
|
defaultFilters: {
|
|
16
12
|
[x: string]: boolean;
|
|
@@ -21,7 +17,7 @@ export declare const generateFilters: (chunkIds: Array<string>, multipleJobs: bo
|
|
|
21
17
|
};
|
|
22
18
|
interface GetFiltersFormDataParams {
|
|
23
19
|
filters: Record<string, boolean>;
|
|
24
|
-
chunks: Array<
|
|
20
|
+
chunks: Array<WebpackChunk>;
|
|
25
21
|
compareMode: boolean;
|
|
26
22
|
}
|
|
27
23
|
export declare const generateFilterFieldsData: (params: GetFiltersFormDataParams) => FilterFieldsData;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { Job } from '@bundle-stats/utils';
|
|
2
3
|
import type { SortAction } from '../../types';
|
|
3
4
|
import { BundleModules as BaseComponent } from './bundle-modules';
|
|
4
|
-
import * as types from './bundle-modules.types';
|
|
5
5
|
interface BundleModulesProps extends Omit<React.ComponentProps<typeof BaseComponent>, 'jobs' | 'items' | 'chunks' | 'updateFilters' | 'resetFilters' | 'resetAllFilters' | 'filters' | 'sort' | 'updateSort' | 'search' | 'updateSearch' | 'allItems' | 'totalRowCount' | 'hideEntryInfo' | 'showEntryInfo'> {
|
|
6
|
-
jobs: Array<
|
|
6
|
+
jobs: Array<Job>;
|
|
7
7
|
filters: Record<string, boolean>;
|
|
8
8
|
search?: string;
|
|
9
9
|
metric?: string;
|
|
@@ -18,3 +18,11 @@ export interface Tree {
|
|
|
18
18
|
}
|
|
19
19
|
export type TreeNode = Tree | TreeLeaf;
|
|
20
20
|
export type TreeNodeChildren = Array<TreeNode>;
|
|
21
|
+
export type TileGroupSizeDisplay = 'minimal' | 'small' | 'default';
|
|
22
|
+
export type TileSizeDisplay = 'minimal' | 'small' | 'default';
|
|
23
|
+
export declare const SQUARIFY_RATIO = 1.66;
|
|
24
|
+
export declare const PADDING_OUTER = 1;
|
|
25
|
+
export declare const PADDING_INNER = 2;
|
|
26
|
+
export declare const NESTED_PADDING = 4;
|
|
27
|
+
export declare const NESTED_PADDING_TOP: number;
|
|
28
|
+
export declare const NESTED_PADDING_LEFT = 8;
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
1
2
|
import { DeltaType, type MetricRunInfoBaseline, type MetricRunInfo, type ReportMetricRow } from '@bundle-stats/utils';
|
|
2
|
-
import type
|
|
3
|
+
import { type Tree, type TileSizeDisplay, type TileGroupSizeDisplay } from './metrics-treemap.constants';
|
|
3
4
|
export declare function getTreemapNodes(items: Array<ReportMetricRow>): Tree;
|
|
4
5
|
export declare function getTreemapNodesGroupedByPath(items: Array<ReportMetricRow>): Tree;
|
|
5
6
|
export declare function resolveGroupDeltaType(metricRunInfo?: MetricRunInfo | MetricRunInfoBaseline): DeltaType.NO_CHANGE | DeltaType.NEGATIVE | DeltaType.POSITIVE;
|
|
7
|
+
export declare function resolveTileGroupSizeDisplay(width: number, height: number): TileGroupSizeDisplay;
|
|
8
|
+
export declare function resolveTileSizeDisplay(width: number, height: number): TileSizeDisplay;
|
|
9
|
+
interface UseTooltipStateWithMouseFollowOptions {
|
|
10
|
+
parentRef: RefObject<Element>;
|
|
11
|
+
gutter?: number;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function useTooltipStateWithMouseFollow(options: UseTooltipStateWithMouseFollowOptions): import("ariakit/tooltip").TooltipState;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getColors(count?: number): string[];
|