Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5cc7dc6

Browse files
committed
Rewrite legend visibility toggling
1 parent 11bb997 commit 5cc7dc6

File tree

2 files changed

+356
-58
lines changed

2 files changed

+356
-58
lines changed

src/components/legend/draw.js

Lines changed: 154 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -480,26 +480,76 @@ function setupTraceToggle(g, gd) {
480480

481481
function handleClick(g, gd, numClicks) {
482482
if(gd._dragged || gd._editing) return;
483+
483484
var hiddenSlices = gd._fullLayout.hiddenlabels ?
484485
gd._fullLayout.hiddenlabels.slice() :
485486
[];
486487

487-
var legendItem = g.data()[0][0],
488-
fullData = gd._fullData,
489-
trace = legendItem.trace,
490-
legendgroup = trace.legendgroup,
491-
traceIndicesInGroup = [],
492-
tracei,
493-
newVisible;
488+
var legendItem = g.data()[0][0];
489+
var fullData = gd._fullData;
490+
var fullTrace = legendItem.trace;
491+
var legendgroup = fullTrace.legendgroup;
492+
493+
var i, j, carr, key, keys, val;
494+
var attrUpdate = {};
495+
var attrIndices = [];
496+
var carrs = [];
497+
var carrIdx = [];
498+
499+
function insertUpdate(traceIndex, key, value) {
500+
var attrIndex = attrIndices.indexOf(traceIndex);
501+
var valueArray = attrUpdate[key];
502+
if(!valueArray) {
503+
valueArray = attrUpdate[key] = [];
504+
}
505+
506+
if(attrIndices.indexOf(traceIndex) === -1) {
507+
attrIndices.push(traceIndex);
508+
attrIndex = attrIndices.length - 1;
509+
}
510+
511+
valueArray[attrIndex] = value;
512+
513+
return attrIndex;
514+
}
515+
516+
function setVisibility(fullTrace, visibility) {
517+
var fullInput = fullTrace._fullInput;
518+
if(Registry.hasTransform(fullInput, 'groupby')) {
519+
var carr = carrs[fullInput.index];
520+
if(!carr) {
521+
var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
522+
var lastGroupbyIndex = groupbyIndices[groupbyIndices.length - 1];
523+
carr = Lib.keyedContainer(fullInput, 'transforms[' + lastGroupbyIndex + '].styles', 'target', 'value.visible');
524+
carrs[fullInput.index] = carr;
525+
}
526+
527+
// If not specified, assume visible:
528+
var curState = carr.get(fullTrace._group) || true;
494529

530+
if(curState !== false) {
531+
// true -> legendonly. All others toggle to true:
532+
carr.set(fullTrace._group, visibility);
533+
}
534+
carrIdx[fullInput.index] = insertUpdate(fullInput.index, 'visible', fullInput.visible === false ? false : true);
535+
} else {
536+
// false -> false (not possible since will not be visible in legend)
537+
// true -> legendonly
538+
// legendonly -> true
539+
var nextVisibility = fullInput.visible === false ? false : visibility;
540+
541+
insertUpdate(fullInput.index, 'visible', nextVisibility);
542+
}
543+
}
495544

496545
if(numClicks === 1 && SHOWISOLATETIP && gd.data && gd._context.showTips) {
497546
Lib.notifier('Double click on legend to isolate individual trace', 'long');
498547
SHOWISOLATETIP = false;
499548
} else {
500549
SHOWISOLATETIP = false;
501550
}
502-
if(Registry.traceIs(trace, 'pie')) {
551+
552+
if(Registry.traceIs(fullTrace, 'pie')) {
503553
var thisLabel = legendItem.label,
504554
thisLabelIndex = hiddenSlices.indexOf(thisLabel);
505555

@@ -520,52 +570,116 @@ function handleClick(g, gd, numClicks) {
520570

521571
Plotly.relayout(gd, 'hiddenlabels', hiddenSlices);
522572
} else {
523-
var allTraces = [],
524-
traceVisibility = [],
525-
i;
526-
527-
for(i = 0; i < fullData.length; i++) {
528-
allTraces.push(i);
529-
// Allow the legendonly state through for *all* trace types (including
530-
// carpet for which it's overridden with true/false in supplyDefaults)
531-
traceVisibility.push(
532-
Registry.traceIs(fullData[i], 'notLegendIsolatable') ? true : 'legendonly'
533-
);
534-
}
535-
536-
if(legendgroup === '') {
537-
traceIndicesInGroup = [trace.index];
538-
traceVisibility[trace.index] = true;
539-
} else {
540-
for(i = 0; i < fullData.length; i++) {
573+
var hasLegendgroup = legendgroup && legendgroup.length;
574+
var traceIndicesInGroup = [];
575+
var tracei;
576+
if (hasLegendgroup) {
577+
for (i = 0; i < fullData.length; i++) {
541578
tracei = fullData[i];
542-
if(tracei.legendgroup === legendgroup) {
543-
traceIndicesInGroup.push(tracei.index);
544-
traceVisibility[allTraces.indexOf(i)] = true;
579+
if (!tracei.visible) continue;
580+
if (tracei.legendgroup === legendgroup) {
581+
traceIndicesInGroup.push(i);
545582
}
546583
}
547584
}
548585

549586
if(numClicks === 1) {
550-
newVisible = trace.visible === true ? 'legendonly' : true;
551-
Plotly.restyle(gd, 'visible', newVisible, traceIndicesInGroup);
587+
var nextVisibility;
588+
589+
switch(fullTrace.visible) {
590+
case true:
591+
nextVisibility = 'legendonly';
592+
break;
593+
case false:
594+
nextVisibility = false;
595+
break;
596+
default:
597+
case 'legendonly':
598+
nextVisibility = true;
599+
break;
600+
}
601+
602+
if (hasLegendgroup) {
603+
for (i = 0; i < fullData.length; i++) {
604+
if (fullData[i].visible && fullData[i].legendgroup === legendgroup) {
605+
setVisibility(fullData[i], nextVisibility);
606+
}
607+
}
608+
} else {
609+
setVisibility(fullTrace, nextVisibility);
610+
}
552611
} else if(numClicks === 2) {
553-
var sameAsLast = true;
612+
// Compute the clicked index. expandedIndex does what we want for expanded traces
613+
// but also culls hidden traces. That means we have some work to do.
614+
var clickedIndex;
615+
for(clickedIndex = 0; clickedIndex < fullData.length; clickedIndex++) {
616+
if(fullData[clickedIndex] === fullTrace) break;
617+
}
618+
619+
var isIsolated = true;
554620
for(i = 0; i < fullData.length; i++) {
555-
if(fullData[i].visible !== traceVisibility[i]) {
556-
sameAsLast = false;
621+
var isClicked = fullData[i] === fullTrace;
622+
if (isClicked) continue;
623+
624+
var isInGroup = (hasLegendgroup && fullData[i].legendgroup === legendgroup);
625+
626+
if(!isInGroup && fullData[i].visible === true && !Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
627+
isIsolated = false;
557628
break;
558629
}
559630
}
560-
if(sameAsLast) {
561-
traceVisibility = true;
562-
}
563-
var visibilityUpdates = [];
631+
564632
for(i = 0; i < fullData.length; i++) {
565-
visibilityUpdates.push(allTraces[i]);
633+
// False is sticky; we don't change it.
634+
if(fullData[i].visible === false) continue;
635+
636+
if(Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
637+
continue;
638+
}
639+
640+
switch(fullTrace.visible) {
641+
case 'legendonly':
642+
setVisibility(fullData[i], true);
643+
break;
644+
case true:
645+
var otherState = isIsolated ? true : 'legendonly';
646+
var isClicked = fullData[i] === fullTrace;
647+
var isInGroup = isClicked || (hasLegendgroup && fullData[i].legendgroup === legendgroup);
648+
setVisibility(fullData[i], isInGroup ? true : otherState);
649+
break;
650+
}
566651
}
567-
Plotly.restyle(gd, 'visible', traceVisibility, visibilityUpdates);
568652
}
653+
654+
for(i = 0; i < carrs.length; i++) {
655+
carr = carrs[i];
656+
if(!carr) continue;
657+
var update = carr.constructUpdate();
658+
659+
var updateKeys = Object.keys(update);
660+
for(j = 0; j < updateKeys.length; j++) {
661+
key = updateKeys[j];
662+
val = attrUpdate[key] = attrUpdate[key] || [];
663+
val[carrIdx[i]] = update[key];
664+
}
665+
}
666+
667+
// The length of the value arrays should be equal and any unspecified
668+
// values should be explicitly undefined for them to get properly culled
669+
// as updates and not accidentally reset to the default value. This fills
670+
// out sparse arrays with the required number of undefined values:
671+
keys = Object.keys(attrUpdate);
672+
for(i = 0; i < keys.length; i++) {
673+
key = keys[i];
674+
for(j = 0; j < attrIndices.length; j++) {
675+
// Use hasOwnPropety to protect against falsey values:
676+
if(!attrUpdate[key].hasOwnProperty(j)) {
677+
attrUpdate[key][j] = undefined;
678+
}
679+
}
680+
}
681+
682+
Plotly.restyle(gd, attrUpdate, attrIndices);
569683
}
570684
}
571685

0 commit comments

Comments
 (0)