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

update(panel): refactor positioning to allow multiple. #8211

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/components/panel/demoBasicUsage/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
123 changes: 73 additions & 50 deletions src/components/panel/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
*/

/**
Expand Down Expand Up @@ -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:
*
Expand All @@ -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:
*
Expand All @@ -485,6 +481,7 @@ angular
* H: CENTER
* I: ALIGN_BOTTOMS
* J: ABOVE
* @returns {MdPanelPosition}
*/

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1436,11 +1433,8 @@ function MdPanelPosition() {
/** @private {!Array<string>} */
this._translateY = [];

/** @private {string} */
this._xPosition = '';

/** @private {string} */
this._yPosition = '';
/** @private {!Array<{x:string, y:string}>} */
this._positions = [];
}


Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return true;? Or does the above line not need to return a boolean.

}
}

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(' | '));

};


Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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';
Expand All @@ -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;
Expand Down
51 changes: 34 additions & 17 deletions src/components/panel/panel.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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();
Expand Down