1️⃣ What happens if we make a change after undoing? Here we've pressed undo twice, then changed a box's color from blue to red.
In this case, we always trim off any pending redos...
...before we push the new patch to the stack.
After a change, the pointer should always be pointing to the last item in the stack. A user should never be able to make a change and then redo!
2️⃣ What happens if we need to "pause" the undo / redo manager? This is often necessary during "continuous" interactions like dragging, resizing, or using a color picker.
Let's say we've just paused our undo / redo manager. Apart from the fact that we're now "paused", we need to keep track of whether any change has occurred while we've been paused.
If we "resume" (aka "unpause") without any change occurring, then we can just go back to where we were.
However, if a change occurs *while* the system is paused, then we need to handle things differently.
First, we need to mark that we've now had a change while paused.
However, we can stop here—we don't have to make a new patch, just keep the previous one around. We can take additional changes too.
Finally, if we "resume" after a change has made, we just push a new patch—exactly as usual.
As far as the system is concerned, its as if we had only made the most recent change!
When we're done, we reset the "changed while paused" flag for next time.
3️⃣ What if you press undo while paused? This is kind of tricky. We always will resume the history on an undo. However, if we do have pending undos, or if we've made a change while paused...
Then we'll resume and then immediately undo.
This way, we can "redo" back to where we were when we pressed undo. ✅
Redo while paused is a little easier—it just triggers a resume. In some apps (@figma ) it does redo the last undo, which I think is weird. IMO once you start making a change, you should be at the end of the stack!
BTW, if you're looking for bugs in your favorite infinite canvas app, this is the place to find them!
For one, it's important that the system triggers some sort of event that cancels the continuous interaction that caused us to pause in the first place, or else we'll end up paused again! (@canva has this one)
Working on a new Undo / Redo manager that runs on mobx and JSON patches. Here's how it works! 🧵
First off, here's a CodeSandbox. Looks pretty simple but there's a lot going on. I've included my tests! codesandbox.io/s/mobx-undo-re…
In our system, we're tracking changes to a "document". Each time the document changes, we generate a "snapshot" and compare it with our previous snapshot in order to create a "patch" that describes how to get from the current snapshot back to the previous.
Implemented click detection all the way up to quadruple clicks. 🤯
It sort of works like this: each time the user clicks, we fire three events—we fire onPointerDown when they start clicking, then onPointerUp _and_ onClick when the they stop clicking.
We also keep track of a clicking state.
This starts in "idle" and can be either "idle", "pendingDoubleClick", "pendingTripleClick", "pendingQuadrupleClick", or "overflow".
What do I mean by smarter? Let's say each of our blocks has a position in a grid, [x, y]. (It's actually a 3D grid, so [x, y, z], but we can ignore the z for now)
The positions map to the block's index in z/y/x arrays. This means that a block's position can only be an integer like 1 or 2, but never a float like 1.25 or 2.81.
So a position like [0, 0] is fine but [0.5, 0] is not.
I'm currently experiencing a "where do I put whitespace in my code" crisis.
I've usually placed empty lines between pretty much everything except maybe variable definitions, but this makes different methods hard to spot and sometimes makes methods themselves harder to read?
But saving empty lines for just separating methods / class fields can lead to some very dense code.