Created
January 24, 2022 14:27
-
-
Save mr47/33eb36aa33a1b4a8dd9cf527cdfc9b7a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as React from 'react'; | |
| import createDetectElementResize from './detectElementResize'; | |
| import {useEffect, useLayoutEffect, useRef, useState} from "react"; | |
| // type Size = { | |
| // height: number, | |
| // width: number, | |
| // }; | |
| // | |
| // type Props = { | |
| // /** Function responsible for rendering children.*/ | |
| // children: Size => React.Element<*>, | |
| // | |
| // /** Optional custom CSS class name to attach to root AutoSizer element. */ | |
| // className?: string, | |
| // | |
| // /** Default height to use for initial render; useful for SSR */ | |
| // defaultHeight?: number, | |
| // | |
| // /** Default width to use for initial render; useful for SSR */ | |
| // defaultWidth?: number, | |
| // | |
| // /** Disable dynamic :height property */ | |
| // disableHeight: boolean, | |
| // | |
| // /** Disable dynamic :width property */ | |
| // disableWidth: boolean, | |
| // | |
| // /** Nonce of the inlined stylesheet for Content Security Policy */ | |
| // nonce?: string, | |
| // | |
| // /** Callback to be invoked on-resize */ | |
| // onResize: Size => void, | |
| // | |
| // /** Optional inline style */ | |
| // style: ?Object, | |
| // }; | |
| // | |
| // type State = { | |
| // height: number, | |
| // width: number, | |
| // }; | |
| // | |
| // type ResizeHandler = (element: HTMLElement, onResize: () => void) => void; | |
| // | |
| // type DetectElementResize = { | |
| // addResizeListener: ResizeHandler, | |
| // removeResizeListener: ResizeHandler, | |
| // }; | |
| const AutoSizerV2 = (props) => { | |
| let _parentNode; | |
| let _autoSizer = useRef(null); | |
| let _detectElementResize; | |
| let _window = typeof window !== "undefined" ? window : global; | |
| const { | |
| children, | |
| className, | |
| disableHeight, | |
| disableWidth, | |
| style, | |
| defaultHeight, | |
| defaultWidth | |
| } = props; | |
| const [state, setState] = useState({ | |
| height: defaultHeight || 0, | |
| width: defaultWidth || 0, | |
| }); | |
| const _onResize = () => { | |
| const {disableHeight, disableWidth, onResize} = props; | |
| if (_parentNode) { | |
| // Guard against AutoSizer component being removed from the DOM immediately after being added. | |
| // This can result in invalid style values which can result in NaN values if we don't handle them. | |
| // See issue #150 for more context. | |
| const height = _parentNode.offsetHeight || 0; | |
| const width = _parentNode.offsetWidth || 0; | |
| const style = _window.getComputedStyle(_parentNode) || {}; | |
| const paddingLeft = parseInt(style.paddingLeft, 10) || 0; | |
| const paddingRight = parseInt(style.paddingRight, 10) || 0; | |
| const paddingTop = parseInt(style.paddingTop, 10) || 0; | |
| const paddingBottom = parseInt(style.paddingBottom, 10) || 0; | |
| const newHeight = height - paddingTop - paddingBottom; | |
| const newWidth = width - paddingLeft - paddingRight; | |
| if ( | |
| (!disableHeight && state.height !== newHeight) || | |
| (!disableWidth && state.width !== newWidth) | |
| ) { | |
| setState({ | |
| height: height - paddingTop - paddingBottom, | |
| width: width - paddingLeft - paddingRight, | |
| }); | |
| if (onResize) { | |
| onResize({height, width}); | |
| } | |
| } | |
| } | |
| }; | |
| useLayoutEffect(() => { | |
| const { nonce } = props; | |
| if ( | |
| _autoSizer.current && | |
| _autoSizer.current.parentNode && | |
| _autoSizer.current.parentNode.ownerDocument && | |
| _autoSizer.current.parentNode.ownerDocument.defaultView && | |
| _autoSizer.current.parentNode instanceof | |
| _autoSizer.current.parentNode.ownerDocument.defaultView.HTMLElement | |
| ) { | |
| // Delay access of parentNode until mount. | |
| // This handles edge-cases where the component has already been unmounted before its ref has been set, | |
| // As well as libraries like react-lite which have a slightly different lifecycle. | |
| _parentNode = _autoSizer.current.parentNode; | |
| _window = _autoSizer.current.parentNode.ownerDocument.defaultView; | |
| // Defer requiring resize handler in order to support server-side rendering. | |
| // See issue #41 | |
| _detectElementResize = createDetectElementResize( | |
| nonce, | |
| _window, | |
| ); | |
| _detectElementResize.addResizeListener( | |
| _parentNode, | |
| _onResize, | |
| ); | |
| _onResize(); | |
| } | |
| return () => { | |
| if (_detectElementResize && _parentNode) { | |
| _detectElementResize.removeResizeListener( | |
| _parentNode, | |
| _onResize, | |
| ); | |
| } | |
| } | |
| }, []) | |
| const {height, width} = state; | |
| const outerStyle = { overflow: 'visible' }; | |
| const childParams = {}; | |
| if (!disableHeight) { | |
| outerStyle.height = 0; | |
| childParams.height = height; | |
| } | |
| if (!disableWidth) { | |
| outerStyle.width = 0; | |
| childParams.width = width; | |
| } | |
| return ( | |
| <div | |
| className={className} | |
| ref={_autoSizer} | |
| style={{ | |
| ...outerStyle, | |
| ...style, | |
| }}> | |
| {children(childParams)} | |
| </div> | |
| ); | |
| } | |
| export default AutoSizerV2; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment