So now I want to develop one last thing before we add the export and upload feature. And that will be the hotkeys, right? So I want to use the common shortcuts such as Command or Control A to select everything on the canvas. I want command C and command V to copy and paste, I want to be able to delete with the backspace button and things like that. So let's go ahead inside of the use editor and let's go inside of hooks here and prepare use hotkeys.ts let's export const use hotkeys like this and while we are here let's also prepare the interface so I'm gonna import fabric from fabric and I will create an interface use hotkeys props with the capital U here.
So we're going to have a canvas which will be a type of fabric dot canvas or now we're going to have the undo method. We're going to have redo, save, copy and paste. And the save method will also include the optional skip boolean. Like that. Now let's assign these props here so use hotkeys props and we can destructure the canvas undo, redo, save, copy and paste.
There we go And now let's go inside of the use editor here. And I want to go ahead and just above the editor below the use canvas events, let's add use hotkeys. Like this, and we're going to have to pass in the options. Let me just change my import because I prefer it this way. There we go.
And now inside of use hotkeys, we have to pass all of those options. So let's go ahead and pass in the undo, redo, copy, paste, save and canvas. There we go. So we have all of those methods here. Perfect.
And now we're ready to create our listeners. So I'm going to go ahead and install a package called bun-add-react-use simply because we are later going to use more hooks from this package. I just think it's a nice little package to have so we don't have to think too much about defining our own hooks when they have things, for example, which we can use here, use event. I just think it's way cleaner from this package right here, which is maintained, nicely typed, and well, functional. So I'm gonna go ahead and add use event key down we're gonna get to the event here and now what we're gonna do is we're gonna define the following first is control key so that will check whether the event is control key or event is any other meta key for example if we are on a MacBook right that now let's check is backspace so event dot key is backspace and now let's check if we are currently in an input so we don't want to accidentally trigger these events if we are inside of an input, if we are typing something, right?
Like in a text box, for example. So we're gonna add isInput here so that we can detect that by checking whether input or text area. So those two are part of the current event.target, which I purposely wrote in brackets here because we have to define it as HTML element. So any, right? .TagName.
My apologies, this bracket was supposed to go to the end here. There we go like this. Perfect. And now very simply if we are inside of an input we are not gonna do anything right We want to prevent anything from happening so we don't interrupt the user while they are typing. And now let's go ahead and do the following.
If isBackspace we're going to do canvas?remove and we are very simply going to spread inside canvas.getActiveObjects and then canvas.discardActiveObject if it was selected. And now let's go ahead and check if isControlKey and eventKey is Zed we're going to prevent default and I also have a typo here so isCtrlKey and then we are going to undo like this. Now let's go ahead and let's check for the Y key where we are going to redo and now let's go ahead and let's check for the C key which will represent the copy method. So in here we're going to call copy. Now we're going to go ahead and do the same thing for V which we'll call the paste method.
After that we can go ahead and activate the S key which we'll call the save method and here's the cool part. So we are going to skip saving this to history because manually clicking command or control save should not push anything new to our history state so we shouldn't have any more undo or redo actions as opposed to what we had just before we clicked command save So that's why our little skip is useful. So let me just revisit our use history here. When if I find the save, so this will be true, which means we will not do this. Instead, we will just do a save to database manually.
So we will just trigger this part of the code which will simply save to the database which is exactly what we want to happen once the user presses this. But we don't want another thing to be pushed to the history event. The only way I want things to be pushed to history is during the canvas events like object added, modified or removed or when we change the workspace color or workspace size. Great! And now the last one I want here is if the event key is A.
So again, we're gonna prevent default and first thing we're gonna do is call canvas and discard active object. And then we're gonna get all objects here to be canvas, get objects. But we have to be vigilant here. There is one object we should not select and that is, if you remember, inside of our init method, inside of our init method right here the initial workspace which has the name of clip. So we should not select that one.
So let's go ahead and filter object and we have two ways of doing that. So an easy way of doing it is just checking the object name to not be clip, right? You can do that or you can also simply ensure that when we trigger this, you know, command all or control all, only select those objects which are selectable. Like this. Because use editor is not selectable so it should be skipped inside of this useHotkeys so choose a method that you like so I'm gonna use this one for example and then what we're gonna do is canvas.setActiveObject new Fabric.activeSelection and pass in all objects as the first argument and canvas as the second argument so I'm just gonna collapse this so you can see so all of this is a single parameter inside of the setActive object So let me just move this to the top.
There we go. Like that. And then we're gonna do canvas.renderAll. There we go. And we don't have to return anything from here because we are just registering the event useKeyDown and this React hook will also unregister the event for us so you don't have to worry about any overflow here.
So now Let's go ahead and think we can already try it out, right? Because we have added our use hotkeys here and we have access to all of these methods. So let's try it out. I'm just going to refresh for good luck here. Let me just zoom out slightly.
And let's add, for example, let me just see whether this is still loading or not. So I will just pause my screen for a bit. Let's try it out. I'm gonna go ahead and add a shape here. And first things first, let's try CMD C and CMD V.
There we go. I can press command V as many times and you can see how it will create new elements. Perfect. Now let's try selecting something and pressing backspace. Perfect.
Let's try selecting more items. There we go. Let's try and select command all. Perfect! Everything is now selected.
Amazing! So looks like it is working. And now let's go ahead and let's do the following. Let's try pressing command Z. There we go.
So our undo is working. And let's try command Y, our redo is working as well. And our command save is also working but we currently can't see that so let's just confirm that it is working by going inside of a use history here and let's add console.log todo save to database so I'm just going to add a todo here inside of the use history so I ensure that this is being called on my hotkey So let me go ahead and open this and I will query by to do. And there we go. Every time I press command and S I get that little to do.
Perfect. So I can now remove that console log. Our hotkeys are officially working. Amazing, amazing job. So what we're going to do next is we're going to finally add the export functionality for all of these formats here and the open file functionality.
Thank you.