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