Skip to content

Commit 7bb5daa

Browse files
authored
Merge pull request #3236 from plotly/ui-key
Preserve ui state across Plotly.react redraws
2 parents db66ff1 + 067bd7d commit 7bb5daa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2768
-1124
lines changed

src/components/annotations/draw.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
584584
});
585585
},
586586
doneFn: function() {
587-
Registry.call('relayout', gd, getUpdateObj());
587+
Registry.call('_guiRelayout', gd, getUpdateObj());
588588
var notesBox = document.querySelector('.js-notes-box-panel');
589589
if(notesBox) notesBox.redraw(notesBox.selectedObj);
590590
}
@@ -667,7 +667,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
667667
},
668668
doneFn: function() {
669669
setCursor(annTextGroupInner);
670-
Registry.call('relayout', gd, getUpdateObj());
670+
Registry.call('_guiRelayout', gd, getUpdateObj());
671671
var notesBox = document.querySelector('.js-notes-box-panel');
672672
if(notesBox) notesBox.redraw(notesBox.selectedObj);
673673
}
@@ -691,7 +691,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
691691
modifyBase(ya._name + '.autorange', true);
692692
}
693693

694-
Registry.call('relayout', gd, getUpdateObj());
694+
Registry.call('_guiRelayout', gd, getUpdateObj());
695695
});
696696
}
697697
else annText.call(textLayout);

src/components/colorbar/draw.js

+14-12
Original file line numberDiff line numberDiff line change
@@ -485,15 +485,10 @@ module.exports = function draw(gd, id) {
485485
}
486486

487487
function drawTitle(titleClass, titleOpts) {
488-
var trace = getTrace();
489-
var propName = 'colorbar.title';
490-
var containerName = trace._module.colorbar.container;
491-
if(containerName) propName = containerName + '.' + propName;
492-
493488
var dfltTitleOpts = {
494489
propContainer: cbAxisOut,
495-
propName: propName,
496-
traceIndex: trace.index,
490+
propName: getPropName('title'),
491+
traceIndex: getTrace().index,
497492
placeholder: fullLayout._dfltTitle.colorbar,
498493
containerGroup: container.select('.cbtitle')
499494
};
@@ -645,11 +640,10 @@ module.exports = function draw(gd, id) {
645640
setCursor(container);
646641

647642
if(xf !== undefined && yf !== undefined) {
648-
Registry.call('restyle',
649-
gd,
650-
{'colorbar.x': xf, 'colorbar.y': yf},
651-
getTrace().index
652-
);
643+
var update = {};
644+
update[getPropName('x')] = xf;
645+
update[getPropName('y')] = yf;
646+
Registry.call('_guiRestyle', gd, update, getTrace().index);
653647
}
654648
}
655649
});
@@ -667,6 +661,14 @@ module.exports = function draw(gd, id) {
667661
}
668662
}
669663

664+
function getPropName(suffix) {
665+
var trace = getTrace();
666+
var propName = 'colorbar.';
667+
var containerName = trace._module.colorbar.container;
668+
if(containerName) propName = containerName + '.' + propName;
669+
return propName + suffix;
670+
}
671+
670672
// setter/getters for every item defined in opts
671673
Object.keys(opts).forEach(function(name) {
672674
component[name] = function(v) {

src/components/legend/attributes.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,15 @@ module.exports = {
120120
'or *bottom* of the legend.'
121121
].join(' ')
122122
},
123-
editType: 'legend',
123+
uirevision: {
124+
valType: 'any',
125+
role: 'info',
126+
editType: 'none',
127+
description: [
128+
'Controls persistence of legend-driven changes in trace and pie label',
129+
'visibility. Defaults to `layout.uirevision`.'
130+
].join(' ')
131+
},
124132
valign: {
125133
valType: 'enumerated',
126134
values: ['top', 'middle', 'bottom'],
@@ -130,5 +138,6 @@ module.exports = {
130138
description: [
131139
'Sets the vertical alignment of the symbols with respect to their associated text.',
132140
].join(' ')
133-
}
141+
},
142+
editType: 'legend'
134143
};

src/components/legend/defaults.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,18 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
6666
basePlotLayoutAttributes, 'showlegend',
6767
legendReallyHasATrace && legendTraceCount > 1);
6868

69-
if(showLegend === false) return;
69+
if(showLegend === false && !containerIn.uirevision) return;
7070

7171
var containerOut = Template.newContainer(layoutOut, 'legend');
7272

7373
function coerce(attr, dflt) {
7474
return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
7575
}
7676

77+
coerce('uirevision', layoutOut.uirevision);
78+
79+
if(showLegend === false) return;
80+
7781
coerce('bgcolor', layoutOut.paper_bgcolor);
7882
coerce('bordercolor');
7983
coerce('borderwidth');

src/components/legend/draw.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ module.exports = function draw(gd) {
338338
},
339339
doneFn: function() {
340340
if(xf !== undefined && yf !== undefined) {
341-
Registry.call('relayout', gd, {'legend.x': xf, 'legend.y': yf});
341+
Registry.call('_guiRelayout', gd, {'legend.x': xf, 'legend.y': yf});
342342
}
343343
},
344344
clickFn: function(numClicks, e) {
@@ -445,7 +445,7 @@ function drawTexts(g, gd, maxLength) {
445445
update.name = newName;
446446
}
447447

448-
return Registry.call('restyle', gd, update, traceIndex);
448+
return Registry.call('_guiRestyle', gd, update, traceIndex);
449449
});
450450
} else {
451451
textLayout(textEl);

src/components/legend/handle_click.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ module.exports = function handleClick(g, gd, numClicks) {
111111
}
112112
}
113113

114-
Registry.call('relayout', gd, 'hiddenlabels', hiddenSlices);
114+
Registry.call('_guiRelayout', gd, 'hiddenlabels', hiddenSlices);
115115
} else {
116116
var hasLegendgroup = legendgroup && legendgroup.length;
117117
var traceIndicesInGroup = [];
@@ -217,6 +217,6 @@ module.exports = function handleClick(g, gd, numClicks) {
217217
}
218218
}
219219

220-
Registry.call('restyle', gd, attrUpdate, attrIndices);
220+
Registry.call('_guiRestyle', gd, attrUpdate, attrIndices);
221221
}
222222
};

src/components/modebar/buttons.js

+35-41
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ function handleCartesian(gd, ev) {
261261
aobj[astr] = val;
262262
}
263263

264-
Registry.call('relayout', gd, aobj);
264+
Registry.call('_guiRelayout', gd, aobj);
265265
}
266266

267267
modeBarButtons.zoom3d = {
@@ -317,7 +317,7 @@ function handleDrag3d(gd, ev) {
317317
var val2d = (val === 'pan') ? val : 'zoom';
318318
layoutUpdate.dragmode = val2d;
319319

320-
Registry.call('relayout', gd, layoutUpdate);
320+
Registry.call('_guiRelayout', gd, layoutUpdate);
321321
}
322322

323323
modeBarButtons.resetCameraDefault3d = {
@@ -356,7 +356,7 @@ function handleCamera3d(gd, ev) {
356356
}
357357
}
358358

359-
Registry.call('relayout', gd, aobj);
359+
Registry.call('_guiRelayout', gd, aobj);
360360
}
361361

362362
modeBarButtons.hoverClosest3d = {
@@ -370,54 +370,48 @@ modeBarButtons.hoverClosest3d = {
370370
click: handleHover3d
371371
};
372372

373-
function handleHover3d(gd, ev) {
373+
function getNextHover3d(gd, ev) {
374374
var button = ev.currentTarget;
375-
var val = button._previousVal || false;
376-
var layout = gd.layout;
375+
var val = button._previousVal;
377376
var fullLayout = gd._fullLayout;
378377
var sceneIds = fullLayout._subplots.gl3d;
379378

380379
var axes = ['xaxis', 'yaxis', 'zaxis'];
381-
var spikeAttrs = ['showspikes', 'spikesides', 'spikethickness', 'spikecolor'];
382380

383381
// initialize 'current spike' object to be stored in the DOM
384382
var currentSpikes = {};
385-
var axisSpikes = {};
386383
var layoutUpdate = {};
387384

388385
if(val) {
389-
layoutUpdate = Lib.extendDeep(layout, val);
386+
layoutUpdate = val;
390387
button._previousVal = null;
391388
}
392389
else {
393-
layoutUpdate = {
394-
'allaxes.showspikes': false
395-
};
396-
397390
for(var i = 0; i < sceneIds.length; i++) {
398-
var sceneId = sceneIds[i],
399-
sceneLayout = fullLayout[sceneId],
400-
sceneSpikes = currentSpikes[sceneId] = {};
391+
var sceneId = sceneIds[i];
392+
var sceneLayout = fullLayout[sceneId];
401393

402-
sceneSpikes.hovermode = sceneLayout.hovermode;
403-
layoutUpdate[sceneId + '.hovermode'] = false;
394+
var hovermodeAStr = sceneId + '.hovermode';
395+
currentSpikes[hovermodeAStr] = sceneLayout.hovermode;
396+
layoutUpdate[hovermodeAStr] = false;
404397

405398
// copy all the current spike attrs
406399
for(var j = 0; j < 3; j++) {
407400
var axis = axes[j];
408-
axisSpikes = sceneSpikes[axis] = {};
409-
410-
for(var k = 0; k < spikeAttrs.length; k++) {
411-
var spikeAttr = spikeAttrs[k];
412-
axisSpikes[spikeAttr] = sceneLayout[axis][spikeAttr];
413-
}
401+
var spikeAStr = sceneId + '.' + axis + '.showspikes';
402+
layoutUpdate[spikeAStr] = false;
403+
currentSpikes[spikeAStr] = sceneLayout[axis].showspikes;
414404
}
415405
}
416406

417-
button._previousVal = Lib.extendDeep({}, currentSpikes);
407+
button._previousVal = currentSpikes;
418408
}
409+
return layoutUpdate;
410+
}
419411

420-
Registry.call('relayout', gd, layoutUpdate);
412+
function handleHover3d(gd, ev) {
413+
var layoutUpdate = getNextHover3d(gd, ev);
414+
Registry.call('_guiRelayout', gd, layoutUpdate);
421415
}
422416

423417
modeBarButtons.zoomInGeo = {
@@ -473,7 +467,7 @@ function handleGeo(gd, ev) {
473467
var scale = geoLayout.projection.scale;
474468
var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;
475469

476-
Registry.call('relayout', gd, id + '.projection.scale', newScale);
470+
Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale);
477471
} else if(attr === 'reset') {
478472
resetView(gd, 'geo');
479473
}
@@ -501,18 +495,20 @@ modeBarButtons.hoverClosestPie = {
501495
click: toggleHover
502496
};
503497

504-
function toggleHover(gd) {
498+
function getNextHover(gd) {
505499
var fullLayout = gd._fullLayout;
506500

507-
var onHoverVal;
501+
if(fullLayout.hovermode) return false;
502+
508503
if(fullLayout._has('cartesian')) {
509-
onHoverVal = fullLayout._isHoriz ? 'y' : 'x';
504+
return fullLayout._isHoriz ? 'y' : 'x';
510505
}
511-
else onHoverVal = 'closest';
512-
513-
var newHover = gd._fullLayout.hovermode ? false : onHoverVal;
506+
return 'closest';
507+
}
514508

515-
Registry.call('relayout', gd, 'hovermode', newHover);
509+
function toggleHover(gd) {
510+
var newHover = getNextHover(gd);
511+
Registry.call('_guiRelayout', gd, 'hovermode', newHover);
516512
}
517513

518514
// buttons when more then one plot types are present
@@ -526,12 +522,10 @@ modeBarButtons.toggleHover = {
526522
icon: Icons.tooltip_basic,
527523
gravity: 'ne',
528524
click: function(gd, ev) {
529-
toggleHover(gd);
525+
var layoutUpdate = getNextHover3d(gd, ev);
526+
layoutUpdate.hovermode = getNextHover(gd);
530527

531-
// the 3d hovermode update must come
532-
// last so that layout.hovermode update does not
533-
// override scene?.hovermode?.layout.
534-
handleHover3d(gd, ev);
528+
Registry.call('_guiRelayout', gd, layoutUpdate);
535529
}
536530
};
537531

@@ -567,7 +561,7 @@ modeBarButtons.toggleSpikelines = {
567561

568562
var aobj = setSpikelineVisibility(gd);
569563

570-
Registry.call('relayout', gd, aobj);
564+
Registry.call('_guiRelayout', gd, aobj);
571565
}
572566
};
573567

@@ -614,5 +608,5 @@ function resetView(gd, subplotType) {
614608
}
615609
}
616610

617-
Registry.call('relayout', gd, aObj);
611+
Registry.call('_guiRelayout', gd, aObj);
618612
}

src/components/rangeselector/draw.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ module.exports = function draw(gd) {
6868
button.on('click', function() {
6969
if(gd._dragged) return;
7070

71-
Registry.call('relayout', gd, update);
71+
Registry.call('_guiRelayout', gd, update);
7272
});
7373

7474
button.on('mouseover', function() {

src/components/rangeslider/draw.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ function setDataRange(rangeSlider, gd, axisOpts, opts) {
286286
dataMax = clamp(opts.p2d(opts._pixelMax));
287287

288288
window.requestAnimationFrame(function() {
289-
Registry.call('relayout', gd, axisOpts._name + '.range', [dataMin, dataMax]);
289+
Registry.call('_guiRelayout', gd, axisOpts._name + '.range', [dataMin, dataMax]);
290290
});
291291
}
292292

src/components/shapes/draw.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer) {
307307

308308
// Don't rely on clipPath being activated during re-layout
309309
setClipPath(shapePath, gd, shapeOptions);
310-
Registry.call('relayout', gd, editHelpers.getUpdateObj());
310+
Registry.call('_guiRelayout', gd, editHelpers.getUpdateObj());
311311
}
312312

313313
function abortDrag() {

src/components/titles/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,9 @@ function draw(gd, titleClass, options) {
239239
el.call(svgTextUtils.makeEditable, {gd: gd})
240240
.on('edit', function(text) {
241241
if(traceIndex !== undefined) {
242-
Registry.call('restyle', gd, prop, text, traceIndex);
242+
Registry.call('_guiRestyle', gd, prop, text, traceIndex);
243243
} else {
244-
Registry.call('relayout', gd, prop, text);
244+
Registry.call('_guiRelayout', gd, prop, text);
245245
}
246246
})
247247
.on('cancel', function() {

src/core.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ var plotApi = require('./plot_api');
2929
var methodNames = Object.keys(plotApi);
3030
for(var i = 0; i < methodNames.length; i++) {
3131
var name = methodNames[i];
32-
exports[name] = plotApi[name];
32+
// _ -> private API methods, but still registered for internal use
33+
if(name.charAt(0) !== '_') exports[name] = plotApi[name];
3334
register({
3435
moduleType: 'apiMethod',
3536
name: name,

src/plot_api/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ exports.restyle = main.restyle;
1616
exports.relayout = main.relayout;
1717
exports.redraw = main.redraw;
1818
exports.update = main.update;
19+
exports._guiRestyle = main._guiRestyle;
20+
exports._guiRelayout = main._guiRelayout;
21+
exports._guiUpdate = main._guiUpdate;
22+
exports._storeDirectGUIEdit = main._storeDirectGUIEdit;
1923
exports.react = main.react;
2024
exports.extendTraces = main.extendTraces;
2125
exports.prependTraces = main.prependTraces;

0 commit comments

Comments
 (0)