diff --git a/src/components/panel/demoBasicUsage/script.js b/src/components/panel/demoBasicUsage/script.js
index 97c93829c77..6298923ca90 100644
--- a/src/components/panel/demoBasicUsage/script.js
+++ b/src/components/panel/demoBasicUsage/script.js
@@ -78,7 +78,8 @@ BasicDemoCtrl.prototype.showMenu = function(ev) {
openFrom: ev,
clickOutsideToClose: true,
escapeToClose: true,
- focusOnOpen: false
+ focusOnOpen: false,
+ zIndex: 2
};
this._mdPanel.open(config);
diff --git a/src/components/panel/panel.js b/src/components/panel/panel.js
index 2ee6673f8d1..86079551c32 100644
--- a/src/components/panel/panel.js
+++ b/src/components/panel/panel.js
@@ -766,6 +766,12 @@ function MdPanelRef(config, $injector) {
/** @private @const {!angular.$log} */
this._$log = $injector.get('$log');
+ /** @private @const {!angular.$window} */
+ this._$window = $injector.get('$window');
+
+ /** @private @const {!Function} */
+ this._$$rAF = $injector.get('$$rAF');
+
// Public variables.
/**
* Unique id for the panelRef.
@@ -1148,22 +1154,32 @@ MdPanelRef.prototype._addStyles = function() {
// correctly. This is necessary so that the panel will have a defined height
// and width.
self._$rootScope['$$postDigest'](function() {
- positionConfig._calculatePanelPosition(self._panelEl);
- self._panelEl.css('top', positionConfig.getTop());
- self._panelEl.css('bottom', positionConfig.getBottom());
- self._panelEl.css('left', positionConfig.getLeft());
- self._panelEl.css('right', positionConfig.getRight());
-
- // Use the vendor prefixed version of transform.
- var prefixedTransform = self._$mdConstant.CSS.TRANSFORM;
- self._panelEl.css(prefixedTransform, positionConfig.getTransform());
-
+ self._updatePosition();
resolve(self);
});
});
};
+/**
+ * Calculates and updates the position of the panel.
+ * @private
+ */
+MdPanelRef.prototype._updatePosition = function() {
+ var positionConfig = this._config['position'];
+
+ positionConfig._calculatePanelPosition(this._panelEl);
+ this._panelEl.css('top', positionConfig.getTop());
+ this._panelEl.css('bottom', positionConfig.getBottom());
+ this._panelEl.css('left', positionConfig.getLeft());
+ this._panelEl.css('right', positionConfig.getRight());
+
+ // Use the vendor prefixed version of transform.
+ var prefixedTransform = this._$mdConstant.CSS.TRANSFORM;
+ this._panelEl.css(prefixedTransform, positionConfig.getTransform());
+};
+
+
/**
* Focuses on the panel or the first focus target.
* @private
@@ -1221,6 +1237,7 @@ MdPanelRef.prototype._createBackdrop = function() {
MdPanelRef.prototype._addEventListeners = function() {
this._configureEscapeToClose();
this._configureClickOutsideToClose();
+ this._configureScrollListener();
};
@@ -1310,6 +1327,31 @@ MdPanelRef.prototype._configureClickOutsideToClose = function() {
};
+/**
+ * Configures the listeners for updating the panel position on scroll.
+ * @private
+*/
+MdPanelRef.prototype._configureScrollListener = function() {
+ var updatePosition = angular.bind(this, this._updatePosition);
+ var debouncedUpdatePosition = this._$$rAF.throttle(updatePosition);
+ var self = this;
+
+ var onScroll = function() {
+ if (!self._config['disableParentScroll']) {
+ debouncedUpdatePosition();
+ }
+ };
+
+ // Add listeners.
+ this._$window.addEventListener('scroll', onScroll, true);
+
+ // Queue remove listeners function.
+ this._removeListeners.push(function() {
+ self._$window.removeEventListener('scroll', onScroll, true);
+ });
+};
+
+
/**
* Setup the focus traps. These traps will wrap focus when tabbing past the
* panel. When shift-tabbing, the focus will stick in place.
@@ -1460,8 +1502,8 @@ function MdPanelPosition() {
/** @private {boolean} */
this._absolute = false;
- /** @private {!DOMRect} */
- this._relativeToRect;
+ /** @private {!angular.JQLite} */
+ this._relativeToEl;
/** @private {string} */
this._top = '';
@@ -1619,7 +1661,7 @@ MdPanelPosition.prototype.center = function() {
*/
MdPanelPosition.prototype.relativeTo = function(element) {
this._absolute = false;
- this._relativeToRect = getElement(element)[0].getBoundingClientRect();
+ this._relativeToEl = getElement(element);
return this;
};
@@ -1631,7 +1673,7 @@ MdPanelPosition.prototype.relativeTo = function(element) {
* @returns {MdPanelPosition}
*/
MdPanelPosition.prototype.addPanelPosition = function(xPosition, yPosition) {
- if (!this._relativeToRect) {
+ if (!this._relativeToEl) {
throw new Error('addPanelPosition can only be used with relative ' +
'positioning. Set relativeTo first.');
}
@@ -1810,14 +1852,13 @@ MdPanelPosition.prototype._calculatePanelPosition = function(panelEl) {
return;
}
- // TODO(ErinCoughlan): Update position on scroll.
// TODO(ErinCoughlan): Position panel intelligently to keep it on screen.
var panelBounds = panelEl[0].getBoundingClientRect();
var panelWidth = panelBounds.width;
var panelHeight = panelBounds.height;
- var targetBounds = this._relativeToRect;
+ var targetBounds = this._relativeToEl[0].getBoundingClientRect();
var targetLeft = targetBounds.left;
var targetRight = targetBounds.right;
diff --git a/src/components/panel/panel.spec.js b/src/components/panel/panel.spec.js
index 91557d56cb5..7d9357f2f40 100644
--- a/src/components/panel/panel.spec.js
+++ b/src/components/panel/panel.spec.js
@@ -1,6 +1,6 @@
describe('$mdPanel', function() {
var $mdPanel, $rootScope, $rootEl, $templateCache, $q, $material, $mdConstant,
- $mdUtil, $animate;
+ $mdUtil, $animate, $$rAF, $window;
var panelRef;
var attachedElements = [];
var PANEL_WRAPPER_CLASS = '.md-panel-outer-wrapper';
@@ -28,6 +28,8 @@ describe('$mdPanel', function() {
$mdConstant = $injector.get('$mdConstant');
$mdUtil = $injector.get('$mdUtil');
$animate = $injector.get('$animate');
+ $window = $injector.get('$window');
+ $$rAF = $injector.get('$$rAF');
};
beforeEach(function() {
@@ -1426,7 +1428,7 @@ describe('$mdPanel', function() {
.relativeTo(myButton)
.addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS)
.addPanelPosition(xPosition.ALIGN_END, yPosition.ALIGN_BOTTOMS)
- .addPanelPosition(xPosition.ALIGN_OFFSET_END, yPosition.BELOW);
+ .addPanelPosition(xPosition.OFFSET_END, yPosition.BELOW);
config['position'] = position;
@@ -1590,34 +1592,26 @@ describe('$mdPanel', function() {
expect(panelRect.left).toBeApproximately(myButtonRect.right);
});
});
- });
- it('should throw if xPosition is not valid', function() {
- var myButton = '';
- attachToBody(myButton);
- myButton = angular.element(document.querySelector('button'));
-
- var expression = function() {
- mdPanelPosition
- .relativeTo(myButton)
- .addPanelPosition('fake-x-position', null);
- };
-
- expect(expression).toThrow();
- });
+ it('should throw if xPosition is not valid', function() {
+ var expression = function() {
+ mdPanelPosition
+ .relativeTo(myButton)
+ .addPanelPosition('fake-x-position', null);
+ };
- it('should throw if yPosition is not valid', function() {
- var myButton = '';
- attachToBody(myButton);
- myButton = angular.element(document.querySelector('button'));
+ expect(expression).toThrow();
+ });
- var expression = function() {
- mdPanelPosition
- .relativeTo(myButton)
- .withPanelYPosition('fake-y-position');
- };
+ it('should throw if yPosition is not valid', function() {
+ var expression = function() {
+ mdPanelPosition
+ .relativeTo(myButton)
+ .addPanelPosition(null, 'fake-y-position');
+ };
- expect(expression).toThrow();
+ expect(expression).toThrow();
+ });
});
});