@@ -11,6 +11,7 @@ import DragHandleWrapper from '../../internal/components/drag-handle-wrapper';
1111import { useVisualRefresh } from '../../internal/hooks/use-visual-mode' ;
1212import { KeyCode } from '../../internal/keycode' ;
1313import handleKey , { isEventLike } from '../../internal/utils/handle-key' ;
14+ import { scrollElementIntoView } from '../../internal/utils/scrollable-containers' ;
1415import { DEFAULT_COLUMN_WIDTH } from '../use-column-widths' ;
1516import { getHeaderWidth , getResizerElements } from './resizer-lookup' ;
1617
@@ -53,12 +54,14 @@ export function Resizer({
5354 const isVisualRefresh = useVisualRefresh ( ) ;
5455
5556 const separatorId = useUniqueId ( ) ;
56- const resizerToggleRef = useRef < HTMLButtonElement > ( null ) ;
57- const resizerSeparatorRef = useRef < HTMLSpanElement > ( null ) ;
57+ const positioningWrapperRef = useRef < HTMLDivElement | null > ( null ) ;
58+ const resizerToggleRef = useRef < HTMLButtonElement | null > ( null ) ;
59+ const resizerSeparatorRef = useRef < HTMLSpanElement | null > ( null ) ;
5860
5961 const [ isPointerDown , setIsPointerDown ] = useState ( false ) ;
6062 const [ isDragging , setIsDragging ] = useState ( false ) ;
6163 const [ showUapButtons , setShowUapButtons ] = useState ( false ) ;
64+ const [ resizerObscured , setResizerObscured ] = useState ( false ) ;
6265 const [ isKeyboardDragging , setIsKeyboardDragging ] = useState ( false ) ;
6366 const autoGrowTimeout = useRef < ReturnType < typeof setTimeout > | undefined > ( ) ;
6467 const [ resizerHasFocus , setResizerHasFocus ] = useState ( false ) ;
@@ -69,6 +72,70 @@ export function Resizer({
6972 setHeaderCellWidth ( getHeaderWidth ( resizerToggleRef . current ) ) ;
7073 } , [ ] ) ;
7174
75+ const isObscured = useCallback ( ( ) => {
76+ const elements = getResizerElements ( resizerToggleRef . current ) ;
77+ if ( ! elements || ! positioningWrapperRef . current ) {
78+ return false ;
79+ }
80+
81+ let scrollPaddingInlineStart = 0 ;
82+ let scrollPaddingInlineEnd = 0 ;
83+
84+ // Calculate size of the headers at the exact moment of the call to deal
85+ // with auto-width columns and cached sticky column state.
86+ elements . allHeaders . forEach ( header => {
87+ const { inlineSize } = getLogicalBoundingClientRect ( header ) ;
88+ if ( header . style . insetInlineStart ) {
89+ scrollPaddingInlineStart += inlineSize ;
90+ }
91+ if ( header . style . insetInlineEnd ) {
92+ scrollPaddingInlineEnd += inlineSize ;
93+ }
94+ } ) ;
95+
96+ const { insetInlineStart : scrollParentInsetInlineStart , insetInlineEnd : scrollParentInsetInlineEnd } =
97+ getLogicalBoundingClientRect ( elements . scrollParent ) ;
98+ const { insetInlineStart, insetInlineEnd, inlineSize } = getLogicalBoundingClientRect ( elements . header ) ;
99+ const relativeInsetInlineStart = insetInlineStart - scrollParentInsetInlineStart ;
100+ const relativeInsetInlineEnd = scrollParentInsetInlineEnd - insetInlineEnd ;
101+ const isSticky = ! ! elements . header . style . insetInlineStart || ! ! elements . header . style . insetInlineEnd ;
102+
103+ return (
104+ // Is positioningWrapper obscured behind the left edge of scrollParent?
105+ Math . ceil ( relativeInsetInlineStart + inlineSize ) < ( isSticky ? 0 : scrollPaddingInlineStart ) ||
106+ // Is positioningWrapper obscured behind the right edge of scrollParent?
107+ Math . ceil ( relativeInsetInlineEnd ) < ( isSticky ? 0 : scrollPaddingInlineEnd )
108+ ) ;
109+ } , [ ] ) ;
110+
111+ const scrollIntoViewIfNeeded = useCallback ( ( ) => {
112+ if ( resizerSeparatorRef . current && isObscured ( ) ) {
113+ scrollElementIntoView ( resizerSeparatorRef . current ) ;
114+ }
115+ } , [ isObscured ] ) ;
116+
117+ useEffect ( ( ) => {
118+ if ( ! showUapButtons ) {
119+ setResizerObscured ( false ) ;
120+ return ;
121+ }
122+
123+ const elements = getResizerElements ( resizerToggleRef . current ) ;
124+ if ( ! elements ) {
125+ return ;
126+ }
127+
128+ const onScroll = ( ) => setResizerObscured ( isObscured ( ) ) ;
129+ elements . scrollParent . addEventListener ( 'scroll' , onScroll ) ;
130+ return ( ) => elements . scrollParent . removeEventListener ( 'scroll' , onScroll ) ;
131+ } , [ isObscured , showUapButtons ] ) ;
132+
133+ useEffect ( ( ) => {
134+ if ( showUapButtons ) {
135+ setResizerObscured ( isObscured ( ) ) ;
136+ }
137+ } , [ headerCellWidth , isObscured , showUapButtons ] ) ;
138+
72139 const updateTrackerPosition = useCallback ( ( newOffset : number ) => {
73140 const elements = getResizerElements ( resizerToggleRef . current ) ;
74141 if ( ! elements ) {
@@ -180,9 +247,11 @@ export function Resizer({
180247 } ,
181248 onInlineStart : ( ) => {
182249 updateColumnWidth ( getLogicalBoundingClientRect ( elements . header ) . inlineSize - 10 ) ;
250+ scrollIntoViewIfNeeded ( ) ;
183251 } ,
184252 onInlineEnd : ( ) => {
185253 updateColumnWidth ( getLogicalBoundingClientRect ( elements . header ) . inlineSize + 10 ) ;
254+ scrollIntoViewIfNeeded ( ) ;
186255 } ,
187256 } ) ;
188257 }
@@ -234,6 +303,7 @@ export function Resizer({
234303 isKeyboardDragging ,
235304 isPointerDown ,
236305 resizerHasFocus ,
306+ scrollIntoViewIfNeeded ,
237307 onWidthUpdateCommit ,
238308 resizeColumn ,
239309 updateColumnWidth ,
@@ -249,18 +319,22 @@ export function Resizer({
249319 const { tabIndex : resizerTabIndex } = useSingleTabStopNavigation ( resizerToggleRef , { tabIndex } ) ;
250320
251321 return (
252- < div className = { clsx ( styles [ 'resizer-wrapper' ] , isVisualRefresh && styles [ 'is-visual-refresh' ] ) } >
322+ < div
323+ className = { clsx ( styles [ 'resizer-wrapper' ] , isVisualRefresh && styles [ 'is-visual-refresh' ] ) }
324+ ref = { positioningWrapperRef }
325+ >
253326 < DragHandleWrapper
254327 clickDragThreshold = { 3 }
255328 hideButtonsOnDrag = { false }
256329 directions = { {
257- 'inline-start' : headerCellWidth > minWidth ? 'active' : 'disabled' ,
258- 'inline-end' : 'active' ,
330+ 'inline-start' : resizerObscured ? undefined : headerCellWidth > minWidth ? 'active' : 'disabled' ,
331+ 'inline-end' : resizerObscured ? undefined : 'active' ,
259332 } }
260333 triggerMode = "controlled"
261- controlledShowButtons = { showUapButtons }
334+ controlledShowButtons = { showUapButtons && ! resizerObscured }
262335 wrapperClassName = { styles [ 'resizer-button-wrapper' ] }
263336 tooltipText = { tooltipText }
337+ data-obscured = { resizerObscured }
264338 onDirectionClick = { direction => {
265339 const elements = getResizerElements ( resizerToggleRef . current ) ;
266340 if ( ! elements ) {
@@ -269,10 +343,16 @@ export function Resizer({
269343
270344 if ( direction === 'inline-start' ) {
271345 updateColumnWidth ( getLogicalBoundingClientRect ( elements . header ) . inlineSize - 20 ) ;
272- requestAnimationFrame ( onWidthUpdateCommit ) ;
346+ requestAnimationFrame ( ( ) => {
347+ onWidthUpdateCommit ( ) ;
348+ scrollIntoViewIfNeeded ( ) ;
349+ } ) ;
273350 } else if ( direction === 'inline-end' ) {
274351 updateColumnWidth ( getLogicalBoundingClientRect ( elements . header ) . inlineSize + 20 ) ;
275- requestAnimationFrame ( onWidthUpdateCommit ) ;
352+ requestAnimationFrame ( ( ) => {
353+ onWidthUpdateCommit ( ) ;
354+ scrollIntoViewIfNeeded ( ) ;
355+ } ) ;
276356 }
277357 } }
278358 >
0 commit comments