@@ -480,26 +480,76 @@ function setupTraceToggle(g, gd) {
480
480
481
481
function handleClick ( g , gd , numClicks ) {
482
482
if ( gd . _dragged || gd . _editing ) return ;
483
+
483
484
var hiddenSlices = gd . _fullLayout . hiddenlabels ?
484
485
gd . _fullLayout . hiddenlabels . slice ( ) :
485
486
[ ] ;
486
487
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 ;
494
529
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
+ }
495
544
496
545
if ( numClicks === 1 && SHOWISOLATETIP && gd . data && gd . _context . showTips ) {
497
546
Lib . notifier ( 'Double click on legend to isolate individual trace' , 'long' ) ;
498
547
SHOWISOLATETIP = false ;
499
548
} else {
500
549
SHOWISOLATETIP = false ;
501
550
}
502
- if ( Registry . traceIs ( trace , 'pie' ) ) {
551
+
552
+ if ( Registry . traceIs ( fullTrace , 'pie' ) ) {
503
553
var thisLabel = legendItem . label ,
504
554
thisLabelIndex = hiddenSlices . indexOf ( thisLabel ) ;
505
555
@@ -520,52 +570,116 @@ function handleClick(g, gd, numClicks) {
520
570
521
571
Plotly . relayout ( gd , 'hiddenlabels' , hiddenSlices ) ;
522
572
} 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 ++ ) {
541
578
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 ) ;
545
582
}
546
583
}
547
584
}
548
585
549
586
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
+ }
552
611
} 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 ;
554
620
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 ;
557
628
break ;
558
629
}
559
630
}
560
- if ( sameAsLast ) {
561
- traceVisibility = true ;
562
- }
563
- var visibilityUpdates = [ ] ;
631
+
564
632
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
+ }
566
651
}
567
- Plotly . restyle ( gd , 'visible' , traceVisibility , visibilityUpdates ) ;
568
652
}
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 ) ;
569
683
}
570
684
}
571
685
0 commit comments