horizontalScroller.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import React, { useState, useRef, useEffect, useCallback } from "react";
  2. import { IconChevronRightStroked } from "@douyinfe/semi-icons";
  3. import { numbers } from "@douyinfe/semi-foundation/aiChatInput/constants";
  4. const HorizontalScroller = ({ children, prefix }) => {
  5. const scrollContainerRef = useRef(null);
  6. const [canScrollLeft, setCanScrollLeft] = useState(false);
  7. const [canScrollRight, setCanScrollRight] = useState(false);
  8. const checkScrollAbility = useCallback(() => {
  9. const container = scrollContainerRef.current;
  10. if (container) {
  11. const { scrollLeft, scrollWidth, clientWidth } = container;
  12. setCanScrollLeft(scrollLeft > 1);
  13. setCanScrollRight(Math.ceil(scrollLeft) < scrollWidth - clientWidth);
  14. }
  15. }, []);
  16. useEffect(() => {
  17. const container = scrollContainerRef.current;
  18. if (!container) return undefined;
  19. checkScrollAbility();
  20. const resizeObserver = new ResizeObserver(checkScrollAbility);
  21. resizeObserver.observe(container);
  22. container.addEventListener("scroll", checkScrollAbility);
  23. return () => {
  24. resizeObserver.disconnect();
  25. container.removeEventListener("scroll", checkScrollAbility);
  26. };
  27. }, [checkScrollAbility, children]);
  28. const handleScroll = useCallback((scrollAmount: number) => {
  29. const container = scrollContainerRef.current;
  30. if (container) {
  31. container.scrollBy({
  32. left: scrollAmount,
  33. behavior: "smooth",
  34. });
  35. }
  36. }, []);
  37. const handleScrollLeft = useCallback(() => {
  38. // Todo, scroll amount can be custom by user through props?
  39. handleScroll(-numbers.SCROLL_AMOUNT);
  40. }, [handleScroll]);
  41. const handleScrollRight = useCallback(() => {
  42. handleScroll(numbers.SCROLL_AMOUNT);
  43. }, [handleScroll]);
  44. return (
  45. <div className={`${prefix}-scroll-wrapper`}>
  46. {canScrollLeft && (
  47. <button
  48. className={`${prefix}-scroll-button ${prefix}-scroll-button-left`}
  49. onClick={handleScrollLeft}
  50. aria-label="Scroll left"
  51. >
  52. <IconChevronRightStroked className={`${prefix}-scroll-button-left-icon`} />
  53. </button>
  54. )}
  55. <div className={`${prefix}-scroll-container`} ref={scrollContainerRef}>
  56. {children}
  57. </div>
  58. {canScrollRight && (
  59. <button
  60. className={`${prefix}-scroll-button ${prefix}-scroll-button-right `}
  61. onClick={handleScrollRight}
  62. aria-label="Scroll right"
  63. >
  64. <IconChevronRightStroked />
  65. </button>
  66. )}
  67. </div>
  68. );
  69. };
  70. export default HorizontalScroller;