1 | 'use strict';
|
2 |
|
3 | exports.__esModule = true;
|
4 |
|
5 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
6 |
|
7 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
8 |
|
9 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
|
10 |
|
11 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
|
12 |
|
13 | var _inherits2 = require('babel-runtime/helpers/inherits');
|
14 |
|
15 | var _inherits3 = _interopRequireDefault(_inherits2);
|
16 |
|
17 | var _class, _temp;
|
18 |
|
19 | var _propTypes = require('prop-types');
|
20 |
|
21 | var _propTypes2 = _interopRequireDefault(_propTypes);
|
22 |
|
23 | var _react = require('react');
|
24 |
|
25 | var _react2 = _interopRequireDefault(_react);
|
26 |
|
27 | var _classnames = require('classnames');
|
28 |
|
29 | var _classnames2 = _interopRequireDefault(_classnames);
|
30 |
|
31 | var _reactLifecyclesCompat = require('react-lifecycles-compat');
|
32 |
|
33 | var _reactDom = require('react-dom');
|
34 |
|
35 | var _util = require('../util');
|
36 |
|
37 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
38 |
|
39 | var NOOP = function NOOP() {};
|
40 | var MAX_SYNC_UPDATES = 40;
|
41 |
|
42 | var isEqualSubset = function isEqualSubset(a, b) {
|
43 | for (var key in b) {
|
44 | if (a[key] !== b[key]) {
|
45 | return false;
|
46 | }
|
47 | }
|
48 |
|
49 | return true;
|
50 | };
|
51 |
|
52 | var getOffset = function getOffset(el) {
|
53 | var offset = el.clientLeft || 0;
|
54 | do {
|
55 | offset += el.offsetTop || 0;
|
56 | el = el.offsetParent;
|
57 | } while (el);
|
58 | return offset;
|
59 | };
|
60 |
|
61 | var constrain = function constrain(from, size, _ref) {
|
62 | var children = _ref.children,
|
63 | minSize = _ref.minSize;
|
64 |
|
65 | var length = children && children.length;
|
66 | size = Math.max(size, minSize);
|
67 | if (size > length) {
|
68 | size = length;
|
69 | }
|
70 | from = from ? Math.max(Math.min(from, length - size), 0) : 0;
|
71 |
|
72 | return { from: from, size: size };
|
73 | };
|
74 |
|
75 | var VirtualList = (_temp = _class = function (_Component) {
|
76 | (0, _inherits3.default)(VirtualList, _Component);
|
77 |
|
78 | function VirtualList(props) {
|
79 | (0, _classCallCheck3.default)(this, VirtualList);
|
80 |
|
81 | var _this = (0, _possibleConstructorReturn3.default)(this, _Component.call(this, props));
|
82 |
|
83 | var jumpIndex = props.jumpIndex;
|
84 |
|
85 | var _constrain = constrain(jumpIndex, 0, props),
|
86 | from = _constrain.from,
|
87 | size = _constrain.size;
|
88 |
|
89 | _this.state = { from: from, size: size };
|
90 | _this.cache = {};
|
91 | _this.cacheAdd = {};
|
92 | _this.scrollTo = _this.scrollTo.bind(_this);
|
93 | _this.cachedScroll = null;
|
94 | _this.unstable = false;
|
95 | _this.updateCounter = 0;
|
96 | return _this;
|
97 | }
|
98 |
|
99 | VirtualList.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
|
100 | var from = prevState.from,
|
101 | size = prevState.size;
|
102 |
|
103 |
|
104 | return constrain(from, size, nextProps);
|
105 | };
|
106 |
|
107 | VirtualList.prototype.componentDidMount = function componentDidMount() {
|
108 | var jumpIndex = this.props.jumpIndex;
|
109 |
|
110 |
|
111 | this.updateFrameAndClearCache = this.updateFrameAndClearCache.bind(this);
|
112 |
|
113 | _util.events.on(window, 'resize', this.updateFrameAndClearCache);
|
114 |
|
115 | this.updateFrame(this.scrollTo.bind(this, jumpIndex));
|
116 | };
|
117 |
|
118 | VirtualList.prototype.componentDidUpdate = function componentDidUpdate(prevProps) {
|
119 | var _this2 = this;
|
120 |
|
121 | var oldIndex = prevProps.jumpIndex;
|
122 | var newIndex = this.props.jumpIndex;
|
123 |
|
124 | if (oldIndex !== newIndex) {
|
125 | this.updateFrame(this.scrollTo.bind(this, newIndex));
|
126 | }
|
127 |
|
128 |
|
129 | if (this.unstable) {
|
130 | return;
|
131 | }
|
132 |
|
133 | if (++this.updateCounter > MAX_SYNC_UPDATES) {
|
134 | this.unstable = true;
|
135 | }
|
136 |
|
137 | if (!this.updateCounterTimeoutId) {
|
138 | this.updateCounterTimeoutId = setTimeout(function () {
|
139 | _this2.updateCounter = 0;
|
140 | delete _this2.updateCounterTimeoutId;
|
141 | }, 0);
|
142 | }
|
143 |
|
144 | this.updateFrame();
|
145 | };
|
146 |
|
147 | VirtualList.prototype.componentWillUnmount = function componentWillUnmount() {
|
148 | _util.events.off(window, 'resize', this.updateFrameAndClearCache);
|
149 |
|
150 | _util.events.off(this.scrollParent, 'scroll', this.updateFrameAndClearCache);
|
151 | _util.events.off(this.scrollParent, 'mousewheel', NOOP);
|
152 | };
|
153 |
|
154 | VirtualList.prototype.maybeSetState = function maybeSetState(b, cb) {
|
155 | if (isEqualSubset(this.state, b)) {
|
156 | return cb();
|
157 | }
|
158 |
|
159 | this.setState(b, cb);
|
160 | };
|
161 |
|
162 | VirtualList.prototype.getEl = function getEl() {
|
163 | return this.el || this.items || {};
|
164 | };
|
165 |
|
166 | VirtualList.prototype.getScrollParent = function getScrollParent() {
|
167 | var el = this.getEl();
|
168 | el = el.parentElement;
|
169 |
|
170 | switch (window.getComputedStyle(el).overflowY) {
|
171 | case 'auto':
|
172 | case 'scroll':
|
173 | case 'overlay':
|
174 | case 'visible':
|
175 | return el;
|
176 | }
|
177 |
|
178 | return window;
|
179 | };
|
180 |
|
181 | VirtualList.prototype.getScroll = function getScroll() {
|
182 |
|
183 |
|
184 |
|
185 |
|
186 | var scrollParent = this.scrollParent;
|
187 |
|
188 | var scrollKey = 'scrollTop';
|
189 | var actual = scrollParent === window ?
|
190 |
|
191 |
|
192 | document.body[scrollKey] || document.documentElement[scrollKey] : scrollParent[scrollKey];
|
193 | var max = this.getScrollSize() - this.getViewportSize();
|
194 |
|
195 | var scroll = Math.max(0, Math.min(actual, max));
|
196 | var el = this.getEl();
|
197 | this.cachedScroll = getOffset(scrollParent) + scroll - getOffset(el);
|
198 |
|
199 | return this.cachedScroll;
|
200 | };
|
201 |
|
202 | VirtualList.prototype.setScroll = function setScroll(offset) {
|
203 | var scrollParent = this.scrollParent;
|
204 |
|
205 | offset += getOffset(this.getEl());
|
206 | if (scrollParent === window) {
|
207 | return window.scrollTo(0, offset);
|
208 | }
|
209 |
|
210 | offset -= getOffset(this.scrollParent);
|
211 | scrollParent.scrollTop = offset;
|
212 | };
|
213 |
|
214 | VirtualList.prototype.getViewportSize = function getViewportSize() {
|
215 | var scrollParent = this.scrollParent;
|
216 |
|
217 | return scrollParent === window ? window.innerHeight : scrollParent.clientHeight;
|
218 | };
|
219 |
|
220 | VirtualList.prototype.getScrollSize = function getScrollSize() {
|
221 | var scrollParent = this.scrollParent;
|
222 | var _document = document,
|
223 | body = _document.body,
|
224 | documentElement = _document.documentElement;
|
225 |
|
226 | var key = 'scrollHeight';
|
227 | return scrollParent === window ? Math.max(body[key], documentElement[key]) : scrollParent[key];
|
228 | };
|
229 |
|
230 | VirtualList.prototype.getStartAndEnd = function getStartAndEnd() {
|
231 | var threshold = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props.threshold;
|
232 |
|
233 | var scroll = this.getScroll();
|
234 |
|
235 | var trueScroll = scroll;
|
236 | var start = Math.max(0, trueScroll - threshold);
|
237 | var end = trueScroll + this.getViewportSize() + threshold;
|
238 |
|
239 | return { start: start, end: end };
|
240 | };
|
241 |
|
242 |
|
243 |
|
244 |
|
245 | VirtualList.prototype.updateFrameAndClearCache = function updateFrameAndClearCache(cb) {
|
246 | this.cachedScroll = null;
|
247 | return this.updateFrame(cb);
|
248 | };
|
249 |
|
250 | VirtualList.prototype.updateFrame = function updateFrame(cb) {
|
251 | this.updateScrollParent();
|
252 |
|
253 | if (typeof cb !== 'function') {
|
254 | cb = NOOP;
|
255 | }
|
256 |
|
257 | return this.updateVariableFrame(cb);
|
258 | };
|
259 |
|
260 | VirtualList.prototype.updateScrollParent = function updateScrollParent() {
|
261 | var prev = this.scrollParent;
|
262 | this.scrollParent = this.getScrollParent();
|
263 |
|
264 | if (prev === this.scrollParent) {
|
265 | return;
|
266 | }
|
267 | if (prev) {
|
268 | _util.events.off(prev, 'scroll', this.updateFrameAndClearCache);
|
269 | _util.events.off(prev, 'mousewheel', NOOP);
|
270 | }
|
271 |
|
272 | _util.events.on(this.scrollParent, 'scroll', this.updateFrameAndClearCache);
|
273 | _util.events.on(this.scrollParent, 'mousewheel', NOOP);
|
274 |
|
275 |
|
276 |
|
277 | };
|
278 |
|
279 | VirtualList.prototype.updateVariableFrame = function updateVariableFrame(cb) {
|
280 | if (!this.props.itemSizeGetter) {
|
281 | this.cacheSizes();
|
282 | }
|
283 |
|
284 | var _getStartAndEnd = this.getStartAndEnd(),
|
285 | start = _getStartAndEnd.start,
|
286 | end = _getStartAndEnd.end;
|
287 |
|
288 | var _props = this.props,
|
289 | pageSize = _props.pageSize,
|
290 | children = _props.children;
|
291 |
|
292 | var length = children.length;
|
293 | var space = 0;
|
294 | var from = 0;
|
295 | var size = 0;
|
296 | var maxFrom = length - 1;
|
297 |
|
298 | while (from < maxFrom) {
|
299 | var itemSize = this.getSizeOf(from);
|
300 | if (itemSize === null || itemSize === undefined || space + itemSize > start) {
|
301 | break;
|
302 | }
|
303 | space += itemSize;
|
304 | ++from;
|
305 | }
|
306 |
|
307 | var maxSize = length - from;
|
308 |
|
309 | while (size < maxSize && space < end) {
|
310 | var _itemSize = this.getSizeOf(from + size);
|
311 | if (_itemSize === null || _itemSize === undefined) {
|
312 | size = Math.min(size + pageSize, maxSize);
|
313 | break;
|
314 | }
|
315 | space += _itemSize;
|
316 | ++size;
|
317 | }
|
318 |
|
319 | this.maybeSetState({ from: from, size: size }, cb);
|
320 | };
|
321 |
|
322 | VirtualList.prototype.getSpaceBefore = function getSpaceBefore(index) {
|
323 | var cache = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
324 |
|
325 | if (!index) {
|
326 | return 0;
|
327 | }
|
328 | if (cache[index] !== null && cache[index] !== undefined) {
|
329 | return cache[index] || 0;
|
330 | }
|
331 |
|
332 |
|
333 | var from = index;
|
334 | while (from > 0 && (cache[from] === null || cache[from] === undefined)) {
|
335 | from--;
|
336 | }
|
337 |
|
338 |
|
339 | var space = cache[from] || 0;
|
340 | for (var i = from; i < index; ++i) {
|
341 | cache[i] = space;
|
342 | var itemSize = this.getSizeOf(i);
|
343 | if (itemSize === null || itemSize === undefined) {
|
344 | break;
|
345 | }
|
346 | space += itemSize;
|
347 | }
|
348 |
|
349 | cache[index] = space;
|
350 |
|
351 | return cache[index] || 0;
|
352 | };
|
353 |
|
354 | VirtualList.prototype.cacheSizes = function cacheSizes() {
|
355 | var cache = this.cache;
|
356 | var from = this.state.from;
|
357 | var _items = this.items,
|
358 | children = _items.children,
|
359 | _items$props = _items.props,
|
360 | props = _items$props === undefined ? {} : _items$props;
|
361 |
|
362 | var itemEls = children || props.children || [];
|
363 |
|
364 | try {
|
365 |
|
366 | for (var i = 0, l = itemEls.length; i < l; ++i) {
|
367 | var ulRef = (0, _reactDom.findDOMNode)(this.items);
|
368 | var height = ulRef.children[i].offsetHeight;
|
369 | if (height > 0) {
|
370 | cache[from + i] = height;
|
371 | }
|
372 | }
|
373 | } catch (error) {
|
374 |
|
375 | }
|
376 | };
|
377 |
|
378 | VirtualList.prototype.getSizeOf = function getSizeOf(index) {
|
379 | var cache = this.cache;
|
380 | var _props2 = this.props,
|
381 | itemSizeGetter = _props2.itemSizeGetter,
|
382 | jumpIndex = _props2.jumpIndex;
|
383 |
|
384 |
|
385 |
|
386 | if (index in cache) {
|
387 | return cache[index];
|
388 | }
|
389 | if (itemSizeGetter) {
|
390 | return itemSizeGetter(index);
|
391 | }
|
392 |
|
393 | if (!this.defaultItemHeight && jumpIndex > -1) {
|
394 | var keysList = Object.keys(this.cache);
|
395 | var len = keysList.length;
|
396 | var height = this.cache[len - 1];
|
397 | this.defaultItemHeight = height;
|
398 | }
|
399 |
|
400 | if (this.defaultItemHeight) {
|
401 | return this.defaultItemHeight;
|
402 | }
|
403 | };
|
404 |
|
405 | VirtualList.prototype.scrollTo = function scrollTo(index) {
|
406 | this.setScroll(this.getSpaceBefore(index, this.cacheAdd));
|
407 | };
|
408 |
|
409 | VirtualList.prototype.renderMenuItems = function renderMenuItems() {
|
410 | var _this3 = this;
|
411 |
|
412 | var _props3 = this.props,
|
413 | children = _props3.children,
|
414 | itemsRenderer = _props3.itemsRenderer;
|
415 | var _state = this.state,
|
416 | from = _state.from,
|
417 | size = _state.size;
|
418 |
|
419 | var items = [];
|
420 |
|
421 | for (var i = 0; i < size; ++i) {
|
422 | items.push(children[from + i]);
|
423 | }
|
424 |
|
425 | return itemsRenderer(items, function (c) {
|
426 | _this3.items = c;
|
427 | return _this3.items;
|
428 | });
|
429 | };
|
430 |
|
431 | VirtualList.prototype.render = function render() {
|
432 | var _cx,
|
433 | _this4 = this;
|
434 |
|
435 | var _props4 = this.props,
|
436 | _props4$children = _props4.children,
|
437 | children = _props4$children === undefined ? [] : _props4$children,
|
438 | prefix = _props4.prefix,
|
439 | className = _props4.className;
|
440 |
|
441 | var length = children.length;
|
442 | var from = this.state.from;
|
443 |
|
444 | var items = this.renderMenuItems();
|
445 |
|
446 | var style = { position: 'relative' };
|
447 |
|
448 | var size = this.getSpaceBefore(length, {});
|
449 |
|
450 | if (size) {
|
451 | style.height = size;
|
452 | }
|
453 | var offset = this.getSpaceBefore(from, this.cacheAdd);
|
454 | var transform = 'translate(0px, ' + offset + 'px)';
|
455 | var listStyle = {
|
456 | msTransform: transform,
|
457 | WebkitTransform: transform,
|
458 | transform: transform
|
459 | };
|
460 |
|
461 | var cls = (0, _classnames2.default)((_cx = {}, _cx[prefix + 'virtual-list-wrapper'] = true, _cx[className] = !!className, _cx));
|
462 |
|
463 | return _react2.default.createElement(
|
464 | 'div',
|
465 | {
|
466 | className: cls,
|
467 | style: style,
|
468 | ref: function ref(c) {
|
469 | _this4.el = c;
|
470 | return _this4.el;
|
471 | }
|
472 | },
|
473 | _react2.default.createElement(
|
474 | 'div',
|
475 | { style: listStyle },
|
476 | items
|
477 | )
|
478 | );
|
479 | };
|
480 |
|
481 | return VirtualList;
|
482 | }(_react.Component), _class.displayName = 'VirtualList', _class.propTypes = {
|
483 | prefix: _propTypes2.default.string,
|
484 | |
485 |
|
486 |
|
487 | children: _propTypes2.default.any,
|
488 | |
489 |
|
490 |
|
491 | minSize: _propTypes2.default.number,
|
492 | |
493 |
|
494 |
|
495 | pageSize: _propTypes2.default.number,
|
496 | |
497 |
|
498 |
|
499 | itemsRenderer: _propTypes2.default.func,
|
500 | |
501 |
|
502 |
|
503 | threshold: _propTypes2.default.number,
|
504 | |
505 |
|
506 |
|
507 | itemSizeGetter: _propTypes2.default.func,
|
508 | |
509 |
|
510 |
|
511 | jumpIndex: _propTypes2.default.number,
|
512 | className: _propTypes2.default.string
|
513 | }, _class.defaultProps = {
|
514 | prefix: 'next-',
|
515 | itemsRenderer: function itemsRenderer(items, ref) {
|
516 | return _react2.default.createElement(
|
517 | 'ul',
|
518 | { ref: ref },
|
519 | items
|
520 | );
|
521 | },
|
522 | minSize: 1,
|
523 | pageSize: 10,
|
524 | jumpIndex: 0,
|
525 | threshold: 100
|
526 | }, _temp);
|
527 | VirtualList.displayName = 'VirtualList';
|
528 | exports.default = (0, _reactLifecyclesCompat.polyfill)(VirtualList);
|
529 | module.exports = exports['default']; |
\ | No newline at end of file |