UNPKG

7.59 kBJavaScriptView Raw
1import { applyCssTransform } from '@angular2-material/core';
2import { ConnectionPositionPair } from './connected-position';
3/**
4 * A strategy for positioning overlays. Using this strategy, an overlay is given an
5 * implict position relative some origin element. The relative position is defined in terms of
6 * a point on the origin element that is connected to a point on the overlay element. For example,
7 * a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner
8 * of the overlay.
9 */
10export var ConnectedPositionStrategy = (function () {
11 function ConnectedPositionStrategy(_connectedTo, _originPos, _overlayPos, _viewportRuler) {
12 this._connectedTo = _connectedTo;
13 this._originPos = _originPos;
14 this._overlayPos = _overlayPos;
15 this._viewportRuler = _viewportRuler;
16 // TODO(jelbourn): set RTL to the actual value from the app.
17 /** Whether the we're dealing with an RTL context */
18 this._isRtl = false;
19 /** Ordered list of preferred positions, from most to least desirable. */
20 this._preferredPositions = [];
21 this._origin = this._connectedTo.nativeElement;
22 this.withFallbackPosition(_originPos, _overlayPos);
23 }
24 Object.defineProperty(ConnectedPositionStrategy.prototype, "positions", {
25 get: function () {
26 return this._preferredPositions;
27 },
28 enumerable: true,
29 configurable: true
30 });
31 /**
32 * Updates the position of the overlay element, using whichever preferred position relative
33 * to the origin fits on-screen.
34 * TODO: internal
35 */
36 ConnectedPositionStrategy.prototype.apply = function (element) {
37 // We need the bounding rects for the origin and the overlay to determine how to position
38 // the overlay relative to the origin.
39 var originRect = this._origin.getBoundingClientRect();
40 var overlayRect = element.getBoundingClientRect();
41 // We use the viewport rect to determine whether a position would go off-screen.
42 var viewportRect = this._viewportRuler.getViewportRect();
43 var firstOverlayPoint = null;
44 // We want to place the overlay in the first of the preferred positions such that the
45 // overlay fits on-screen.
46 for (var _i = 0, _a = this._preferredPositions; _i < _a.length; _i++) {
47 var pos = _a[_i];
48 // Get the (x, y) point of connection on the origin, and then use that to get the
49 // (top, left) coordinate for the overlay at `pos`.
50 var originPoint = this._getOriginConnectionPoint(originRect, pos);
51 var overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos);
52 firstOverlayPoint = firstOverlayPoint || overlayPoint;
53 // If the overlay in the calculated position fits on-screen, put it there and we're done.
54 if (this._willOverlayFitWithinViewport(overlayPoint, overlayRect, viewportRect)) {
55 this._setElementPosition(element, overlayPoint);
56 return Promise.resolve(null);
57 }
58 }
59 // TODO(jelbourn): fallback behavior for when none of the preferred positions fit on-screen.
60 // For now, just stick it in the first position and let it go off-screen.
61 this._setElementPosition(element, firstOverlayPoint);
62 return Promise.resolve(null);
63 };
64 ConnectedPositionStrategy.prototype.withFallbackPosition = function (originPos, overlayPos) {
65 this._preferredPositions.push(new ConnectionPositionPair(originPos, overlayPos));
66 return this;
67 };
68 /**
69 * Gets the horizontal (x) "start" dimension based on whether the overlay is in an RTL context.
70 * @param rect
71 */
72 ConnectedPositionStrategy.prototype._getStartX = function (rect) {
73 return this._isRtl ? rect.right : rect.left;
74 };
75 /**
76 * Gets the horizontal (x) "end" dimension based on whether the overlay is in an RTL context.
77 * @param rect
78 */
79 ConnectedPositionStrategy.prototype._getEndX = function (rect) {
80 return this._isRtl ? rect.left : rect.right;
81 };
82 /**
83 * Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
84 * @param originRect
85 * @param pos
86 */
87 ConnectedPositionStrategy.prototype._getOriginConnectionPoint = function (originRect, pos) {
88 var originStartX = this._getStartX(originRect);
89 var originEndX = this._getEndX(originRect);
90 var x;
91 if (pos.originX == 'center') {
92 x = originStartX + (originRect.width / 2);
93 }
94 else {
95 x = pos.originX == 'start' ? originStartX : originEndX;
96 }
97 var y;
98 if (pos.originY == 'center') {
99 y = originRect.top + (originRect.height / 2);
100 }
101 else {
102 y = pos.originY == 'top' ? originRect.top : originRect.bottom;
103 }
104 return { x: x, y: y };
105 };
106 /**
107 * Gets the (x, y) coordinate of the top-left corner of the overlay given a given position and
108 * origin point to which the overlay should be connected.
109 * @param originPoint
110 * @param overlayRect
111 * @param pos
112 */
113 ConnectedPositionStrategy.prototype._getOverlayPoint = function (originPoint, overlayRect, pos) {
114 // Calculate the (overlayStartX, overlayStartY), the start of the potential overlay position
115 // relative to the origin point.
116 var overlayStartX;
117 if (pos.overlayX == 'center') {
118 overlayStartX = -overlayRect.width / 2;
119 }
120 else {
121 overlayStartX = pos.overlayX == 'start' ? 0 : -overlayRect.width;
122 }
123 var overlayStartY;
124 if (pos.overlayY == 'center') {
125 overlayStartY = -overlayRect.height / 2;
126 }
127 else {
128 overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height;
129 }
130 return {
131 x: originPoint.x + overlayStartX,
132 y: originPoint.y + overlayStartY
133 };
134 };
135 /**
136 * Gets whether the overlay positioned at the given point will fit on-screen.
137 * @param overlayPoint The top-left coordinate of the overlay.
138 * @param overlayRect Bounding rect of the overlay, used to get its size.
139 * @param viewportRect The bounding viewport.
140 */
141 ConnectedPositionStrategy.prototype._willOverlayFitWithinViewport = function (overlayPoint, overlayRect, viewportRect) {
142 // TODO(jelbourn): probably also want some space between overlay edge and viewport edge.
143 return overlayPoint.x >= viewportRect.left &&
144 overlayPoint.x + overlayRect.width <= viewportRect.right &&
145 overlayPoint.y >= viewportRect.top &&
146 overlayPoint.y + overlayRect.height <= viewportRect.bottom;
147 };
148 /**
149 * Physically positions the overlay element to the given coordinate.
150 * @param element
151 * @param overlayPoint
152 */
153 ConnectedPositionStrategy.prototype._setElementPosition = function (element, overlayPoint) {
154 var scrollPos = this._viewportRuler.getViewportScrollPosition();
155 var x = overlayPoint.x + scrollPos.left;
156 var y = overlayPoint.y + scrollPos.top;
157 // TODO(jelbourn): we don't want to always overwrite the transform property here,
158 // because it will need to be used for animations.
159 applyCssTransform(element, "translateX(" + x + "px) translateY(" + y + "px)");
160 };
161 return ConnectedPositionStrategy;
162}());
163
164//# sourceMappingURL=connected-position-strategy.js.map