When I found this calendar module on MPM the first thing that popped into my head looking at that sample output is that it really looks a lot like a calendar you would see in a UI. I thought immediately, "How easy would this be to turn into an actual calendar using D3?" Today we're going to take a look and find out.
If we save this and run it in our browser you can see that we do get outputs similar to what we saw on the module page on MPM's website. That's all well and good, but something in the console isn't exactly what we're looking for. Let's take a look at how we're going to turn this into a UI.
The first thing we'll do is pull in D3, which I've also loaded via MPM. Then we're going to go ahead and add a table tag to our markup up with the id of calendar. That's going to give us something that we can build our calendar inside of.
We can go ahead and get rid of this since we're not worried about printing out to the console anymore. We're actually going to store the results of that call in a variable named weeks. Once we have that we can iterate over weeks again with a forEach method. We will then pass the week into each function.
First let's get a reference to our table up here because we don't need to do that inside of our loop. We're going to say, "D3 select calendar." That's our table. Now down here within our loop we can say, "Table.appendtr." We're going to append a row for each week.
Then once we do that we can use the select all method which, remember, is going to act on the row before it has any TDs in it. We're then going to assign the data and then use the enter method of D3. That's going to match any pieces of data that do not yet have a piece of UI.
In that case we're going to append a new TD. For now we're just going to stick some text into that table cell. We'll do text and then pass the function. The id that's getting passed in is the day of the week. If we now spit that out you can see, right here in our UI, we now have that same basic structure actually on our page.
You'll notice that we have zeros where days actually fall outside of the current month that we're displaying. We'll update our function here to only return the day if that number is greater than zero. If it's not then we'll just return a blank string here.
Now that we have a basic structure in place for our UI, let's go ahead and add some styles to make it look a little bit more like a real calendar. If we go over here to our CSS file I've got some styles here that I created beforehand, the first one just being the calendar itself, giving it a size and telling it to collapse those borders between cells, telling it to give all the TDs a solid border and align their text to the right and the top.
Then this last style here for table cells with a class of empty for border of none, we'll take a look at that here in just a second. If we save that we can see that our UI does, in fact, update to show that. I've got LiveReload running here.
We now have what pretty much looks like a real calendar, but we've got these empty blocks which you may or may not want. I've decided to take them out. That's where that empty class is going to come into play.vIf we add an attribute call here and use this to set the class, we can then use the same sort of test of "is D greater than zero". If it's not then we will apply the empty class. Now our calendar does not have those empty cells.
We've now built a pretty decent, if basic, calendar in about four and a half minutes. Let's take it a little bit further and go ahead and add a proper calendar header to it. We'll show the month and the days of the week.
The first thing that we're going to do to accomplish that is we're going to split our table into a head and a body. We're going to update our code here so that the existing rows get attached to the body. We'll use the head here in just a second.
The first thing I'm going to do before that, though, is bring in this consts file which just has a list of month names and day names. Then we're going to pull out our month and year properties that we're passing to the month days call so that we can use those elsewhere.
Now that we have all the variables and data set up, we can go back and actually create our header rows. In this first instance here you see we have the header. We're going to append a row. We're going to append a single TD. We're going to give that TD a col span of seven. Then we're going to tell it to align the text in the center and just put that month in that cell there.
You can see we now have December right up at the top of our calendar there just as you would expect. We don't really probably want this border around it. If we update this style to make it only apply to TDs within the body, then that will fix that and get rid of that border there that we don't really want.
The next thing we're going to do is add another row to our header. This time it will be for the day names. We're going to get rid of our col span there. We'll keep the center line text, but in this case we're actually going to use some data to create these. We're going to go back and, once again, use our select all call. Tell it to select all of the TDs, which, again, are not going to exist. We're going to pass in the names of the days of the week as our dataset and then, once again, use the enter call so that we're getting the new items.
In that case we will append a TD. We will now update this so that the text is actually the day of the week that corresponds. If we just return that D, we've got it there. You can now see the days of the week as our column headers. You'll notice that the Wednesday column is a little wide, so we can just go in and tell these TDs to have a width of 14 percent so that they're evenly distributed. There you go. That's a pretty basic calendar in about seven minutes.