@@ -2,10 +2,10 @@ import {PositionStrategy} from './position-strategy';
2
2
import { ElementRef } from '@angular/core' ;
3
3
import { ViewportRuler } from './viewport-ruler' ;
4
4
import {
5
- ConnectionPositionPair ,
6
- OriginConnectionPosition ,
7
- OverlayConnectionPosition ,
8
- ConnectedOverlayPositionChange
5
+ ConnectionPositionPair ,
6
+ OriginConnectionPosition ,
7
+ OverlayConnectionPosition ,
8
+ ConnectedOverlayPositionChange , ScrollableViewProperties
9
9
} from './connected-position' ;
10
10
import { Subject } from 'rxjs/Subject' ;
11
11
import { Observable } from 'rxjs/Observable' ;
@@ -35,7 +35,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
35
35
private _offsetY : number = 0 ;
36
36
37
37
/** The Scrollable containers that may cause the overlay's connectedTo element to be clipped */
38
- private scrollables : Scrollable [ ] = [ ] ;
38
+ private scrollables : Scrollable [ ] ;
39
39
40
40
/** Whether the we're dealing with an RTL context */
41
41
get _isRtl ( ) {
@@ -48,7 +48,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
48
48
/** The origin element against which the overlay will be positioned. */
49
49
private _origin : HTMLElement ;
50
50
51
- private _onPositionChange :
51
+ _onPositionChange :
52
52
Subject < ConnectedOverlayPositionChange > = new Subject < ConnectedOverlayPositionChange > ( ) ;
53
53
54
54
/** Emits an event when the connection point changes. */
@@ -106,8 +106,12 @@ export class ConnectedPositionStrategy implements PositionStrategy {
106
106
// If the overlay in the calculated position fits on-screen, put it there and we're done.
107
107
if ( overlayPoint . fitsInViewport ) {
108
108
this . _setElementPosition ( element , overlayPoint ) ;
109
- const isClipped = this . isOverlayElementClipped ( element ) ;
110
- this . _onPositionChange . next ( new ConnectedOverlayPositionChange ( pos , isClipped ) ) ;
109
+
110
+ // Notify that the position has been changed along with its change properties.
111
+ const scrollableViewProperties = this . getScrollableViewProperties ( element ) ;
112
+ const positionChange = new ConnectedOverlayPositionChange ( pos , scrollableViewProperties ) ;
113
+ this . _onPositionChange . next ( positionChange ) ;
114
+
111
115
return Promise . resolve ( null ) ;
112
116
} else if ( ! fallbackPoint || fallbackPoint . visibleArea < overlayPoint . visibleArea ) {
113
117
fallbackPoint = overlayPoint ;
@@ -262,16 +266,48 @@ export class ConnectedPositionStrategy implements PositionStrategy {
262
266
return { x, y, fitsInViewport, visibleArea} ;
263
267
}
264
268
265
- /** Whether the overlay element is clipped out of view of one of the scrollable containers. */
266
- private isOverlayElementClipped ( element : HTMLElement ) : boolean {
267
- const elementBounds = this . _getElementBounds ( element ) ;
268
- return this . scrollables . some ( ( scrollable : Scrollable ) => {
269
- const scrollingContainerBounds = this . _getElementBounds ( scrollable . getElementRef ( ) . nativeElement ) ;
269
+ /**
270
+ * Gets the view properties of the trigger and overlay, including whether they are clipped
271
+ * or completely outside the view of any of the strategy's scrollables.
272
+ */
273
+ private getScrollableViewProperties ( overlay : HTMLElement ) : ScrollableViewProperties {
274
+ const triggerBounds = this . _getElementBounds ( this . _connectedTo . nativeElement ) ;
275
+ const overlayBounds = this . _getElementBounds ( overlay ) ;
276
+ const scrollContainerBounds = this . scrollables . map ( ( scrollable : Scrollable ) => {
277
+ return this . _getElementBounds ( scrollable . getElementRef ( ) . nativeElement ) ;
278
+ } ) ;
270
279
271
- const clippedAbove = elementBounds . top < scrollingContainerBounds . top ;
272
- const clippedBelow = elementBounds . bottom > scrollingContainerBounds . bottom ;
273
- const clippedLeft = elementBounds . left < scrollingContainerBounds . left ;
274
- const clippedRight = elementBounds . right > scrollingContainerBounds . right ;
280
+ return {
281
+ isTriggerClipped : this . isElementClipped ( triggerBounds , scrollContainerBounds ) ,
282
+ isTriggerOutsideView : this . isElementOutsideView ( triggerBounds , scrollContainerBounds ) ,
283
+ isOverlayClipped : this . isElementClipped ( overlayBounds , scrollContainerBounds ) ,
284
+ isOverlayOutsideView : this . isElementOutsideView ( overlayBounds , scrollContainerBounds ) ,
285
+ } ;
286
+ }
287
+
288
+ /** Whether the element is completely out of the view of any of the containers. */
289
+ private isElementOutsideView (
290
+ elementBounds : ElementBoundingPositions ,
291
+ containersBounds : ElementBoundingPositions [ ] ) : boolean {
292
+ return containersBounds . some ( ( containerBounds : ElementBoundingPositions ) => {
293
+ const outsideAbove = elementBounds . bottom < containerBounds . top ;
294
+ const outsideBelow = elementBounds . top > containerBounds . bottom ;
295
+ const outsideLeft = elementBounds . right < containerBounds . left ;
296
+ const outsideRight = elementBounds . left > containerBounds . right ;
297
+
298
+ return outsideAbove || outsideBelow || outsideLeft || outsideRight ;
299
+ } ) ;
300
+ }
301
+
302
+ /** Whether the element is clipped by any of the containers. */
303
+ private isElementClipped (
304
+ elementBounds : ElementBoundingPositions ,
305
+ containersBounds : ElementBoundingPositions [ ] ) : boolean {
306
+ return containersBounds . some ( ( containerBounds : ElementBoundingPositions ) => {
307
+ const clippedAbove = elementBounds . top < containerBounds . top ;
308
+ const clippedBelow = elementBounds . bottom > containerBounds . bottom ;
309
+ const clippedLeft = elementBounds . left < containerBounds . left ;
310
+ const clippedRight = elementBounds . right > containerBounds . right ;
275
311
276
312
return clippedAbove || clippedBelow || clippedLeft || clippedRight ;
277
313
} ) ;
@@ -311,7 +347,7 @@ export class ConnectedPositionStrategy implements PositionStrategy {
311
347
interface Point {
312
348
x : number ;
313
349
y : number ;
314
- } ;
350
+ }
315
351
316
352
/**
317
353
* Expands the simple (x, y) coordinate by adding info about whether the
0 commit comments