@@ -86,7 +86,8 @@ class Modal extends Component<ModalProps, ModalState> {
8686
8787 this . state = {
8888 transitioning : false ,
89- open : props . open ?? false
89+ open : props . open ?? false ,
90+ windowHeight : window . innerHeight
9091 }
9192 }
9293
@@ -101,6 +102,7 @@ class Modal extends Component<ModalProps, ModalState> {
101102
102103 componentDidMount ( ) {
103104 this . props . makeStyles ?.( )
105+ window . addEventListener ( 'resize' , this . updateHeight )
104106 }
105107
106108 componentDidUpdate ( prevProps : ModalProps ) {
@@ -110,6 +112,14 @@ class Modal extends Component<ModalProps, ModalState> {
110112 this . props . makeStyles ?.( )
111113 }
112114
115+ componentWillUnmount ( ) {
116+ window . removeEventListener ( 'resize' , this . updateHeight )
117+ }
118+
119+ updateHeight = ( ) => {
120+ this . setState ( { windowHeight : window . innerHeight } )
121+ }
122+
113123 get defaultFocusElement ( ) {
114124 return this . props . defaultFocusElement
115125 }
@@ -146,21 +156,85 @@ class Modal extends Component<ModalProps, ModalState> {
146156 }
147157 }
148158
159+ getWindowHeightInRem = ( ) : number => {
160+ if ( typeof window === 'undefined' || typeof document === 'undefined' ) {
161+ // Default to a large number to avoid SSR issues
162+ return Infinity
163+ }
164+ const rootFontSize = parseFloat (
165+ getComputedStyle ( document . documentElement ) ?. fontSize || '16'
166+ )
167+ if ( isNaN ( rootFontSize ) || rootFontSize <= 0 ) {
168+ return Infinity
169+ }
170+ return window . innerHeight / rootFontSize
171+ }
172+
149173 renderChildren ( ) {
150174 const { children, variant, overflow } = this . props
151175
176+ // header should be non-sticky for small viewport height (ca. 320px)
177+ if ( this . getWindowHeightInRem ( ) <= 20 ) {
178+ return this . renderForSmallViewportHeight ( )
179+ }
180+
152181 return Children . map ( children as ReactElement , ( child ) => {
153182 if ( ! child ) return // ignore null, falsy children
183+ return this . cloneChildWithProps ( child , variant , overflow )
184+ } )
185+ }
186+
187+ renderForSmallViewportHeight ( ) {
188+ const { children, variant, overflow, styles } = this . props
154189
190+ const headerAndBody : React . ReactNode [ ] = [ ]
191+
192+ const childrenArray = Children . toArray ( children )
193+
194+ // Separate header and body elements
195+ const filteredChildren = childrenArray . filter ( ( child ) => {
155196 if ( isValidElement ( child ) ) {
156- return safeCloneElement ( child , {
157- variant : variant ,
158- overflow : ( child ?. props as { overflow : string } ) ?. overflow || overflow
159- } )
160- } else {
161- return child
197+ if ( child . type === Modal . Header || child . type === Modal . Body ) {
198+ if ( child . type === Modal . Header ) {
199+ const headerWithProp = safeCloneElement ( child , {
200+ smallPortView : true
201+ } )
202+ headerAndBody . push ( headerWithProp )
203+ } else {
204+ headerAndBody . push ( child )
205+ }
206+ return false
207+ }
162208 }
209+ return true
163210 } )
211+
212+ // Adds the <div> to the beginning of the filteredChildren array
213+ if ( headerAndBody . length > 0 ) {
214+ filteredChildren . unshift (
215+ < div css = { styles ?. joinedHeaderAndBody } > { headerAndBody } </ div >
216+ )
217+ }
218+
219+ return Children . map ( filteredChildren as ReactElement [ ] , ( child ) => {
220+ if ( ! child ) return // ignore null, falsy children
221+ return this . cloneChildWithProps ( child , variant , overflow )
222+ } )
223+ }
224+
225+ cloneChildWithProps (
226+ child : React . ReactNode ,
227+ variant : string | undefined ,
228+ overflow : string | undefined
229+ ) {
230+ if ( isValidElement ( child ) ) {
231+ return safeCloneElement ( child , {
232+ variant : variant ,
233+ overflow : ( child ?. props as { overflow : string } ) ?. overflow || overflow
234+ } )
235+ } else {
236+ return child
237+ }
164238 }
165239
166240 renderDialog (
0 commit comments