forked from react-grid-layout/react-grid-layout
-
Notifications
You must be signed in to change notification settings - Fork 1
/
WidthProvider.jsx
109 lines (92 loc) · 2.95 KB
/
WidthProvider.jsx
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
// @flow
import React from "react";
import PropTypes from "prop-types";
import ReactDOM from "react-dom";
import type { ComponentType as ReactComponentType } from "react";
type WPProps = {
className?: string,
measureBeforeMount: boolean,
style?: Object,
breakpointFromViewport: boolean,
resizeDelay: number
};
type WPState = {
width: number,
viewportWidth: number
};
/*
* A simple HOC that provides facility for listening to container resizes.
*/
export default function WidthProvider<
Props,
ComposedProps: { ...Props, ...WPProps }
>(
ComposedComponent: ReactComponentType<Props>
): ReactComponentType<ComposedProps> {
class WidthProvider extends React.Component<ComposedProps, WPState> {
static defaultProps = {
measureBeforeMount: false,
breakpointFromViewport: false,
resizeDelay: 0
};
static propTypes = {
// If true, will not render children until mounted. Useful for getting the exact width before
// rendering, to prevent any unsightly resizing.
measureBeforeMount: PropTypes.bool,
breakpointFromViewport: PropTypes.bool,
resizeDelay: PropTypes.number
};
state = {
width: 1280,
viewportWidth: 1280
};
mounted: boolean = false;
resizeTimeout: ?TimeoutID = null;
componentDidMount() {
this.mounted = true;
window.addEventListener('resize', this.onWindowResize);
// Call to properly set the breakpoint and resize the elements.
// Note that if you're doing a full-width element, this can get a little wonky if a scrollbar
// appears because of the grid. In that case, fire your own resize event, or set `overflow: scroll` on your body.
this.calculateWidth();
}
componentWillUnmount() {
this.mounted = false;
window.removeEventListener('resize', this.onWindowResize);
}
onWindowResize = (_event: ?Event) => {
const { resizeDelay } = this.props;
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(() => {
this.calculateWidth();
}, resizeDelay);
};
calculateWidth = () => {
if (!this.mounted) return;
let newState = {};
const node = ReactDOM.findDOMNode(this); // Flow casts this to Text | Element
if (node instanceof HTMLElement) {
newState.width = node.offsetWidth;
}
if (this.props.breakpointFromViewport && typeof window !== "undefined") {
newState.viewportWidth = window.innerWidth;
}
if (Object.keys(newState).length)
this.setState(newState);
};
render() {
const { measureBeforeMount, ...rest } = this.props;
if (measureBeforeMount && !this.mounted) {
return (
<div className={this.props.className} style={this.props.style} />
);
}
return (
<ComposedComponent {...rest} {...this.state} />
);
}
}
return React.forwardRef((props, ref) => {
return <WidthProvider {...props} forwardedRef={ref} />;
});
}