So in order to create the selection net we have to follow the flow of events happening to make this easier for us to understand. So the selection net will start on mouse point down. So let's confirm that you have that. We have an onPointerDown event here. So in this onPointerDown event what's important is that we set the canvas state with an origin and canvasMode pressing.
And then what we can do is go inside of our OnPointerMove method and in here add a case for what we want to happen if we are just pressing. So if we are not translating and we are not resizing we are just pressing on the canvas. So Let's go ahead and do another main if which is going to replace this below with an else if. So if canvas state.mode is canvas mode.pressing in that case we're going to go ahead and change this to be else if which is important because we want this to be the priority. In that case we're going to go ahead and call startMultiSelection and we're going to pass in the current and the canvas state origin.
So now we have to create the startMultiSelection method. So let's go ahead and find where is the right place to put this. So I want to do it somewhere where we handle layers. So let's do it just above this resize selected layers. Let's write const startMultiSelection to be useCallback, not useMutation.
So just useCallback. And inside of here, let's prepare this with an empty dependency array, we are going to accept current to be a type of point, which we have from types canvas, and origin to be point as well. So we are going to know where it started and where it ends and now let's go ahead and write if math.absolute current.x minus origin.x plus math. Whoops so this is yes it's still inside of the if clause. So another Math.absolute which is going to say if current.y minus origin.y Let me go ahead and collapse this so it's maybe easier for all of us to understand.
So if Math.AbsoluteCurrent.x minus Origin.x plus Math.AbsoluteCurrent.y coordinate minus Origin.y coordinate is less is sorry higher than 5 in that case we're gonna go ahead and write set state set canvas state mode canvas mode.selection net origin and current So why more than 5? What does that mean? Basically, we decide to start the multi-selection only when the difference between the two origin and current points is larger than 5. So this is just a threshold which we've established that is enough for us to say okay what this user is trying to do is initiate a selection net. Because just by clicking on our canvas that doesn't mean necessarily that they are trying to change the canvas state to the selection net.
Only if they hold and drag for a bit so that the difference between these two points is so obvious that we can notice it on our threshold that's when we're going to activate this canvas state right here. So let's see if we can maybe console log that already. So I'm going to add a console log here. Attempting to selection that. It doesn't matter something that you can recognize right?
So let me go ahead here and just clicking does nothing but you can see if I hold and drag and kind of make a space then it triggers that. If I just click nothing happens. If I hold nothing happens. But if I click, hold and move it then that indicates the change, right? So number five is just like a magic, It seems like it's a magic number.
Usually there is a practice when you're working companies, this is called the magic number. And this is not like a good thing to just put in your code, but this is a tutorial, so I don't really have the highest standards right now because I'm trying to explain something here. But you would probably store this in a constant for example selection net threshold or something like that right that would be a better practice to do but I'm gonna leave it like this for now just not to create any more complex code because this is complex enough already. So just confirm that this is working for you. And now what I want to do is go back inside of the onPointerMove method and now we're gonna go ahead and add another else if here before the translate selected layers here.
So let's go ahead and inject our else if here again. Let's open it and let's just prepare it like this. So else if canvasState.mode is equal to canvasMode.selectionNet, then we're gonna go ahead and do updateSelectionNet. And we're gonna pass in the current and we're gonna pass in the canvas state origin. So we don't have the update selection net so let's create it just above our start multi selection.
So update selection net is going to be use mutation because we want this to be broadcasted to everyone who is on the screen. So useMutation. And let's go ahead and let's extract the following. So from the first argument, the storage and setMyPresence. And from the second argument, we're going to have a current, which is a type of point.
And the last argument is the origin which is a type of point. So it's basically going to be a continuation of what was just started with the multi-selection. So in here let's go ahead and get our layers using storage.getLayers.toImmutable and then setCanvasState. Let's write if mode, canvas mode is going to be selection net and then we need to pass in the origin which is going to be our origin and we need to pass in the current so you can do it like this in a shorthand if you want to. And now we have to create a util called Find Intersecting Layers with a Rectangle.
So we're going to use that so that we define which layers did we just select by dragging using our coordinates. So let's go ahead inside of our lib utils. Again, if you don't want to write this method, you can just go inside of my utils folder in GitHub and copy the finished method. So export function findIntersectingLayersWithRectangle. So, export function, find intersecting layers with rectangle.
The first argument is going to be layer IDs, which is going to be a read-only string. The second argument is going to be layers, which is going to be read-only map with string and layer. Layer is going to be from types canvas so make sure you import the layer from types canvas here and then we're going to have a which is a point and b which is another point like this great and let's go ahead and write const rectangle to be x math min between a.x and b.x so the smaller one of the two points same thing for math for the y coordinates so a.y and b.y and the width is going to be the absolute result of a.x minus b.x so the two points from one another and the height is going to be the same thing but the y coordinates so a.y minus b.y basically the difference between the two points on all axes of their coordinates and now let's create an array of ids here and let's iterate over them so for const layer ID of layer IDs we're gonna go ahead and write const layer to be layers.get layer ID if we cannot find that layer and let's use null specifically like this.
Let's write continue and then let's go ahead and extract from the layer XY height and width. Let's go ahead and write if rectangle.x plus rectangle.width is higher than X and if rectangle.x is smaller than y plus height id is dot push layer id and finally in the end return the ids great so we're going to use this method to determine which we have selected. And now let's go back inside of our canvas and let's go ahead and let's get our IDs, so const IDs. Find intersecting layers with rectangle. So make sure you import this from libutils and now in here we're gonna pass in the layer IDs we're gonna pass in the layers, the origin and the current and then we're gonna do set my presence selection is going to be IDs and remember to put the layer IDs inside of the dependency array right here and now we have the update selection net So I think that we can already try and select multiple items.
Let's see if I drag, there we go. You can see how now my selection box is expanding on how I move my cursor. So I'm pressing down and holding but obviously something is missing. So this is working very nicely. You can see that we don't have to write any additional code.
That's it. This already works. We can change color, we can change them all to the same elements. We can remove all of them together. So all of this works, but obviously what's missing is an actual indicator of how far I'm actually reaching, because it's very hard to understand what's going on right now.
We know what's going on here because we just coded it but let's go ahead and create a proper preview of our selection net But that is it for our code. You can see that that works just fine. So now we just have to use those points and add a new component inside of our SVG element right here. So let's go ahead and let's find our SelectionBox component. And just below it, we're gonna add a condition if canvasState.mode is canvasModeSelectionNet and If canvasState.current is not null, only then are we going to render a rectangle element with the following properties.
The class name is going to be fillBlue500.5 stroke is going to be blue500 and stroke is going to be 1. Then we're going to have the x coordinate to be math.min canvas state.origin.x and canvas state.current.x. And let me just see if I made any mistakes here. Canvas state.current is possibly undefined. So let me just confirm.
That shouldn't be an issue because of this canvasState.current. We can use... I use the identical property. So if I just do this, there we go. Now it works.
So don't use the identical property. Use just exclamation point equals like that. And let's just go ahead and copy this and do the same thing for the Y coordinate, but this one is going to use obviously the Y coordinates here. And now let's determine the width to be math.absolute. And let's use canvasState.origin.x minus canvasState.current.x.
And let's go ahead and do height to be very similar. So it's going to be math absolute. Let's pass in the canvas state dot origin dot Y minus canvas state dot current dot Y. So let me zoom out to show you how all of these are supposed to look like in one line and that should be it. Let's try it out.
So now if I just randomly select, there we go. You can see how we have a nice selection net and you can see how it's very visible exactly what it is selecting. So you can see that we can move the selected area, we can go ahead and change their layering, we can change all of the elements colors which are selected, we can also delete all the elements. Great! So we've wrapped up all the functionality for the basic elements.
What we're gonna build next is the other elements and then we're finally gonna go ahead and render our pen. Great, great job.