diff --git a/src/components/panel/demoBasicUsage/script.js b/src/components/panel/demoBasicUsage/script.js index 3126facae94..97c93829c77 100644 --- a/src/components/panel/demoBasicUsage/script.js +++ b/src/components/panel/demoBasicUsage/script.js @@ -48,8 +48,7 @@ BasicDemoCtrl.prototype.showDialog = function() { BasicDemoCtrl.prototype.showMenu = function(ev) { var position = this._mdPanel.newPanelPosition() .relativeTo('.demo-menu-open-button') - .withPanelXPosition(this._mdPanel.xPosition.ALIGN_START) - .withPanelYPosition(this._mdPanel.yPosition.BELOW); + .addPanelPosition(this._mdPanel.xPosition.ALIGN_START, this._mdPanel.yPosition.BELOW); var config = { attachTo: '.demo-md-panel', diff --git a/src/components/panel/panel.js b/src/components/panel/panel.js index b5c2ad71114..e62f66e7f81 100644 --- a/src/components/panel/panel.js +++ b/src/components/panel/panel.js @@ -338,14 +338,12 @@ angular * Overlapping the panel with an element: * `new MdPanelPosition() * .relativeTo(someElement) - * .withPanelXPosition($mdPanel.xPosition.ALIGN_START) - * .withPanelYPosition($mdPanel.yPosition.ALIGN_TOPS);` + * .addPanelPosition($mdPanel.xPosition.ALIGN_START, $mdPanel.yPosition.ALIGN_TOPS);` * * Aligning the panel with the bottom of an element: * `new MdPanelPosition() * .relativeTo(someElement) - * .withPanelXPosition($mdPanel.xPosition.CENTER) - * .withPanelYPosition($mdPanel.yPosition.BELOW);` + * .addPanelPosition($mdPanel.xPosition.CENTER, $mdPanel.yPosition.BELOW); */ /** @@ -438,10 +436,15 @@ angular /** * @ngdoc method - * @name MdPanelPosition#withPanelXPosition + * @name MdPanelPosition#addPanelPosition * @param {string} xPosition + * @param {string} yPosition * @description - * Sets the x position for the panel relative to another element. + * Sets the x and y position for the panel relative to another element. Can be + * called multiple times to specify an ordered list of panel positions. The + * first position which allows the panel to be completely on-screen will be + * chosen; the last position will be chose whether it is on-screen or not. + * * xPosition must be one of the following values available on * $mdPanel.xPosition: * @@ -459,14 +462,7 @@ angular * C: CENTER * D: ALIGN_END (for LTR displays) * E: OFFSET_END (for LTR displays) - */ - -/** - * @ngdoc method - * @name MdPanelPosition#withPanelYPosition - * @param {string} yPosition - * @description - * Sets the y position for the panel relative to another element. + * * yPosition must be one of the following values available on * $mdPanel.yPosition: * @@ -485,6 +481,7 @@ angular * H: CENTER * I: ALIGN_BOTTOMS * J: ABOVE + * @returns {MdPanelPosition} */ /** @@ -1090,7 +1087,7 @@ MdPanelRef.prototype._addStyles = function() { return this._$q(function(resolve) { self._panelContainer.css('z-index', self._config['zIndex']); self._panelEl.css('z-index', self._config['zIndex'] + 1); - + if (self._config['fullscreen']) { self._panelEl.addClass('_md-panel-fullscreen'); resolve(self); @@ -1436,11 +1433,8 @@ function MdPanelPosition() { /** @private {!Array} */ this._translateY = []; - /** @private {string} */ - this._xPosition = ''; - - /** @private {string} */ - this._yPosition = ''; + /** @private {!Array<{x:string, y:string}>} */ + this._positions = []; } @@ -1583,60 +1577,77 @@ MdPanelPosition.prototype.relativeTo = function(element) { /** - * Sets the x position for the panel relative to another element. - * xPosition must be one of the MdPanelPosition.xPosition values. - * - * @param {string} xPosition + * Sets the x and y positions for the panel relative to another element. + * @param {string} xPosition must be one of the MdPanelPosition.xPosition values. + * @param {string} yPosition must be one of the MdPanelPosition.yPosition values. * @returns {MdPanelPosition} */ -MdPanelPosition.prototype.withPanelXPosition = function(xPosition) { +MdPanelPosition.prototype.addPanelPosition = function(xPosition, yPosition) { if (!this._relativeToRect) { - throw new Error('withPanelXPosition can only be used with relative' + + throw new Error('addPanelPosition can only be used with relative ' + 'positioning. Set relativeTo first.'); } - var positionKeys = Object.keys(MdPanelPosition.xPosition); + this._validateXPosition(xPosition); + this._validateYPosition(yPosition); + + this._positions.push({ + x: xPosition, + y: yPosition, + }); + return this; +}; + + +/** + * Ensure that yPosition is a valid position name. Throw an exception if not. + * @param {string} yPosition + */ +MdPanelPosition.prototype._validateYPosition = function(yPosition) { + // empty is ok + if (yPosition == null) { + return; + } + + var positionKeys = Object.keys(MdPanelPosition.yPosition); var positionValues = []; for (var key, i = 0; key = positionKeys[i]; i++) { - var position = MdPanelPosition.xPosition[key]; + var position = MdPanelPosition.yPosition[key]; positionValues.push(position); - if (position === xPosition) { - this._xPosition = xPosition; - return this; + + if (position === yPosition) { + return; } } - throw new Error('withPanelXPosition only accepts the following values:\n' + - positionValues.join(' | ')); + throw new Error('Panel y position only accepts the following values:\n' + + positionValues.join(' | ')); }; /** - * Sets the y position for the panel relative to another element. - * yPosition must be one of the MdPanelPosition.yPosition values. - * - * @param {string} yPosition - * @returns {MdPanelPosition} + * Ensure that xPosition is a valid position name. Throw an exception if not. + * @param {string} xPosition */ -MdPanelPosition.prototype.withPanelYPosition = function(yPosition) { - if (!this._relativeToRect) { - throw new Error('withPanelYPosition can only be used with relative ' + - 'positioning. Set relativeTo first.'); +MdPanelPosition.prototype._validateXPosition = function(xPosition) { + // empty is ok + if (xPosition == null) { + return; } - var positionKeys = Object.keys(MdPanelPosition.yPosition); + var positionKeys = Object.keys(MdPanelPosition.xPosition); var positionValues = []; for (var key, i = 0; key = positionKeys[i]; i++) { - var position = MdPanelPosition.yPosition[key]; + var position = MdPanelPosition.xPosition[key]; positionValues.push(position); - if (position === yPosition) { - this._yPosition = yPosition; - return this; + if (position === xPosition) { + return; } } - throw new Error('withPanelYPosition only accepts the following values:\n' + + throw new Error('Panel x Position only accepts the following values:\n' + positionValues.join(' | ')); + }; @@ -1722,6 +1733,16 @@ MdPanelPosition.prototype.getTransform = function() { }; +/** + * Gets the first x/y position that can fit on-screen. + * @returns {string} + */ +MdPanelPosition.prototype.getActualPosition = function() { + // TODO(gmoothart): intelligently pick the first on-screen position. + return this._positions[0] || {}; +}; + + /** * Reduces a list of translate values to a string that can be used within * transform. @@ -1765,7 +1786,9 @@ MdPanelPosition.prototype._calculatePanelPosition = function(panelEl) { var targetRight = targetBounds.right; var targetWidth = targetBounds.width; - switch (this._xPosition) { + var pos = this.getActualPosition(); + + switch (pos.x) { case MdPanelPosition.xPosition.OFFSET_START: // TODO(ErinCoughlan): Change OFFSET_START for rtl vs ltr. this._left = targetLeft - panelWidth + 'px'; @@ -1792,7 +1815,7 @@ MdPanelPosition.prototype._calculatePanelPosition = function(panelEl) { var targetBottom = targetBounds.bottom; var targetHeight = targetBounds.height; - switch (this._yPosition) { + switch (pos.y) { case MdPanelPosition.yPosition.ABOVE: this._top = targetTop - panelHeight + 'px'; break; diff --git a/src/components/panel/panel.spec.js b/src/components/panel/panel.spec.js index 4e7f4594ffa..1e3f2ff6272 100644 --- a/src/components/panel/panel.spec.js +++ b/src/components/panel/panel.spec.js @@ -1118,8 +1118,7 @@ describe('$mdPanel', function() { it('with respect to an element', function() { var position = mdPanelPosition .relativeTo(myButton[0]) - .withPanelXPosition(xPosition.ALIGN_START) - .withPanelYPosition(yPosition.ALIGN_TOPS); + .addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS); config['position'] = position; @@ -1133,8 +1132,7 @@ describe('$mdPanel', function() { it('with respect to a query selector', function() { var position = mdPanelPosition .relativeTo('button') - .withPanelXPosition(xPosition.ALIGN_START) - .withPanelYPosition(yPosition.ALIGN_TOPS); + .addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS); config['position'] = position; @@ -1148,8 +1146,7 @@ describe('$mdPanel', function() { it('with respect to a JQLite object', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelXPosition(xPosition.ALIGN_START) - .withPanelYPosition(yPosition.ALIGN_TOPS); + .addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS); config['position'] = position; @@ -1160,11 +1157,31 @@ describe('$mdPanel', function() { expect(panelCss.top).toBeApproximately(myButtonRect.top); }); + it('chooses first on-screen position from a list of positions', function() { + var position = mdPanelPosition + .relativeTo(myButton) + .addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS) + .addPanelPosition(xPosition.ALIGN_END, yPosition.ALIGN_BOTTOMS) + .addPanelPosition(xPosition.ALIGN_OFFSET_END, yPosition.BELOW); + + config['position'] = position; + + openPanel(config); + + expect(position.getActualPosition()).toEqual({ + x: xPosition.ALIGN_START, + y: yPosition.ALIGN_TOPS, + }); + var panelCss = document.querySelector(PANEL_EL).style; + expect(panelCss.left).toBeApproximately(myButtonRect.left); + expect(panelCss.top).toBeApproximately(myButtonRect.top); + }); + describe('vertically', function() { it('above an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelYPosition(yPosition.ABOVE); + .addPanelPosition(null, yPosition.ABOVE); config['position'] = position; @@ -1178,7 +1195,7 @@ describe('$mdPanel', function() { it('top aligned with an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelYPosition(yPosition.ALIGN_TOPS); + .addPanelPosition(null, yPosition.ALIGN_TOPS); config['position'] = position; @@ -1192,7 +1209,7 @@ describe('$mdPanel', function() { it('centered with an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelYPosition(yPosition.CENTER); + .addPanelPosition(null, yPosition.CENTER); config['position'] = position; @@ -1209,7 +1226,7 @@ describe('$mdPanel', function() { it('bottom aligned with an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelYPosition(yPosition.ALIGN_BOTTOMS); + .addPanelPosition(null, yPosition.ALIGN_BOTTOMS); config['position'] = position; @@ -1223,7 +1240,7 @@ describe('$mdPanel', function() { it('below an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelYPosition(yPosition.BELOW); + .addPanelPosition(null, yPosition.BELOW); config['position'] = position; @@ -1239,7 +1256,7 @@ describe('$mdPanel', function() { it('offset to the left of an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelXPosition(xPosition.OFFSET_START); + .addPanelPosition(xPosition.OFFSET_START, null); config['position'] = position; @@ -1253,7 +1270,7 @@ describe('$mdPanel', function() { it('right aligned with an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelXPosition(xPosition.ALIGN_END); + .addPanelPosition(xPosition.ALIGN_END, null); config['position'] = position; @@ -1267,7 +1284,7 @@ describe('$mdPanel', function() { it('centered with an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelXPosition(xPosition.CENTER); + .addPanelPosition(xPosition.CENTER, null); config['position'] = position; @@ -1284,7 +1301,7 @@ describe('$mdPanel', function() { it('left aligned with an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelXPosition(xPosition.ALIGN_START); + .addPanelPosition(xPosition.ALIGN_START, null); config['position'] = position; @@ -1298,7 +1315,7 @@ describe('$mdPanel', function() { it('offset to the right of an element', function() { var position = mdPanelPosition .relativeTo(myButton) - .withPanelXPosition(xPosition.OFFSET_END); + .addPanelPosition(xPosition.OFFSET_END, null); config['position'] = position; @@ -1319,7 +1336,7 @@ describe('$mdPanel', function() { var expression = function() { mdPanelPosition .relativeTo(myButton) - .withPanelXPosition('fake-x-position'); + .addPanelPosition('fake-x-position', null); }; expect(expression).toThrow();