prevwong / craft.js

🚀 A React Framework for building extensible drag and drop page editors
https://craft.js.org
MIT License
7.6k stars 740 forks source link

Auto scroll to the top of the page does not work while dragging an element #453

Open AminRafaey opened 2 years ago

AminRafaey commented 2 years ago

Describe the bug Auto scroll to the top of the page does not work while dragging an element.

To Reproduce Steps to reproduce the behavior:

  1. Go to (https://craft.js.org/examples/landing/).
  2. Scroll to the bottom of the page.
  3. Start dragging any element from the end.
  4. Try to go with that element on top.

Expected behavior In this video below I would expect an auto-scroll to the top of the page.

Video Demonstration See the video (see expected behavior above) https://www.loom.com/share/984feca6f62448d6b051653aa3b6a375

LukasBlu1 commented 2 years ago

Hi @prevwong! I think you will directly have an idea what the solution could be. Can you give us a hint? Thanks in advance :)

gcphost commented 1 year ago

There is nothing I can see to tell to scroll, looking into the code it seems one would need to replicate a drag handler, check scrolling conditions, and scroll the window (or dom element).

https://github.com/prevwong/craft.js/blob/993a6bf49c54c97a0ab1959ac151e64d64b1ca4c/packages/layers/src/events/LayerHandlers.ts

AminRafaey commented 1 year ago

I implement my own scroll behavior while dragging.

Viewport.ts

import { throttle } from '../../helper/throttle';
import useWindowDimensions from '../../hooks/useWindowDimensions';

export const Viewport: React.FC = ({ children }) => {
  const step = 1;
  const scrollRef = useRef<Element | null>(null);
  const isScrollRef = useRef<boolean>(false);
  const { height } = useWindowDimensions();

  const setMove = (state: boolean) => {
    isScrollRef.current = state;
  };

  const moveToTop = () => {
    if (scrollRef.current?.scrollTop && isScrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollTop - step;
      requestAnimationFrame(moveToTop);
    }
  };

  const moveToBottom = () => {
    if (
      (scrollRef.current?.scrollTop === 0 || scrollRef.current?.scrollTop) &&
      isScrollRef.current
    ) {
      scrollRef.current.scrollTop = scrollRef.current.scrollTop + step;
      requestAnimationFrame(moveToBottom);
    }
  };

  const throttledMoveToTop = useRef(throttle(moveToTop, 100)).current;
  const throttledMoveToBottom = useRef(throttle(moveToBottom, 100)).current;

  useEffect(() => {
    scrollRef.current = document.getElementsByClassName('builder__content')[0];

    document.addEventListener(
      'drag',
      function(e) {
        e = e || window.event;
        const dragY = e.pageY;

        //Distance from bottom less then 50px
        if (dragY > height - 50) {
          setMove(true);
          throttledMoveToBottom();
        } else if (dragY < 100) {
          setMove(true);
          throttledMoveToTop();
        } else {
          setMove(false);
        }
      },
      false
    );

    document.addEventListener(
      'dragend',
      function(e) {
        setMove(false);
      },
      false
    );
  }, []);

  return (
    <div className="page-container">
      <div className={'craftjs-renderer'}>{children}</div>
    </div>
  );
};

throttle.ts

  let inThrottle: boolean;
  return function(this: any) {
    const args: any = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
};

useWindowDimensions.ts


function getWindowDimensions() {
  const { innerWidth: width, innerHeight: height } = window;
  return {
    width,
    height
  };
}

export default function useWindowDimensions() {
  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());

  useEffect(() => {
    function handleResize() {
      setWindowDimensions(getWindowDimensions());
    }

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return windowDimensions;
}
thirstycode commented 1 year ago

Thanks for sharing this @AminRafaey !