@@ -22,6 +22,7 @@ import {coerceCssPixelValue, coerceArray} from '@angular/cdk/coercion';
2222import { Platform } from '@angular/cdk/platform' ;
2323import { OverlayContainer } from '../overlay-container' ;
2424import { OverlayRef } from '../overlay-ref' ;
25+ import { ViewportMargin } from './viewport-margin' ;
2526
2627// TODO: refactor clipping detection into a separate thing (part of scrolling module)
2728// TODO: doesn't handle both flexible width and height when it has to scroll along both axis.
@@ -88,8 +89,8 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
8889 /** Cached container dimensions */
8990 private _containerRect : Dimensions ;
9091
91- /** Amount of space that must be maintained between the overlay and the edge of the viewport. */
92- private _viewportMargin = 0 ;
92+ /** Amount of space that must be maintained between the overlay and the right edge of the viewport. */
93+ private _viewportMargin : ViewportMargin = 0 ;
9394
9495 /** The Scrollable containers used to check scrollable view properties on position change. */
9596 private _scrollables : CdkScrollable [ ] = [ ] ;
@@ -411,10 +412,11 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
411412 }
412413
413414 /**
414- * Sets a minimum distance the overlay may be positioned to the edge of the viewport.
415- * @param margin Required margin between the overlay and the viewport edge in pixels.
415+ * Sets a minimum distance the overlay may be positioned from the bottom edge of the viewport.
416+ * @param margin Required margin between the overlay and the viewport.
417+ * It can be a number to be applied to all directions, or an object to supply different values for each direction.
416418 */
417- withViewportMargin ( margin : number ) : this {
419+ withViewportMargin ( margin : ViewportMargin ) : this {
418420 this . _viewportMargin = margin ;
419421 return this ;
420422 }
@@ -682,13 +684,17 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
682684 if ( overlay . width <= viewport . width ) {
683685 pushX = overflowLeft || - overflowRight ;
684686 } else {
685- pushX = start . x < this . _viewportMargin ? viewport . left - scrollPosition . left - start . x : 0 ;
687+ pushX =
688+ start . x < this . _getViewportMarginStart ( )
689+ ? viewport . left - scrollPosition . left - start . x
690+ : 0 ;
686691 }
687692
688693 if ( overlay . height <= viewport . height ) {
689694 pushY = overflowTop || - overflowBottom ;
690695 } else {
691- pushY = start . y < this . _viewportMargin ? viewport . top - scrollPosition . top - start . y : 0 ;
696+ pushY =
697+ start . y < this . _getViewportMarginTop ( ) ? viewport . top - scrollPosition . top - start . y : 0 ;
692698 }
693699
694700 this . _previousPushAmount = { x : pushX , y : pushY } ;
@@ -777,13 +783,14 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
777783 if ( position . overlayY === 'top' ) {
778784 // Overlay is opening "downward" and thus is bound by the bottom viewport edge.
779785 top = origin . y ;
780- height = viewport . height - top + this . _viewportMargin ;
786+ height = viewport . height - top + this . _getViewportMarginBottom ( ) ;
781787 } else if ( position . overlayY === 'bottom' ) {
782788 // Overlay is opening "upward" and thus is bound by the top viewport edge. We need to add
783789 // the viewport margin back in, because the viewport rect is narrowed down to remove the
784790 // margin, whereas the `origin` position is calculated based on its `DOMRect`.
785- bottom = viewport . height - origin . y + this . _viewportMargin * 2 ;
786- height = viewport . height - bottom + this . _viewportMargin ;
791+ bottom =
792+ viewport . height - origin . y + this . _getViewportMarginTop ( ) + this . _getViewportMarginBottom ( ) ;
793+ height = viewport . height - bottom + this . _getViewportMarginTop ( ) ;
787794 } else {
788795 // If neither top nor bottom, it means that the overlay is vertically centered on the
789796 // origin point. Note that we want the position relative to the viewport, rather than
@@ -815,11 +822,12 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
815822 let width : number , left : number , right : number ;
816823
817824 if ( isBoundedByLeftViewportEdge ) {
818- right = viewport . width - origin . x + this . _viewportMargin * 2 ;
819- width = origin . x - this . _viewportMargin ;
825+ right =
826+ viewport . width - origin . x + this . _getViewportMarginStart ( ) + this . _getViewportMarginEnd ( ) ;
827+ width = origin . x - this . _getViewportMarginStart ( ) ;
820828 } else if ( isBoundedByRightViewportEdge ) {
821829 left = origin . x ;
822- width = viewport . right - origin . x ;
830+ width = viewport . right - origin . x - this . _getViewportMarginEnd ( ) ;
823831 } else {
824832 // If neither start nor end, it means that the overlay is horizontally centered on the
825833 // origin point. Note that we want the position relative to the viewport, rather than
@@ -1098,12 +1106,12 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
10981106 const scrollPosition = this . _viewportRuler . getViewportScrollPosition ( ) ;
10991107
11001108 return {
1101- top : scrollPosition . top + this . _viewportMargin ,
1102- left : scrollPosition . left + this . _viewportMargin ,
1103- right : scrollPosition . left + width - this . _viewportMargin ,
1104- bottom : scrollPosition . top + height - this . _viewportMargin ,
1105- width : width - 2 * this . _viewportMargin ,
1106- height : height - 2 * this . _viewportMargin ,
1109+ top : scrollPosition . top + this . _getViewportMarginTop ( ) ,
1110+ left : scrollPosition . left + this . _getViewportMarginStart ( ) ,
1111+ right : scrollPosition . left + width - this . _getViewportMarginEnd ( ) ,
1112+ bottom : scrollPosition . top + height - this . _getViewportMarginBottom ( ) ,
1113+ width : width - this . _getViewportMarginStart ( ) - this . _getViewportMarginEnd ( ) ,
1114+ height : height - this . _getViewportMarginTop ( ) - this . _getViewportMarginBottom ( ) ,
11071115 } ;
11081116 }
11091117
@@ -1168,6 +1176,42 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
11681176 }
11691177 }
11701178
1179+ /**
1180+ * Returns either the _viewportMargin directly (if it is a number) or its 'start' value.
1181+ * @private
1182+ */
1183+ private _getViewportMarginStart ( ) : number {
1184+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1185+ return this . _viewportMargin . start ?? 0 ;
1186+ }
1187+
1188+ /**
1189+ * Returns either the _viewportMargin directly (if it is a number) or its 'end' value.
1190+ * @private
1191+ */
1192+ private _getViewportMarginEnd ( ) : number {
1193+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1194+ return this . _viewportMargin . end ?? 0 ;
1195+ }
1196+
1197+ /**
1198+ * Returns either the _viewportMargin directly (if it is a number) or its 'top' value.
1199+ * @private
1200+ */
1201+ private _getViewportMarginTop ( ) : number {
1202+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1203+ return this . _viewportMargin . top ?? 0 ;
1204+ }
1205+
1206+ /**
1207+ * Returns either the _viewportMargin directly (if it is a number) or its 'bottom' value.
1208+ * @private
1209+ */
1210+ private _getViewportMarginBottom ( ) : number {
1211+ if ( typeof this . _viewportMargin === 'number' ) return this . _viewportMargin ;
1212+ return this . _viewportMargin . bottom ?? 0 ;
1213+ }
1214+
11711215 /** Returns the DOMRect of the current origin. */
11721216 private _getOriginRect ( ) : Dimensions {
11731217 const origin = this . _origin ;
0 commit comments