See the Pen Haml Calendar by Katy DeCorah (@katydecorah) on CodePen
While I was looking through the Haml docs today, I found the ability to print out the date. My gears started spinning. Why not create a calendar?
It took a lot of research, but I made a functioning Haml Calendar. I say functioning, because it will display the correct date and tomorrow it will automatically display tomorrow’s date. You may also change the month, year, and day variables to display a specific date.
I realize this is not a practical calendar solution, but it was fun to figure out.
I used these shots from Dribbble for layout and color inspiration.
The first step: print out today’s date. Once I got that, I created variables for month, year, and day that default to today’s date unless otherwise specified. I also created an array
@days to hold the days of the week in the order I prefer.
I looped through
@days to create the headers for the calendar, but I decided to only display the first letter of each day. I used a list because I find that they are easier to style and they help keep my code organized.
- @days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] %ul.weekdays - @days.each do |i| %li.weekday = i
Next I created a new list for the ordinal days of the calendar using a new loop:
%ul.week - (1...32).each do |i| %li.day = i
I started by printing 31 days (with the start of the month at Sunday for now). I styled the list items so that only 7 can fit in each row. The list items naturally fell into calendar order.
At this point I had a calendar, but it wasn’t correct. August begins on Thursday, not Sunday. I found that if I adjusted the starting value of my loop, I could get the first of the month to start on Thursday
- (-3...32).each do |i|. That works, except now Sunday reads as -3 and so forth. I added a few constraints:
%ul.week - (-3...32).each do |i| %li.day - if i > 0 = i
The items that are
i <= 0 still exist, but they don’t print a number. Instead they create blank space allowing August to begin on Thursday.
The calendar works, but it’s not dynamic.
Making it Dynamic
I wrote a new variable called
monthStart. Using the date function along with my variables for month and year, I could get the day each month starts on. I wrote another variable to find the position of the first of the month in my
@days array. So in
@days Thursday’s position is 4, but my loop needs to start at -3 for the month to start on that day. I wrote down every day, its position in the array, and what number the loop needs to start on for the month to start on that day. From these findings, I created a formula. I take the negative of the hash and add 1.
- monthStart = Date.new(year, month, 1).strftime("%a") - @days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] - hashDays = Hash[@days.map.with_index.to_a] - monthStartNum = hashDays[monthStart] - adjustMonthStartNum = -monthStartNum + 1
So I updated my loop:
%ul.week - (adjustMonthStartNum...32).each do |i| %li.day - if i > 0 = i
To make sure my loop worked, I changed my
month variable to 9 to display September. It worked!
But, September only has 30 days and I’m still printing out 31.
I wrote another variable called
monthEnd. This new variable was slightly easier than
monthStart as I automatically receive an integer.
- monthEnd = Integer(Date.new(year, month, -1).strftime("%d"))
I updated my loop once more (Here I add 1 to
monthEnd to make sure it gets the last day and not up until the last day of the month):
%ul.week - (adjustMonthStartNum...monthEnd + 1).each do |i| %li.day - if i > 0 = i
I added a few more constraints when printing out the days to highlight today and to mute the days before today. I think I can write this more efficiently, but I’m ok with my progress so far.
If you edit in CodePen, try writing in a custom date. I commented out the space for you to give it a try.
I really enjoyed figuring this out. I’m new to Ruby, but I think I will do more experimenting!
p.s. I’m not sure about editing the timezone, so it might be a day ahead.
Did you enjoy this post? Support The Human Utility. The Human Utility provides assistance to families and makes sure they always have running water at home.