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

Commit 18aa360

Browse files
committed
feat(panel): Update the panel position on scroll.
1 parent 0d2e489 commit 18aa360

File tree

3 files changed

+79
-43
lines changed

3 files changed

+79
-43
lines changed

src/components/panel/demoBasicUsage/script.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ BasicDemoCtrl.prototype.showMenu = function(ev) {
7878
openFrom: ev,
7979
clickOutsideToClose: true,
8080
escapeToClose: true,
81-
focusOnOpen: false
81+
focusOnOpen: false,
82+
zIndex: 2
8283
};
8384

8485
this._mdPanel.open(config);

src/components/panel/panel.js

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,12 @@ function MdPanelRef(config, $injector) {
766766
/** @private @const {!angular.$log} */
767767
this._$log = $injector.get('$log');
768768

769+
/** @private @const {!angular.$window} */
770+
this._$window = $injector.get('$window');
771+
772+
/** @private @const {!Function} */
773+
this._$$rAF = $injector.get('$$rAF');
774+
769775
// Public variables.
770776
/**
771777
* Unique id for the panelRef.
@@ -1148,22 +1154,32 @@ MdPanelRef.prototype._addStyles = function() {
11481154
// correctly. This is necessary so that the panel will have a defined height
11491155
// and width.
11501156
self._$rootScope['$$postDigest'](function() {
1151-
positionConfig._calculatePanelPosition(self._panelEl);
1152-
self._panelEl.css('top', positionConfig.getTop());
1153-
self._panelEl.css('bottom', positionConfig.getBottom());
1154-
self._panelEl.css('left', positionConfig.getLeft());
1155-
self._panelEl.css('right', positionConfig.getRight());
1156-
1157-
// Use the vendor prefixed version of transform.
1158-
var prefixedTransform = self._$mdConstant.CSS.TRANSFORM;
1159-
self._panelEl.css(prefixedTransform, positionConfig.getTransform());
1160-
1157+
self._updatePosition();
11611158
resolve(self);
11621159
});
11631160
});
11641161
};
11651162

11661163

1164+
/**
1165+
* Calculates and updates the position of the panel.
1166+
* @private
1167+
*/
1168+
MdPanelRef.prototype._updatePosition = function() {
1169+
var positionConfig = this._config['position'];
1170+
1171+
positionConfig._calculatePanelPosition(this._panelEl);
1172+
this._panelEl.css('top', positionConfig.getTop());
1173+
this._panelEl.css('bottom', positionConfig.getBottom());
1174+
this._panelEl.css('left', positionConfig.getLeft());
1175+
this._panelEl.css('right', positionConfig.getRight());
1176+
1177+
// Use the vendor prefixed version of transform.
1178+
var prefixedTransform = this._$mdConstant.CSS.TRANSFORM;
1179+
this._panelEl.css(prefixedTransform, positionConfig.getTransform());
1180+
};
1181+
1182+
11671183
/**
11681184
* Focuses on the panel or the first focus target.
11691185
* @private
@@ -1221,6 +1237,7 @@ MdPanelRef.prototype._createBackdrop = function() {
12211237
MdPanelRef.prototype._addEventListeners = function() {
12221238
this._configureEscapeToClose();
12231239
this._configureClickOutsideToClose();
1240+
this._configureScrollListener();
12241241
};
12251242

12261243

@@ -1310,6 +1327,31 @@ MdPanelRef.prototype._configureClickOutsideToClose = function() {
13101327
};
13111328

13121329

1330+
/**
1331+
* Configures the listeners for updating the panel position on scroll.
1332+
* @private
1333+
*/
1334+
MdPanelRef.prototype._configureScrollListener = function() {
1335+
var updatePosition = angular.bind(this, this._updatePosition);
1336+
var debouncedUpdatePosition = this._$$rAF.throttle(updatePosition);
1337+
var self = this;
1338+
1339+
var onScroll = function() {
1340+
if (!self._config['disableParentScroll']) {
1341+
debouncedUpdatePosition();
1342+
}
1343+
};
1344+
1345+
// Add listeners.
1346+
this._$window.addEventListener('scroll', onScroll, true);
1347+
1348+
// Queue remove listeners function.
1349+
this._removeListeners.push(function() {
1350+
self._$window.removeEventListener('scroll', onScroll, true);
1351+
});
1352+
};
1353+
1354+
13131355
/**
13141356
* Setup the focus traps. These traps will wrap focus when tabbing past the
13151357
* panel. When shift-tabbing, the focus will stick in place.
@@ -1460,8 +1502,8 @@ function MdPanelPosition() {
14601502
/** @private {boolean} */
14611503
this._absolute = false;
14621504

1463-
/** @private {!DOMRect} */
1464-
this._relativeToRect;
1505+
/** @private {!angular.JQLite} */
1506+
this._relativeToEl;
14651507

14661508
/** @private {string} */
14671509
this._top = '';
@@ -1619,7 +1661,7 @@ MdPanelPosition.prototype.center = function() {
16191661
*/
16201662
MdPanelPosition.prototype.relativeTo = function(element) {
16211663
this._absolute = false;
1622-
this._relativeToRect = getElement(element)[0].getBoundingClientRect();
1664+
this._relativeToEl = getElement(element);
16231665
return this;
16241666
};
16251667

@@ -1631,7 +1673,7 @@ MdPanelPosition.prototype.relativeTo = function(element) {
16311673
* @returns {MdPanelPosition}
16321674
*/
16331675
MdPanelPosition.prototype.addPanelPosition = function(xPosition, yPosition) {
1634-
if (!this._relativeToRect) {
1676+
if (!this._relativeToEl) {
16351677
throw new Error('addPanelPosition can only be used with relative ' +
16361678
'positioning. Set relativeTo first.');
16371679
}
@@ -1810,14 +1852,13 @@ MdPanelPosition.prototype._calculatePanelPosition = function(panelEl) {
18101852
return;
18111853
}
18121854

1813-
// TODO(ErinCoughlan): Update position on scroll.
18141855
// TODO(ErinCoughlan): Position panel intelligently to keep it on screen.
18151856

18161857
var panelBounds = panelEl[0].getBoundingClientRect();
18171858
var panelWidth = panelBounds.width;
18181859
var panelHeight = panelBounds.height;
18191860

1820-
var targetBounds = this._relativeToRect;
1861+
var targetBounds = this._relativeToEl[0].getBoundingClientRect();
18211862

18221863
var targetLeft = targetBounds.left;
18231864
var targetRight = targetBounds.right;

src/components/panel/panel.spec.js

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
describe('$mdPanel', function() {
22
var $mdPanel, $rootScope, $rootEl, $templateCache, $q, $material, $mdConstant,
3-
$mdUtil, $animate;
3+
$mdUtil, $animate, $$rAF, $window;
44
var panelRef;
55
var attachedElements = [];
66
var PANEL_WRAPPER_CLASS = '.md-panel-outer-wrapper';
@@ -28,6 +28,8 @@ describe('$mdPanel', function() {
2828
$mdConstant = $injector.get('$mdConstant');
2929
$mdUtil = $injector.get('$mdUtil');
3030
$animate = $injector.get('$animate');
31+
$window = $injector.get('$window');
32+
$$rAF = $injector.get('$$rAF');
3133
};
3234

3335
beforeEach(function() {
@@ -1426,7 +1428,7 @@ describe('$mdPanel', function() {
14261428
.relativeTo(myButton)
14271429
.addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS)
14281430
.addPanelPosition(xPosition.ALIGN_END, yPosition.ALIGN_BOTTOMS)
1429-
.addPanelPosition(xPosition.ALIGN_OFFSET_END, yPosition.BELOW);
1431+
.addPanelPosition(xPosition.OFFSET_END, yPosition.BELOW);
14301432

14311433
config['position'] = position;
14321434

@@ -1590,34 +1592,26 @@ describe('$mdPanel', function() {
15901592
expect(panelRect.left).toBeApproximately(myButtonRect.right);
15911593
});
15921594
});
1593-
});
15941595

1595-
it('should throw if xPosition is not valid', function() {
1596-
var myButton = '<button>myButton</button>';
1597-
attachToBody(myButton);
1598-
myButton = angular.element(document.querySelector('button'));
1599-
1600-
var expression = function() {
1601-
mdPanelPosition
1602-
.relativeTo(myButton)
1603-
.addPanelPosition('fake-x-position', null);
1604-
};
1605-
1606-
expect(expression).toThrow();
1607-
});
1596+
it('should throw if xPosition is not valid', function() {
1597+
var expression = function() {
1598+
mdPanelPosition
1599+
.relativeTo(myButton)
1600+
.addPanelPosition('fake-x-position', null);
1601+
};
16081602

1609-
it('should throw if yPosition is not valid', function() {
1610-
var myButton = '<button>myButton</button>';
1611-
attachToBody(myButton);
1612-
myButton = angular.element(document.querySelector('button'));
1603+
expect(expression).toThrow();
1604+
});
16131605

1614-
var expression = function() {
1615-
mdPanelPosition
1616-
.relativeTo(myButton)
1617-
.withPanelYPosition('fake-y-position');
1618-
};
1606+
it('should throw if yPosition is not valid', function() {
1607+
var expression = function() {
1608+
mdPanelPosition
1609+
.relativeTo(myButton)
1610+
.addPanelPosition(null, 'fake-y-position');
1611+
};
16191612

1620-
expect(expression).toThrow();
1613+
expect(expression).toThrow();
1614+
});
16211615
});
16221616
});
16231617

0 commit comments

Comments
 (0)