@@ -21,18 +21,23 @@ var Color = require('../color');
21
21
var svgTextUtils = require ( '../../lib/svg_text_utils' ) ;
22
22
23
23
var constants = require ( './constants' ) ;
24
+ var interactConstants = require ( '../../constants/interactions' ) ;
24
25
var getLegendData = require ( './get_legend_data' ) ;
25
26
var style = require ( './style' ) ;
26
27
var helpers = require ( './helpers' ) ;
27
28
var anchorUtils = require ( './anchor_utils' ) ;
28
29
30
+ var SHOWISOLATETIP = true ;
31
+ var DBLCLICKDELAY = interactConstants . DBLCLICKDELAY ;
29
32
30
33
module . exports = function draw ( gd ) {
31
34
var fullLayout = gd . _fullLayout ;
32
35
var clipId = 'legend' + fullLayout . _uid ;
33
36
34
37
if ( ! fullLayout . _infolayer || ! gd . calcdata ) return ;
35
38
39
+ if ( ! gd . _legendMouseDownTime ) gd . _legendMouseDownTime = 0 ;
40
+
36
41
var opts = fullLayout . legend ,
37
42
legendData = fullLayout . showlegend && getLegendData ( gd . calcdata , opts ) ,
38
43
hiddenSlices = fullLayout . hiddenlabels || [ ] ;
@@ -325,9 +330,26 @@ module.exports = function draw(gd) {
325
330
xf = dragElement . align ( newX , 0 , gs . l , gs . l + gs . w , opts . xanchor ) ;
326
331
yf = dragElement . align ( newY , 0 , gs . t + gs . h , gs . t , opts . yanchor ) ;
327
332
} ,
328
- doneFn : function ( dragged ) {
333
+ doneFn : function ( dragged , numClicks , e ) {
329
334
if ( dragged && xf !== undefined && yf !== undefined ) {
330
335
Plotly . relayout ( gd , { 'legend.x' : xf , 'legend.y' : yf } ) ;
336
+ } else {
337
+ var clickedTrace =
338
+ fullLayout . _infolayer . selectAll ( 'g.traces' ) . filter ( function ( ) {
339
+ var bbox = this . getBoundingClientRect ( ) ;
340
+ return ( e . clientX >= bbox . left && e . clientX <= bbox . right &&
341
+ e . clientY >= bbox . top && e . clientY <= bbox . bottom ) ;
342
+ } ) ;
343
+ if ( clickedTrace . size ( ) > 0 ) {
344
+ if ( numClicks === 1 ) {
345
+ legend . _clickTimeout = setTimeout ( function ( ) { handleClick ( clickedTrace , gd , numClicks ) ; } , DBLCLICKDELAY ) ;
346
+ } else if ( numClicks === 2 ) {
347
+ if ( legend . _clickTimeout ) {
348
+ clearTimeout ( legend . _clickTimeout ) ;
349
+ }
350
+ handleClick ( clickedTrace , gd , numClicks ) ;
351
+ }
352
+ }
331
353
}
332
354
}
333
355
} ) ;
@@ -395,9 +417,8 @@ function drawTexts(g, gd) {
395
417
}
396
418
397
419
function setupTraceToggle ( g , gd ) {
398
- var hiddenSlices = gd . _fullLayout . hiddenlabels ?
399
- gd . _fullLayout . hiddenlabels . slice ( ) :
400
- [ ] ;
420
+ var newMouseDownTime ,
421
+ numClicks = 1 ;
401
422
402
423
var traceToggle = g . selectAll ( 'rect' )
403
424
. data ( [ 0 ] ) ;
@@ -408,41 +429,120 @@ function setupTraceToggle(g, gd) {
408
429
. attr ( 'pointer-events' , 'all' )
409
430
. call ( Color . fill , 'rgba(0,0,0,0)' ) ;
410
431
411
- traceToggle . on ( 'click' , function ( ) {
412
- if ( gd . _dragged ) return ;
413
432
414
- var legendItem = g . data ( ) [ 0 ] [ 0 ] ,
415
- fullData = gd . _fullData ,
416
- trace = legendItem . trace ,
417
- legendgroup = trace . legendgroup ,
418
- traceIndicesInGroup = [ ] ,
419
- tracei ,
420
- newVisible ;
433
+ traceToggle . on ( 'mousedown' , function ( ) {
434
+ newMouseDownTime = ( new Date ( ) ) . getTime ( ) ;
435
+ if ( newMouseDownTime - gd . _legendMouseDownTime < DBLCLICKDELAY ) {
436
+ // in a click train
437
+ numClicks += 1 ;
438
+ }
439
+ else {
440
+ // new click train
441
+ numClicks = 1 ;
442
+ gd . _legendMouseDownTime = newMouseDownTime ;
443
+ }
444
+ } ) ;
445
+ traceToggle . on ( 'mouseup' , function ( ) {
446
+ if ( gd . _dragged || gd . _editing ) return ;
447
+ var legend = gd . _fullLayout . legend ;
421
448
422
- if ( Registry . traceIs ( trace , 'pie' ) ) {
423
- var thisLabel = legendItem . label ,
424
- thisLabelIndex = hiddenSlices . indexOf ( thisLabel ) ;
449
+ if ( ( new Date ( ) ) . getTime ( ) - gd . _legendMouseDownTime > DBLCLICKDELAY ) {
450
+ numClicks = Math . max ( numClicks - 1 , 1 ) ;
451
+ }
452
+
453
+ if ( numClicks === 1 ) {
454
+ legend . _clickTimeout = setTimeout ( function ( ) { handleClick ( g , gd , numClicks ) ; } , DBLCLICKDELAY ) ;
455
+ } else if ( numClicks === 2 ) {
456
+ if ( legend . _clickTimeout ) {
457
+ clearTimeout ( legend . _clickTimeout ) ;
458
+ }
459
+ gd . _legendMouseDownTime = 0 ;
460
+ handleClick ( g , gd , numClicks ) ;
461
+ }
462
+ } ) ;
463
+ }
464
+
465
+ function handleClick ( g , gd , numClicks ) {
466
+ if ( gd . _dragged || gd . _editing ) return ;
467
+ var hiddenSlices = gd . _fullLayout . hiddenlabels ?
468
+ gd . _fullLayout . hiddenlabels . slice ( ) :
469
+ [ ] ;
470
+
471
+ var legendItem = g . data ( ) [ 0 ] [ 0 ] ,
472
+ fullData = gd . _fullData ,
473
+ trace = legendItem . trace ,
474
+ legendgroup = trace . legendgroup ,
475
+ traceIndicesInGroup = [ ] ,
476
+ tracei ,
477
+ newVisible ;
425
478
479
+
480
+ if ( numClicks === 1 && SHOWISOLATETIP && gd . data && gd . _context . showTips ) {
481
+ Lib . notifier ( 'Double click on legend to isolate individual trace' , 'long' ) ;
482
+ SHOWISOLATETIP = false ;
483
+ } else {
484
+ SHOWISOLATETIP = false ;
485
+ }
486
+ if ( Registry . traceIs ( trace , 'pie' ) ) {
487
+ var thisLabel = legendItem . label ,
488
+ thisLabelIndex = hiddenSlices . indexOf ( thisLabel ) ;
489
+
490
+ if ( numClicks === 1 ) {
426
491
if ( thisLabelIndex === - 1 ) hiddenSlices . push ( thisLabel ) ;
427
492
else hiddenSlices . splice ( thisLabelIndex , 1 ) ;
493
+ } else if ( numClicks === 2 ) {
494
+ hiddenSlices = [ ] ;
495
+ gd . calcdata [ 0 ] . forEach ( function ( d ) {
496
+ if ( thisLabel !== d . label ) {
497
+ hiddenSlices . push ( d . label ) ;
498
+ }
499
+ } ) ;
500
+ if ( gd . _fullLayout . hiddenlabels && gd . _fullLayout . hiddenlabels . length === hiddenSlices . length && thisLabelIndex === - 1 ) {
501
+ hiddenSlices = [ ] ;
502
+ }
503
+ }
504
+
505
+ Plotly . relayout ( gd , 'hiddenlabels' , hiddenSlices ) ;
506
+ } else {
507
+ var allTraces = [ ] ,
508
+ traceVisibility = [ ] ,
509
+ i ;
428
510
429
- Plotly . relayout ( gd , 'hiddenlabels' , hiddenSlices ) ;
511
+ for ( i = 0 ; i < fullData . length ; i ++ ) {
512
+ allTraces . push ( i ) ;
513
+ traceVisibility . push ( 'legendonly' ) ;
514
+ }
515
+
516
+ if ( legendgroup === '' ) {
517
+ traceIndicesInGroup = [ trace . index ] ;
518
+ traceVisibility [ trace . index ] = true ;
430
519
} else {
431
- if ( legendgroup === '' ) {
432
- traceIndicesInGroup = [ trace . index ] ;
433
- } else {
434
- for ( var i = 0 ; i < fullData . length ; i ++ ) {
435
- tracei = fullData [ i ] ;
436
- if ( tracei . legendgroup === legendgroup ) {
437
- traceIndicesInGroup . push ( tracei . index ) ;
438
- }
520
+ for ( i = 0 ; i < fullData . length ; i ++ ) {
521
+ tracei = fullData [ i ] ;
522
+ if ( tracei . legendgroup === legendgroup ) {
523
+ traceIndicesInGroup . push ( tracei . index ) ;
524
+ traceVisibility [ allTraces . indexOf ( i ) ] = true ;
439
525
}
440
526
}
527
+ }
441
528
529
+ if ( numClicks === 1 ) {
442
530
newVisible = trace . visible === true ? 'legendonly' : true ;
443
531
Plotly . restyle ( gd , 'visible' , newVisible , traceIndicesInGroup ) ;
532
+ } else if ( numClicks === 2 ) {
533
+ var sameAsLast = true ;
534
+ for ( i = 0 ; i < fullData . length ; i ++ ) {
535
+ if ( fullData [ i ] . visible !== traceVisibility [ i ] ) {
536
+ sameAsLast = false ;
537
+ break ;
538
+ }
539
+ }
540
+ if ( sameAsLast ) {
541
+ traceVisibility = true ;
542
+ }
543
+ Plotly . restyle ( gd , 'visible' , traceVisibility , allTraces ) ;
444
544
}
445
- } ) ;
545
+ }
446
546
}
447
547
448
548
function computeTextDimensions ( g , gd ) {
0 commit comments