Skip to content

Commit 196118b

Browse files
s-r-xyifancong
andauthored
Fix missing breadcrumb in treemap chart (#1825)
Co-authored-by: yifancong <easy_cong@126.com>
1 parent f11024d commit 196118b

2 files changed

Lines changed: 132 additions & 139 deletions

File tree

packages/client/src/components/Charts/TreeMap.tsx

Lines changed: 130 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ export const TreeMap: React.FC<TreeMapProps> = memo(
133133
({
134134
treeData,
135135
sizeType,
136-
style,
137136
onChartClick,
138137
highlightNodeId,
139138
centerNodeId,
@@ -314,11 +313,11 @@ export const TreeMap: React.FC<TreeMapProps> = memo(
314313
obj.right = size.viewSize[0] - pos[0] + 10;
315314
}
316315
return obj;
317-
} as TooltipComponentOption['position'],
318-
formatter: function (
319-
info: CallbackDataParams & { data?: TreemapDataNode },
320-
) {
321-
const node = info.data || {};
316+
},
317+
formatter: function (info) {
318+
const node =
319+
(info as CallbackDataParams & { data?: TreemapDataNode }).data ||
320+
{};
322321
let path =
323322
typeof node.path === 'string'
324323
? node.path
@@ -386,13 +385,10 @@ export const TreeMap: React.FC<TreeMapProps> = memo(
386385
</div>
387386
`;
388387
},
389-
} as TooltipComponentOption,
388+
},
390389
series: [
391390
{
392391
type: 'treemap',
393-
itemStyle: {
394-
gapColor: '#ffffff',
395-
},
396392
label: {
397393
show: true,
398394
formatter: '{b}',
@@ -422,17 +418,17 @@ export const TreeMap: React.FC<TreeMapProps> = memo(
422418
borderColor: 'transparent',
423419
borderWidth: 0,
424420
borderRadius: 0,
421+
textStyle: {
422+
fontFamily: 'sans-serif',
423+
fontSize: 12,
424+
color: 'white',
425+
},
425426
},
426427
emphasis: {
427428
itemStyle: {
428429
color: '#333',
429430
},
430431
},
431-
textStyle: {
432-
fontFamily: 'sans-serif',
433-
fontSize: 12,
434-
color: '#666',
435-
},
436432
},
437433
roam: true,
438434
nodeClick: false,
@@ -444,11 +440,11 @@ export const TreeMap: React.FC<TreeMapProps> = memo(
444440
bottom: 30,
445441
left: 0,
446442
right: 0,
447-
zoomLimit: {
443+
scaleLimit: {
448444
min: 0.5,
449445
max: 5,
450446
},
451-
} as TreemapSeriesOption,
447+
},
452448
],
453449
});
454450
}, [treeData, sizeType, highlightNodeId, rootPath, themeToken]);
@@ -555,52 +551,58 @@ export const TreeMap: React.FC<TreeMapProps> = memo(
555551
}, []);
556552

557553
return option ? (
558-
<div className={Styles['chart-container']} style={style}>
554+
<div className={Styles['chart-container']}>
559555
<Alert
560556
message="If parsed size lacks detailed module information, you can enable sourceMap when RSDOCTOR = true. This is because Rsdoctor relies on SourceMap to obtain Parsed Size. Rspack provides SourceMap information to Rsdoctor by default without affecting the build output."
561557
type="info"
562558
showIcon
563559
style={{ marginBottom: 0 }}
564560
/>
565-
<EChartsReactCore
566-
ref={chartRef}
567-
option={option}
568-
echarts={echarts}
569-
onEvents={{
570-
click: (params: ECElementEvent) => {
571-
// Delay to differentiate from double-click; only zoom on single click
572-
if (clickTimeoutRef.current) {
573-
window.clearTimeout(clickTimeoutRef.current);
574-
}
575-
clickTimeoutRef.current = window.setTimeout(() => {
576-
if (chartRef.current) {
577-
const instance =
578-
chartRef.current.getEchartsInstance() as unknown as EChartsType;
579-
const data = params?.data as TreemapDataNode | undefined;
580-
if (instance && data?.id !== undefined) {
581-
instance.dispatchAction({
582-
type: 'treemapZoomToNode',
583-
seriesIndex: 0,
584-
targetNodeId: String(data.id),
585-
});
586-
}
587-
}
588-
}, 180);
589-
},
590-
dblclick: (params: ECElementEvent) => {
591-
// Double click: cancel pending single-click action and trigger analyze
592-
if (clickTimeoutRef.current) {
593-
window.clearTimeout(clickTimeoutRef.current);
594-
clickTimeoutRef.current = null;
595-
}
596-
onChartClick?.(params);
597-
},
598-
}}
561+
<div
599562
style={{
600-
width: '100%',
601-
height: '100%',
563+
flex: 1,
602564
}}
603-
/>
565+
>
566+
<EChartsReactCore
567+
ref={chartRef}
568+
option={option}
569+
echarts={echarts}
570+
onEvents={{
571+
click: (params: ECElementEvent) => {
572+
// Delay to differentiate from double-click; only zoom on single click
573+
if (clickTimeoutRef.current) {
574+
window.clearTimeout(clickTimeoutRef.current);
575+
}
576+
clickTimeoutRef.current = window.setTimeout(() => {
577+
if (chartRef.current) {
578+
const instance =
579+
chartRef.current.getEchartsInstance() as unknown as EChartsType;
580+
const data = params?.data as TreemapDataNode | undefined;
581+
if (instance && data?.id !== undefined) {
582+
instance.dispatchAction({
583+
type: 'treemapZoomToNode',
584+
seriesIndex: 0,
585+
targetNodeId: String(data.id),
586+
});
587+
}
588+
}
589+
}, 180);
590+
},
591+
dblclick: (params: ECElementEvent) => {
592+
// Double click: cancel pending single-click action and trigger analyze
593+
if (clickTimeoutRef.current) {
594+
window.clearTimeout(clickTimeoutRef.current);
595+
clickTimeoutRef.current = null;
596+
}
597+
onChartClick?.(params);
598+
},
599+
}}
600+
style={{
601+
width: '100%',
602+
height: '100%',
603+
}}
604+
/>
605+
</div>
604606
</div>
605607
) : null;
606608
},
@@ -693,60 +695,59 @@ const AssetTreemapWithFilterInner: React.FC<{
693695
[onChartClick],
694696
);
695697

696-
const enterFullscreen = useCallback(() => {
697-
if (containerRef.current) {
698-
const el = containerRef.current as HTMLElement & {
699-
webkitRequestFullscreen?: () => void;
700-
mozRequestFullScreen?: () => void;
701-
msRequestFullscreen?: () => void;
702-
};
703-
if (el.requestFullscreen) {
704-
el.requestFullscreen()
705-
.then(() => setIsFullscreen(true))
706-
.catch((err: unknown) =>
707-
console.error('Failed to enter fullscreen:', err),
708-
);
709-
} else if (el.webkitRequestFullscreen) {
710-
try {
711-
el.webkitRequestFullscreen();
712-
setIsFullscreen(true);
713-
} catch (err) {
714-
console.error('Failed to enter fullscreen (webkit):', err);
715-
}
716-
} else if (el.mozRequestFullScreen) {
717-
try {
718-
el.mozRequestFullScreen();
719-
setIsFullscreen(true);
720-
} catch (err) {
721-
console.error('Failed to enter fullscreen (moz):', err);
722-
}
723-
} else if (el.msRequestFullscreen) {
724-
try {
725-
el.msRequestFullscreen();
726-
setIsFullscreen(true);
727-
} catch (err) {
728-
console.error('Failed to enter fullscreen (ms):', err);
729-
}
730-
} else {
731-
console.error('Fullscreen API is not supported in this browser.');
698+
const enterFullscreen = () => {
699+
if (!containerRef.current) return;
700+
const el = containerRef.current as HTMLElement & {
701+
webkitRequestFullscreen?: () => void;
702+
mozRequestFullScreen?: () => void;
703+
msRequestFullscreen?: () => void;
704+
};
705+
if (el.requestFullscreen) {
706+
el.requestFullscreen()
707+
.then(() => setIsFullscreen(true))
708+
.catch((err: unknown) =>
709+
console.error('Failed to enter fullscreen:', err),
710+
);
711+
} else if (el.webkitRequestFullscreen) {
712+
try {
713+
el.webkitRequestFullscreen();
714+
setIsFullscreen(true);
715+
} catch (err) {
716+
console.error('Failed to enter fullscreen (webkit):', err);
717+
}
718+
} else if (el.mozRequestFullScreen) {
719+
try {
720+
el.mozRequestFullScreen();
721+
setIsFullscreen(true);
722+
} catch (err) {
723+
console.error('Failed to enter fullscreen (moz):', err);
732724
}
725+
} else if (el.msRequestFullscreen) {
726+
try {
727+
el.msRequestFullscreen();
728+
setIsFullscreen(true);
729+
} catch (err) {
730+
console.error('Failed to enter fullscreen (ms):', err);
731+
}
732+
} else {
733+
console.error('Fullscreen API is not supported in this browser.');
733734
}
734-
}, []);
735+
};
735736

736-
const exitFullscreen = useCallback(() => {
737+
const exitFullscreen = () => {
737738
document
738739
.exitFullscreen()
739740
.then(() => setIsFullscreen(false))
740741
.catch((err) => console.error('Failed to exit fullscreen:', err));
741-
}, []);
742+
};
742743

743-
const toggleFullscreen = useCallback(() => {
744+
const toggleFullscreen = () => {
744745
if (isFullscreen) {
745746
exitFullscreen();
746747
} else {
747748
enterFullscreen();
748749
}
749-
}, [isFullscreen, enterFullscreen, exitFullscreen]);
750+
};
750751

751752
useEffect(() => {
752753
const handleFullscreenChange = () => {
@@ -804,64 +805,55 @@ const AssetTreemapWithFilterInner: React.FC<{
804805
return results;
805806
}, [filteredTreeData, searchQuery]);
806807

807-
const handleSearchResultClick = useCallback((nodeId: number) => {
808+
const handleSearchResultClick = (nodeId: number) => {
808809
setHighlightNodeId(nodeId);
809810
setCenterNodeId(nodeId);
810-
}, []);
811+
};
811812

812-
const removeRootPath = useCallback(
813-
(filepath: string): string => {
814-
if (!rootPath || !filepath) return filepath;
815-
const normalizedRoot = rootPath.replace(/\\/g, '/').replace(/\/$/, '');
816-
const normalizedPath = filepath.replace(/\\/g, '/');
813+
const removeRootPath = (filepath: string): string => {
814+
if (!rootPath || !filepath) return filepath;
815+
const normalizedRoot = rootPath.replace(/\\/g, '/').replace(/\/$/, '');
816+
const normalizedPath = filepath.replace(/\\/g, '/');
817817

818-
if (normalizedPath.startsWith(normalizedRoot + '/')) {
819-
return normalizedPath.slice(normalizedRoot.length + 1);
820-
} else if (normalizedPath === normalizedRoot) {
821-
return '';
822-
}
823-
return filepath;
824-
},
825-
[rootPath],
826-
);
818+
if (normalizedPath.startsWith(normalizedRoot + '/')) {
819+
return normalizedPath.slice(normalizedRoot.length + 1);
820+
} else if (normalizedPath === normalizedRoot) {
821+
return '';
822+
}
823+
return filepath;
824+
};
827825

828-
const getSize = useCallback((node: TreeNode, type?: SizeType) => {
826+
const getSize = (node: TreeNode, type?: SizeType) => {
829827
if (type === 'stat') return node.sourceSize || 0;
830828
if (type === 'parsed') return node.bundledSize || 0;
831829
if (type === 'gzip') return node.gzipSize || 0;
832830
if (type === 'value') return node.value || 0;
833831
if (node.value) return node.value;
834832
return 0;
835-
}, []);
833+
};
836834

837-
const calculateNodeTotalSize = useCallback(
838-
(node: TreeNode, type: SizeType): number => {
839-
let size = getSize(node, type);
835+
const calculateNodeTotalSize = (node: TreeNode, type: SizeType): number => {
836+
let size = getSize(node, type);
840837

841-
if (node.children && node.children.length > 0) {
842-
const childrenSize = node.children.reduce(
843-
(sum, child) => sum + calculateNodeTotalSize(child, type),
844-
0,
845-
);
846-
if (size === 0 || (!node.path && childrenSize > 0)) {
847-
size = childrenSize;
848-
}
838+
if (node.children && node.children.length > 0) {
839+
const childrenSize = node.children.reduce(
840+
(sum, child) => sum + calculateNodeTotalSize(child, type),
841+
0,
842+
);
843+
if (size === 0 || (!node.path && childrenSize > 0)) {
844+
size = childrenSize;
849845
}
846+
}
850847

851-
return size;
852-
},
853-
[getSize],
854-
);
848+
return size;
849+
};
855850

856-
const getChunkSize = useCallback(
857-
(name: string, type?: SizeType) => {
858-
const node = treeData.find((n) => n.name === name);
859-
if (!node) return 0;
860-
const sizeTypeToUse = type || sizeType;
861-
return calculateNodeTotalSize(node, sizeTypeToUse);
862-
},
863-
[treeData, sizeType, calculateNodeTotalSize],
864-
);
851+
const getChunkSize = (name: string, type?: SizeType) => {
852+
const node = treeData.find((n) => n.name === name);
853+
if (!node) return 0;
854+
const sizeTypeToUse = type || sizeType;
855+
return calculateNodeTotalSize(node, sizeTypeToUse);
856+
};
865857

866858
return (
867859
<div className={Styles.treemap} ref={containerRef}>
@@ -1022,7 +1014,6 @@ const AssetTreemapWithFilterInner: React.FC<{
10221014
highlightNodeId={highlightNodeId}
10231015
centerNodeId={centerNodeId}
10241016
rootPath={rootPath}
1025-
style={{ width: '100%', height: '100%' }}
10261017
/>
10271018
{moduleId ? (
10281019
<ServerAPIProvider

packages/client/src/components/Charts/treemap.module.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@
8686
}
8787

8888
.chart-container {
89+
display: flex;
90+
flex-direction: column;
8991
position: relative;
9092
width: 100%;
9193
height: 100%;

0 commit comments

Comments
 (0)