@@ -19,7 +19,7 @@ import {
1919import { Observable , Subscription , Subject , Observer } from 'rxjs' ;
2020import { OverlayReference } from '../overlay-reference' ;
2121import { isElementScrolledOutsideView , isElementClippedByScrolling } from './scroll-clip' ;
22- import { coerceCssPixelValue , coerceArray , coerceElement } from '@angular/cdk/coercion' ;
22+ import { coerceCssPixelValue , coerceArray } from '@angular/cdk/coercion' ;
2323import { Platform } from '@angular/cdk/platform' ;
2424import { OverlayContainer } from '../overlay-container' ;
2525
@@ -29,6 +29,9 @@ import {OverlayContainer} from '../overlay-container';
2929/** Class to be added to the overlay bounding box. */
3030const boundingBoxClass = 'cdk-overlay-connected-position-bounding-box' ;
3131
32+ /** Possible values that can be set as the origin of a FlexibleConnectedPositionStrategy. */
33+ export type FlexibleConnectedPositionStrategyOrigin = ElementRef | HTMLElement | Point ;
34+
3235/**
3336 * A strategy for positioning overlays. Using this strategy, an overlay is given an
3437 * implicit position relative some origin element. The relative position is defined in terms of
@@ -80,7 +83,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
8083 _preferredPositions : ConnectionPositionPair [ ] = [ ] ;
8184
8285 /** The origin element against which the overlay will be positioned. */
83- private _origin : HTMLElement ;
86+ private _origin : FlexibleConnectedPositionStrategyOrigin ;
8487
8588 /** The overlay pane element. */
8689 private _pane : HTMLElement ;
@@ -139,7 +142,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
139142 }
140143
141144 constructor (
142- connectedTo : ElementRef | HTMLElement ,
145+ connectedTo : FlexibleConnectedPositionStrategyOrigin ,
143146 private _viewportRuler : ViewportRuler ,
144147 private _document : Document ,
145148 // @breaking -change 8.0.0 `_platform` and `_overlayContainer` parameters to be made required.
@@ -211,7 +214,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
211214 // the overlay relative to the origin.
212215 // We use the viewport rect to determine whether a position would go off-screen.
213216 this . _viewportRect = this . _getNarrowedViewportRect ( ) ;
214- this . _originRect = this . _origin . getBoundingClientRect ( ) ;
217+ this . _originRect = this . _getOriginRect ( ) ;
215218 this . _overlayRect = this . _pane . getBoundingClientRect ( ) ;
216219
217220 const originRect = this . _originRect ;
@@ -350,7 +353,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
350353 */
351354 reapplyLastPosition ( ) : void {
352355 if ( ! this . _isDisposed && ( ! this . _platform || this . _platform . isBrowser ) ) {
353- this . _originRect = this . _origin . getBoundingClientRect ( ) ;
356+ this . _originRect = this . _getOriginRect ( ) ;
354357 this . _overlayRect = this . _pane . getBoundingClientRect ( ) ;
355358 this . _viewportRect = this . _getNarrowedViewportRect ( ) ;
356359
@@ -427,11 +430,14 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
427430 }
428431
429432 /**
430- * Sets the origin element, relative to which to position the overlay.
431- * @param origin Reference to the new origin element.
433+ * Sets the origin, relative to which to position the overlay.
434+ * Using an element origin is useful for building components that need to be positioned
435+ * relatively to a trigger (e.g. dropdown menus or tooltips), whereas using a point can be
436+ * used for cases like contextual menus which open relative to the user's pointer.
437+ * @param origin Reference to the new origin.
432438 */
433- setOrigin ( origin : ElementRef | HTMLElement ) : this {
434- this . _origin = coerceElement ( origin ) ;
439+ setOrigin ( origin : FlexibleConnectedPositionStrategyOrigin ) : this {
440+ this . _origin = origin ;
435441 return this ;
436442 }
437443
@@ -988,7 +994,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
988994 */
989995 private _getScrollVisibility ( ) : ScrollingVisibility {
990996 // Note: needs fresh rects since the position could've changed.
991- const originBounds = this . _origin . getBoundingClientRect ( ) ;
997+ const originBounds = this . _getOriginRect ( ) ;
992998 const overlayBounds = this . _pane . getBoundingClientRect ( ) ;
993999
9941000 // TODO(jelbourn): instead of needing all of the client rects for these scrolling containers
@@ -1090,6 +1096,29 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
10901096 this . _appliedPanelClasses = [ ] ;
10911097 }
10921098 }
1099+
1100+ /** Returns the ClientRect of the current origin. */
1101+ private _getOriginRect ( ) : ClientRect {
1102+ const origin = this . _origin ;
1103+
1104+ if ( origin instanceof ElementRef ) {
1105+ return origin . nativeElement . getBoundingClientRect ( ) ;
1106+ }
1107+
1108+ if ( origin instanceof HTMLElement ) {
1109+ return origin . getBoundingClientRect ( ) ;
1110+ }
1111+
1112+ // If the origin is a point, return a client rect as if it was a 0x0 element at the point.
1113+ return {
1114+ top : origin . y ,
1115+ bottom : origin . y ,
1116+ left : origin . x ,
1117+ right : origin . x ,
1118+ height : 0 ,
1119+ width : 0
1120+ } ;
1121+ }
10931122}
10941123
10951124/** A simple (x, y) coordinate. */
0 commit comments