vizhub-core / vzcode

Mob Programming Code Editor
MIT License
61 stars 13 forks source link

Add touch/stylus support to side panel resizer #420

Open natpbs opened 8 months ago

natpbs commented 8 months ago

The draggable bar that is used to adjust the width of the side panel bar should work with touch or stylus based interactions such as on tablets, phones or convertible laptops.

Currently, it can only be dragged with a mouse.

curran commented 8 months ago

Nice catch! I agree.

curran commented 8 months ago

From ChatGPT 4:

To make the drag interaction of your split pane resizer component work on touch screens, you'll need to add touch event listeners in addition to the existing mouse event listeners. The primary touch events you'll want to handle are touchstart, touchmove, and touchend.

Here's how you can modify the existing code to include touch event handling:

  1. Add Touch Event Handlers: Similar to onMouseDown, onMouseMove, and onMouseUp, create corresponding touch event handlers: onTouchStart, onTouchMove, and onTouchEnd.

  2. Adjust Event Handling Logic: Touch events have a different structure than mouse events. For instance, the clientX position is obtained from event.touches[0].clientX in a touchmove event.

  3. Bind and Unbind Touch Event Listeners: In the useEffect hook where you're adding and removing mouse event listeners, do the same for the touch event listeners.

Here is an example of how you might modify your component to handle touch events:

// ... (existing imports and code)

export const VZResizer = ({ side }: { side: Side }) => {
  // ... (existing useContext and useRef hooks)

  // Modify existing onMouseDown
  const onMouseDown = useCallback((event: React.MouseEvent | React.TouchEvent) => {
    event.preventDefault();
    const clientX = event instanceof MouseEvent ? event.clientX : event.touches[0].clientX;
    previousClientX.current = clientX;
    setIsDragging(true, side);
    document.body.style.cursor = 'col-resize';
  }, [setIsDragging, side]);

  // New onTouchMove handler
  const onTouchMove = useCallback((event: TouchEvent) => {
    event.preventDefault();
    const clientX = event.touches[0].clientX;
    const movementX = clientX - previousClientX.current;
    previousClientX.current = clientX;
    moveSplitPane(movementX, side);
  }, [moveSplitPane, side]);

  // ... (modify existing onMouseMove and onMouseUp to handle both mouse and touch events)

  useEffect(() => {
    if (isDragging) {
      document.addEventListener('mousemove', onMouseMove);
      document.addEventListener('mouseup', onMouseUp);
      document.addEventListener('touchmove', onTouchMove, { passive: false });
      document.addEventListener('touchend', onMouseUp);
      return () => {
        document.removeEventListener('mousemove', onMouseMove);
        document.removeEventListener('mouseup', onMouseUp);
        document.removeEventListener('touchmove', onTouchMove);
        document.removeEventListener('touchend', onMouseUp);
      };
    }
  }, [isDragging, onMouseMove, onMouseUp, onTouchMove]);

  // ... (rest of the component)
};

In this modification:

With these changes, your resizer should be functional on both mouse-driven and touch-driven devices. Remember to thoroughly test on various devices to ensure compatibility and usability.