Before we move on I want to fix a little bug that we have. So go ahead and add two elements to your workspace. I'm going to use two rectangles. And if I change the color of this one to blue and this one to orange you can see that it says in my toolbar the color orange is selected right? Well that's technically correct but if I select this rectangle the blue one I expect the color in the toolbar to change to blue so let's go ahead and resolve that issue.
We're gonna go inside of our source features editor hooks use editor and we're gonna go ahead and modify something with the build editor. So right now the only thing that we do to get the current fill color is we pass the fill color which we hold here in the state. So it has actually no idea what is the active selected object color. So instead of passing this, let's create something more useful. Like a method called getActiveFillColor.
And now let's go ahead and get the selected object from selected objects. And let's simply get the first in the array. So we are passing the selected objects, which we hold here in the state and we assign it in the use canvas events. Every time a selection is created, we pass in the selection here. And we are also passing the selected objects here in the build editor.
And that's why we finally are able to use it here. Let's do if there is no selected object, meaning there is no active selection, then the only thing that the active fill color can be is the fill color which we last used, which is this, which comes from our set state. So that's the latest fill color. Otherwise, let's go ahead and let's return selected object dot get fill. Like this.
Now we have a slight type problem here. If I hover over this, you can see that the getActiveFillColor method returns either a string or a fabric.pattern or fabric.gradient. So let's go ahead and do the following. I wanna go ahead and define const final value or let me can actually do value, right? Selected object, get, fill or fill color, right?
Because this can be undefined. So now the value is either a string or fabric.pattern or fabric.gradient. And since I know that I will not be working with those patterns or gradients, I can return value as string because I know it's always going to be the RGBA string because of the way our color picker uses the RGBA object the string. So that's the only thing we are going to store are these types of strings right so I'm kind of sure about that. So I'm going to add a comment here in case you forget why we do this.
So currently gradients and patterns are not supported. So we know that only string is supported. Now let's go ahead inside of our types in the features editor, and let's change the interface editor to no longer return a fill color, instead get active fill color. And it's a method which returns a string. So here's an important thing.
If you remove this as, if you ever in the future want to support the pattern and the gradient, you're also going to have to fix your types here, right? So you're going to have to change this to also be fabric.pattern and fabric.gradient. Like this. And then you can see the errors go away. But for this project I know that I'm going to be working with strings so I will simplify things for myself and simply add a rule that I expect a string here.
So I will add value as string that's the only type of fill color that I will support. Great! Now let's go ahead, let's go inside of the toolbar. Inside of the toolbar here we are attempting to get the fill color which we just removed in favor of this method. So instead let's do that.
Get active fill color. And let's see if that resolved our issue. I'm gonna go ahead and add two shapes here. I will mark this one as blue and there we go. In my toolbar the color is now blue.
If I click on this one, there we go. The color is black. But we do have an issue here and that is that the active color does not reflect the color picker. So we have to go instead of the fill color sidebar right here. So we did the same thing here.
So what we have to do is do get active fill color like this. And we have to leave this end end because editor can be undefined. So this value without this can be undefined which will then cause errors here because color picker requires a value of sorts so that's why we're going to add this as the end end. Great! Now that we have this it should also work here so I can go ahead and click here and there we go the colors are reflected in toolbar in the rectangle and in the color picker and if I click here there we go the color here as you can see has turned to black but I can change it easily to anything else and you can see how it gets saved here or if I click here it gets saved here.
Perfect! Amazing, amazing job! So what I want to do now is I want to create... Well first of all let's resolve this. Once I click outside, I want this picker to close, right?
Because, well, it's not exactly doing anything except defining what the next color will be, right? So it's kind of weird whether we're gonna allow that functionality or not. To me, it honestly seems like a bug. When nothing is selected, what I want is for this to close. Let's go ahead and see how we can do that.
So I'm gonna close everything here and instead I'm gonna go inside of Features, Editor, components, and I'm gonna go inside of the editor itself, right? And now I'm gonna find my use editor here and what I'm gonna do is I'm gonna do const onClearSelection. I'm gonna give this a use callback so I can safely call it in my dependency arrays inside of use effects and similar stuff. So inside of here, first of all, I'm gonna define my selection dependent tools. And perhaps this is something that can be in our types in the editor.
Yeah, I think this is a better place to put it. So let's remember to export that. So our selection dependent tools will be the fill tool, the font, the filter, the opacity, remove background, stroke color and stroke width. So all of these tools should be closed if there is no active selection. So now I'm going to go ahead and do the following.
I'm gonna check if SelectionDependentTools, which we now have to import, so we have the active, whoops, we have ActiveTool and SelectionDependentTools from our features editor types, so make sure you add that in your source features, editor components editor. If selection dependent tools, dot includes the current active tool, in that case, we're going to set active tool back to select. And now let's go ahead and add active tool here as the dependency in the dependency array. Great, now we have to find a way to call this method every time we clear our selection, Right? Do you perhaps remember an event where we can call this?
That's right! We have an event inside of our editor hooks useCanvasEvents. In here we have selection cleared. So we have to find a way to pass that function here. Let's go back inside of the editor here and inside of useEditor let's go ahead and extract it and let's go ahead and add clear selection callback.
And inside of here I'm gonna pass on clear selection. Like this. And now we have to go ahead and write the proper use editor props here. So we are currently expecting clear selection callback but we have no way of defining those types so let's go ahead and create editor hook props. We're gonna create those in our types That's the only proper place for it.
And let's put it together with the existing editor stuff. So build editor and let's do export type, editor hook props. Or an interface. It really does not matter for this kind of things. So for now, the only thing it's gonna accept is clear selection callback.
And it can be optional, right? If you wanna use this in some other place, you don't need to clear the selection. And now I'm going to add the import. So just make sure you've added editor hook props from features editor types alongside all of these things here. And there we go.
No editors in here and we can pass Any method we want here, right? So that's what we're gonna use this for. Perfect. Now we have to pass this clear selection callback inside of our useCanvasEvents. So let's go ahead and pass it here.
Let's go inside of useCanvasEvents right here. And I'm gonna go ahead and define clear selection callback as an optional prop, which will simply be a method. Let's destructure it here. And finally, when selection is cleared, what we are going to do, after we set selected objects to an empty array, we are going to chainly call this function with a chain because it can be optional, right? So we can just do this.
There we go. Let's go ahead and try this out now. So this method will now be called. Let's see if we defined it properly. So if the current active tool is dependent on the selection, which is all of these right here, we're gonna go ahead and close that sidebar by simply switching the active tool back to the select.
So if I click on shapes here you can see that that just because we don't have any selection this sidebar will not close right so if I click outside this will not close and that's fine. But if I click on the color here and click outside, oh looks like it did not exactly work. Let's see why. Let's see what we did wrong. So I'm gonna go ahead and do the first thing when I debug which is to check whether the on clear selection is actually being called.
Oh, but I think I know what the bug is. I think I already know inside of the use canvas events, we forgot to add the clear selection callback to our dependency array. That's why I insisted on adding the use callback here so we can properly add it to the dependency array. And I myself forgot to add it. Let's try this one more time.
So I'm gonna go ahead here and I'm gonna select the shape. I'm gonna play around with the colors here. I'm gonna click outside and there we go. You can see how the sidebar is now closed. Perfect!
Let's try this again. So if I go ahead and click on this color and change it to orange, the sidebar is open. If I click on this one, it stays open. But if I click outside, it's closed. If I select both, I can still change both of their colors together.
But if I Just select one, it stays on. If I go ahead, none of it exists. Perfect. Amazing, amazing job. So what I wanna do next is I wanna add the ability to change the stroke color, right?
Currently you can see our Stroke is black. So let's go ahead and quickly repeat what we've just learned, but this time with some pre-existing components. Let's head back inside of our toolbar component. So let me close everything here. Toolbar and inside of here, I wanna copy and paste this hint and the button inside of it.
The hint for this one will be border color or stroke color however you want to call it. The active tool for this will be stroke-color because our active tool is a type of active tool and stroke color is one of the tools available. Great! And now let's go ahead and quickly modify this. So instead of background color here, this will be border color.
Right? And let's leave the fill color as it is for now. So we're gonna go ahead and just give this a border of 2. And let's see if we need to give it a BG of white. So it shows better.
So let's just try it out. There we go. So this is how I expect this to look like, right? So what I want to do now is that when I click on it, it opens up the stroke or the border color for the element right and not the fill color. So let's go ahead and do that now.
Let me just try one thing here. So currently both of my divs are encapsulated inside of this div. So let me end this div here. And let me copy it again here. Like this.
And I'm just going to go ahead and indent the hint. Or actually I just have to do this. There we go. Like this. So let me just see how that looks like.
There we go. That's how I wanted it to look like. I wanted them to be a little bit separated. Great! So now what we're gonna go ahead is we're gonna go ahead and copy the fill color in features, editor, components, copy the fill color sidebar and let's call this stroke color sidebar.
It's right down here. Let's rename it along with the props here. So stroke color sidebar. This will pretty much be exactly the same. Let's change this to stroke color and let's do the add stroke color to your element.
And since I'm calling everything a stroke, I might as well do it in the toolbar here. So instead of border color, let's call it stroke color. There we go. And I have to go inside of the stroke color sidebar and I have to change the active tool to be stroke color. There we go, like this.
Now we're gonna have to create the methods and change this default values here. Before we do that, let's go inside of the editor itself. Let's go ahead and copy and paste the import for the fill color and add the stroke color sidebar from components, stroke color sidebar. There we go. We can then copy and paste the fill color sidebar and rename it to stroke color sidebar.
We now have to go back to the toolbar component, find our new button for the stroke color and instead of calling the onChangeActiveTool with fill it's going to be strokeColor and confirm that you change the active tool to be equivalent to stroke color here as well instead of fill as it's here. Let's go ahead and try it out now. So I'm going to refresh my page. I'm going to add a shape here and if I click on stroke color here, There we go, stroke color changes. Right now the logic is exactly the same as the fill color, so it changes my fill color.
We're gonna go ahead and resolve that now. First things first, I wanna go ahead back inside of my use editor here, and I wanna go inside of my build editor method and just as I've added the method called getActiveFillColor I want to go ahead and copy and paste this with getActiveStrokeColor like this and instead of looking for fill we're gonna be looking for stroke so now I'm just interested in seeing what can this be so if I return this like this the value is a string perfect so it's just gonna be a stroke or stroke color my apologies stroke color from the state like this And then we don't need this as a string because value for the stroke can only be a type of string or undefined. And now I no longer need this stroke color here because we are doing this in favor of that. Let's go back inside of our types here. Let's find our interface editor and in here we have get active fill color.
So let's just copy and paste that and rename it to get active stroke color and remove the stroke color right here. There we go. No errors anywhere. Perfect. So now I want to go back inside of my toolbar here and I'm going to change this const to stroke color.
Editor, get active stroke color. There we go. Let's go ahead now inside of, well, let's go ahead and use the color. That's what we should do. So instead of border color being fill color, it should now be stroke color.
So now if I refresh this and if I go inside of shapes and select the shape, and if I change the fill color, there we go, you can see how stroke color stays exactly the same. Perfect. So let's go ahead and go inside of the use my apologies stroke color sidebar here and in here we have to change the same thing so it's no longer gonna be get active fill color instead it's gonna be get active stroke color like this And we're gonna replace this with the default stroke color from the types. And we no longer need the fill color import. There we go.
And let's go ahead and change the onChange method to be changeStrokeColor value. Let's go ahead and try this out now if we've done it correctly. I'm gonna refresh just for good luck. I'm gonna select a random rounded element here and I'm gonna change the color to white so I can only focus on the stroke here. And if I select stroke, there we go.
I can change it to blue and it's blue and it works with selection as well. So if I change this to a circle, the circle is also in that style, but if I go ahead and change this to a red, there we go. Perfect. Amazing, amazing job. So what we're going to build next is the third option here, which is to control the stroke width.
So we can increase the width of this and we're also going to change the type of stroke which can be dashed, for example. Great, great job.