1
- import disableScroll from 'disable-scroll' ;
2
- import React , { useCallback , useState } from 'react' ;
1
+ import React , { useCallback , useEffect , useRef , useState } from 'react' ;
3
2
import { createPortal } from 'react-dom' ;
3
+ import { disableBodyScroll , enableBodyScroll } from 'body-scroll-lock' ;
4
4
import { useKeyDown } from './useKeyDown' ;
5
5
6
6
export interface ModalProps {
@@ -9,6 +9,7 @@ export interface ModalProps {
9
9
close : ( ) => void ;
10
10
onOverlayClick : React . MouseEventHandler < HTMLDivElement > ;
11
11
elementId : 'root' | string ;
12
+ preventScroll ?: boolean ;
12
13
}
13
14
14
15
export interface ModalOptions {
@@ -59,15 +60,42 @@ const Modal: React.FC<ModalProps> = ({
59
60
close,
60
61
onOverlayClick,
61
62
elementId = 'root' ,
63
+ preventScroll = false ,
62
64
} ) => {
65
+ const containerRef = useRef < HTMLDivElement > ( null ) ;
63
66
const [ ref ] = useKeyDown < HTMLDivElement > ( isOpen , close ) ;
64
67
68
+ useEffect ( ( ) => {
69
+ if ( containerRef . current === null ) {
70
+ return ;
71
+ }
72
+ if ( isOpen ) {
73
+ if ( preventScroll ) {
74
+ disableBodyScroll ( containerRef . current , {
75
+ reserveScrollBarGap : true ,
76
+ } ) ;
77
+ }
78
+ } else {
79
+ if ( preventScroll ) {
80
+ enableBodyScroll ( containerRef . current )
81
+ }
82
+ }
83
+ return ( ) => {
84
+ if ( containerRef . current === null ) {
85
+ return ;
86
+ }
87
+ if ( preventScroll ) {
88
+ enableBodyScroll ( containerRef . current )
89
+ }
90
+ }
91
+ } , [ containerRef , isOpen , preventScroll ] ) ;
92
+
65
93
if ( isOpen === false ) {
66
94
return null ;
67
95
}
68
96
69
97
return createPortal (
70
- < div style = { wrapperStyle } >
98
+ < div ref = { containerRef } style = { wrapperStyle } >
71
99
< div style = { overlayStyle } onClick = { onOverlayClick } />
72
100
< div ref = { ref } role = "dialog" aria-modal = { isOpen } style = { containerStyle } tabIndex = { 0 } >
73
101
{ children }
@@ -78,23 +106,17 @@ const Modal: React.FC<ModalProps> = ({
78
106
} ;
79
107
80
108
export const useModal : UseModal = ( elementId = 'root' , options = { } ) => {
81
- const { preventScroll = false , closeOnOverlayClick = true } = options ;
109
+ const { preventScroll, closeOnOverlayClick = true } = options ;
82
110
const [ isOpen , setOpen ] = useState < boolean > ( false ) ;
83
111
84
112
const open = useCallback ( ( ) => {
85
113
setOpen ( true ) ;
86
- if ( preventScroll ) {
87
- disableScroll . on ( ) ;
88
- }
89
- } , [ setOpen , preventScroll ] ) ;
90
-
114
+ } , [ setOpen ] ) ;
115
+
91
116
const close = useCallback ( ( ) => {
92
117
setOpen ( false ) ;
93
- if ( preventScroll ) {
94
- disableScroll . off ( ) ;
95
- }
96
- } , [ setOpen , preventScroll ] ) ;
97
-
118
+ } , [ setOpen ] ) ;
119
+
98
120
const onOverlayClick = useCallback (
99
121
( event : React . MouseEvent < HTMLDivElement > ) => {
100
122
event . stopPropagation ( ) ;
@@ -113,6 +135,7 @@ export const useModal: UseModal = (elementId = 'root', options = {}) => {
113
135
close = { close }
114
136
onOverlayClick = { onOverlayClick }
115
137
elementId = { elementId }
138
+ preventScroll = { preventScroll }
116
139
>
117
140
{ children }
118
141
</ Modal >
0 commit comments