@pearpages/heatmap 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -300,6 +300,12 @@ weeks Monday to Sunday, so a translated heatmap that still starts on Sunday read
300
300
  wrong. It defaults to `0` (Sunday, the GitHub convention), and the component picks the
301
301
  order of its day labels up from the data.
302
302
 
303
+ ## What's new in 0.4.1
304
+
305
+ **No empty table header.** The corner cell above the day-name column was an empty `<th>`,
306
+ which axe reports (`empty-table-header`) and a screen reader announces as a blank header.
307
+ It is now a `<td>`. Nothing renders differently.
308
+
303
309
  ## What's new in 0.4.0
304
310
 
305
311
  **Every custom property now carries the component's prefix, `--contribution-heatmap-`.**
package/dist/index.js CHANGED
@@ -186,7 +186,7 @@ function ContributionHeatmap({
186
186
  children: [
187
187
  /* @__PURE__ */ jsx("div", { className: "contribution-heatmap__scroll", children: /* @__PURE__ */ jsx("table", { className: "contribution-heatmap__table", children: isReverse ? /* @__PURE__ */ jsxs(Fragment, { children: [
188
188
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
189
- /* @__PURE__ */ jsx("th", { className: "contribution-heatmap__month-header" }),
189
+ /* @__PURE__ */ jsx("td", { className: "contribution-heatmap__month-header" }),
190
190
  dayNames2.map((dayName, dayIndex) => /* @__PURE__ */ jsx("th", { className: "contribution-heatmap__day-header", children: dayName }, dayIndex))
191
191
  ] }) }),
192
192
  /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => {
@@ -216,7 +216,7 @@ function ContributionHeatmap({
216
216
  }) })
217
217
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
218
218
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
219
- /* @__PURE__ */ jsx("th", { className: "contribution-heatmap__day-header" }),
219
+ /* @__PURE__ */ jsx("td", { className: "contribution-heatmap__day-header" }),
220
220
  months.map((month, index) => /* @__PURE__ */ jsx(
221
221
  "th",
222
222
  {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/models.tsx","../src/shared/formatTooltip.tsx","../src/ContributionHeatmap/getMonthsForHeader.ts","../src/shared/intl.ts","../src/shared/Legend.tsx","../src/ContributionHeatmap/index.tsx","../src/shared/groupByWeeks.ts","../src/mocks/createRealisticMockedCount.ts","../src/mocks/index.tsx"],"names":["monthNames","jsx","dayNames","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["type Theme = '' | 'ocean' | 'sunset' | 'purple';\n\ninterface ContributionData {\n date: string;\n count: number;\n level: 0 | 1 | 2 | 3 | 4;\n}\n\ntype Week = [\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n];\n\ntype Period = { start: Date; end: Date };\n\n// The strings Intl cannot derive from a locale. Day and month names are not here:\n// those come from the locale itself, via src/shared/intl.ts.\ninterface HeatmapLabels {\n less?: string;\n more?: string;\n level?: (level: number) => string;\n noContributions?: string;\n contributions?: (count: number) => string;\n}\n\nconst defaultLabels: Required<HeatmapLabels> = {\n less: 'Less',\n more: 'More',\n level: (level) => `Level ${level}`,\n noContributions: 'No contributions',\n contributions: (count) => `${count} contribution${count !== 1 ? 's' : ''}`,\n};\n\nconst DEFAULT_LOCALE = 'en-US';\n\nconst monthNames = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n] as const;\nconst dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as const;\n\n// Local midnight, `months` whole months away, with the day-of-month clamped to the\n// target month's length: Date would otherwise roll Feb 31 forward into March.\nfunction shiftMonthsClamped(date: Date, months: number): Date {\n const year = date.getFullYear();\n const month = date.getMonth() + months;\n const daysInTarget = new Date(year, month + 1, 0).getDate();\n return new Date(year, month, Math.min(date.getDate(), daysInTarget));\n}\n\nconst startOfToday = (): Date => {\n const now = new Date();\n return new Date(now.getFullYear(), now.getMonth(), now.getDate());\n};\n\n// Both helpers span back to the day after the same date one unit ago, so today is\n// the last day and the window is exactly one year / one month long.\nfunction getLastYearPeriod(): Period {\n const end = startOfToday();\n const start = shiftMonthsClamped(end, -12);\n start.setDate(start.getDate() + 1);\n return { start, end };\n}\n\nfunction getLastMonthPeriod(): Period {\n const end = startOfToday();\n const start = shiftMonthsClamped(end, -1);\n start.setDate(start.getDate() + 1);\n return { start, end };\n}\n\n// The library reads every Date as a calendar day in the local time zone, and these two\n// are the only conversions between Date and the YYYY-MM-DD strings the data carries.\n// Neither goes through toISOString() or `new Date(string)`: both of those are UTC, and\n// mixing them with local Dates shifts the first and last day of a period by one\n// anywhere outside Greenwich.\nconst pad = (n: number): string => String(n).padStart(2, '0');\n\nconst createDateString = (date: Date): string =>\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n\nconst parseDateString = (dateString: string): Date => {\n const [year, month, day] = dateString.split('-').map(Number);\n return new Date(year, month - 1, day);\n};\n\nexport type { ContributionData, Week, Period, Theme, HeatmapLabels };\nexport {\n monthNames,\n dayNames,\n defaultLabels,\n DEFAULT_LOCALE,\n createDateString,\n parseDateString,\n getLastYearPeriod,\n getLastMonthPeriod,\n};\n","import {\n createDateString,\n parseDateString,\n defaultLabels,\n DEFAULT_LOCALE,\n type ContributionData,\n type HeatmapLabels,\n type Period,\n} from '@/shared/models';\n\n// Compares whole days, in the same YYYY-MM-DD form the contributions carry.\n// Period boundaries are Date objects that keep a time of day, so comparing them\n// directly against a date-only value put the first and last day of the period\n// out of range. ISO date strings also sort correctly, so > and < still work.\nfunction isInRange(date: string, { start, end }: Period): boolean {\n return date >= createDateString(start) && date <= createDateString(end);\n}\n\ninterface FormatTooltipOptions {\n locale?: string;\n labels?: HeatmapLabels;\n}\n\nconst formatTooltip = (\n contribution: ContributionData,\n { start, end }: Period,\n { locale = DEFAULT_LOCALE, labels }: FormatTooltipOptions = {},\n): string => {\n const text = { ...defaultLabels, ...labels };\n // parseDateString gives local midnight, so formatting in the local zone names the\n // right day. `new Date(string)` would parse as UTC midnight, which is the previous\n // day anywhere west of Greenwich.\n const formattedDate = parseDateString(contribution.date).toLocaleDateString(locale, {\n weekday: 'short',\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n\n if (!isInRange(contribution.date, { start, end }) || contribution.count === 0) {\n return `${formattedDate}: ${text.noContributions}`;\n }\n\n return `${formattedDate}: ${text.contributions(contribution.count)}`;\n};\n\nexport { formatTooltip, isInRange };\nexport type { FormatTooltipOptions };\n","import { isInRange } from '@/shared/formatTooltip';\nimport {\n monthNames as defaultMonthNames,\n parseDateString,\n type Period,\n type Week,\n} from '@/shared/models';\n\nfunction getMonthsForHeader({\n weeks,\n period,\n monthNames = defaultMonthNames,\n}: {\n weeks: Week[];\n period: Period;\n // Locale-derived; falls back to the exported English constants.\n monthNames?: readonly string[];\n}): {\n name: string;\n span: number;\n start: number;\n}[] {\n const months: { name: string; span: number; start: number }[] = [];\n let currentMonth = -1;\n let currentSpan = 0;\n\n weeks.forEach((week, weekIndex) => {\n if (week.length > 0) {\n // Find the first day in the week that falls within our actual data range\n let monthToUse = -1;\n\n for (const day of week) {\n if (isInRange(day.date, period)) {\n monthToUse = parseDateString(day.date).getMonth();\n break;\n }\n }\n\n // If no day in the week is in our actual range, use the middle of the week\n // (index 3 - which weekday that is depends on weekStartsOn)\n if (monthToUse === -1) {\n const middleDay = week[3];\n monthToUse = parseDateString(middleDay.date).getMonth();\n }\n\n if (monthToUse !== currentMonth) {\n if (currentMonth !== -1) {\n months.push({ name: monthNames[currentMonth], span: currentSpan, start: weekIndex - currentSpan });\n }\n currentMonth = monthToUse;\n currentSpan = 1;\n } else {\n currentSpan++;\n }\n }\n });\n\n if (currentMonth !== -1) {\n months.push({ name: monthNames[currentMonth], span: currentSpan, start: weeks.length - currentSpan });\n }\n\n return months;\n}\n\nexport { getMonthsForHeader };\n","// Day and month names derived from a locale, so the component ships no translations.\n//\n// timeZone is pinned to UTC throughout: the reference dates below are built with\n// Date.UTC, and formatting them in a local zone would shift them across a day boundary\n// in negative offsets and hand back the wrong name.\n\n// 7 Jan 2024 is a Sunday, so this week runs Sunday -> Saturday.\nconst REFERENCE_WEEK_START = Date.UTC(2024, 0, 7);\nconst DAYS_IN_WEEK = 7;\n\nfunction getDayNames(locale: string, weekStartsOn: number = 0): string[] {\n const format = new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n timeZone: 'UTC',\n });\n\n return Array.from({ length: DAYS_IN_WEEK }, (_, index) => {\n const dayOfWeek = (weekStartsOn + index) % DAYS_IN_WEEK;\n return format.format(new Date(REFERENCE_WEEK_START + dayOfWeek * 86400000));\n });\n}\n\nfunction getMonthNames(locale: string): string[] {\n const format = new Intl.DateTimeFormat(locale, {\n month: 'short',\n timeZone: 'UTC',\n });\n\n // The 15th avoids any month-length edge case.\n return Array.from({ length: 12 }, (_, month) =>\n format.format(new Date(Date.UTC(2024, month, 15))),\n );\n}\n\nexport { getDayNames, getMonthNames };\n","import { defaultLabels, type HeatmapLabels } from '@/shared/models';\n\nfunction Legend({ labels }: { labels?: HeatmapLabels }): React.ReactNode {\n const text = { ...defaultLabels, ...labels };\n\n return (\n <div className=\"contribution-heatmap__legend\">\n <span className=\"contribution-heatmap__legend-text\">{text.less}</span>\n {[0, 1, 2, 3, 4].map((level) => (\n <div\n key={level}\n className={`contribution-heatmap__legend-item contribution-heatmap__legend-item--level-${level}`}\n title={text.level(level)}\n ></div>\n ))}\n <span className=\"contribution-heatmap__legend-text\">{text.more}</span>\n </div>\n );\n}\n\nexport { Legend };\n","import './index.scss';\nimport type {\n ContributionData,\n HeatmapLabels,\n Period,\n Week as WeekType,\n} from '../shared/models';\nimport { getMonthsForHeader } from './getMonthsForHeader';\nimport { formatTooltip, isInRange } from '@/shared/formatTooltip';\nimport { DEFAULT_LOCALE, parseDateString } from '@/shared/models';\nimport { getDayNames, getMonthNames } from '@/shared/intl';\nimport { Legend } from '@/shared/Legend';\n\n\ninterface DayCellProps {\n contribution: ContributionData;\n period: Period;\n locale: string;\n labels?: HeatmapLabels;\n // Position in the grid, used to stagger the load animation.\n delayIndex: number;\n}\n\n// groupByWeeks pads every week out to Sun-Sat, so the first and last week carry days\n// that fall outside the period. They keep their slot - the columns depend on seven\n// cells per row - but render no square and stay out of the a11y tree.\nfunction DayCell({ contribution, period, locale, labels, delayIndex }: DayCellProps) {\n const inRange = isInRange(contribution.date, period);\n const tooltip = formatTooltip(contribution, period, { locale, labels });\n\n return (\n <td\n className={[\n 'contribution-heatmap__day',\n `contribution-heatmap__day--level-${contribution.level}`,\n inRange ? '' : 'contribution-heatmap__day--outside',\n ]\n .filter(Boolean)\n .join(' ')}\n title={inRange ? tooltip : undefined}\n aria-label={inRange ? tooltip : undefined}\n role={inRange ? 'button' : undefined}\n tabIndex={inRange ? 0 : undefined}\n data-count={contribution.count}\n data-date={contribution.date}\n style={{ '--animation-delay': `${delayIndex * 0.005}s` } as React.CSSProperties}\n ></td>\n );\n}\n\ninterface ContributionHeatmapProps {\n data: { contribution: ContributionData[]; period: Period; weeks: WeekType[] };\n className?: string;\n isReverse?: boolean;\n // Drives day names, month names and the tooltip date through Intl.\n locale?: string;\n // The handful of strings Intl cannot derive.\n labels?: HeatmapLabels;\n}\n\nfunction ContributionHeatmap({\n data: { period, weeks },\n className = '',\n isReverse = false,\n locale = DEFAULT_LOCALE,\n labels,\n}: ContributionHeatmapProps) {\n // Read the week start off the data rather than taking a prop: groupByWeeks already\n // decided it, and a prop could disagree with the weeks it was handed.\n const weekStartsOn = weeks.length ? parseDateString(weeks[0][0].date).getDay() : 0;\n const dayNames = getDayNames(locale, weekStartsOn);\n const months = getMonthsForHeader({ weeks, period, monthNames: getMonthNames(locale) });\n\n return (\n <div\n className={`contribution-heatmap${isReverse ? ' contribution-heatmap--reverse' : ''} ${className}`}\n >\n {/* Only the grid scrolls; the padding and the legend stay put. */}\n <div className=\"contribution-heatmap__scroll\">\n <table className=\"contribution-heatmap__table\">\n {isReverse ? (\n <>\n <thead>\n <tr>\n <th className=\"contribution-heatmap__month-header\"></th>\n {dayNames.map((dayName, dayIndex) => (\n <th key={dayIndex} className=\"contribution-heatmap__day-header\">\n {dayName}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {weeks.map((week, weekIndex) => {\n // Find month info for this week\n const month = months.find(m => weekIndex === m.start);\n const showMonthLabel = !!month;\n return (\n <tr key={weekIndex}>\n {showMonthLabel && (\n <td\n className=\"contribution-heatmap__month-label\"\n rowSpan={month.span}\n >\n {month.name}\n </td>\n )}\n {week.map((contribution, dayIndex) => (\n <DayCell\n key={dayIndex}\n contribution={contribution}\n period={period}\n locale={locale}\n labels={labels}\n delayIndex={weekIndex * 7 + dayIndex}\n />\n ))}\n </tr>\n );\n })}\n </tbody>\n </>\n ) : (\n <>\n <thead>\n <tr>\n <th className=\"contribution-heatmap__day-header\"></th>\n {months.map((month, index) => (\n <th\n key={index}\n className={`contribution-heatmap__month-header${\n index === months.length - 1\n ? ' contribution-heatmap__month-header--last'\n : ''\n }`}\n colSpan={month.span}\n >\n <span className=\"contribution-heatmap__month-header-text\">\n {month.name}\n </span>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {dayNames.map((dayName, dayIndex) => (\n <tr key={dayIndex}>\n <td className=\"contribution-heatmap__day-label\">\n {dayName}\n </td>\n {weeks.map((week, weekIndex) => {\n const contribution = week[dayIndex];\n return (\n <DayCell\n key={weekIndex}\n contribution={contribution}\n period={period}\n locale={locale}\n labels={labels}\n delayIndex={weekIndex * 7 + dayIndex}\n />\n );\n })}\n </tr>\n ))}\n </tbody>\n </>\n )}\n </table>\n </div>\n <Legend labels={labels} />\n </div>\n );\n}\n\nexport { ContributionHeatmap };\n","import {\n createDateString,\n parseDateString,\n type ContributionData,\n type Week,\n} from '@/shared/models';\n\nconst createEmpyContribution = (date: string): ContributionData => ({\n date,\n count: 0,\n level: 0,\n});\n\nconst createContributionMap = (\n contributions: ContributionData[],\n): Map<string, ContributionData> => {\n const contributionMap = new Map<string, ContributionData>();\n contributions.forEach((contribution) => {\n contributionMap.set(contribution.date, contribution);\n });\n return contributionMap;\n};\n\n// Days from weekStartsOn back to the given date's weekday, e.g. with a Monday start\n// (1) a Sunday (0) is six days into the week, not minus one.\nconst daysIntoWeek = (date: Date, weekStartsOn: number): number =>\n (date.getDay() - weekStartsOn + 7) % 7;\n\nconst getFirstDayOfWeek = (firstDate: Date, weekStartsOn: number): Date => {\n const startOfWeek = new Date(firstDate);\n startOfWeek.setDate(firstDate.getDate() - daysIntoWeek(firstDate, weekStartsOn));\n return startOfWeek;\n};\n\nconst getLastDayOfWeek = (lastDate: Date, weekStartsOn: number): Date => {\n const endOfWeek = new Date(lastDate);\n endOfWeek.setDate(lastDate.getDate() + (6 - daysIntoWeek(lastDate, weekStartsOn)));\n return endOfWeek;\n};\n\nconst createWeek = (\n startDate: Date,\n contributionMap: Map<string, ContributionData>,\n): Week => {\n const week: ContributionData[] = [];\n const currentDate = new Date(startDate);\n for (let day = 0; day < 7; day++) {\n const dateString = createDateString(currentDate);\n const contribution = contributionMap.get(dateString);\n\n if (contribution) {\n week.push(contribution);\n } else {\n week.push(createEmpyContribution(dateString));\n }\n currentDate.setDate(currentDate.getDate() + 1);\n }\n\n return week as Week;\n};\n\ninterface GroupByWeeksOptions {\n // 0 = Sunday (the default, and the US/GitHub convention), 1 = Monday, and so on.\n weekStartsOn?: number;\n}\n\nconst groupByWeeks = (\n contributions: ContributionData[],\n { weekStartsOn = 0 }: GroupByWeeksOptions = {},\n): Week[] => {\n const weeks: Week[] = [];\n const contributionMap = createContributionMap(contributions);\n\n const firstDay = getFirstDayOfWeek(parseDateString(contributions[0].date), weekStartsOn);\n const lastDate = getLastDayOfWeek(\n parseDateString(contributions[contributions.length - 1].date),\n weekStartsOn,\n );\n\n const currentDate = new Date(firstDay);\n while (currentDate <= lastDate) {\n weeks.push(createWeek(currentDate, contributionMap));\n currentDate.setDate(currentDate.getDate() + 7);\n }\n\n return weeks;\n};\n\nexport { groupByWeeks };\nexport type { GroupByWeeksOptions };\n","import type { ContributionData } from '@/shared/models';\n\nconst isSummerMonth = (month: number) => {\n return month >= 5 && month <= 7;\n};\n\nexport function createRealisticMockedCount(\n date: Date,\n): ContributionData['count'] {\n // Create realistic patterns\n const dayOfWeek = date.getDay();\n const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;\n const month = date.getMonth();\n\n let baseCount = isWeekend ? 2 : 8;\n\n // Lower activity in summer months (June, July, August)\n if (isSummerMonth(month)) {\n baseCount = Math.max(0, baseCount - 3);\n }\n\n // Add some randomness\n const randomFactor = Math.random();\n let count = 0;\n\n if (randomFactor > 0.3) {\n count = Math.floor(Math.random() * baseCount);\n }\n\n // Occasional high activity days\n if (randomFactor > 0.95) {\n count += Math.floor(Math.random() * 10);\n }\n\n return count;\n}\n","import { createDateString, type ContributionData, type Period } from '@/shared/models';\nimport { createRealisticMockedCount } from './createRealisticMockedCount';\n\nconst getRandomCount = () => Math.floor(Math.random() * 15);\n\nconst getLevel = (count: number): ContributionData['level'] => {\n let level: 0 | 1 | 2 | 3 | 4 = 0;\n if (count === 0) level = 0;\n else if (count <= 2) level = 1;\n else if (count <= 5) level = 2;\n else if (count <= 8) level = 3;\n else level = 4;\n\n return level;\n};\n\nfunction getRange(period: Period | 'lastYear') {\n if (period !== 'lastYear') {\n return period;\n }\n const end = new Date(); // Today\n const start = new Date();\n start.setFullYear(start.getFullYear() - 1); // One year ago\n return { start, end };\n}\n\nfunction generateMockData({\n period,\n isRealistic,\n}: {\n period: Period | 'lastYear';\n isRealistic: boolean;\n}): ContributionData[] {\n const data: ContributionData[] = [];\n const { start, end } = getRange(period);\n\n for (\n let date = new Date(start);\n date <= end;\n date.setDate(date.getDate() + 1)\n ) {\n const count = isRealistic\n ? createRealisticMockedCount(date)\n : getRandomCount();\n data.push({\n date: createDateString(date),\n count,\n level: getLevel(count),\n });\n }\n\n return data;\n}\n\nexport { generateMockData };\n"]}
1
+ {"version":3,"sources":["../src/shared/models.tsx","../src/shared/formatTooltip.tsx","../src/ContributionHeatmap/getMonthsForHeader.ts","../src/shared/intl.ts","../src/shared/Legend.tsx","../src/ContributionHeatmap/index.tsx","../src/shared/groupByWeeks.ts","../src/mocks/createRealisticMockedCount.ts","../src/mocks/index.tsx"],"names":["monthNames","jsx","dayNames","jsxs"],"mappings":";;;AA8BA,IAAM,aAAA,GAAyC;AAAA,EAC7C,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,CAAC,KAAA,KAAU,CAAA,MAAA,EAAS,KAAK,CAAA,CAAA;AAAA,EAChC,eAAA,EAAiB,kBAAA;AAAA,EACjB,aAAA,EAAe,CAAC,KAAA,KAAU,CAAA,EAAG,KAAK,CAAA,aAAA,EAAgB,KAAA,KAAU,CAAA,GAAI,GAAA,GAAM,EAAE,CAAA;AAC1E,CAAA;AAEA,IAAM,cAAA,GAAiB,OAAA;AAEvB,IAAM,UAAA,GAAa;AAAA,EACjB,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF;AACA,IAAM,QAAA,GAAW,CAAC,KAAA,EAAO,KAAA,EAAO,OAAO,KAAA,EAAO,KAAA,EAAO,OAAO,KAAK;AAIjE,SAAS,kBAAA,CAAmB,MAAY,MAAA,EAAsB;AAC5D,EAAA,MAAM,IAAA,GAAO,KAAK,WAAA,EAAY;AAC9B,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,QAAA,EAAS,GAAI,MAAA;AAChC,EAAA,MAAM,YAAA,GAAe,IAAI,IAAA,CAAK,IAAA,EAAM,QAAQ,CAAA,EAAG,CAAC,EAAE,OAAA,EAAQ;AAC1D,EAAA,OAAO,IAAI,IAAA,CAAK,IAAA,EAAM,KAAA,EAAO,IAAA,CAAK,IAAI,IAAA,CAAK,OAAA,EAAQ,EAAG,YAAY,CAAC,CAAA;AACrE;AAEA,IAAM,eAAe,MAAY;AAC/B,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,EAAA,OAAO,IAAI,IAAA,CAAK,GAAA,CAAI,WAAA,EAAY,EAAG,IAAI,QAAA,EAAS,EAAG,GAAA,CAAI,OAAA,EAAS,CAAA;AAClE,CAAA;AAIA,SAAS,iBAAA,GAA4B;AACnC,EAAA,MAAM,MAAM,YAAA,EAAa;AACzB,EAAA,MAAM,KAAA,GAAQ,kBAAA,CAAmB,GAAA,EAAK,GAAG,CAAA;AACzC,EAAA,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAQ,GAAI,CAAC,CAAA;AACjC,EAAA,OAAO,EAAE,OAAO,GAAA,EAAI;AACtB;AAEA,SAAS,kBAAA,GAA6B;AACpC,EAAA,MAAM,MAAM,YAAA,EAAa;AACzB,EAAA,MAAM,KAAA,GAAQ,kBAAA,CAAmB,GAAA,EAAK,EAAE,CAAA;AACxC,EAAA,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,OAAA,EAAQ,GAAI,CAAC,CAAA;AACjC,EAAA,OAAO,EAAE,OAAO,GAAA,EAAI;AACtB;AAOA,IAAM,GAAA,GAAM,CAAC,CAAA,KAAsB,MAAA,CAAO,CAAC,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAE5D,IAAM,mBAAmB,CAAC,IAAA,KACxB,GAAG,IAAA,CAAK,WAAA,EAAa,CAAA,CAAA,EAAI,GAAA,CAAI,KAAK,QAAA,EAAS,GAAI,CAAC,CAAC,CAAA,CAAA,EAAI,IAAI,IAAA,CAAK,OAAA,EAAS,CAAC,CAAA;AAE1E,IAAM,eAAA,GAAkB,CAAC,UAAA,KAA6B;AACpD,EAAA,MAAM,CAAC,IAAA,EAAM,KAAA,EAAO,GAAG,CAAA,GAAI,WAAW,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA;AAC3D,EAAA,OAAO,IAAI,IAAA,CAAK,IAAA,EAAM,KAAA,GAAQ,GAAG,GAAG,CAAA;AACtC;;;ACrFA,SAAS,SAAA,CAAU,IAAA,EAAc,EAAE,KAAA,EAAO,KAAI,EAAoB;AAChE,EAAA,OAAO,QAAQ,gBAAA,CAAiB,KAAK,CAAA,IAAK,IAAA,IAAQ,iBAAiB,GAAG,CAAA;AACxE;AAOA,IAAM,aAAA,GAAgB,CACpB,YAAA,EACA,EAAE,KAAA,EAAO,GAAA,EAAI,EACb,EAAE,MAAA,GAAS,cAAA,EAAgB,MAAA,EAAO,GAA0B,EAAC,KAClD;AACX,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,aAAA,EAAe,GAAG,MAAA,EAAO;AAI3C,EAAA,MAAM,gBAAgB,eAAA,CAAgB,YAAA,CAAa,IAAI,CAAA,CAAE,mBAAmB,MAAA,EAAQ;AAAA,IAClF,OAAA,EAAS,OAAA;AAAA,IACT,IAAA,EAAM,SAAA;AAAA,IACN,KAAA,EAAO,OAAA;AAAA,IACP,GAAA,EAAK;AAAA,GACN,CAAA;AAED,EAAA,IAAI,CAAC,SAAA,CAAU,YAAA,CAAa,IAAA,EAAM,EAAE,KAAA,EAAO,GAAA,EAAK,CAAA,IAAK,YAAA,CAAa,KAAA,KAAU,CAAA,EAAG;AAC7E,IAAA,OAAO,CAAA,EAAG,aAAa,CAAA,EAAA,EAAK,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,EAClD;AAEA,EAAA,OAAO,GAAG,aAAa,CAAA,EAAA,EAAK,KAAK,aAAA,CAAc,YAAA,CAAa,KAAK,CAAC,CAAA,CAAA;AACpE,CAAA;;;ACpCA,SAAS,kBAAA,CAAmB;AAAA,EAC1B,KAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAAA,WAAAA,GAAa;AACf,CAAA,EASI;AACF,EAAA,MAAM,SAA0D,EAAC;AACjE,EAAA,IAAI,YAAA,GAAe,EAAA;AACnB,EAAA,IAAI,WAAA,GAAc,CAAA;AAElB,EAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,EAAM,SAAA,KAAc;AACjC,IAAA,IAAI,IAAA,CAAK,SAAS,CAAA,EAAG;AAEnB,MAAA,IAAI,UAAA,GAAa,EAAA;AAEjB,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,IAAI,SAAA,CAAU,GAAA,CAAI,IAAA,EAAM,MAAM,CAAA,EAAG;AAC/B,UAAA,UAAA,GAAa,eAAA,CAAgB,GAAA,CAAI,IAAI,CAAA,CAAE,QAAA,EAAS;AAChD,UAAA;AAAA,QACF;AAAA,MACF;AAIA,MAAA,IAAI,eAAe,EAAA,EAAI;AACrB,QAAA,MAAM,SAAA,GAAY,KAAK,CAAC,CAAA;AACxB,QAAA,UAAA,GAAa,eAAA,CAAgB,SAAA,CAAU,IAAI,CAAA,CAAE,QAAA,EAAS;AAAA,MACxD;AAEA,MAAA,IAAI,eAAe,YAAA,EAAc;AAC/B,QAAA,IAAI,iBAAiB,EAAA,EAAI;AACvB,UAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAMA,WAAAA,CAAW,YAAY,CAAA,EAAG,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,SAAA,GAAY,WAAA,EAAa,CAAA;AAAA,QACnG;AACA,QAAA,YAAA,GAAe,UAAA;AACf,QAAA,WAAA,GAAc,CAAA;AAAA,MAChB,CAAA,MAAO;AACL,QAAA,WAAA,EAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAED,EAAA,IAAI,iBAAiB,EAAA,EAAI;AACvB,IAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAMA,WAAAA,CAAW,YAAY,CAAA,EAAG,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,KAAA,CAAM,MAAA,GAAS,WAAA,EAAa,CAAA;AAAA,EACtG;AAEA,EAAA,OAAO,MAAA;AACT;;;ACvDA,IAAM,oBAAA,GAAuB,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,GAAG,CAAC,CAAA;AAChD,IAAM,YAAA,GAAe,CAAA;AAErB,SAAS,WAAA,CAAY,MAAA,EAAgB,YAAA,GAAuB,CAAA,EAAa;AACvE,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ;AAAA,IAC7C,OAAA,EAAS,OAAA;AAAA,IACT,QAAA,EAAU;AAAA,GACX,CAAA;AAED,EAAA,OAAO,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,cAAa,EAAG,CAAC,GAAG,KAAA,KAAU;AACxD,IAAA,MAAM,SAAA,GAAA,CAAa,eAAe,KAAA,IAAS,YAAA;AAC3C,IAAA,OAAO,OAAO,MAAA,CAAO,IAAI,KAAK,oBAAA,GAAuB,SAAA,GAAY,KAAQ,CAAC,CAAA;AAAA,EAC5E,CAAC,CAAA;AACH;AAEA,SAAS,cAAc,MAAA,EAA0B;AAC/C,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ;AAAA,IAC7C,KAAA,EAAO,OAAA;AAAA,IACP,QAAA,EAAU;AAAA,GACX,CAAA;AAGD,EAAA,OAAO,KAAA,CAAM,IAAA;AAAA,IAAK,EAAE,QAAQ,EAAA,EAAG;AAAA,IAAG,CAAC,CAAA,EAAG,KAAA,KACpC,MAAA,CAAO,MAAA,CAAO,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,KAAA,EAAO,EAAE,CAAC,CAAC;AAAA,GACnD;AACF;AC9BA,SAAS,MAAA,CAAO,EAAE,MAAA,EAAO,EAAgD;AACvE,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,aAAA,EAAe,GAAG,MAAA,EAAO;AAE3C,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8BAAA,EACb,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAqC,QAAA,EAAA,IAAA,CAAK,IAAA,EAAK,CAAA;AAAA,IAC9D,CAAC,GAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,KAAA,qBACpB,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAEC,SAAA,EAAW,8EAA8E,KAAK,CAAA,CAAA;AAAA,QAC9F,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,KAAK;AAAA,OAAA;AAAA,MAFlB;AAAA,KAIR,CAAA;AAAA,oBACD,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mCAAA,EAAqC,eAAK,IAAA,EAAK;AAAA,GAAA,EACjE,CAAA;AAEJ;ACQA,SAAS,QAAQ,EAAE,YAAA,EAAc,QAAQ,MAAA,EAAQ,MAAA,EAAQ,YAAW,EAAiB;AACnF,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,YAAA,CAAa,IAAA,EAAM,MAAM,CAAA;AACnD,EAAA,MAAM,UAAU,aAAA,CAAc,YAAA,EAAc,QAAQ,EAAE,MAAA,EAAQ,QAAQ,CAAA;AAEtE,EAAA,uBACEC,GAAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW;AAAA,QACT,2BAAA;AAAA,QACA,CAAA,iCAAA,EAAoC,aAAa,KAAK,CAAA,CAAA;AAAA,QACtD,UAAU,EAAA,GAAK;AAAA,OACjB,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,MACX,KAAA,EAAO,UAAU,OAAA,GAAU,MAAA;AAAA,MAC3B,YAAA,EAAY,UAAU,OAAA,GAAU,MAAA;AAAA,MAChC,IAAA,EAAM,UAAU,QAAA,GAAW,MAAA;AAAA,MAC3B,QAAA,EAAU,UAAU,CAAA,GAAI,MAAA;AAAA,MACxB,cAAY,YAAA,CAAa,KAAA;AAAA,MACzB,aAAW,YAAA,CAAa,IAAA;AAAA,MACxB,OAAO,EAAE,mBAAA,EAAqB,CAAA,EAAG,UAAA,GAAa,IAAK,CAAA,CAAA,CAAA;AAAI;AAAA,GACxD;AAEL;AAYA,SAAS,mBAAA,CAAoB;AAAA,EAC3B,IAAA,EAAM,EAAE,MAAA,EAAQ,KAAA,EAAM;AAAA,EACtB,SAAA,GAAY,EAAA;AAAA,EACZ,SAAA,GAAY,KAAA;AAAA,EACZ,MAAA,GAAS,cAAA;AAAA,EACT;AACF,CAAA,EAA6B;AAG3B,EAAA,MAAM,YAAA,GAAe,KAAA,CAAM,MAAA,GAAS,eAAA,CAAgB,KAAA,CAAM,CAAC,CAAA,CAAE,CAAC,CAAA,CAAE,IAAI,CAAA,CAAE,MAAA,EAAO,GAAI,CAAA;AACjF,EAAA,MAAMC,SAAAA,GAAW,WAAA,CAAY,MAAA,EAAQ,YAAY,CAAA;AACjD,EAAA,MAAM,MAAA,GAAS,mBAAmB,EAAE,KAAA,EAAO,QAAQ,UAAA,EAAY,aAAA,CAAc,MAAM,CAAA,EAAG,CAAA;AAEtF,EAAA,uBACEC,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,WAAW,CAAA,oBAAA,EAAuB,SAAA,GAAY,gCAAA,GAAmC,EAAE,IAAI,SAAS,CAAA,CAAA;AAAA,MAGhG,QAAA,EAAA;AAAA,wBAAAF,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8BAAA,EACb,QAAA,kBAAAA,GAAAA,CAAC,OAAA,EAAA,EAAM,SAAA,EAAU,6BAAA,EACd,QAAA,EAAA,SAAA,mBACCE,IAAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,0BAAAF,GAAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAAE,IAAAA,CAAC,IAAA,EAAA,EAGC,QAAA,EAAA;AAAA,4BAAAF,GAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,oCAAA,EAAqC,CAAA;AAAA,YAClDC,SAAAA,CAAS,GAAA,CAAI,CAAC,OAAA,EAAS,QAAA,qBACtBD,GAAAA,CAAC,IAAA,EAAA,EAAkB,SAAA,EAAU,kCAAA,EAC1B,QAAA,EAAA,OAAA,EAAA,EADM,QAET,CACD;AAAA,WAAA,EACH,CAAA,EACF,CAAA;AAAA,0BACAA,GAAAA,CAAC,OAAA,EAAA,EACE,gBAAM,GAAA,CAAI,CAAC,MAAM,SAAA,KAAc;AAE9B,YAAA,MAAM,QAAQ,MAAA,CAAO,IAAA,CAAK,CAAA,CAAA,KAAK,SAAA,KAAc,EAAE,KAAK,CAAA;AACpD,YAAA,MAAM,cAAA,GAAiB,CAAC,CAAC,KAAA;AACzB,YAAA,uBACEE,KAAC,IAAA,EAAA,EACE,QAAA,EAAA;AAAA,cAAA,cAAA,oBACCF,GAAAA;AAAA,gBAAC,IAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAU,mCAAA;AAAA,kBACV,SAAS,KAAA,CAAM,IAAA;AAAA,kBAEd,QAAA,EAAA,KAAA,CAAM;AAAA;AAAA,eACT;AAAA,cAED,IAAA,CAAK,GAAA,CAAI,CAAC,YAAA,EAAc,6BACvBA,GAAAA;AAAA,gBAAC,OAAA;AAAA,gBAAA;AAAA,kBAEC,YAAA;AAAA,kBACA,MAAA;AAAA,kBACA,MAAA;AAAA,kBACA,MAAA;AAAA,kBACA,UAAA,EAAY,YAAY,CAAA,GAAI;AAAA,iBAAA;AAAA,gBALvB;AAAA,eAOR;AAAA,aAAA,EAAA,EAlBM,SAmBT,CAAA;AAAA,UAEJ,CAAC,CAAA,EACH;AAAA,SAAA,EACF,CAAA,mBAEAE,IAAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,0BAAAF,GAAAA,CAAC,OAAA,EAAA,EACC,QAAA,kBAAAE,IAAAA,CAAC,IAAA,EAAA,EAGC,QAAA,EAAA;AAAA,4BAAAF,GAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,kCAAA,EAAmC,CAAA;AAAA,YAChD,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,EAAO,0BAClBA,GAAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBAEC,WAAW,CAAA,kCAAA,EACT,KAAA,KAAU,OAAO,MAAA,GAAS,CAAA,GACtB,8CACA,EACN,CAAA,CAAA;AAAA,gBACA,SAAS,KAAA,CAAM,IAAA;AAAA,gBAEf,0BAAAA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yCAAA,EACb,gBAAM,IAAA,EACT;AAAA,eAAA;AAAA,cAVK;AAAA,aAYR;AAAA,WAAA,EACH,CAAA,EACF,CAAA;AAAA,0BACAA,GAAAA,CAAC,OAAA,EAAA,EACE,QAAA,EAAAC,SAAAA,CAAS,GAAA,CAAI,CAAC,OAAA,EAAS,QAAA,qBACtBC,IAAAA,CAAC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAAF,GAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,iCAAA,EACX,QAAA,EAAA,OAAA,EACH,CAAA;AAAA,YACC,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAM,SAAA,KAAc;AAC9B,cAAA,MAAM,YAAA,GAAe,KAAK,QAAQ,CAAA;AAClC,cAAA,uBACEA,GAAAA;AAAA,gBAAC,OAAA;AAAA,gBAAA;AAAA,kBAEC,YAAA;AAAA,kBACA,MAAA;AAAA,kBACA,MAAA;AAAA,kBACA,MAAA;AAAA,kBACA,UAAA,EAAY,YAAY,CAAA,GAAI;AAAA,iBAAA;AAAA,gBALvB;AAAA,eAMP;AAAA,YAEJ,CAAC;AAAA,WAAA,EAAA,EAhBM,QAiBT,CACD,CAAA,EACH;AAAA,SAAA,EACF,GAEJ,CAAA,EACF,CAAA;AAAA,wBACAA,GAAAA,CAAC,MAAA,EAAA,EAAO,MAAA,EAAgB;AAAA;AAAA;AAAA,GAC1B;AAEJ;;;AC1KA,IAAM,sBAAA,GAAyB,CAAC,IAAA,MAAoC;AAAA,EAClE,IAAA;AAAA,EACA,KAAA,EAAO,CAAA;AAAA,EACP,KAAA,EAAO;AACT,CAAA,CAAA;AAEA,IAAM,qBAAA,GAAwB,CAC5B,aAAA,KACkC;AAClC,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAA8B;AAC1D,EAAA,aAAA,CAAc,OAAA,CAAQ,CAAC,YAAA,KAAiB;AACtC,IAAA,eAAA,CAAgB,GAAA,CAAI,YAAA,CAAa,IAAA,EAAM,YAAY,CAAA;AAAA,EACrD,CAAC,CAAA;AACD,EAAA,OAAO,eAAA;AACT,CAAA;AAIA,IAAM,YAAA,GAAe,CAAC,IAAA,EAAY,YAAA,KAAA,CAC/B,KAAK,MAAA,EAAO,GAAI,eAAe,CAAA,IAAK,CAAA;AAEvC,IAAM,iBAAA,GAAoB,CAAC,SAAA,EAAiB,YAAA,KAA+B;AACzE,EAAA,MAAM,WAAA,GAAc,IAAI,IAAA,CAAK,SAAS,CAAA;AACtC,EAAA,WAAA,CAAY,QAAQ,SAAA,CAAU,OAAA,KAAY,YAAA,CAAa,SAAA,EAAW,YAAY,CAAC,CAAA;AAC/E,EAAA,OAAO,WAAA;AACT,CAAA;AAEA,IAAM,gBAAA,GAAmB,CAAC,QAAA,EAAgB,YAAA,KAA+B;AACvE,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,CAAK,QAAQ,CAAA;AACnC,EAAA,SAAA,CAAU,OAAA,CAAQ,SAAS,OAAA,EAAQ,IAAK,IAAI,YAAA,CAAa,QAAA,EAAU,YAAY,CAAA,CAAE,CAAA;AACjF,EAAA,OAAO,SAAA;AACT,CAAA;AAEA,IAAM,UAAA,GAAa,CACjB,SAAA,EACA,eAAA,KACS;AACT,EAAA,MAAM,OAA2B,EAAC;AAClC,EAAA,MAAM,WAAA,GAAc,IAAI,IAAA,CAAK,SAAS,CAAA;AACtC,EAAA,KAAA,IAAS,GAAA,GAAM,CAAA,EAAG,GAAA,GAAM,CAAA,EAAG,GAAA,EAAA,EAAO;AAChC,IAAA,MAAM,UAAA,GAAa,iBAAiB,WAAW,CAAA;AAC/C,IAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,GAAA,CAAI,UAAU,CAAA;AAEnD,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,IAAA,CAAK,KAAK,YAAY,CAAA;AAAA,IACxB,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,IAAA,CAAK,sBAAA,CAAuB,UAAU,CAAC,CAAA;AAAA,IAC9C;AACA,IAAA,WAAA,CAAY,OAAA,CAAQ,WAAA,CAAY,OAAA,EAAQ,GAAI,CAAC,CAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,IAAA;AACT,CAAA;AAOA,IAAM,YAAA,GAAe,CACnB,aAAA,EACA,EAAE,eAAe,CAAA,EAAE,GAAyB,EAAC,KAClC;AACX,EAAA,MAAM,QAAgB,EAAC;AACvB,EAAA,MAAM,eAAA,GAAkB,sBAAsB,aAAa,CAAA;AAE3D,EAAA,MAAM,QAAA,GAAW,kBAAkB,eAAA,CAAgB,aAAA,CAAc,CAAC,CAAA,CAAE,IAAI,GAAG,YAAY,CAAA;AACvF,EAAA,MAAM,QAAA,GAAW,gBAAA;AAAA,IACf,gBAAgB,aAAA,CAAc,aAAA,CAAc,MAAA,GAAS,CAAC,EAAE,IAAI,CAAA;AAAA,IAC5D;AAAA,GACF;AAEA,EAAA,MAAM,WAAA,GAAc,IAAI,IAAA,CAAK,QAAQ,CAAA;AACrC,EAAA,OAAO,eAAe,QAAA,EAAU;AAC9B,IAAA,KAAA,CAAM,IAAA,CAAK,UAAA,CAAW,WAAA,EAAa,eAAe,CAAC,CAAA;AACnD,IAAA,WAAA,CAAY,OAAA,CAAQ,WAAA,CAAY,OAAA,EAAQ,GAAI,CAAC,CAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,KAAA;AACT;;;ACpFA,IAAM,aAAA,GAAgB,CAAC,KAAA,KAAkB;AACvC,EAAA,OAAO,KAAA,IAAS,KAAK,KAAA,IAAS,CAAA;AAChC,CAAA;AAEO,SAAS,2BACd,IAAA,EAC2B;AAE3B,EAAA,MAAM,SAAA,GAAY,KAAK,MAAA,EAAO;AAC9B,EAAA,MAAM,SAAA,GAAY,SAAA,KAAc,CAAA,IAAK,SAAA,KAAc,CAAA;AACnD,EAAA,MAAM,KAAA,GAAQ,KAAK,QAAA,EAAS;AAE5B,EAAA,IAAI,SAAA,GAAY,YAAY,CAAA,GAAI,CAAA;AAGhC,EAAA,IAAI,aAAA,CAAc,KAAK,CAAA,EAAG;AACxB,IAAA,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAA,GAAY,CAAC,CAAA;AAAA,EACvC;AAGA,EAAA,MAAM,YAAA,GAAe,KAAK,MAAA,EAAO;AACjC,EAAA,IAAI,KAAA,GAAQ,CAAA;AAEZ,EAAA,IAAI,eAAe,GAAA,EAAK;AACtB,IAAA,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,SAAS,CAAA;AAAA,EAC9C;AAGA,EAAA,IAAI,eAAe,IAAA,EAAM;AACvB,IAAA,KAAA,IAAS,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,EAAE,CAAA;AAAA,EACxC;AAEA,EAAA,OAAO,KAAA;AACT;;;AChCA,IAAM,iBAAiB,MAAM,IAAA,CAAK,MAAM,IAAA,CAAK,MAAA,KAAW,EAAE,CAAA;AAE1D,IAAM,QAAA,GAAW,CAAC,KAAA,KAA6C;AAC7D,EAAA,IAAI,KAAA,GAA2B,CAAA;AAC/B,EAAA,IAAI,KAAA,KAAU,GAAG,KAAA,GAAQ,CAAA;AAAA,OAAA,IAChB,KAAA,IAAS,GAAG,KAAA,GAAQ,CAAA;AAAA,OAAA,IACpB,KAAA,IAAS,GAAG,KAAA,GAAQ,CAAA;AAAA,OAAA,IACpB,KAAA,IAAS,GAAG,KAAA,GAAQ,CAAA;AAAA,OACxB,KAAA,GAAQ,CAAA;AAEb,EAAA,OAAO,KAAA;AACT,CAAA;AAEA,SAAS,SAAS,MAAA,EAA6B;AAC7C,EAAA,IAAI,WAAW,UAAA,EAAY;AACzB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,EAAA,MAAM,KAAA,uBAAY,IAAA,EAAK;AACvB,EAAA,KAAA,CAAM,WAAA,CAAY,KAAA,CAAM,WAAA,EAAY,GAAI,CAAC,CAAA;AACzC,EAAA,OAAO,EAAE,OAAO,GAAA,EAAI;AACtB;AAEA,SAAS,gBAAA,CAAiB;AAAA,EACxB,MAAA;AAAA,EACA;AACF,CAAA,EAGuB;AACrB,EAAA,MAAM,OAA2B,EAAC;AAClC,EAAA,MAAM,EAAE,KAAA,EAAO,GAAA,EAAI,GAAI,SAAS,MAAM,CAAA;AAEtC,EAAA,KAAA,IACM,IAAA,GAAO,IAAI,IAAA,CAAK,KAAK,CAAA,EACzB,IAAA,IAAQ,GAAA,EACR,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,OAAA,EAAQ,GAAI,CAAC,CAAA,EAC/B;AACA,IAAA,MAAM,KAAA,GAAQ,WAAA,GACV,0BAAA,CAA2B,IAAI,IAC/B,cAAA,EAAe;AACnB,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,IAAA,EAAM,iBAAiB,IAAI,CAAA;AAAA,MAC3B,KAAA;AAAA,MACA,KAAA,EAAO,SAAS,KAAK;AAAA,KACtB,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,IAAA;AACT","file":"index.js","sourcesContent":["type Theme = '' | 'ocean' | 'sunset' | 'purple';\n\ninterface ContributionData {\n date: string;\n count: number;\n level: 0 | 1 | 2 | 3 | 4;\n}\n\ntype Week = [\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n];\n\ntype Period = { start: Date; end: Date };\n\n// The strings Intl cannot derive from a locale. Day and month names are not here:\n// those come from the locale itself, via src/shared/intl.ts.\ninterface HeatmapLabels {\n less?: string;\n more?: string;\n level?: (level: number) => string;\n noContributions?: string;\n contributions?: (count: number) => string;\n}\n\nconst defaultLabels: Required<HeatmapLabels> = {\n less: 'Less',\n more: 'More',\n level: (level) => `Level ${level}`,\n noContributions: 'No contributions',\n contributions: (count) => `${count} contribution${count !== 1 ? 's' : ''}`,\n};\n\nconst DEFAULT_LOCALE = 'en-US';\n\nconst monthNames = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n] as const;\nconst dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as const;\n\n// Local midnight, `months` whole months away, with the day-of-month clamped to the\n// target month's length: Date would otherwise roll Feb 31 forward into March.\nfunction shiftMonthsClamped(date: Date, months: number): Date {\n const year = date.getFullYear();\n const month = date.getMonth() + months;\n const daysInTarget = new Date(year, month + 1, 0).getDate();\n return new Date(year, month, Math.min(date.getDate(), daysInTarget));\n}\n\nconst startOfToday = (): Date => {\n const now = new Date();\n return new Date(now.getFullYear(), now.getMonth(), now.getDate());\n};\n\n// Both helpers span back to the day after the same date one unit ago, so today is\n// the last day and the window is exactly one year / one month long.\nfunction getLastYearPeriod(): Period {\n const end = startOfToday();\n const start = shiftMonthsClamped(end, -12);\n start.setDate(start.getDate() + 1);\n return { start, end };\n}\n\nfunction getLastMonthPeriod(): Period {\n const end = startOfToday();\n const start = shiftMonthsClamped(end, -1);\n start.setDate(start.getDate() + 1);\n return { start, end };\n}\n\n// The library reads every Date as a calendar day in the local time zone, and these two\n// are the only conversions between Date and the YYYY-MM-DD strings the data carries.\n// Neither goes through toISOString() or `new Date(string)`: both of those are UTC, and\n// mixing them with local Dates shifts the first and last day of a period by one\n// anywhere outside Greenwich.\nconst pad = (n: number): string => String(n).padStart(2, '0');\n\nconst createDateString = (date: Date): string =>\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n\nconst parseDateString = (dateString: string): Date => {\n const [year, month, day] = dateString.split('-').map(Number);\n return new Date(year, month - 1, day);\n};\n\nexport type { ContributionData, Week, Period, Theme, HeatmapLabels };\nexport {\n monthNames,\n dayNames,\n defaultLabels,\n DEFAULT_LOCALE,\n createDateString,\n parseDateString,\n getLastYearPeriod,\n getLastMonthPeriod,\n};\n","import {\n createDateString,\n parseDateString,\n defaultLabels,\n DEFAULT_LOCALE,\n type ContributionData,\n type HeatmapLabels,\n type Period,\n} from '@/shared/models';\n\n// Compares whole days, in the same YYYY-MM-DD form the contributions carry.\n// Period boundaries are Date objects that keep a time of day, so comparing them\n// directly against a date-only value put the first and last day of the period\n// out of range. ISO date strings also sort correctly, so > and < still work.\nfunction isInRange(date: string, { start, end }: Period): boolean {\n return date >= createDateString(start) && date <= createDateString(end);\n}\n\ninterface FormatTooltipOptions {\n locale?: string;\n labels?: HeatmapLabels;\n}\n\nconst formatTooltip = (\n contribution: ContributionData,\n { start, end }: Period,\n { locale = DEFAULT_LOCALE, labels }: FormatTooltipOptions = {},\n): string => {\n const text = { ...defaultLabels, ...labels };\n // parseDateString gives local midnight, so formatting in the local zone names the\n // right day. `new Date(string)` would parse as UTC midnight, which is the previous\n // day anywhere west of Greenwich.\n const formattedDate = parseDateString(contribution.date).toLocaleDateString(locale, {\n weekday: 'short',\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n\n if (!isInRange(contribution.date, { start, end }) || contribution.count === 0) {\n return `${formattedDate}: ${text.noContributions}`;\n }\n\n return `${formattedDate}: ${text.contributions(contribution.count)}`;\n};\n\nexport { formatTooltip, isInRange };\nexport type { FormatTooltipOptions };\n","import { isInRange } from '@/shared/formatTooltip';\nimport {\n monthNames as defaultMonthNames,\n parseDateString,\n type Period,\n type Week,\n} from '@/shared/models';\n\nfunction getMonthsForHeader({\n weeks,\n period,\n monthNames = defaultMonthNames,\n}: {\n weeks: Week[];\n period: Period;\n // Locale-derived; falls back to the exported English constants.\n monthNames?: readonly string[];\n}): {\n name: string;\n span: number;\n start: number;\n}[] {\n const months: { name: string; span: number; start: number }[] = [];\n let currentMonth = -1;\n let currentSpan = 0;\n\n weeks.forEach((week, weekIndex) => {\n if (week.length > 0) {\n // Find the first day in the week that falls within our actual data range\n let monthToUse = -1;\n\n for (const day of week) {\n if (isInRange(day.date, period)) {\n monthToUse = parseDateString(day.date).getMonth();\n break;\n }\n }\n\n // If no day in the week is in our actual range, use the middle of the week\n // (index 3 - which weekday that is depends on weekStartsOn)\n if (monthToUse === -1) {\n const middleDay = week[3];\n monthToUse = parseDateString(middleDay.date).getMonth();\n }\n\n if (monthToUse !== currentMonth) {\n if (currentMonth !== -1) {\n months.push({ name: monthNames[currentMonth], span: currentSpan, start: weekIndex - currentSpan });\n }\n currentMonth = monthToUse;\n currentSpan = 1;\n } else {\n currentSpan++;\n }\n }\n });\n\n if (currentMonth !== -1) {\n months.push({ name: monthNames[currentMonth], span: currentSpan, start: weeks.length - currentSpan });\n }\n\n return months;\n}\n\nexport { getMonthsForHeader };\n","// Day and month names derived from a locale, so the component ships no translations.\n//\n// timeZone is pinned to UTC throughout: the reference dates below are built with\n// Date.UTC, and formatting them in a local zone would shift them across a day boundary\n// in negative offsets and hand back the wrong name.\n\n// 7 Jan 2024 is a Sunday, so this week runs Sunday -> Saturday.\nconst REFERENCE_WEEK_START = Date.UTC(2024, 0, 7);\nconst DAYS_IN_WEEK = 7;\n\nfunction getDayNames(locale: string, weekStartsOn: number = 0): string[] {\n const format = new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n timeZone: 'UTC',\n });\n\n return Array.from({ length: DAYS_IN_WEEK }, (_, index) => {\n const dayOfWeek = (weekStartsOn + index) % DAYS_IN_WEEK;\n return format.format(new Date(REFERENCE_WEEK_START + dayOfWeek * 86400000));\n });\n}\n\nfunction getMonthNames(locale: string): string[] {\n const format = new Intl.DateTimeFormat(locale, {\n month: 'short',\n timeZone: 'UTC',\n });\n\n // The 15th avoids any month-length edge case.\n return Array.from({ length: 12 }, (_, month) =>\n format.format(new Date(Date.UTC(2024, month, 15))),\n );\n}\n\nexport { getDayNames, getMonthNames };\n","import { defaultLabels, type HeatmapLabels } from '@/shared/models';\n\nfunction Legend({ labels }: { labels?: HeatmapLabels }): React.ReactNode {\n const text = { ...defaultLabels, ...labels };\n\n return (\n <div className=\"contribution-heatmap__legend\">\n <span className=\"contribution-heatmap__legend-text\">{text.less}</span>\n {[0, 1, 2, 3, 4].map((level) => (\n <div\n key={level}\n className={`contribution-heatmap__legend-item contribution-heatmap__legend-item--level-${level}`}\n title={text.level(level)}\n ></div>\n ))}\n <span className=\"contribution-heatmap__legend-text\">{text.more}</span>\n </div>\n );\n}\n\nexport { Legend };\n","import './index.scss';\nimport type {\n ContributionData,\n HeatmapLabels,\n Period,\n Week as WeekType,\n} from '../shared/models';\nimport { getMonthsForHeader } from './getMonthsForHeader';\nimport { formatTooltip, isInRange } from '@/shared/formatTooltip';\nimport { DEFAULT_LOCALE, parseDateString } from '@/shared/models';\nimport { getDayNames, getMonthNames } from '@/shared/intl';\nimport { Legend } from '@/shared/Legend';\n\n\ninterface DayCellProps {\n contribution: ContributionData;\n period: Period;\n locale: string;\n labels?: HeatmapLabels;\n // Position in the grid, used to stagger the load animation.\n delayIndex: number;\n}\n\n// groupByWeeks pads every week out to Sun-Sat, so the first and last week carry days\n// that fall outside the period. They keep their slot - the columns depend on seven\n// cells per row - but render no square and stay out of the a11y tree.\nfunction DayCell({ contribution, period, locale, labels, delayIndex }: DayCellProps) {\n const inRange = isInRange(contribution.date, period);\n const tooltip = formatTooltip(contribution, period, { locale, labels });\n\n return (\n <td\n className={[\n 'contribution-heatmap__day',\n `contribution-heatmap__day--level-${contribution.level}`,\n inRange ? '' : 'contribution-heatmap__day--outside',\n ]\n .filter(Boolean)\n .join(' ')}\n title={inRange ? tooltip : undefined}\n aria-label={inRange ? tooltip : undefined}\n role={inRange ? 'button' : undefined}\n tabIndex={inRange ? 0 : undefined}\n data-count={contribution.count}\n data-date={contribution.date}\n style={{ '--animation-delay': `${delayIndex * 0.005}s` } as React.CSSProperties}\n ></td>\n );\n}\n\ninterface ContributionHeatmapProps {\n data: { contribution: ContributionData[]; period: Period; weeks: WeekType[] };\n className?: string;\n isReverse?: boolean;\n // Drives day names, month names and the tooltip date through Intl.\n locale?: string;\n // The handful of strings Intl cannot derive.\n labels?: HeatmapLabels;\n}\n\nfunction ContributionHeatmap({\n data: { period, weeks },\n className = '',\n isReverse = false,\n locale = DEFAULT_LOCALE,\n labels,\n}: ContributionHeatmapProps) {\n // Read the week start off the data rather than taking a prop: groupByWeeks already\n // decided it, and a prop could disagree with the weeks it was handed.\n const weekStartsOn = weeks.length ? parseDateString(weeks[0][0].date).getDay() : 0;\n const dayNames = getDayNames(locale, weekStartsOn);\n const months = getMonthsForHeader({ weeks, period, monthNames: getMonthNames(locale) });\n\n return (\n <div\n className={`contribution-heatmap${isReverse ? ' contribution-heatmap--reverse' : ''} ${className}`}\n >\n {/* Only the grid scrolls; the padding and the legend stay put. */}\n <div className=\"contribution-heatmap__scroll\">\n <table className=\"contribution-heatmap__table\">\n {isReverse ? (\n <>\n <thead>\n <tr>\n {/* The corner above the label column heads nothing, so it is a td: an empty th\n is a header with no text, which axe reports and a screen reader announces. */}\n <td className=\"contribution-heatmap__month-header\"></td>\n {dayNames.map((dayName, dayIndex) => (\n <th key={dayIndex} className=\"contribution-heatmap__day-header\">\n {dayName}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {weeks.map((week, weekIndex) => {\n // Find month info for this week\n const month = months.find(m => weekIndex === m.start);\n const showMonthLabel = !!month;\n return (\n <tr key={weekIndex}>\n {showMonthLabel && (\n <td\n className=\"contribution-heatmap__month-label\"\n rowSpan={month.span}\n >\n {month.name}\n </td>\n )}\n {week.map((contribution, dayIndex) => (\n <DayCell\n key={dayIndex}\n contribution={contribution}\n period={period}\n locale={locale}\n labels={labels}\n delayIndex={weekIndex * 7 + dayIndex}\n />\n ))}\n </tr>\n );\n })}\n </tbody>\n </>\n ) : (\n <>\n <thead>\n <tr>\n {/* The corner above the label column heads nothing, so it is a td: an empty th\n is a header with no text, which axe reports and a screen reader announces. */}\n <td className=\"contribution-heatmap__day-header\"></td>\n {months.map((month, index) => (\n <th\n key={index}\n className={`contribution-heatmap__month-header${\n index === months.length - 1\n ? ' contribution-heatmap__month-header--last'\n : ''\n }`}\n colSpan={month.span}\n >\n <span className=\"contribution-heatmap__month-header-text\">\n {month.name}\n </span>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {dayNames.map((dayName, dayIndex) => (\n <tr key={dayIndex}>\n <td className=\"contribution-heatmap__day-label\">\n {dayName}\n </td>\n {weeks.map((week, weekIndex) => {\n const contribution = week[dayIndex];\n return (\n <DayCell\n key={weekIndex}\n contribution={contribution}\n period={period}\n locale={locale}\n labels={labels}\n delayIndex={weekIndex * 7 + dayIndex}\n />\n );\n })}\n </tr>\n ))}\n </tbody>\n </>\n )}\n </table>\n </div>\n <Legend labels={labels} />\n </div>\n );\n}\n\nexport { ContributionHeatmap };\n","import {\n createDateString,\n parseDateString,\n type ContributionData,\n type Week,\n} from '@/shared/models';\n\nconst createEmpyContribution = (date: string): ContributionData => ({\n date,\n count: 0,\n level: 0,\n});\n\nconst createContributionMap = (\n contributions: ContributionData[],\n): Map<string, ContributionData> => {\n const contributionMap = new Map<string, ContributionData>();\n contributions.forEach((contribution) => {\n contributionMap.set(contribution.date, contribution);\n });\n return contributionMap;\n};\n\n// Days from weekStartsOn back to the given date's weekday, e.g. with a Monday start\n// (1) a Sunday (0) is six days into the week, not minus one.\nconst daysIntoWeek = (date: Date, weekStartsOn: number): number =>\n (date.getDay() - weekStartsOn + 7) % 7;\n\nconst getFirstDayOfWeek = (firstDate: Date, weekStartsOn: number): Date => {\n const startOfWeek = new Date(firstDate);\n startOfWeek.setDate(firstDate.getDate() - daysIntoWeek(firstDate, weekStartsOn));\n return startOfWeek;\n};\n\nconst getLastDayOfWeek = (lastDate: Date, weekStartsOn: number): Date => {\n const endOfWeek = new Date(lastDate);\n endOfWeek.setDate(lastDate.getDate() + (6 - daysIntoWeek(lastDate, weekStartsOn)));\n return endOfWeek;\n};\n\nconst createWeek = (\n startDate: Date,\n contributionMap: Map<string, ContributionData>,\n): Week => {\n const week: ContributionData[] = [];\n const currentDate = new Date(startDate);\n for (let day = 0; day < 7; day++) {\n const dateString = createDateString(currentDate);\n const contribution = contributionMap.get(dateString);\n\n if (contribution) {\n week.push(contribution);\n } else {\n week.push(createEmpyContribution(dateString));\n }\n currentDate.setDate(currentDate.getDate() + 1);\n }\n\n return week as Week;\n};\n\ninterface GroupByWeeksOptions {\n // 0 = Sunday (the default, and the US/GitHub convention), 1 = Monday, and so on.\n weekStartsOn?: number;\n}\n\nconst groupByWeeks = (\n contributions: ContributionData[],\n { weekStartsOn = 0 }: GroupByWeeksOptions = {},\n): Week[] => {\n const weeks: Week[] = [];\n const contributionMap = createContributionMap(contributions);\n\n const firstDay = getFirstDayOfWeek(parseDateString(contributions[0].date), weekStartsOn);\n const lastDate = getLastDayOfWeek(\n parseDateString(contributions[contributions.length - 1].date),\n weekStartsOn,\n );\n\n const currentDate = new Date(firstDay);\n while (currentDate <= lastDate) {\n weeks.push(createWeek(currentDate, contributionMap));\n currentDate.setDate(currentDate.getDate() + 7);\n }\n\n return weeks;\n};\n\nexport { groupByWeeks };\nexport type { GroupByWeeksOptions };\n","import type { ContributionData } from '@/shared/models';\n\nconst isSummerMonth = (month: number) => {\n return month >= 5 && month <= 7;\n};\n\nexport function createRealisticMockedCount(\n date: Date,\n): ContributionData['count'] {\n // Create realistic patterns\n const dayOfWeek = date.getDay();\n const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;\n const month = date.getMonth();\n\n let baseCount = isWeekend ? 2 : 8;\n\n // Lower activity in summer months (June, July, August)\n if (isSummerMonth(month)) {\n baseCount = Math.max(0, baseCount - 3);\n }\n\n // Add some randomness\n const randomFactor = Math.random();\n let count = 0;\n\n if (randomFactor > 0.3) {\n count = Math.floor(Math.random() * baseCount);\n }\n\n // Occasional high activity days\n if (randomFactor > 0.95) {\n count += Math.floor(Math.random() * 10);\n }\n\n return count;\n}\n","import { createDateString, type ContributionData, type Period } from '@/shared/models';\nimport { createRealisticMockedCount } from './createRealisticMockedCount';\n\nconst getRandomCount = () => Math.floor(Math.random() * 15);\n\nconst getLevel = (count: number): ContributionData['level'] => {\n let level: 0 | 1 | 2 | 3 | 4 = 0;\n if (count === 0) level = 0;\n else if (count <= 2) level = 1;\n else if (count <= 5) level = 2;\n else if (count <= 8) level = 3;\n else level = 4;\n\n return level;\n};\n\nfunction getRange(period: Period | 'lastYear') {\n if (period !== 'lastYear') {\n return period;\n }\n const end = new Date(); // Today\n const start = new Date();\n start.setFullYear(start.getFullYear() - 1); // One year ago\n return { start, end };\n}\n\nfunction generateMockData({\n period,\n isRealistic,\n}: {\n period: Period | 'lastYear';\n isRealistic: boolean;\n}): ContributionData[] {\n const data: ContributionData[] = [];\n const { start, end } = getRange(period);\n\n for (\n let date = new Date(start);\n date <= end;\n date.setDate(date.getDate() + 1)\n ) {\n const count = isRealistic\n ? createRealisticMockedCount(date)\n : getRandomCount();\n data.push({\n date: createDateString(date),\n count,\n level: getLevel(count),\n });\n }\n\n return data;\n}\n\nexport { generateMockData };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pearpages/heatmap",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "GitHub-style contribution heatmap as a React component.",
5
5
  "keywords": [
6
6
  "react",