@@ -110,7 +110,11 @@ function getContainerDimensions(containerNode: Element): Dimensions {
110110  let  scroll : Position  =  { } ; 
111111  let  isPinchZoomedIn  =  ( visualViewport ?. scale  ??  1 )  >  1 ; 
112112
113-   if  ( containerNode . tagName  ===  'BODY' )  { 
113+   // In the case where the container is `html` or `body` and the container doesn't have something like `position: relative`, 
114+   // then position absolute will be positioned relative to the viewport, also known as the `initial containing block`. 
115+   // That's why we use the visual viewport instead. 
116+ 
117+   if  ( containerNode . tagName  ===  'BODY'  ||  containerNode . tagName  ===  'HTML' )  { 
114118    let  documentElement  =  document . documentElement ; 
115119    totalWidth  =  documentElement . clientWidth ; 
116120    totalHeight  =  documentElement . clientHeight ; 
@@ -179,10 +183,13 @@ function getDelta(
179183  let  boundarySize  =  boundaryDimensions [ AXIS_SIZE [ axis ] ] ; 
180184  // Calculate the edges of the boundary (accomodating for the boundary padding) and the edges of the overlay. 
181185  // Note that these values are with respect to the visual viewport (aka 0,0 is the top left of the viewport) 
182-   let  boundaryStartEdge  =  boundaryDimensions . scroll [ AXIS [ axis ] ]  +  padding ; 
183-   let  boundaryEndEdge  =  boundarySize  +  boundaryDimensions . scroll [ AXIS [ axis ] ]  -  padding ; 
184-   let  startEdgeOffset  =  offset  -  containerScroll  +  containerOffsetWithBoundary [ axis ]  -  boundaryDimensions [ AXIS [ axis ] ] ; 
185-   let  endEdgeOffset  =  offset  -  containerScroll  +  size  +  containerOffsetWithBoundary [ axis ]  -  boundaryDimensions [ AXIS [ axis ] ] ; 
186+ 
187+   let  boundaryStartEdge  =   containerOffsetWithBoundary [ axis ]  +  boundaryDimensions . scroll [ AXIS [ axis ] ]  +   padding ; 
188+   let  boundaryEndEdge  =   containerOffsetWithBoundary [ axis ]  +  boundaryDimensions . scroll [ AXIS [ axis ] ]  +  boundarySize  -  padding ; 
189+   // transformed value of the left edge of the overlay 
190+   let  startEdgeOffset  =  offset  -  containerScroll  +  boundaryDimensions . scroll [ AXIS [ axis ] ]  +  containerOffsetWithBoundary [ axis ]  -  boundaryDimensions [ AXIS [ axis ] ] ; 
191+   // transformed value of the right edge of the overlay 
192+   let  endEdgeOffset  =  offset  -  containerScroll  +  size  +  boundaryDimensions . scroll [ AXIS [ axis ] ]   +  containerOffsetWithBoundary [ axis ]  -  boundaryDimensions [ AXIS [ axis ] ] ; 
186193
187194  // If any of the overlay edges falls outside of the boundary, shift the overlay the required amount to align one of the overlay's 
188195  // edges with the closest boundary edge. 
@@ -234,7 +241,8 @@ function computePosition(
234241  containerOffsetWithBoundary : Offset , 
235242  isContainerPositioned : boolean , 
236243  arrowSize : number , 
237-   arrowBoundaryOffset : number 
244+   arrowBoundaryOffset : number , 
245+   containerDimensions : Dimensions 
238246)  { 
239247  let  { placement,  crossPlacement,  axis,  crossAxis,  size,  crossSize}  =  placementInfo ; 
240248  let  position : Position  =  { } ; 
@@ -255,9 +263,9 @@ function computePosition(
255263
256264  position [ crossAxis ] !  +=  crossOffset ; 
257265
258-   // overlay top overlapping arrow with button bottom 
266+   // overlay top or left  overlapping arrow with button bottom or right  
259267  const  minPosition  =  childOffset [ crossAxis ]  -  overlaySize [ crossSize ]  +  arrowSize  +  arrowBoundaryOffset ; 
260-   // overlay bottom overlapping arrow with button top 
268+   // overlay bottom or right  overlapping arrow with button top or left  
261269  const  maxPosition  =  childOffset [ crossAxis ]  +  childOffset [ crossSize ]  -  arrowSize  -  arrowBoundaryOffset ; 
262270  position [ crossAxis ]  =  clamp ( position [ crossAxis ] ! ,  minPosition ,  maxPosition ) ; 
263271
@@ -266,8 +274,8 @@ function computePosition(
266274    // If the container is positioned (non-static), then we use the container's actual 
267275    // height, as `bottom` will be relative to this height.  But if the container is static, 
268276    // then it can only be the `document.body`, and `bottom` will be relative to _its_ 
269-     // container, which should be as large as boundaryDimensions . 
270-     const  containerHeight  =  ( isContainerPositioned  ? containerOffsetWithBoundary [ size ]  : boundaryDimensions [ TOTAL_SIZE [ size ] ] ) ; 
277+     // container. 
278+     const  containerHeight  =  ( isContainerPositioned  ? containerOffsetWithBoundary [ size ]  : containerDimensions [ TOTAL_SIZE [ size ] ] ) ; 
271279    position [ FLIPPED_DIRECTION [ axis ] ]  =  Math . floor ( containerHeight  -  childOffset [ axis ]  +  offset ) ; 
272280  }  else  { 
273281    position [ axis ]  =  Math . floor ( childOffset [ axis ]  +  childOffset [ size ]  +  offset ) ; 
@@ -283,42 +291,55 @@ function getMaxHeight(
283291  margins : Position , 
284292  padding : number , 
285293  overlayHeight : number , 
286-   heightGrowthDirection : HeightGrowthDirection 
294+   heightGrowthDirection : HeightGrowthDirection , 
295+   containerDimensions : Dimensions 
287296)  { 
288-   const  containerHeight  =  ( isContainerPositioned  ? containerOffsetWithBoundary . height  : boundaryDimensions [ TOTAL_SIZE . height ] ) ; 
289-   // For cases where position is set via "bottom" instead of "top", we need to calculate the true overlay top with respect to the boundary. Reverse calculate this with the same method  
290-   // used in computePosition . 
291-   let  overlayTop  =  position . top  !=  null  ? containerOffsetWithBoundary . top   +   position . top  :  containerOffsetWithBoundary . top   +  ( containerHeight  -  ( position . bottom  ??  0 )  -  overlayHeight ) ; 
297+   const  containerHeight  =  ( isContainerPositioned  ? containerOffsetWithBoundary . height  : containerDimensions [ TOTAL_SIZE . height ] ) ; 
298+   // For cases where position is set via "bottom" instead of "top", we need to calculate the true overlay top 
299+   // with respect to the container . 
300+   let  overlayTop  =  position . top  !=  null  ? position . top  : ( containerHeight  -  ( position . bottom  ??  0 )  -  overlayHeight ) ; 
292301  let  maxHeight  =  heightGrowthDirection  !==  'top'  ?
293302    // We want the distance between the top of the overlay to the bottom of the boundary 
294303    Math . max ( 0 , 
295-       ( boundaryDimensions . height  +  boundaryDimensions . top   +   ( boundaryDimensions . scroll . top   ??   0 ) )  // this is the bottom of the boundary 
304+       ( boundaryDimensions . height  +  boundaryDimensions . top )  // this is the bottom of the boundary 
296305      -  overlayTop  // this is the top of the overlay 
297306      -  ( ( margins . top  ??  0 )  +  ( margins . bottom  ??  0 )  +  padding )  // save additional space for margin and padding 
298307    ) 
299308    // We want the distance between the bottom of the overlay to the top of the boundary 
300309    : Math . max ( 0 , 
301310      ( overlayTop  +  overlayHeight )  // this is the bottom of the overlay 
302-       -  ( boundaryDimensions . top   +   ( boundaryDimensions . scroll . top   ??   0 ) )  // this is the top of the boundary 
311+       -  boundaryDimensions . top  // this is the top of the boundary 
303312      -  ( ( margins . top  ??  0 )  +  ( margins . bottom  ??  0 )  +  padding )  // save additional space for margin and padding 
304313    ) ; 
305314  return  Math . min ( boundaryDimensions . height  -  ( padding  *  2 ) ,  maxHeight ) ; 
306315} 
307316
308317function  getAvailableSpace ( 
309-   boundaryDimensions : Dimensions , 
318+   boundaryDimensions : Dimensions ,   // boundary 
310319  containerOffsetWithBoundary : Offset , 
311-   childOffset : Offset , 
312-   margins : Position , 
313-   padding : number , 
320+   childOffset : Offset ,   // trigger 
321+   margins : Position ,   // overlay 
322+   padding : number ,   // overlay <-> boundary 
314323  placementInfo : ParsedPlacement 
315324)  { 
316325  let  { placement,  axis,  size}  =  placementInfo ; 
317326  if  ( placement  ===  axis )  { 
318-     return  Math . max ( 0 ,  childOffset [ axis ]  -  boundaryDimensions [ axis ]  -  ( boundaryDimensions . scroll [ axis ]  ??  0 )  +  containerOffsetWithBoundary [ axis ]  -  ( margins [ axis ]  ??  0 )  -  margins [ FLIPPED_DIRECTION [ axis ] ]  -  padding ) ; 
327+     return  Math . max ( 0 , 
328+       childOffset [ axis ]  // trigger start 
329+       -  boundaryDimensions [ axis ]  // boundary start 
330+       -  ( margins [ axis ]  ??  0 )  // margins usually for arrows or other decorations 
331+       -  margins [ FLIPPED_DIRECTION [ axis ] ] 
332+       -  padding ) ;  // padding between overlay and boundary 
319333  } 
320334
321-   return  Math . max ( 0 ,  boundaryDimensions [ size ]  +  boundaryDimensions [ axis ]  +  boundaryDimensions . scroll [ axis ]  -  containerOffsetWithBoundary [ axis ]  -  childOffset [ axis ]  -  childOffset [ size ]  -  ( margins [ axis ]  ??  0 )  -  margins [ FLIPPED_DIRECTION [ axis ] ]  -  padding ) ; 
335+   return  Math . max ( 0 , 
336+     boundaryDimensions [ size ] 
337+     +  boundaryDimensions [ axis ] 
338+      -  childOffset [ axis ] 
339+      -  childOffset [ size ] 
340+      -  ( margins [ axis ]  ??  0 ) 
341+      -  margins [ FLIPPED_DIRECTION [ axis ] ] 
342+      -  padding ) ; 
322343} 
323344
324345export  function  calculatePositionInternal ( 
@@ -341,7 +362,7 @@ export function calculatePositionInternal(
341362) : PositionResult  { 
342363  let  placementInfo  =  parsePlacement ( placementInput ) ; 
343364  let  { size,  crossAxis,  crossSize,  placement,  crossPlacement}  =  placementInfo ; 
344-   let  position  =  computePosition ( childOffset ,  boundaryDimensions ,  overlaySize ,  placementInfo ,  offset ,  crossOffset ,  containerOffsetWithBoundary ,  isContainerPositioned ,  arrowSize ,  arrowBoundaryOffset ) ; 
365+   let  position  =  computePosition ( childOffset ,  boundaryDimensions ,  overlaySize ,  placementInfo ,  offset ,  crossOffset ,  containerOffsetWithBoundary ,  isContainerPositioned ,  arrowSize ,  arrowBoundaryOffset ,   containerDimensions ) ; 
345366  let  normalizedOffset  =  offset ; 
346367  let  space  =  getAvailableSpace ( 
347368    boundaryDimensions , 
@@ -355,7 +376,8 @@ export function calculatePositionInternal(
355376  // Check if the scroll size of the overlay is greater than the available space to determine if we need to flip 
356377  if  ( flip  &&  scrollSize [ size ]  >  space )  { 
357378    let  flippedPlacementInfo  =  parsePlacement ( `${ FLIPPED_DIRECTION [ placement ] } ${ crossPlacement }   as  Placement ) ; 
358-     let  flippedPosition  =  computePosition ( childOffset ,  boundaryDimensions ,  overlaySize ,  flippedPlacementInfo ,  offset ,  crossOffset ,  containerOffsetWithBoundary ,  isContainerPositioned ,  arrowSize ,  arrowBoundaryOffset ) ; 
379+     let  flippedPosition  =  computePosition ( childOffset ,  boundaryDimensions ,  overlaySize ,  flippedPlacementInfo ,  offset ,  crossOffset ,  containerOffsetWithBoundary ,  isContainerPositioned ,  arrowSize ,  arrowBoundaryOffset ,  containerDimensions ) ; 
380+ 
359381    let  flippedSpace  =  getAvailableSpace ( 
360382      boundaryDimensions , 
361383      containerOffsetWithBoundary , 
@@ -400,7 +422,8 @@ export function calculatePositionInternal(
400422    margins , 
401423    padding , 
402424    overlaySize . height , 
403-     heightGrowthDirection 
425+     heightGrowthDirection , 
426+     containerDimensions 
404427  ) ; 
405428
406429  if  ( userSetMaxHeight  &&  userSetMaxHeight  <  maxHeight )  { 
@@ -409,7 +432,7 @@ export function calculatePositionInternal(
409432
410433  overlaySize . height  =  Math . min ( overlaySize . height ,  maxHeight ) ; 
411434
412-   position  =  computePosition ( childOffset ,  boundaryDimensions ,  overlaySize ,  placementInfo ,  normalizedOffset ,  crossOffset ,  containerOffsetWithBoundary ,  isContainerPositioned ,  arrowSize ,  arrowBoundaryOffset ) ; 
435+   position  =  computePosition ( childOffset ,  boundaryDimensions ,  overlaySize ,  placementInfo ,  normalizedOffset ,  crossOffset ,  containerOffsetWithBoundary ,  isContainerPositioned ,  arrowSize ,  arrowBoundaryOffset ,   containerDimensions ) ; 
413436  delta  =  getDelta ( crossAxis ,  position [ crossAxis ] ! ,  overlaySize [ crossSize ] ,  boundaryDimensions ,  containerDimensions ,  padding ,  containerOffsetWithBoundary ) ; 
414437  position [ crossAxis ] !  +=  delta ; 
415438
@@ -507,7 +530,7 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
507530  // If the container is the HTML element wrapping the body element, the retrieved scrollTop/scrollLeft will be equal to the 
508531  // body element's scroll. Set the container's scroll values to 0 since the overlay's edge position value in getDelta don't then need to be further offset 
509532  // by the container scroll since they are essentially the same containing element and thus in the same coordinate system 
510-   let  containerOffsetWithBoundary : Offset  =  boundaryElement . tagName  ===  'BODY'  ? getOffset ( container ,  false )  : getPosition ( container ,   boundaryElement ,  false ) ; 
533+   let  containerOffsetWithBoundary : Offset  =  boundaryElement . tagName  ===  'BODY'  ? getOffset ( container ,  false )  : getPosition ( boundaryElement ,   container ,    false ) ; 
511534  if  ( container . tagName  ===  'HTML'  &&  boundaryElement . tagName  ===  'BODY' )  { 
512535    containerDimensions . scroll . top  =  0 ; 
513536    containerDimensions . scroll . left  =  0 ; 
@@ -536,7 +559,7 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
536559export  function  getRect ( node : Element ,  ignoreScale : boolean )  { 
537560  let  { top,  left,  width,  height}  =  node . getBoundingClientRect ( ) ; 
538561
539-   // Use offsetWidth and offsetHeight if this is an HTML element, so that   
562+   // Use offsetWidth and offsetHeight if this is an HTML element, so that 
540563  // the size is not affected by scale transforms. 
541564  if  ( ignoreScale  &&  node  instanceof  node . ownerDocument . defaultView ! . HTMLElement )  { 
542565    width  =  node . offsetWidth ; 
0 commit comments