Skip to content

Commit c789b36

Browse files
committed
feat(plot): add shouldPinPlotXScaleToLogRange utility and update usePlotChart logic
Introduce a new utility function to determine if the X axis should be pinned to the full log range during incremental updates. Update the usePlotChart implementation to utilize this function, ensuring the X scale remains consistent while data is loading. Additionally, refine the scoped preflight CSS for better integration with the host application.
1 parent e310973 commit c789b36

3 files changed

Lines changed: 77 additions & 11 deletions

File tree

src/features/panels/Plot/usePlotChart.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from 'vitest';
22
import {
33
diffSeriesTopology,
4+
shouldPinPlotXScaleToLogRange,
45
shouldRemountForIncrementalSeriesUpdate,
56
type SeriesSignature,
67
} from './usePlotChart';
@@ -86,3 +87,19 @@ describe('shouldRemountForIncrementalSeriesUpdate', () => {
8687
.toBe(false);
8788
});
8889
});
90+
91+
describe('shouldPinPlotXScaleToLogRange', () => {
92+
const logRange = { min: 0, max: 55 };
93+
94+
it('pins when log range exists and following view is off', () => {
95+
expect(shouldPinPlotXScaleToLogRange(logRange, 0)).toBe(true);
96+
});
97+
98+
it('does not pin without log range', () => {
99+
expect(shouldPinPlotXScaleToLogRange(undefined, 0)).toBe(false);
100+
});
101+
102+
it('does not pin when playhead following owns the X axis', () => {
103+
expect(shouldPinPlotXScaleToLogRange(logRange, 10)).toBe(false);
104+
});
105+
});

src/features/panels/Plot/usePlotChart.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,25 @@ export function shouldRemountForIncrementalSeriesUpdate(
171171
return false;
172172
}
173173

174+
/**
175+
* Whether incremental updates should pin the X axis to the full log range.
176+
* Skipped when playhead-following mode owns the X scale.
177+
*/
178+
export function shouldPinPlotXScaleToLogRange(
179+
xRange: { min: number; max: number } | undefined,
180+
followingViewWidthSec: number,
181+
): xRange is { min: number; max: number } {
182+
return xRange != null && followingViewWidthSec <= 0;
183+
}
184+
185+
/** Keep the X viewport on the full recording duration during range reads. */
186+
export function pinPlotXScaleToLogRange(
187+
chart: uPlot,
188+
xRange: { min: number; max: number },
189+
): void {
190+
chart.setScale('x', xRange);
191+
}
192+
174193
export function usePlotChart({
175194
containerRef,
176195
player,
@@ -228,8 +247,15 @@ export function usePlotChart({
228247
isLoading: () => loadingRef.current,
229248
});
230249

231-
uplotRef.current = mountPlotChart(container, dataset, options, xRange);
250+
const chart = mountPlotChart(container, dataset, options, xRange);
251+
uplotRef.current = chart;
232252
seriesSignaturesRef.current = seriesSignatures(dataset, hiddenSeries);
253+
if (
254+
loadingRef.current
255+
&& shouldPinPlotXScaleToLogRange(xRange, followingViewWidthRef.current)
256+
) {
257+
pinPlotXScaleToLogRange(chart, xRange);
258+
}
233259

234260
const observer = new ResizeObserver(() => {
235261
const chart = uplotRef.current;
@@ -309,13 +335,32 @@ export function usePlotChart({
309335
// setData second arg = false avoids a hard scale reset every batch, which is
310336
// what made the chart flash while data was streaming in.
311337
chart.setData(dataset.data, false);
338+
// setData(false) can shrink X to the loaded points only; pin to the full log
339+
// range so the axis stays 0…duration while curves grow incrementally.
340+
if (
341+
loadingRef.current
342+
&& shouldPinPlotXScaleToLogRange(xRange, followingViewWidthRef.current)
343+
) {
344+
pinPlotXScaleToLogRange(chart, xRange);
345+
}
312346
// Style mutations are read at draw time, so force one rebuild+redraw to
313347
// surface the new width/dash/stroke immediately.
314348
if (diff.kind === 'styleUpdate') {
315349
chart.redraw(true);
350+
} else if (loadingRef.current) {
351+
chart.redraw(false);
316352
}
317353
seriesSignaturesRef.current = nextSignatures;
318-
}, [config.followingViewWidthSec, config.xAxisMode, containerRef, dataset, destroyChart, hiddenSeries, mountChart]);
354+
}, [
355+
config.followingViewWidthSec,
356+
config.xAxisMode,
357+
containerRef,
358+
dataset,
359+
destroyChart,
360+
hiddenSeries,
361+
mountChart,
362+
xRange,
363+
]);
319364

320365
// When loading completes, force one Y-scale recompute so the locked-min/max
321366
// is replaced with the natural auto-fit range.

src/index.css

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22
@tailwind components;
33
@tailwind utilities;
44

5-
/* ── Scoped preflight ─────────────────────────────────────────────
6-
Replaces Tailwind's global CSS reset with one limited to
7-
#rosview-root so it doesn't bleed into the host application.
8-
──────────────────────────────────────────────────────────────── */
5+
/* ── Scoped preflight (tailwind preflight: false in tailwind.config.js) ──
6+
Limited to #rosview-root so resets never touch the host page.
7+
Only box-sizing is applied on * — do NOT zero borders globally; libraries
8+
such as uPlot and Dockview rely on border-* for chrome (e.g. hover crosshair).
9+
Border color for Tailwind utilities comes from `#rosview-root * { border-border }`.
10+
─────────────────────────────────────────────────────────────────────── */
911
@layer base {
10-
#rosview-root *, #rosview-root ::before, #rosview-root ::after {
12+
#rosview-root *,
13+
#rosview-root ::before,
14+
#rosview-root ::after {
1115
box-sizing: border-box;
12-
border-width: 0;
13-
border-style: solid;
14-
border-color: hsl(var(--border));
1516
}
16-
#rosview-root ::before, #rosview-root ::after { --tw-content: ''; }
17+
#rosview-root ::before,
18+
#rosview-root ::after {
19+
--tw-content: '';
20+
}
1721
#rosview-root {
1822
line-height: 1.5;
1923
-webkit-text-size-adjust: 100%;

0 commit comments

Comments
 (0)