react-dnd#DndContext TypeScript Examples

The following examples show how to use react-dnd#DndContext. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: create-scroll-component.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function createScrollingComponent(WrappedComponent: string) {
  const ScrollingComponent = ({
    horizontalBuffer = DEFAULT_BUFFER,
    verticalBuffer = DEFAULT_BUFFER,
    strengthMultiplier = 60,
    onScrollChange = noop,
    ease = true,
    ...rest
  }) => {
    const { dragDropManager } = useContext(DndContext);
    const staticRef = React.useRef({
      scaleX: 0,
      scaleY: 0,
      frame: 0,
      attached: false,
      dragging: false,
      container: null,
    });

    useMount(() => {
      const horizontalStrength = createHorizontalStrength(horizontalBuffer);
      const verticalStrength = createVerticalStrength(verticalBuffer);
      const handleEvent = (evt: MouseEvent | TouchEvent) => {
        if (staticRef.current.dragging && !staticRef.current.attached) {
          attach();
          updateScrolling(evt);
        }
      };

      const attach = () => {
        staticRef.current.attached = true;
        window.document.body.addEventListener('dragover', updateScrolling);
        window.document.body.addEventListener('touchmove', updateScrolling);
      };

      const detach = () => {
        staticRef.current.attached = false;
        window.document.body.removeEventListener('dragover', updateScrolling);
        window.document.body.removeEventListener('touchmove', updateScrolling);
      };

      const container = staticRef.current.container as unknown as HTMLDivElement;
      // Update scaleX and scaleY every 100ms or so
      // and start scrolling if necessary
      const updateScrolling = throttle(
        (evt) => {
          const { left: x, top: y, width: w, height: h } = container.getBoundingClientRect();
          const box = { x, y, w, h };
          const coords = getCoords(evt);
          // calculate strength
          staticRef.current.scaleX = horizontalStrength(box, coords);
          staticRef.current.scaleY = verticalStrength(box, coords);
          if (ease) {
            staticRef.current.scaleX = easeFn(staticRef.current.scaleX);
            staticRef.current.scaleY = easeFn(staticRef.current.scaleY);
          }
          // start scrolling if we need to
          if (!staticRef.current.frame && (staticRef.current.scaleX || staticRef.current.scaleY)) {
            startScrolling();
          }
        },
        100,
        {
          trailing: false,
        },
      );

      const startScrolling = () => {
        let i = 0;
        const tick = () => {
          const { scaleX, scaleY } = staticRef.current;
          // stop scrolling if there's nothing to do
          if (strengthMultiplier === 0 || scaleX + scaleY === 0) {
            stopScrolling();
            return;
          }
          // there's a bug in safari where it seems like we can't get
          // mousemove events from a container that also emits a scroll
          // event that same frame. So we double the strengthMultiplier and only adjust
          // the scroll position at 30fps
          if (i++ % 2) {
            const { scrollLeft, scrollTop, scrollWidth, scrollHeight, clientWidth, clientHeight } = container;
            const newLeft = scaleX
              ? (container.scrollLeft = intBetween(
                  0,
                  scrollWidth - clientWidth,
                  scrollLeft + scaleX * strengthMultiplier,
                ))
              : scrollLeft;
            const newTop = scaleY
              ? (container.scrollTop = intBetween(
                  0,
                  scrollHeight - clientHeight,
                  scrollTop + scaleY * strengthMultiplier,
                ))
              : scrollTop;
            onScrollChange(newLeft, newTop);
          }
          staticRef.current.frame = window.requestAnimationFrame(tick);
        };
        tick();
      };

      const stopScrolling = () => {
        detach();
        staticRef.current.scaleX = 0;
        staticRef.current.scaleY = 0;
        if (staticRef.current.frame) {
          window.cancelAnimationFrame(staticRef.current.frame);
          staticRef.current.frame = 0;
        }
      };

      container.addEventListener('dragover', handleEvent);
      // touchmove events don't seem to work across siblings, so we unfortunately
      // have to attach the listeners to the body
      window.document.body.addEventListener('touchmove', handleEvent);
      const clearMonitorSubscription = dragDropManager.getMonitor().subscribeToStateChange(() => {
        const isDragging = dragDropManager.getMonitor().isDragging();
        if (!staticRef.current.dragging && isDragging) {
          staticRef.current.dragging = true;
        } else if (staticRef.current.dragging && !isDragging) {
          staticRef.current.dragging = false;
          stopScrolling();
        }
      });
      return () => {
        container.removeEventListener('dragover', handleEvent);
        window.document.body.removeEventListener('touchmove', handleEvent);
        clearMonitorSubscription();
        stopScrolling();
      };
    });

    return (
      <WrappedComponent
        ref={(ref: any) => {
          staticRef.current.container = ref;
        }}
        {...rest}
      />
    );
  };
  ScrollingComponent.displayName = `Scrolling${WrappedComponent}`;
  return ScrollingComponent;
}