Skip to content

Instantly share code, notes, and snippets.

@mr47
Created January 24, 2022 14:27
Show Gist options
  • Select an option

  • Save mr47/33eb36aa33a1b4a8dd9cf527cdfc9b7a to your computer and use it in GitHub Desktop.

Select an option

Save mr47/33eb36aa33a1b4a8dd9cf527cdfc9b7a to your computer and use it in GitHub Desktop.
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