1
- import { useState } from 'react' ;
1
+ import { useEffect , useRef , useState } from 'react' ;
2
+ import { useMergeRefs } from '@floating-ui/react' ;
2
3
import {
3
4
Box ,
4
5
BoxProps ,
@@ -47,7 +48,7 @@ export interface ScrollAreaProps
47
48
scrollbars ?: 'x' | 'y' | 'xy' | false ;
48
49
49
50
/** Determines whether scrollbars should be offset with padding on given axis, `false` by default */
50
- offsetScrollbars ?: boolean | 'x' | 'y' ;
51
+ offsetScrollbars ?: boolean | 'x' | 'y' | 'present' ;
51
52
52
53
/** Assigns viewport element (scrollable container) ref */
53
54
viewportRef ?: React . ForwardedRef < HTMLDivElement > ;
@@ -120,6 +121,8 @@ export const ScrollArea = factory<ScrollAreaFactory>((_props, ref) => {
120
121
} = props ;
121
122
122
123
const [ scrollbarHovered , setScrollbarHovered ] = useState ( false ) ;
124
+ const [ verticalThumbVisible , setVerticalThumbVisible ] = useState ( false ) ;
125
+ const [ horizontalThumbVisible , setHorizontalThumbVisible ] = useState ( false ) ;
123
126
124
127
const getStyles = useStyles < ScrollAreaFactory > ( {
125
128
name : 'ScrollArea' ,
@@ -134,6 +137,27 @@ export const ScrollArea = factory<ScrollAreaFactory>((_props, ref) => {
134
137
varsResolver,
135
138
} ) ;
136
139
140
+ const localViewportRef = useRef < HTMLDivElement > ( null ) ;
141
+ const combinedViewportRef = useMergeRefs ( [ viewportRef , localViewportRef ] ) ;
142
+
143
+ useEffect ( ( ) => {
144
+ if ( ! localViewportRef . current ) {
145
+ return ;
146
+ }
147
+
148
+ const element = localViewportRef . current ;
149
+
150
+ const observer = new ResizeObserver ( ( ) => {
151
+ const { scrollHeight, clientHeight, scrollWidth, clientWidth } = element ;
152
+ setVerticalThumbVisible ( scrollHeight > clientHeight ) ;
153
+ setHorizontalThumbVisible ( scrollWidth > clientWidth ) ;
154
+ } ) ;
155
+
156
+ observer . observe ( element ) ;
157
+
158
+ return ( ) => observer . disconnect ( ) ;
159
+ } , [ localViewportRef , offsetScrollbars ] ) ;
160
+
137
161
return (
138
162
< ScrollAreaRoot
139
163
type = { type === 'never' ? 'always' : type }
@@ -146,9 +170,15 @@ export const ScrollArea = factory<ScrollAreaFactory>((_props, ref) => {
146
170
< ScrollAreaViewport
147
171
{ ...viewportProps }
148
172
{ ...getStyles ( 'viewport' , { style : viewportProps ?. style } ) }
149
- ref = { viewportRef }
173
+ ref = { combinedViewportRef }
150
174
data-offset-scrollbars = { offsetScrollbars === true ? 'xy' : offsetScrollbars || undefined }
151
175
data-scrollbars = { scrollbars || undefined }
176
+ data-horizontal-hidden = {
177
+ offsetScrollbars === 'present' && ! horizontalThumbVisible ? 'true' : undefined
178
+ }
179
+ data-vertical-hidden = {
180
+ offsetScrollbars === 'present' && ! verticalThumbVisible ? 'true' : undefined
181
+ }
152
182
onScroll = { ( e ) => {
153
183
viewportProps ?. onScroll ?.( e ) ;
154
184
onScrollPositionChange ?.( { x : e . currentTarget . scrollLeft , y : e . currentTarget . scrollTop } ) ;
0 commit comments