@@ -26,8 +26,9 @@ var FROM_BR = alignmentConstants.FROM_BR;
26
26
var constants = require ( './constants' ) ;
27
27
var getUpdateObject = require ( './get_update_object' ) ;
28
28
29
- module . exports = function draw ( gd ) {
29
+ function draw ( gd ) {
30
30
var fullLayout = gd . _fullLayout ;
31
+ var gs = fullLayout . _size ;
31
32
32
33
var selectors = fullLayout . _infolayer . selectAll ( '.rangeselector' )
33
34
. data ( makeSelectorData ( gd ) , selectorKeyFunc ) ;
@@ -42,58 +43,82 @@ module.exports = function draw(gd) {
42
43
'pointer-events' : 'all'
43
44
} ) ;
44
45
45
- selectors . each ( function ( d ) {
46
+ selectors . each ( function ( ax ) {
46
47
var selector = d3 . select ( this ) ;
47
- var axisLayout = d ;
48
- var selectorLayout = axisLayout . rangeselector ;
48
+ var opts = ax . rangeselector ;
49
+ var dims = opts . _dims ;
50
+ var bw = opts . borderwidth ;
51
+
52
+ var lx = Math . round (
53
+ gs . l + gs . w * opts . x -
54
+ dims . width * FROM_TL [ Lib . getXanchor ( opts ) ]
55
+ ) ;
56
+ var ly = Math . round (
57
+ gs . t + gs . h * ( 1 - opts . y ) -
58
+ dims . height * FROM_TL [ Lib . getYanchor ( opts ) ]
59
+ ) ;
60
+
61
+ selector . attr ( 'transform' , 'translate(' + lx + ',' + ly + ')' ) ;
49
62
50
63
var buttons = selector . selectAll ( 'g.button' )
51
- . data ( Lib . filterVisible ( selectorLayout . buttons ) ) ;
64
+ . data ( Lib . filterVisible ( opts . buttons ) ) ;
52
65
53
66
buttons . enter ( ) . append ( 'g' )
54
67
. classed ( 'button' , true ) ;
55
68
56
69
buttons . exit ( ) . remove ( ) ;
57
70
58
- buttons . each ( function ( d ) {
71
+ var posX = 0 ;
72
+
73
+ buttons . each ( function ( d , i ) {
59
74
var button = d3 . select ( this ) ;
60
- var update = getUpdateObject ( axisLayout , d ) ;
75
+ var update = getUpdateObject ( ax , d ) ;
61
76
62
- d . _isActive = isActive ( axisLayout , d , update ) ;
77
+ d . _isActive = isActive ( ax , d , update ) ;
63
78
64
- button . call ( drawButtonRect , selectorLayout , d ) ;
65
- button . call ( drawButtonText , selectorLayout , d , gd ) ;
79
+ button . call ( drawButtonRect , opts , d ) ;
80
+ button . call ( drawButtonText , opts , d , gd ) ;
81
+
82
+ button . attr ( 'transform' , 'translate(' + [ bw + posX , bw ] + ')' ) ;
83
+ posX += dims . widths [ i ] + 5 ;
84
+
85
+ Drawing . setRect ( button . select ( '.selector-rect' ) , 0 , 0 ,
86
+ dims . widths [ i ] ,
87
+ dims . height
88
+ ) ;
89
+
90
+ svgTextUtils . positionText ( button . select ( '.selector-text' ) ,
91
+ dims . widths [ i ] / 2 ,
92
+ dims . height / 2 + dims . tyOffsets [ i ]
93
+ ) ;
66
94
67
95
button . on ( 'click' , function ( ) {
68
96
if ( gd . _dragged ) return ;
69
-
70
97
Registry . call ( '_guiRelayout' , gd , update ) ;
71
98
} ) ;
72
99
73
100
button . on ( 'mouseover' , function ( ) {
74
101
d . _isHovered = true ;
75
- button . call ( drawButtonRect , selectorLayout , d ) ;
102
+ button . call ( drawButtonRect , opts , d ) ;
76
103
} ) ;
77
104
78
105
button . on ( 'mouseout' , function ( ) {
79
106
d . _isHovered = false ;
80
- button . call ( drawButtonRect , selectorLayout , d ) ;
107
+ button . call ( drawButtonRect , opts , d ) ;
81
108
} ) ;
82
109
} ) ;
83
-
84
- reposition ( gd , buttons , selectorLayout , axisLayout . _name , selector ) ;
85
110
} ) ;
86
- } ;
111
+ }
87
112
88
113
function makeSelectorData ( gd ) {
89
- var axes = axisIds . list ( gd , 'x' , true ) ;
114
+ var axList = axisIds . list ( gd , 'x' , true ) ;
90
115
var data = [ ] ;
91
116
92
- for ( var i = 0 ; i < axes . length ; i ++ ) {
93
- var axis = axes [ i ] ;
117
+ for ( var i = 0 ; i < axList . length ; i ++ ) {
118
+ var ax = axList [ i ] ;
94
119
95
- if ( axis . rangeselector && axis . rangeselector . visible ) {
96
- data . push ( axis ) ;
120
+ if ( ax . rangeselector && ax . rangeselector . visible ) {
121
+ data . push ( ax ) ;
97
122
}
98
123
}
99
124
@@ -117,7 +142,7 @@ function isActive(axisLayout, opts, update) {
117
142
}
118
143
}
119
144
120
- function drawButtonRect ( button , selectorLayout , d ) {
145
+ function drawButtonRect ( button , opts , d ) {
121
146
var rect = Lib . ensureSingle ( button , 'rect' , 'selector-rect' , function ( s ) {
122
147
s . attr ( 'shape-rendering' , 'crispEdges' ) ;
123
148
} ) ;
@@ -127,129 +152,117 @@ function drawButtonRect(button, selectorLayout, d) {
127
152
'ry' : constants . ry
128
153
} ) ;
129
154
130
- rect . call ( Color . stroke , selectorLayout . bordercolor )
131
- . call ( Color . fill , getFillColor ( selectorLayout , d ) )
132
- . style ( 'stroke-width' , selectorLayout . borderwidth + 'px' ) ;
155
+ rect . call ( Color . stroke , opts . bordercolor )
156
+ . call ( Color . fill , getFillColor ( opts , d ) )
157
+ . style ( 'stroke-width' , opts . borderwidth + 'px' ) ;
133
158
}
134
159
135
- function getFillColor ( selectorLayout , d ) {
160
+ function getFillColor ( opts , d ) {
136
161
return ( d . _isActive || d . _isHovered ) ?
137
- selectorLayout . activecolor :
138
- selectorLayout . bgcolor ;
162
+ opts . activecolor :
163
+ opts . bgcolor ;
139
164
}
140
165
141
- function drawButtonText ( button , selectorLayout , d , gd ) {
166
+ function drawButtonText ( button , opts , d , gd ) {
142
167
function textLayout ( s ) {
143
168
svgTextUtils . convertToTspans ( s , gd ) ;
144
169
}
145
170
171
+ // TODO add MathJax support
172
+
146
173
var text = Lib . ensureSingle ( button , 'text' , 'selector-text' , function ( s ) {
147
174
s . classed ( 'user-select-none' , true )
148
175
. attr ( 'text-anchor' , 'middle' ) ;
149
176
} ) ;
150
177
151
- text . call ( Drawing . font , selectorLayout . font )
178
+ text . call ( Drawing . font , opts . font )
152
179
. text ( getLabel ( d , gd . _fullLayout . _meta ) )
153
180
. call ( textLayout ) ;
154
181
}
155
182
156
- function getLabel ( opts , _meta ) {
157
- if ( opts . label ) {
183
+ function getLabel ( d , _meta ) {
184
+ if ( d . label ) {
158
185
return _meta ?
159
- Lib . templateString ( opts . label , _meta ) :
160
- opts . label ;
186
+ Lib . templateString ( d . label , _meta ) :
187
+ d . label ;
161
188
}
162
189
163
- if ( opts . step === 'all' ) return 'all' ;
190
+ if ( d . step === 'all' ) return 'all' ;
164
191
165
- return opts . count + opts . step . charAt ( 0 ) ;
192
+ return d . count + d . step . charAt ( 0 ) ;
166
193
}
167
194
168
- function reposition ( gd , buttons , opts , axName , selector ) {
169
- var width = 0 ;
170
- var height = 0 ;
171
-
172
- var borderWidth = opts . borderwidth ;
173
-
174
- buttons . each ( function ( ) {
195
+ function findDimensions ( gd , ax ) {
196
+ var opts = ax . rangeselector ;
197
+ var buttonData = Lib . filterVisible ( opts . buttons ) ;
198
+ var nButtons = buttonData . length ;
199
+
200
+ var dims = opts . _dims = {
201
+ // width of each button
202
+ widths : new Array ( nButtons ) ,
203
+ // y offset for multi-line button text
204
+ tyOffsets : new Array ( nButtons ) ,
205
+ // height of range selector
206
+ height : 0 ,
207
+ // (total) width of range selector
208
+ width : 0
209
+ } ;
210
+
211
+ var fakeButtons = Drawing . tester . selectAll ( 'g.button' )
212
+ . data ( Lib . filterVisible ( opts . buttons ) ) ;
213
+
214
+ fakeButtons . enter ( ) . append ( 'g' )
215
+ . classed ( 'g.button' , true ) ;
216
+
217
+ fakeButtons . each ( function ( d , i ) {
175
218
var button = d3 . select ( this ) ;
219
+ drawButtonText ( button , opts , d , gd ) ;
176
220
var text = button . select ( '.selector-text' ) ;
177
221
222
+ var tLines = svgTextUtils . lineCount ( text ) ;
178
223
var tHeight = opts . font . size * LINE_SPACING ;
179
- var hEff = Math . max ( tHeight * svgTextUtils . lineCount ( text ) , 16 ) + 3 ;
180
-
181
- height = Math . max ( height , hEff ) ;
182
- } ) ;
183
-
184
- buttons . each ( function ( ) {
185
- var button = d3 . select ( this ) ;
186
- var rect = button . select ( '.selector-rect' ) ;
187
- var text = button . select ( '.selector-text' ) ;
188
-
189
224
var tWidth = text . node ( ) && Drawing . bBox ( text . node ( ) ) . width ;
190
- var tHeight = opts . font . size * LINE_SPACING ;
191
- var tLines = svgTextUtils . lineCount ( text ) ;
192
-
193
- var wEff = Math . max ( tWidth + 10 , constants . minButtonWidth ) ;
194
225
195
- // TODO add MathJax support
226
+ var hEff = Math . ceil ( Math . max ( tHeight * tLines , 16 ) + 3 ) ;
227
+ var wEff = Math . ceil ( Math . max ( tWidth + 10 , constants . minButtonWidth ) ) ;
196
228
197
- // TODO add buttongap attribute
229
+ dims . widths [ i ] = wEff ;
230
+ dims . tyOffsets [ i ] = 3 - ( tLines - 1 ) * tHeight / 2 ;
198
231
199
- button . attr ( 'transform' , 'translate(' +
200
- ( borderWidth + width ) + ',' + borderWidth +
201
- ')' ) ;
202
-
203
- rect . attr ( {
204
- x : 0 ,
205
- y : 0 ,
206
- width : wEff ,
207
- height : height
208
- } ) ;
209
-
210
- svgTextUtils . positionText ( text , wEff / 2 ,
211
- height / 2 - ( ( tLines - 1 ) * tHeight / 2 ) + 3 ) ;
212
-
213
- width += wEff + 5 ;
232
+ dims . width += wEff + 5 ;
233
+ dims . height = Math . max ( dims . height , hEff ) ;
214
234
} ) ;
215
235
216
- var graphSize = gd . _fullLayout . _size ;
217
- var lx = graphSize . l + graphSize . w * opts . x ;
218
- var ly = graphSize . t + graphSize . h * ( 1 - opts . y ) ;
219
-
220
- var xanchor = 'left' ;
221
- if ( Lib . isRightAnchor ( opts ) ) {
222
- lx -= width ;
223
- xanchor = 'right' ;
224
- }
225
- if ( Lib . isCenterAnchor ( opts ) ) {
226
- lx -= width / 2 ;
227
- xanchor = 'center' ;
228
- }
236
+ fakeButtons . remove ( ) ;
229
237
230
- var yanchor = 'top' ;
231
- if ( Lib . isBottomAnchor ( opts ) ) {
232
- ly -= height ;
233
- yanchor = 'bottom' ;
234
- }
235
- if ( Lib . isMiddleAnchor ( opts ) ) {
236
- ly -= height / 2 ;
237
- yanchor = 'middle' ;
238
- }
238
+ dims . width = Math . ceil ( dims . width ) ;
239
+ dims . height = Math . ceil ( dims . height ) ;
239
240
240
- width = Math . ceil ( width ) ;
241
- height = Math . ceil ( height ) ;
242
- lx = Math . round ( lx ) ;
243
- ly = Math . round ( ly ) ;
244
-
245
- Plots . autoMargin ( gd , axName + '-range-selector' , {
246
- x : opts . x ,
247
- y : opts . y ,
248
- l : width * FROM_TL [ xanchor ] ,
249
- r : width * FROM_BR [ xanchor ] ,
250
- b : height * FROM_BR [ yanchor ] ,
251
- t : height * FROM_TL [ yanchor ]
252
- } ) ;
241
+ return dims ;
242
+ }
253
243
254
- selector . attr ( 'transform' , 'translate(' + lx + ',' + ly + ')' ) ;
244
+ function pushMargin ( gd ) {
245
+ var selectorData = makeSelectorData ( gd ) ;
246
+
247
+ for ( var i = 0 ; i < selectorData . length ; i ++ ) {
248
+ var ax = selectorData [ i ] ;
249
+ var opts = ax . rangeselector ;
250
+ var xanchor = Lib . getXanchor ( opts ) ;
251
+ var yanchor = Lib . getYanchor ( opts ) ;
252
+ var dims = findDimensions ( gd , ax ) ;
253
+
254
+ Plots . autoMargin ( gd , ax . _id + '-range-selector' , {
255
+ x : opts . x ,
256
+ y : opts . y ,
257
+ l : dims . width * FROM_TL [ xanchor ] ,
258
+ r : dims . width * FROM_BR [ xanchor ] ,
259
+ b : dims . height * FROM_BR [ yanchor ] ,
260
+ t : dims . height * FROM_TL [ yanchor ]
261
+ } ) ;
262
+ }
255
263
}
264
+
265
+ module . exports = {
266
+ pushMargin : pushMargin ,
267
+ draw : draw
268
+ } ;
0 commit comments