| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 (function() { | 5 (function() { |
| 6 /** | 6 /** |
| 7 * Size of additional padding in the inner scrollable section of the dropdown. | 7 * Size of additional padding in the inner scrollable section of the dropdown. |
| 8 */ | 8 */ |
| 9 var DROPDOWN_INNER_PADDING = 12; | 9 var DROPDOWN_INNER_PADDING = 12; |
| 10 | 10 |
| 11 /** Size of vertical padding on the outer #dropdown element. */ | 11 /** Size of vertical padding on the outer #dropdown element. */ |
| 12 var DROPDOWN_OUTER_PADDING = 2; | 12 var DROPDOWN_OUTER_PADDING = 2; |
| 13 | 13 |
| 14 /** Minimum height of toolbar dropdowns (px). */ | 14 /** Minimum height of toolbar dropdowns (px). */ |
| 15 var MIN_DROPDOWN_HEIGHT = 200; | 15 var MIN_DROPDOWN_HEIGHT = 200; |
| 16 | 16 |
| 17 Polymer({ | 17 Polymer({ |
| 18 is: 'viewer-toolbar-dropdown', | 18 is: 'viewer-toolbar-dropdown', |
| 19 | 19 |
| 20 properties: { | 20 properties: { |
| 21 /** String to be displayed at the top of the dropdown. */ | 21 /** String to be displayed at the top of the dropdown. */ |
| 22 header: String, | 22 header: String, |
| 23 | 23 |
| 24 /** Icon to display when the dropdown is closed. */ | 24 /** Icon to display when the dropdown is closed. */ |
| 25 closedIcon: String, | 25 closedIcon: String, |
| 26 | 26 |
| 27 /** Icon to display when the dropdown is open. */ | 27 /** Icon to display when the dropdown is open. */ |
| 28 openIcon: String, | 28 openIcon: String, |
| 29 | 29 |
| 30 /** True if the dropdown is currently open. */ | 30 /** True if the dropdown is currently open. */ |
| 31 dropdownOpen: { | 31 dropdownOpen: {type: Boolean, reflectToAttribute: true, value: false}, |
| 32 type: Boolean, | |
| 33 reflectToAttribute: true, | |
| 34 value: false | |
| 35 }, | |
| 36 | 32 |
| 37 /** Toolbar icon currently being displayed. */ | 33 /** Toolbar icon currently being displayed. */ |
| 38 dropdownIcon: { | 34 dropdownIcon: { |
| 39 type: String, | 35 type: String, |
| 40 computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)' | 36 computed: 'computeIcon_(dropdownOpen, closedIcon, openIcon)' |
| 41 }, | |
| 42 | |
| 43 /** Lowest vertical point that the dropdown should occupy (px). */ | |
| 44 lowerBound: { | |
| 45 type: Number, | |
| 46 observer: 'lowerBoundChanged_' | |
| 47 }, | |
| 48 | |
| 49 /** | |
| 50 * True if the max-height CSS property for the dropdown scroll container | |
| 51 * is valid. If false, the height will be updated the next time the | |
| 52 * dropdown is visible. | |
| 53 */ | |
| 54 maxHeightValid_: false, | |
| 55 | |
| 56 /** Current animation being played, or null if there is none. */ | |
| 57 animation_: Object | |
| 58 }, | 37 }, |
| 59 | 38 |
| 60 computeIcon_: function(dropdownOpen, closedIcon, openIcon) { | 39 /** Lowest vertical point that the dropdown should occupy (px). */ |
| 61 return dropdownOpen ? openIcon : closedIcon; | 40 lowerBound: {type: Number, observer: 'lowerBoundChanged_'}, |
| 62 }, | |
| 63 | |
| 64 lowerBoundChanged_: function() { | |
| 65 this.maxHeightValid_ = false; | |
| 66 if (this.dropdownOpen) | |
| 67 this.updateMaxHeight(); | |
| 68 }, | |
| 69 | |
| 70 toggleDropdown: function() { | |
| 71 this.dropdownOpen = !this.dropdownOpen; | |
| 72 if (this.dropdownOpen) { | |
| 73 this.$.dropdown.style.display = 'block'; | |
| 74 if (!this.maxHeightValid_) | |
| 75 this.updateMaxHeight(); | |
| 76 } | |
| 77 this.cancelAnimation_(); | |
| 78 this.playAnimation_(this.dropdownOpen); | |
| 79 }, | |
| 80 | |
| 81 updateMaxHeight: function() { | |
| 82 var scrollContainer = this.$['scroll-container']; | |
| 83 var height = this.lowerBound - | |
| 84 scrollContainer.getBoundingClientRect().top - | |
| 85 DROPDOWN_INNER_PADDING; | |
| 86 height = Math.max(height, MIN_DROPDOWN_HEIGHT); | |
| 87 scrollContainer.style.maxHeight = height + 'px'; | |
| 88 this.maxHeightValid_ = true; | |
| 89 }, | |
| 90 | |
| 91 cancelAnimation_: function() { | |
| 92 if (this._animation) | |
| 93 this._animation.cancel(); | |
| 94 }, | |
| 95 | 41 |
| 96 /** | 42 /** |
| 97 * Start an animation on the dropdown. | 43 * True if the max-height CSS property for the dropdown scroll container |
| 98 * @param {boolean} isEntry True to play entry animation, false to play | 44 * is valid. If false, the height will be updated the next time the |
| 99 * exit. | 45 * dropdown is visible. |
| 100 * @private | |
| 101 */ | 46 */ |
| 102 playAnimation_: function(isEntry) { | 47 maxHeightValid_: false, |
| 103 this.animation_ = isEntry ? this.animateEntry_() : this.animateExit_(); | |
| 104 this.animation_.onfinish = function() { | |
| 105 this.animation_ = null; | |
| 106 if (!this.dropdownOpen) | |
| 107 this.$.dropdown.style.display = 'none'; | |
| 108 }.bind(this); | |
| 109 }, | |
| 110 | 48 |
| 111 animateEntry_: function() { | 49 /** Current animation being played, or null if there is none. */ |
| 112 var maxHeight = this.$.dropdown.getBoundingClientRect().height - | 50 animation_: Object |
| 113 DROPDOWN_OUTER_PADDING; | 51 }, |
| 114 | 52 |
| 115 if (maxHeight < 0) | 53 computeIcon_: function(dropdownOpen, closedIcon, openIcon) { |
| 116 maxHeight = 0; | 54 return dropdownOpen ? openIcon : closedIcon; |
| 55 }, |
| 117 | 56 |
| 118 var fade = new KeyframeEffect(this.$.dropdown, [ | 57 lowerBoundChanged_: function() { |
| 119 {opacity: 0}, | 58 this.maxHeightValid_ = false; |
| 120 {opacity: 1} | 59 if (this.dropdownOpen) |
| 121 ], {duration: 150, easing: 'cubic-bezier(0, 0, 0.2, 1)'}); | 60 this.updateMaxHeight(); |
| 122 var slide = new KeyframeEffect(this.$.dropdown, [ | 61 }, |
| 123 {height: '20px', transform: 'translateY(-10px)'}, | |
| 124 {height: maxHeight + 'px', transform: 'translateY(0)'} | |
| 125 ], {duration: 250, easing: 'cubic-bezier(0, 0, 0.2, 1)'}); | |
| 126 | 62 |
| 127 return document.timeline.play(new GroupEffect([fade, slide])); | 63 toggleDropdown: function() { |
| 128 }, | 64 this.dropdownOpen = !this.dropdownOpen; |
| 65 if (this.dropdownOpen) { |
| 66 this.$.dropdown.style.display = 'block'; |
| 67 if (!this.maxHeightValid_) |
| 68 this.updateMaxHeight(); |
| 69 } |
| 70 this.cancelAnimation_(); |
| 71 this.playAnimation_(this.dropdownOpen); |
| 72 }, |
| 129 | 73 |
| 130 animateExit_: function() { | 74 updateMaxHeight: function() { |
| 131 return this.$.dropdown.animate([ | 75 var scrollContainer = this.$['scroll-container']; |
| 132 {transform: 'translateY(0)', opacity: 1}, | 76 var height = this.lowerBound - scrollContainer.getBoundingClientRect().top - |
| 133 {transform: 'translateY(-5px)', opacity: 0} | 77 DROPDOWN_INNER_PADDING; |
| 134 ], {duration: 100, easing: 'cubic-bezier(0.4, 0, 1, 1)'}); | 78 height = Math.max(height, MIN_DROPDOWN_HEIGHT); |
| 135 } | 79 scrollContainer.style.maxHeight = height + 'px'; |
| 136 }); | 80 this.maxHeightValid_ = true; |
| 81 }, |
| 82 |
| 83 cancelAnimation_: function() { |
| 84 if (this._animation) |
| 85 this._animation.cancel(); |
| 86 }, |
| 87 |
| 88 /** |
| 89 * Start an animation on the dropdown. |
| 90 * @param {boolean} isEntry True to play entry animation, false to play |
| 91 * exit. |
| 92 * @private |
| 93 */ |
| 94 playAnimation_: function(isEntry) { |
| 95 this.animation_ = isEntry ? this.animateEntry_() : this.animateExit_(); |
| 96 this.animation_.onfinish = function() { |
| 97 this.animation_ = null; |
| 98 if (!this.dropdownOpen) |
| 99 this.$.dropdown.style.display = 'none'; |
| 100 }.bind(this); |
| 101 }, |
| 102 |
| 103 animateEntry_: function() { |
| 104 var maxHeight = |
| 105 this.$.dropdown.getBoundingClientRect().height - DROPDOWN_OUTER_PADDING; |
| 106 |
| 107 if (maxHeight < 0) |
| 108 maxHeight = 0; |
| 109 |
| 110 var fade = new KeyframeEffect( |
| 111 this.$.dropdown, [{opacity: 0}, {opacity: 1}], |
| 112 {duration: 150, easing: 'cubic-bezier(0, 0, 0.2, 1)'}); |
| 113 var slide = new KeyframeEffect( |
| 114 this.$.dropdown, |
| 115 [ |
| 116 {height: '20px', transform: 'translateY(-10px)'}, |
| 117 {height: maxHeight + 'px', transform: 'translateY(0)'} |
| 118 ], |
| 119 {duration: 250, easing: 'cubic-bezier(0, 0, 0.2, 1)'}); |
| 120 |
| 121 return document.timeline.play(new GroupEffect([fade, slide])); |
| 122 }, |
| 123 |
| 124 animateExit_: function() { |
| 125 return this.$.dropdown.animate( |
| 126 [ |
| 127 {transform: 'translateY(0)', opacity: 1}, |
| 128 {transform: 'translateY(-5px)', opacity: 0} |
| 129 ], |
| 130 {duration: 100, easing: 'cubic-bezier(0.4, 0, 1, 1)'}); |
| 131 } |
| 132 }); |
| 137 | 133 |
| 138 })(); | 134 })(); |
| OLD | NEW |