UNPKG

12.4 kBJavaScriptView Raw
1import _extends from 'babel-runtime/helpers/extends';
2import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
3import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
4import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
5import _inherits from 'babel-runtime/helpers/inherits';
6import React from 'react';
7import { findDOMNode } from 'react-dom';
8import PropTypes from 'prop-types';
9import { polyfill } from 'react-lifecycles-compat';
10import { dom } from '../util';
11import VirtualBody from './virtual/body';
12import { statics } from './util';
13
14var noop = function noop() {};
15var THRESHOLD = 10;
16export default function virtual(BaseComponent) {
17 var _class, _temp;
18
19 var VirtualTable = (_temp = _class = function (_React$Component) {
20 _inherits(VirtualTable, _React$Component);
21
22 function VirtualTable(props, context) {
23 _classCallCheck(this, VirtualTable);
24
25 var _this = _possibleConstructorReturn(this, _React$Component.call(this, props, context));
26
27 _this.onScroll = function () {
28 // 避免横向滚动带来的性能问题
29 var scrollTop = _this.bodyNode.scrollTop;
30 if (scrollTop === _this.lastScrollTop) {
31 return;
32 }
33 var start = _this.computeScrollToRow(scrollTop);
34 if (!('scrollToRow' in _this.props)) {
35 _this.setState({
36 scrollToRow: start
37 });
38 }
39 _this.props.onBodyScroll(start);
40 _this.lastScrollTop = scrollTop;
41 };
42
43 _this.getBodyNode = function (node, lockType) {
44 lockType = lockType ? lockType.charAt(0).toUpperCase() + lockType.substr(1) : '';
45 _this['body' + lockType + 'Node'] = node;
46 };
47
48 _this.getTableInstance = function (type, instance) {
49 type = type ? type.charAt(0).toUpperCase() + type.substr(1) : '';
50 _this['table' + type + 'Inc'] = instance;
51 };
52
53 var useVirtual = props.useVirtual,
54 dataSource = props.dataSource;
55
56
57 var hasVirtualData = useVirtual && dataSource && dataSource.length > 0;
58
59 _this.state = {
60 rowHeight: _this.props.rowHeight,
61 scrollToRow: _this.props.scrollToRow,
62 height: _this.props.maxBodyHeight,
63 hasVirtualData: hasVirtualData
64 };
65 return _this;
66 }
67
68 VirtualTable.prototype.getChildContext = function getChildContext() {
69 return {
70 onVirtualScroll: this.onScroll,
71 bodyHeight: this.computeBodyHeight(),
72 innerTop: this.computeInnerTop(),
73 getBodyNode: this.getBodyNode,
74 getTableInstanceForVirtual: this.getTableInstance,
75 rowSelection: this.rowSelection
76 };
77 };
78
79 VirtualTable.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
80 var state = {};
81
82 if ('maxBodyHeight' in nextProps) {
83 if (prevState.height !== nextProps.maxBodyHeight) {
84 state.height = nextProps.maxBodyHeight;
85 }
86 }
87
88 if ('scrollToRow' in nextProps) {
89 state.scrollToRow = nextProps.scrollToRow;
90 }
91
92 if (prevState.useVirtual !== nextProps.useVirtual || prevState.dataSource !== nextProps.dataSource) {
93 state.hasVirtualData = nextProps.useVirtual && nextProps.dataSource && nextProps.dataSource.length > 0;
94 }
95
96 return state;
97 };
98
99 VirtualTable.prototype.componentDidMount = function componentDidMount() {
100 if (this.state.hasVirtualData && this.bodyNode) {
101 this.lastScrollTop = this.bodyNode.scrollTop;
102 }
103
104 this.adjustScrollTop();
105 this.adjustSize();
106 this.reComputeSize();
107 };
108
109 VirtualTable.prototype.componentDidUpdate = function componentDidUpdate() {
110 this.adjustScrollTop();
111 this.adjustSize();
112 this.reComputeSize();
113 };
114
115 VirtualTable.prototype.reComputeSize = function reComputeSize() {
116 var _state = this.state,
117 rowHeight = _state.rowHeight,
118 hasVirtualData = _state.hasVirtualData;
119
120 if (typeof rowHeight === 'function' && hasVirtualData) {
121 var row = this.getRowNode();
122 var rowClientHeight = row && row.clientHeight;
123 if (rowClientHeight !== this.state.rowHeight) {
124 this.setState({
125 rowHeight: rowClientHeight
126 });
127 }
128 }
129 };
130
131 VirtualTable.prototype.computeBodyHeight = function computeBodyHeight() {
132 var rowHeight = this.state.rowHeight;
133 var dataSource = this.props.dataSource;
134
135 if (typeof rowHeight === 'function') {
136 return 0;
137 }
138 var count = 0;
139 dataSource.forEach(function (item) {
140 if (!item.__hidden) {
141 count += 1;
142 }
143 });
144 return count * rowHeight;
145 };
146
147 VirtualTable.prototype.computeInnerTop = function computeInnerTop() {
148 var rowHeight = this.state.rowHeight;
149
150 if (typeof rowHeight === 'function') {
151 return 0;
152 }
153
154 var start = Math.max(this.start - THRESHOLD, 0);
155
156 return start * rowHeight;
157 };
158
159 VirtualTable.prototype.getVisibleRange = function getVisibleRange(ExpectStart) {
160 var _state2 = this.state,
161 height = _state2.height,
162 rowHeight = _state2.rowHeight;
163
164 var len = this.props.dataSource.length;
165
166 var end = void 0,
167 visibleCount = 0;
168 var start = 0;
169 if (typeof rowHeight === 'function') {
170 // try get cell height;
171 end = 1;
172 } else {
173 visibleCount = parseInt(dom.getPixels(height) / rowHeight, 10);
174
175 if ('number' === typeof ExpectStart) {
176 start = ExpectStart < len ? ExpectStart : 0;
177 }
178
179 end = Math.min(+start + 1 + visibleCount + 10, len);
180 }
181 this.end = end;
182 this.visibleCount = visibleCount;
183 return {
184 start: start,
185 end: end
186 };
187 };
188
189 VirtualTable.prototype.adjustScrollTop = function adjustScrollTop() {
190 if (this.state.hasVirtualData && this.bodyNode) {
191 this.bodyNode.scrollTop = this.lastScrollTop % this.state.rowHeight + this.state.rowHeight * this.state.scrollToRow;
192 }
193 };
194
195 VirtualTable.prototype.adjustSize = function adjustSize() {
196 if (this.state.hasVirtualData && this.bodyNode) {
197 var body = this.bodyNode;
198 var virtualScrollNode = body.querySelector('div');
199 var clientHeight = body.clientHeight,
200 clientWidth = body.clientWidth;
201
202
203 var tableInc = this.tableInc;
204 var tableNode = findDOMNode(tableInc);
205 var prefix = this.props.prefix;
206
207 var headerNode = tableNode.querySelector('.' + prefix + 'table-header table');
208 var headerClientWidth = headerNode && headerNode.clientWidth;
209 // todo 2.0 设置宽度这个可以去掉
210 if (clientWidth < headerClientWidth) {
211 dom.setStyle(virtualScrollNode, 'min-width', headerClientWidth);
212 var leftNode = this.bodyLeftNode;
213 var rightNode = this.bodyRightNode;
214 leftNode && dom.setStyle(leftNode, 'max-height', clientHeight);
215 rightNode && dom.setStyle(rightNode, 'max-height', clientHeight);
216 } else {
217 dom.setStyle(virtualScrollNode, 'min-width', 'auto');
218 }
219 }
220 };
221
222 VirtualTable.prototype.computeScrollToRow = function computeScrollToRow(offset) {
223 var rowHeight = this.state.rowHeight;
224
225 var start = parseInt(offset / rowHeight);
226 this.start = start;
227 return start;
228 };
229
230 VirtualTable.prototype.getRowNode = function getRowNode() {
231 try {
232 // in case of finding an unmounted component due to cached data
233 // need to clear refs of this.tableInc when dataSource Changed
234 // use try catch for temporary
235 return findDOMNode(this.tableInc.getRowRef(0));
236 } catch (error) {
237 return null;
238 }
239 };
240
241 VirtualTable.prototype.render = function render() {
242 /* eslint-disable no-unused-vars, prefer-const */
243 var _props = this.props,
244 useVirtual = _props.useVirtual,
245 components = _props.components,
246 dataSource = _props.dataSource,
247 fixedHeader = _props.fixedHeader,
248 rowHeight = _props.rowHeight,
249 scrollToRow = _props.scrollToRow,
250 onBodyScroll = _props.onBodyScroll,
251 others = _objectWithoutProperties(_props, ['useVirtual', 'components', 'dataSource', 'fixedHeader', 'rowHeight', 'scrollToRow', 'onBodyScroll']);
252
253 var entireDataSource = dataSource;
254 var newDataSource = dataSource;
255
256 this.rowSelection = this.props.rowSelection;
257 if (this.state.hasVirtualData) {
258 newDataSource = [];
259 components = _extends({}, components);
260
261 var _getVisibleRange = this.getVisibleRange(this.state.scrollToRow),
262 start = _getVisibleRange.start,
263 end = _getVisibleRange.end;
264
265 var count = -1;
266 dataSource.forEach(function (current, index, record) {
267 if (!current.__hidden) {
268 count += 1;
269 if (count >= Math.max(start - THRESHOLD, 0) && count < end) {
270 newDataSource.push(current);
271 }
272 }
273 current.__rowIndex = index;
274 });
275
276 if (!components.Body) {
277 components.Body = VirtualBody;
278 }
279 fixedHeader = true;
280 }
281
282 return React.createElement(BaseComponent, _extends({}, others, {
283 scrollToRow: scrollToRow,
284 dataSource: newDataSource,
285 entireDataSource: entireDataSource,
286 components: components,
287 fixedHeader: fixedHeader
288 }));
289 };
290
291 return VirtualTable;
292 }(React.Component), _class.VirtualBody = VirtualBody, _class.propTypes = _extends({
293 /**
294 * 是否开启虚拟滚动
295 */
296 useVirtual: PropTypes.bool,
297 /**
298 * 设置行高
299 */
300 rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
301 maxBodyHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
302 primaryKey: PropTypes.oneOfType([PropTypes.symbol, PropTypes.string]),
303 dataSource: PropTypes.array,
304 /**
305 * 在内容区域滚动的时候触发的函数
306 */
307 onBodyScroll: PropTypes.func
308 }, BaseComponent.propTypes), _class.defaultProps = _extends({}, BaseComponent.defaultProps, {
309 primaryKey: 'id',
310 rowHeight: noop,
311 maxBodyHeight: 200,
312 components: {},
313 prefix: 'next-',
314 onBodyScroll: noop
315 }), _class.childContextTypes = {
316 onVirtualScroll: PropTypes.func,
317 bodyHeight: PropTypes.number,
318 innerTop: PropTypes.number,
319 getBodyNode: PropTypes.func,
320 getTableInstanceForVirtual: PropTypes.func,
321 rowSelection: PropTypes.object
322 }, _temp);
323 VirtualTable.displayName = 'VirtualTable';
324
325 statics(VirtualTable, BaseComponent);
326 return polyfill(VirtualTable);
327}
\No newline at end of file