fix: refine treemap sizing and add earth bgp collectors

This commit is contained in:
linkong
2026-03-27 16:35:40 +08:00
parent 62f2d9f403
commit 7a3ca6e1b3
11 changed files with 741 additions and 49 deletions

View File

@@ -703,6 +703,22 @@ body {
overflow: hidden;
}
.data-list-treemap-tile--compact {
align-items: center;
justify-content: center;
gap: 8px;
text-align: center;
}
.data-list-treemap-tile--compact .data-list-treemap-head {
justify-content: center;
}
.data-list-treemap-tile--compact .data-list-treemap-body {
margin-top: 0;
align-items: center;
}
.data-list-treemap-tile--ocean {
background: linear-gradient(135deg, #dbeafe 0%, #93c5fd 100%);
}

View File

@@ -242,6 +242,56 @@ function estimateTreemapRows(
return Math.max(occupancy.length, 1)
}
function getTreemapSpan(value: number, maxValue: number, columns: number) {
if (columns <= 1) return 1
const normalized = Math.log10(value + 1) / Math.log10(maxValue + 1)
if (columns >= 4 && normalized >= 0.94) return 3
if (normalized >= 0.62) return 2
return 1
}
function isCompactTreemapItem(item: { colSpan: number; rowSpan: number }) {
return item.colSpan === 1 && item.rowSpan === 1
}
function getTreemapColumnCount(
width: number,
minCellSize: number,
gap: number,
isCompact: boolean
) {
const visualCap = isCompact ? 4 : 8
if (width <= 0) return Math.min(visualCap, isCompact ? 2 : 4)
const maxColumnsByWidth = Math.max(1, Math.floor((width + gap) / (minCellSize + gap)))
return Math.max(1, Math.min(maxColumnsByWidth, visualCap))
}
function getTreemapBaseSize(width: number, columns: number, gap: number, minCellSize: number) {
const fittedSize = Math.floor((Math.max(width, 0) - Math.max(0, columns - 1) * gap) / columns)
return Math.max(minCellSize, fittedSize || minCellSize)
}
function getTreemapTypography(rowHeight: number) {
const tilePadding = rowHeight <= 72 ? 8 : rowHeight <= 84 ? 10 : 12
const labelSize = rowHeight <= 72 ? 10 : rowHeight <= 84 ? 11 : 12
const valueSize = rowHeight <= 72 ? 13 : rowHeight <= 84 ? 15 : 16
return { tilePadding, labelSize, valueSize }
}
function getTreemapItemValueSize(
item: { colSpan: number; rowSpan: number },
baseValueSize: number
) {
if (isCompactTreemapItem(item)) {
return Math.max(11, baseValueSize - 2)
}
return baseValueSize
}
function DataList() {
const screens = useBreakpoint()
const isCompact = !screens.lg
@@ -579,57 +629,44 @@ function DataList() {
}))
}, [summary, treemapDimension])
const treemapGap = isCompact ? 8 : 10
const treemapMinCellSize = isCompact ? 72 : 52
const treemapColumns = useMemo(() => {
if (isCompact) return summaryBodyWidth >= 320 ? 2 : 1
if (leftPanelWidth < 360) return 2
if (leftPanelWidth < 520) return 3
return 4
}, [isCompact, leftPanelWidth, summaryBodyWidth])
return getTreemapColumnCount(summaryBodyWidth, treemapMinCellSize, treemapGap, isCompact)
}, [isCompact, summaryBodyWidth, treemapGap, treemapMinCellSize])
const treemapItems = useMemo(() => {
const palette = ['ocean', 'sky', 'mint', 'amber', 'rose', 'violet', 'slate']
const limitedItems = distributionItems.slice(0, isCompact ? 6 : 10)
const maxItems = isCompact ? 6 : 10
const limitedItems = distributionItems.slice(0, maxItems)
const maxValue = Math.max(...limitedItems.map((item) => item.value), 1)
const allowFeaturedTile = !isCompact && treemapColumns > 1 && limitedItems.length > 2
return limitedItems.map((item, index) => {
const ratio = item.value / maxValue
let colSpan = 1
let rowSpan = 1
if (allowFeaturedTile && index === 0 && ratio >= 0.35) {
colSpan = Math.min(2, treemapColumns)
rowSpan = colSpan
}
const span = Math.min(getTreemapSpan(item.value, maxValue, treemapColumns), treemapColumns)
return {
...item,
colSpan,
rowSpan,
colSpan: span,
rowSpan: span,
tone: palette[index % palette.length],
}
})
}, [distributionItems, isCompact, leftPanelWidth, treemapColumns])
}, [distributionItems, isCompact, treemapColumns])
const treemapRows = useMemo(
() => estimateTreemapRows(treemapItems, treemapColumns),
[treemapColumns, treemapItems]
)
const treemapGap = isCompact ? 8 : 10
const treemapBaseSize = Math.max(
isCompact ? 88 : 68,
Math.min(
isCompact ? 220 : 180,
Math.floor((Math.max(summaryBodyWidth, 0) - Math.max(0, treemapColumns - 1) * treemapGap) / treemapColumns)
) || (isCompact ? 88 : 68)
treemapMinCellSize,
getTreemapBaseSize(summaryBodyWidth, treemapColumns, treemapGap, treemapMinCellSize)
)
const treemapAvailableHeight = Math.max(summaryBodyHeight, 0)
const treemapRowHeight = treemapBaseSize
const treemapContentHeight = treemapRows * treemapRowHeight + Math.max(0, treemapRows - 1) * treemapGap
const treemapTilePadding = treemapRowHeight <= 72 ? 8 : treemapRowHeight <= 84 ? 10 : 12
const treemapLabelSize = treemapRowHeight <= 72 ? 10 : treemapRowHeight <= 84 ? 11 : 12
const treemapValueSize = treemapRowHeight <= 72 ? 13 : treemapRowHeight <= 84 ? 15 : 16
const { tilePadding: treemapTilePadding, labelSize: treemapLabelSize, valueSize: treemapValueSize } =
getTreemapTypography(treemapRowHeight)
const pageHeight = '100%'
const desktopTableHeight = rightColumnHeight - tableHeaderHeight - 132
@@ -801,18 +838,28 @@ function DataList() {
{treemapItems.length > 0 ? treemapItems.map((item) => (
<div
key={item.key}
className={`data-list-treemap-tile data-list-treemap-tile--${item.tone}`}
className={`data-list-treemap-tile data-list-treemap-tile--${item.tone}${isCompactTreemapItem(item) ? ' data-list-treemap-tile--compact' : ''}`}
style={{
gridColumn: `span ${item.colSpan}`,
gridRow: `span ${item.rowSpan}`,
}}
>
<div className="data-list-treemap-head">
<span className="data-list-summary-tile-icon">{item.icon}</span>
<Text className="data-list-treemap-label">{item.label}</Text>
<Tooltip title={item.label}>
<span className="data-list-summary-tile-icon">{item.icon}</span>
</Tooltip>
{!isCompactTreemapItem(item) ? (
<Tooltip title={item.label}>
<Text className="data-list-treemap-label">{item.label}</Text>
</Tooltip>
) : null}
</div>
<div className="data-list-treemap-body">
<Text strong className="data-list-summary-tile-value">
<Text
strong
className="data-list-summary-tile-value"
style={{ fontSize: getTreemapItemValueSize(item, treemapValueSize) }}
>
{item.value.toLocaleString()}
</Text>
</div>