UNPKG

@angular2-material/core

Version:
165 lines (163 loc) 7.59 kB
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