Now I want to implement the copy and paste functionality. So let's go ahead and let's revisit our use editor hook. So inside of features editor hooks we have the use editor. And let's go ahead and go directly inside of the use editor. And just above the use resize let's add use clipboard.
Of course we have an error because we have not yet implemented this. So inside of the hooks create a new file use clipboard.ts. Let's simply add an empty function inside. So export const useClipboard like this. And then we can go back inside of the use editor and we can import useClipboard from .slash useClipboard.
You can of course change the way you import if you want to like I do. There we go. Let's go back inside of the use clipboard and what I want to do is I want to create an interface use clipboard props. The only thing we're going to accept is a canvas which will be a type of fabric.canvas or null. And then let's go ahead and also add the import for fabric from fabric.
Then I want to go ahead and I want to assign these props right here. And let's destructure the canvas. After you save this, go ahead and actually add the canvas to the use clipboard hook from the use editor hook. Like this. Now we have to implement two methods inside.
The copy method, both of them will be wrapped in useCallback so that they can be used as dependency arrays. And we're also going to have the paste method. First things first, let's create the copy method. So for that we're going to have to find a way to store what the user copies and we're not going to be using state for that. Instead we're going to be using ref.
So let's go ahead and create a clipboard as useRef with a default value of null and we're gonna define the value as any. Inside of copy here let's go ahead and get canvas question mark get active object execute the method and then execute the clone method on it. Inside of here you're gonna get the cloned parameter. Let's mark it as any. And let's simply assign to clipboard.current the currently cloned object.
There we go. And all we have to do is pass in the canvas in the dependency array. And then in the paste method we're going to have to do some more logic. First of all, we're going to check whether the clipboard is empty. So if clipboard.current is empty, we will simply break the method.
Then let's go ahead and do the following. We're going to proceed with what is copied inside of the clipboard and we're going to clone it. So execute the clone method and then you will get the cloned object. So let me go ahead and scroll down so you can see. Go ahead and give this a value of any.
And then we're gonna go ahead and call canvas? And we're going to discard the current active object which was selected up until now in order to copy. And what I want to do now is the following. So imagine that we are copying this circle. If I just click copy and paste like this, this would happen.
We now have two circles, but you don't know that until you actually move them. So I don't want that to happen when we copy and paste. Instead, if you copy this circle and when you click paste, I want the second circle to appear somewhere here. Right? So not exactly on this position.
I want to offset it. And then when you click paste again, I want it to move further and further and further and I also wanted to discard the current active object which is what we're doing right here and I wanted to focus on this object instead. So that's what we want to achieve. So let's go ahead and create that little positioning trick so it moves further to the left. So we discard the active object and then to the new cloned object we are going to call set.
We're going to change the left to clonedobject.left plus 10 and the top to cloned object dot top plus 10 like this and let's also add evented to be true just be careful when writing this so you don't misspell any of these things because we are using any. And now let's go ahead and call if clonedObject.type is equal to activeSelection. Go ahead and write clonedObject.Canvas and pass in the canvas from our props. And then on the cloned object we're gonna call forEachObject here, like this, get the object, which is a type of any and simply call canvas add object. Like this, and then call cloned object dot set quotes.
In the else clause, we are just gonna call canvas question mark, add cloned object Like that. And then what we have to do is we have to preserve the value of our clipboard. So after we paste we are not going to remove things from the clipboard. We are still leaving them in the clipboard. But what we have to do is we have to further move the next element to the left and to the top.
Right? Because over the next iteration it doesn't matter if we do this plus 10 because it's just gonna be in the same position as our last pasted element. So we have to prepare for that case by doing the following. We are going to access our clipboard current top and we are going to increase it by 10. And then we're gonna do the same thing for the left position.
Like that. Then let's call canvas.setActiveObject and let's click on the new cloned object. Like that. And finally canvas.renderRequestAll. RequestRenderAll.
Like this. And in the dependency array we're gonna pass in the canvas and finally let's go ahead and let's return the copy and paste so since we are using a lot of this any options just make sure that you didn't do any typos you can always double check with the source code. The reason we're using any so much here is because I could not find the appropriate types for this even if I tried using fabric that object most of the time as you can see for example here it's okay but I'm not sure if it's exactly a fabric.object or if it's something else but if I try in the paste method we just get a lot of errors so it's just easier to work with any If you want you can explore what are the correct types for this. Great, so just make sure you don't have any obvious typos in these methods right here. And now that we have that we can go inside of the use editor and from the const use clipboard we can now destructure those two methods.
So copy and paste like this. There we go. So now, well, you have the option of how you want to call these copy and paste methods. So if you want to, you can throw them here in the editor or you can directly return them from here. Like you can do copy and paste here.
So however you want to, for some reason, I really like if everything is stored instead of one like util for me. So I just want to return the init method and then I want to use this editor so for me what I'm going to do is I'm going to add them to the build editor here so I'm going to pass in copy and paste I'm also going to add them to the dependency array I'm going to go inside of my types here. And of course, we now have to actually define this. So let's find the build editor props here. So copy is a simple void and paste is a simple void.
So build editor props is now satisfied. And there we go. So these are the build editor props, which means that in here, we now have copy and paste. And what I'm simply gonna do here is in the return, the copy will simply call copy and paste will simply call paste. If you want to, you can change this on copy and on paste, for example.
And now let's go inside of types here and this time inside of editor so on copy We'll call a void and on paste will call a void as well There we go so now let's go ahead and let's go inside of our toolbar here and what I want to do is I want to actually implement the copy and paste. So let's find our transparency grid right here and it's actually easier to copy the delete button. So let's just go ahead and put this before the delete button and let's call this duplicate and the icon for this will be the copy icon from Lucid React and inside of here we're gonna call two methods editor copy and editor on paste like this I'm not sure about this naming to be honest but you know you can of course change this. And let's just ensure that you have the copy icon from Lucid React right here. And then this should be working.
Let's go ahead and try it out. I'm going to refresh this. I'm going to go ahead and add a shape here and there we go. We have the duplicate and when I click you can see how it moves along. So that's what we wanted to achieve.
Beautiful. And let me just confirm. Can it work here? There we go. Works here can it copy two things there we go it can copy two things as well amazing amazing job.