@@ -480,26 +480,76 @@ function setupTraceToggle(g, gd) {
480480
481481function 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