UNPKG

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