@@ -85,6 +85,9 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
85
85
/** Cached viewport dimensions */
86
86
private _viewportRect : Dimensions ;
87
87
88
+ /** Cached container dimensions */
89
+ private _containerRect : Dimensions ;
90
+
88
91
/** Amount of space that must be maintained between the overlay and the edge of the viewport. */
89
92
private _viewportMargin = 0 ;
90
93
@@ -213,16 +216,18 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
213
216
this . _resetOverlayElementStyles ( ) ;
214
217
this . _resetBoundingBoxStyles ( ) ;
215
218
216
- // We need the bounding rects for the origin and the overlay to determine how to position
219
+ // We need the bounding rects for the origin, the overlay and the container to determine how to position
217
220
// the overlay relative to the origin.
218
221
// We use the viewport rect to determine whether a position would go off-screen.
219
222
this . _viewportRect = this . _getNarrowedViewportRect ( ) ;
220
223
this . _originRect = this . _getOriginRect ( ) ;
221
224
this . _overlayRect = this . _pane . getBoundingClientRect ( ) ;
225
+ this . _containerRect = this . _overlayContainer . getContainerElement ( ) . getBoundingClientRect ( ) ;
222
226
223
227
const originRect = this . _originRect ;
224
228
const overlayRect = this . _overlayRect ;
225
229
const viewportRect = this . _viewportRect ;
230
+ const containerRect = this . _containerRect ;
226
231
227
232
// Positions where the overlay will fit with flexible dimensions.
228
233
const flexibleFits : FlexibleFit [ ] = [ ] ;
@@ -234,7 +239,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
234
239
// If a good fit is found, it will be applied immediately.
235
240
for ( let pos of this . _preferredPositions ) {
236
241
// Get the exact (x, y) coordinate for the point-of-origin on the origin element.
237
- let originPoint = this . _getOriginPoint ( originRect , pos ) ;
242
+ let originPoint = this . _getOriginPoint ( originRect , containerRect , pos ) ;
238
243
239
244
// From that point-of-origin, get the exact (x, y) coordinate for the top-left corner of the
240
245
// overlay in this position. We use the top-left corner for calculations and later translate
@@ -359,9 +364,10 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
359
364
this . _originRect = this . _getOriginRect ( ) ;
360
365
this . _overlayRect = this . _pane . getBoundingClientRect ( ) ;
361
366
this . _viewportRect = this . _getNarrowedViewportRect ( ) ;
367
+ this . _containerRect = this . _overlayContainer . getContainerElement ( ) . getBoundingClientRect ( ) ;
362
368
363
369
const lastPosition = this . _lastPosition || this . _preferredPositions [ 0 ] ;
364
- const originPoint = this . _getOriginPoint ( this . _originRect , lastPosition ) ;
370
+ const originPoint = this . _getOriginPoint ( this . _originRect , this . _containerRect , lastPosition ) ;
365
371
366
372
this . _applyPosition ( lastPosition , originPoint ) ;
367
373
}
@@ -479,7 +485,11 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
479
485
/**
480
486
* Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
481
487
*/
482
- private _getOriginPoint ( originRect : Dimensions , pos : ConnectedPosition ) : Point {
488
+ private _getOriginPoint (
489
+ originRect : Dimensions ,
490
+ containerRect : Dimensions ,
491
+ pos : ConnectedPosition ,
492
+ ) : Point {
483
493
let x : number ;
484
494
if ( pos . originX == 'center' ) {
485
495
// Note: when centering we should always use the `left`
@@ -491,13 +501,28 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
491
501
x = pos . originX == 'start' ? startX : endX ;
492
502
}
493
503
504
+ // When zooming in Safari the container rectangle contains negative values for the position
505
+ // and we need to re-add them to the calculated coordinates.
506
+ if ( containerRect . left < 0 ) {
507
+ x -= containerRect . left ;
508
+ }
509
+
494
510
let y : number ;
495
511
if ( pos . originY == 'center' ) {
496
512
y = originRect . top + originRect . height / 2 ;
497
513
} else {
498
514
y = pos . originY == 'top' ? originRect . top : originRect . bottom ;
499
515
}
500
516
517
+ // Normally the containerRect's top value would be zero, however when the overlay is attached to an input
518
+ // (e.g. in an autocomplete), mobile browsers will shift everything in order to put the input in the middle
519
+ // of the screen and to make space for the virtual keyboard. We need to account for this offset,
520
+ // otherwise our positioning will be thrown off.
521
+ // Additionally, when zooming in Safari this fixes the vertical position.
522
+ if ( containerRect . top < 0 ) {
523
+ y -= containerRect . top ;
524
+ }
525
+
501
526
return { x, y} ;
502
527
}
503
528
@@ -580,7 +605,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
580
605
/**
581
606
* Whether the overlay can fit within the viewport when it may resize either its width or height.
582
607
* @param fit How well the overlay fits in the viewport at some position.
583
- * @param point The (x, y) coordinates of the overlat at some position.
608
+ * @param point The (x, y) coordinates of the overlay at some position.
584
609
* @param viewport The geometry of the viewport.
585
610
*/
586
611
private _canFitWithFlexibleDimensions ( fit : OverlayFit , point : Point , viewport : Dimensions ) {
@@ -606,7 +631,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
606
631
* right and bottom).
607
632
*
608
633
* @param start Starting point from which the overlay is pushed.
609
- * @param overlay Dimensions of the overlay.
634
+ * @param rawOverlayRect Dimensions of the overlay.
610
635
* @param scrollPosition Current viewport scroll position.
611
636
* @returns The point at which to position the overlay after pushing. This is effectively a new
612
637
* originPoint.
@@ -958,16 +983,6 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
958
983
overlayPoint = this . _pushOverlayOnScreen ( overlayPoint , this . _overlayRect , scrollPosition ) ;
959
984
}
960
985
961
- let virtualKeyboardOffset = this . _overlayContainer
962
- . getContainerElement ( )
963
- . getBoundingClientRect ( ) . top ;
964
-
965
- // Normally this would be zero, however when the overlay is attached to an input (e.g. in an
966
- // autocomplete), mobile browsers will shift everything in order to put the input in the middle
967
- // of the screen and to make space for the virtual keyboard. We need to account for this offset,
968
- // otherwise our positioning will be thrown off.
969
- overlayPoint . y -= virtualKeyboardOffset ;
970
-
971
986
// We want to set either `top` or `bottom` based on whether the overlay wants to appear
972
987
// above or below the origin and the direction in which the element will expand.
973
988
if ( position . overlayY === 'bottom' ) {
@@ -1183,7 +1198,7 @@ interface OverlayFit {
1183
1198
visibleArea : number ;
1184
1199
}
1185
1200
1186
- /** Record of the measurments determining whether an overlay will fit in a specific position. */
1201
+ /** Record of the measurements determining whether an overlay will fit in a specific position. */
1187
1202
interface FallbackPosition {
1188
1203
position : ConnectedPosition ;
1189
1204
originPoint : Point ;
0 commit comments