Now let's create the methods necessary to export our canvas in all of these formats. So let's head back inside of the Use Editor and we can entirely focus on the Build Editor util that we have right here. So inside of the Build Editor we're gonna go ahead and add a method here. So above getWorkspace, I'm gonna go ahead and add const generateSaveOptions. And I'm going to make this a method.
And we are going to extract from get workspace which I'm going to immediately define as fabric.rect. We're going to extract width, height, left and top. And then I'm going to return name to the image. I'm going to make the format png. Quality will be 1.
Width, height, left and top will be passed as they are. And then we can create the method const saveAsPNG. Or we can make it, we can rather call it Save PNG like this. Inside of here we get the options using Generate Save Options. Then we're going to center this to the canvas, right?
Because right now our workspace and stuff is not centered for the entire canvas It's centered towards us the user because we have the sidebars and all these things but when we're taking an image of it, we need to center it in a way that it will look good once downloaded as an image. So canvas.setViewport.transform Go ahead and pass in the array and inside of the array write the following 100100 and then let's go ahead and create the data URL to be canvas to data URL and passing the options and then let's go ahead and let's create a method to download this file. There is of course tons of ways you can download the data URL But let's go ahead and let's just create a simple util so I'm gonna go ahead and go inside of my editor Utils So here where we have our create filter methods and such and let's go ahead and just add a little package which will help us. So bun add UUID version 4 so we can have some uniqueness to our downloaded documents here and Now after you run the app again, make sure you refresh your localhost Go ahead and import UUID From UUID version 4 And now let's export function Download file It's going to accept the name which will be a string and type which will be a string And actually this should not be name This can be the file, right?
Which is basically our, what is it? Data URL, that's what it's going to be. So inside of this method here, we're gonna create the anchor element to be document, create element, anchor like that. So basically we are going to simulate an href, right? An href with our data URL here, and then we're going to, you know, make it a type of download.
That's basically what we are doing. So we're simulating a click on it so our anchor element dot href will be our file and our anchor anchor element dot download will be a string which is UUID dot our type like that and now let's also add document body append child anchor element anchor element dot click and anchor element dot remove there we go, so that's our download file method you can of course find even a bunch of packages which do this you can think of an alternative method, honestly, any solution is completely fine. And now that we have this, let's go ahead and let's get our download file, util, from features, editor, utils, passing the data URL here and PNG. And just make sure you've added the download file. So we have so far create filter, download file and is text type.
Great. And let's call auto zoom here. There we go. So we now have a save as PNG and we can just copy and paste this one and we can call this one save SVG and the only thing that will change is the extension so this will be SVG So the data URL is exactly the same for PNG and SVG and you can ignore the format here. Right?
So it doesn't matter. It will work either way. So let's go ahead and copy this and let's create Save JPEG. It's going to be exactly the same but the extension will be JPEG. And then we're finally getting the one that's different and that's gonna be SaveJSON.
So SaveJSON is going to be a little bit different. So let's remove everything inside of here. First of all, let's get the data URL by using canvas to JSON and passing the JSON keys. So you should already have JSON keys imported here because we've used it a couple of times here. Great.
So we have JSON keys here and we have the data URL and now what we have to do is we have to create a method which will transform all text elements. That's because once converting this from JSON back to the fabric compatible whatever it is, some text styles get lost. More specifically text elements get lost. So we're going to create a recursive method which will transform text into its proper types here. Let's go back inside of the utils where we've just created the download file function and now let's export function transform text and it will accept objects and give them a type of any.
Now if there are no objects passed you can break the method Otherwise, objects for each of the object, give the item a type of any as well. If item has its own objects, we are recursively going to call the transform text again with item objects inside. Like that. Else, if we've gotten to the end, item.type text, in that case, we are going to assign the item.type to be textbox, like this. There we go.
Now let's go inside of the use editor here, and let's simply await transformText from features.editor.utils and passing data.url.objects and make this an asynchronous method. Make sure you've added the transform text feature here like that and now we have to define the file string here to be open back text data colon text slash JSON semi colon char set UTF-8 comma and code URI char set utf-8, encode URI component, and then json.stringify data URL null and backward slash T. So this options right here inside null and backwards slash T as far as I understand will not have any effect on the actual structure of the JSON but it will make it more readable. And this part is needed so that the download button recognizes it as a JSON download. Same thing with this.
So just make sure you didn't do any misspellings here. So charset, utf-8, data, column, text, slash, JSON, semicolon. Make sure you have all of those things ready and now that you have the file string you can go ahead and download the file pass in the file string and json as the format there we go And now let's go ahead and let's define one last method here. Const loadFromJSON. Oh, let's see, did I forgot to do...
No, okay, everything's fine. My apologies. So loadFromJSON will do the following. It will accept the JSON in a string. It will turn it into data using JSON parse JSON.
And then we're simply gonna call canvas load from JSON. We're gonna get the data. And then in the callback, after we've loaded from JSON, we're just gonna call autosum. There we go. We now have all the methods needed, So we can go inside of the return method here and we can add all of those.
So let's add Save PNG, Save JPEG, Save SVG, Save JSON and Load from JSON. Let's do that and well let's be consistent. If I named all of these save and then the format, let's do the same thing here. So load JSON. And let's rename this here, load JSON.
And then let's go inside of the types here and let's add all of those so save png is a void and then save jpeg is a void save svg is a void save json and load json is a void And I think that loadJSON accepts something, right? Yes The other ones they don't. Great. So loadJSON accepts a string. So inside of the types addJSON string There we go.
We now have all the functionality ready so we can head inside of our navbar now. So inside of the navbar we were kind of already prepared some features here. So now let's go ahead and actually add them. So let's find which one is the first. The JSON.
So we're gonna go ahead and call editor save JSON like this. So let's go ahead and remove the todo here. Let's go ahead and find the PNG so save PNG let's go ahead here and let's call save JPEG and inside of here save SVG like this and we are going to leave this to do as it is so let's see if this is working now so I'm going to go ahead and I will simply add a text in the center so let's keep it simple I'm going to click export and let's try PNG. There we go. So something here has been downloaded and let me go ahead and check it out now.
So I'm going to go ahead and open and there we go. This is my image. Perfect. So I'm going to go ahead and try adding a random image here and I'm gonna put it here in the corner For example, I'm gonna put the heading below that I'm gonna add some drawings here and I don't know. Let's add a little shape here something like that So I just want to test out the positioning and let me also change the color of my workspace.
And let's export as JPEG this time and let's see what that could look like. And let me just move that here. Just a second. Here we go. Perfect!
So exactly what we see on our canvas is now being transferred. Great! So you can go ahead and try out. I'm just gonna test out if I can. Oh, come on.
Why is this shortcut here? No, please go away. Just a second for my shortcut to hide. And let's go ahead and try SVG now. There we go.
So this opens the SVG. Great. And now I also want to test out JSON, right? So JSON works as well, perfect. And now we have to create this.
Open a JSON file so that we can confirm whether the JSON file is correct or not. So let me go ahead and open my JSON, which I've just downloaded. So it will take some time because for some reason it opens in Xcode, which always takes some time to load. So I'm just gonna pause the video. So there we go.
You can see how the JSON file looks like. We have the version of Fabric which we are using which is 5.3.0 and then inside we have all the objects starting from the first one which is our workspace, right? So instead of here, you should find where is the name, clip, selectable false and has control false. And then we should have the heading here somewhere. I'm not sure what is the heading.
So this is the clip path. Where is my text here? Oh, it is somewhere, I guess. I don't know I can't find it now basically we do have something here perfect oh actually I don't have it because right here I just tried it on an empty document. Great, so now we have to implement the file open.
Let's go ahead and try it out. So for that, we're going to need to find a way to open the file explorer and the easiest way is honestly just to use a package. So let's do bun add use file picker. If you don't want to add a dependency for this, if you want to you can find this package and you can simply copy its contents and paste it directly inside of your code or you can code your own solution. Now let's go ahead and run dev.
Let's refresh the app. Inside of the navbar here let's add an import useFilePicker from useFilePicker. Like this. And now let's go ahead and add this hook it's inside of the navbar here I'm gonna go ahead and add useFilePicker in the arguments here we're gonna pass the accept to be .json and on files successfully selected we are going to go ahead and open the method we're going to gain access to plain files we're going to give the entire prop simply a type of any here and if plain files is even available and if plain files length is larger than zero then we're going to transform this from plain files into an actual readable file using the reader. So new file reader, let's just not misspell the file reader here.
And let's add reader, read as text file, utf-8 format and reader on load be careful don't write on load here that does not exist so it's on load like this on load will call editor question mark dot load from JSON or load JSON reader dot result as string there we go so that editor?.loadFromJson or loadJsonReader.resultAsString There we go. So that is our method here and inside of here we have to destructure open file picker there we go and now let's find our onclick for the open adjacent file and we are very simply going to call the open file picker here and let's remove the comment like this so this is what I'm going to do now I'm gonna go ahead and I will add a text and I'm going to name this exporting and I'm gonna add a little shape here. I'm gonna, you know, draw something here. Just gonna add some elements to prove that these things are working here. So image, right?
Something that needs to be preserved in a JSON. Let's go ahead and let's export this as JSON. And now I'm going to refresh my entire page here. Right from scratch. And I'm going to click open.
There we go. This now opened my workspace. So I'm going to go inside of my downloads here. I will select the latest document. And there we go.
In the exact place where it was. Perfect! So amazing, amazing job! You just finished exporting and importing. And what I want to add now is just one little thing before we wrap it up.
So we're gonna add a little hook call use window events and we're gonna add here inside of editor hooks so use window events we're going to import use event from from react-use and let's export const useWindowEvents here. We are not going to need any props. So useEvent before unload will get the event and if event or window.event.returnValue we're simply gonna assign it to are you sure you want to leave and now go inside of the use editor and add the use window event anywhere so let's go ahead and let's add it here use window events so just make sure you are adding this inside of the use editor don't add it inside of the build editor. Build editor is not a hook right so instead of the use editor and I will just change this here. And now basically what this does is the following thing which I think is pretty cool.
So if you're in the middle of your project here, you know, you're working on something and if you accidentally now hit refresh, there we go. We now have this little pop-up which shows up, you know, are you sure changes that you made may not be saved. So it's like those actual editors online, which warn you that you will lose your progress. Amazing. So we now wrapped up pretty much everything we can do without being logged in.
So the next features are the design feature, which is basically, you know, the templates for which we need the user, which will create them. And we need to create, you know, saving things to the database. And now we're going to go back to this homepage here and we're going to start with authentication finally. So we're going to be using NextOut in the next chapter and we will set up our authentication. Great great job