chart1 = {
const width = 1000;
const height = 950;
const margin = { top: 20, right: 50, bottom: 20, left: 20 };
const proposedColor = "#ff006e";
const nonProposedColor = "#8338ec";
const svg = d3
.select(DOM.svg(width + margin, height + margin))
.attr("viewBox", [0, 0, width, height])
.style("font", "10px sans-serif")
.style("text-anchor", "middle");
const x = d3
.scaleBand()
.domain(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"])
.range([0, 280])
.padding(0.3);
const y = d3.scaleBand().domain(d3.range(1, 7)).range([20, 180]).padding(0.3);
const calculateWeek = (day, startDay) => {
return Math.floor((day + startDay - 1) / 7) + 1;
};
const monthGrid = (monthData, monthIndex) => {
const monthGroup = svg
.append("g")
.attr(
"transform",
`translate(${margin.left + (monthIndex % 3) * 300},${
margin.top + Math.floor(monthIndex / 3) * 220
})`
);
monthGroup
.append("text")
.attr("x", 140)
.attr("y", -10)
.attr("dy", "0.35em")
.style("font-weight", "bold")
.style("font-size", "15px")
.style("font-family", "Chivo")
.text(monthData[0].month_name);
const startDay = monthData.find((d) => d.month_day === 1).day_abbr;
const startDayIndex = x.domain().indexOf(startDay);
monthData.forEach((d) => {
d.month_week = calculateWeek(d.month_day, startDayIndex);
});
const dates = monthGroup
.selectAll("g.date")
.data(monthData)
.enter()
.append("g")
.attr("class", "date")
.attr(
"transform",
(d) => `translate(${x(d.day_abbr)},${y(d.month_week)})`
);
// Append circles for holidays with appropriate fill color
const circles = dates
.filter((d) => d.holiday_name !== null && d.holiday_name !== "")
.append("circle")
.attr("r", 12)
.attr("fill", (d) =>
d.is_proposed === "yes" ? proposedColor : nonProposedColor)
.attr("opacity", 0.7);
// Append the date text
dates
.append("text")
.attr("dy", "0.35em")
.style("font-size", "14px")
.style("fill", (d) =>
d.holiday_name !== null && d.holiday_name !== "" ? "white" : "black"
)
.text((d) => d.month_day)
.style("font-family", "Chivo");
// Create a div element for the tooltip
const tooltip = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("max-width", "200px") // Set the max-width of the tooltip
.style("position", "absolute")
.style("color", "white")
.style("padding", "8px")
.style("border-radius", "4px")
.style("visibility", "hidden")
.style("text-align", "left")
.style("font-size", "12px")
.style("font-family", "Chivo")
.style("box-shadow", "0 4px 8px rgba(0, 0, 0, 0.2)");
// Append tooltip for holidays and add stroke on hover
dates
.filter((d) => d.holiday_name !== null && d.holiday_name !== "")
.on("mouseover", function (event, d) {
tooltip
.style("visibility", "visible")
.html(
`<strong style="font-size: 16px;">${d.holiday_name}</strong><br>${d.details}`
)
.style(
"background-color",
d.is_proposed === "yes" ? proposedColor : nonProposedColor
)
.style("opacity", 0.9);
// Add stroke to the hovered circle
d3.select(this)
.select("circle")
.attr("stroke", "black")
.attr("stroke-width", 2);
})
.on("mousemove", function (event) {
tooltip
.style("top", event.pageY + 15 + "px")
.style("left", event.pageX + 15 + "px");
})
.on("mouseout", function () {
tooltip.style("visibility", "hidden");
// Remove stroke from the circle
d3.select(this).select("circle").attr("stroke", "none");
});
// Add day labels only for October, November, and December
if (["October", "November", "December"].includes(monthData[0].month_name)) {
const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
monthGroup
.selectAll("text.day-label")
.data(days)
.enter()
.append("text")
.attr("class", "day-label")
.attr("x", (d, i) => x(d) + x.bandwidth() - 30)
.attr("y", y.range()[1]) // Position below the last row of dates
.attr("dy", "0.35em")
.style("font-size", "13px")
.style("font-family", "Chivo")
.text((d) => d);
}
};
const monthsData = d3.groups(data, (d) => d.month_name);
monthsData.forEach((month, index) => monthGrid(month[1], index));
return svg.node();
}