OLD | NEW |
1 <!-- | 1 <!-- |
2 @license | 2 @license |
3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | 4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
7 Code distributed by Google as part of the polymer project is also | 7 Code distributed by Google as part of the polymer project is also |
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
9 --> | 9 --> |
10 | 10 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 Polymer.IronControlState, | 74 Polymer.IronControlState, |
75 Polymer.IronA11yKeysBehavior, | 75 Polymer.IronA11yKeysBehavior, |
76 Polymer.IronOverlayBehavior, | 76 Polymer.IronOverlayBehavior, |
77 Polymer.NeonAnimationRunnerBehavior | 77 Polymer.NeonAnimationRunnerBehavior |
78 ], | 78 ], |
79 | 79 |
80 properties: { | 80 properties: { |
81 /** | 81 /** |
82 * The orientation against which to align the dropdown content | 82 * The orientation against which to align the dropdown content |
83 * horizontally relative to the dropdown trigger. | 83 * horizontally relative to the dropdown trigger. |
| 84 * Overridden from `Polymer.IronFitBehavior`. |
84 */ | 85 */ |
85 horizontalAlign: { | 86 horizontalAlign: { |
86 type: String, | 87 type: String, |
87 value: 'left', | 88 value: 'left', |
88 reflectToAttribute: true | 89 reflectToAttribute: true |
89 }, | 90 }, |
90 | 91 |
91 /** | 92 /** |
92 * The orientation against which to align the dropdown content | 93 * The orientation against which to align the dropdown content |
93 * vertically relative to the dropdown trigger. | 94 * vertically relative to the dropdown trigger. |
| 95 * Overridden from `Polymer.IronFitBehavior`. |
94 */ | 96 */ |
95 verticalAlign: { | 97 verticalAlign: { |
96 type: String, | 98 type: String, |
97 value: 'top', | 99 value: 'top', |
98 reflectToAttribute: true | 100 reflectToAttribute: true |
99 }, | 101 }, |
100 | 102 |
101 /** | 103 /** |
102 * A pixel value that will be added to the position calculated for the | |
103 * given `horizontalAlign`, in the direction of alignment. You can thi
nk | |
104 * of it as increasing or decreasing the distance to the side of the | |
105 * screen given by `horizontalAlign`. | |
106 * | |
107 * If `horizontalAlign` is "left", this offset will increase or decrea
se | |
108 * the distance to the left side of the screen: a negative offset will | |
109 * move the dropdown to the left; a positive one, to the right. | |
110 * | |
111 * Conversely if `horizontalAlign` is "right", this offset will increa
se | |
112 * or decrease the distance to the right side of the screen: a negativ
e | |
113 * offset will move the dropdown to the right; a positive one, to the
left. | |
114 */ | |
115 horizontalOffset: { | |
116 type: Number, | |
117 value: 0, | |
118 notify: true | |
119 }, | |
120 | |
121 /** | |
122 * A pixel value that will be added to the position calculated for the | |
123 * given `verticalAlign`, in the direction of alignment. You can think | |
124 * of it as increasing or decreasing the distance to the side of the | |
125 * screen given by `verticalAlign`. | |
126 * | |
127 * If `verticalAlign` is "top", this offset will increase or decrease | |
128 * the distance to the top side of the screen: a negative offset will | |
129 * move the dropdown upwards; a positive one, downwards. | |
130 * | |
131 * Conversely if `verticalAlign` is "bottom", this offset will increas
e | |
132 * or decrease the distance to the bottom side of the screen: a negati
ve | |
133 * offset will move the dropdown downwards; a positive one, upwards. | |
134 */ | |
135 verticalOffset: { | |
136 type: Number, | |
137 value: 0, | |
138 notify: true | |
139 }, | |
140 | |
141 /** | |
142 * The element that should be used to position the dropdown when | |
143 * it is opened. | |
144 */ | |
145 positionTarget: { | |
146 type: Object | |
147 }, | |
148 | |
149 /** | |
150 * An animation config. If provided, this will be used to animate the | 104 * An animation config. If provided, this will be used to animate the |
151 * opening of the dropdown. | 105 * opening of the dropdown. |
152 */ | 106 */ |
153 openAnimationConfig: { | 107 openAnimationConfig: { |
154 type: Object | 108 type: Object |
155 }, | 109 }, |
156 | 110 |
157 /** | 111 /** |
158 * An animation config. If provided, this will be used to animate the | 112 * An animation config. If provided, this will be used to animate the |
159 * closing of the dropdown. | 113 * closing of the dropdown. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 }, | 146 }, |
193 | 147 |
194 listeners: { | 148 listeners: { |
195 'neon-animation-finish': '_onNeonAnimationFinish' | 149 'neon-animation-finish': '_onNeonAnimationFinish' |
196 }, | 150 }, |
197 | 151 |
198 observers: [ | 152 observers: [ |
199 '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign
, verticalOffset, horizontalOffset)' | 153 '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign
, verticalOffset, horizontalOffset)' |
200 ], | 154 ], |
201 | 155 |
202 attached: function() { | |
203 // Memoize this to avoid expensive calculations & relayouts. | |
204 this._isRTL = window.getComputedStyle(this).direction == 'rtl'; | |
205 this.positionTarget = this.positionTarget || this._defaultPositionTarg
et; | |
206 }, | |
207 | |
208 /** | 156 /** |
209 * The element that is contained by the dropdown, if any. | 157 * The element that is contained by the dropdown, if any. |
210 */ | 158 */ |
211 get containedElement() { | 159 get containedElement() { |
212 return Polymer.dom(this.$.content).getDistributedNodes()[0]; | 160 return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
213 }, | 161 }, |
214 | 162 |
215 /** | 163 /** |
216 * The element that should be focused when the dropdown opens. | 164 * The element that should be focused when the dropdown opens. |
217 * @deprecated | 165 * @deprecated |
218 */ | 166 */ |
219 get _focusTarget() { | 167 get _focusTarget() { |
220 return this.focusTarget || this.containedElement; | 168 return this.focusTarget || this.containedElement; |
221 }, | 169 }, |
222 | 170 |
223 /** | 171 detached: function() { |
224 * The element that should be used to position the dropdown when | 172 this.cancelAnimation(); |
225 * it opens, if no position target is configured. | 173 Polymer.IronDropdownScrollManager.removeScrollLock(this); |
226 */ | |
227 get _defaultPositionTarget() { | |
228 var parent = Polymer.dom(this).parentNode; | |
229 | |
230 if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | |
231 parent = parent.host; | |
232 } | |
233 | |
234 return parent; | |
235 }, | 174 }, |
236 | 175 |
237 /** | 176 /** |
238 * The horizontal align value, accounting for the RTL/LTR text direction
. | |
239 */ | |
240 get _localeHorizontalAlign() { | |
241 // In RTL, "left" becomes "right". | |
242 if (this._isRTL) { | |
243 return this.horizontalAlign === 'right' ? 'left' : 'right'; | |
244 } else { | |
245 return this.horizontalAlign; | |
246 } | |
247 }, | |
248 | |
249 /** | |
250 * The horizontal offset value used to position the dropdown. | |
251 * @param {ClientRect} dropdownRect | |
252 * @param {ClientRect} positionRect | |
253 * @param {boolean=} fromRight | |
254 * @return {number} pixels | |
255 * @private | |
256 */ | |
257 _horizontalAlignTargetValue: function(dropdownRect, positionRect, fromRi
ght) { | |
258 var target; | |
259 if (fromRight) { | |
260 target = document.documentElement.clientWidth - positionRect.right -
(this._fitWidth - dropdownRect.right); | |
261 } else { | |
262 target = positionRect.left - dropdownRect.left; | |
263 } | |
264 target += this.horizontalOffset; | |
265 | |
266 return Math.max(target, 0); | |
267 }, | |
268 | |
269 /** | |
270 * The vertical offset value used to position the dropdown. | |
271 * @param {ClientRect} dropdownRect | |
272 * @param {ClientRect} positionRect | |
273 * @param {boolean=} fromBottom | |
274 * @return {number} pixels | |
275 * @private | |
276 */ | |
277 _verticalAlignTargetValue: function(dropdownRect, positionRect, fromBott
om) { | |
278 var target; | |
279 if (fromBottom) { | |
280 target = document.documentElement.clientHeight - positionRect.bottom
- (this._fitHeight - dropdownRect.bottom); | |
281 } else { | |
282 target = positionRect.top - dropdownRect.top; | |
283 } | |
284 target += this.verticalOffset; | |
285 | |
286 return Math.max(target, 0); | |
287 }, | |
288 | |
289 /** | |
290 * Called when the value of `opened` changes. | 177 * Called when the value of `opened` changes. |
291 * Overridden from `IronOverlayBehavior` | 178 * Overridden from `IronOverlayBehavior` |
292 */ | 179 */ |
293 _openedChanged: function() { | 180 _openedChanged: function() { |
294 if (this.opened && this.disabled) { | 181 if (this.opened && this.disabled) { |
295 this.cancel(); | 182 this.cancel(); |
296 } else { | 183 } else { |
297 this.cancelAnimation(); | 184 this.cancelAnimation(); |
298 this.sizingTarget = this.containedElement || this.sizingTarget; | 185 this.sizingTarget = this.containedElement || this.sizingTarget; |
299 this._updateAnimationConfig(); | 186 this._updateAnimationConfig(); |
300 if (this.opened && !this.allowOutsideScroll) { | 187 if (this.opened && !this.allowOutsideScroll) { |
301 Polymer.IronDropdownScrollManager.pushScrollLock(this); | 188 Polymer.IronDropdownScrollManager.pushScrollLock(this); |
302 } else { | 189 } else { |
303 Polymer.IronDropdownScrollManager.removeScrollLock(this); | 190 Polymer.IronDropdownScrollManager.removeScrollLock(this); |
304 } | 191 } |
305 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); | 192 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); |
306 } | 193 } |
307 }, | 194 }, |
308 | 195 |
309 /** | 196 /** |
310 * Overridden from `IronOverlayBehavior`. | 197 * Overridden from `IronOverlayBehavior`. |
311 */ | 198 */ |
312 _renderOpened: function() { | 199 _renderOpened: function() { |
313 if (!this.noAnimations && this.animationConfig && this.animationConfig
.open) { | 200 if (!this.noAnimations && this.animationConfig.open) { |
314 if (this.withBackdrop) { | |
315 this.backdropElement.open(); | |
316 } | |
317 this.$.contentWrapper.classList.add('animating'); | 201 this.$.contentWrapper.classList.add('animating'); |
318 this.playAnimation('open'); | 202 this.playAnimation('open'); |
319 } else { | 203 } else { |
320 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; | 204 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; |
321 } | 205 } |
322 }, | 206 }, |
323 | 207 |
324 /** | 208 /** |
325 * Overridden from `IronOverlayBehavior`. | 209 * Overridden from `IronOverlayBehavior`. |
326 */ | 210 */ |
327 _renderClosed: function() { | 211 _renderClosed: function() { |
328 if (!this.noAnimations && this.animationConfig && this.animationConfig
.close) { | 212 if (!this.noAnimations && this.animationConfig.close) { |
329 if (this.withBackdrop) { | |
330 this.backdropElement.close(); | |
331 } | |
332 this.$.contentWrapper.classList.add('animating'); | 213 this.$.contentWrapper.classList.add('animating'); |
333 this.playAnimation('close'); | 214 this.playAnimation('close'); |
334 } else { | 215 } else { |
335 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; | 216 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; |
336 } | 217 } |
337 }, | 218 }, |
338 | 219 |
339 /** | 220 /** |
340 * Called when animation finishes on the dropdown (when opening or | 221 * Called when animation finishes on the dropdown (when opening or |
341 * closing). Responsible for "completing" the process of opening or | 222 * closing). Responsible for "completing" the process of opening or |
342 * closing the dropdown by positioning it or setting its display to | 223 * closing the dropdown by positioning it or setting its display to |
343 * none. | 224 * none. |
344 */ | 225 */ |
345 _onNeonAnimationFinish: function() { | 226 _onNeonAnimationFinish: function() { |
346 this.$.contentWrapper.classList.remove('animating'); | 227 this.$.contentWrapper.classList.remove('animating'); |
347 if (this.opened) { | 228 if (this.opened) { |
348 Polymer.IronOverlayBehaviorImpl._finishRenderOpened.apply(this); | 229 this._finishRenderOpened(); |
349 } else { | 230 } else { |
350 Polymer.IronOverlayBehaviorImpl._finishRenderClosed.apply(this); | 231 this._finishRenderClosed(); |
351 } | 232 } |
352 }, | 233 }, |
353 | 234 |
354 /** | 235 /** |
355 * Constructs the final animation config from different properties used | 236 * Constructs the final animation config from different properties used |
356 * to configure specific parts of the opening and closing animations. | 237 * to configure specific parts of the opening and closing animations. |
357 */ | 238 */ |
358 _updateAnimationConfig: function() { | 239 _updateAnimationConfig: function() { |
359 var animationConfig = {}; | 240 var animations = (this.openAnimationConfig || []).concat(this.closeAni
mationConfig || []); |
360 var animations = []; | 241 for (var i = 0; i < animations.length; i++) { |
361 | 242 animations[i].node = this.containedElement; |
362 if (this.openAnimationConfig) { | |
363 // NOTE(cdata): When making `display:none` elements visible in Safar
i, | |
364 // the element will paint once in a fully visible state, causing the | |
365 // dropdown to flash before it fades in. We prepend an | |
366 // `opaque-animation` to fix this problem: | |
367 animationConfig.open = [{ | |
368 name: 'opaque-animation', | |
369 }].concat(this.openAnimationConfig); | |
370 animations = animations.concat(animationConfig.open); | |
371 } | 243 } |
372 | 244 this.animationConfig = { |
373 if (this.closeAnimationConfig) { | 245 open: this.openAnimationConfig, |
374 animationConfig.close = this.closeAnimationConfig; | 246 close: this.closeAnimationConfig |
375 animations = animations.concat(animationConfig.close); | 247 }; |
376 } | |
377 | |
378 animations.forEach(function(animation) { | |
379 animation.node = this.containedElement; | |
380 }, this); | |
381 | |
382 this.animationConfig = animationConfig; | |
383 }, | 248 }, |
384 | 249 |
385 /** | 250 /** |
386 * Updates the overlay position based on configured horizontal | 251 * Updates the overlay position based on configured horizontal |
387 * and vertical alignment. | 252 * and vertical alignment. |
388 */ | 253 */ |
389 _updateOverlayPosition: function() { | 254 _updateOverlayPosition: function() { |
390 if (this.isAttached) { | 255 if (this.isAttached) { |
391 // This triggers iron-resize, and iron-overlay-behavior will call re
fit if needed. | 256 // This triggers iron-resize, and iron-overlay-behavior will call re
fit if needed. |
392 this.notifyResize(); | 257 this.notifyResize(); |
393 } | 258 } |
394 }, | 259 }, |
395 | 260 |
396 /** | 261 /** |
397 * Useful to call this after the element, the window, or the `fitInfo` | |
398 * element has been resized. Will maintain the scroll position. | |
399 */ | |
400 refit: function () { | |
401 if (!this.opened) { | |
402 return | |
403 } | |
404 var containedElement = this.containedElement; | |
405 var scrollTop; | |
406 var scrollLeft; | |
407 | |
408 if (containedElement) { | |
409 scrollTop = containedElement.scrollTop; | |
410 scrollLeft = containedElement.scrollLeft; | |
411 } | |
412 Polymer.IronFitBehavior.refit.apply(this, arguments); | |
413 | |
414 if (containedElement) { | |
415 containedElement.scrollTop = scrollTop; | |
416 containedElement.scrollLeft = scrollLeft; | |
417 } | |
418 }, | |
419 | |
420 /** | |
421 * Resets the target element's position and size constraints, and clear | |
422 * the memoized data. | |
423 */ | |
424 resetFit: function() { | |
425 Polymer.IronFitBehavior.resetFit.apply(this, arguments); | |
426 | |
427 var hAlign = this._localeHorizontalAlign; | |
428 var vAlign = this.verticalAlign; | |
429 // Set to 0, 0 in order to discover any offset caused by parent stacki
ng contexts. | |
430 this.style[hAlign] = this.style[vAlign] = '0px'; | |
431 | |
432 var dropdownRect = this.getBoundingClientRect(); | |
433 var positionRect = this.positionTarget.getBoundingClientRect(); | |
434 var horizontalValue = this._horizontalAlignTargetValue(dropdownRect, p
ositionRect, hAlign === 'right'); | |
435 var verticalValue = this._verticalAlignTargetValue(dropdownRect, posit
ionRect, vAlign === 'bottom'); | |
436 | |
437 this.style[hAlign] = horizontalValue + 'px'; | |
438 this.style[vAlign] = verticalValue + 'px'; | |
439 }, | |
440 | |
441 /** | |
442 * Overridden from `IronFitBehavior`. | |
443 * Ensure positionedBy has correct values for horizontally & vertically. | |
444 */ | |
445 _discoverInfo: function() { | |
446 Polymer.IronFitBehavior._discoverInfo.apply(this, arguments); | |
447 // Note(valdrin): in Firefox, an element with style `position: fixed;
bottom: 90vh; height: 20vh` | |
448 // would have `getComputedStyle(element).top < 0` (instead of being `a
uto`) http://jsbin.com/cofired/3/edit?html,output | |
449 // This would cause IronFitBehavior's `constrain` to wrongly calculate
sizes | |
450 // (it would use `top` instead of `bottom`), so we ensure we give the
correct values. | |
451 this._fitInfo.positionedBy.horizontally = this._localeHorizontalAlign; | |
452 this._fitInfo.positionedBy.vertically = this.verticalAlign; | |
453 }, | |
454 | |
455 /** | |
456 * Apply focus to focusTarget or containedElement | 262 * Apply focus to focusTarget or containedElement |
457 */ | 263 */ |
458 _applyFocus: function () { | 264 _applyFocus: function () { |
459 var focusTarget = this.focusTarget || this.containedElement; | 265 var focusTarget = this.focusTarget || this.containedElement; |
460 if (focusTarget && this.opened && !this.noAutoFocus) { | 266 if (focusTarget && this.opened && !this.noAutoFocus) { |
461 focusTarget.focus(); | 267 focusTarget.focus(); |
462 } else { | 268 } else { |
463 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); | 269 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); |
464 } | 270 } |
465 } | 271 } |
466 }); | 272 }); |
467 })(); | 273 })(); |
468 </script> | 274 </script> |
469 </dom-module> | 275 </dom-module> |
OLD | NEW |