@@ -43,12 +43,12 @@ module.exports = function draw(gd) {
4343 * <g item header />
4444 * <text item header-arrow />
4545 * <g header-group />
46- * <g item header />
46+ *
4747 * <text item header-arrow />
4848 * ...
4949 *
5050 * <g button-group />
51- * <g item button />
51+ *
5252 * <g item button />
5353 * ...
5454 */
@@ -77,12 +77,12 @@ module.exports = function draw(gd) {
7777 headerGroups . enter ( ) . append ( 'g' )
7878 . classed ( constants . headerGroupClassName , true ) ;
7979
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 )
8282 . data ( [ 0 ] ) ;
8383
8484 gButton . enter ( ) . append ( 'g' )
85- . classed ( constants . buttonGroupClassName , true )
85+ . classed ( constants . dropdownButtonGroupClassName , true )
8686 . style ( 'pointer-events' , 'all' ) ;
8787
8888 // whenever we add new menu, attach 'state' variable to node
@@ -114,12 +114,18 @@ module.exports = function draw(gd) {
114114 // draw headers!
115115 headerGroups . each ( function ( menuOpts ) {
116116 var gHeader = d3 . select ( this ) ;
117- drawHeader ( gd , gHeader , gButton , menuOpts ) ;
118117
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 ) ;
122127 }
128+
123129 } ) ;
124130} ;
125131
@@ -163,11 +169,15 @@ function drawHeader(gd, gHeader, gButton, menuOpts) {
163169
164170 var active = menuOpts . active ,
165171 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+ } ;
167177
168178 header
169179 . call ( drawItem , menuOpts , headerOpts )
170- . call ( setItemPosition , menuOpts , posOpts ) ;
180+ . call ( setItemPosition , menuOpts , posOpts , positionOverrides ) ;
171181
172182 // draw drop arrow at the right edge
173183 var arrow = gHeader . selectAll ( 'text.' + constants . headerArrowClassName )
@@ -181,8 +191,8 @@ function drawHeader(gd, gHeader, gButton, menuOpts) {
181191 . text ( '▼' ) ;
182192
183193 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
186196 } ) ;
187197
188198 header . on ( 'click' , function ( ) {
@@ -211,27 +221,63 @@ function drawHeader(gd, gHeader, gButton, menuOpts) {
211221}
212222
213223function 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' ) ?
215230 menuOpts . buttons :
216231 [ ] ;
217232
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 )
219236 . data ( buttonData ) ;
220237
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+
226255
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+ }
231274
232275 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 ,
235281 } ;
236282
237283 buttons . each ( function ( buttonOpts , buttonIndex ) {
@@ -247,7 +293,11 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
247293
248294 // fold up buttons and redraw header
249295 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+
251301 drawButtons ( gd , gHeader , gButton , menuOpts ) ;
252302
253303 // call button method
@@ -332,20 +382,27 @@ function styleOnMouseOut(item, menuOpts) {
332382
333383// find item dimensions (this mutates menuOpts)
334384function findDimenstions ( gd , menuOpts ) {
385+ var i ;
386+
335387 menuOpts . width = 0 ;
388+ menuOpts . width1 = 0 ;
336389 menuOpts . height = 0 ;
337390 menuOpts . height1 = 0 ;
391+ menuOpts . heights = [ ] ;
392+ menuOpts . widths = [ ] ;
393+ menuOpts . totalWidth = 0 ;
394+ menuOpts . totalHeight = 0 ;
338395 menuOpts . lx = 0 ;
339396 menuOpts . ly = 0 ;
340397
341- var fakeButtons = gd . _tester . selectAll ( 'g.' + constants . buttonClassName )
398+ var fakeButtons = gd . _tester . selectAll ( 'g.' + constants . dropdownButtonClassName )
342399 . data ( menuOpts . buttons ) ;
343400
344401 fakeButtons . enter ( ) . append ( 'g' )
345- . classed ( constants . buttonClassName , true ) ;
402+ . classed ( constants . dropdownButtonClassName , true ) ;
346403
347404 // loop over fake buttons to find width / height
348- fakeButtons . each ( function ( buttonOpts ) {
405+ fakeButtons . each ( function ( buttonOpts , i ) {
349406 var button = d3 . select ( this ) ;
350407
351408 button . call ( drawItem , menuOpts , buttonOpts ) ;
@@ -362,11 +419,49 @@ function findDimenstions(gd, menuOpts) {
362419 tLines = tspans [ 0 ] . length || 1 ,
363420 hEff = Math . max ( tHeight * tLines , constants . minHeight ) + constants . textOffsetY ;
364421
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:
366433 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+ }
368443 } ) ;
369444
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+
370465 fakeButtons . remove ( ) ;
371466
372467 var graphSize = gd . _fullLayout . _size ;
@@ -409,19 +504,21 @@ function findDimenstions(gd, menuOpts) {
409504}
410505
411506// set item positions (mutates posOpts)
412- function setItemPosition ( item , menuOpts , posOpts ) {
507+ function setItemPosition ( item , menuOpts , posOpts , overrideOpts ) {
508+ overrideOpts = overrideOpts || { } ;
413509 var rect = item . select ( '.' + constants . itemRectClassName ) ,
414510 text = item . select ( '.' + constants . itemTextClassName ) ,
415511 tspans = text . selectAll ( 'tspan' ) ,
416- borderWidth = menuOpts . borderwidth ;
512+ borderWidth = menuOpts . borderwidth ,
513+ index = posOpts . index ;
417514
418- Lib . setTranslate ( item , borderWidth , borderWidth + posOpts . y ) ;
515+ Lib . setTranslate ( item , borderWidth + posOpts . x , borderWidth + posOpts . y ) ;
419516
420517 rect . attr ( {
421518 x : 0 ,
422519 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 )
425522 } ) ;
426523
427524 var tHeight = menuOpts . font . size * constants . fontSizeToHeight ,
@@ -430,17 +527,23 @@ function setItemPosition(item, menuOpts, posOpts) {
430527
431528 var textAttrs = {
432529 x : constants . textOffsetX ,
433- y : menuOpts . height1 / 2 - spanOffset + constants . textOffsetY
530+ y : menuOpts . heights [ index ] / 2 - spanOffset + constants . textOffsetY
434531 } ;
435532
436533 text . attr ( textAttrs ) ;
437534 tspans . attr ( textAttrs ) ;
438535
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 ++ ;
440543}
441544
442545function removeAllButtons ( gButton ) {
443- gButton . selectAll ( 'g.' + constants . buttonClassName ) . remove ( ) ;
546+ gButton . selectAll ( 'g.' + constants . dropdownButtonClassName ) . remove ( ) ;
444547}
445548
446549function clearPushMargins ( gd ) {
0 commit comments