useScrollTo.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
  3. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
  4. Object.defineProperty(exports, "__esModule", {
  5. value: true
  6. });
  7. exports.default = useScrollTo;
  8. var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
  9. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  10. var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
  11. var React = _interopRequireWildcard(require("react"));
  12. var _raf = _interopRequireDefault(require("rc-util/lib/raf"));
  13. var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect"));
  14. var _rcUtil = require("rc-util");
  15. /* eslint-disable no-param-reassign */
  16. var MAX_TIMES = 10;
  17. function useScrollTo(containerRef, data, heights, itemHeight, getKey, collectHeight, syncScrollTop, triggerFlash) {
  18. var scrollRef = React.useRef();
  19. var _React$useState = React.useState(null),
  20. _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
  21. syncState = _React$useState2[0],
  22. setSyncState = _React$useState2[1];
  23. // ========================== Sync Scroll ==========================
  24. (0, _useLayoutEffect.default)(function () {
  25. if (syncState && syncState.times < MAX_TIMES) {
  26. // Never reach
  27. if (!containerRef.current) {
  28. setSyncState(function (ori) {
  29. return (0, _objectSpread2.default)({}, ori);
  30. });
  31. return;
  32. }
  33. collectHeight();
  34. var targetAlign = syncState.targetAlign,
  35. originAlign = syncState.originAlign,
  36. index = syncState.index,
  37. offset = syncState.offset;
  38. var height = containerRef.current.clientHeight;
  39. var needCollectHeight = false;
  40. var newTargetAlign = targetAlign;
  41. var targetTop = null;
  42. // Go to next frame if height not exist
  43. if (height) {
  44. var mergedAlign = targetAlign || originAlign;
  45. // Get top & bottom
  46. var stackTop = 0;
  47. var itemTop = 0;
  48. var itemBottom = 0;
  49. var maxLen = Math.min(data.length - 1, index);
  50. for (var i = 0; i <= maxLen; i += 1) {
  51. var key = getKey(data[i]);
  52. itemTop = stackTop;
  53. var cacheHeight = heights.get(key);
  54. itemBottom = itemTop + (cacheHeight === undefined ? itemHeight : cacheHeight);
  55. stackTop = itemBottom;
  56. }
  57. // Check if need sync height (visible range has item not record height)
  58. var leftHeight = mergedAlign === 'top' ? offset : height - offset;
  59. for (var _i = maxLen; _i >= 0; _i -= 1) {
  60. var _key = getKey(data[_i]);
  61. var _cacheHeight = heights.get(_key);
  62. if (_cacheHeight === undefined) {
  63. needCollectHeight = true;
  64. break;
  65. }
  66. leftHeight -= _cacheHeight;
  67. if (leftHeight <= 0) {
  68. break;
  69. }
  70. }
  71. // Scroll to
  72. switch (mergedAlign) {
  73. case 'top':
  74. targetTop = itemTop - offset;
  75. break;
  76. case 'bottom':
  77. targetTop = itemBottom - height + offset;
  78. break;
  79. default:
  80. {
  81. var scrollTop = containerRef.current.scrollTop;
  82. var scrollBottom = scrollTop + height;
  83. if (itemTop < scrollTop) {
  84. newTargetAlign = 'top';
  85. } else if (itemBottom > scrollBottom) {
  86. newTargetAlign = 'bottom';
  87. }
  88. }
  89. }
  90. if (targetTop !== null) {
  91. syncScrollTop(targetTop);
  92. }
  93. // One more time for sync
  94. if (targetTop !== syncState.lastTop) {
  95. needCollectHeight = true;
  96. }
  97. }
  98. // Trigger next effect
  99. if (needCollectHeight) {
  100. setSyncState((0, _objectSpread2.default)((0, _objectSpread2.default)({}, syncState), {}, {
  101. times: syncState.times + 1,
  102. targetAlign: newTargetAlign,
  103. lastTop: targetTop
  104. }));
  105. }
  106. } else if (process.env.NODE_ENV !== 'production' && (syncState === null || syncState === void 0 ? void 0 : syncState.times) === MAX_TIMES) {
  107. (0, _rcUtil.warning)(false, 'Seems `scrollTo` with `rc-virtual-list` reach the max limitation. Please fire issue for us. Thanks.');
  108. }
  109. }, [syncState, containerRef.current]);
  110. // =========================== Scroll To ===========================
  111. return function (arg) {
  112. // When not argument provided, we think dev may want to show the scrollbar
  113. if (arg === null || arg === undefined) {
  114. triggerFlash();
  115. return;
  116. }
  117. // Normal scroll logic
  118. _raf.default.cancel(scrollRef.current);
  119. if (typeof arg === 'number') {
  120. syncScrollTop(arg);
  121. } else if (arg && (0, _typeof2.default)(arg) === 'object') {
  122. var index;
  123. var align = arg.align;
  124. if ('index' in arg) {
  125. index = arg.index;
  126. } else {
  127. index = data.findIndex(function (item) {
  128. return getKey(item) === arg.key;
  129. });
  130. }
  131. var _arg$offset = arg.offset,
  132. offset = _arg$offset === void 0 ? 0 : _arg$offset;
  133. setSyncState({
  134. times: 0,
  135. index: index,
  136. offset: offset,
  137. originAlign: align
  138. });
  139. }
  140. };
  141. }