-
Notifications
You must be signed in to change notification settings - Fork 718
/
withParentSize.tsx
111 lines (98 loc) · 3.23 KB
/
withParentSize.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import debounce from 'lodash/debounce';
import React from 'react';
import {
DebounceSettings,
Simplify,
PrivateWindow,
ResizeObserverPolyfill,
ResizeObserver,
} from '../types';
const CONTAINER_STYLES = { width: '100%', height: '100%' };
/**
* @deprecated
* @TODO remove in the next major version - exported for backwards compatibility
*/
export type WithParentSizeProps = DebounceSettings;
type WithParentSizeConfig = {
initialWidth?: number;
initialHeight?: number;
} & DebounceSettings;
type WithParentSizeState = {
parentWidth?: number;
parentHeight?: number;
};
export type WithParentSizeProvidedProps = WithParentSizeState;
type WithParentSizeComponentProps<P extends WithParentSizeProvidedProps> = Simplify<
Omit<P, keyof WithParentSizeProvidedProps> & WithParentSizeConfig
>;
export default function withParentSize<P extends WithParentSizeProvidedProps>(
BaseComponent: React.ComponentType<P>,
/** Optionally inject a ResizeObserver polyfill, else this *must* be globally available. */
resizeObserverPolyfill?: ResizeObserverPolyfill,
): React.ComponentType<WithParentSizeComponentProps<P>> {
return class WrappedComponent extends React.Component<
WithParentSizeComponentProps<P>,
WithParentSizeState
> {
displayName = `withParentSize(${
BaseComponent.displayName ?? BaseComponent.name ?? 'Component'
})`;
state = {
parentWidth: undefined,
parentHeight: undefined,
};
animationFrameID: number = 0;
resizeObserver: ResizeObserver | undefined;
container: HTMLDivElement | null = null;
componentDidMount() {
const ResizeObserverLocal =
resizeObserverPolyfill || (window as unknown as PrivateWindow).ResizeObserver;
this.resizeObserver = new ResizeObserverLocal((entries) => {
entries.forEach((entry) => {
const { width, height } = entry.contentRect;
this.animationFrameID = window.requestAnimationFrame(() => {
this.resize({
width,
height,
});
});
});
});
if (this.container) this.resizeObserver.observe(this.container);
}
componentWillUnmount() {
window.cancelAnimationFrame(this.animationFrameID);
if (this.resizeObserver) this.resizeObserver.disconnect();
this.resize.cancel();
}
setRef = (ref: HTMLDivElement) => {
this.container = ref;
};
resize = debounce(
// eslint-disable-next-line unicorn/consistent-function-scoping
({ width, height }: { width: number; height: number }) => {
this.setState({
parentWidth: width,
parentHeight: height,
});
},
this.props.debounceTime ?? 300,
{ leading: this.props.enableDebounceLeadingCall ?? true },
);
render() {
const { initialWidth, initialHeight } = this.props;
const { parentWidth = initialWidth, parentHeight = initialHeight } = this.state;
return (
<div style={CONTAINER_STYLES} ref={this.setRef}>
{parentWidth != null && parentHeight != null && (
<BaseComponent
parentWidth={parentWidth}
parentHeight={parentHeight}
{...(this.props as P)}
/>
)}
</div>
);
}
};
}