Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions webview-ui/src/diagnostics_panel/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,71 @@ main {
vertical-align: middle;
}

.minecraft-grouped-statistic-child-label {
display: flex;
align-items: center;
min-width: 0;
}

.minecraft-grouped-statistic-child-key-expandable {
padding-left: 0;
}

.minecraft-grouped-statistic-row-expanded {
border-bottom: none;
}

.minecraft-grouped-statistic-detail-row {
background: color-mix(in srgb, var(--vscode-list-activeSelectionBackground) 10%, var(--vscode-editor-background));
}

.minecraft-grouped-statistic-detail-row td {
padding: 10px 14px !important;
}

.minecraft-entity-row-details {
display: flex;
flex-direction: column;
gap: 10px;
}

.minecraft-entity-row-details-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 8px;
}

.minecraft-entity-row-details-item {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}

.minecraft-entity-row-details-label {
font-size: 11px;
color: var(--vscode-descriptionForeground);
}

.minecraft-entity-row-details-value {
font-size: 12px;
color: var(--vscode-foreground);
font-weight: 600;
overflow-wrap: anywhere;
}

.minecraft-entity-row-details-chart {
display: flex;
flex-direction: column;
gap: 6px;
overflow-x: auto;
}

.minecraft-entity-row-details-chart-title {
font-size: 12px;
color: var(--vscode-descriptionForeground);
}

.minecraft-grouped-statistic-row-pinned {
background: color-mix(in srgb, var(--vscode-list-highlightForeground) 12%, var(--vscode-editor-background));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ type GroupedStatisticTableRowAction = {
width?: string;
};

export type GroupedStatisticTableRowDetailsContext = {
sparklineHistory: number[];
sparklineDisplayedMin: number;
sparklineDisplayedMax: number;
sparklineTickRange: number;
};

export type GroupedStatisticTableRowDetailsRenderer = (
row: GroupedStatisticTableRow,
groupKey: string,
context: GroupedStatisticTableRowDetailsContext,
) => JSX.Element;

type NonConsolidatedColumnResolver = (event: StatisticUpdatedMessage, valueLabels: string[]) => number | undefined;

export type MinecraftGroupedStatisticTableSelectionSnapshot = {
Expand Down Expand Up @@ -109,6 +122,7 @@ type MinecraftGroupedStatisticTableProps = {
sparklineColumnIndex?: number;
sparklineTickRange?: number;
sparklineValueFormatter?: (value: number) => string;
rowDetailsRenderer?: GroupedStatisticTableRowDetailsRenderer;
};

const sortOrderOptions = [
Expand Down Expand Up @@ -333,6 +347,10 @@ function getGroupExpansionKey(groupKey: string, isPinnedSection: boolean, isSpli
return `${groupKey}_${isPinnedSection ? 'pinned' : 'unpinned'}`;
}

function getRowDetailExpansionKey(groupKey: string, rowCategory: string): string {
return `detail:${getRowPinKey(groupKey, rowCategory)}`;
}

function buildGroupedStatisticTableGroup(
key: string,
rows: GroupedStatisticTableRow[],
Expand Down Expand Up @@ -442,6 +460,7 @@ const MinecraftGroupedStatisticTable = forwardRef<
sparklineColumnIndex = 0,
sparklineTickRange = 0,
sparklineValueFormatter,
rowDetailsRenderer,
}: MinecraftGroupedStatisticTableProps,
ref,
): JSX.Element {
Expand All @@ -466,6 +485,7 @@ const MinecraftGroupedStatisticTable = forwardRef<
const [selectedGroups, setSelectedGroups] = useState<Set<string>>(new Set());
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
const [deselectedRowsInSelectedGroups, setDeselectedRowsInSelectedGroups] = useState<Set<string>>(new Set());
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
const [hasInitializedDefaultSelection, setHasInitializedDefaultSelection] = useState<boolean>(false);
const nonConsolidatedTickCounterRef = useRef(0);
const nonConsolidatedLastEventTimeRef = useRef<number | undefined>(undefined);
Expand Down Expand Up @@ -706,6 +726,17 @@ const MinecraftGroupedStatisticTable = forwardRef<
[selectedGroups],
);

const onToggleExpandedRow = useCallback(
(groupKey: string, rowCategory: string): void => {
const expansionKey = getRowDetailExpansionKey(groupKey, rowCategory);

setExpandedRows(previousExpandedRows => {
return handleSetToggle(previousExpandedRows, expansionKey);
});
},
[handleSetToggle],
);

const selectionSnapshot = useMemo((): MinecraftGroupedStatisticTableSelectionSnapshot => {
const resolvedRowsByKey = new Map<string, GroupedStatisticTableRow>();

Expand Down Expand Up @@ -995,11 +1026,13 @@ const MinecraftGroupedStatisticTable = forwardRef<
useEffect(() => {
const validGroupKeys = new Set<string>();
const validRowKeys = new Set<string>();
const validExpandedRowKeys = new Set<string>();

data.forEach(row => {
const groupKey = groupKeyResolver(row.category);
validGroupKeys.add(groupKey);
validRowKeys.add(getRowPinKey(groupKey, row.category));
validExpandedRowKeys.add(getRowDetailExpansionKey(groupKey, row.category));
});

const handleUpdateSet = (set: Set<string>, validKeys: Set<string>): Set<string> => {
Expand Down Expand Up @@ -1038,6 +1071,10 @@ const MinecraftGroupedStatisticTable = forwardRef<
return handleUpdateSet(previousDeselectedRows, validRowKeys);
});

setExpandedRows(previousExpandedRows => {
return handleUpdateSet(previousExpandedRows, validExpandedRowKeys);
});

const validCategories = new Set(data.map(row => row.category));
validCategories.forEach(category => {
rowHistoryMissingTickCountRef.current.delete(category);
Expand Down Expand Up @@ -1319,17 +1356,27 @@ const MinecraftGroupedStatisticTable = forwardRef<
});
}, [defaultCollapsed, groupedData, isGroupedMode]);

const renderLeafRow = (row: GroupedStatisticTableRow, rowKey: string, groupKey: string): JSX.Element => {
const totalColumnCount =
2 +
valueLabels.length +
(selectionEnabled ? 1 : 0) +
(sparklineColumnIndex !== undefined ? 1 : 0) +
(rowAction ? 1 : 0);

const renderLeafRow = (row: GroupedStatisticTableRow, rowKey: string, groupKey: string): JSX.Element[] => {
const rowPinKey = getRowPinKey(groupKey, row.category);
const rowExpansionKey = getRowDetailExpansionKey(groupKey, row.category);
const isPinned = pinnedRows.has(rowPinKey);
const isSelected = isRowSelected(groupKey, rowPinKey);
const canExpandRow = rowDetailsRenderer !== undefined;
const isRowExpanded = canExpandRow && expandedRows.has(rowExpansionKey);
const sparklineHistory = rowHistoryRef.current.get(row.category) ?? [];
const sparklineBounds = getSmoothedSparklineBounds(getRowSparklineSeriesKey(row.category), sparklineHistory);

return (
const rows: JSX.Element[] = [
<tr
key={rowKey}
className={`minecraft-grouped-statistic-child-row${isPinned ? ' minecraft-grouped-statistic-row-pinned' : ''}${isSelected ? ' minecraft-grouped-statistic-row-selected' : ''}`}
className={`minecraft-grouped-statistic-child-row${isPinned ? ' minecraft-grouped-statistic-row-pinned' : ''}${isSelected ? ' minecraft-grouped-statistic-row-selected' : ''}${isRowExpanded ? ' minecraft-grouped-statistic-row-expanded' : ''}`}
>
<td className="minecraft-grouped-statistic-table-grid-pin">
<VSCodeCheckbox
Expand All @@ -1348,9 +1395,28 @@ const MinecraftGroupedStatisticTable = forwardRef<
</td>
)}
<td>
<span className="minecraft-grouped-statistic-child-key">
{keyFormatter ? keyFormatter(row.category) : row.category}
</span>
<div className="minecraft-grouped-statistic-child-label">
{canExpandRow && (
<button
type="button"
className="minecraft-grouped-statistic-toggle"
onClick={() => onToggleExpandedRow(groupKey, row.category)}
aria-expanded={isRowExpanded}
aria-label={
isRowExpanded
? `Collapse details for ${row.category}`
: `Expand details for ${row.category}`
}
>
{isRowExpanded ? '▾' : '▸'}
</button>
)}
<span
className={`minecraft-grouped-statistic-child-key${canExpandRow ? ' minecraft-grouped-statistic-child-key-expandable' : ''}`}
>
{keyFormatter ? keyFormatter(row.category) : row.category}
</span>
</div>
</td>
{row.values.map((value, valueIndex) => (
<td key={valueIndex} className="minecraft-grouped-statistic-table-grid-numeric">
Expand Down Expand Up @@ -1380,8 +1446,31 @@ const MinecraftGroupedStatisticTable = forwardRef<
</VSCodeButton>
</td>
)}
</tr>
</tr>,
];

if (!isRowExpanded || !rowDetailsRenderer) {
return rows;
}

rows.push(
<tr
key={`${rowKey}-details`}
className="minecraft-grouped-statistic-detail-row"
aria-label={`Details for ${row.category}`}
>
<td colSpan={totalColumnCount}>
{rowDetailsRenderer(row, groupKey, {
sparklineHistory,
sparklineDisplayedMin: sparklineBounds.min,
sparklineDisplayedMax: sparklineBounds.max,
sparklineTickRange,
})}
</td>
</tr>,
);

return rows;
};

return (
Expand Down Expand Up @@ -1599,7 +1688,7 @@ const MinecraftGroupedStatisticTable = forwardRef<

return [
...rows,
...group.rows.map((row, rowIndex) =>
...group.rows.flatMap((row, rowIndex) =>
renderLeafRow(
row,
`group-${group.expansionKey}-${groupIndex}-row-${row.category}-${rowIndex}`,
Expand All @@ -1608,7 +1697,7 @@ const MinecraftGroupedStatisticTable = forwardRef<
),
];
})
: sortedFlatData.map((row, rowIndex) =>
: sortedFlatData.flatMap((row, rowIndex) =>
renderLeafRow(
row,
`flat-row-${row.category}-${rowIndex}`,
Expand Down
55 changes: 52 additions & 3 deletions webview-ui/src/diagnostics_panel/controls/SparklineCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ type SparklineCellProps = {
formatValue?: (value: number) => string;
displayedMin?: number;
displayedMax?: number;
showYAxisTicks?: boolean;
yAxisTickCount?: number;
yAxisLabelFormatter?: (value: number) => string;
lineStrokeWidth?: number;
};

function formatSparklineLabelValue(value: number, formatValue?: (value: number) => string): string {
Expand Down Expand Up @@ -47,6 +51,10 @@ export function SparklineCell({
formatValue,
displayedMin,
displayedMax,
showYAxisTicks = false,
yAxisTickCount = 4,
yAxisLabelFormatter,
lineStrokeWidth = 1.5,
}: SparklineCellProps): JSX.Element {
if (values.length === 0) {
return <svg width={width} height={height} style={{ display: 'block' }} />;
Expand All @@ -60,11 +68,22 @@ export function SparklineCell({
const max = displayedMax ?? rawMax;
const min = displayedMin ?? rawMin;
const range = max - min || 1;
const yAxisWidth = showYAxisTicks ? 50 : 0;
const chartLeft = yAxisWidth;
const chartRight = width;
const topPadding = showYAxisTicks ? 6 : 2;
const bottomPadding = showYAxisTicks ? 6 : 2;
const chartTop = topPadding;
const chartBottom = height - bottomPadding;
const chartWidth = Math.max(1, chartRight - chartLeft);
const chartHeight = Math.max(1, chartBottom - chartTop);
const horizontalDivisor = Math.max(1, values.length - 1);
const normalizedTickCount = Math.max(2, yAxisTickCount);

const points = values
.map((v, i) => {
const x = (i / (values.length - 1)) * width;
const y = height - ((v - min) / range) * (height - 4) - 2;
const x = chartLeft + (i / horizontalDivisor) * chartWidth;
const y = chartBottom - ((v - min) / range) * chartHeight;
return `${x},${y}`;
})
.join(' ');
Expand All @@ -77,12 +96,42 @@ export function SparklineCell({
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<svg width={width} height={height} style={{ display: 'block' }}>
{showYAxisTicks &&
Array.from({ length: normalizedTickCount }).map((_, index) => {
const ratio = index / (normalizedTickCount - 1);
const tickValue = max - ratio * range;
const tickY = chartTop + ratio * chartHeight;
const tickLabel = formatSparklineLabelValue(tickValue, yAxisLabelFormatter || formatValue);

return (
<g key={`sparkline-y-tick-${index}`}>
<line
x1={chartLeft}
y1={tickY}
x2={chartRight}
y2={tickY}
stroke="var(--vscode-editorGroup-border)"
strokeWidth="1"
opacity="0.35"
/>
<text
x={chartLeft - 4}
y={tickY + 3}
textAnchor="end"
fontSize="9"
fill="var(--vscode-descriptionForeground)"
>
{tickLabel}
</text>
</g>
);
})}
{values.length >= 2 && (
<polyline
points={points}
fill="none"
stroke="var(--vscode-charts-blue)"
strokeWidth="1.5"
strokeWidth={lineStrokeWidth}
strokeLinejoin="round"
strokeLinecap="round"
/>
Expand Down
Loading
Loading