@@ -24,32 +24,61 @@ var helpers = require('./helpers');
24
24
25
25
var MAIN_TITLE = 1 ;
26
26
27
+ var LEGEND_PATTERN = / ^ l e g e n d [ 0 - 9 ] * $ / ;
28
+
27
29
module . exports = function draw ( gd , opts ) {
28
- if ( ! opts ) opts = gd . _fullLayout . legend || { } ;
29
- return _draw ( gd , opts ) ;
30
+ if ( opts ) {
31
+ drawOne ( gd , opts ) ;
32
+ } else {
33
+ var fullLayout = gd . _fullLayout ;
34
+ var newLegends = fullLayout . _legends ;
35
+
36
+ // remove old legends that won't stay on the graph
37
+ var oldLegends = fullLayout . _infolayer . selectAll ( '[class^="legend"]' ) ;
38
+
39
+ oldLegends . each ( function ( ) {
40
+ var el = d3 . select ( this ) ;
41
+ var classes = el . attr ( 'class' ) ;
42
+ var cls = classes . split ( ' ' ) [ 0 ] ;
43
+ if ( cls . match ( LEGEND_PATTERN ) && newLegends . indexOf ( cls ) === - 1 ) {
44
+ el . remove ( ) ;
45
+ }
46
+ } ) ;
47
+
48
+ // draw/update new legends
49
+ for ( var i = 0 ; i < newLegends . length ; i ++ ) {
50
+ var legendId = newLegends [ i ] ;
51
+ var legendObj = gd . _fullLayout [ legendId ] ;
52
+ drawOne ( gd , legendObj ) ;
53
+ }
54
+ }
30
55
} ;
31
56
32
- function _draw ( gd , legendObj ) {
57
+ function drawOne ( gd , opts ) {
58
+ var legendObj = opts || { } ;
59
+
33
60
var fullLayout = gd . _fullLayout ;
34
- var clipId = 'legend' + fullLayout . _uid ;
35
- var layer ;
61
+ var legendId = getId ( legendObj ) ;
62
+
63
+ var clipId , layer ;
36
64
37
65
var inHover = legendObj . _inHover ;
38
66
if ( inHover ) {
39
67
layer = legendObj . layer ;
40
- clipId + = '- hover' ;
68
+ clipId = 'hover' ;
41
69
} else {
42
70
layer = fullLayout . _infolayer ;
71
+ clipId = legendId ;
43
72
}
44
-
45
73
if ( ! layer ) return ;
74
+ clipId += fullLayout . _uid ;
46
75
47
76
if ( ! gd . _legendMouseDownTime ) gd . _legendMouseDownTime = 0 ;
48
77
49
78
var legendData ;
50
79
if ( ! inHover ) {
51
80
if ( ! gd . calcdata ) return ;
52
- legendData = fullLayout . showlegend && getLegendData ( gd . calcdata , legendObj ) ;
81
+ legendData = fullLayout . showlegend && getLegendData ( gd . calcdata , legendObj , fullLayout . _legends . length > 1 ) ;
53
82
} else {
54
83
if ( ! legendObj . entries ) return ;
55
84
legendData = getLegendData ( legendObj . entries , legendObj ) ;
@@ -58,12 +87,12 @@ function _draw(gd, legendObj) {
58
87
var hiddenSlices = fullLayout . hiddenlabels || [ ] ;
59
88
60
89
if ( ! inHover && ( ! fullLayout . showlegend || ! legendData . length ) ) {
61
- layer . selectAll ( '.legend' ) . remove ( ) ;
90
+ layer . selectAll ( '.' + legendId ) . remove ( ) ;
62
91
fullLayout . _topdefs . select ( '#' + clipId ) . remove ( ) ;
63
- return Plots . autoMargin ( gd , 'legend' ) ;
92
+ return Plots . autoMargin ( gd , legendId ) ;
64
93
}
65
94
66
- var legend = Lib . ensureSingle ( layer , 'g' , 'legend' , function ( s ) {
95
+ var legend = Lib . ensureSingle ( layer , 'g' , legendId , function ( s ) {
67
96
if ( ! inHover ) s . attr ( 'pointer-events' , 'all' ) ;
68
97
} ) ;
69
98
@@ -84,14 +113,14 @@ function _draw(gd, legendObj) {
84
113
legendObj . _titleWidth = 0 ;
85
114
legendObj . _titleHeight = 0 ;
86
115
if ( title . text ) {
87
- var titleEl = Lib . ensureSingle ( scrollBox , 'text' , 'legendtitletext ') ;
116
+ var titleEl = Lib . ensureSingle ( scrollBox , 'text' , legendId + 'titletext ') ;
88
117
titleEl . attr ( 'text-anchor' , 'start' )
89
118
. call ( Drawing . font , title . font )
90
119
. text ( title . text ) ;
91
120
92
121
textLayout ( titleEl , scrollBox , gd , legendObj , MAIN_TITLE ) ; // handle mathjax or multi-line text and compute title height
93
122
} else {
94
- scrollBox . selectAll ( '.legendtitletext ' ) . remove ( ) ;
123
+ scrollBox . selectAll ( '.' + legendId + 'titletext ') . remove ( ) ;
95
124
}
96
125
97
126
var scrollBar = Lib . ensureSingle ( legend , 'rect' , 'scrollbar' , function ( s ) {
@@ -117,7 +146,7 @@ function _draw(gd, legendObj) {
117
146
} )
118
147
. each ( function ( ) { d3 . select ( this ) . call ( drawTexts , gd , legendObj ) ; } )
119
148
. call ( style , gd , legendObj )
120
- . each ( function ( ) { if ( ! inHover ) d3 . select ( this ) . call ( setupTraceToggle , gd ) ; } ) ;
149
+ . each ( function ( ) { if ( ! inHover ) d3 . select ( this ) . call ( setupTraceToggle , gd , legendId ) ; } ) ;
121
150
122
151
Lib . syncOrAsync ( [
123
152
Plots . previousPromises ,
@@ -127,7 +156,7 @@ function _draw(gd, legendObj) {
127
156
var bw = legendObj . borderwidth ;
128
157
129
158
if ( ! inHover ) {
130
- var expMargin = expandMargin ( gd ) ;
159
+ var expMargin = expandMargin ( gd , legendId ) ;
131
160
132
161
// IF expandMargin return a Promise (which is truthy),
133
162
// we're under a doAutoMargin redraw, so we don't have to
@@ -145,10 +174,10 @@ function _draw(gd, legendObj) {
145
174
ly = Lib . constrain ( ly , 0 , fullLayout . height - legendObj . _effHeight ) ;
146
175
147
176
if ( lx !== lx0 ) {
148
- Lib . log ( 'Constrain legend .x to make legend fit inside graph' ) ;
177
+ Lib . log ( 'Constrain ' + legendId + ' .x to make legend fit inside graph') ;
149
178
}
150
179
if ( ly !== ly0 ) {
151
- Lib . log ( 'Constrain legend .y to make legend fit inside graph' ) ;
180
+ Lib . log ( 'Constrain ' + legendId + ' .y to make legend fit inside graph') ;
152
181
}
153
182
}
154
183
@@ -294,7 +323,7 @@ function _draw(gd, legendObj) {
294
323
}
295
324
296
325
function scrollHandler ( scrollBoxY , scrollBarHeight , scrollRatio ) {
297
- legendObj . _scrollY = gd . _fullLayout . legend . _scrollY = scrollBoxY ;
326
+ legendObj . _scrollY = gd . _fullLayout [ legendId ] . _scrollY = scrollBoxY ;
298
327
Drawing . setTranslate ( scrollBox , 0 , - scrollBoxY ) ;
299
328
300
329
Drawing . setRect (
@@ -330,11 +359,14 @@ function _draw(gd, legendObj) {
330
359
} ,
331
360
doneFn : function ( ) {
332
361
if ( xf !== undefined && yf !== undefined ) {
333
- Registry . call ( '_guiRelayout' , gd , { 'legend.x' : xf , 'legend.y' : yf } ) ;
362
+ var obj = { } ;
363
+ obj [ legendId + '.x' ] = xf ;
364
+ obj [ legendId + '.y' ] = yf ;
365
+ Registry . call ( '_guiRelayout' , gd , obj ) ;
334
366
}
335
367
} ,
336
368
clickFn : function ( numClicks , e ) {
337
- var clickedTrace = layer . selectAll ( 'g.traces' ) . filter ( function ( ) {
369
+ var clickedTrace = groups . selectAll ( 'g.traces' ) . filter ( function ( ) {
338
370
var bbox = this . getBoundingClientRect ( ) ;
339
371
return (
340
372
e . clientX >= bbox . left && e . clientX <= bbox . right &&
@@ -402,6 +434,7 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
402
434
}
403
435
404
436
function drawTexts ( g , gd , legendObj ) {
437
+ var legendId = getId ( legendObj ) ;
405
438
var legendItem = g . data ( ) [ 0 ] [ 0 ] ;
406
439
var trace = legendItem . trace ;
407
440
var isPieLike = Registry . traceIs ( trace , 'pie-like' ) ;
@@ -424,7 +457,7 @@ function drawTexts(g, gd, legendObj) {
424
457
}
425
458
}
426
459
427
- var textEl = Lib . ensureSingle ( g , 'text' , 'legendtext ') ;
460
+ var textEl = Lib . ensureSingle ( g , 'text' , legendId + 'text ') ;
428
461
429
462
textEl . attr ( 'text-anchor' , 'start' )
430
463
. call ( Drawing . font , font )
@@ -478,12 +511,12 @@ function ensureLength(str, maxLength) {
478
511
return str ;
479
512
}
480
513
481
- function setupTraceToggle ( g , gd ) {
514
+ function setupTraceToggle ( g , gd , legendId ) {
482
515
var doubleClickDelay = gd . _context . doubleClickDelay ;
483
516
var newMouseDownTime ;
484
517
var numClicks = 1 ;
485
518
486
- var traceToggle = Lib . ensureSingle ( g , 'rect' , 'legendtoggle ', function ( s ) {
519
+ var traceToggle = Lib . ensureSingle ( g , 'rect' , legendId + 'toggle ', function ( s ) {
487
520
if ( ! gd . _context . staticPlot ) {
488
521
s . style ( 'cursor' , 'pointer' ) . attr ( 'pointer-events' , 'all' ) ;
489
522
}
@@ -505,7 +538,7 @@ function setupTraceToggle(g, gd) {
505
538
} ) ;
506
539
traceToggle . on ( 'mouseup' , function ( ) {
507
540
if ( gd . _dragged || gd . _editing ) return ;
508
- var legend = gd . _fullLayout . legend ;
541
+ var legend = gd . _fullLayout [ legendId ] ;
509
542
510
543
if ( ( new Date ( ) ) . getTime ( ) - gd . _legendMouseDownTime > doubleClickDelay ) {
511
544
numClicks = Math . max ( numClicks - 1 , 1 ) ;
@@ -531,7 +564,11 @@ function computeTextDimensions(g, gd, legendObj, aTitle) {
531
564
532
565
var mathjaxGroup = g . select ( 'g[class*=math-group]' ) ;
533
566
var mathjaxNode = mathjaxGroup . node ( ) ;
534
- if ( ! legendObj ) legendObj = gd . _fullLayout . legend ;
567
+
568
+ var legendId = getId ( legendObj ) ;
569
+ if ( ! legendObj ) {
570
+ legendObj = gd . _fullLayout [ legendId ] ;
571
+ }
535
572
var bw = legendObj . borderwidth ;
536
573
var font ;
537
574
if ( aTitle === MAIN_TITLE ) {
@@ -556,9 +593,12 @@ function computeTextDimensions(g, gd, legendObj, aTitle) {
556
593
Drawing . setTranslate ( mathjaxGroup , 0 , height * 0.25 ) ;
557
594
}
558
595
} else {
559
- var textEl = g . select ( aTitle === MAIN_TITLE ?
560
- '.legendtitletext' : '.legendtext'
561
- ) ;
596
+ var cls = '.' + legendId + (
597
+ aTitle === MAIN_TITLE ? 'title' : ''
598
+ ) + 'text' ;
599
+
600
+ var textEl = g . select ( cls ) ;
601
+
562
602
var textLines = svgTextUtils . lineCount ( textEl ) ;
563
603
var textNode = textEl . node ( ) ;
564
604
@@ -619,7 +659,7 @@ function getTitleSize(legendObj) {
619
659
}
620
660
621
661
/*
622
- * Computes in fullLayout.legend :
662
+ * Computes in fullLayout[legendId] :
623
663
*
624
664
* - _height: legend height including items past scrollbox height
625
665
* - _maxHeight: maximum legend height before scrollbox is required
@@ -630,7 +670,10 @@ function getTitleSize(legendObj) {
630
670
*/
631
671
function computeLegendDimensions ( gd , groups , traces , legendObj ) {
632
672
var fullLayout = gd . _fullLayout ;
633
- if ( ! legendObj ) legendObj = fullLayout . legend ;
673
+ var legendId = getId ( legendObj ) ;
674
+ if ( ! legendObj ) {
675
+ legendObj = fullLayout [ legendId ] ;
676
+ }
634
677
var gs = fullLayout . _size ;
635
678
636
679
var isVertical = helpers . isVertical ( legendObj ) ;
@@ -818,7 +861,7 @@ function computeLegendDimensions(gd, groups, traces, legendObj) {
818
861
var edits = gd . _context . edits ;
819
862
var isEditable = edits . legendText || edits . legendPosition ;
820
863
traces . each ( function ( d ) {
821
- var traceToggle = d3 . select ( this ) . select ( '.legendtoggle ' ) ;
864
+ var traceToggle = d3 . select ( this ) . select ( '.' + legendId + 'toggle ') ;
822
865
var h = d [ 0 ] . height ;
823
866
var legendgroup = d [ 0 ] . trace . legendgroup ;
824
867
var traceWidth = getTraceWidth ( d , legendObj , textGap ) ;
@@ -833,13 +876,13 @@ function computeLegendDimensions(gd, groups, traces, legendObj) {
833
876
} ) ;
834
877
}
835
878
836
- function expandMargin ( gd ) {
879
+ function expandMargin ( gd , legendId ) {
837
880
var fullLayout = gd . _fullLayout ;
838
- var legendObj = fullLayout . legend ;
881
+ var legendObj = fullLayout [ legendId ] ;
839
882
var xanchor = getXanchor ( legendObj ) ;
840
883
var yanchor = getYanchor ( legendObj ) ;
841
884
842
- return Plots . autoMargin ( gd , 'legend' , {
885
+ return Plots . autoMargin ( gd , legendId , {
843
886
x : legendObj . x ,
844
887
y : legendObj . y ,
845
888
l : legendObj . _width * ( FROM_TL [ xanchor ] ) ,
@@ -860,3 +903,7 @@ function getYanchor(legendObj) {
860
903
Lib . isMiddleAnchor ( legendObj ) ? 'middle' :
861
904
'top' ;
862
905
}
906
+
907
+ function getId ( legendObj ) {
908
+ return legendObj . _id || 'legend' ;
909
+ }
0 commit comments