diff --git a/webview-ui/src/diagnostics_panel/App.css b/webview-ui/src/diagnostics_panel/App.css index 4818ea5..630322f 100644 --- a/webview-ui/src/diagnostics_panel/App.css +++ b/webview-ui/src/diagnostics_panel/App.css @@ -78,6 +78,13 @@ main { gap: 6px; } +.axis-selection-label { + display: inline-flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; +} + .stat-group-selection-help { margin: 8px 0px 2px 8px; display: inline-flex; @@ -174,6 +181,14 @@ main { fill: var(--vscode-editor-background); } +.minecraft-statistic-stacked-line-chart-section-label { + text-anchor: end; + font-size: 11px; + stroke: var(--vscode-editor-background); + stroke-width: 3; + paint-order: stroke; +} + .minecraft-statistic-stacked-line-chart-swatches { font-family: system-ui, sans-serif; font-size: 10px; diff --git a/webview-ui/src/diagnostics_panel/StatisticResolver.tsx b/webview-ui/src/diagnostics_panel/StatisticResolver.tsx index 4b48161..9e7a052 100644 --- a/webview-ui/src/diagnostics_panel/StatisticResolver.tsx +++ b/webview-ui/src/diagnostics_panel/StatisticResolver.tsx @@ -15,9 +15,7 @@ export enum YAxisStyle { SquareRoot = 'sqrt', Pow = 'pow', Logarithmic = 'log', - SymLog = 'symlog', - Time = 'time', - UTC = 'utc', + SymLog = 'symlog' } export type TrackedStat = { diff --git a/webview-ui/src/diagnostics_panel/controls/LineChart/LineChartYAxisSelectionBox.tsx b/webview-ui/src/diagnostics_panel/controls/LineChart/LineChartYAxisSelectionBox.tsx index c73bde2..dbfa3ed 100644 --- a/webview-ui/src/diagnostics_panel/controls/LineChart/LineChartYAxisSelectionBox.tsx +++ b/webview-ui/src/diagnostics_panel/controls/LineChart/LineChartYAxisSelectionBox.tsx @@ -17,15 +17,15 @@ export default function LineChartYAxisSelectionBox({ onChange, defaultValue }: L const options = useMemo(() => Object.values(YAxisType), []); const _onChange = useCallback((e: Event | React.FormEvent): void => { - const selectedOption = (e.target as HTMLSelectElement).value as YAxisType; + const selectedOption = (e.target as HTMLSelectElement).value as YAxisType; onChange(selectedOption); setSelectedResolver(selectedOption); }, []); return ( -
- +
+ ); } diff --git a/webview-ui/src/diagnostics_panel/controls/MinecraftStatisticStackedLineChart.tsx b/webview-ui/src/diagnostics_panel/controls/MinecraftStatisticStackedLineChart.tsx index 0621dba..d39b80f 100644 --- a/webview-ui/src/diagnostics_panel/controls/MinecraftStatisticStackedLineChart.tsx +++ b/webview-ui/src/diagnostics_panel/controls/MinecraftStatisticStackedLineChart.tsx @@ -5,6 +5,7 @@ import * as Plot from '@observablehq/plot'; import { StatisticProvider, StatisticUpdatedMessage } from '../StatisticProvider'; import { StatisticResolver, TrackedStat, YAxisStyle } from '../StatisticResolver'; import { removeAllStyleElements } from '../../util/CSPUtilities'; +import YAxisStyleDropdown from './YAxisStyleDropdown'; type MinecraftStatisticStackedLineChartProps = { title: string; @@ -17,6 +18,7 @@ type MinecraftStatisticStackedLineChartProps = { statisticDataProvider: StatisticProvider; statisticResolver: StatisticResolver; yAxisStyle?: YAxisStyle; + showSectionLabels?: boolean; }; export default function MinecraftStatisticStackedLineChart({ @@ -28,9 +30,11 @@ export default function MinecraftStatisticStackedLineChart({ statisticResolver, catageoryLabels, yAxisStyle, + showSectionLabels = false, }: MinecraftStatisticStackedLineChartProps) { // states const [data, setData] = useState([]); + const [selectedYAxisStyle, setSelectedYAxisStyle] = useState(yAxisStyle ?? YAxisStyle.Linear); // refs const containerRef = useRef(null); @@ -68,6 +72,30 @@ export default function MinecraftStatisticStackedLineChart({ statisticDataProvider.addSubscriber(eventHandler); const latestTime = data.length !== 0 ? data[data.length - 1].time : 0; + const latestData = data.filter(d => d.time === latestTime); + + let sectionStart = 0; + const sectionLabels = latestData.flatMap(stat => { + const sectionHeight = Math.max(0, stat.value); + const y = sectionStart + sectionHeight / 2; + sectionStart += sectionHeight; + + if (stat.category === null || stat.category === undefined) { + console.warn( + `Skipping section labels for ${stat} because it is missing a value category.` + ); + return []; + } + + return [ + { + category: stat.category, + label: catageoryLabels?.[stat.category] ?? stat.category, + time: stat.time, + y, + }, + ]; + }); const plot = Plot.plot({ className: 'minecraft-statistic-stacked-line-chart', @@ -98,7 +126,7 @@ export default function MinecraftStatisticStackedLineChart({ return Math.floor(tickDifference / 20) + 's'; }, }, - y: { grid: true, label: yLabel, type: yAxisStyle }, + y: { grid: true, label: yLabel, type: selectedYAxisStyle }, marks: [ Plot.areaY(data, { x: 'time', @@ -109,6 +137,16 @@ export default function MinecraftStatisticStackedLineChart({ fontSize: 12, }, }), + showSectionLabels + ? Plot.text(sectionLabels, { + className: 'minecraft-statistic-stacked-line-chart-section-label', + x: 'time', + y: 'y', + text: 'label', + fill: 'category', + dx: -6, + }) + : undefined, Plot.ruleY([0]), targetValue ? Plot.ruleY([targetValue]) : undefined, ], @@ -130,7 +168,12 @@ export default function MinecraftStatisticStackedLineChart({ plot.remove(); } }; - }, [data, statisticDataProvider]); - - return
; + }, [data, selectedYAxisStyle, statisticDataProvider]); + + return ( +
+ +
+
+ ); } diff --git a/webview-ui/src/diagnostics_panel/controls/YAxisStyleDropdown.tsx b/webview-ui/src/diagnostics_panel/controls/YAxisStyleDropdown.tsx new file mode 100644 index 0000000..cdc72da --- /dev/null +++ b/webview-ui/src/diagnostics_panel/controls/YAxisStyleDropdown.tsx @@ -0,0 +1,40 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. + +import { VSCodeDropdown, VSCodeOption } from '@vscode/webview-ui-toolkit/react'; +import { YAxisStyle } from '../StatisticResolver'; + +type YAxisStyleDropdownProps = { + value: YAxisStyle; + onChange: (value: YAxisStyle) => void; +}; + +const Y_AXIS_STYLE_LABELS: Record = { + [YAxisStyle.Linear]: 'Linear', + [YAxisStyle.SquareRoot]: 'Square Root', + [YAxisStyle.Pow]: 'Power', + [YAxisStyle.Logarithmic]: 'Logarithmic', + [YAxisStyle.SymLog]: 'Symmetric Logarithmic' +}; + +export default function YAxisStyleDropdown({ value, onChange }: YAxisStyleDropdownProps) { + return ( + + ); +} diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx index f1ba78c..67cfb9b 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx +++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx @@ -2,7 +2,7 @@ import MinecraftStatisticLineChart from '../../controls/MinecraftStatisticLineCh import MinecraftStatisticStackedLineChart from '../../controls/MinecraftStatisticStackedLineChart'; import { StatisticPrefab } from '../StatisticPrefab'; import { SimpleStatisticProvider, NestedStatisticProvider } from '../../StatisticProvider'; -import { StatisticType, YAxisType, NestedStatResolver, createStatResolver } from '../../StatisticResolver'; +import { StatisticType, YAxisType, NestedStatResolver, createStatResolver, YAxisStyle } from '../../StatisticResolver'; import { TabPrefab, TabPrefabDataSource } from '../TabPrefab'; import { generateRowsFromStatsPrefabs } from '../utilities'; @@ -36,6 +36,8 @@ const loadedChunks: StatisticPrefab = { }) )} yLabel="Number of Chunks" + yAxisStyle={YAxisStyle.SquareRoot} + showSectionLabels /> ), };