@@ -13,8 +13,8 @@ import {
1313 Overlay ,
1414 OverlayConfig ,
1515 OverlayRef ,
16- PositionStrategy ,
1716 ScrollStrategy ,
17+ FlexibleConnectedPositionStrategy ,
1818} from '@angular/cdk/overlay' ;
1919import { ComponentPortal , ComponentType } from '@angular/cdk/portal' ;
2020import { DOCUMENT } from '@angular/common' ;
@@ -36,6 +36,8 @@ import {
3636 ViewContainerRef ,
3737 ViewEncapsulation ,
3838 ChangeDetectorRef ,
39+ OnChanges ,
40+ SimpleChanges ,
3941} from '@angular/core' ;
4042import {
4143 CanColor ,
@@ -72,6 +74,12 @@ export const MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {
7274 useFactory : MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY ,
7375} ;
7476
77+ /** Possible positions for the datepicker dropdown along the X axis. */
78+ export type DatepickerDropdownPositionX = 'start' | 'end' ;
79+
80+ /** Possible positions for the datepicker dropdown along the Y axis. */
81+ export type DatepickerDropdownPositionY = 'above' | 'below' ;
82+
7583// Boilerplate for applying mixins to MatDatepickerContent.
7684/** @docs -private */
7785class MatDatepickerContentBase {
@@ -164,7 +172,7 @@ export class MatDatepickerContent<D> extends _MatDatepickerContentMixinBase
164172 changeDetection : ChangeDetectionStrategy . OnPush ,
165173 encapsulation : ViewEncapsulation . None ,
166174} )
167- export class MatDatepicker < D > implements OnDestroy , CanColor {
175+ export class MatDatepicker < D > implements OnDestroy , CanColor , OnChanges {
168176 private _scrollStrategy : ( ) => ScrollStrategy ;
169177
170178 /** An input indicating the type of the custom header component for the calendar, if set. */
@@ -223,6 +231,14 @@ export class MatDatepicker<D> implements OnDestroy, CanColor {
223231 }
224232 private _disabled : boolean ;
225233
234+ /** Preferred position of the datepicker in the X axis. */
235+ @Input ( )
236+ xPosition : DatepickerDropdownPositionX = 'start' ;
237+
238+ /** Preferred position of the datepicker in the Y axis. */
239+ @Input ( )
240+ yPosition : DatepickerDropdownPositionY = 'below' ;
241+
226242 /**
227243 * Emits selected year in multiyear view.
228244 * This doesn't imply a change on the selected date.
@@ -315,6 +331,19 @@ export class MatDatepicker<D> implements OnDestroy, CanColor {
315331 this . _scrollStrategy = scrollStrategy ;
316332 }
317333
334+ ngOnChanges ( changes : SimpleChanges ) {
335+ const positionChange = changes [ 'xPosition' ] || changes [ 'yPosition' ] ;
336+
337+ if ( positionChange && ! positionChange . firstChange && this . _popupRef ) {
338+ this . _setConnectedPositions (
339+ this . _popupRef . getConfig ( ) . positionStrategy as FlexibleConnectedPositionStrategy ) ;
340+
341+ if ( this . opened ) {
342+ this . _popupRef . updatePosition ( ) ;
343+ }
344+ }
345+ }
346+
318347 ngOnDestroy ( ) {
319348 this . _destroyPopup ( ) ;
320349 this . close ( ) ;
@@ -471,8 +500,15 @@ export class MatDatepicker<D> implements OnDestroy, CanColor {
471500
472501 /** Create the popup. */
473502 private _createPopup ( ) : void {
503+ const positionStrategy = this . _overlay . position ( )
504+ . flexibleConnectedTo ( this . _datepickerInput . getConnectedOverlayOrigin ( ) )
505+ . withTransformOriginOn ( '.mat-datepicker-content' )
506+ . withFlexibleDimensions ( false )
507+ . withViewportMargin ( 8 )
508+ . withLockedPosition ( ) ;
509+
474510 const overlayConfig = new OverlayConfig ( {
475- positionStrategy : this . _createPopupPositionStrategy ( ) ,
511+ positionStrategy : this . _setConnectedPositions ( positionStrategy ) ,
476512 hasBackdrop : true ,
477513 backdropClass : 'mat-overlay-transparent-backdrop' ,
478514 direction : this . _dir ,
@@ -508,40 +544,39 @@ export class MatDatepicker<D> implements OnDestroy, CanColor {
508544 }
509545 }
510546
511- /** Create the popup PositionStrategy. */
512- private _createPopupPositionStrategy ( ) : PositionStrategy {
513- return this . _overlay . position ( )
514- . flexibleConnectedTo ( this . _datepickerInput . getConnectedOverlayOrigin ( ) )
515- . withTransformOriginOn ( '.mat-datepicker-content' )
516- . withFlexibleDimensions ( false )
517- . withViewportMargin ( 8 )
518- . withLockedPosition ( )
519- . withPositions ( [
520- {
521- originX : 'start' ,
522- originY : 'bottom' ,
523- overlayX : 'start' ,
524- overlayY : 'top'
525- } ,
526- {
527- originX : 'start' ,
528- originY : 'top' ,
529- overlayX : 'start' ,
530- overlayY : 'bottom'
531- } ,
532- {
533- originX : 'end' ,
534- originY : 'bottom' ,
535- overlayX : 'end' ,
536- overlayY : 'top'
537- } ,
538- {
539- originX : 'end' ,
540- originY : 'top' ,
541- overlayX : 'end' ,
542- overlayY : 'bottom'
543- }
544- ] ) ;
547+ /** Sets the positions of the datepicker in dropdown mode based on the current configuration. */
548+ private _setConnectedPositions ( strategy : FlexibleConnectedPositionStrategy ) {
549+ const primaryX = this . xPosition === 'end' ? 'end' : 'start' ;
550+ const secondaryX = primaryX === 'start' ? 'end' : 'start' ;
551+ const primaryY = this . yPosition === 'above' ? 'bottom' : 'top' ;
552+ const secondaryY = primaryY === 'top' ? 'bottom' : 'top' ;
553+
554+ return strategy . withPositions ( [
555+ {
556+ originX : primaryX ,
557+ originY : secondaryY ,
558+ overlayX : primaryX ,
559+ overlayY : primaryY
560+ } ,
561+ {
562+ originX : primaryX ,
563+ originY : primaryY ,
564+ overlayX : primaryX ,
565+ overlayY : secondaryY
566+ } ,
567+ {
568+ originX : secondaryX ,
569+ originY : secondaryY ,
570+ overlayX : secondaryX ,
571+ overlayY : primaryY
572+ } ,
573+ {
574+ originX : secondaryX ,
575+ originY : primaryY ,
576+ overlayX : secondaryX ,
577+ overlayY : secondaryY
578+ }
579+ ] ) ;
545580 }
546581
547582 /**
0 commit comments