license import { Direction , Directionality } from '@angular/cdk/bidi' ; import { BooleanInput , coerceBooleanProperty } from '@angular/cdk/coercion' ; import { ESCAPE , hasModifierKey } from '@angular/cdk/keycodes' ; import { TemplatePortal } from '@angular/cdk/portal' ; import { Directive , ElementRef , EventEmitter , Inject , InjectionToken , Input , OnChanges , OnDestroy , Optional , Output , SimpleChanges , TemplateRef , ViewContainerRef , } from '@angular/core' ; import { Subscription } from 'rxjs' ; import { Overlay } from './overlay' ; import { OverlayConfig } from './overlay-config' ; import { OverlayRef } from './overlay-ref' ; import { ConnectedOverlayPositionChange } from './position/connected-position' ; import { ConnectedPosition , FlexibleConnectedPositionStrategy , } from './position/flexible-connected-position-strategy' ; import { RepositionScrollStrategy , RepositionScrollStrategyConfig , ScrollStrategy , } from './scroll/index' ; /** Default set of positions for the overlay. Follows the behavior of a dropdown. */ const defaultPositionList = [ { originX : 'start' , originY : 'bottom' , overlayX : 'start' , overlayY : 'top' } , { originX : 'start' , originY : 'top' , overlayX : 'start' , overlayY : 'bottom' } , { originX : 'end' , originY : 'top' , overlayX : 'end' , overlayY : 'bottom' } , { originX : 'end' , originY : 'bottom' , overlayX : 'end' , overlayY : 'top' } ] ; /** Injection token that determines the scroll handling while the connected overlay is open. */ export const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY = new InjectionToken < ( ) => ScrollStrategy > ( 'cdk-connected-overlay-scroll-strategy' ) ; docs-private deprecated breaking-change export function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_FACTORY ( overlay ) { return ( config ? ) => overlay . scrollStrategies . reposition ( config ) ; } /**
 * Directive applied to an element to make it usable as an origin for an Overlay using a
 * ConnectedPositionStrategy.
 */ @ Directive ( { selector : '[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]' , exportAs : 'cdkOverlayOrigin' , } ) export class CdkOverlayOrigin { constructor ( /** Reference to the element on which the directive is applied. */ public elementRef ) { } } /**
 * Directive to facilitate declarative creation of an
 * Overlay using a FlexibleConnectedPositionStrategy.
 */ @ Directive ( { selector : '[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]' , exportAs : 'cdkConnectedOverlay' } ) export class CdkConnectedOverlay implements OnDestroy , OnChanges { private _overlayRef : OverlayRef ; private _templatePortal : TemplatePortal ; private _hasBackdrop = false ; private _lockPosition = false ; private _growAfterOpen = false ; private _flexibleDimensions = false ; private _push = false ; private _backdropSubscription = Subscription . EMPTY ; private _offsetX : number ; private _offsetY : number ; private _position : FlexibleConnectedPositionStrategy ; private _scrollStrategyFactory : ( ) => ScrollStrategy ; /** Origin for the connected overlay. */ @ Input ( 'cdkConnectedOverlayOrigin' ) origin : CdkOverlayOrigin ; /** Registered connected position pairs. */ @ Input ( 'cdkConnectedOverlayPositions' ) positions : ConnectedPosition [ ] ; /**
     * This input overrides the positions input if specified. It lets users pass
     * in arbitrary positioning strategies.
     */ @ Input ( 'cdkConnectedOverlayPositionStrategy' ) positionStrategy : FlexibleConnectedPositionStrategy ; /** The offset in pixels for the overlay connection point on the x-axis */ @ Input ( 'cdkConnectedOverlayOffsetX' ) get offsetX ( ) : number { return this . _offsetX ; } set offsetX ( offsetX ) { this . _offsetX = offsetX ; if ( this . _position ) { this . _updatePositionStrategy ( this . _position ) ; } } /** The offset in pixels for the overlay connection point on the y-axis */ @ Input ( 'cdkConnectedOverlayOffsetY' ) get offsetY ( ) { return this . _offsetY ; } set offsetY ( offsetY ) { this . _offsetY = offsetY ; if ( this . _position ) { this . _updatePositionStrategy ( this . _position ) ; } } /** The width of the overlay panel. */ @ Input ( 'cdkConnectedOverlayWidth' ) width : number | string ; /** The height of the overlay panel. */ @ Input ( 'cdkConnectedOverlayHeight' ) height : number | string ; /** The min width of the overlay panel. */ @ Input ( 'cdkConnectedOverlayMinWidth' ) minWidth : number | string ; /** The min height of the overlay panel. */ @ Input ( 'cdkConnectedOverlayMinHeight' ) minHeight : number | string ; /** The custom class to be set on the backdrop element. */ @ Input ( 'cdkConnectedOverlayBackdropClass' ) backdropClass : string ; /** The custom class to add to the overlay pane element. */ @ Input ( 'cdkConnectedOverlayPanelClass' ) panelClass : string | string [ ] ; /** Margin between the overlay and the viewport edges. */ @ Input ( 'cdkConnectedOverlayViewportMargin' ) viewportMargin : number = 0 ; /** Strategy to be used when handling scroll events while the overlay is open. */ @ Input ( 'cdkConnectedOverlayScrollStrategy' ) scrollStrategy : ScrollStrategy ; /** Whether the overlay is open. */ @ Input ( 'cdkConnectedOverlayOpen' ) open : boolean = false ; /** CSS selector which to set the transform origin. */ @ Input ( 'cdkConnectedOverlayTransformOriginOn' ) transformOriginSelector : string ; /** Whether or not the overlay should attach a backdrop. */ @ Input ( 'cdkConnectedOverlayHasBackdrop' ) get hasBackdrop ( ) { return this . _hasBackdrop ; } set hasBackdrop ( value ) { this . _hasBackdrop = coerceBooleanProperty ( value ) ; } /** Whether or not the overlay should be locked when scrolling. */ @ Input ( 'cdkConnectedOverlayLockPosition' ) get lockPosition ( ) { return this . _lockPosition ; } set lockPosition ( value ) { this . _lockPosition = coerceBooleanProperty ( value ) ; } /** Whether the overlay's width and height can be constrained to fit within the viewport. */ @ Input ( 'cdkConnectedOverlayFlexibleDimensions' ) get flexibleDimensions ( ) { return this . _flexibleDimensions ; } set flexibleDimensions ( value ) { this . _flexibleDimensions = coerceBooleanProperty ( value ) ; } /** Whether the overlay can grow after the initial open when flexible positioning is turned on. */ @ Input ( 'cdkConnectedOverlayGrowAfterOpen' ) get growAfterOpen ( ) { return this . _growAfterOpen ; } set growAfterOpen ( value ) { this . _growAfterOpen = coerceBooleanProperty ( value ) ; } /** Whether the overlay can be pushed on-screen if none of the provided positions fit. */ @ Input ( 'cdkConnectedOverlayPush' ) get push ( ) { return this . _push ; } set push ( value ) { this . _push = coerceBooleanProperty ( value ) ; } /** Event emitted when the backdrop is clicked. */ @ Output ( ) backdropClick = new EventEmitter < MouseEvent > ( ) ; /** Event emitted when the position has changed. */ @ Output ( ) positionChange = new EventEmitter < ConnectedOverlayPositionChange > ( ) ; /** Event emitted when the overlay has been attached. */ @ Output ( ) attach = new EventEmitter < void > ( ) ; /** Event emitted when the overlay has been detached. */ @ Output ( ) detach = new EventEmitter < void > ( ) ; /** Emits when there are keyboard events that are targeted at the overlay. */ @ Output ( ) overlayKeydown = new EventEmitter < KeyboardEvent > ( ) ; constructor ( private _overlay , templateRef , viewContainerRef , @ Inject ( CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY ) scrollStrategyFactory , @ Optional ( ) private _dir ) { this . _templatePortal = new TemplatePortal ( templateRef , viewContainerRef ) ; this . _scrollStrategyFactory = scrollStrategyFactory ; this . scrollStrategy = this . _scrollStrategyFactory ( ) ; } /** The associated overlay reference. */ get overlayRef ( ) : OverlayRef { return this . _overlayRef ; } /** The element's layout direction. */ get dir ( ) : Direction { return this . _dir ? this . _dir . value : 'ltr' ; } ngOnDestroy ( ) { if ( this . _overlayRef ) { this . _overlayRef . dispose ( ) ; } this . _backdropSubscription . unsubscribe ( ) ; } ngOnChanges ( changes ) { if ( this . _position ) { this . _updatePositionStrategy ( this . _position ) ; this . _overlayRef . updateSize ( { width : this . width , minWidth : this . minWidth , height : this . height , minHeight : this . minHeight , } ) ; if ( changes [ 'origin' ] && this . open ) { this . _position . apply ( ) ; } } if ( changes [ 'open' ] ) { this . open ? this . _attachOverlay ( ) : this . _detachOverlay ( ) ; } } /** Creates an overlay */ private _createOverlay ( ) { if ( ! this . positions || ! this . positions . length ) { this . positions = defaultPositionList ; } this . _overlayRef = this . _overlay . create ( this . _buildConfig ( ) ) ; this . _overlayRef . keydownEvents ( ) . subscribe ( ( event ) => { this . overlayKeydown . next ( event ) ; if ( event . keyCode === ESCAPE && ! hasModifierKey ( event ) ) { event . preventDefault ( ) ; this . _detachOverlay ( ) ; } } ) ; } /** Builds the overlay config based on the directive's inputs */ private _buildConfig ( ) { const positionStrategy = this . _position = this . positionStrategy || this . _createPositionStrategy ( ) ; const overlayConfig = new OverlayConfig ( { direction : this . _dir , positionStrategy , scrollStrategy : this . scrollStrategy , hasBackdrop : this . hasBackdrop } ) ; if ( this . width || this . width === 0 ) { overlayConfig . width = this . width ; } if ( this . height || this . height === 0 ) { overlayConfig . height = this . height ; } if ( this . minWidth || this . minWidth === 0 ) { overlayConfig . minWidth = this . minWidth ; } if ( this . minHeight || this . minHeight === 0 ) { overlayConfig . minHeight = this . minHeight ; } if ( this . backdropClass ) { overlayConfig . backdropClass = this . backdropClass ; } if ( this . panelClass ) { overlayConfig . panelClass = this . panelClass ; } return overlayConfig ; } /** Updates the state of a position strategy, based on the values of the directive inputs. */ private _updatePositionStrategy ( positionStrategy ) { const positions = this . positions . map ( currentPosition => ( { originX : currentPosition . originX , originY : currentPosition . originY , overlayX : currentPosition . overlayX , overlayY : currentPosition . overlayY , offsetX : currentPosition . offsetX || this . offsetX , offsetY : currentPosition . offsetY || this . offsetY , panelClass : currentPosition . panelClass || undefined , } ) ) ; return positionStrategy . setOrigin ( this . origin . elementRef ) . withPositions ( positions ) . withFlexibleDimensions ( this . flexibleDimensions ) . withPush ( this . push ) . withGrowAfterOpen ( this . growAfterOpen ) . withViewportMargin ( this . viewportMargin ) . withLockedPosition ( this . lockPosition ) . withTransformOriginOn ( this . transformOriginSelector ) ; } /** Returns the position strategy of the overlay to be set on the overlay config */ private _createPositionStrategy ( ) { const strategy = this . _overlay . position ( ) . flexibleConnectedTo ( this . origin . elementRef ) ; this . _updatePositionStrategy ( strategy ) ; strategy . positionChanges . subscribe ( p => this . positionChange . emit ( p ) ) ; return strategy ; } /** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */ private _attachOverlay ( ) { if ( ! this . _overlayRef ) { this . _createOverlay ( ) ; } else { this . _overlayRef . getConfig ( ) . hasBackdrop = this . hasBackdrop ; } if ( ! this . _overlayRef . hasAttached ( ) ) { this . _overlayRef . attach ( this . _templatePortal ) ; this . attach . emit ( ) ; } if ( this . hasBackdrop ) { this . _backdropSubscription = this . _overlayRef . backdropClick ( ) . subscribe ( event => { this . backdropClick . emit ( event ) ; } ) ; } else { this . _backdropSubscription . unsubscribe ( ) ; } } /** Detaches the overlay and unsubscribes to backdrop clicks if backdrop exists */ private _detachOverlay ( ) { if ( this . _overlayRef ) { this . _overlayRef . detach ( ) ; this . detach . emit ( ) ; } this . _backdropSubscription . unsubscribe ( ) ; } static ngAcceptInputType_hasBackdrop : BooleanInput ; static ngAcceptInputType_lockPosition : BooleanInput ; static ngAcceptInputType_flexibleDimensions : BooleanInput ; static ngAcceptInputType_growAfterOpen : BooleanInput ; static ngAcceptInputType_push : BooleanInput ; } docs-private export function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY ( overlay ) { return ( ) => overlay . scrollStrategies . reposition ( ) ; } docs-private export const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { provide : CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY , deps : [ Overlay ] , useFactory : CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY , } ;