Skip to content

Commit ab23975

Browse files
ryan-williamsclaude
andcommitted
Replace react-plotly.js with pltly's Plot, fix stddev bands with smoothing
- Remove `react-plotly.js` and `@types/react-plotly.js` dependencies - Use pltly's `Plot` component (calls `Plotly.react()` directly) - Bump `pltly` to `8ca11d0` (adds `onRelayout`/`onDoubleClick`/`onAfterPlot` callbacks, `Data[]` prop type, `plotly.js-dist-min` default loader) - Bump `@types/plotly.js` to 3.0.10 to match pltly - Switch `use-kbd` from GitHub dist to npm `^0.12.0` - Fix stddev bands not showing when smoothing is active on raw data (`isRawData` guard now allows bands when `hasSmoothing`) - Plotly is now code-split into its own chunk via dynamic import Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 866a53d commit ab23975

File tree

4 files changed

+31
-65
lines changed

4 files changed

+31
-65
lines changed

www/package.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,13 @@
3131
"d3": "^7.9.0",
3232
"hyparquet": "github:runsascoded/hyparquet#6d0c51c423776ef04b21fab9f469fdc05a5dc4dd",
3333
"plotly.js": "github:runsascoded/plotly.js#f54c0537957a275ad24151b4a94c1a9ba4bd1878",
34-
"pltly": "https://gitlab.com/runsascoded/js/pltly/-/archive/c923a595cb3a907cbf8f506665429edb6bca124e/pltly-c923a595cb3a907cbf8f506665429edb6bca124e.tar.gz",
34+
"pltly": "https://gitlab.com/runsascoded/js/pltly/-/archive/6357eb6f6ae1473daeed999548f6262de550b381/pltly-6357eb6f6ae1473daeed999548f6262de550b381.tar.gz",
3535
"react": "^19.1.0",
3636
"react-dom": "^19.1.0",
3737
"react-icons": "^5.5.0",
38-
"react-plotly.js": "^2.6.0",
3938
"recharts": "^3.1.0",
4039
"uplot": "^1.6.32",
41-
"use-kbd": "https://github.com/runsascoded/use-kbd#34745efff8efdfd2c46e3e39d479a708aba704b2",
40+
"use-kbd": "^0.12.0",
4241
"use-prms": "^0.4.1",
4342
"use-session-storage-state": "^19.0.1"
4443
},
@@ -48,10 +47,9 @@
4847
"@rdub/eslint-config": "^0.0.3",
4948
"@rdub/og-lambda": "0.1.0",
5049
"@types/node": "^24.10.1",
51-
"@types/plotly.js": "^3.0.8",
50+
"@types/plotly.js": "^3.0.10",
5251
"@types/react": "^19.1.8",
5352
"@types/react-dom": "^19.1.6",
54-
"@types/react-plotly.js": "^2.6.3",
5553
"@vitejs/plugin-react": "^4.6.0",
5654
"@vitest/ui": "^3.2.4",
5755
"eslint": "^9.30.1",

www/pnpm-lock.yaml

Lines changed: 14 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

www/src/components/AwairChart.tsx

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { abs, ceil, max } from "@rdub/base"
2-
import { generateDateTicks, usePlotlyHoverDismiss, useTheme as usePlotTheme } from 'pltly'
2+
import { generateDateTicks, Plot, usePlotlyHoverDismiss, useTheme as usePlotTheme } from 'pltly'
33
import { useState, useMemo, useCallback, useEffect, useRef, memo } from 'react'
4-
import Plot from 'react-plotly.js'
54
import { useAction, useActions } from 'use-kbd'
65
import { useUrlState } from 'use-prms'
76
import { ChartControls, metricConfig, getRangeFloor } from './ChartControls'
@@ -806,7 +805,7 @@ export const AwairChart = memo(function AwairChart(
806805

807806
// Stddev fill regions (±σ shaded areas) - from smoothed data when available
808807
// Split into segments at gaps (null values) so Plotly doesn't interpolate across gaps
809-
if (!isRawData) {
808+
if (!isRawData || hasSmoothing) {
810809
deviceData.forEach(d => {
811810
// Primary stddev bands
812811
const bandTimestamps = hasSmoothing ? d.smoothedTimestamps : d.timestamps
@@ -985,18 +984,13 @@ export const AwairChart = memo(function AwairChart(
985984
)
986985
})()}
987986
<Plot
988-
className="plot-react"
989987
data={plotTraces}
990988
layout={{
991989
autosize: true,
992990
height: chartHeight,
993-
// uirevision keeps pan/zoom state stable during re-renders
994-
// Only changes when we explicitly want to reset the view
995991
uirevision: 'stable',
996992
xaxis: {
997993
type: 'date',
998-
// Always set autorange: false to ensure consistent drag behavior
999-
// If xAxisRange is null, Plotly will compute a default range
1000994
autorange: !xAxisRange,
1001995
...(xAxisRange && { range: xAxisRange }),
1002996
...(data.length > 0 && {
@@ -1008,14 +1002,11 @@ export const AwairChart = memo(function AwairChart(
10081002
linecolor: plotColors.gridcolor,
10091003
zerolinecolor: plotColors.gridcolor,
10101004
hoverformat: '',
1011-
// Spike line (vertical line at hover position)
10121005
showspikes: true,
10131006
spikemode: 'across',
10141007
spikethickness: 0.5,
10151008
spikecolor: plotColors.spikeColor,
10161009
spikedash: 'solid',
1017-
// Use custom format for unified hover title - this overrides tick labels in hover
1018-
// Cast needed because unifiedhovertitle isn't in @types/plotly.js yet
10191010
...({ unifiedhovertitle: { text: '%{x|%b %-d, %-I:%M%p}' } } as object),
10201011
...(tickvals.length > 0 && {
10211012
tickvals: tickvals,
@@ -1025,9 +1016,8 @@ export const AwairChart = memo(function AwairChart(
10251016
},
10261017
yaxis: createYAxisConfig('left', leftAutoRangeDisplay, getEffectiveFloor(l.val)),
10271018
...(secondaryConfig && r.val !== 'none' && { yaxis2: createYAxisConfig('right', rightAutoRangeDisplay, getEffectiveFloor(r.val as Metric)) }),
1028-
// Legend is now in flow above plot, so minimal top margin needed
10291019
margin: isOgMode
1030-
? { l: 50, r: 50, t: 10, b: 115 } // Minimal top (title is absolute), extra bottom for x-axis
1020+
? { l: 50, r: 50, t: 10, b: 115 }
10311021
: { l: 35, r: secondaryConfig ? 35 : 10, t: 5, b: 45 },
10321022
hovermode: 'x unified',
10331023
hoverlabel: {
@@ -1041,7 +1031,7 @@ export const AwairChart = memo(function AwairChart(
10411031
plot_bgcolor: plotColors.plotBg,
10421032
paper_bgcolor: plotColors.plotBg,
10431033
dragmode: 'pan',
1044-
showlegend: false, // Custom legend rendered separately for pixel-based positioning
1034+
showlegend: false,
10451035
selectdirection: 'h'
10461036
}}
10471037
config={{
@@ -1060,13 +1050,19 @@ export const AwairChart = memo(function AwairChart(
10601050
scale: 1
10611051
}
10621052
}}
1063-
useResizeHandler={true}
1053+
disableTheme
1054+
disableLegendHover
1055+
disableSoloTrace
1056+
disableHoverDismiss
1057+
disableMobileFix
10641058
onRelayout={handleRelayout}
10651059
onDoubleClick={handleDoubleClick}
1060+
onInitialized={() => {
1061+
(window as Window & { chartReady?: boolean }).chartReady = true
1062+
setupHoverDismiss()
1063+
}}
10661064
onAfterPlot={() => {
1067-
// Signal to og-lambda screenshot that the chart is ready
10681065
(window as Window & { chartReady?: boolean }).chartReady = true
1069-
// Setup click-outside-to-dismiss hover behavior
10701066
setupHoverDismiss()
10711067
}}
10721068
/>

www/vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ export default defineConfig({
1919
'plotly.js/dist/plotly': 'plotly.js/dist/plotly-basic.min.js',
2020
'plotly.js-dist-min': 'plotly.js/dist/plotly-basic.min.js',
2121
},
22-
},
22+
}
2323
})

0 commit comments

Comments
 (0)