react-resize-reporter
Version:
Lightweight React Component that reports width and height changes when its parent resizes.
122 lines • 4.98 kB
JavaScript
import React, { PureComponent } from 'react';
import { checkParent } from './check-parent';
const scrollerStyles = {
overflow: 'hidden',
position: 'absolute',
zIndex: -1000,
top: 0,
right: 0,
width: '100%',
height: '100%',
opacity: 0,
pointerEvents: 'none'
};
const expandDetectorStyles = {
transition: '0s',
animation: 'none'
};
const shrinkDetectorStyles = {
transition: '0s',
animation: 'none',
width: '250%',
height: '250%'
};
/**
* Scroll event based resize detector
*/
export class ResizeReporter extends PureComponent {
constructor() {
super(...arguments);
this.containerRef = React.createRef();
this.lastWidth = -1;
this.lastHeight = -1;
this.resetPosition = (container, newWidth, newHeight) => {
const $expand = container.firstChild;
const $shrink = $expand.nextSibling;
// ensure expand detector can scroll
// shrink detector uses percentage so it will always be able to scroll
const $expandDetector = $expand.firstChild;
$expandDetector.style.width = newWidth + 1000 + 'px';
$expandDetector.style.height = newHeight + 1000 + 'px';
// after styles are applied
this._setScrollPositionTicket = window.setTimeout(() => {
$expand.scrollLeft = newWidth + 1000;
$expand.scrollTop = newHeight + 1000;
$shrink.scrollLeft = newWidth * 2.5;
$shrink.scrollTop = newHeight * 2.5;
}, 0);
};
this.checkSize = () => {
if (this.containerRef.current) {
const parent = this.containerRef.current.parentElement;
if (parent) {
const newRect = parent.getBoundingClientRect();
const { width: newWidth, height: newHeight } = newRect;
if (this.props.onSizeChanged) {
if (newWidth !== this.lastWidth || newHeight !== this.lastHeight) {
this.props.onSizeChanged(newWidth, newHeight, newRect);
}
}
if (this.props.onWidthChanged && newWidth !== this.lastWidth) {
this.props.onWidthChanged(newWidth, newRect);
}
if (this.props.onHeightChanged && newHeight !== this.lastHeight) {
this.props.onHeightChanged(newHeight, newRect);
}
this.resetPosition(this.containerRef.current, newWidth, newHeight);
this.lastWidth = newWidth;
this.lastHeight = newHeight;
}
}
};
this.onScroll = () => {
if (this._debounceTicket) {
window.clearTimeout(this._debounceTicket);
this._debounceTicket = null;
}
// height and width scrolling happens on next loop
// add timeout even for 0
this._debounceTicket = window.setTimeout(this.checkSize, this.props.debounce || 0);
};
}
componentDidMount() {
if (this.containerRef.current) {
const parent = this.containerRef.current.parentElement;
if (process.env.NODE_ENV !== 'production') {
checkParent(parent);
}
if (parent) {
if (this.props.reportInit) {
this.checkSize();
}
else {
const rect = parent.getBoundingClientRect();
this.lastWidth = rect.width;
this.lastHeight = rect.height;
this.resetPosition(this.containerRef.current, this.lastWidth, this.lastHeight);
}
}
}
}
componentWillUnmount() {
if (this._debounceTicket) {
window.clearTimeout(this._debounceTicket);
this._debounceTicket = null;
}
if (this._setScrollPositionTicket) {
window.clearTimeout(this._setScrollPositionTicket);
this._setScrollPositionTicket = null;
}
}
render() {
const { reportInit, debounce, onSizeChanged, onWidthChanged, onHeightChanged, children, ...restProps } = this.props;
return (React.createElement("div", Object.assign({ "aria-hidden": true, "aria-label": "react-resize-reporter", tabIndex: -1 }, restProps, { ref: this.containerRef }),
React.createElement("div", { style: scrollerStyles, onScroll: this.onScroll },
React.createElement("div", { style: expandDetectorStyles })),
React.createElement("div", { style: scrollerStyles, onScroll: this.onScroll },
React.createElement("div", { style: shrinkDetectorStyles })),
children));
}
}
export default ResizeReporter;
//# sourceMappingURL=scroll.js.map