Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit c3a30fb

Browse files
crisbetoSplaktar
authored andcommitted
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 #9641. Fixes #9905.
1 parent 98e259b commit c3a30fb

File tree

3 files changed

+157
-108
lines changed

3 files changed

+157
-108
lines changed

src/components/panel/panel.js

+65-48
Original file line numberDiff line numberDiff line change
@@ -1306,10 +1306,10 @@ MdPanelService.prototype._closeFirstOpenedPanel = function(groupName) {
13061306

13071307

13081308
/**
1309-
* Wraps the users template in two elements, md-panel-outer-wrapper, which
1310-
* covers the entire attachTo element, and md-panel, which contains only the
1311-
* template. This allows the panel control over positioning, animations,
1312-
* and similar properties.
1309+
* Wraps the user's template in three elements:
1310+
* - md-panel-outer-wrapper - covers the entire `attachTo` element.
1311+
* - md-panel-inner-wrapper - handles the positioning.
1312+
* - md-panel - contains the user's content and deals with the animations.
13131313
* @param {string} origTemplate The original template.
13141314
* @returns {string} The wrapped template.
13151315
* @private
@@ -1321,26 +1321,32 @@ MdPanelService.prototype._wrapTemplate = function(origTemplate) {
13211321
// height and width for positioning.
13221322
return '' +
13231323
'<div class="md-panel-outer-wrapper">' +
1324-
' <div class="md-panel _md-panel-offscreen">' + template + '</div>' +
1324+
'<div class="md-panel-inner-wrapper" style="left: -9999px;">' +
1325+
'<div class="md-panel _md-panel-offscreen">' + template + '</div>' +
1326+
'</div>' +
13251327
'</div>';
13261328
};
13271329

13281330

13291331
/**
1330-
* Wraps a content element in a md-panel-outer wrapper and
1331-
* positions it off-screen. Allows for proper control over positoning
1332-
* and animations.
1332+
* Wraps a content element in a `md-panel-outer-wrapper`, as well as
1333+
* a `md-panel-inner-wrapper`, and positions it off-screen. Allows for
1334+
* proper control over positoning and animations.
13331335
* @param {!angular.JQLite} contentElement Element to be wrapped.
13341336
* @return {!angular.JQLite} Wrapper element.
13351337
* @private
13361338
*/
13371339
MdPanelService.prototype._wrapContentElement = function(contentElement) {
1338-
var wrapper = angular.element('<div class="md-panel-outer-wrapper">');
1340+
var outerWrapper = angular.element(
1341+
'<div class="md-panel-outer-wrapper">' +
1342+
'<div class="md-panel-inner-wrapper" style="left: -9999px;"></div>' +
1343+
'</div>'
1344+
);
13391345

13401346
contentElement.addClass('md-panel _md-panel-offscreen');
1341-
wrapper.append(contentElement);
1347+
outerWrapper.children().eq(0).append(contentElement);
13421348

1343-
return wrapper;
1349+
return outerWrapper;
13441350
};
13451351

13461352

@@ -1407,6 +1413,9 @@ function MdPanelRef(config, $injector) {
14071413
/** @type {!angular.JQLite|undefined} */
14081414
this.panelEl;
14091415

1416+
/** @type {!angular.JQLite|undefined} */
1417+
this.innerWrapper;
1418+
14101419
/**
14111420
* Whether the panel is attached. This is synchronous. When attach is called,
14121421
* isAttached is set to true. When detach is called, isAttached is set to
@@ -1853,6 +1862,11 @@ MdPanelRef.prototype._compile = function() {
18531862
);
18541863
}
18551864

1865+
// Save a reference to the inner wrapper.
1866+
self.innerWrapper = angular.element(
1867+
self.panelContainer[0].querySelector('.md-panel-inner-wrapper')
1868+
);
1869+
18561870
// Save a reference to the cleanup function from the compiler.
18571871
self._compilerCleanup = compileData.cleanup;
18581872

@@ -1929,14 +1943,17 @@ MdPanelRef.prototype._addStyles = function() {
19291943
var self = this;
19301944
return this._$q(function(resolve) {
19311945
self.panelContainer.css('z-index', self.config['zIndex']);
1932-
self.panelEl.css('z-index', self.config['zIndex'] + 1);
1946+
self.innerWrapper.css('z-index', self.config['zIndex'] + 1);
19331947

19341948
var hideAndResolve = function() {
19351949
// Theme the element and container.
19361950
self._setTheming();
19371951

19381952
// Remove offscreen class and add hidden class.
19391953
self.panelEl.removeClass('_md-panel-offscreen');
1954+
1955+
// Remove left: -9999px and add hidden class.
1956+
self.innerWrapper.css('left', '');
19401957
self.panelContainer.addClass(MD_PANEL_HIDDEN);
19411958

19421959
resolve(self);
@@ -2003,27 +2020,27 @@ MdPanelRef.prototype._updatePosition = function(init) {
20032020
var positionConfig = this.config['position'];
20042021

20052022
if (positionConfig) {
2006-
positionConfig._setPanelPosition(this.panelEl);
2023+
positionConfig._setPanelPosition(this.innerWrapper);
20072024

20082025
// Hide the panel now that position is known.
20092026
if (init) {
20102027
this.panelEl.removeClass('_md-panel-offscreen');
20112028
this.panelContainer.addClass(MD_PANEL_HIDDEN);
20122029
}
20132030

2014-
this.panelEl.css(
2031+
this.innerWrapper.css(
20152032
MdPanelPosition.absPosition.TOP,
20162033
positionConfig.getTop()
20172034
);
2018-
this.panelEl.css(
2035+
this.innerWrapper.css(
20192036
MdPanelPosition.absPosition.BOTTOM,
20202037
positionConfig.getBottom()
20212038
);
2022-
this.panelEl.css(
2039+
this.innerWrapper.css(
20232040
MdPanelPosition.absPosition.LEFT,
20242041
positionConfig.getLeft()
20252042
);
2026-
this.panelEl.css(
2043+
this.innerWrapper.css(
20272044
MdPanelPosition.absPosition.RIGHT,
20282045
positionConfig.getRight()
20292046
);
@@ -2880,38 +2897,38 @@ MdPanelPosition.prototype.getTransform = function() {
28802897

28812898

28822899
/**
2883-
* Sets the `transform` value for a panel element.
2884-
* @param {!angular.JQLite} panelEl
2900+
* Sets the `transform` value for an element.
2901+
* @param {!angular.JQLite} el
28852902
* @returns {!angular.JQLite}
28862903
* @private
28872904
*/
2888-
MdPanelPosition.prototype._setTransform = function(panelEl) {
2889-
return panelEl.css(this._$mdConstant.CSS.TRANSFORM, this.getTransform());
2905+
MdPanelPosition.prototype._setTransform = function(el) {
2906+
return el.css(this._$mdConstant.CSS.TRANSFORM, this.getTransform());
28902907
};
28912908

28922909

28932910
/**
28942911
* True if the panel is completely on-screen with this positioning; false
28952912
* otherwise.
2896-
* @param {!angular.JQLite} panelEl
2913+
* @param {!angular.JQLite} el
28972914
* @return {boolean}
28982915
* @private
28992916
*/
2900-
MdPanelPosition.prototype._isOnscreen = function(panelEl) {
2917+
MdPanelPosition.prototype._isOnscreen = function(el) {
29012918
// this works because we always use fixed positioning for the panel,
29022919
// which is relative to the viewport.
29032920
var left = parseInt(this.getLeft());
29042921
var top = parseInt(this.getTop());
29052922

29062923
if (this._translateX.length || this._translateY.length) {
29072924
var prefixedTransform = this._$mdConstant.CSS.TRANSFORM;
2908-
var offsets = getComputedTranslations(panelEl, prefixedTransform);
2925+
var offsets = getComputedTranslations(el, prefixedTransform);
29092926
left += offsets.x;
29102927
top += offsets.y;
29112928
}
29122929

2913-
var right = left + panelEl[0].offsetWidth;
2914-
var bottom = top + panelEl[0].offsetHeight;
2930+
var right = left + el[0].offsetWidth;
2931+
var bottom = top + el[0].offsetHeight;
29152932

29162933
return (left >= 0) &&
29172934
(top >= 0) &&
@@ -2950,53 +2967,53 @@ MdPanelPosition.prototype._reduceTranslateValues =
29502967
/**
29512968
* Sets the panel position based on the created panel element and best x/y
29522969
* positioning.
2953-
* @param {!angular.JQLite} panelEl
2970+
* @param {!angular.JQLite} el
29542971
* @private
29552972
*/
2956-
MdPanelPosition.prototype._setPanelPosition = function(panelEl) {
2957-
// Remove the "position adjusted" class in case it has been added before.
2958-
panelEl.removeClass('_md-panel-position-adjusted');
2973+
MdPanelPosition.prototype._setPanelPosition = function(el) {
2974+
// Remove the class in case it has been added before.
2975+
el.removeClass('_md-panel-position-adjusted');
29592976

29602977
// Only calculate the position if necessary.
29612978
if (this._absolute) {
2962-
this._setTransform(panelEl);
2979+
this._setTransform(el);
29632980
return;
29642981
}
29652982

29662983
if (this._actualPosition) {
2967-
this._calculatePanelPosition(panelEl, this._actualPosition);
2968-
this._setTransform(panelEl);
2969-
this._constrainToViewport(panelEl);
2984+
this._calculatePanelPosition(el, this._actualPosition);
2985+
this._setTransform(el);
2986+
this._constrainToViewport(el);
29702987
return;
29712988
}
29722989

29732990
for (var i = 0; i < this._positions.length; i++) {
29742991
this._actualPosition = this._positions[i];
2975-
this._calculatePanelPosition(panelEl, this._actualPosition);
2976-
this._setTransform(panelEl);
2992+
this._calculatePanelPosition(el, this._actualPosition);
2993+
this._setTransform(el);
29772994

2978-
if (this._isOnscreen(panelEl)) {
2995+
if (this._isOnscreen(el)) {
29792996
return;
29802997
}
29812998
}
29822999

2983-
this._constrainToViewport(panelEl);
3000+
this._constrainToViewport(el);
29843001
};
29853002

29863003

29873004
/**
29883005
* Constrains a panel's position to the viewport.
2989-
* @param {!angular.JQLite} panelEl
3006+
* @param {!angular.JQLite} el
29903007
* @private
29913008
*/
2992-
MdPanelPosition.prototype._constrainToViewport = function(panelEl) {
3009+
MdPanelPosition.prototype._constrainToViewport = function(el) {
29933010
var margin = MdPanelPosition.viewportMargin;
29943011
var initialTop = this._top;
29953012
var initialLeft = this._left;
29963013

29973014
if (this.getTop()) {
29983015
var top = parseInt(this.getTop());
2999-
var bottom = panelEl[0].offsetHeight + top;
3016+
var bottom = el[0].offsetHeight + top;
30003017
var viewportHeight = this._$window.innerHeight;
30013018

30023019
if (top < margin) {
@@ -3008,7 +3025,7 @@ MdPanelPosition.prototype._constrainToViewport = function(panelEl) {
30083025

30093026
if (this.getLeft()) {
30103027
var left = parseInt(this.getLeft());
3011-
var right = panelEl[0].offsetWidth + left;
3028+
var right = el[0].offsetWidth + left;
30123029
var viewportWidth = this._$window.innerWidth;
30133030

30143031
if (left < margin) {
@@ -3019,7 +3036,7 @@ MdPanelPosition.prototype._constrainToViewport = function(panelEl) {
30193036
}
30203037

30213038
// Class that can be used to re-style the panel if it was repositioned.
3022-
panelEl.toggleClass(
3039+
el.toggleClass(
30233040
'_md-panel-position-adjusted',
30243041
this._top !== initialTop || this._left !== initialLeft
30253042
);
@@ -3058,15 +3075,15 @@ MdPanelPosition.prototype._bidi = function(position) {
30583075
/**
30593076
* Calculates the panel position based on the created panel element and the
30603077
* provided positioning.
3061-
* @param {!angular.JQLite} panelEl
3078+
* @param {!angular.JQLite} el
30623079
* @param {!{x:string, y:string}} position
30633080
* @private
30643081
*/
3065-
MdPanelPosition.prototype._calculatePanelPosition = function(panelEl, position) {
3082+
MdPanelPosition.prototype._calculatePanelPosition = function(el, position) {
30663083

3067-
var panelBounds = panelEl[0].getBoundingClientRect();
3068-
var panelWidth = Math.max(panelBounds.width, panelEl[0].clientWidth);
3069-
var panelHeight = Math.max(panelBounds.height, panelEl[0].clientHeight);
3084+
var panelBounds = el[0].getBoundingClientRect();
3085+
var panelWidth = Math.max(panelBounds.width, el[0].clientWidth);
3086+
var panelHeight = Math.max(panelBounds.height, el[0].clientHeight);
30703087

30713088
var targetBounds = this._relativeToEl[0].getBoundingClientRect();
30723089

src/components/panel/panel.scss

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

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

1313
._md-panel-offscreen {
1414
left: -9999px;
1515
}
1616

17-
._md-panel-fullscreen {
18-
border-radius: 0;
19-
left: 0;
20-
min-height: 100%;
21-
min-width: 100%;
22-
position: fixed;
23-
top: 0;
17+
._md-panel-hidden {
18+
display: none;
2419
}
2520

2621
// Only used when no animations are present.
@@ -31,7 +26,7 @@
3126

3227
.md-panel {
3328
opacity: 0;
34-
position: fixed;
29+
position: relative;
3530

3631
&._md-panel-shown {
3732
// Only used when custom animations are present.
@@ -57,7 +52,7 @@
5752

5853
&._md-panel-backdrop {
5954
height: 100%;
60-
position: absolute;
55+
position: fixed;
6156
width: 100%;
6257
}
6358

@@ -70,3 +65,12 @@
7065
transition: opacity $material-leave-duration $material-leave-timing-function;
7166
}
7267
}
68+
69+
._md-panel-fullscreen {
70+
border-radius: 0;
71+
left: 0;
72+
min-height: 100%;
73+
min-width: 100%;
74+
position: fixed;
75+
top: 0;
76+
}

0 commit comments

Comments
 (0)