Skip to content

Commit ae4bdca

Browse files
committed
fix(panel): allow transform to be animated on an offset panel
Previously, if a panel had a position with an offset (e.g. `$mdPanel.newPanelPosition().center()`), the positioning would break any `transform` animations on the panel. This was due to the fact that `mdPanel` uses inline `transform` styles to do the offsetting. These changes introduce a wrapper around the panel (`.md-panel-inner-wrapper`), which will handle all of the positioning, allowing for any animations to be applied to the `.md-panel` itself. Relates to angular#9641. Fixes angular#9905.
1 parent 8801ef8 commit ae4bdca

File tree

3 files changed

+154
-108
lines changed

3 files changed

+154
-108
lines changed

src/components/panel/panel.js

+62-48
Original file line numberDiff line numberDiff line change
@@ -1044,10 +1044,10 @@ MdPanelService.prototype._closeFirstOpenedPanel = function(groupName) {
10441044

10451045

10461046
/**
1047-
* Wraps the users template in two elements, md-panel-outer-wrapper, which
1048-
* covers the entire attachTo element, and md-panel, which contains only the
1049-
* template. This allows the panel control over positioning, animations,
1050-
* and similar properties.
1047+
* Wraps the user's template in three elements:
1048+
* - md-panel-outer-wrapper - covers the entire `attachTo` element.
1049+
* - md-panel-inner-wrapper - handles the positioning.
1050+
* - md-panel - contains the user's content and deals with the animations.
10511051
* @param {string} origTemplate The original template.
10521052
* @returns {string} The wrapped template.
10531053
* @private
@@ -1059,26 +1059,32 @@ MdPanelService.prototype._wrapTemplate = function(origTemplate) {
10591059
// height and width for positioning.
10601060
return '' +
10611061
'<div class="md-panel-outer-wrapper">' +
1062-
' <div class="md-panel" style="left: -9999px;">' + template + '</div>' +
1062+
'<div class="md-panel-inner-wrapper" style="left: -9999px;">' +
1063+
'<div class="md-panel">' + template + '</div>' +
1064+
'</div>' +
10631065
'</div>';
10641066
};
10651067

10661068

10671069
/**
1068-
* Wraps a content element in a md-panel-outer wrapper and
1069-
* positions it off-screen. Allows for proper control over positoning
1070-
* and animations.
1070+
* Wraps a content element in a `md-panel-outer-wrapper`, as well as
1071+
* a `md-panel-inner-wrapper`, and positions it off-screen. Allows for
1072+
* proper control over positoning and animations.
10711073
* @param {!angular.JQLite} contentElement Element to be wrapped.
10721074
* @return {!angular.JQLite} Wrapper element.
10731075
* @private
10741076
*/
10751077
MdPanelService.prototype._wrapContentElement = function(contentElement) {
1076-
var wrapper = angular.element('<div class="md-panel-outer-wrapper">');
1078+
var outerWrapper = angular.element(
1079+
'<div class="md-panel-outer-wrapper">' +
1080+
'<div class="md-panel-inner-wrapper" style="left: -9999px;"></div>' +
1081+
'</div>'
1082+
);
10771083

1078-
contentElement.addClass('md-panel').css('left', '-9999px');
1079-
wrapper.append(contentElement);
1084+
contentElement.addClass('md-panel');
1085+
outerWrapper.children().eq(0).append(contentElement);
10801086

1081-
return wrapper;
1087+
return outerWrapper;
10821088
};
10831089

10841090

@@ -1145,6 +1151,9 @@ function MdPanelRef(config, $injector) {
11451151
/** @type {!angular.JQLite|undefined} */
11461152
this.panelEl;
11471153

1154+
/** @type {!angular.JQLite|undefined} */
1155+
this.innerWrapper;
1156+
11481157
/**
11491158
* Whether the panel is attached. This is synchronous. When attach is called,
11501159
* isAttached is set to true. When detach is called, isAttached is set to
@@ -1587,6 +1596,11 @@ MdPanelRef.prototype._compile = function() {
15871596
);
15881597
}
15891598

1599+
// Save a reference to the inner wrapper.
1600+
self.innerWrapper = angular.element(
1601+
self.panelContainer[0].querySelector('.md-panel-inner-wrapper')
1602+
);
1603+
15901604
// Save a reference to the cleanup function from the compiler.
15911605
self._compilerCleanup = compileData.cleanup;
15921606

@@ -1662,14 +1676,14 @@ MdPanelRef.prototype._addStyles = function() {
16621676
var self = this;
16631677
return this._$q(function(resolve) {
16641678
self.panelContainer.css('z-index', self.config['zIndex']);
1665-
self.panelEl.css('z-index', self.config['zIndex'] + 1);
1679+
self.innerWrapper.css('z-index', self.config['zIndex'] + 1);
16661680

16671681
var hideAndResolve = function() {
16681682
// Theme the element and container.
16691683
self._setTheming();
16701684

16711685
// Remove left: -9999px and add hidden class.
1672-
self.panelEl.css('left', '');
1686+
self.innerWrapper.css('left', '');
16731687
self.panelContainer.addClass(MD_PANEL_HIDDEN);
16741688

16751689
resolve(self);
@@ -1736,26 +1750,26 @@ MdPanelRef.prototype._updatePosition = function(init) {
17361750
var positionConfig = this.config['position'];
17371751

17381752
if (positionConfig) {
1739-
positionConfig._setPanelPosition(this.panelEl);
1753+
positionConfig._setPanelPosition(this.innerWrapper);
17401754

17411755
// Hide the panel now that position is known.
17421756
if (init) {
17431757
this.panelContainer.addClass(MD_PANEL_HIDDEN);
17441758
}
17451759

1746-
this.panelEl.css(
1760+
this.innerWrapper.css(
17471761
MdPanelPosition.absPosition.TOP,
17481762
positionConfig.getTop()
17491763
);
1750-
this.panelEl.css(
1764+
this.innerWrapper.css(
17511765
MdPanelPosition.absPosition.BOTTOM,
17521766
positionConfig.getBottom()
17531767
);
1754-
this.panelEl.css(
1768+
this.innerWrapper.css(
17551769
MdPanelPosition.absPosition.LEFT,
17561770
positionConfig.getLeft()
17571771
);
1758-
this.panelEl.css(
1772+
this.innerWrapper.css(
17591773
MdPanelPosition.absPosition.RIGHT,
17601774
positionConfig.getRight()
17611775
);
@@ -2662,38 +2676,38 @@ MdPanelPosition.prototype.getTransform = function() {
26622676

26632677

26642678
/**
2665-
* Sets the `transform` value for a panel element.
2666-
* @param {!angular.JQLite} panelEl
2679+
* Sets the `transform` value for an element.
2680+
* @param {!angular.JQLite} el
26672681
* @returns {!angular.JQLite}
26682682
* @private
26692683
*/
2670-
MdPanelPosition.prototype._setTransform = function(panelEl) {
2671-
return panelEl.css(this._$mdConstant.CSS.TRANSFORM, this.getTransform());
2684+
MdPanelPosition.prototype._setTransform = function(el) {
2685+
return el.css(this._$mdConstant.CSS.TRANSFORM, this.getTransform());
26722686
};
26732687

26742688

26752689
/**
26762690
* True if the panel is completely on-screen with this positioning; false
26772691
* otherwise.
2678-
* @param {!angular.JQLite} panelEl
2692+
* @param {!angular.JQLite} el
26792693
* @return {boolean}
26802694
* @private
26812695
*/
2682-
MdPanelPosition.prototype._isOnscreen = function(panelEl) {
2696+
MdPanelPosition.prototype._isOnscreen = function(el) {
26832697
// this works because we always use fixed positioning for the panel,
26842698
// which is relative to the viewport.
26852699
var left = parseInt(this.getLeft());
26862700
var top = parseInt(this.getTop());
26872701

26882702
if (this._translateX.length || this._translateY.length) {
26892703
var prefixedTransform = this._$mdConstant.CSS.TRANSFORM;
2690-
var offsets = getComputedTranslations(panelEl, prefixedTransform);
2704+
var offsets = getComputedTranslations(el, prefixedTransform);
26912705
left += offsets.x;
26922706
top += offsets.y;
26932707
}
26942708

2695-
var right = left + panelEl[0].offsetWidth;
2696-
var bottom = top + panelEl[0].offsetHeight;
2709+
var right = left + el[0].offsetWidth;
2710+
var bottom = top + el[0].offsetHeight;
26972711

26982712
return (left >= 0) &&
26992713
(top >= 0) &&
@@ -2733,53 +2747,53 @@ MdPanelPosition.prototype._reduceTranslateValues =
27332747
/**
27342748
* Sets the panel position based on the created panel element and best x/y
27352749
* positioning.
2736-
* @param {!angular.JQLite} panelEl
2750+
* @param {!angular.JQLite} el
27372751
* @private
27382752
*/
2739-
MdPanelPosition.prototype._setPanelPosition = function(panelEl) {
2740-
// Remove the "position adjusted" class in case it has been added before.
2741-
panelEl.removeClass('_md-panel-position-adjusted');
2753+
MdPanelPosition.prototype._setPanelPosition = function(el) {
2754+
// Remove the class in case it has been added before.
2755+
el.removeClass('_md-panel-position-adjusted');
27422756

27432757
// Only calculate the position if necessary.
27442758
if (this._absolute) {
2745-
this._setTransform(panelEl);
2759+
this._setTransform(el);
27462760
return;
27472761
}
27482762

27492763
if (this._actualPosition) {
2750-
this._calculatePanelPosition(panelEl, this._actualPosition);
2751-
this._setTransform(panelEl);
2752-
this._constrainToViewport(panelEl);
2764+
this._calculatePanelPosition(el, this._actualPosition);
2765+
this._setTransform(el);
2766+
this._constrainToViewport(el);
27532767
return;
27542768
}
27552769

27562770
for (var i = 0; i < this._positions.length; i++) {
27572771
this._actualPosition = this._positions[i];
2758-
this._calculatePanelPosition(panelEl, this._actualPosition);
2759-
this._setTransform(panelEl);
2772+
this._calculatePanelPosition(el, this._actualPosition);
2773+
this._setTransform(el);
27602774

2761-
if (this._isOnscreen(panelEl)) {
2775+
if (this._isOnscreen(el)) {
27622776
return;
27632777
}
27642778
}
27652779

2766-
this._constrainToViewport(panelEl);
2780+
this._constrainToViewport(el);
27672781
};
27682782

27692783

27702784
/**
27712785
* Constrains a panel's position to the viewport.
2772-
* @param {!angular.JQLite} panelEl
2786+
* @param {!angular.JQLite} el
27732787
* @private
27742788
*/
2775-
MdPanelPosition.prototype._constrainToViewport = function(panelEl) {
2789+
MdPanelPosition.prototype._constrainToViewport = function(el) {
27762790
var margin = MdPanelPosition.viewportMargin;
27772791
var initialTop = this._top;
27782792
var initialLeft = this._left;
27792793

27802794
if (this.getTop()) {
27812795
var top = parseInt(this.getTop());
2782-
var bottom = panelEl[0].offsetHeight + top;
2796+
var bottom = el[0].offsetHeight + top;
27832797
var viewportHeight = this._$window.innerHeight;
27842798

27852799
if (top < margin) {
@@ -2791,7 +2805,7 @@ MdPanelPosition.prototype._constrainToViewport = function(panelEl) {
27912805

27922806
if (this.getLeft()) {
27932807
var left = parseInt(this.getLeft());
2794-
var right = panelEl[0].offsetWidth + left;
2808+
var right = el[0].offsetWidth + left;
27952809
var viewportWidth = this._$window.innerWidth;
27962810

27972811
if (left < margin) {
@@ -2802,7 +2816,7 @@ MdPanelPosition.prototype._constrainToViewport = function(panelEl) {
28022816
}
28032817

28042818
// Class that can be used to re-style the panel if it was repositioned.
2805-
panelEl.toggleClass(
2819+
el.toggleClass(
28062820
'_md-panel-position-adjusted',
28072821
this._top !== initialTop || this._left !== initialLeft
28082822
);
@@ -2841,13 +2855,13 @@ MdPanelPosition.prototype._bidi = function(position) {
28412855
/**
28422856
* Calculates the panel position based on the created panel element and the
28432857
* provided positioning.
2844-
* @param {!angular.JQLite} panelEl
2858+
* @param {!angular.JQLite} el
28452859
* @param {!{x:string, y:string}} position
28462860
* @private
28472861
*/
2848-
MdPanelPosition.prototype._calculatePanelPosition = function(panelEl, position) {
2862+
MdPanelPosition.prototype._calculatePanelPosition = function(el, position) {
28492863

2850-
var panelBounds = panelEl[0].getBoundingClientRect();
2864+
var panelBounds = el[0].getBoundingClientRect();
28512865
var panelWidth = panelBounds.width;
28522866
var panelHeight = panelBounds.height;
28532867

src/components/panel/panel.scss

+15-11
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,12 @@
66
width: 100%;
77
}
88

9-
._md-panel-hidden {
10-
display: none;
9+
.md-panel-inner-wrapper {
10+
position: fixed;
1111
}
1212

13-
._md-panel-fullscreen {
14-
border-radius: 0;
15-
left: 0;
16-
min-height: 100%;
17-
min-width: 100%;
18-
position: fixed;
19-
top: 0;
13+
._md-panel-hidden {
14+
display: none;
2015
}
2116

2217
// Only used when no animations are present.
@@ -27,7 +22,7 @@
2722

2823
.md-panel {
2924
opacity: 0;
30-
position: fixed;
25+
position: relative;
3126

3227
&._md-panel-shown {
3328
// Only used when custom animations are present.
@@ -53,7 +48,7 @@
5348

5449
&._md-panel-backdrop {
5550
height: 100%;
56-
position: absolute;
51+
position: fixed;
5752
width: 100%;
5853
}
5954

@@ -66,3 +61,12 @@
6661
transition: opacity $material-leave-duration $material-leave-timing-function;
6762
}
6863
}
64+
65+
._md-panel-fullscreen {
66+
border-radius: 0;
67+
left: 0;
68+
min-height: 100%;
69+
min-width: 100%;
70+
position: fixed;
71+
top: 0;
72+
}

0 commit comments

Comments
 (0)