@@ -15,6 +15,7 @@ import {
1515 ScrollingVisibility ,
1616 validateHorizontalPosition ,
1717 validateVerticalPosition ,
18+ ViewportMargin ,
1819} from './connected-position' ;
1920import { Observable , Subscription , Subject } from 'rxjs' ;
2021import { isElementScrolledOutsideView , isElementClippedByScrolling } from './scroll-clip' ;
@@ -106,8 +107,8 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
106107 /** Cached container dimensions */
107108 private _containerRect : Dimensions ;
108109
109- /** Amount of space that must be maintained between the overlay and the edge of the viewport. */
110- private _viewportMargin = 0 ;
110+ /** Amount of space that must be maintained between the overlay and the right edge of the viewport. */
111+ private _viewportMargin : ViewportMargin = 0 ;
111112
112113 /** The Scrollable containers used to check scrollable view properties on position change. */
113114 private _scrollables : CdkScrollable [ ] = [ ] ;
@@ -429,10 +430,11 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
429430 }
430431
431432 /**
432- * Sets a minimum distance the overlay may be positioned to the edge of the viewport.
433- * @param margin Required margin between the overlay and the viewport edge in pixels.
433+ * Sets a minimum distance the overlay may be positioned from the bottom edge of the viewport.
434+ * @param margin Required margin between the overlay and the viewport.
435+ * It can be a number to be applied to all directions, or an object to supply different values for each direction.
434436 */
435- withViewportMargin ( margin : number ) : this {
437+ withViewportMargin ( margin : ViewportMargin ) : this {
436438 this . _viewportMargin = margin ;
437439 return this ;
438440 }
@@ -700,13 +702,17 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
700702 if ( overlay . width <= viewport . width ) {
701703 pushX = overflowLeft || - overflowRight ;
702704 } else {
703- pushX = start . x < this . _viewportMargin ? viewport . left - scrollPosition . left - start . x : 0 ;
705+ pushX =
706+ start . x < this . _getViewportMarginStart ( )
707+ ? viewport . left - scrollPosition . left - start . x
708+ : 0 ;
704709 }
705710
706711 if ( overlay . height <= viewport . height ) {
707712 pushY = overflowTop || - overflowBottom ;
708713 } else {
709- pushY = start . y < this . _viewportMargin ? viewport . top - scrollPosition . top - start . y : 0 ;
714+ pushY =
715+ start . y < this . _getViewportMarginTop ( ) ? viewport . top - scrollPosition . top - start . y : 0 ;
710716 }
711717
712718 this . _previousPushAmount = { x : pushX , y : pushY } ;
@@ -795,13 +801,14 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
795801 if ( position . overlayY === 'top' ) {
796802 // Overlay is opening "downward" and thus is bound by the bottom viewport edge.
797803 top = origin . y ;
798- height = viewport . height - top + this . _viewportMargin ;
804+ height = viewport . height - top + this . _getViewportMarginBottom ( ) ;
799805 } else if ( position . overlayY === 'bottom' ) {
800806 // Overlay is opening "upward" and thus is bound by the top viewport edge. We need to add
801807 // the viewport margin back in, because the viewport rect is narrowed down to remove the
802808 // margin, whereas the `origin` position is calculated based on its `DOMRect`.
803- bottom = viewport . height - origin . y + this . _viewportMargin * 2 ;
804- height = viewport . height - bottom + this . _viewportMargin ;
809+ bottom =
810+ viewport . height - origin . y + this . _getViewportMarginTop ( ) + this . _getViewportMarginBottom ( ) ;
811+ height = viewport . height - bottom + this . _getViewportMarginTop ( ) ;
805812 } else {
806813 // If neither top nor bottom, it means that the overlay is vertically centered on the
807814 // origin point. Note that we want the position relative to the viewport, rather than
@@ -833,11 +840,12 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
833840 let width : number , left : number , right : number ;
834841
835842 if ( isBoundedByLeftViewportEdge ) {
836- right = viewport . width - origin . x + this . _viewportMargin * 2 ;
837- width = origin . x - this . _viewportMargin ;
843+ right =
844+ viewport . width - origin . x + this . _getViewportMarginStart ( ) + this . _getViewportMarginEnd ( ) ;
845+ width = origin . x - this . _getViewportMarginStart ( ) ;
838846 } else if ( isBoundedByRightViewportEdge ) {
839847 left = origin . x ;
840- width = viewport . right - origin . x ;
848+ width = viewport . right - origin . x - this . _getViewportMarginEnd ( ) ;
841849 } else {
842850 // If neither start nor end, it means that the overlay is horizontally centered on the
843851 // origin point. Note that we want the position relative to the viewport, rather than
@@ -1116,12 +1124,12 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
11161124 const scrollPosition = this . _viewportRuler . getViewportScrollPosition ( ) ;
11171125
11181126 return {
1119- top : scrollPosition . top + this . _viewportMargin ,
1120- left : scrollPosition . left + this . _viewportMargin ,
1121- right : scrollPosition . left + width - this . _viewportMargin ,
1122- bottom : scrollPosition . top + height - this . _viewportMargin ,
1123- width : width - 2 * this . _viewportMargin ,
1124- height : height - 2 * this . _viewportMargin ,
1127+ top : scrollPosition . top + this . _getViewportMarginTop ( ) ,
1128+ left : scrollPosition . left + this . _getViewportMarginStart ( ) ,
1129+ right : scrollPosition . left + width - this . _getViewportMarginEnd ( ) ,
1130+ bottom : scrollPosition . top + height - this . _getViewportMarginBottom ( ) ,
1131+ width : width - this . _getViewportMarginStart ( ) - this . _getViewportMarginEnd ( ) ,
1132+ height : height - this . _getViewportMarginTop ( ) - this . _getViewportMarginBottom ( ) ,
11251133 } ;
11261134 }
11271135
@@ -1186,6 +1194,42 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
11861194 }
11871195 }
11881196
1197+ /**
1198+ * Returns either the _viewportMargin directly (if it is a number) or its 'start' value.
1199+ * @private
1200+ */
1201+ private _getViewportMarginStart ( ) : number {
1202+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1203+ return this . _viewportMargin ?. start ?? 0 ;
1204+ }
1205+
1206+ /**
1207+ * Returns either the _viewportMargin directly (if it is a number) or its 'end' value.
1208+ * @private
1209+ */
1210+ private _getViewportMarginEnd ( ) : number {
1211+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1212+ return this . _viewportMargin ?. end ?? 0 ;
1213+ }
1214+
1215+ /**
1216+ * Returns either the _viewportMargin directly (if it is a number) or its 'top' value.
1217+ * @private
1218+ */
1219+ private _getViewportMarginTop ( ) : number {
1220+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1221+ return this . _viewportMargin ?. top ?? 0 ;
1222+ }
1223+
1224+ /**
1225+ * Returns either the _viewportMargin directly (if it is a number) or its 'bottom' value.
1226+ * @private
1227+ */
1228+ private _getViewportMarginBottom ( ) : number {
1229+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1230+ return this . _viewportMargin ?. bottom ?? 0 ;
1231+ }
1232+
11891233 /** Returns the DOMRect of the current origin. */
11901234 private _getOriginRect ( ) : Dimensions {
11911235 const origin = this . _origin ;
0 commit comments