So while I was looking for our bug I've noticed two things happening. First of all, whenever I refresh my editor page here I took a look in the terminal and I saw that we were getting a 500 error every time we refreshed this page. Turns out this is because of a missing module, jsdom. Lucky for us, it is very, very easy to install this. So all you have to do is npm install jsdom or bun add jsdom, as I am doing, or yarn add jsdom, right?
So in my case, it's going to be this we're going to go inside of our package json here we're going to confirm that in our dependencies we now have JS DOM. Here is my version if you're interested. And let's go ahead inside of the terminal now and do bun run dev or npm run dev. Let's go ahead and let's refresh our editor again and see if we get the same error and there we go no more errors in our server logs but we still have this bug that whenever we click on this it does a zoom and also it's not exactly responsive because there is this huge overflow happening right here And I found a way to fix that. It is the reason, the reason for this bug is not in the auto resize hook, it's actually in the editor component itself.
So let's go inside of the editor here. And in here when we initialize the canvas we also have to dispose of it. So inside of this use effect where we run the init function go ahead and add an unmount method here which will very simply call the canvas.dispose method. Like this. So how can this fix our issue?
Let's first test out whether it actually fixes it or not. I'm just going to refresh and there we go. Now, when I click, there is no trigger, there is no zoom. And if I go ahead and scale, there we go. You can see how it stays responsive and there is no extra white space anywhere.
I can even play around with zooming in and zooming out and you can see how nicely it stays responsive right here. I can even go ahead and play around with my arc sidebar here and you can see how it stays very responsive. There is this little sidebar for me here but it does not seem to affect the canvas at all. We can play around with this and perfecting it by the end. But I believe it's also like this in my finished product and I didn't find any bugs regarding that.
There we go. So we have a great product here. Let me just zoom out so just to confirm how it works. There we go. Everything is now fine and our canvas is responsive which is the important part.
Perfect. So how can this unmount method fix it? Well, if you go ahead and add a log here like this. Let's add a log and if you go ahead and visit your inspect element you're going to notice that it triggers two of this which means that every time that we refreshed the editor page it actually initiated two canvas elements. That's normal in development mode, right?
So React in strict mode in development fires useEffect twice and all you have to do to ensure that you don't have production bugs with this is to properly unmount and dispose of certain elements in the unmount function here, right? So that's actually showing how this running useEffect twice in development is actually useful because using this I noticed that we have a bug that would usually be very hard to reproduce in production but because of this it kind of told me something is wrong here and I noticed that I'm missing an unmount disposal here and once I've added that our bugs went away. There we go. So we now have a proper layout here with the navbar, the sidebar, the toolbar and also with the footer right here. So what I want to do now is I want to go ahead and I want to start styling our navbar.
So first of all, I want to go ahead and grab a logo for our project. So you can use any image you want or you can visit the logo Ipsum page. And in here just go ahead and pick any logo that you like. So I'm just going to go ahead and find the one that I've used originally. It is this one.
And you can just click and it will copy the SVG for you. So now I'm going to close this. I have my SVG copied and I'm simply going to go inside of my public folder where we keep our assets. And I'm going to add logo.svg and I will simply paste my SVG inside. You can also use the download button on Logo Ibsen page.
But again, you can use any image you want. Now that we have our logo, I want to go ahead inside of my source, features, editor, components and let's create logo.psx here. And let me import link from next link. Let me import image from next image. And export const logo right here.
So this will be not a link, but it's going to be not a div, but a link, my apologies, which will always lead to the homepage when clicked. And then inside, we're gonna have a div, which will render the image. Let's give this div a class name of size8 which is the equivalent of writing h8nw8. So you can shorten that by using size8. Let's also give it a relative and a shrink 0.
Now for the image here let's give it a source of slash logo dot svg so that is our logo in the public folder right here so you don't have to put the prefix for the public folder right public folder assets are accessible under the root here and now let's go ahead and give this a fill property because we gave it a relative to its parent so it's going to fill up the max space of size 8 let's give it an alt of image.ai or whatever is your project's name and let's give it a class name of shrink0 as well hover opacity 75 so it has a little effect when hovered and transition Now let's go ahead and let's go back inside of the editor component. And we actually have to go inside of the nav bar. So make sure you're just inside of source, features, editor components, nav bar right here. Let's go ahead and let's import our logo either from .slash logo or features editor components depending on how you like it and let's go ahead and add our logo here and now if you take a look at your project you should see the logo in the root of your app which when clicked will lead back to the root page so now I have to go back to editor 1, 2, 3 and there we go I'm back inside of here.
Perfect! So now I want to go ahead and I actually want to create something else. So I want to create a couple of components which we're going to have to add from ShadCN first. So let me shut down my app right here and let's start with the drop down menu. So I'm going to use BUNIX, chat-cnui-latest-add-dropdown-menu.
Or if you're using NPM, you would use npx-chat-cnui-latest-add-dropdown-menu. So let's just wait a second for this to be installed. There we go. I think that's everything we need here. And I also want to add react icons package.
There we go. So just make sure you have this as well. Let's run our app again. And now I'm going to go back inside of the navbar and I will focus on developing right here. So first things first, I want to add all the new imports from my new drop-down menu right here.
So we need the drop down menu, we need the drop down menu item, drop down menu content and drop down menu trigger. You should have all of those in components UI drop down menu because we just did that using the NPX chat CNUI latest add drop down menu. There we go. Now let's go ahead and let's start developing this. So I'm gonna go ahead and after the logo I'm gonna create a div here with the class name of full width flex items center gap x1 and height full.
Now inside of here I'm going to add our drop down menu here and I'm going to give it a little prop model to be false because if you use the drop down menu to open dialogues in that case your website will actually freeze because of a bug with overflow on the body element. And you can fix that, so we are doing that proactively. Later, if I remember, we can go ahead and remove this so I actually show you the bug. But for now, just remember to add this to your drop-down menu elements. Now let's add the drop-down menu trigger and let's go ahead and add a button component here.
So just make sure you've added the components UI button here. And also always make sure that whenever you're importing the Shazam components, you never import from a Radix package. Those are unstyled components and they will just cause bugs in your project. So always check that all of your imports are coming from components. Alright and now if we want to use the button as the trigger element we have to append as child property here so now this element will become this button right here.
And let's go ahead and give this a text of file and let's add Chevron down from Lucid React. Let's give this Chevron down class name a size of 4 and ml of 2. Let's just not misspell class name. And I also want to give this button right here size of small and a variant of ghost. I'm just going to refresh my localhost which you should do also if you've restarted your server.
And there we go! Now let me zoom in. You should have this file right here, which when clicked will open up some options. Let's go ahead and continue developing here. So we now have the trigger and now I wanna add the dropdown, whoops, content.
So we should have all of those imported let's give it another line of start and let's give the class name of min with 60 So I want it to be a little bit larger than usual. So 240 pixels. Now inside of here, I'm gonna add the dropdown menu item. And we're gonna go ahead and render a CI, whoops, file on from react-icons.ci. Let me move this global imports at the top like this.
So CI file on from react-icons.ci and Chevron down from Lucida React. So CI file on is going to be a self-closing tag and we're just going to give it a class name of size 8. Let's go ahead and give the drop-down menu item itself, a class name here, flex items center and gap X of two. And now below the CI file on, we're gonna have a div with a paragraph, which will very simply say open. And then we're gonna add another paragraph, which will say open a JSON file, like a little description.
So we're gonna give it a different class name because this is kind of descriptive text. So it's gonna be extra small text, and we're gonna give it a text of muted foreground. There we go. So now if I go ahead and expand this, when you click on the file button you will have a nice little drop down to open a JSON file. So you saw that functionality in the demo.
We are later going to come around to actually develop this. For now, I wanna prepare the UI for that. So if you want to, you can also prepare the onClick here, which for now will not do anything, but later it will be, of course, add functionality, right? Right now, this does not exist. Great, so now I wanna go ahead and add a couple of more elements and a couple of more buttons here.
So first of all let's go back inside of our terminal here and let's do bunx-chat-cn-ui-latest let's add a tooltip component and let's also add a separator component. So we need those two. There we go. Tooltip and separator. So let's go ahead and run around Dev again.
If you've shut down your server, make sure you restart your website here. I meant refresh And now I want to go ahead where the dropdown menu ends and I'm going to add a separator component from components UI separator. So this is what I was talking about. Don't import it from erratics UI slash react separator. Import it from add slash components ui separator so as I've added it right here there we go and so now we have the separator which will be a self-closing tag and let's give it an orientation to be vertical oops like this and the class name mx2 there we go so now if you notice after a file we have this vertical line right here separating the other actions which we are going to have.
Great! So what we have to develop now is a little tooltip component but before we do that let's actually create an element for which the tooltip will be for. So let's add mouse pointer click from Lucid React here. Let's give this button a variant ghost. Let's give it a size of icon.
Let's give it an on click which will be an empty arrow function and let's add a little to do add functionality and let's also just go ahead and give it a class name here for now it will be empty but we are just going to prepare for something that will come here later. Right, so I'm gonna add to do add dynamic class. All right, and for the mouse pointer click let's give it a class name of size 4. So it should be a small button. There we go, So this will be the button which we will click when we want to go back to this mode of selecting and dragging, right?
Because we're going to have a lot of modes. We're going to have a drawing mode. We're going to have the shape insert mode. We're going to have the image insert mode, all of those things. So whenever we want to go back, we'll just click here and be able to do this.
Great. So now what I want to do is I want to go ahead and actually create the hint or the tool tip button. So in order to reuse that, I'm gonna go inside of the source components, not inside of the UI. So I'm gonna keep UI for directly importing from the chat CN, right? For my custom components, I'm gonna make them directly in the components folder.
So I'm going to call this hint.vsx like this and I'm going to use this tooltip for that. So first of all, let me go ahead and import everything we need from the tooltip. So tooltip, tooltip content, tooltip provider and tooltip trigger from add slash components UI tooltip. And then let's go ahead and let's create an interface hint props. We're gonna accept the label which will be a string, a children which will be react react node, side which will be an optional top, bottom, left or right.
And now that I think of it, maybe we can actually reuse that from the tooltip itself. I'm not exactly sure. Let's just do it ourselves for now. We can come back to this later. So align will also be start, center and end.
Like this. And we're gonna have side offset, which will be a number inside myApologize, align offset, which will be a number. There we go. So just make sure that side, align, side offset and align offset are all optional by adding a little question mark at the end of their types. Now let's export const this hint component here.
So I'm just gonna go ahead and we have to add all of those props which we've just defined. So label, children, side, align, side offset, and align offset. And let's just go ahead and align this with hint props. So we got rid of the TypeScript errors like this. And now let's go ahead and let's return the following.
So we're gonna return a tooltip provider. And inside of this, I'm gonna add a tooltip itself. I'm gonna change the delay duration of this to 100. So it appears a bit quicker than usual. And then we're gonna add a tooltip trigger.
I'm gonna give it an as child prop. And that's where I'm going to render the children. So we're going to wrap our, where was I? My nav bar. So we are going to wrap this button, which we just created around the tool tip, around the hint component.
So this is where the button will be rendered. That's why this as child is useful. So now below the tool tip trigger here, let's go ahead and add a tooltip content. And inside of here, we're gonna render a paragraph with the label. And now we just have to give it some styles and some props.
So class name for the tooltip content will be text white, background slate 800 and border slate 800. Let me just scroll a bit so my VS Code tooltip doesn't cover the code. So side will be side, align will be align. So we are basically forwarding the props, right? Side offset will be side offset, align offset will be align offset.
Like this. Let's give our paragraph here a class name of FontSemibold and capitalize. There we go. So I think our hint component should be ready for use now. Let's go ahead and let's go inside of our navbar and let's add an import for hint from components hint.
So we're not using UI because this is our custom component, right? That's where I decided to keep it. And now what we're going to do is we're going to wrap our button inside of a hint component here. Like this. And then we can go ahead and decide what label is it going to be.
So label here will be select. Side will be bottom and side offset will be 10. There we go. Let's try it out. So now when I go ahead and hover, it says select.
And we're gonna go ahead and reuse this hint component now throughout our, well, a lot of buttons, which we're going to have, which are basically telling the user more in depth about what a specific action does, because we have to conserve space by just using the icons, but at the same time, we need to provide information to the user. So tool tips are a great solution for that. So that's why I wanted to reuse and create this reusable simple hint component which we can add anywhere in our project. Great! Let's go ahead and let's continue now.
So I'm going to go ahead and copy and paste this hint and this button and paste it below. And this second one will be the undo button. And let's go ahead and do this. So this to do will actually stay the same. The logic will be the same.
We are just changing the icon to undo to from Lucid React. So just make sure you've added undo to from Lucid React. All right. Then go ahead and copy and paste this again. This will be redo and it's going to use the redo to icon from Lucid React.
So let me just go ahead and format this. Okay, looks like everything is fine. Just make sure you have redo to imported. There we go. All right, so now we have those.
Let's go ahead and see if there's anything else we need. So I think that for now, let's actually go ahead and add some more stuff here. So I wanted to stop here, but there are some other things which we can do. So I want to go ahead and do the following. After this hint I'm going to go ahead and I'm going to add our separator one more time.
So where is it? It's right here after the drop down menu. Let's go ahead and add it after this hint here. Great, so a new section is now separated. And now inside of here, I'm gonna go ahead and do a very specific look here.
So I wanna create the little indicator, which will tell the user whether changes are being saved or whether there is an error or something like that. But since we don't have those states we can only do the success message. So let's go ahead and just simply add a static success message here. So we're gonna use BS Cloud Check from React icons BS. So just make sure you add this.
Let me move this to the top alongside other icons and we're gonna have BS Cloud Check, all right? Make sure you have that. Let's give this icon a class name. The class name is going to be size-20px and text-muted-foreground. And then give the wrapping div a class name, which will be flex-items-center-and-gap-x-of-2.
And now, below that, go ahead and add another div element which will simply say saved. This can also be a paragraph if you want to. Let's give this a class name of TextExtraSmall and TextMutedForeground. And now, let's go ahead and there we go. This is how it should look like.
So obviously later this will be dynamic. For now we've hard-coded it, but later this will be a loading indicator while it's saving, or it's gonna be an error icon if something went wrong. So what I want to do now to wrap up this chapter is add one more drop down similar to this one on this side which will be used to export our current content. So let's go ahead and do that. Looks like we are having some overflow here, which happens, but that's on a very, very small viewport, so we can decide later how we're going to fix that.
What's important now is the desktop mode to be working as intended. Right. Let's go ahead now and we can actually... How about we do this? Let's go after the separator we've just added this div right here for the cloud check so we're gonna add another div here with the class name ML auto flex item center and gap x of 4.
Let me just write hello here so I can see where this has been added now. It should be in the opposite corner right here. That's what I want to do. So make sure that after this div which comes after the separator you've added a new section and an ML auto. Great.
And now inside of here we're gonna do a very similar thing to what we did before. So we're going to add our drop down menu here like this. We're going to give it a model of false. I'm going to add the closing tag here. Let's go ahead and let's add the trigger with an as child property.
We're gonna add a button component here. The button this time will say export and we're gonna use the download from Lucid React. So let me go ahead and confirm that you have download imported from Lucid React. Let me just go ahead and align these elements here. There we go.
So you should have the download. We're going to give the download a class name of size 4 and ml of 2 and the button itself will have a size of small and it's going to have a variant of ghost. So now if you take a look, there we go, you have the export button. Now let's go ahead and let's create the content. So drop down menu, content right here.
Let's go ahead and give it an align of end and a class name. Same as above, min width of 60 or 240 pixels in other words. Great, and now let's add the dropdown menu item here. We're gonna go ahead and we're gonna reuse our CI file on icon. We already have this imported from react-icons.ci, so just confirm that you have it.
There we go. So this is the first one which we used and now we're using it again here on the bottom. We're gonna give it a class name here of size eight. And then we're gonna add a div here with a paragraph JSON and another paragraph save for later editing. Since this is a description it's gonna be extra small and text muted foreground so the same as above.
There we go. And now let's go ahead and give the drop-down menu item itself a class name which will be flex-item-center-and-gap-x2. There we go. And let's add an onClick here. And a little to do add functionality because right now it does not exist.
And now we can copy and paste the dropdown menu items again. So the second one will now be PNG and the description here will be something else. Best for sharing on the web for example. We can copy the drop down menu item one more time. This will not be PNG.
Let's call this YPJPEG, not YPJ, JPEG, right? So best for printing, for example, you can of course add your own descriptions for these. And the last one we're going to support is gonna be SVG export. So we're gonna say best for editing in vector software. There we go.
So now we should have the majority of our navbar elements right here. Perfect. So we have the export button right here and we have the file button right here. Let me just zoom out to show you how this will look on somewhat normal size. Like this.
There we go. So JSON, PNG, JPEG and SVG. And we are going to have one more thing after the export and that's gonna be the user button but we don't have any authentication set up so what I'm gonna do is I'm gonna come after the drop-down menu and I will add a to-do here add user button component. There we go. So that will be it for this chapter.
So let's go ahead and let's see what we are going to do next. Great, great job!