@@ -64,6 +64,7 @@ export class RowEditPositionStrategy extends ConnectedPositioningStrategy {
6464 super . position ( contentElement , { width : targetElement . clientWidth , height : targetElement . clientHeight } ,
6565 document , initialCall , targetElement ) ;
6666
67+ // After positioning in the top layer, keep the overlay clipped to the visible grid body.
6768 this . updateContentClip ( contentElement ) ;
6869 }
6970
@@ -77,32 +78,38 @@ export class RowEditPositionStrategy extends ConnectedPositioningStrategy {
7778 const clippingRect = this . getClippingRect ( container ) ;
7879 const contentRect = contentElement . getBoundingClientRect ( ) ;
7980
81+ // Convert the clipped overflow on each side to CSS inset values.
8082 const top = Math . round ( Math . max ( clippingRect . top - contentRect . top , 0 ) ) ;
8183 const right = Math . round ( Math . max ( contentRect . right - clippingRect . right , 0 ) ) ;
8284 const bottom = Math . round ( Math . max ( contentRect . bottom - clippingRect . bottom , 0 ) ) ;
8385 const left = Math . round ( Math . max ( clippingRect . left - contentRect . left , 0 ) ) ;
8486
87+ // When the overlay is fully outside the clipping rect, hide it and block its action buttons.
8588 const fullyClipped = top >= contentRect . height || bottom >= contentRect . height ||
8689 left >= contentRect . width || right >= contentRect . width ;
8790
91+ // Row-edit overlays are rendered in the top layer, so clip the content explicitly to the grid's visible area.
8892 contentElement . style . clipPath = fullyClipped ? 'inset(100%)' :
8993 ( top || right || bottom || left ? `inset(${ top } px ${ right } px ${ bottom } px ${ left } px)` : '' ) ;
9094
9195 contentElement . style . pointerEvents = fullyClipped ? 'none' : '' ;
92- contentElement . style . visibility = '' ;
96+ contentElement . style . visibility = fullyClipped ? 'hidden' : '' ;
9397 }
9498
9599 private getClippingRect ( element : HTMLElement ) : Pick < DOMRect , 'top' | 'right' | 'bottom' | 'left' > {
96100 const document = element . ownerDocument ;
97101 const rect = element . getBoundingClientRect ( ) ;
102+ // Start with the current grid body, then narrow it by every clipping parent.
98103 const clippingRect = { top : rect . top , right : rect . right , bottom : rect . bottom , left : rect . left } ;
99104
100105 let parent = element . parentElement ;
101106
107+ // Intersect with clipping ancestors so nested grids respect their parent grid scroll bounds.
102108 while ( parent && parent !== document . body && parent !== document . documentElement ) {
103- const style = getComputedStyle ( parent ) ;
109+ const style = document . defaultView ?. getComputedStyle ( parent ) ?? parent . style ;
104110 const overflow = `${ style . overflow } ${ style . overflowX } ${ style . overflowY } ` ;
105111
112+ // Only ancestors that clip their children should reduce the visible area.
106113 if ( / ( a u t o | s c r o l l | h i d d e n | c l i p ) / . test ( overflow ) ) {
107114 const parentRect = parent . getBoundingClientRect ( ) ;
108115
@@ -115,6 +122,7 @@ export class RowEditPositionStrategy extends ConnectedPositioningStrategy {
115122 parent = parent . parentElement ;
116123 }
117124
125+ // Keep the clipping area inside the viewport because popover content is viewport-positioned.
118126 return {
119127 top : Math . max ( clippingRect . top , 0 ) ,
120128 right : Math . min ( clippingRect . right , document . documentElement . clientWidth ) ,
0 commit comments