@angular2-material/core
Version:
Angular 2 Material core
165 lines (163 loc) • 7.59 kB
JavaScript
import { applyCssTransform } from '@angular2-material/core';
import { ConnectionPositionPair } from './connected-position';
/**
* A strategy for positioning overlays. Using this strategy, an overlay is given an
* implict position relative some origin element. The relative position is defined in terms of
* a point on the origin element that is connected to a point on the overlay element. For example,
* a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner
* of the overlay.
*/
export var ConnectedPositionStrategy = (function () {
function ConnectedPositionStrategy(_connectedTo, _originPos, _overlayPos, _viewportRuler) {
this._connectedTo = _connectedTo;
this._originPos = _originPos;
this._overlayPos = _overlayPos;
this._viewportRuler = _viewportRuler;
// TODO(jelbourn): set RTL to the actual value from the app.
/** Whether the we're dealing with an RTL context */
this._isRtl = false;
/** Ordered list of preferred positions, from most to least desirable. */
this._preferredPositions = [];
this._origin = this._connectedTo.nativeElement;
this.withFallbackPosition(_originPos, _overlayPos);
}
Object.defineProperty(ConnectedPositionStrategy.prototype, "positions", {
get: function () {
return this._preferredPositions;
},
enumerable: true,
configurable: true
});
/**
* Updates the position of the overlay element, using whichever preferred position relative
* to the origin fits on-screen.
* TODO: internal
*/
ConnectedPositionStrategy.prototype.apply = function (element) {
// We need the bounding rects for the origin and the overlay to determine how to position
// the overlay relative to the origin.
var originRect = this._origin.getBoundingClientRect();
var overlayRect = element.getBoundingClientRect();
// We use the viewport rect to determine whether a position would go off-screen.
var viewportRect = this._viewportRuler.getViewportRect();
var firstOverlayPoint = null;
// We want to place the overlay in the first of the preferred positions such that the
// overlay fits on-screen.
for (var _i = 0, _a = this._preferredPositions; _i < _a.length; _i++) {
var pos = _a[_i];
// Get the (x, y) point of connection on the origin, and then use that to get the
// (top, left) coordinate for the overlay at `pos`.
var originPoint = this._getOriginConnectionPoint(originRect, pos);
var overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos);
firstOverlayPoint = firstOverlayPoint || overlayPoint;
// If the overlay in the calculated position fits on-screen, put it there and we're done.
if (this._willOverlayFitWithinViewport(overlayPoint, overlayRect, viewportRect)) {
this._setElementPosition(element, overlayPoint);
return Promise.resolve(null);
}
}
// TODO(jelbourn): fallback behavior for when none of the preferred positions fit on-screen.
// For now, just stick it in the first position and let it go off-screen.
this._setElementPosition(element, firstOverlayPoint);
return Promise.resolve(null);
};
ConnectedPositionStrategy.prototype.withFallbackPosition = function (originPos, overlayPos) {
this._preferredPositions.push(new ConnectionPositionPair(originPos, overlayPos));
return this;
};
/**
* Gets the horizontal (x) "start" dimension based on whether the overlay is in an RTL context.
* @param rect
*/
ConnectedPositionStrategy.prototype._getStartX = function (rect) {
return this._isRtl ? rect.right : rect.left;
};
/**
* Gets the horizontal (x) "end" dimension based on whether the overlay is in an RTL context.
* @param rect
*/
ConnectedPositionStrategy.prototype._getEndX = function (rect) {
return this._isRtl ? rect.left : rect.right;
};
/**
* Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
* @param originRect
* @param pos
*/
ConnectedPositionStrategy.prototype._getOriginConnectionPoint = function (originRect, pos) {
var originStartX = this._getStartX(originRect);
var originEndX = this._getEndX(originRect);
var x;
if (pos.originX == 'center') {
x = originStartX + (originRect.width / 2);
}
else {
x = pos.originX == 'start' ? originStartX : originEndX;
}
var y;
if (pos.originY == 'center') {
y = originRect.top + (originRect.height / 2);
}
else {
y = pos.originY == 'top' ? originRect.top : originRect.bottom;
}
return { x: x, y: y };
};
/**
* Gets the (x, y) coordinate of the top-left corner of the overlay given a given position and
* origin point to which the overlay should be connected.
* @param originPoint
* @param overlayRect
* @param pos
*/
ConnectedPositionStrategy.prototype._getOverlayPoint = function (originPoint, overlayRect, pos) {
// Calculate the (overlayStartX, overlayStartY), the start of the potential overlay position
// relative to the origin point.
var overlayStartX;
if (pos.overlayX == 'center') {
overlayStartX = -overlayRect.width / 2;
}
else {
overlayStartX = pos.overlayX == 'start' ? 0 : -overlayRect.width;
}
var overlayStartY;
if (pos.overlayY == 'center') {
overlayStartY = -overlayRect.height / 2;
}
else {
overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height;
}
return {
x: originPoint.x + overlayStartX,
y: originPoint.y + overlayStartY
};
};
/**
* Gets whether the overlay positioned at the given point will fit on-screen.
* @param overlayPoint The top-left coordinate of the overlay.
* @param overlayRect Bounding rect of the overlay, used to get its size.
* @param viewportRect The bounding viewport.
*/
ConnectedPositionStrategy.prototype._willOverlayFitWithinViewport = function (overlayPoint, overlayRect, viewportRect) {
// TODO(jelbourn): probably also want some space between overlay edge and viewport edge.
return overlayPoint.x >= viewportRect.left &&
overlayPoint.x + overlayRect.width <= viewportRect.right &&
overlayPoint.y >= viewportRect.top &&
overlayPoint.y + overlayRect.height <= viewportRect.bottom;
};
/**
* Physically positions the overlay element to the given coordinate.
* @param element
* @param overlayPoint
*/
ConnectedPositionStrategy.prototype._setElementPosition = function (element, overlayPoint) {
var scrollPos = this._viewportRuler.getViewportScrollPosition();
var x = overlayPoint.x + scrollPos.left;
var y = overlayPoint.y + scrollPos.top;
// TODO(jelbourn): we don't want to always overwrite the transform property here,
// because it will need to be used for animations.
applyCssTransform(element, "translateX(" + x + "px) translateY(" + y + "px)");
};
return ConnectedPositionStrategy;
}());
//# sourceMappingURL=connected-position-strategy.js.map