@@ -43,12 +43,12 @@ module.exports = function draw(gd) {
43
43
* <g item header />
44
44
* <text item header-arrow />
45
45
* <g header-group />
46
- * <g item header />
46
+ *
47
47
* <text item header-arrow />
48
48
* ...
49
49
*
50
50
* <g button-group />
51
- * <g item button />
51
+ *
52
52
* <g item button />
53
53
* ...
54
54
*/
@@ -77,12 +77,12 @@ module.exports = function draw(gd) {
77
77
headerGroups . enter ( ) . append ( 'g' )
78
78
. classed ( constants . headerGroupClassName , true ) ;
79
79
80
- // draw button container
81
- var gButton = menus . selectAll ( 'g.' + constants . buttonGroupClassName )
80
+ // draw dropdown button container
81
+ var gButton = menus . selectAll ( 'g.' + constants . dropdownButtonGroupClassName )
82
82
. data ( [ 0 ] ) ;
83
83
84
84
gButton . enter ( ) . append ( 'g' )
85
- . classed ( constants . buttonGroupClassName , true )
85
+ . classed ( constants . dropdownButtonGroupClassName , true )
86
86
. style ( 'pointer-events' , 'all' ) ;
87
87
88
88
// whenever we add new menu, attach 'state' variable to node
@@ -114,12 +114,18 @@ module.exports = function draw(gd) {
114
114
// draw headers!
115
115
headerGroups . each ( function ( menuOpts ) {
116
116
var gHeader = d3 . select ( this ) ;
117
- drawHeader ( gd , gHeader , gButton , menuOpts ) ;
118
117
119
- // update buttons if they are dropped
120
- if ( areMenuButtonsDropped ( gButton , menuOpts ) ) {
121
- drawButtons ( gd , gHeader , gButton , menuOpts ) ;
118
+ if ( menuOpts . type === 'dropdown' ) {
119
+ drawHeader ( gd , gHeader , gButton , menuOpts ) ;
120
+
121
+ // update buttons if they are dropped
122
+ if ( areMenuButtonsDropped ( gButton , menuOpts ) ) {
123
+ drawButtons ( gd , gHeader , gButton , menuOpts ) ;
124
+ }
125
+ } else {
126
+ drawButtons ( gd , gHeader , null , menuOpts ) ;
122
127
}
128
+
123
129
} ) ;
124
130
} ;
125
131
@@ -163,11 +169,15 @@ function drawHeader(gd, gHeader, gButton, menuOpts) {
163
169
164
170
var active = menuOpts . active ,
165
171
headerOpts = menuOpts . buttons [ active ] || constants . blankHeaderOpts ,
166
- posOpts = { y : 0 , yPad : 0 } ;
172
+ posOpts = { y : 0 , yPad : 0 , x : 0 , xPad : 0 , index : 0 } ,
173
+ positionOverrides = {
174
+ width : menuOpts . headerWidth ,
175
+ height : menuOpts . headerHeight
176
+ } ;
167
177
168
178
header
169
179
. call ( drawItem , menuOpts , headerOpts )
170
- . call ( setItemPosition , menuOpts , posOpts ) ;
180
+ . call ( setItemPosition , menuOpts , posOpts , positionOverrides ) ;
171
181
172
182
// draw drop arrow at the right edge
173
183
var arrow = gHeader . selectAll ( 'text.' + constants . headerArrowClassName )
@@ -181,8 +191,8 @@ function drawHeader(gd, gHeader, gButton, menuOpts) {
181
191
. text ( '▼' ) ;
182
192
183
193
arrow . attr ( {
184
- x : menuOpts . width - constants . arrowOffsetX ,
185
- y : menuOpts . height1 / 2 + constants . textOffsetY
194
+ x : menuOpts . headerWidth - constants . arrowOffsetX ,
195
+ y : menuOpts . headerHeight / 2 + constants . textOffsetY
186
196
} ) ;
187
197
188
198
header . on ( 'click' , function ( ) {
@@ -211,27 +221,63 @@ function drawHeader(gd, gHeader, gButton, menuOpts) {
211
221
}
212
222
213
223
function drawButtons ( gd , gHeader , gButton , menuOpts ) {
214
- var buttonData = gButton . attr ( constants . menuIndexAttrName ) !== '-1' ?
224
+ if ( ! gButton ) {
225
+ gButton = gHeader ;
226
+ gButton . attr ( 'pointer-events' , 'all' ) ;
227
+ }
228
+
229
+ var buttonData = ( gButton . attr ( constants . menuIndexAttrName ) !== '-1' || menuOpts . type === 'buttons' ) ?
215
230
menuOpts . buttons :
216
231
[ ] ;
217
232
218
- var buttons = gButton . selectAll ( 'g.' + constants . buttonClassName )
233
+ var klass = menuOpts . type === 'dropdown' ? constants . dropdownButtonClassName : constants . buttonClassName ;
234
+
235
+ var buttons = gButton . selectAll ( 'g.' + klass )
219
236
. data ( buttonData ) ;
220
237
221
- buttons . enter ( ) . append ( 'g' )
222
- . classed ( constants . buttonClassName , true )
223
- . attr ( 'opacity' , '0' )
224
- . transition ( )
225
- . attr ( 'opacity' , '1' ) ;
238
+ var enter = buttons . enter ( ) . append ( 'g' )
239
+ . classed ( klass , true ) ;
240
+
241
+ var exit = buttons . exit ( ) ;
242
+
243
+ if ( menuOpts . type === 'dropdown' ) {
244
+ enter . attr ( 'opacity' , '0' )
245
+ . transition ( )
246
+ . attr ( 'opacity' , '1' ) ;
247
+
248
+ exit . transition ( )
249
+ . attr ( 'opacity' , '0' )
250
+ . remove ( ) ;
251
+ } else {
252
+ exit . remove ( ) ;
253
+ }
254
+
226
255
227
- buttons . exit ( )
228
- . transition ( )
229
- . attr ( 'opacity' , '0' )
230
- . remove ( ) ;
256
+ var x0 = 0 ;
257
+ var y0 = 0 ;
258
+
259
+ if ( menuOpts . type === 'dropdown' ) {
260
+ if ( menuOpts . orientation === 'v' ) {
261
+ y0 = menuOpts . headerHeight + constants . gapButtonHeader ;
262
+ } else {
263
+ x0 = menuOpts . headerWidth + constants . gapButtonHeader ;
264
+ }
265
+ }
266
+
267
+ if ( menuOpts . type === 'dropdown' && menuOpts . openreverse ) {
268
+ if ( menuOpts . orientation === 'v' ) {
269
+ y0 = - 2 * constants . gapButtonHeader - constants . gapButton - menuOpts . totalHeight ;
270
+ } else {
271
+ x0 = - 2 * constants . gapButtonHeader - constants . gapButton - menuOpts . totalWidth ;
272
+ }
273
+ }
231
274
232
275
var posOpts = {
233
- y : menuOpts . height1 + constants . gapButtonHeader ,
234
- yPad : constants . gapButton
276
+ x : x0 ,
277
+ y : y0 ,
278
+ yPad : constants . gapButton ,
279
+ xPad : constants . gapButton ,
280
+ index : 0 ,
235
281
} ;
236
282
237
283
buttons . each ( function ( buttonOpts , buttonIndex ) {
@@ -247,7 +293,11 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
247
293
248
294
// fold up buttons and redraw header
249
295
gButton . attr ( constants . menuIndexAttrName , '-1' ) ;
250
- drawHeader ( gd , gHeader , gButton , menuOpts ) ;
296
+
297
+ if ( menuOpts . type === 'dropdown' ) {
298
+ drawHeader ( gd , gHeader , gButton , menuOpts ) ;
299
+ }
300
+
251
301
drawButtons ( gd , gHeader , gButton , menuOpts ) ;
252
302
253
303
// call button method
@@ -332,20 +382,27 @@ function styleOnMouseOut(item, menuOpts) {
332
382
333
383
// find item dimensions (this mutates menuOpts)
334
384
function findDimenstions ( gd , menuOpts ) {
385
+ var i ;
386
+
335
387
menuOpts . width = 0 ;
388
+ menuOpts . width1 = 0 ;
336
389
menuOpts . height = 0 ;
337
390
menuOpts . height1 = 0 ;
391
+ menuOpts . heights = [ ] ;
392
+ menuOpts . widths = [ ] ;
393
+ menuOpts . totalWidth = 0 ;
394
+ menuOpts . totalHeight = 0 ;
338
395
menuOpts . lx = 0 ;
339
396
menuOpts . ly = 0 ;
340
397
341
- var fakeButtons = gd . _tester . selectAll ( 'g.' + constants . buttonClassName )
398
+ var fakeButtons = gd . _tester . selectAll ( 'g.' + constants . dropdownButtonClassName )
342
399
. data ( menuOpts . buttons ) ;
343
400
344
401
fakeButtons . enter ( ) . append ( 'g' )
345
- . classed ( constants . buttonClassName , true ) ;
402
+ . classed ( constants . dropdownButtonClassName , true ) ;
346
403
347
404
// loop over fake buttons to find width / height
348
- fakeButtons . each ( function ( buttonOpts ) {
405
+ fakeButtons . each ( function ( buttonOpts , i ) {
349
406
var button = d3 . select ( this ) ;
350
407
351
408
button . call ( drawItem , menuOpts , buttonOpts ) ;
@@ -362,11 +419,49 @@ function findDimenstions(gd, menuOpts) {
362
419
tLines = tspans [ 0 ] . length || 1 ,
363
420
hEff = Math . max ( tHeight * tLines , constants . minHeight ) + constants . textOffsetY ;
364
421
365
- menuOpts . width = Math . max ( menuOpts . width , wEff ) ;
422
+ // Store per-item sizes since a row of horizontal buttons, for example,
423
+ // don't all need to be the same width:
424
+ menuOpts . widths [ i ] = wEff ;
425
+ menuOpts . heights [ i ] = hEff ;
426
+
427
+ if ( menuOpts . orientation === 'v' | menuOpts . type === 'dropdown' ) {
428
+ menuOpts . width = Math . max ( menuOpts . width , wEff ) ;
429
+ } else {
430
+ menuOpts . width += wEff ;
431
+ }
432
+ // Height and width of individual element:
366
433
menuOpts . height1 = Math . max ( menuOpts . height1 , hEff ) ;
367
- menuOpts . height += menuOpts . height1 ;
434
+ menuOpts . width1 = Math . max ( menuOpts . width1 , wEff ) ;
435
+
436
+ if ( menuOpts . orientation === 'v' ) {
437
+ menuOpts . totalWidth = Math . max ( menuOpts . totalWidth , wEff ) ;
438
+ menuOpts . totalHeight += hEff ;
439
+ } else {
440
+ menuOpts . totalWidth += wEff ;
441
+ menuOpts . totalHeight += Math . max ( menuOpts . totalWidth , hEff ) ;
442
+ }
368
443
} ) ;
369
444
445
+ menuOpts . headerWidth = menuOpts . width1 + constants . arrowPadX ;
446
+ menuOpts . headerHeight = menuOpts . height1 ;
447
+
448
+ if ( menuOpts . orientation === 'v' ) {
449
+ menuOpts . width = menuOpts . width1 ;
450
+
451
+ if ( menuOpts . type === 'dropdown' ) {
452
+ menuOpts . width1 += constants . arrowPadX ;
453
+ }
454
+
455
+ for ( i = 0 ; i < menuOpts . heights ; i ++ ) {
456
+ menuOpts . height += menuOpts . heights [ i ] ;
457
+ }
458
+ } else {
459
+ menuOpts . height = menuOpts . height1 ;
460
+ for ( i = 0 ; i < menuOpts . heights ; i ++ ) {
461
+ menuOpts . width += menuOpts . width [ i ] ;
462
+ }
463
+ }
464
+
370
465
fakeButtons . remove ( ) ;
371
466
372
467
var graphSize = gd . _fullLayout . _size ;
@@ -409,19 +504,21 @@ function findDimenstions(gd, menuOpts) {
409
504
}
410
505
411
506
// set item positions (mutates posOpts)
412
- function setItemPosition ( item , menuOpts , posOpts ) {
507
+ function setItemPosition ( item , menuOpts , posOpts , overrideOpts ) {
508
+ overrideOpts = overrideOpts || { } ;
413
509
var rect = item . select ( '.' + constants . itemRectClassName ) ,
414
510
text = item . select ( '.' + constants . itemTextClassName ) ,
415
511
tspans = text . selectAll ( 'tspan' ) ,
416
- borderWidth = menuOpts . borderwidth ;
512
+ borderWidth = menuOpts . borderwidth ,
513
+ index = posOpts . index ;
417
514
418
- Lib . setTranslate ( item , borderWidth , borderWidth + posOpts . y ) ;
515
+ Lib . setTranslate ( item , borderWidth + posOpts . x , borderWidth + posOpts . y ) ;
419
516
420
517
rect . attr ( {
421
518
x : 0 ,
422
519
y : 0 ,
423
- width : menuOpts . width ,
424
- height : menuOpts . height1
520
+ width : overrideOpts . width || ( menuOpts . orientation === 'v' ? menuOpts . width1 : menuOpts . widths [ index ] ) ,
521
+ height : overrideOpts . height || ( menuOpts . orientation === 'v' ? menuOpts . heights [ index ] : menuOpts . height1 )
425
522
} ) ;
426
523
427
524
var tHeight = menuOpts . font . size * constants . fontSizeToHeight ,
@@ -430,17 +527,23 @@ function setItemPosition(item, menuOpts, posOpts) {
430
527
431
528
var textAttrs = {
432
529
x : constants . textOffsetX ,
433
- y : menuOpts . height1 / 2 - spanOffset + constants . textOffsetY
530
+ y : menuOpts . heights [ index ] / 2 - spanOffset + constants . textOffsetY
434
531
} ;
435
532
436
533
text . attr ( textAttrs ) ;
437
534
tspans . attr ( textAttrs ) ;
438
535
439
- posOpts . y += menuOpts . height1 + posOpts . yPad ;
536
+ if ( menuOpts . orientation === 'v' ) {
537
+ posOpts . y += menuOpts . heights [ index ] + posOpts . yPad ;
538
+ } else {
539
+ posOpts . x += menuOpts . widths [ index ] + posOpts . xPad ;
540
+ }
541
+
542
+ posOpts . index ++ ;
440
543
}
441
544
442
545
function removeAllButtons ( gButton ) {
443
- gButton . selectAll ( 'g.' + constants . buttonClassName ) . remove ( ) ;
546
+ gButton . selectAll ( 'g.' + constants . dropdownButtonClassName ) . remove ( ) ;
444
547
}
445
548
446
549
function clearPushMargins ( gd ) {
0 commit comments