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 |