UNPKG

18.8 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports.RawList = RawList;
9exports.default = void 0;
10var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11var _objectSpread3 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
12var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
13var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
14var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
15var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
16var React = _interopRequireWildcard(require("react"));
17var _reactDom = require("react-dom");
18var _classnames = _interopRequireDefault(require("classnames"));
19var _rcResizeObserver = _interopRequireDefault(require("rc-resize-observer"));
20var _Filler = _interopRequireDefault(require("./Filler"));
21var _ScrollBar = _interopRequireDefault(require("./ScrollBar"));
22var _useChildren = _interopRequireDefault(require("./hooks/useChildren"));
23var _useHeights3 = _interopRequireDefault(require("./hooks/useHeights"));
24var _useScrollTo = _interopRequireDefault(require("./hooks/useScrollTo"));
25var _useDiffItem3 = _interopRequireDefault(require("./hooks/useDiffItem"));
26var _useFrameWheel3 = _interopRequireDefault(require("./hooks/useFrameWheel"));
27var _useMobileTouchMove = _interopRequireDefault(require("./hooks/useMobileTouchMove"));
28var _useOriginScroll = _interopRequireDefault(require("./hooks/useOriginScroll"));
29var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect"));
30var _scrollbarUtil = require("./utils/scrollbarUtil");
31var _rcUtil = require("rc-util");
32var _excluded = ["prefixCls", "className", "height", "itemHeight", "fullHeight", "style", "data", "children", "itemKey", "virtual", "direction", "scrollWidth", "component", "onScroll", "onVirtualScroll", "onVisibleChange", "innerProps", "extraRender"];
33var EMPTY_DATA = [];
34var ScrollStyle = {
35 overflowY: 'auto',
36 overflowAnchor: 'none'
37};
38function RawList(props, ref) {
39 var _props$prefixCls = props.prefixCls,
40 prefixCls = _props$prefixCls === void 0 ? 'rc-virtual-list' : _props$prefixCls,
41 className = props.className,
42 height = props.height,
43 itemHeight = props.itemHeight,
44 _props$fullHeight = props.fullHeight,
45 fullHeight = _props$fullHeight === void 0 ? true : _props$fullHeight,
46 style = props.style,
47 data = props.data,
48 children = props.children,
49 itemKey = props.itemKey,
50 virtual = props.virtual,
51 direction = props.direction,
52 scrollWidth = props.scrollWidth,
53 _props$component = props.component,
54 Component = _props$component === void 0 ? 'div' : _props$component,
55 onScroll = props.onScroll,
56 onVirtualScroll = props.onVirtualScroll,
57 onVisibleChange = props.onVisibleChange,
58 innerProps = props.innerProps,
59 extraRender = props.extraRender,
60 restProps = (0, _objectWithoutProperties2.default)(props, _excluded);
61 // ================================= MISC =================================
62 var useVirtual = !!(virtual !== false && height && itemHeight);
63 var inVirtual = useVirtual && data && itemHeight * data.length > height;
64 var isRTL = direction === 'rtl';
65 var mergedClassName = (0, _classnames.default)(prefixCls, (0, _defineProperty2.default)({}, "".concat(prefixCls, "-rtl"), isRTL), className);
66 var mergedData = data || EMPTY_DATA;
67 var componentRef = (0, React.useRef)();
68 var fillerInnerRef = (0, React.useRef)();
69 // =============================== Item Key ===============================
70 var _useState = (0, React.useState)(0),
71 _useState2 = (0, _slicedToArray2.default)(_useState, 2),
72 offsetTop = _useState2[0],
73 setOffsetTop = _useState2[1];
74 var _useState3 = (0, React.useState)(0),
75 _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
76 offsetLeft = _useState4[0],
77 setOffsetLeft = _useState4[1];
78 var _useState5 = (0, React.useState)(false),
79 _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
80 scrollMoving = _useState6[0],
81 setScrollMoving = _useState6[1];
82 var onScrollbarStartMove = function onScrollbarStartMove() {
83 setScrollMoving(true);
84 };
85 var onScrollbarStopMove = function onScrollbarStopMove() {
86 setScrollMoving(false);
87 };
88 // =============================== Item Key ===============================
89 var getKey = React.useCallback(function (item) {
90 if (typeof itemKey === 'function') {
91 return itemKey(item);
92 }
93 return item === null || item === void 0 ? void 0 : item[itemKey];
94 }, [itemKey]);
95 var sharedConfig = {
96 getKey: getKey
97 };
98 // ================================ Scroll ================================
99 function syncScrollTop(newTop) {
100 setOffsetTop(function (origin) {
101 var value;
102 if (typeof newTop === 'function') {
103 value = newTop(origin);
104 } else {
105 value = newTop;
106 }
107 var alignedTop = keepInRange(value);
108 componentRef.current.scrollTop = alignedTop;
109 return alignedTop;
110 });
111 }
112 // ================================ Legacy ================================
113 // Put ref here since the range is generate by follow
114 var rangeRef = (0, React.useRef)({
115 start: 0,
116 end: mergedData.length
117 });
118 var diffItemRef = (0, React.useRef)();
119 var _useDiffItem = (0, _useDiffItem3.default)(mergedData, getKey),
120 _useDiffItem2 = (0, _slicedToArray2.default)(_useDiffItem, 1),
121 diffItem = _useDiffItem2[0];
122 diffItemRef.current = diffItem;
123 // ================================ Height ================================
124 var _useHeights = (0, _useHeights3.default)(getKey, null, null),
125 _useHeights2 = (0, _slicedToArray2.default)(_useHeights, 4),
126 setInstanceRef = _useHeights2[0],
127 collectHeight = _useHeights2[1],
128 heights = _useHeights2[2],
129 heightUpdatedMark = _useHeights2[3];
130 // ========================== Visible Calculation =========================
131 var _React$useMemo = React.useMemo(function () {
132 if (!useVirtual) {
133 return {
134 scrollHeight: undefined,
135 start: 0,
136 end: mergedData.length - 1,
137 offset: undefined
138 };
139 }
140 // Always use virtual scroll bar in avoid shaking
141 if (!inVirtual) {
142 var _fillerInnerRef$curre;
143 return {
144 scrollHeight: ((_fillerInnerRef$curre = fillerInnerRef.current) === null || _fillerInnerRef$curre === void 0 ? void 0 : _fillerInnerRef$curre.offsetHeight) || 0,
145 start: 0,
146 end: mergedData.length - 1,
147 offset: undefined
148 };
149 }
150 var itemTop = 0;
151 var startIndex;
152 var startOffset;
153 var endIndex;
154 var dataLen = mergedData.length;
155 for (var i = 0; i < dataLen; i += 1) {
156 var item = mergedData[i];
157 var key = getKey(item);
158 var cacheHeight = heights.get(key);
159 var currentItemBottom = itemTop + (cacheHeight === undefined ? itemHeight : cacheHeight);
160 // Check item top in the range
161 if (currentItemBottom >= offsetTop && startIndex === undefined) {
162 startIndex = i;
163 startOffset = itemTop;
164 }
165 // Check item bottom in the range. We will render additional one item for motion usage
166 if (currentItemBottom > offsetTop + height && endIndex === undefined) {
167 endIndex = i;
168 }
169 itemTop = currentItemBottom;
170 }
171 // When scrollTop at the end but data cut to small count will reach this
172 if (startIndex === undefined) {
173 startIndex = 0;
174 startOffset = 0;
175 endIndex = Math.ceil(height / itemHeight);
176 }
177 if (endIndex === undefined) {
178 endIndex = mergedData.length - 1;
179 }
180 // Give cache to improve scroll experience
181 endIndex = Math.min(endIndex + 1, mergedData.length);
182 return {
183 scrollHeight: itemTop,
184 start: startIndex,
185 end: endIndex,
186 offset: startOffset
187 };
188 }, [inVirtual, useVirtual, offsetTop, mergedData, heightUpdatedMark, height]),
189 scrollHeight = _React$useMemo.scrollHeight,
190 start = _React$useMemo.start,
191 end = _React$useMemo.end,
192 fillerOffset = _React$useMemo.offset;
193 rangeRef.current.start = start;
194 rangeRef.current.end = end;
195 // ================================= Size =================================
196 var _React$useState = React.useState({
197 width: 0,
198 height: height
199 }),
200 _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
201 size = _React$useState2[0],
202 setSize = _React$useState2[1];
203 var onHolderResize = function onHolderResize(sizeInfo) {
204 setSize(sizeInfo);
205 };
206 // Hack on scrollbar to enable flash call
207 var verticalScrollBarRef = (0, React.useRef)();
208 var horizontalScrollBarRef = (0, React.useRef)();
209 var horizontalScrollBarSpinSize = React.useMemo(function () {
210 return (0, _scrollbarUtil.getSpinSize)(size.width, scrollWidth);
211 }, [size.width, scrollWidth]);
212 var verticalScrollBarSpinSize = React.useMemo(function () {
213 return (0, _scrollbarUtil.getSpinSize)(size.height, scrollHeight);
214 }, [size.height, scrollHeight]);
215 // =============================== In Range ===============================
216 var maxScrollHeight = scrollHeight - height;
217 var maxScrollHeightRef = (0, React.useRef)(maxScrollHeight);
218 maxScrollHeightRef.current = maxScrollHeight;
219 function keepInRange(newScrollTop) {
220 var newTop = newScrollTop;
221 if (!Number.isNaN(maxScrollHeightRef.current)) {
222 newTop = Math.min(newTop, maxScrollHeightRef.current);
223 }
224 newTop = Math.max(newTop, 0);
225 return newTop;
226 }
227 var isScrollAtTop = offsetTop <= 0;
228 var isScrollAtBottom = offsetTop >= maxScrollHeight;
229 var originScroll = (0, _useOriginScroll.default)(isScrollAtTop, isScrollAtBottom);
230 // ================================ Scroll ================================
231 var getVirtualScrollInfo = function getVirtualScrollInfo() {
232 return {
233 x: isRTL ? -offsetLeft : offsetLeft,
234 y: offsetTop
235 };
236 };
237 var lastVirtualScrollInfoRef = (0, React.useRef)(getVirtualScrollInfo());
238 var triggerScroll = (0, _rcUtil.useEvent)(function () {
239 if (onVirtualScroll) {
240 var nextInfo = getVirtualScrollInfo();
241 // Trigger when offset changed
242 if (lastVirtualScrollInfoRef.current.x !== nextInfo.x || lastVirtualScrollInfoRef.current.y !== nextInfo.y) {
243 onVirtualScroll(nextInfo);
244 lastVirtualScrollInfoRef.current = nextInfo;
245 }
246 }
247 });
248 function onScrollBar(newScrollOffset, horizontal) {
249 var newOffset = newScrollOffset;
250 if (horizontal) {
251 (0, _reactDom.flushSync)(function () {
252 setOffsetLeft(newOffset);
253 });
254 triggerScroll();
255 } else {
256 syncScrollTop(newOffset);
257 }
258 }
259 // When data size reduce. It may trigger native scroll event back to fit scroll position
260 function onFallbackScroll(e) {
261 var newScrollTop = e.currentTarget.scrollTop;
262 if (newScrollTop !== offsetTop) {
263 syncScrollTop(newScrollTop);
264 }
265 // Trigger origin onScroll
266 onScroll === null || onScroll === void 0 ? void 0 : onScroll(e);
267 triggerScroll();
268 }
269 var keepInHorizontalRange = function keepInHorizontalRange(nextOffsetLeft) {
270 var tmpOffsetLeft = nextOffsetLeft;
271 var max = scrollWidth - size.width;
272 tmpOffsetLeft = Math.max(tmpOffsetLeft, 0);
273 tmpOffsetLeft = Math.min(tmpOffsetLeft, max);
274 return tmpOffsetLeft;
275 };
276 var onWheelDelta = (0, _rcUtil.useEvent)(function (offsetXY, fromHorizontal) {
277 if (fromHorizontal) {
278 // Horizontal scroll no need sync virtual position
279 (0, _reactDom.flushSync)(function () {
280 setOffsetLeft(function (left) {
281 var nextOffsetLeft = left + (isRTL ? -offsetXY : offsetXY);
282 return keepInHorizontalRange(nextOffsetLeft);
283 });
284 });
285 triggerScroll();
286 } else {
287 syncScrollTop(function (top) {
288 var newTop = top + offsetXY;
289 return newTop;
290 });
291 }
292 });
293 // Since this added in global,should use ref to keep update
294 var _useFrameWheel = (0, _useFrameWheel3.default)(useVirtual, isScrollAtTop, isScrollAtBottom, !!scrollWidth, onWheelDelta),
295 _useFrameWheel2 = (0, _slicedToArray2.default)(_useFrameWheel, 2),
296 onRawWheel = _useFrameWheel2[0],
297 onFireFoxScroll = _useFrameWheel2[1];
298 // Mobile touch move
299 (0, _useMobileTouchMove.default)(useVirtual, componentRef, function (deltaY, smoothOffset) {
300 if (originScroll(deltaY, smoothOffset)) {
301 return false;
302 }
303 onRawWheel({
304 preventDefault: function preventDefault() {},
305 deltaY: deltaY
306 });
307 return true;
308 });
309 (0, _useLayoutEffect.default)(function () {
310 // Firefox only
311 function onMozMousePixelScroll(e) {
312 if (useVirtual) {
313 e.preventDefault();
314 }
315 }
316 var componentEle = componentRef.current;
317 componentEle.addEventListener('wheel', onRawWheel);
318 componentEle.addEventListener('DOMMouseScroll', onFireFoxScroll);
319 componentEle.addEventListener('MozMousePixelScroll', onMozMousePixelScroll);
320 return function () {
321 componentEle.removeEventListener('wheel', onRawWheel);
322 componentEle.removeEventListener('DOMMouseScroll', onFireFoxScroll);
323 componentEle.removeEventListener('MozMousePixelScroll', onMozMousePixelScroll);
324 };
325 }, [useVirtual]);
326 // ================================= Ref ==================================
327 var delayHideScrollBar = function delayHideScrollBar() {
328 var _verticalScrollBarRef, _horizontalScrollBarR;
329 (_verticalScrollBarRef = verticalScrollBarRef.current) === null || _verticalScrollBarRef === void 0 ? void 0 : _verticalScrollBarRef.delayHidden();
330 (_horizontalScrollBarR = horizontalScrollBarRef.current) === null || _horizontalScrollBarR === void 0 ? void 0 : _horizontalScrollBarR.delayHidden();
331 };
332 var _scrollTo = (0, _useScrollTo.default)(componentRef, mergedData, heights, itemHeight, getKey, collectHeight, syncScrollTop, delayHideScrollBar);
333 React.useImperativeHandle(ref, function () {
334 return {
335 getScrollInfo: getVirtualScrollInfo,
336 scrollTo: function scrollTo(config) {
337 function isPosScroll(arg) {
338 return arg && (0, _typeof2.default)(arg) === 'object' && ('left' in arg || 'top' in arg);
339 }
340 if (isPosScroll(config)) {
341 // Scroll X
342 if (config.left !== undefined) {
343 setOffsetLeft(keepInHorizontalRange(config.left));
344 }
345 // Scroll Y
346 _scrollTo(config.top);
347 } else {
348 _scrollTo(config);
349 }
350 }
351 };
352 });
353 // ================================ Effect ================================
354 /** We need told outside that some list not rendered */
355 (0, _useLayoutEffect.default)(function () {
356 if (onVisibleChange) {
357 var renderList = mergedData.slice(start, end + 1);
358 onVisibleChange(renderList, mergedData);
359 }
360 }, [start, end, mergedData]);
361 // ================================ Extra =================================
362 var getSize = function getSize(startKey) {
363 var endKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : startKey;
364 var top = 0;
365 var bottom = 0;
366 var total = 0;
367 var dataLen = mergedData.length;
368 for (var i = 0; i < dataLen; i += 1) {
369 var _heights$get;
370 var item = mergedData[i];
371 var key = getKey(item);
372 var cacheHeight = (_heights$get = heights.get(key)) !== null && _heights$get !== void 0 ? _heights$get : itemHeight;
373 bottom = total + cacheHeight;
374 if (key === startKey) {
375 top = total;
376 }
377 if (key === endKey) {
378 break;
379 }
380 total = bottom;
381 }
382 return {
383 top: top,
384 bottom: bottom
385 };
386 };
387 var extraContent = extraRender === null || extraRender === void 0 ? void 0 : extraRender({
388 start: start,
389 end: end,
390 virtual: inVirtual,
391 offsetX: offsetLeft,
392 offsetY: fillerOffset,
393 rtl: isRTL,
394 getSize: getSize
395 });
396 // ================================ Render ================================
397 var listChildren = (0, _useChildren.default)(mergedData, start, end, scrollWidth, setInstanceRef, children, sharedConfig);
398 var componentStyle = null;
399 if (height) {
400 componentStyle = (0, _objectSpread3.default)((0, _defineProperty2.default)({}, fullHeight ? 'height' : 'maxHeight', height), ScrollStyle);
401 if (useVirtual) {
402 componentStyle.overflowY = 'hidden';
403 if (scrollWidth) {
404 componentStyle.overflowX = 'hidden';
405 }
406 if (scrollMoving) {
407 componentStyle.pointerEvents = 'none';
408 }
409 }
410 }
411 var containerProps = {};
412 if (isRTL) {
413 containerProps.dir = 'rtl';
414 }
415 return /*#__PURE__*/React.createElement("div", (0, _extends2.default)({
416 style: (0, _objectSpread3.default)((0, _objectSpread3.default)({}, style), {}, {
417 position: 'relative'
418 }),
419 className: mergedClassName
420 }, containerProps, restProps), /*#__PURE__*/React.createElement(_rcResizeObserver.default, {
421 onResize: onHolderResize
422 }, /*#__PURE__*/React.createElement(Component, {
423 className: "".concat(prefixCls, "-holder"),
424 style: componentStyle,
425 ref: componentRef,
426 onScroll: onFallbackScroll,
427 onMouseEnter: delayHideScrollBar
428 }, /*#__PURE__*/React.createElement(_Filler.default, {
429 prefixCls: prefixCls,
430 height: scrollHeight,
431 offsetX: offsetLeft,
432 offsetY: fillerOffset,
433 scrollWidth: scrollWidth,
434 onInnerResize: collectHeight,
435 ref: fillerInnerRef,
436 innerProps: innerProps,
437 rtl: isRTL,
438 extra: extraContent
439 }, listChildren))), inVirtual && scrollHeight > height && /*#__PURE__*/React.createElement(_ScrollBar.default, {
440 ref: verticalScrollBarRef,
441 prefixCls: prefixCls,
442 scrollOffset: offsetTop,
443 scrollRange: scrollHeight,
444 rtl: isRTL,
445 onScroll: onScrollBar,
446 onStartMove: onScrollbarStartMove,
447 onStopMove: onScrollbarStopMove,
448 spinSize: verticalScrollBarSpinSize,
449 containerSize: size.height
450 }), inVirtual && scrollWidth && /*#__PURE__*/React.createElement(_ScrollBar.default, {
451 ref: horizontalScrollBarRef,
452 prefixCls: prefixCls,
453 scrollOffset: offsetLeft,
454 scrollRange: scrollWidth,
455 rtl: isRTL,
456 onScroll: onScrollBar,
457 onStartMove: onScrollbarStartMove,
458 onStopMove: onScrollbarStopMove,
459 spinSize: horizontalScrollBarSpinSize,
460 containerSize: size.width,
461 horizontal: true
462 }));
463}
464var List = /*#__PURE__*/React.forwardRef(RawList);
465List.displayName = 'List';
466var _default = List;
467exports.default = _default;
\No newline at end of file