| Index: chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
|
| diff --git a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
|
| index c3af59def0a63d4c78f4df1776562746284d1618..bcf1b5da57765b497e2c992534f60320c6d37339 100644
|
| --- a/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
|
| +++ b/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.js
|
| @@ -294,6 +294,17 @@ Polymer({
|
| },
|
|
|
| /**
|
| + * Whether the search input should be padded as if it were at the bottom of
|
| + * the dialog.
|
| + * @type {boolean}
|
| + */
|
| + searchUseBottomPadding: {
|
| + type: Boolean,
|
| + reflectToAttribute: true,
|
| + value: true,
|
| + },
|
| +
|
| + /**
|
| * Whether to show the user domain of sinks associated with identity.
|
| * @type {boolean|undefined}
|
| */
|
| @@ -748,6 +759,18 @@ Polymer({
|
| },
|
|
|
| /**
|
| + * @param {?Element} element Element to compute padding for.
|
| + * @return {number} Computes the amount of vertical padding (top + bottom) on
|
| + * |element|.
|
| + * @private
|
| + */
|
| + computeElementVerticalPadding_: function(element) {
|
| + var paddingBottom, paddingTop;
|
| + [paddingBottom, paddingTop] = this.getElementVerticalPadding_(element);
|
| + return paddingBottom + paddingTop;
|
| + },
|
| +
|
| + /**
|
| * Computes an array of substring indices that mark where substrings of
|
| * |searchString| occur in |sinkName|.
|
| *
|
| @@ -983,18 +1006,21 @@ Polymer({
|
| * Computes the height of the sink list view element when search results are
|
| * being shown.
|
| *
|
| + * @param {?Element} deviceMissing No devices message element.
|
| * @param {?Element} noMatches No search matches element.
|
| * @param {?Element} results Search results list element.
|
| - * @param {?Element} search Search input container element.
|
| + * @param {number} searchOffsetHeight Search input container element height.
|
| * @param {number} maxHeight Max height of the list elements.
|
| * @return {number} The height of the sink list view when search results are
|
| * being shown.
|
| * @private
|
| */
|
| - computeTotalSearchHeight_: function(noMatches, results, search, maxHeight) {
|
| - var contentHeight = (noMatches.hasAttribute('hidden')) ?
|
| - results.offsetHeight : noMatches.offsetHeight;
|
| - return Math.min(contentHeight, maxHeight) + search.offsetHeight;
|
| + computeTotalSearchHeight_: function(
|
| + deviceMissing, noMatches, results, searchOffsetHeight, maxHeight) {
|
| + var contentHeight = deviceMissing.offsetHeight +
|
| + ((noMatches.hasAttribute('hidden')) ?
|
| + results.offsetHeight : noMatches.offsetHeight);
|
| + return Math.min(contentHeight, maxHeight) + searchOffsetHeight;
|
| },
|
|
|
| /**
|
| @@ -1083,6 +1109,18 @@ Polymer({
|
| },
|
|
|
| /**
|
| + * @param {?Element} element Element to compute padding for.
|
| + * @return {!Array<number>} Array containing the element's bottom padding
|
| + * value and the element's top padding value, in that order.
|
| + * @private
|
| + */
|
| + getElementVerticalPadding_: function(element) {
|
| + var style = window.getComputedStyle(element);
|
| + return [parseInt(style.getPropertyValue('padding-bottom'), 10) || 0,
|
| + parseInt(style.getPropertyValue('padding-top'), 10) || 0];
|
| + },
|
| +
|
| + /**
|
| * Retrieves the first run flow cloud preferences text, if it exists. On
|
| * non-officially branded builds, the string is not defined.
|
| *
|
| @@ -1255,11 +1293,24 @@ Polymer({
|
| var view = this.$['sink-list-view'];
|
|
|
| var hasList = this.hasConditionalElement_(list);
|
| - // Same reason for omission of |search.offsetHeight| as above.
|
| - var initialHeight = resultsContainer.offsetHeight;
|
| - // Force the view height to be max height.
|
| + var initialHeight = view.offsetHeight;
|
| + // Force the view height to be max dialog height.
|
| view.style['overflow'] = 'hidden';
|
|
|
| + var searchInitialOffsetHeight = search.offsetHeight;
|
| + var searchInitialPaddingBottom, searchInitialPaddingTop;
|
| + [searchInitialPaddingBottom, searchInitialPaddingTop] =
|
| + this.getElementVerticalPadding_(search);
|
| + var searchPadding = searchInitialPaddingBottom + searchInitialPaddingTop;
|
| + var searchHeight = search.offsetHeight - searchPadding;
|
| + this.searchUseBottomPadding = true;
|
| + var searchFinalPaddingBottom, searchFinalPaddingTop;
|
| + [searchFinalPaddingBottom, searchFinalPaddingTop] =
|
| + this.getElementVerticalPadding_(search);
|
| + var searchFinalOffsetHeight =
|
| + searchHeight + searchFinalPaddingBottom + searchFinalPaddingTop;
|
| +
|
| + var resultsInitialTop = 0;
|
| var finalHeight = 0;
|
| // Get final view height ahead of animation.
|
| if (hasList) {
|
| @@ -1268,6 +1319,10 @@ Polymer({
|
| this.hideSinkListForAnimation_ = false;
|
| finalHeight += list.offsetHeight;
|
| list.style['position'] = 'relative';
|
| + } else {
|
| + resultsInitialTop +=
|
| + deviceMissing.offsetHeight + searchInitialOffsetHeight;
|
| + finalHeight += deviceMissing.offsetHeight;
|
| }
|
|
|
| var searchInitialTop = hasList ? 0 : deviceMissing.offsetHeight;
|
| @@ -1278,27 +1333,39 @@ Polymer({
|
|
|
| var duration =
|
| this.computeAnimationDuration_(searchFinalTop - searchInitialTop);
|
| + var timing = {duration: duration, easing: 'ease-in-out', fill: 'forwards'};
|
|
|
| // This GroupEffect does the reverse of |moveSearchToTop_|. It fades the
|
| // sink list in while sliding the search input and search results list down.
|
| // The dialog height is also adjusted smoothly to the sink list height.
|
| + var deviceMissingEffect = new KeyframeEffect(deviceMissing,
|
| + [{'marginBottom': searchInitialOffsetHeight},
|
| + {'marginBottom': searchFinalOffsetHeight}],
|
| + timing);
|
| var listEffect = new KeyframeEffect(list,
|
| [{'opacity': '0'}, {'opacity': '1'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + timing);
|
| var resultsEffect = new KeyframeEffect(resultsContainer,
|
| - [{'top': '0px', 'paddingTop': resultsContainer.style['padding-top']},
|
| + [{'top': resultsInitialTop + 'px',
|
| + 'paddingTop': resultsContainer.style['padding-top']},
|
| {'top': '100%', 'paddingTop': '0px'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + timing);
|
| var searchEffect = new KeyframeEffect(search,
|
| - [{'top': searchInitialTop + 'px', 'marginTop': '0px'},
|
| - {'top': '100%', 'marginTop': '-' + (search.offsetHeight + 16) + 'px'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + [{'top': searchInitialTop + 'px', 'marginTop': '0px',
|
| + 'paddingBottom': searchInitialPaddingBottom + 'px',
|
| + 'paddingTop': searchInitialPaddingTop + 'px'},
|
| + {'top': '100%', 'marginTop': '-' + searchFinalOffsetHeight + 'px',
|
| + 'paddingBottom': searchFinalPaddingBottom + 'px',
|
| + 'paddingTop': searchFinalPaddingTop + 'px'}],
|
| + timing);
|
| var viewEffect = new KeyframeEffect(view,
|
| - [{'height': initialHeight + 'px'}, {'height': finalHeight + 'px'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + [{'height': initialHeight + 'px', 'paddingBottom': '0px'},
|
| + {'height': finalHeight + 'px',
|
| + 'paddingBottom': searchFinalOffsetHeight + 'px'}],
|
| + timing);
|
| var player = document.timeline.play(new GroupEffect(hasList ?
|
| [listEffect, resultsEffect, searchEffect, viewEffect] :
|
| - [resultsEffect, searchEffect, viewEffect]));
|
| + [deviceMissingEffect, resultsEffect, searchEffect, viewEffect]));
|
|
|
| var that = this;
|
| var finalizeAnimation = function() {
|
| @@ -1337,48 +1404,71 @@ Polymer({
|
|
|
| // Saves current search container |offsetHeight| which includes bottom
|
| // padding.
|
| - var searchOffsetHeight = search.offsetHeight;
|
| + var searchInitialOffsetHeight = search.offsetHeight;
|
| var hasList = this.hasConditionalElement_(list);
|
| - var searchInitialTop = hasList ? list.offsetHeight - searchOffsetHeight :
|
| - deviceMissing.offsetHeight;
|
| + var searchInitialTop = hasList ?
|
| + list.offsetHeight - searchInitialOffsetHeight :
|
| + deviceMissing.offsetHeight;
|
| var searchFinalTop = hasList ? 0 : deviceMissing.offsetHeight;
|
| - resultsContainer.style['max-height'] = this.sinkListMaxHeight_ + 'px';
|
| -
|
| - // Omitting |search.offsetHeight| because |list| is padded with
|
| - // |search.offsetHeight| and |offsetHeight| includes padding.
|
| - var initialHeight = hasList ?
|
| - list.offsetHeight :
|
| - deviceMissing.offsetHeight + searchOffsetHeight;
|
| + var searchInitialPaddingBottom, searchInitialPaddingTop;
|
| + [searchInitialPaddingBottom, searchInitialPaddingTop] =
|
| + this.getElementVerticalPadding_(search);
|
| + var searchPadding = searchInitialPaddingBottom + searchInitialPaddingTop;
|
| + var searchHeight = search.offsetHeight - searchPadding;
|
| + this.searchUseBottomPadding =
|
| + this.shouldSearchUseBottomPadding_(deviceMissing);
|
| + var searchFinalPaddingBottom, searchFinalPaddingTop;
|
| + [searchFinalPaddingBottom, searchFinalPaddingTop] =
|
| + this.getElementVerticalPadding_(search);
|
| + var searchFinalOffsetHeight =
|
| + searchHeight + searchFinalPaddingBottom + searchFinalPaddingTop;
|
| +
|
| + // Omitting |search.offsetHeight| because it is handled by view animation
|
| + // separately.
|
| + var initialHeight =
|
| + hasList ? list.offsetHeight : deviceMissing.offsetHeight;
|
| view.style['overflow'] = 'hidden';
|
| - search.className = '';
|
|
|
| - var finalHeight = this.computeTotalSearchHeight_(noMatches, results, search,
|
| - this.sinkListMaxHeight_);
|
| + var resultsPadding = this.computeElementVerticalPadding_(results);
|
| + var finalHeight = this.computeTotalSearchHeight_(
|
| + deviceMissing, noMatches, results, searchFinalOffsetHeight,
|
| + this.sinkListMaxHeight_ + resultsPadding);
|
|
|
| var duration =
|
| this.computeAnimationDuration_(searchFinalTop - searchInitialTop);
|
| + var timing = {duration: duration, easing: 'ease-in-out', fill: 'forwards'};
|
|
|
| // This GroupEffect will cause the sink list to fade out while the search
|
| // input and search results list slide up. The dialog will also resize
|
| // smoothly to the new search result list height.
|
| + var deviceMissingEffect = new KeyframeEffect(deviceMissing,
|
| + [{'marginBottom': searchInitialOffsetHeight},
|
| + {'marginBottom': searchFinalOffsetHeight}],
|
| + timing);
|
| var listEffect = new KeyframeEffect(list,
|
| [{'opacity': '1'}, {'opacity': '0'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + timing);
|
| var resultsEffect = new KeyframeEffect(resultsContainer,
|
| [{'top': '100%', 'paddingTop': '0px'},
|
| {'top': searchFinalTop + 'px',
|
| - 'paddingTop': search.offsetHeight + 'px'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + 'paddingTop': searchFinalOffsetHeight + 'px'}],
|
| + timing);
|
| var searchEffect = new KeyframeEffect(search,
|
| - [{'top': '100%', 'marginTop': '-' + searchOffsetHeight + 'px'},
|
| - {'top': searchFinalTop + 'px', 'marginTop': '0px'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + [{'top': '100%', 'marginTop': '-' + searchInitialOffsetHeight + 'px',
|
| + 'paddingBottom': searchInitialPaddingBottom + 'px',
|
| + 'paddingTop': searchInitialPaddingTop + 'px'},
|
| + {'top': searchFinalTop + 'px', 'marginTop': '0px',
|
| + 'paddingBottom': searchFinalPaddingBottom + 'px',
|
| + 'paddingTop': searchFinalPaddingTop + 'px'}],
|
| + timing);
|
| var viewEffect = new KeyframeEffect(view,
|
| - [{'height': initialHeight + 'px'}, {'height': finalHeight + 'px'}],
|
| - {duration: duration, easing: 'ease-in-out', fill: 'forwards'});
|
| + [{'height': initialHeight + 'px',
|
| + 'paddingBottom': searchInitialOffsetHeight + 'px'},
|
| + {'height': finalHeight + 'px', 'paddingBottom': '0px'}],
|
| + timing);
|
| var player = document.timeline.play(new GroupEffect(hasList ?
|
| [listEffect, resultsEffect, searchEffect, viewEffect] :
|
| - [resultsEffect, searchEffect, viewEffect]));
|
| + [deviceMissingEffect, resultsEffect, searchEffect, viewEffect]));
|
|
|
| var that = this;
|
| var finalizeAnimation = function() {
|
| @@ -1620,7 +1710,8 @@ Polymer({
|
| var list = this.$$('#sink-list');
|
| var resultsContainer = this.$$('#search-results-container');
|
| var search = this.$['sink-search'];
|
| - search.className = 'bottom';
|
| + var view = this.$['sink-list-view'];
|
| + this.searchUseBottomPadding = true;
|
| search.style['top'] = '';
|
| if (resultsContainer) {
|
| resultsContainer.style['position'] = '';
|
| @@ -1631,7 +1722,7 @@ Polymer({
|
| var hasList = this.hasConditionalElement_(list);
|
| if (hasList) {
|
| search.style['margin-top'] = '-' + search.offsetHeight + 'px';
|
| - list.style['padding-bottom'] = search.offsetHeight + 'px';
|
| + view.style['padding-bottom'] = search.offsetHeight + 'px';
|
| list.style['opacity'] = '';
|
| } else {
|
| deviceMissing.style['margin-bottom'] = search.offsetHeight + 'px';
|
| @@ -1665,8 +1756,9 @@ Polymer({
|
| // If there is a height mismatch between where the animation calculated the
|
| // height should be and where it is now because the search results changed
|
| // during the animation, correct it with... another animation.
|
| - var finalHeight = this.computeTotalSearchHeight_(noMatches, results, search,
|
| - this.sinkListMaxHeight_);
|
| + var resultsPadding = this.computeElementVerticalPadding_(results);
|
| + var finalHeight = this.computeTotalSearchHeight_(deviceMissing, noMatches,
|
| + results, search.offsetHeight, this.sinkListMaxHeight_ + resultsPadding);
|
| if (finalHeight != view.offsetHeight) {
|
| var viewEffect = new KeyframeEffect(view,
|
| [{'height': view.offsetHeight + 'px'},
|
| @@ -1674,7 +1766,6 @@ Polymer({
|
| {duration:
|
| this.computeAnimationDuration_(finalHeight - view.offsetHeight),
|
| easing: 'ease-in-out', fill: 'forwards'});
|
| - var that = this;
|
| var player = document.timeline.play(viewEffect);
|
| if (this.heightAdjustmentPlayer_) {
|
| this.heightAdjustmentPlayer_.cancel();
|
| @@ -1686,6 +1777,7 @@ Polymer({
|
|
|
| var hasList = this.hasConditionalElement_(list);
|
| search.style['margin-top'] = '';
|
| + deviceMissing.style['margin-bottom'] = search.offsetHeight + 'px';
|
| var searchFinalTop = hasList ? 0 : deviceMissing.offsetHeight;
|
| var resultsPaddingTop = hasList ? search.offsetHeight + 'px' : '0px';
|
| search.style['top'] = searchFinalTop + 'px';
|
| @@ -1696,6 +1788,7 @@ Polymer({
|
| resultsContainer.style['overflow-y'] = 'auto';
|
|
|
| view.style['overflow'] = '';
|
| + view.style['padding-bottom'] = '';
|
| if (this.filterTransitionPlayer_) {
|
| this.filterTransitionPlayer_.cancel();
|
| this.filterTransitionPlayer_ = null;
|
| @@ -1966,6 +2059,16 @@ Polymer({
|
| },
|
|
|
| /**
|
| + * @param {?Element} deviceMissing Device missing message element.
|
| + * @return {boolean} Whether the search input should use vertical padding as
|
| + * if it were the lowest (at the very bottom) item in the dialog.
|
| + * @private
|
| + */
|
| + shouldSearchUseBottomPadding_: function(deviceMissing) {
|
| + return !deviceMissing.hasAttribute('hidden');
|
| + },
|
| +
|
| + /**
|
| * Shows the cast mode list.
|
| *
|
| * @private
|
| @@ -2056,8 +2159,11 @@ Polymer({
|
| showSinkList_: function() {
|
| if (this.currentView_ == media_router.MediaRouterView.FILTER) {
|
| this.queueMoveSearchToBottom_();
|
| + this.currentView_ = media_router.MediaRouterView.SINK_LIST;
|
| + } else {
|
| + this.currentView_ = media_router.MediaRouterView.SINK_LIST;
|
| + this.putSearchAtBottom_();
|
| }
|
| - this.currentView_ = media_router.MediaRouterView.SINK_LIST;
|
| },
|
|
|
| /**
|
| @@ -2104,20 +2210,27 @@ Polymer({
|
| var issueHeight = this.$$('#issue-banner') &&
|
| this.$$('#issue-banner').style.display != 'none' ?
|
| this.$$('#issue-banner').offsetHeight : 0;
|
| - var searchHeight = this.$$('#sink-search').offsetHeight;
|
| + var search = this.$['sink-search'];
|
| + var searchHeight = search.offsetHeight;
|
|
|
| this.$['container-header'].style.marginTop = firstRunFlowHeight + 'px';
|
| this.$['content'].style.marginTop =
|
| firstRunFlowHeight + headerHeight + 'px';
|
|
|
| - this.sinkListMaxHeight_ = this.dialogHeight_ - headerHeight -
|
| - firstRunFlowHeight - issueHeight - searchHeight;
|
| var sinkList = this.$$('#sink-list');
|
| + var sinkListPadding =
|
| + sinkList ? this.computeElementVerticalPadding_(sinkList) : 0;
|
| + var searchPadding = this.computeElementVerticalPadding_(search);
|
| +
|
| + var sinkListMaxHeight = this.dialogHeight_ - headerHeight -
|
| + firstRunFlowHeight - issueHeight - searchHeight + searchPadding -
|
| + sinkListPadding;
|
| + this.sinkListMaxHeight_ = sinkListMaxHeight;
|
| if (sinkList) {
|
| sinkList.style.maxHeight = this.sinkListMaxHeight_ + 'px';
|
| var searchResults = this.$$('#search-results');
|
| if (searchResults)
|
| - searchResults.style.maxHeight = sinkList.style.maxHeight;
|
| + searchResults.style.maxHeight = this.sinkListMaxHeight_ + 'px';
|
| }
|
| });
|
| },
|
|
|