-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
CellMeasurer.js
148 lines (123 loc) · 3.88 KB
/
CellMeasurer.js
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/** @flow */
import * as React from 'react';
import {findDOMNode} from 'react-dom';
import CellMeasurerCache from './CellMeasurerCache.js';
type Children = (params: {measure: () => void}) => React.Element<*>;
type Cell = {
columnIndex: number,
rowIndex: number,
};
type Props = {
cache: CellMeasurerCache,
children: Children | React.Element<*>,
columnIndex?: number,
index?: number,
parent: {
invalidateCellSizeAfterRender?: (cell: Cell) => void,
recomputeGridSize?: (cell: Cell) => void,
},
rowIndex?: number,
};
/**
* Wraps a cell and measures its rendered content.
* Measurements are stored in a per-cell cache.
* Cached-content is not be re-measured.
*/
export default class CellMeasurer extends React.PureComponent<Props> {
static __internalCellMeasurerFlag = false;
componentDidMount() {
this._maybeMeasureCell();
}
componentDidUpdate() {
this._maybeMeasureCell();
}
render() {
const {children} = this.props;
return typeof children === 'function'
? children({measure: this._measure})
: children;
}
_getCellMeasurements() {
const {cache} = this.props;
const node = findDOMNode(this);
// TODO Check for a bad combination of fixedWidth and missing numeric width or vice versa with height
if (node instanceof HTMLElement) {
const styleWidth = node.style.width;
const styleHeight = node.style.height;
// If we are re-measuring a cell that has already been measured,
// It will have a hard-coded width/height from the previous measurement.
// The fact that we are measuring indicates this measurement is probably stale,
// So explicitly clear it out (eg set to "auto") so we can recalculate.
// See issue #593 for more info.
// Even if we are measuring initially- if we're inside of a MultiGrid component,
// Explicitly clear width/height before measuring to avoid being tainted by another Grid.
// eg top/left Grid renders before bottom/right Grid
// Since the CellMeasurerCache is shared between them this taints derived cell size values.
if (!cache.hasFixedWidth()) {
node.style.width = 'auto';
}
if (!cache.hasFixedHeight()) {
node.style.height = 'auto';
}
const height = Math.ceil(node.offsetHeight);
const width = Math.ceil(node.offsetWidth);
// Reset after measuring to avoid breaking styles; see #660
if (styleWidth) {
node.style.width = styleWidth;
}
if (styleHeight) {
node.style.height = styleHeight;
}
return {height, width};
} else {
return {height: 0, width: 0};
}
}
_maybeMeasureCell() {
const {
cache,
columnIndex = 0,
parent,
rowIndex = this.props.index || 0,
} = this.props;
if (!cache.has(rowIndex, columnIndex)) {
const {height, width} = this._getCellMeasurements();
cache.set(rowIndex, columnIndex, width, height);
// If size has changed, let Grid know to re-render.
if (
parent &&
typeof parent.invalidateCellSizeAfterRender === 'function'
) {
parent.invalidateCellSizeAfterRender({
columnIndex,
rowIndex,
});
}
}
}
_measure = () => {
const {
cache,
columnIndex = 0,
parent,
rowIndex = this.props.index || 0,
} = this.props;
const {height, width} = this._getCellMeasurements();
if (
height !== cache.getHeight(rowIndex, columnIndex) ||
width !== cache.getWidth(rowIndex, columnIndex)
) {
cache.set(rowIndex, columnIndex, width, height);
if (parent && typeof parent.recomputeGridSize === 'function') {
parent.recomputeGridSize({
columnIndex,
rowIndex,
});
}
}
};
}
// Used for DEV mode warning check
if (process.env.NODE_ENV !== 'production') {
CellMeasurer.__internalCellMeasurerFlag = true;
}