1
1
import React , {
2
+ CSSProperties ,
3
+ MutableRefObject ,
4
+ ReactElement ,
5
+ ReactNode ,
2
6
useContext ,
3
7
useEffect ,
4
8
useMemo ,
5
9
useReducer ,
6
10
useRef
7
11
} from 'react' ;
8
- import PropTypes from 'prop-types' ;
9
12
10
- const Context = React . createContext ( ) ;
13
+ enum Direction {
14
+ up = 'up' ,
15
+ left = 'left' ,
16
+ right = 'right' ,
17
+ down = 'down'
18
+ } ;
19
+
20
+ interface CanScroll {
21
+ [ Direction . up ] : boolean
22
+ [ Direction . left ] : boolean
23
+ [ Direction . right ] : boolean
24
+ [ Direction . down ] : boolean
25
+ }
26
+
27
+ interface Dispatch {
28
+ type : string
29
+ direction : keyof typeof Direction
30
+ canScroll : boolean
31
+ }
32
+
33
+ interface OverflowContext {
34
+ tolerance ?: number | string
35
+ refs : { viewport : MutableRefObject < HTMLDivElement | null > }
36
+ canScroll ?: CanScroll
37
+ state : {
38
+ canScroll : CanScroll
39
+ }
40
+ dispatch : ( { type, direction, canScroll} : Dispatch ) => void
41
+ }
42
+
43
+ const Context = React . createContext < OverflowContext > ( { } ) ;
11
44
12
45
export function useOverflow ( ) {
13
46
return useContext ( Context ) ;
14
47
}
15
48
16
- const containerStyle = {
49
+ const containerStyle : CSSProperties = {
17
50
display : 'flex' ,
18
51
flexDirection : 'column' ,
19
52
position : 'relative'
20
53
} ;
21
54
22
- const viewportStyle = {
55
+ const viewportStyle : CSSProperties = {
23
56
position : 'relative' ,
24
57
flexBasis : '100%' ,
25
58
flexShrink : 1 ,
26
59
flexGrow : 0 ,
27
60
overflow : 'auto'
28
61
} ;
29
62
30
- const contentStyle = {
63
+ const contentStyle : CSSProperties = {
31
64
display : 'inline-block' ,
32
65
position : 'relative' ,
33
66
minWidth : '100%' ,
34
67
boxSizing : 'border-box'
35
68
} ;
36
69
37
- function reducer ( state , action ) {
70
+ function reducer ( state , action : PayloadAction < > ) {
38
71
switch ( action . type ) {
39
72
case 'CHANGE' : {
40
73
const currentValue = state . canScroll [ action . direction ] ;
@@ -104,10 +137,10 @@ export default function Overflow({
104
137
style : styleProp ,
105
138
tolerance = 0 ,
106
139
...rest
107
- } ) {
140
+ } : Overflow ) {
108
141
const [ state , dispatch ] = useReducer ( reducer , null , getInitialState ) ;
109
142
const hidden = rest . hidden ;
110
- const viewportRef = useRef ( ) ;
143
+ const viewportRef = useRef < HTMLDivElement > ( null ) ;
111
144
112
145
const style = useMemo (
113
146
( ) => ( {
@@ -151,26 +184,28 @@ export default function Overflow({
151
184
) ;
152
185
}
153
186
154
- Overflow . propTypes = {
187
+ interface Overflow {
155
188
/**
156
189
* Elements to render inside the outer container. This should include an
157
190
* `<Overflow.Content>` element at a minimum, but should also include your
158
191
* scroll indicators if you’d like to overlay them on the scrollable viewport.
159
192
*/
160
- children : PropTypes . node ,
193
+ children : ReactNode ,
161
194
/**
162
195
* Callback that receives the latest overflow state and an object of refs, if
163
196
* you’d like to react to overflow in a custom way.
164
197
*/
165
- onStateChange : PropTypes . func ,
198
+ onStateChange : ( state : string , refs : { viewport : MutableRefObject < HTMLDivElement | null > } ) => void ,
166
199
/**
167
200
* Distance (number of pixels or CSS length unit like `1em`) to the edge of
168
201
* the content at which to consider the viewport fully scrolled. For example,
169
202
* if set to 10, then it will consider scrolling to have reached the end as
170
203
* long as it’s within 10 pixels of the border. You can use this when your
171
204
* content has padding and scrolling close to the edge should be good enough.
172
205
*/
173
- tolerance : PropTypes . oneOfType ( [ PropTypes . number , PropTypes . string ] )
206
+ tolerance : number | string
207
+ style : CSSProperties
208
+ hidden : boolean
174
209
} ;
175
210
176
211
// For Firefox, update on a threshold of 0 in addition to any intersection at
@@ -188,20 +223,20 @@ const threshold = [0, 1e-12];
188
223
* own element inside `<Overflow.Content>` instead – otherwise you risk
189
224
* interfering with the styles this component needs to function.
190
225
*/
191
- function OverflowContent ( { children, style : styleProp , ...rest } ) {
226
+ function OverflowContent ( { children, style : styleProp , ...rest } : OverflowContent ) {
192
227
const { dispatch, tolerance, refs } = useOverflow ( ) ;
193
228
const { viewport : viewportRef } = refs ;
194
- const contentRef = useRef ( ) ;
195
- const toleranceRef = useRef ( ) ;
229
+ const contentRef = useRef < HTMLDivElement | null > ( null ) ;
230
+ const toleranceRef = useRef < HTMLDivElement | null > ( null ) ;
196
231
const watchRef = tolerance ? toleranceRef : contentRef ;
197
- const observersRef = useRef ( ) ;
232
+ const observersRef = useRef < HTMLDivElement | null > ( null ) ;
198
233
199
234
useEffect ( ( ) => {
200
235
let ignore = false ;
201
236
202
237
const root = viewportRef . current ;
203
238
204
- const createObserver = ( direction , rootMargin ) => {
239
+ const createObserver = ( direction : Direction , rootMargin : string ) => {
205
240
return new IntersectionObserver (
206
241
( [ entry ] ) => {
207
242
if ( ignore ) {
@@ -230,10 +265,10 @@ function OverflowContent({ children, style: styleProp, ...rest }) {
230
265
} ;
231
266
232
267
const observers = {
233
- up : createObserver ( 'up' , '100% 0px -100% 0px' ) ,
234
- left : createObserver ( ' left' , '0px -100% 0px 100%' ) ,
235
- right : createObserver ( ' right' , '0px 100% 0px -100%' ) ,
236
- down : createObserver ( ' down' , '-100% 0px 100% 0px' )
268
+ up : createObserver ( Direction . up , '100% 0px -100% 0px' ) ,
269
+ left : createObserver ( Direction . left , '0px -100% 0px 100%' ) ,
270
+ right : createObserver ( Direction . right , '0px 100% 0px -100%' ) ,
271
+ down : createObserver ( Direction . down , '-100% 0px 100% 0px' )
237
272
} ;
238
273
239
274
observersRef . current = observers ;
@@ -251,7 +286,7 @@ function OverflowContent({ children, style: styleProp, ...rest }) {
251
286
const observers = observersRef . current ;
252
287
const watchNode = watchRef . current ;
253
288
254
- observers . up . observe ( watchNode ) ;
289
+ observers ? .up . observe ( watchNode ) ;
255
290
observers . left . observe ( watchNode ) ;
256
291
observers . right . observe ( watchNode ) ;
257
292
observers . down . observe ( watchNode ) ;
@@ -304,11 +339,12 @@ function OverflowContent({ children, style: styleProp, ...rest }) {
304
339
305
340
OverflowContent . displayName = 'Overflow.Content' ;
306
341
307
- OverflowContent . propTypes = {
342
+ interface OverflowContent {
308
343
/**
309
344
* Content to render inside the scrollable viewport.
310
345
*/
311
- children : PropTypes . node
346
+ children : ReactNode
347
+ style : CSSProperties
312
348
} ;
313
349
314
350
/**
@@ -352,7 +388,7 @@ OverflowContent.propTypes = {
352
388
* </Overflow>
353
389
* ```
354
390
*/
355
- function OverflowIndicator ( { children, direction } ) {
391
+ function OverflowIndicator ( { children, direction } : OverflowIndicator ) {
356
392
const { state, refs } = useOverflow ( ) ;
357
393
const { canScroll } = state ;
358
394
const isActive = direction
@@ -372,19 +408,19 @@ function OverflowIndicator({ children, direction }) {
372
408
373
409
OverflowIndicator . displayName = 'Overflow.Indicator' ;
374
410
375
- OverflowIndicator . propTypes = {
411
+ interface OverflowIndicator {
376
412
/**
377
413
* Indicator to render when scrolling is allowed in the requested direction.
378
414
* If given a function, it will be passed the overflow state and an object
379
415
* containing the `viewport` ref. You can use this `refs` parameter to render
380
416
* an indicator that is also a button that scrolls the viewport (for example).
381
417
*/
382
- children : PropTypes . oneOfType ( [ PropTypes . node , PropTypes . func ] ) ,
418
+ children : ReactElement | ( ( stateArg : boolean | CanScroll , refs : OverflowContext [ 'refs' ] ) => ReactElement )
383
419
/**
384
420
* The scrollabe direction to watch for. If not supplied, the indicator will
385
421
* be active when scrolling is allowed in any direction.
386
422
*/
387
- direction : PropTypes . oneOf ( [ 'up' , 'down' , 'left' , 'right' ] )
423
+ direction : keyof typeof Direction
388
424
} ;
389
425
390
426
Overflow . Indicator = OverflowIndicator ;
0 commit comments