‫Our last use case
‫of the compound component pattern
‫will be to build a reusable context menu,
‫which will make our application really feel
‫like an actual application
‫that you use every day on the web.
‫And this will be a long one,
‫but let's still do it all in one go.
‫So what we want to do here is to give each
‫of these cabin rows a button here that we can click.
‫And then when we click that button,
‫a small context menu appears,
‫which ultimately will contain these three actions here,
‫so duplicating, editing, and deleting.
‫Now what's special about these menus is that only one
‫of them can be open at the same time,
‫which means that we need to wrap the entire table here
‫into that menus component that we are going to build.
‫And then inside each of the rows,
‫we will have a menu child component.
‫All right.
‫And so let's actually write that in our code.
‫So we already have, again, the table component right here
‫or actually not table, but menus.
‫So that's what we're building this time.
‫And so let me immediately
‫create the function component here.
‫And then for now, I will just return the children.
‫All right, and so as I was saying,
‫we actually need to wrap everything here,
‫so all the rows inside those menus
‫so that those menus can then keep track
‫which of the four menus,
‫so four right now because we have four rows.
‫And so again, that menu needs to keep track
‫which of the four menus are actually opened at the time.
‫So let's come to our CabinTable,
‫and as I said, we will then wrap all of this
‫into that menus component.
‫Let's place that here at the end, and nice.
‫So for now, nothing happens here, of course,
‫but now inside the table body inside of each cabin row,
‫we will have a Menus.Menu.
‫So let's come here, and for now,
‫we will place this here after all of this stuff.
‫So after this Modal, we will have,
‫as I just said, one Menus.Menu.
‫And we need to import this menus component here, of course,
‫as I just did initially.
‫So, very important that we have this one here.
‫And so now let's again build out the API
‫of this component that we want to implement.
‫And this one is going to be a very complex one,
‫so this lecture here will be the hardest one
‫of this section.
‫And so if it's too much,
‫then that's absolutely no problem at all.
‫This is really, really advanced stuff already.
‫But the reason for that is
‫that we are already approaching the end of the course,
‫and many students have asked me
‫for advanced content like this.
‫And so I'm basically giving it to you.
‫And also you might be noticing that at this point,
‫it is not so much about React itself anymore,
‫but really more about application development itself.
‫And things are also getting a bit more opinionated,
‫so many of these things are basically my way
‫of doing things and with my opinion in them,
‫but, of course, I tried to make it as general
‫and as broad as possible
‫so that all of you can learn a lot from this
‫and use it in your own applications in the future.
‫All right, but with all that being said,
‫let's now see what we need inside a menu.
‫So, a menu, as you have seen many times
‫on the web already, is always composed of a button.
‫And so then when you click on that toggle button,
‫a list will open,
‫and then that list itself is composed of many buttons.
‫So let's do that.
‫So we will have a Menus.Toggle,
‫which we can use to open and close this menu right here.
‫And then we will have a Menu.List,
‫which will then contain a bunch of buttons.
‫So, Menu.Button.
‫So basically each of them will be one list element
‫of the list.
‫And so here, let's just write something for now.
‫So let's say Duplicate, then Edit,
‫and then Delete.
‫Now, just like before with the modal,
‫we will have many of these menus on the page
‫and therefore many toggles and many lists.
‫And so therefore, we will have to connect this toggle
‫with this list again
‫so that we know that this exact toggle
‫should then open up this list.
‫So previously we used the name and the opens prop,
‫but here let's make it a bit more simple
‫and simply use an ID.
‫So for this, we will just use the current cabinId,
‫so both on the toggle and on the list.
‫So, cabinId.
‫And then the parent menus component will keep track
‫which of the menu ID is currently being displayed.
‫All right.
‫And for now, I think that's it.
‫So later we will pass in some more props,
‫but for now, we can work with this.
‫So we have a menu, a toggle, a list, and a button.
‫And so let's come to our menus here and implement those.
‫So the menu we actually don't really need
‫because all that is is some styling,
‫so this one right here.
‫So actually let's change this just to Menu.
‫All right.
‫And so here let's then implement the toggle,
‫which remember has an ID.
‫Then we have the list itself,
‫which also needs an ID.
‫And finally we have the button.
‫So for now, no props, but yeah,
‫we are going to need the children for sure.
‫And so now in the end, let's do Menus.Menu is = Menu,
‫which, as I mentioned earlier,
‫is simply that styled component.
‫Then we have the toggle.
‫Then we have the list
‫and finally also the button.
‫All right.
‫And then next up, as always,
‫let's create our MenusContext.
‫And then here, as we already mentioned earlier,
‫we will need to keep track
‫which one is the currently open ID.
‫So, of course, we use some state for that.
‫And then here, let's, as always,
‫return MenusContext.Provider.
‫And then in here, we provide the openId
‫and later also some setter functions.
‫And let's actually immediately
‫create those setter functions.
‫So here by default, we start off with an empty string.
‫And so that basically means
‫that none of the menus is currently open.
‫And so basically that means that the close function is
‫to set the OpenId
‫to that empty string.
‫On the other hand,
‫the open function is simply exactly the same as setOpenId,
‫so just like we did earlier also in the modal component.
‫So let's pass these down in here as well.
‫And then let's build out our other three components.
‫And let's begin with the easiest one here.
‫So for now, here we will simply return a list item
‫because this will be inside the list, remember?
‫And so this will be an unordered list.
‫So here to be semantically correct, we need a list item,
‫and then in there, we will have a styled button.
‫And so for now, all we're gonna do
‫is to pass in the children.
‫So this is the easy one.
‫Let's now tackle the two complicated ones.
‫So here we want to return the StyledToggle,
‫and please make sure to always check out these styles here
‫'cause otherwise this is all going
‫to feel even more like black magic.
‫And the same, of course, is true
‫for the other components that we built previously.
‫So we cannot build all the styles here in the videos
‫because then this project will be like 50 hours long,
‫and that we can't have.
‫But anyway, let's here create a function called handleClick,
‫which we will call,
‫and actually let's only specify it here like this for now.
‫And then here we will want an icon.
‫So this one is called Hi, then EllipsisVertical.
‫So it's like these three dots
‫that I'm sure you have seen before.
‫Here then let's create that handleClick function.
‫And so here is where we will then decide
‫whether we want to open or to close the menu,
‫or actually the list.
‫So let's grab those two or actually all the values.
‫So we need both the ID
‫and the close and the open functions
‫in order to do this decision.
‫And so we get them from useContext
‫on the MenuContext.
‫All right, and so here, let's now do the check.
‫So if the openId is empty,
‫meaning that there is no ID,
‫or if the openId,
‫so basically if the currently open menu is different
‫from the ID of this exact button that is being clicked,
‫then let's open the menu.
‫And we do this by passing in the ID of exactly this button
‫because this button is connected to that menu by this ID.
‫And otherwise we just close the menu.
‫So again, if right now none of the menus is open,
‫which is what we have right here, so then open this one,
‫so the one with the ID that has been clicked.
‫Or also if there is currently an open menu already,
‫but it's different than the one that's being clicked,
‫then we also want to open this one,
‫so the one that has been clicked.
‫But in all other cases, then we just want to close it.
‫So that other case is basically whenever the openId
‫is equal to this id,
‫or in other words, whenever the menu is already open.
‫So if we then click again, we close that,
‫and this will all make sense
‫once we see this here actually working.
‫But for that, we need to also finish this list component.
‫So I believe that this one
‫for now will just work bare bones,
‫and so let's move on to the last one.
‫So here from the context, we will need the openId,
‫so MenuContext, because now we need to compare
‫that openId with the ID of this list,
‫so basically just like before with the modal window
‫where we compared the window name
‫with the currently opened name.
‫And so here we now have to do exactly the same thing.
‫So basically if the currently openId is different
‫from this list ID, then return nothing.
‫But if the ID of the list matches the one
‫that is currently open, then we want to render something.
‫So then we do render this list of buttons.
‫And so here, let's actually again return a portal.
‫So create portal because this element here will also float
‫on top of the UI.
‫And so in cases like that,
‫it's always a good idea to use a portal.
‫So here we will have our JSX,
‫which is the StyledList for now,
‫and then in here, we will pass the children,
‫which are going to be all of these buttons,
‫if you remember, from here.
‫So we have the list, and that receives these buttons.
‫So those are their children.
‫Just like this.
‫And then the second argument of the portal,
‫let's again do document.body.
‫Okay, but now let's actually go check out this StyledList
‫because there is something very particular about it.
‫So this StyledList here is with the position of fixed,
‫so just like the modal window before.
‫And what is special here is the fact that the right
‫and the top CSS properties come from the position prop.
‫So basically here we need to pass in the X
‫and the Y coordinates of this menu
‫'cause we will want it to be displayed exactly
‫below the button that will appear there.
‫So we will have to later calculate that position.
‫But for now, while we are still experimenting around
‫with this, let's just do some fictional position.
‫So here, this expects an object with X.
‫Let's set it to 20
‫and Y also to 20.
‫All right.
‫Now here, I'm not sure why we're getting this error,
‫but...
‫Ah, well, it is actually Menus,
‫not just Menu.
‫And we have an error, but that's no problem.
‫I'm sure we can fix it.
‫So that's a lot of errors, 77.
‫Let's go all the way to the top.
‫So here it says we maybe forgot to export some component.
‫Let's see what we have next.
‫And okay, let's see if that's the case.
‫Ah, here it's Menus.
‫So maybe that's the only problem, but I don't believe that.
‫Or, well, actually it looks like it.
‫So here we have a nice-looking button,
‫and so let's check what happens when we click it.
‫Well, beautiful, there is our list.
‫Let's click it again, and then it disappears.
‫Great, so something is already working here.
‫Let's click here, and it shows up again.
‫Click here, and it closes.
‫And also if we click this one and then another one,
‫well, now we can't really see
‫because it appears in the same place,
‫but trust me that it did close and then open another one.
‫Great, so we have some bare-bones functionality
‫here already working.
‫And so next up, let's make this a little bit nicer,
‫so add a few more features there
‫and starting with the actual positioning of the list.
‫Now this position of the list needs to be calculated
‫as soon as this button here is clicked.
‫And so let's do that right here in handleClick.
‫So first off, here let's receive the event
‫that has been fired off, so that click event.
‫And so then on that, we can do e.target,
‫and then we want to get the closest button,
‫so just to make sure that we really get the position
‫of the button and not of the SVG icon here.
‫So this then basically does some DOM traversing,
‫finding the closest button parent.
‫And then here we can then call another DOM function,
‫which is getBoundingClientRect.
‫It's a weird name,
‫but basically what this will do is to give us some data
‫about the element's position.
‫Let's call it rect for rectangle,
‫and then let's start by just logging this to the console.
‫So each time we click here, then we get an error.
‫So it's getBounding.
‫Let's try that again, and nice.
‫So here we get now the X and Y positions.
‫And so based on that,
‫we can now determine where we want to render the list.
‫So let's get that data,
‫but where should we actually store it?
‫Because we will determine the data here in this component,
‫but we do actually need it here.
‫So in order to pass it from one place to another,
‫we will store it in the parents state.
‫So let's create position
‫and setPosition,
‫useState again,
‫and let's start with null.
‫And then we also pass these two into our context,
‫so position and setPosition.
‫Then here we grab that setPosition,
‫and then here,
‫let's do setPosition.
‫And as before, we are expecting an object with X and Y.
‫Now here I already figured out how to calculate this,
‫so it's going to be the window's width, basically,
‫minus the width of the rectangle itself
‫and minus the X coordinate of the rectangle.
‫So I cannot really explain why that is,
‫but if you think about this,
‫then you will actually figure out the exact same thing.
‫And now for the Y position,
‫all we need to do is to take the rectangle's Y position.
‫So basically that's up here.
‫Then we will add the height of the button,
‫which will leave us up here.
‫And then we just add some pixels for margin.
‫So, rect.height,
‫and then plus eight pixels.
‫All right.
‫And again, please try to understand that positioning there.
‫But anyway, here we now grab that position
‫and pass that in here.
‫And so now let's see.
‫Yeah, great.
‫So, the same now here and here and here.
‫Beautiful.
‫There's just one problem with this,
‫which we are actually not going to fix
‫because it takes too much time,
‫which is if we scroll now,
‫then the menu will stay at the same position on our screen
‫because it has the fixed position.
‫So we could, of course, fix this,
‫but that's just a bit too hard.
‫So I'll just leave it like this,
‫click here to close it.
‫And so this is really good enough
‫for what we are building here
‫or what we are actually just learning here.
‫Okay, next up, let's make these buttons here a bit nicer.
‫And for that, we will want to pass in an icon.
‫So we want to allow an icon,
‫and so let's use the same ones that we used here earlier,
‫so this one for duplicating.
‫And then let's do the same thing in the other ones.
‫So here it's HiPencil,
‫and here it is HiTrash.
‫Now of these three buttons right there,
‫only one of them has an onClick handler, I believe.
‫Yeah, just this one right here.
‫And so actually we want our buttons right there
‫to also be able to receive this onClick.
‫So it's not mandatory, but they can receive it.
‫And so let's grab this here as well
‫and then place it right here.
‫So now it's getting a bit hard to find.
‫Let's add some spacing here maybe.
‫So that's better.
‫So let's now handle that icon and onClick prop on here.
‫So, icon and onClick.
‫So first off, let's place that icon here
‫and then placing this children here into a span.
‫All right, then here, let's use onClick
‫and then a function called handleClick.
‫So let's define that here again
‫because we actually need to do multiple things.
‫So, first of all, we want to conditionally call onClick,
‫and for that, again we use optional chaining.
‫And then afterwards, we also want to close our menu.
‫So let's call close,
‫and then, of course, we need to get it from the context,
‫useContext like this with the menus context.
‫And there we go.
‫Let's see.
‫And beautiful.
‫So this is starting to really look like something,
‫and actually this button should already be working.
‫So let's click that,
‫and indeed, our copy of 001 has been created.
‫Great.
‫Now these two here don't work yet,
‫but if we click them, then the menu gets closed already.
‫So that part of closing already works.
‫Now what we also want to do
‫is that when we click outside here,
‫just like with the modal, we want the menu to close.
‫So right now, of course, that doesn't happen,
‫but luckily for us,
‫we already have that useOutsideClick hook.
‫So we can use that here
‫because this is where we want to register that click.
‫So if we click outside of this list,
‫then we want it to close.
‫So this is going to return a ref,
‫so, useOutsideClick.
‫And then the handler here
‫in this case will again be our close function.
‫So let's grab that.
‫And then we just need
‫to attach this ref here to this element.
‫So let's try that.
‫And it seems to be working.
‫So we click outside.
‫It disappears.
‫And as we click here, then that also disappears.
‫Let's just try to see what happens if maybe we pass
‫in false here for that second argument,
‫so that listenCapturing.
‫And so, well, now that doesn't work at all.
‫And so we again need this here to be true basically.
‫All right, and so now this works really, really great,
‫so I'm really happy with the result
‫and with the way this looks like.
‫Now all we need to do
‫is to actually place these actions here in there,
‫so making the edit and the delete work
‫'cause the menu itself is, I think, finished.
‫So, it's a big one, and it was a lot of work, as I said,
‫but the result is really satisfying, I think.
‫But anyway, now we will need to change this a lot.
‫So if you want to keep the old version,
‫I will just duplicate this here,
‫again, calling it version one.
‫And in this one, we can even completely get rid
‫of what we did here.
‫So we should have taken that copy earlier,
‫but we can just do it now as well.
‫Now, okay.
‫And so now comes the time
‫where things will get really, really confusing.
‫So the first button here, we actually no longer need,
‫so that one is already working.
‫But now as for these other two,
‫this is where it gets complicated.
‫So we already have some buttons right here,
‫but these buttons are not that pretty.
‫So they are just these buttons.
‫And so we'll actually want these buttons
‫to be these ones right here.
‫So these menu buttons should be inside this Modal.Open.
‫So here we will now basically mix the two together.
‫So again, that will be quite confusing, but let's do it.
‫So we should actually grab all of this stuff here
‫or maybe even everything, so even where we open the menu.
‫So let's grab that and place it
‫still again inside the modal.
‫So the modal should be outside.
‫Yeah, so right now it's not.
‫That's why we get that error, but there we go.
‫All right, and so now we can do what we just said earlier.
‫So we can grab these buttons right here
‫and place them into Modal.Open.
‫So this one we actually don't need, but this one we need.
‫So this one is for edit,
‫so let's grab this and place it right here,
‫so just like this.
‫And then we take the entire thing
‫and put it back into our menus list.
‫All right, and then we do the same thing with the other one.
‫So let's grab this Modal.Open,
‫place that here,
‫and then we replace that button in there
‫with the button of our menu,
‫just like this.
‫And so now these two windows here are a bit separated,
‫but that's no problem at all.
‫It's going to work just exactly the same way as before.
‫So, let's just recap.
‫So we have, of course, our Menus.Menu,
‫which is now inside the modal.
‫Then we have to toggle.
‫Then we have the list.
‫And then for two of them, so for two of the buttons,
‫we have them inside Modal.Open
‫because these two should actually trigger
‫our modal windows to open.
‫All right, that's no problem at all.
‫And then we have these two windows here outside
‫of the menu list,
‫and I believe that this is actually all.
‫This should work perfectly fine.
‫So let's try to edit,
‫and beautiful.
‫So this is so great.
‫Like, it makes me really happy
‫to see this working so nicely,
‫the same thing for deleting.
‫Let's close this one.
‫If I try to edit this one, then we will get the 008 here.
‫Yeah, so everything again working perfectly fine.
‫We can click outside here.
‫We can open a menu, also click outside.
‫And so this is really just a great experience
‫and a great design as well.
‫So, congratulations for reaching the end
‫of this pretty advanced section right here.
‫So this was really fun for me,
‫and I hope that you feel the same.
‫So study all that we did here,
‫and then we can move on together
‫and build out the rest of the application
‫right in the next section.