From e54fbac945bc5db5b977178485cd44a13cb03307 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Thu, 27 Nov 2025 18:00:31 -0500 Subject: [PATCH 1/2] fix: preserve z-indexed subplots during `relayout` for correct pan/zoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When traces have different `zorder` values, plotly.js creates z-indexed subplots (e.g., `xyz2`, `xyz3`) in `drawFramework`. However, when `relayout` is called (e.g., during resize), `supplyDefaults` resets `_plots` via `linkSubplots`, losing these z-indexed subplots. Since `relayout` doesn't trigger `drawFramework`, they aren't recreated. This caused pan/zoom to fail on the first attempt in react-plotly.js, because `updateSubplots` in `dragbox.js` couldn't find the z-indexed subplots to transform. Fix: 1. In `linkSubplots`, preserve z-indexed subplots from `oldSubplots` 2. In `updateSubplots`, include z-indexed subplots from `_plots` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/plots/cartesian/dragbox.js | 11 ++++++++++- src/plots/plots.js | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index f8fa211ad8f..b6f739bcc13 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -872,9 +872,18 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { function updateSubplots(viewBox) { var fullLayout = gd._fullLayout; var plotinfos = fullLayout._plots; - var subplots = fullLayout._subplots.cartesian; + var subplots = fullLayout._subplots.cartesian.slice(); var i, sp, xa, ya; + // Include z-indexed subplots from _plots that may not be in _subplots.cartesian + // (e.g., after a relayout that resets _subplots.cartesian but preserves _plots) + var zindexSeparator = constants.zindexSeparator; + for(var plotId in plotinfos) { + if(plotId.indexOf(zindexSeparator) !== -1 && subplots.indexOf(plotId) === -1) { + subplots.push(plotId); + } + } + if(hasSplom) { Registry.subplotsRegistry.splom.drag(gd); } diff --git a/src/plots/plots.js b/src/plots/plots.js index 6550eb353a8..41aec59c609 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -14,6 +14,7 @@ var Color = require('../components/color'); var BADNUM = require('../constants/numerical').BADNUM; var axisIDs = require('./cartesian/axis_ids'); +var cartesianConstants = require('./cartesian/constants'); var clearOutline = require('../components/shapes/handle_outline').clearOutline; var scatterAttrs = require('../traces/scatter/layout_attributes'); @@ -895,6 +896,19 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa } } + // Preserve z-indexed subplots (e.g. 'xyz2', 'xyz3') from oldSubplots to _plots only. + // These are created by drawFramework when traces have different zorder values. + // We preserve them to _plots (not _subplots.cartesian) so that drag/pan operations + // in dragbox.js can still transform them during relayout operations that don't + // trigger a full redraw (e.g., resize). If drawFramework runs later, it will + // properly update/remove these subplots as needed. + var zindexSeparator = cartesianConstants.zindexSeparator; + for(var oldId in oldSubplots) { + if(oldId.indexOf(zindexSeparator) !== -1 && !newSubplots[oldId]) { + newSubplots[oldId] = oldSubplots[oldId]; + } + } + // while we're at it, link overlaying axes to their main axes and // anchored axes to the axes they're anchored to var axList = axisIDs.list(mockGd, null, true); From 18f5dc77f4f2cdfef13955d791f8d10af496fc5a Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Thu, 27 Nov 2025 19:49:25 -0500 Subject: [PATCH 2/2] Add draftlog for #7659 --- draftlogs/7659_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7659_fix.md diff --git a/draftlogs/7659_fix.md b/draftlogs/7659_fix.md new file mode 100644 index 00000000000..ab4583be21e --- /dev/null +++ b/draftlogs/7659_fix.md @@ -0,0 +1 @@ + - Fix pan/zoom failing on first interaction when traces have different `zorder` values [[#7659](https://github.com/plotly/plotly.js/pull/7659)]