useScrollTo.js 4.7 KB

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