| 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 |