11/** @flow */
2- import PropTypes from "prop-types" ;
3- import React , { PureComponent } from "react" ;
2+
3+ import React from "react" ;
44import createDetectElementResize from "../vendor/detectElementResize" ;
55
66/**
77 * Decorator component that automatically adjusts the width and height of a single child.
88 * Child component should not be declared as a child but should rather be specified by a `ChildComponent` property.
99 * All other properties will be passed through to the child component.
1010 */
11- export default class AutoSizer extends PureComponent {
12- static propTypes = {
13- /**
14- * Function responsible for rendering children.
15- * This function should implement the following signature:
16- * ({ height, width }) => PropTypes.element
17- */
18- children : PropTypes . func . isRequired ,
1911
20- /** Disable dynamic :height property */
21- disableHeight : PropTypes . bool ,
12+ type Size = {
13+ height : number ,
14+ width : number
15+ } ;
2216
23- /** Disable dynamic :width property */
24- disableWidth : PropTypes . bool ,
17+ type Props = {
18+ /** Function responsible for rendering children.*/
19+ children : ( warams : Size ) => React . Element < * > ,
2520
26- /** Nonce of the inlined stylesheet for Content Security Policy */
27- nonce : PropTypes . string ,
21+ /** Disable dynamic :height property */
22+ disableHeight : boolean ,
2823
29- /** Callback to be invoked on-resize: ({ height, width }) */
30- onResize : PropTypes . func . isRequired
31- } ;
24+ /** Disable dynamic :width property */
25+ disableWidth : boolean ,
26+
27+ /** Nonce of the inlined stylesheet for Content Security Policy */
28+ nonce ?: string ,
3229
30+ /** Callback to be invoked on-resize */
31+ onResize : ( params : Size ) => void
32+ } ;
33+
34+ type ResizeHandler = ( element : HTMLElement , onResize : ( ) => void ) => void ;
35+
36+ type DetectElementResize = {
37+ addResizeListener : ResizeHandler ,
38+ removeResizeListener : ResizeHandler
39+ }
40+
41+ export default class AutoSizer extends React . PureComponent {
3342 static defaultProps = {
34- onResize : ( ) => { }
43+ onResize : ( ) => { } ,
44+ disableHeight : false ,
45+ disableWidth : false
3546 } ;
3647
37- constructor ( props ) {
38- super ( props ) ;
48+ props : Props ;
3949
40- this . state = {
41- height : 0 ,
42- width : 0
43- } ;
50+ state = {
51+ height : 0 ,
52+ width : 0
53+ } ;
4454
45- this . _onResize = this . _onResize . bind ( this ) ;
46- this . _setRef = this . _setRef . bind ( this ) ;
47- }
55+ _parentNode : ? HTMLElement ;
56+ _autoSizer : ? HTMLElement ;
57+ _detectElementResize : DetectElementResize ;
4858
4959 componentDidMount ( ) {
5060 const { nonce } = this . props ;
61+ if ( this . _autoSizer && this . _autoSizer . parentNode instanceof HTMLElement ) {
62+ // Delay access of parentNode until mount.
63+ // This handles edge-cases where the component has already been unmounted before its ref has been set,
64+ // As well as libraries like react-lite which have a slightly different lifecycle.
65+ this . _parentNode = this . _autoSizer . parentNode ;
66+
67+ // Defer requiring resize handler in order to support server-side rendering.
68+ // See issue #41
69+ this . _detectElementResize = createDetectElementResize ( nonce ) ;
70+ this . _detectElementResize . addResizeListener (
71+ this . _parentNode ,
72+ this . _onResize
73+ ) ;
5174
52- // Delay access of parentNode until mount.
53- // This handles edge-cases where the component has already been unmounted before its ref has been set,
54- // As well as libraries like react-lite which have a slightly different lifecycle.
55- this . _parentNode = this . _autoSizer . parentNode ;
56-
57- // Defer requiring resize handler in order to support server-side rendering.
58- // See issue #41
59- this . _detectElementResize = createDetectElementResize ( nonce ) ;
60- this . _detectElementResize . addResizeListener (
61- this . _parentNode ,
62- this . _onResize
63- ) ;
64-
65- this . _onResize ( ) ;
75+ this . _onResize ( ) ;
76+ }
6677 }
6778
6879 componentWillUnmount ( ) {
69- if ( this . _detectElementResize ) {
80+ if ( this . _detectElementResize && this . _parentNode ) {
7081 this . _detectElementResize . removeResizeListener (
7182 this . _parentNode ,
7283 this . _onResize
@@ -81,7 +92,7 @@ export default class AutoSizer extends PureComponent {
8192 // Outer div should not force width/height since that may prevent containers from shrinking.
8293 // Inner component should overflow and use calculated width/height.
8394 // See issue #68 for more information.
84- const outerStyle = { overflow : "visible" } ;
95+ const outerStyle : Object = { overflow : "visible" } ;
8596
8697 if ( ! disableHeight ) {
8798 outerStyle . height = 0 ;
@@ -110,39 +121,42 @@ export default class AutoSizer extends PureComponent {
110121 ) ;
111122 }
112123
113- _onResize ( ) {
124+ _onResize = ( ) => {
114125 const { disableHeight, disableWidth, onResize } = this . props ;
115126
116- // Guard against AutoSizer component being removed from the DOM immediately after being added.
117- // This can result in invalid style values which can result in NaN values if we don't handle them.
118- // See issue #150 for more context.
127+ if ( this . _parentNode ) {
119128
120- const height = this . _parentNode . offsetHeight || 0 ;
121- const width = this . _parentNode . offsetWidth || 0 ;
129+ // Guard against AutoSizer component being removed from the DOM immediately after being added.
130+ // This can result in invalid style values which can result in NaN values if we don't handle them.
131+ // See issue #150 for more context.
122132
123- const style = window . getComputedStyle ( this . _parentNode ) || { } ;
124- const paddingLeft = parseInt ( style . paddingLeft , 10 ) || 0 ;
125- const paddingRight = parseInt ( style . paddingRight , 10 ) || 0 ;
126- const paddingTop = parseInt ( style . paddingTop , 10 ) || 0 ;
127- const paddingBottom = parseInt ( style . paddingBottom , 10 ) || 0 ;
133+ const height = this . _parentNode . offsetHeight || 0 ;
134+ const width = this . _parentNode . offsetWidth || 0 ;
128135
129- const newHeight = height - paddingTop - paddingBottom ;
130- const newWidth = width - paddingLeft - paddingRight ;
136+ const style = window . getComputedStyle ( this . _parentNode ) || { } ;
137+ const paddingLeft = parseInt ( style . paddingLeft , 10 ) || 0 ;
138+ const paddingRight = parseInt ( style . paddingRight , 10 ) || 0 ;
139+ const paddingTop = parseInt ( style . paddingTop , 10 ) || 0 ;
140+ const paddingBottom = parseInt ( style . paddingBottom , 10 ) || 0 ;
131141
132- if (
133- ( ! disableHeight && this . state . height !== newHeight ) ||
134- ( ! disableWidth && this . state . width !== newWidth )
135- ) {
136- this . setState ( {
137- height : height - paddingTop - paddingBottom ,
138- width : width - paddingLeft - paddingRight
139- } ) ;
142+ const newHeight = height - paddingTop - paddingBottom ;
143+ const newWidth = width - paddingLeft - paddingRight ;
144+
145+ if (
146+ ! disableHeight && this . state . height !== newHeight ||
147+ ! disableWidth && this . state . width !== newWidth
148+ ) {
149+ this . setState ( {
150+ height : height - paddingTop - paddingBottom ,
151+ width : width - paddingLeft - paddingRight
152+ } ) ;
140153
141- onResize ( { height, width } ) ;
154+ onResize ( { height, width } ) ;
155+ }
142156 }
143157 }
144158
145- _setRef ( autoSizer ) {
159+ _setRef = ( autoSizer : HTMLElement | null ) => {
146160 this . _autoSizer = autoSizer ;
147161 }
148162}
0 commit comments