So now that we have the working shape insert tool, what I want to build is this toolbar content right here. So it should only appear once we select something in our canvas here. So depending on whether we selected a fabric object or fabric text, we're going to display some different options here. We're also going to detect if it's an image. So we are going to add some filters and AI to remove the background, for example.
But let's start with the simplest, which is just the shapes here. So before we do that we're going to have to create a state which is going to hold our selected objects. So let's go ahead and do the following. I want to go back inside of my use editor. So I'm going to go into source, features, editor, hooks, use editor right here.
And I'm going to go directly inside of this hook here. So I'm going to go ahead and I'm going to add selected objects and set selected objects. Like this. And let's just go ahead and assign a proper type here so the type is going to be fabric.object and it's going to be an array of those objects like this and by default it will be an empty array like that so selected objects and set selected objects great so now what I'm going to immediately do is I'm going to create a new hook which we are going to call use canvas events. So let's go below here and write use canvas events and let's pass in the canvas.
Let's pass in the container and let's pass in the set selected object. Objects like this. There we go. Now let's go ahead and create the use canvas events dot VS let's export cons use canvas events here and I'm gonna go ahead and create the interface. Use canvas events props which will accept the canvas which can be a fabric canvas or null.
I also want to import fabric so let me just import fabric from fabric I also want a container here so that's HTML div element or now we also want to set selected objects to accept or null. We also want setSelectedObjects to accept objects which are fabric.object and an array and it will return a void. And that's it for now. Later we are going to expand this with some more options. So let's go ahead and destructure the canvas, the container and set selected objects and inside of here we're going to go ahead and we're going to add a use effect from react so just make sure you add that let's define it like this and inside of here we're going to check if we have a canvas so let's add it to our dependency right here if we have canvas let's go ahead and add an event to the canvas.
Every time selection is created, we're gonna get an event and let's go ahead and do the following. Set selected objects, event.selected, Like this. And let's just go ahead and see what we are missing here. So I guess we can go ahead and change this to selected or an empty array, like this. There we go.
Let's go ahead and add another one. So this will be selected updated. So in case that happens it's going to be the exact same thing and I want the last one to happen here which will be selected cleared in that case we're just going to go ahead and give it an empty array like this and we don't need the event at all in this case. Great! And we also need to pass in...
So let's do it like this. Let's pass in the canvas and setSelectedObjects but I'm just going to give a comment here. No need for this. This is from setState. Right?
So the linter will give you an error if you don't use this. As you can see, right? Set selected object is missing, but you actually don't need it. That's because set selected objects comes from use state. You never have to put the dispatcher from useState in your dependencies for your memorization or your useCallback or anything like that.
But I just want to leave a comment here. The proper way to fix this is simply by adding it right but it's not gonna change absolutely anything that's why we're leaving a comment here for us to understand that technically we don't need this but because we are using this concept of you know separating into different hooks it has no idea where this is coming from. Even if you gave it a proper you know set state of dispatch type it will not exactly work. So this is the only way you can fix it but just add a comment for you to know that there actually is no need for this right it will work just as much with or without this. Great!
So we have this for now and let's see if we need the container anywhere. Looks like we don't need the container so let's remove it from here. Let's go back inside of here and let's import use canvas events from .slash use canvas events I'm gonna change this to features editor hooks hooks like this and I'm gonna remove the container because we do not need it. There we go. Great!
So let's just go ahead and add some comments here so you don't have to do this. I'm gonna do it myself. So console.log created, updated, cleared. And yes, let's not forget one important thing here. So we also have to clear these.
So if canvas exists, canvas of selection created, selection updated and selection cleared. Like this. This is also very important. All right, let's go ahead and check it out now. So I added this console logs.
So in here I'm going to zoom in this and refresh the entire thing and let's go ahead and let's add something and let's see now looks like nothing is happening at the moment so let's see exactly what we did wrong here. So all of this should fire individually if we have the canvas. So let's see what's going on, why is it not working. So first of all I'm going to check whether the main use effect is here. So use canvas events.
Let's check that first. So I'm going to refresh this. Looks like, okay, it is here. Use canvas events. Perfect.
Besides that, I also want to check if we have the canvas. Let's check that next. So we do have the canvas so it definitely can go inside of this if here. So I'm gonna go ahead and confirm that here and inside if clause Let's check that out Inside if clause is rendered here, which means that it's gotten to this point but it seems like it's not logging this selected, created. So, oh, that's because that's not the name of the events.
My apologies. So selected, all instances of selected should actually be selection like this. So you should have selection created, selection updated and selection cleared. Those are the proper event names. My apologies.
And we actually gave them proper names here in the unmount. My apologies. All right, so that's how we debug this very quickly. We knew that everything's okay with the if clause but something else was wrong. So let's go ahead and try this out again.
There we go. So selection has been created And now selection has been cleared. There we go. And I think that we can simulate selection updated. There we go.
So you can see how that happens. Or if you hold shift and select another element. If you click outside, it's cleared. If I select both of those, it's created. If I click command and click or shift on one there we go now it's updated.
Perfect! So we now have that and now our events are stored, our selected objects are all stored inside of this setSelectedObjects. So we technically don't even have to use this so we can put an underscore here so that way we're telling ourselves mostly that this will not be used the only reason we need this is so it re-renders the entire use editor hook. That's why we need this. But you know, we're gonna use, we're gonna see what we're gonna do with this later.
For now, I wanna leave it like this, but let's actually maybe just remove the underscore just so I can see it darkened, so I know it's unused. So I'm gonna decide how we're gonna handle that later. But for now, I think this is perfectly fine. Great, so now that we have this, what I want to do is I wanna prepare some initial settings here alongside the selected objects. So I'm going to separate that and we're going to start with the fill color.
So fill color and set fill color will be use state and by default I want the fill color to be fill color from my types. Remember that we defined this some time ago to be RGBA 0.0.0.1 right and we use them in this default circle options so now we're using it again to set it right here inside of our use editor initial fill color. Besides that let's also add stroke color and set stroke color, use state stroke color. So make sure you just import stroke color here. And besides that, what can we prepare?
We can also prepare stroke width, set stroke width, view state, stroke width, again from types. So just make sure you added this import as well and whoops I made a mistake here so this is stroke width and that's the only thing I needed to add from my feature types and I don't need this empty import like this so these are my imports from feature editor types let me just add a little comma here there we go perfect so now that we have this What I want to do is I want to pass them down here inside of the build editor. So let's go ahead and pass that. So besides the canvas we're gonna have fill color and set fill color. We're going to have stroke color and set stroke color.
We're gonna have stroke width and set stroke width. And I just like to order these by length, but it doesn't matter how they are ordered. It doesn't matter because we're using an object so they can be positioned however you want it's going to work. And let's just see so fill color exists, stroke color exists, all of these exist that's fine so we just have to add the type for the build editor props here. But before we do that, let's go ahead and let's add canvas.
And let's go ahead and add all of these fill color, stroke color. But the ones we don't actually need are the set fill color, the set stroke color and set stroke width because as I said you never have to put your dispatch actions inside of dependency arrays. That's why inside of here we wrote this comment but this little function does not know that this is coming from set state. Right. Right.
Now let's go ahead and fix inside of this build editor the build editor props here. So we have to accept the fill color which will be a string, stroke color which will be a string, stroke width, which will be a number. And let's go ahead and see how we should now define the set fill color, for example. So set fill color can be a method which simply accepts a value which is a string and it returns a void. Set stroke color can be the exact same thing.
And set stroke width can be the exact same thing except value will be a number. There we go. So now that I've added these elements here, you can see that use editor here, I mean build editor no longer throws any errors because it expects all of these elements right here. Perfect. So now that we have them here, we can go ahead and destructure them inside of the build editor.
So we should now have fill color, we should have set fill color, stroke color, set stroke color, stroke width, and set stroke width. Perfect. So we have all of these now and we can now use them inside of these final methods right here. So let's go ahead and quickly create a couple of methods which can help us. I think the primarily easiest one can be to set fill color.
So let's go ahead and add that. So let's actually add them above, right here. Set fill color again, so same name. Will work as the following. So when the user, when the developer calls this method, it will accept a value, which will be a string.
And the first thing we're gonna do is we're gonna call our existing set fill color and passing the value. So This set fill color will call the one from the use state. So that's gonna change for the main editor, right? And then what we're going to do is the following. We're gonna go ahead and get our app active objects.
So const, actually no need for const. So we can just do canvas get active objects for each get the object and inside of here object.set and simply apply the fill color which will be the new value. Like this. There we go. So you can now go ahead and go inside of your types and the interface editor will now have the set fill color which returns a simple void and accepts the value string.
There we go. And we can do the exact same thing but for the stroke width and the stroke color. So let's do stroke width first. So the value here will be a number. Set stroke width, accepts a number and the same logic follows so we get all active objects which we have and we will simply change the stroke width here to stroke width like this and now we need to apply that to the types here so set stroke width will be a value of number and void.
And now that I think of it perhaps naming this the exact same thing can get confusing. So how about we do change fill color here and change stroke width here. How about that? And then in here, so in the props it's going to be set set set but we are going to do change stroke width and we're going to change fill color. How about that?
I think that kind of makes more sense, right? So these are differently named methods now. These ones are in regards to our set state right here in the use editor And these ones are the ones that we are going to call inside of our components, like toolbar, navbar, right? Wherever we want to activate this methods. And we're gonna do the same thing for the change stroke color.
So the value here is going to be a string again set stroke color will be the value and we can add stroke color is it a stroke color or is it something else oh it's just called a stroke like this there we go let's go inside of the types let's copy and paste this, change stroke color, exact same thing. Perfect, So we now have this method which we can easily call from anywhere inside of our project simply by doing editor.changeStrokeColor and it's going to do everything it needs here in the canvas with the selected objects. Perfect. But one thing that I quickly want to do is define a little method called is text type and let's do that inside of the editor here I'm gonna go ahead and create a new file utils.ts inside of the editor folder inside of the editor feature and let's export function is text type. Type can be string or undefined and let's return if type is equal to text or if type is equal to I dash text or if type is equal to text box.
So these are all possible fabric text types. So it can be text, I dash text and text box. So for all of these we're gonna have to do some different options. For example, inside of change stroke color, if the selected object is actually a text, we cannot change the stroke. We have to change the fill value for it to make sense so let's do it like this if is text type from utils and passing object dot type in that case return and above that object set instead of stroke it's gonna be fill the same way it was above like this so if it is text type we're not gonna change the stroke here but the fill So it's kind of a hacky way, right?
So text types don't have stroke. That's why we have to add, change the fill value instead the same way we're doing this. And what I wanna do is just change this to features, editor, utils. All right, Perfect. So now that we have these values, I also want to return them, right?
So we can do that wherever you want, but Let's go ahead and return those simple things at the end, right? So I want to keep my methods here first and then at the end here after our last function, which is add diamond Let's go ahead and return the fill color stroke width And Stroke color, right And then we have to go back inside of the types here and do the same thing here. So stroke, so fill color is a string, stroke color is a string as well. And stroke width is a string is a number. There we go.
So now that we have those, we have no problems adding those here. So we can now easily access them again, simply by using this editor export, which we are returning here. So we can always know which value to display to the user as the currently active value. Perfect! So we now have kind of the back-end logic behind that.
What I want to build now is the actual editor, a toolbar right here. So let's go inside of source features, inside of editor, components, editor and now we have to pass some props to this toolbar right here. Before we just do that let me quickly check inside of the use editor. I think there's one thing that I want to return here and that's the canvas itself right? So in the build editor I want to add all the way to the bottom here, alongside fill color, stroke color and width.
I always want to have my canvas. So in case there is a method that I don't have built in, I can always directly access the canvas. So let's go ahead and do the following. Now We have to go inside of our editor types here and just as we added fill color we have to add canvas as fabric.canvas here. Oops!
Let me confirm we have fabric and we do. There we go. No errors here. We now have canvas and we can safely return the canvas. Perfect.
Let's go inside of the editor here now and let's do the following. For this toolbar, we're going to give it the editor, which is going to be the editor. Active tool, which will be active tool. We're going to have on change active tool to be on change active tool. And last thing we're going to do is a key because I want to re-render this toolbar every time a new object is selected.
So we're going to do json.stringify and inside of here we're going to pass editor?canvas.getActiveObject like this. So every time the active object changes, this toolbar will get re-rendered. So it's kind of a hacky way to ensure that we always get the newest selected values inside of our toolbar. So let's go ahead and leave that as it is. So you don't have to, you know, add space here.
You can write all of this in one line like this. Let's go inside of the toolbar now and I want to go ahead and properly give it these types. So interface toolbar props will accept the editor which is a type of editor from types or undefined, it's gonna accept an active tool which is a type of active tool from types and on change active tool which accepts a tool active tool and it returns a void. I like to change this to add features editor types. You, of course, don't have to if you don't prefer it that way.
Let's assign the toolbar props here and let's extract the editor, active tool and onChange active tool. Perfect! So now we can go ahead and work with some objects here. First things first, how about we define the selected object here. So I want to use editor canvas, get active object.
Let's go ahead and do it like that. Perfect. And then I want to go ahead and define a little method here. So we're going to do get property, which will accept any property, whoops, a type of any. And we are simply going to do if there is no selected object we can break this method otherwise, return selectedObject.getProperty like this Maybe it's better if we return null.
I think that might make more sense. Great. And then for example, what we can do is we can get the fill property. So const fill property can be get property and pass in the fill option. Like that.
So Let's go ahead now and try and render this. So I'm gonna go ahead and add properties, set properties, use state in React here. So the reason we are doing this, I know it seems a little bit complicated. The reason we're doing that is because by default, this properties, you know, if we just do selected object dot get, you know, we just created an abstraction here, but you can see how the complete here works, right? So we can get, for example, the angle, or we can get the width, or we can get, you know, fill, things like that, right?
But I just created a method which allows us to do that dynamically here. So let me just go back to this. There we go. So this is not going to update every time we call our, what's the name of our method now let me just go ahead and find it so the use editor right every time we change this you call the change stroke width and change stroke color it's not exactly going to change right it's not exactly gonna be displayed So that's why we're gonna have to do tricks like this. There is also an alternative thing that we can do here.
So we could also, let me try this, const fill property two. I think we can do editor fill color. Maybe we can do this as well and we can see if one will change or not. So, but for now, let's go ahead and do it like this. So I'm gonna go ahead and just define the fill here.
So I'm gonna call this fill color and call this fill color too. So fill color, there we go. All right, and now let's just go ahead and try and render this somewhere. So inside of this div here, which we've defined, I'm gonna go ahead and call another div with the class name, flex items center, full height and justify center, like this. And then I'm gonna add a hint component from components hint.
So just make sure you've added the import for that. And inside of the hint component, we're gonna add a button component. Again, make sure you've added the import for the button. Let's give the hint a label, which will be color, side will be bottom, side offset will be five. The button itself will have onClick, change, onChangeActiveTool, and we're gonna change it to fill.
You can see how I have this auto complete active. That's because our active tool has the fill as one of the possible options here. Size will be icon, variant will be ghost, and we're gonna add a class name here, which is gonna be dynamic. So we need our CN import. So just make sure you've added the CN from libutils here.
And now what we're gonna do is the following. So if we have the active tool and the active tool is fill, in that case this is the selected tool. So let's go ahead and give it bg-gray-100 like this. And then inside of here we can open up a div and it can actually be a self-closing tag because I just want to display the current color. So let's go ahead and give it a class name.
Rounded small size 4 and border. And then in here the style will be background color so if type of fill is string only then we're gonna go ahead and do fill and by fill I mean fill color. Otherwise we're gonna default to black. So let's go ahead and try that out. So let me see if I can format this.
No, okay. Perfect. Let's try that out now. If I refresh here, there we go. We can already see this in the corner, right?
So by default, we have this color here. And what should technically happen now is that I should be able to change the color of this and this little box should change as well but we can't really do that now because we have no way of changing the color So let's leave it like this for now and what I want to do now is I want to create the fill color sidebar. So let's go ahead and quickly do that. So let's go inside of source, features, editor components and let's go ahead and add the fill color sidebar, the SX, and I think we can copy some things from the shape sidebar. How about we copy the entire thing and paste it here, and now we're going to change it.
So first of all the props, the component and the props here are now going to be called fill color props like this and the full name will be fill color sidebar like this. So fill color sidebar props and fill color sidebar will be the name of the component. The props are actually exactly the same so this can actually stay as it is. And let's just add constant change here to accept the value and it's gonna call editor on change or change fill color will be the new value. As simple as that.
There we go. So now what I want to do is first of all, change this. So active tool will now be fill. That's the active tool, which opens this up. The toolbar sidebar, the tool sidebar header will show fill color title and the description here will be add fill color to your element.
And we can go ahead and leave the scroll area as it is and let's remove all the shapes tool here and let's just change this to simply be padding for space Y6 like this So we're gonna leave it like this for now. Everything else can stay the same. We no longer need any of these icons and we don't need the shape tool imports. Let's go inside of our editor now and just below the shape sidebar let's add the fill color sidebar. Let's go ahead and copy these props here and let's paste them here because they're the same and I'm just gonna change my import here.
So let's try this out now. If we did everything correctly, It should be closed by default but when I click on the color here, there we go. It opens up fill color and add fill color to your element and I can close it. I can also change between the shapes and the color open sidebar. You can see how they change the active state.
Perfect. Now what I want to do is I want to create a component which we are actually going to use to change the colors. So for that I actually want to use two different color pickers but they all come from the same package. So let's go ahead and do the following. Let's go inside of our terminal and let's do bun add react-color.
Let's do bun run dev again. Let's just refresh our app. And now what I wanna do is I wanna go inside of features editor components. And in here, we're gonna add the color picker component, color-picker.psx. Let's import the Chrome picker and circle picker from react-color.
And it looks like it doesn't have the types, so we have to install that as well. So let me just go ahead and do that. And there's also one more thing that I want to install. So let's do it like this. We're gonna install two things here.
So first of all, bonad-d add types slash react-color or npm install dash d types react-color. That's the first thing, there we go. Now we got rid of this error here the second thing is bon add material colors or npm install material colors those are the things we are going to need so again just refresh this And let's go ahead and do the following. So now I want you to go first inside of the, before we continue here, let's go back inside of our types so we can define all colors which will be inside of our project. So I want to go ahead and import everything as material from material colors.
And again, we don't have the types for this. So let's do it one more time. BunAdd-d addTypes materialColors. Those are the ones we need. Let's wait a second.
There we go. Resolved and now that we have that what I want to do is I want to add export const Colors and I want to add all colors which we're going to have here. And these are the colors we're going to need. So I just copied this so we don't write it out. So I'm going to zoom out of it.
There we go. So you can pause the screen and you can add all of this. So red 500, pink 500, purple 500, deep purple, indigo, blue, light blue, cyan, teal, green, light green, lime, yellow, amber, orange, deep orange, brown and blue gray. So here's the thing. These colors actually come by default inside of this circle and Chrome pickers.
But I want to give you an ability to add more colors if you want to. So that's why I'm doing this. We are installing this material colors package because that's what they actually use underneath here somewhere if you take a look at the source code of how they actually handle this. I don't know where to find this at the moment, but in the source code of this package they actually use material colors. So what we did here is we created our own array of colors which we want as you know quick access.
So we're going to use them here and I also added one that they don't have which is transparent. Right? They don't have that by default. So we ourselves are adding that right here. Great.
Make sure you have these colors. Of course, you don't have to add all of them. If you want to just use red, you can just do this, right? Great. All right, so just ensure you have this.
You can also check from the source code if you're not sure. And now that we have that, we can go back and continue in the color picker here. So let's import colors from types now that we have it. And then interface color picker props will accept a value which will be a string and on change which will accept the value and return a void So this will be a controlled component. So color picker, we'll accept color picker props.
Let's go ahead and destructure the value and onChange and inside of here let's return a div. Inside of this div we're gonna go ahead and give it a class name of bool width and space y4 because we're gonna have two different color pickers here. So first will be the chrome picker. So the Chrome picker will have a color of the current value. And on change for now, will just be an empty function here.
And let's give it a class name of border and rounded large, like this. Then below the chrome picker we're gonna have the circle picker. Again a self-closing tag. Color will be the value. Colors will be colors.
On change complete will be an empty function for now. You're gonna see why these are empty functions in a moment. So now let's go back to our fill color sidebar and we can go ahead now and import the color picker from features, editor, components, color picker like this. And we can render it inside of this empty area, which we've defined right here. So we're just going to go ahead and give it the proper things.
We're gonna pass in the value to the value and on change in here will be our on change. What we're missing here is the value. So there are many ways we can now hold this so we can be constant value. Let's just try editor fill color. Let's see if this will be enough, right?
But there is also an alternative way we can do it if this is not working as string or undefined. All right, so if we cannot find it we're gonna just use the default fill color here from features editor types. There we go. So this resolves in case we don't have anything predefined. Great, So let's try this out now.
I'm going to refresh this page and when I click here, there we go. Looks like we have a color picker here. What I want to do now is I want to add some additional styles here because I don't like how this looks at the moment. And I think we are kind of limited as to how much we can style using CSS here. So what I wanna do is I wanna go to Globals, which is located right here.
So let me close the things we don't need. So inside of the source app, we have global.css. Let's go all the way to the bottom here. So after everything, I want to go ahead and add a circle picker. I want to add width auto important.
I want to also add react. This is react color package. Actually, no need for what I thought. But we also need a Chrome picker here. And I want to go ahead and give it well the same thing, but I also wanna give it box shadow none.
I wanna give it border radius 0.5 rem. I wanna give it overflow hidden. And I think that's it for now. So there we go. Now, if you take a look at it, it looks much, much better.
Perfect. But right now on change is not doing anything here. And also when you try this out, make sure you have your elements selected, right? And then click here. Great, so now what we have to do, the problem now is that inside of our color picker, the way they return this color change handler, as you can see, returns in a very specific way, right?
So the RGB here is an object right so it's not a string it returns an object so that's why I let's go ahead and do this so I'm going to select x here and I'm going to do console log Let's see if we can see how this looks like. So if I go ahead and open my inspect element here and select something, there we go. So you can see, so we have the hex color. That's great. But we are working with rgba values here so this is what we need the problem is we need this in a string so let's go ahead and let's create a little util which we are going to use to convert the RGBA object to a string.
So let's go back inside of our utils here and let's export function rgba object to string and this will accept rgba which will be an RGB color from react color so you can import type RGB color from react color like this or it can be a word transparent because that's our separate use case and this is not there we go like that Let's first do if RGBA is equal to transparent we're gonna go ahead and return RGBA 0.0.0.0 like this. Let's go ahead and do const alpha So if rgba.alpha is undefined I want to explicitly turn it back to 1. Otherwise we're gonna go ahead and use whatever is the alpha right so you have to be careful here. I'll show you there are different ways you can do this. I think you can also do like RGBA or one but I like to be explicit especially for tutorials right because A always needs to be a part of our final string but it doesn't need does not need to be passed back to us right so we have to be careful with how we're going to handle the alpha and then let's go ahead and return in Vectix a generated string rgba so the first argument will be rgba.r representing the red, rgba.g representing the green, rgba.blue representing the blue and finally just the alpha.
There we go. So this will now return a string. We can go back to the color picker now and we can finally change this. So let's go ahead and get the color here and let's do const formatted value rgba object to string color dot RGB. Let's just change the import here to be features editor utils.
And then finally, we're gonna do on change formatted value. And same thing here. So this will be color. There we go. So let's go ahead and try this out now.
So I'm going to go ahead and I'm going to refresh my page. I'm going to add an object here. I'm going to select the color and I'm going to attempt to change it. So okay I can change the color but it looks like it's not affecting the rectangle. Let's go ahead and see what we did wrong so we can finally you know oh looks like it is rendering but only after some time So only after I do something that re-renders it.
So only after I change my width. All right, so it looks like that is the culprit. So let's go ahead and explore just a bit further. So I also noticed that inside of the toolbar the value of the color is also not changing. So that's not good.
So let's go ahead and do the following. I want to go... The color picker works fine, that's for sure. Now go inside of the toolbar here and let's see what we can do to improve here. So first of all, this is our fill color here.
So editor canvas get active object. We get the property fill but it does not seem to change. Let's see how we can improve this. Selected object, get property. How about we add a console log here of all of our options.
So fill color, fill color two, and let's also log this just below the properties so that we can see what's actually going on. So as I said this is a bit of a tricky part because these colors are not reactive right so they don't exactly work as your normal react project. So let's go ahead and let me change this and see what happens. Okay So the fill color itself is null. The fill color too, on the other hand, seems to be working fine.
And my fill color from the properties is null as well. So the fill color too seems to be more precise than my, this solution. So let me try and just use the fill color here. Can I do that? So I'm just using editor.fillcolor.
Let's see what happens now. So if I change this to pink, there we go. So now we fixed that. Looks like our toolbar can now easily understand which ones are the active colors, right? But the problem we have now is that it's not changing the actual value of our selected object here.
So now we have to figure out what's wrong with that. And it looks like we don't need these properties at the moment and we don't need this. And we actually don't need the selected object. So let's not add them if we don't need them, right? We're gonna add them later.
So this is the fill color for now. And perhaps, is there a way I can make this easier? So fill color, well, it's not actually always gonna be a string. So, All right, how about we just did this? Let's just do fill color.
Whoops. Like this. There we go. That seems much better. We have the proper types.
We're gonna come back to this later if it has any bugs. Alright so that's one bug resolved right again I can change the colors here and here and it changes this in the toolbar but it's not changing the actual color here so let's see what's going on with that here. So we have to go inside of our fill color sidebar here. So what is the onChange? So the onChange calls the change fill color in our editor.
So let's go inside of the use editor here. And we have the change fill color here and why is this exactly not working so I think there is one thing we need here and that is canvas render all And I think we also need this for the stroke color. So let's do that also after we iterate over our elements here. And also after we change the stroke width. Let's do that.
So let's call a render all when we change the color. Let's see if that will improve our logic. So first of all, refresh your page. Don't click here yet. Instead, go inside of shapes and select a shape and then click on the color.
Let's change it to this. There we go. And we can now change the colors of our shapes and it follows it right here. Let's see what happens if I select another one. So this is what I expect to happen.
So it needs to stay the last color it was. The only problem is that when I click on a new element, I expect this color to be captured, right? Instead of this black color. So let's just quickly add that so we can finally resolve this. So what we're gonna do is quite easy.
Go back inside of the Use Editor here. And since we already have the fill color here, what we can do here is find our add circle and besides the circle options let's manually change the fill to fill color. The stroke to stroke color, the stroke width to stroke width. Like that. So our options are going to be preserved.
So the same thing for the soft rectangle, for the rectangle, for the triangle, and Let's see if we can do it here for the inverse triangle as well. And for the diamond. So after the diamond options. There we go. So let's try it out again.
Select the shape, Change the color. This has changed, this has changed and this has changed. I'm gonna add a new shape now. And there we go. So that's what I wanted.
I wanted to preserve the last color. Perfect. So if I select the two of these, I should also be able to change both of their colors now. Perfect. And if I select all of this, let's see...
I'm not sure how we define which one is the last selected color. Well, we are just reading what's the last selected thing, right? Perhaps we can kind of improve this by showing a mixed color, but I don't know, you can decide that. But now if you change, everything changes, right? Perfect, so I think this is actually pretty good, right?
So what we're gonna do next is we're gonna do this exact logic to enable some more items here. But there is one more thing I wanna do and I wanna do it in this chapter so we can properly wrap it up. I don't wanna display this option if nothing is selected. So right now, if I do it, it won't do much except just change it for the next object. Which actually might not be the worst idea, right?
But you can decide for yourself. If you're okay with this just being here, like an option for the next selection, that's fine. But I think it might confuse your users. So you decide whether you like that or not. I'm gonna show you how you can quickly fix that.
So for that you're gonna need the selected object. So let's go ahead and do the following. Oh so we have selected objects here So how about I actually pass that to the editor? Right here in the build editor. Pass that to build editor here.
You're not gonna have selected objects. And what we're gonna do is we're just gonna return it back at the bottom alongside canvas, fill color and everything else. We have selected objects now. And now we just have to fix the types here. So I'm doing this because it's gonna help us in the future.
So build editor props now accepts selected objects, which is fabric.object and an array of those. And it also returns those. There we go, no errors. But we do have to add those to the dependency array here in the editor use memo. So let's just add selected objects.
There we go. So now if you go inside of your not tool tip, but toolbar right here, you can get the selected object from the editor directly. So we don't have to do those, you know, editor canvas get active object, right? So what we can do now is the following. If there is no editor selected objects dot length or if editor selected objects dot length is zero, in that case, what you can do is you can just return a div.
You can just copy this first one and just make it a self-closing tag like this. So just an empty div. There we go. So you can see now it's empty because nothing is selected. So let's just go ahead and click on something.
And there we go. Now it's active. And if I deselect then it's cleared, right? And we're also gonna have to close this sidebar once we deselect, but we can do that in the next chapter. I think we've done enough.
It's almost 50 minutes of this chapter. Great, great job! So you have some really good functionality already in your project. Excellent job!