diff --git a/app/features/img-export/components/SeasonSummaryGraphic.module.css b/app/features/img-export/components/SeasonSummaryGraphic.module.css index 4a1508feb..94de1337a 100644 --- a/app/features/img-export/components/SeasonSummaryGraphic.module.css +++ b/app/features/img-export/components/SeasonSummaryGraphic.module.css @@ -151,17 +151,54 @@ below win regardless of which CSS chunk loads last } .calendar { - display: grid; - grid-auto-flow: column; - grid-template-rows: repeat(7, auto); + --calendar-cell-size: 18px; + --calendar-gap: 4px; + + display: flex; + align-items: flex-end; justify-content: center; - gap: 4px; - margin-top: var(--s-4); + gap: var(--calendar-gap); + margin-top: var(--s-2-5); +} + +.calendarWeekdays { + display: flex; + flex-direction: column; + gap: var(--calendar-gap); + margin-inline-end: var(--s-1-5); +} + +.calendarWeekday { + display: flex; + align-items: center; + justify-content: flex-end; + height: var(--calendar-cell-size); +} + +.calendarMonth { + display: flex; + flex-direction: column; + gap: var(--calendar-gap); +} + +.calendarMonthName { + text-align: center; +} + +.calendarWeeks { + display: flex; + gap: var(--calendar-gap); +} + +.calendarWeek { + display: flex; + flex-direction: column; + gap: var(--calendar-gap); } .calendarCell { - width: 18px; - height: 18px; + width: var(--calendar-cell-size, 18px); + height: var(--calendar-cell-size, 18px); border-radius: 3px; background-color: var(--activity-none); diff --git a/app/features/img-export/components/SeasonSummaryGraphic.tsx b/app/features/img-export/components/SeasonSummaryGraphic.tsx index 86f0deee7..75409b066 100644 --- a/app/features/img-export/components/SeasonSummaryGraphic.tsx +++ b/app/features/img-export/components/SeasonSummaryGraphic.tsx @@ -8,6 +8,7 @@ import { } from "date-fns"; import * as React from "react"; import { useTranslation } from "react-i18next"; +import * as R from "remeda"; import { Avatar } from "~/components/Avatar"; import { Flag } from "~/components/Flag"; import { TierImage, WeaponImage } from "~/components/Image"; @@ -43,6 +44,11 @@ const CHART_MARGIN = { top: 26, right: 14, bottom: 22, left: 14 }; const CHART_POINTS_NEEDED = 2; const CHART_PEAK_LABEL_CLAMP = 48; const TOP_MATES_COUNT = 3; +const CALENDAR_WEEK_LENGTH = 7; +/** Thursday, the day that decides which month a week column belongs to */ +const CALENDAR_WEEK_MONTH_DAY_INDEX = 3; +/** Monday and Friday, the only rows the calendar names */ +const CALENDAR_NAMED_WEEKDAY_INDICES = [0, 4]; /** Without weapons the teammates box is alone next to the activity calendar, so it has room for more */ const TOP_MATES_COUNT_WITHOUT_WEAPONS = 6; @@ -533,36 +539,119 @@ function ActivityCalendar({ seasonDateRange: { starts: Date; ends: Date }; activeDays: Array<{ date: string; activity: SeasonSummaryGraphicActivity }>; }) { + const { formatter } = useDateTimeFormat({ month: "long" }); + const activityByDay = new Map( activeDays.map((day) => [day.date, day.activity]), ); const seasonFirstDay = startOfDay(seasonDateRange.starts); - const days = eachDayOfInterval({ - start: startOfWeek(seasonFirstDay, { weekStartsOn: 1 }), - end: seasonDateRange.ends, + const weeks = seasonWeeks({ + seasonFirstDay, + seasonLastDay: seasonDateRange.ends, }); + const months = calendarMonths(weeks); return (