captureAll() method for UndoManager

Our application has certain types of user action – such as dragging an object to change its position – which are continuous while the user has the mouse pressed, and only finalised once the mouse is released. For these types of actions we would like a single operation to be added to the undo stack at the point of release, so that when the user subsequently presses undo, the document state is reverted to the way it was before the drag action started.

I understand that I can use the captureTimeout parameter of UndoManager to achieve something similar to this, but unfortunately I’m finding it unsuitable for the following reasons:

  • Picking a suitable captureTimeout value is difficult, because due to performance differences across devices, the exact time span (or a suitable approximation) between mouse move events is not really knowable – especially when there are GC events or other pauses which can be difficult to anticipate. Furthermore, in other areas of the application we have actions that happen in very quick succession but which should be added to the undo stack as seperate operations.
  • If the user pauses whilst dragging – but doesn’t release the mouse – and then resumes movement, this should be considered as a continuation of the same drag action. However, as no mouse move events are being received during the pause, no operations are being propagated to YJS and so the captureTimeout quickly elapses.

To handle this type of usage, I would like to propose adding a captureAll() method to UndoManager. After captureAll() has been called, all operations would be merged into a single undo stack item until stopCapturing() is called.

Would this be feasible? Or alternatively, is there another way I can achieve the behavior we’re going for.

I should add one detail: we want other users to see in-progress drag operations as they’re happening, i.e. such that they see the layer move smoothly across the screen. For this reason we can’t just hold off on propagating the position change to YJS until the mouse has been released.

{ captureTimeout: Infinity } seems to work, but then you have to use something like _.debounce(undoManager.stopCapturing, myCaptureTimeout) when a capture timeout is needed.