OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 cr.define('settings', function() { |
| 6 'use strict'; |
| 7 |
| 8 /** |
| 9 * Expands a card to fill its container's client height, so it covers the |
| 10 * container from the top padding edge to the bottom padding edge. |
| 11 * @see https://www.w3.org/TR/cssom-view-1/#terminology |
| 12 * @constructor |
| 13 * @implements {settings.animation.AnimationGroup} |
| 14 * @param {!HTMLElement} card Card to expand. |
| 15 * @param {!HTMLElement} container Container whose height the card will span. |
| 16 */ |
| 17 function ExpandCardTransition(card, container) { |
| 18 /** @private */ |
| 19 this.card_ = card; |
| 20 /** @private */ |
| 21 this.container_ = container; |
| 22 } |
| 23 |
| 24 ExpandCardTransition.prototype = { |
| 25 __proto__: settings.animation.AnimationGroup.prototype, |
| 26 |
| 27 /** @override */ |
| 28 play: function() { |
| 29 // If we're running this transition before anything else, we won't have |
| 30 // the information we need, so just expand immediately and finish. |
| 31 if (typeof this.card_.origHeight_ == 'undefined' || |
| 32 this.card_.style.top == '' || this.card_.style.height == '') { |
| 33 this.finished = Promise.resolve(true); |
| 34 return this.finished; |
| 35 } |
| 36 |
| 37 // Align the card with the container's padding edge. |
| 38 var startingTop = this.card_.getBoundingClientRect().top; |
| 39 |
| 40 // Target position is the container's top edge in the viewport. |
| 41 var targetTop = this.container_.getBoundingClientRect().top; |
| 42 |
| 43 // The target height shouldn't use the container's current height, because |
| 44 // the container may resize when the window resizes. And height: 100% |
| 45 // wouldn't use the container's height because we're position: fixed. |
| 46 // |
| 47 // Instead, find the part of the window height *not* used by the container |
| 48 // (e.g., for a toolbar) and calc the height dynamically. This assumes the |
| 49 // excluded height stays constant. |
| 50 var excludedHeight = window.innerHeight - this.container_.clientHeight; |
| 51 var targetHeight = 'calc(100% - ' + excludedHeight + 'px)'; |
| 52 |
| 53 // Expand the card. The card's height must be 100% of the container's |
| 54 // height or taller, so we use minHeight rather than height. |
| 55 var keyframes = [{ |
| 56 top: startingTop + 'px', |
| 57 minHeight: this.card_.style.height, |
| 58 easing: settings.animation.Timing.EASING, |
| 59 }, { |
| 60 top: targetTop + 'px', |
| 61 minHeight: 'calc(100% - ' + targetTop + 'px)', |
| 62 }]; |
| 63 var options = /** @type {!KeyframeEffectOptions} */({ |
| 64 duration: settings.animation.Timing.DURATION, |
| 65 }); |
| 66 |
| 67 /** @type {?settings.animation.Animation} */ |
| 68 this.animation_ = |
| 69 new settings.animation.Animation(this.card_, keyframes, options); |
| 70 this.finished = this.animation_.finished.then(function() { |
| 71 return true; |
| 72 }, function() { |
| 73 return false; |
| 74 }); |
| 75 return this.finished; |
| 76 }, |
| 77 |
| 78 /** @override */ |
| 79 finish: function() { |
| 80 if (!this.animation_) |
| 81 return; |
| 82 this.animation_.finish(); |
| 83 this.animation_ = null; |
| 84 }, |
| 85 |
| 86 /** @override */ |
| 87 cancel: function() { |
| 88 if (!this.animation_) |
| 89 return; |
| 90 this.animation_.cancel(); |
| 91 this.animation_ = null; |
| 92 }, |
| 93 |
| 94 /** @override */ |
| 95 finished: null, |
| 96 }; |
| 97 |
| 98 /** |
| 99 * Collapses an expanded card back into its original section. |
| 100 * @constructor |
| 101 * @implements {settings.animation.AnimationGroup} |
| 102 * @param {!HTMLElement} card Card to collapse. |
| 103 * @param {!HTMLElement} header Header for the section. |
| 104 * @param {!HTMLElement} container Container the card currently spans. |
| 105 */ |
| 106 function CollapseCardTransition(card, header, container) { |
| 107 /** @private */ |
| 108 this.card_ = card; |
| 109 /** @private */ |
| 110 this.header_ = header; |
| 111 /** @private */ |
| 112 this.container_ = container; |
| 113 } |
| 114 |
| 115 CollapseCardTransition.prototype = { |
| 116 __proto__: settings.animation.AnimationGroup.prototype, |
| 117 |
| 118 /** |
| 119 * Prepare for the transition before other page content has been unhidden. |
| 120 * Call before play(). |
| 121 */ |
| 122 setUp: function() { |
| 123 this.card_.style.width = this.card_.clientWidth + 'px'; |
| 124 this.card_.style.height = this.card_.clientHeight + 'px'; |
| 125 }, |
| 126 |
| 127 /** @override */ |
| 128 play: function() { |
| 129 var startingTop = this.container_.getBoundingClientRect().top; |
| 130 |
| 131 // The card is unpositioned, so use its position as the ending state, |
| 132 // but account for scroll. |
| 133 var targetTop = this.card_.getBoundingClientRect().top - |
| 134 this.container_.scrollTop; |
| 135 |
| 136 // Account for the section header. |
| 137 var headerStyle = getComputedStyle(this.header_); |
| 138 targetTop += this.header_.offsetHeight + |
| 139 parseFloat(headerStyle.marginBottom) + |
| 140 parseFloat(headerStyle.marginTop); |
| 141 |
| 142 var keyframes = [{ |
| 143 top: startingTop + 'px', |
| 144 minHeight: this.card_.style.height, |
| 145 easing: settings.animation.Timing.EASING, |
| 146 }, { |
| 147 top: targetTop + 'px', |
| 148 minHeight: |
| 149 /** @type {{origHeight_: number}} */(this.card_).origHeight_ + 'px', |
| 150 }]; |
| 151 var options = /** @type {!KeyframeEffectOptions} */({ |
| 152 duration: settings.animation.Timing.DURATION, |
| 153 }); |
| 154 |
| 155 this.card_.style.height = ''; |
| 156 |
| 157 /** @type {?settings.animation.Animation} */ |
| 158 this.animation_ = |
| 159 new settings.animation.Animation(this.card_, keyframes, options); |
| 160 |
| 161 // Clean up after the animation finishes or cancels. |
| 162 this.finished = this.animation_.finished.then(function() { |
| 163 this.card_.style.width = ''; |
| 164 return true; |
| 165 }.bind(this), function() { |
| 166 this.card_.style.width = ''; |
| 167 return false; |
| 168 }.bind(this)); |
| 169 return this.finished; |
| 170 }, |
| 171 |
| 172 /** @override */ |
| 173 finish: function() { |
| 174 if (!this.animation_) |
| 175 return; |
| 176 this.animation_.finish(); |
| 177 this.animation_ = null; |
| 178 }, |
| 179 |
| 180 /** @override */ |
| 181 cancel: function() { |
| 182 if (!this.animation_) |
| 183 return; |
| 184 this.animation_.cancel(); |
| 185 this.animation_ = null; |
| 186 }, |
| 187 |
| 188 /** @override */ |
| 189 finished: null, |
| 190 }; |
| 191 |
| 192 return { |
| 193 CollapseCardTransition: CollapseCardTransition, |
| 194 ExpandCardTransition: ExpandCardTransition, |
| 195 }; |
| 196 }); |
OLD | NEW |