stickyScrollBar.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  2. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  3. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  4. import { useContext } from '@rc-component/context';
  5. import classNames from 'classnames';
  6. import addEventListener from "rc-util/es/Dom/addEventListener";
  7. import getScrollBarSize from "rc-util/es/getScrollBarSize";
  8. import * as React from 'react';
  9. import TableContext from "./context/TableContext";
  10. import { useLayoutState } from "./hooks/useFrame";
  11. import raf from "rc-util/es/raf";
  12. import { getOffset } from "./utils/offsetUtil";
  13. import { getDOM } from "rc-util/es/Dom/findDOMNode";
  14. var StickyScrollBar = function StickyScrollBar(_ref, ref) {
  15. var _scrollBodyRef$curren, _scrollBodyRef$curren2;
  16. var scrollBodyRef = _ref.scrollBodyRef,
  17. onScroll = _ref.onScroll,
  18. offsetScroll = _ref.offsetScroll,
  19. container = _ref.container,
  20. direction = _ref.direction;
  21. var prefixCls = useContext(TableContext, 'prefixCls');
  22. var bodyScrollWidth = ((_scrollBodyRef$curren = scrollBodyRef.current) === null || _scrollBodyRef$curren === void 0 ? void 0 : _scrollBodyRef$curren.scrollWidth) || 0;
  23. var bodyWidth = ((_scrollBodyRef$curren2 = scrollBodyRef.current) === null || _scrollBodyRef$curren2 === void 0 ? void 0 : _scrollBodyRef$curren2.clientWidth) || 0;
  24. var scrollBarWidth = bodyScrollWidth && bodyWidth * (bodyWidth / bodyScrollWidth);
  25. var scrollBarRef = React.useRef();
  26. var _useLayoutState = useLayoutState({
  27. scrollLeft: 0,
  28. isHiddenScrollBar: true
  29. }),
  30. _useLayoutState2 = _slicedToArray(_useLayoutState, 2),
  31. scrollState = _useLayoutState2[0],
  32. setScrollState = _useLayoutState2[1];
  33. var refState = React.useRef({
  34. delta: 0,
  35. x: 0
  36. });
  37. var _React$useState = React.useState(false),
  38. _React$useState2 = _slicedToArray(_React$useState, 2),
  39. isActive = _React$useState2[0],
  40. setActive = _React$useState2[1];
  41. var rafRef = React.useRef(null);
  42. React.useEffect(function () {
  43. return function () {
  44. raf.cancel(rafRef.current);
  45. };
  46. }, []);
  47. var onMouseUp = function onMouseUp() {
  48. setActive(false);
  49. };
  50. var onMouseDown = function onMouseDown(event) {
  51. event.persist();
  52. refState.current.delta = event.pageX - scrollState.scrollLeft;
  53. refState.current.x = 0;
  54. setActive(true);
  55. event.preventDefault();
  56. };
  57. var onMouseMove = function onMouseMove(event) {
  58. var _window;
  59. // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
  60. var _ref2 = event || ((_window = window) === null || _window === void 0 ? void 0 : _window.event),
  61. buttons = _ref2.buttons;
  62. if (!isActive || buttons === 0) {
  63. // If out body mouse up, we can set isActive false when mouse move
  64. if (isActive) {
  65. setActive(false);
  66. }
  67. return;
  68. }
  69. var left = refState.current.x + event.pageX - refState.current.x - refState.current.delta;
  70. var isRTL = direction === 'rtl';
  71. // Limit scroll range
  72. left = Math.max(isRTL ? scrollBarWidth - bodyWidth : 0, Math.min(isRTL ? 0 : bodyWidth - scrollBarWidth, left));
  73. // Calculate the scroll position and update
  74. var shouldScroll = !isRTL || Math.abs(left) + Math.abs(scrollBarWidth) < bodyWidth;
  75. if (shouldScroll) {
  76. onScroll({
  77. scrollLeft: left / bodyWidth * (bodyScrollWidth + 2)
  78. });
  79. refState.current.x = event.pageX;
  80. }
  81. };
  82. var checkScrollBarVisible = function checkScrollBarVisible() {
  83. raf.cancel(rafRef.current);
  84. rafRef.current = raf(function () {
  85. if (!scrollBodyRef.current) {
  86. return;
  87. }
  88. var tableOffsetTop = getOffset(scrollBodyRef.current).top;
  89. var tableBottomOffset = tableOffsetTop + scrollBodyRef.current.offsetHeight;
  90. var currentClientOffset = container === window ? document.documentElement.scrollTop + window.innerHeight : getOffset(container).top + container.clientHeight;
  91. setScrollState(function (state) {
  92. return _objectSpread(_objectSpread({}, state), {}, {
  93. isHiddenScrollBar: tableBottomOffset - getScrollBarSize() <= currentClientOffset || tableOffsetTop >= currentClientOffset - offsetScroll
  94. });
  95. });
  96. });
  97. };
  98. var setScrollLeft = function setScrollLeft(left) {
  99. setScrollState(function (state) {
  100. return _objectSpread(_objectSpread({}, state), {}, {
  101. scrollLeft: bodyScrollWidth ? left / bodyScrollWidth * bodyWidth : 0
  102. });
  103. });
  104. };
  105. React.useImperativeHandle(ref, function () {
  106. return {
  107. setScrollLeft: setScrollLeft,
  108. checkScrollBarVisible: checkScrollBarVisible
  109. };
  110. });
  111. React.useEffect(function () {
  112. var onMouseUpListener = addEventListener(document.body, 'mouseup', onMouseUp, false);
  113. var onMouseMoveListener = addEventListener(document.body, 'mousemove', onMouseMove, false);
  114. checkScrollBarVisible();
  115. return function () {
  116. onMouseUpListener.remove();
  117. onMouseMoveListener.remove();
  118. };
  119. }, [scrollBarWidth, isActive]);
  120. // Loop for scroll event check
  121. React.useEffect(function () {
  122. if (!scrollBodyRef.current) return;
  123. var scrollParents = [];
  124. var parent = getDOM(scrollBodyRef.current);
  125. while (parent) {
  126. scrollParents.push(parent);
  127. parent = parent.parentElement;
  128. }
  129. scrollParents.forEach(function (p) {
  130. return p.addEventListener('scroll', checkScrollBarVisible, false);
  131. });
  132. window.addEventListener('resize', checkScrollBarVisible, false);
  133. window.addEventListener('scroll', checkScrollBarVisible, false);
  134. container.addEventListener('scroll', checkScrollBarVisible, false);
  135. return function () {
  136. scrollParents.forEach(function (p) {
  137. return p.removeEventListener('scroll', checkScrollBarVisible);
  138. });
  139. window.removeEventListener('resize', checkScrollBarVisible);
  140. window.removeEventListener('scroll', checkScrollBarVisible);
  141. container.removeEventListener('scroll', checkScrollBarVisible);
  142. };
  143. }, [container]);
  144. React.useEffect(function () {
  145. if (!scrollState.isHiddenScrollBar) {
  146. setScrollState(function (state) {
  147. var bodyNode = scrollBodyRef.current;
  148. if (!bodyNode) {
  149. return state;
  150. }
  151. return _objectSpread(_objectSpread({}, state), {}, {
  152. scrollLeft: bodyNode.scrollLeft / bodyNode.scrollWidth * bodyNode.clientWidth
  153. });
  154. });
  155. }
  156. }, [scrollState.isHiddenScrollBar]);
  157. if (bodyScrollWidth <= bodyWidth || !scrollBarWidth || scrollState.isHiddenScrollBar) {
  158. return null;
  159. }
  160. return /*#__PURE__*/React.createElement("div", {
  161. style: {
  162. height: getScrollBarSize(),
  163. width: bodyWidth,
  164. bottom: offsetScroll
  165. },
  166. className: "".concat(prefixCls, "-sticky-scroll")
  167. }, /*#__PURE__*/React.createElement("div", {
  168. onMouseDown: onMouseDown,
  169. ref: scrollBarRef,
  170. className: classNames("".concat(prefixCls, "-sticky-scroll-bar"), _defineProperty({}, "".concat(prefixCls, "-sticky-scroll-bar-active"), isActive)),
  171. style: {
  172. width: "".concat(scrollBarWidth, "px"),
  173. transform: "translate3d(".concat(scrollState.scrollLeft, "px, 0, 0)")
  174. }
  175. }));
  176. };
  177. export default /*#__PURE__*/React.forwardRef(StickyScrollBar);