数据
CalendarHeatmap - 日历热力图
一年的每日读数,画成一周一列的网格。
用法
什么时候用它
来自@misoto22/folio/charts
import { CalendarHeatmap } from '@misoto22/folio/charts'说明
一年的每日读数,画成一周一列的网格。
Heatmap 画的是一张有名字的行和列组成的网格,对日期一无所知;这个组件就是所有人都会在它上面再写一遍、而且没有两次写法相同的那套排法——一周一列、一个星期几一行、月份名标在它碰到的第一列上,窗口两头的日子留成空缺而不是零。
这张网格是按「日期」搭出来的,不是按数组顺序,所以数据里的一个缺口就是日历上的一个缺口。这正是它要防的那种失败:给一串光秃秃的计数,少了一个星期二,它后面的每一条读数都会挪一格,结果是一张看上去很像样、却根本没发生过的一年。
每一个日期都是在 UTC 零点上做算术。一张按本地时间画的日历,在两个时区里就是两张不同的日历,而两者的分歧正好是一天宽——小到能混过评审,大到足以把一条读数挪进错误的那一周。
结构
| 部件 | 说明 |
|---|---|
| 网格必填 | 一个 Heatmap:一周一列、一个星期几一行,按日期搭出来,而不是按数组顺序。每一个日期都是在 UTC 零点上做算术,所以同一个窗口在每个时区里渲染出来都一样。 |
| 窗口必填 | from 和 to;不传就取 values 里最早和最晚的日期。网格会往回退到第一周的开头、往后补到最后一周的结尾,所以一列永远是完整的一周。 |
| 星期几行头必填 | 来自 weekdayLabels 的七个行头,在「数组」里永远是周日在先,再由 weekStartsOn 转到网格上——所以「周一开头」和「翻译」是两个各自独立的决定。 |
| 月份列头 | 来自 monthLabels 的名字,标在每个月碰到的第一列上,其余列留空。那些空白带着一串零宽空格,好让网格仍然分得清它们,而这正是「按列名查找」所要求的。 |
| 空缺 | 窗口里没有对应项的那一天,以及首尾两个不完整的周里的每一天:一圈虚线轮廓,播报出来是「无数据」,绝不是色阶上最淡的那一格。 |
| 单元读数 | describe 为那一天返回的东西,跟着这一格一起播报。它是按单元格取的,不是按数字取的,所以两天数目相同,是两条不同的读数。 |
实践建议
推荐
- 只要这个窗口是关于问题、而不是关于数据的事实,就把 from 和 to 传上。靠数据推出来的话,一个安静的一月干脆就不存在了——网格悄悄变成了和旁边那张不一样的窗口,而两张照样会被拿来比。
- describe 要写成把单位叫出来的样子。一格被播报时带的行和列,是一个星期几加上一个横跨五周的月份,所以不写它,读屏软件拿到的读数就是一个日期加一个光秃秃的数。
- 要比两张日历,就把 domain 钉死。各按自己的值域画,一个清闲的年份和一个忙碌的年份看上去一模一样,读者来做的那个对比不只是丢了,而是反了。
避免
- 什么都没记录下来的那一天,不要送一个零过来。null 画成空缺,零画成色阶上最淡的那一格,而把两者画成一样,是在把一次故障变成一个清闲的时段,网格上没有任何东西说得清那到底是哪一种。
- 不要把带时间或带时区的日期交给它。每一个边界都是从 YYYY-MM-DD 按 UTC 零点解析的,所以一个在某地是 23:00 的时间戳,会落到网格里前一天上。
- 行不是星期几时,不要用这个组件。按小时对星期几的负载图、或者一张混淆矩阵,用的是 Heatmap,而绕道这里意味着要为本来就没有日期的格子编出日期来。
示例
半年的每日读数
半年的数据,按日期搭出来,而不是按数组顺序——这正是它和一串光秃秃的计数之间的全部差别:在那种写法里,少了一天,它后面的每一条读数都会挪一格,画出一张看上去很像样、却根本没发生过的一年。三月里那两周传的是 null 而不是零,所以它渲染成虚线的空缺,并且播报「无数据」:什么都没发生和什么都没记录下来是两条不同的读数,而把它们画成一样的页面,是在请读者去解释一次其实只是采集断了的故障。describe 把单位叫了出来,因为一格被播报时带的行和列,是一个星期几加上一个横跨五周的月份。
| Row | Jan | | | | Feb | | | | Mar | | | | | Apr | | | | May | | | | | Jun | | | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sun | 2 commits on 2026-01-04Sun, Jan | 3 commits on 2026-01-11Sun, | 4 commits on 2026-01-18Sun, | 3 commits on 2026-01-25Sun, | 2 commits on 2026-02-01Sun, Feb | 1 commits on 2026-02-08Sun, | 0 commits on 2026-02-15Sun, | 1 commits on 2026-02-22Sun, | 2 commits on 2026-03-01Sun, Mar | 3 commits on 2026-03-08Sun, | Sun, : no data | Sun, : no data | 2 commits on 2026-03-29Sun, | 1 commits on 2026-04-05Sun, Apr | 0 commits on 2026-04-12Sun, | 0 commits on 2026-04-19Sun, | 2 commits on 2026-04-26Sun, | 3 commits on 2026-05-03Sun, May | 4 commits on 2026-05-10Sun, | 4 commits on 2026-05-17Sun, | 2 commits on 2026-05-24Sun, | 1 commits on 2026-05-31Sun, | 0 commits on 2026-06-07Sun, Jun | 0 commits on 2026-06-14Sun, | 2 commits on 2026-06-21Sun, | 3 commits on 2026-06-28Sun, |
| Mon | 10 commits on 2026-01-05Mon, Jan | 14 commits on 2026-01-12Mon, | 15 commits on 2026-01-19Mon, | 13 commits on 2026-01-26Mon, | 9 commits on 2026-02-02Mon, Feb | 4 commits on 2026-02-09Mon, | 3 commits on 2026-02-16Mon, | 5 commits on 2026-02-23Mon, | 9 commits on 2026-03-02Mon, Mar | 13 commits on 2026-03-09Mon, | Mon, : no data | Mon, : no data | 9 commits on 2026-03-30Mon, | 5 commits on 2026-04-06Mon, Apr | 3 commits on 2026-04-13Mon, | 5 commits on 2026-04-20Mon, | 9 commits on 2026-04-27Mon, | 13 commits on 2026-05-04Mon, May | 15 commits on 2026-05-11Mon, | 13 commits on 2026-05-18Mon, | 9 commits on 2026-05-25Mon, | 5 commits on 2026-06-01Mon, | 3 commits on 2026-06-08Mon, Jun | 4 commits on 2026-06-15Mon, | 9 commits on 2026-06-22Mon, | 13 commits on 2026-06-29Mon, |
| Tue | 10 commits on 2026-01-06Tue, Jan | 14 commits on 2026-01-13Tue, | 15 commits on 2026-01-20Tue, | 12 commits on 2026-01-27Tue, | 8 commits on 2026-02-03Tue, Feb | 4 commits on 2026-02-10Tue, | 3 commits on 2026-02-17Tue, | 6 commits on 2026-02-24Tue, | 10 commits on 2026-03-03Tue, Mar | 14 commits on 2026-03-10Tue, | Tue, : no data | Tue, : no data | 8 commits on 2026-03-31Tue, | 4 commits on 2026-04-07Tue, Apr | 3 commits on 2026-04-14Tue, | 5 commits on 2026-04-21Tue, | 10 commits on 2026-04-28Tue, | 14 commits on 2026-05-05Tue, May | 15 commits on 2026-05-12Tue, | 13 commits on 2026-05-19Tue, | 9 commits on 2026-05-26Tue, | 5 commits on 2026-06-02Tue, | 3 commits on 2026-06-09Tue, Jun | 5 commits on 2026-06-16Tue, | 9 commits on 2026-06-23Tue, | 13 commits on 2026-06-30Tue, |
| Wed | 11 commits on 2026-01-07Wed, Jan | 14 commits on 2026-01-14Wed, | 15 commits on 2026-01-21Wed, | 12 commits on 2026-01-28Wed, | 7 commits on 2026-02-04Wed, Feb | 4 commits on 2026-02-11Wed, | 3 commits on 2026-02-18Wed, | 6 commits on 2026-02-25Wed, | 11 commits on 2026-03-04Wed, Mar | 14 commits on 2026-03-11Wed, | Wed, : no data | Wed, : no data | 8 commits on 2026-04-01Wed, | 4 commits on 2026-04-08Wed, Apr | 3 commits on 2026-04-15Wed, | 6 commits on 2026-04-22Wed, | 10 commits on 2026-04-29Wed, | 14 commits on 2026-05-06Wed, May | 15 commits on 2026-05-13Wed, | 12 commits on 2026-05-20Wed, | 8 commits on 2026-05-27Wed, | 4 commits on 2026-06-03Wed, | 3 commits on 2026-06-10Wed, Jun | 5 commits on 2026-06-17Wed, | 10 commits on 2026-06-24Wed, | 14 commits on 2026-07-01Wed, |
| Thu | 12 commits on 2026-01-08Thu, Jan | 15 commits on 2026-01-15Thu, | 14 commits on 2026-01-22Thu, | 11 commits on 2026-01-29Thu, | 7 commits on 2026-02-05Thu, Feb | 3 commits on 2026-02-12Thu, | 3 commits on 2026-02-19Thu, | 7 commits on 2026-02-26Thu, | 11 commits on 2026-03-05Thu, Mar | 15 commits on 2026-03-12Thu, | Thu, : no data | Thu, : no data | 7 commits on 2026-04-02Thu, | 4 commits on 2026-04-09Thu, Apr | 3 commits on 2026-04-16Thu, | 6 commits on 2026-04-23Thu, | 11 commits on 2026-04-30Thu, | 14 commits on 2026-05-07Thu, May | 15 commits on 2026-05-14Thu, | 12 commits on 2026-05-21Thu, | 7 commits on 2026-05-28Thu, | 4 commits on 2026-06-04Thu, | 3 commits on 2026-06-11Thu, Jun | 6 commits on 2026-06-18Thu, | 11 commits on 2026-06-25Thu, | 14 commits on 2026-07-02Thu, |
| Fri | 12 commits on 2026-01-09Fri, Jan | 15 commits on 2026-01-16Fri, | 14 commits on 2026-01-23Fri, | 11 commits on 2026-01-30Fri, | 6 commits on 2026-02-06Fri, Feb | 3 commits on 2026-02-13Fri, | 4 commits on 2026-02-20Fri, | 7 commits on 2026-02-27Fri, | 12 commits on 2026-03-06Fri, Mar | 15 commits on 2026-03-13Fri, | Fri, : no data | Fri, : no data | 6 commits on 2026-04-03Fri, | 3 commits on 2026-04-10Fri, Apr | 4 commits on 2026-04-17Fri, | 7 commits on 2026-04-24Fri, | 12 commits on 2026-05-01Fri, | 15 commits on 2026-05-08Fri, May | 14 commits on 2026-05-15Fri, | 11 commits on 2026-05-22Fri, | 7 commits on 2026-05-29Fri, | 3 commits on 2026-06-05Fri, | 3 commits on 2026-06-12Fri, Jun | 7 commits on 2026-06-19Fri, | 11 commits on 2026-06-26Fri, | 14 commits on 2026-07-03Fri, |
| Sat | 3 commits on 2026-01-10Sat, Jan | 4 commits on 2026-01-17Sat, | 4 commits on 2026-01-24Sat, | 2 commits on 2026-01-31Sat, | 1 commits on 2026-02-07Sat, Feb | 0 commits on 2026-02-14Sat, | 0 commits on 2026-02-21Sat, | 2 commits on 2026-02-28Sat, | 3 commits on 2026-03-07Sat, Mar | 4 commits on 2026-03-14Sat, | Sat, : no data | Sat, : no data | 1 commits on 2026-04-04Sat, | 0 commits on 2026-04-11Sat, Apr | 0 commits on 2026-04-18Sat, | 2 commits on 2026-04-25Sat, | 3 commits on 2026-05-02Sat, | 4 commits on 2026-05-09Sat, May | 4 commits on 2026-05-16Sat, | 3 commits on 2026-05-23Sat, | 1 commits on 2026-05-30Sat, | 0 commits on 2026-06-06Sat, | 0 commits on 2026-06-13Sat, Jun | 1 commits on 2026-06-20Sat, | 3 commits on 2026-06-27Sat, | Sat, : no data |
周一开头的一周
同一张网格,给一位一周从周一开始的读者,并且用这位读者自己的说法。weekStartsOn 转的是行;weekdayLabels 和 monthLabels 翻译它们。这两件事分成两个属性是有意的:不管网格怎么排,标签数组永远是周日在先,所以一份周一开头的德语日历和一份周一开头的英语日历共用同一个惯例,只在字符串上不同。domain 被钉死了,正是这一点让两张日历可比——各按自己的值域画,一个清闲的年份和一个忙碌的年份看上去一模一样。
| Row | Jan | | | | Feb | | | | Mär | | | | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Mo | 0 Beiträge am 2026-01-05Mo, Jan | 10 Beiträge am 2026-01-12Mo, | 7 Beiträge am 2026-01-19Mo, | 4 Beiträge am 2026-01-26Mo, | 1 Beiträge am 2026-02-02Mo, Feb | 11 Beiträge am 2026-02-09Mo, | 8 Beiträge am 2026-02-16Mo, | 5 Beiträge am 2026-02-23Mo, | 2 Beiträge am 2026-03-02Mo, Mär | 12 Beiträge am 2026-03-09Mo, | 9 Beiträge am 2026-03-16Mo, | 6 Beiträge am 2026-03-23Mo, | 3 Beiträge am 2026-03-30Mo, |
| Di | 7 Beiträge am 2026-01-06Di, Jan | 4 Beiträge am 2026-01-13Di, | 1 Beiträge am 2026-01-20Di, | 11 Beiträge am 2026-01-27Di, | 8 Beiträge am 2026-02-03Di, Feb | 5 Beiträge am 2026-02-10Di, | 2 Beiträge am 2026-02-17Di, | 12 Beiträge am 2026-02-24Di, | 9 Beiträge am 2026-03-03Di, Mär | 6 Beiträge am 2026-03-10Di, | 3 Beiträge am 2026-03-17Di, | 0 Beiträge am 2026-03-24Di, | 10 Beiträge am 2026-03-31Di, |
| Mi | 1 Beiträge am 2026-01-07Mi, Jan | 11 Beiträge am 2026-01-14Mi, | 8 Beiträge am 2026-01-21Mi, | 5 Beiträge am 2026-01-28Mi, | 2 Beiträge am 2026-02-04Mi, Feb | 12 Beiträge am 2026-02-11Mi, | 9 Beiträge am 2026-02-18Mi, | 6 Beiträge am 2026-02-25Mi, | 3 Beiträge am 2026-03-04Mi, Mär | 0 Beiträge am 2026-03-11Mi, | 10 Beiträge am 2026-03-18Mi, | 7 Beiträge am 2026-03-25Mi, | 4 Beiträge am 2026-04-01Mi, |
| Do | 8 Beiträge am 2026-01-08Do, Jan | 5 Beiträge am 2026-01-15Do, | 2 Beiträge am 2026-01-22Do, | 12 Beiträge am 2026-01-29Do, | 9 Beiträge am 2026-02-05Do, Feb | 6 Beiträge am 2026-02-12Do, | 3 Beiträge am 2026-02-19Do, | 0 Beiträge am 2026-02-26Do, | 10 Beiträge am 2026-03-05Do, Mär | 7 Beiträge am 2026-03-12Do, | 4 Beiträge am 2026-03-19Do, | 1 Beiträge am 2026-03-26Do, | 11 Beiträge am 2026-04-02Do, |
| Fr | 2 Beiträge am 2026-01-09Fr, Jan | 12 Beiträge am 2026-01-16Fr, | 9 Beiträge am 2026-01-23Fr, | 6 Beiträge am 2026-01-30Fr, | 3 Beiträge am 2026-02-06Fr, Feb | 0 Beiträge am 2026-02-13Fr, | 10 Beiträge am 2026-02-20Fr, | 7 Beiträge am 2026-02-27Fr, | 4 Beiträge am 2026-03-06Fr, Mär | 1 Beiträge am 2026-03-13Fr, | 11 Beiträge am 2026-03-20Fr, | 8 Beiträge am 2026-03-27Fr, | 5 Beiträge am 2026-04-03Fr, |
| Sa | 9 Beiträge am 2026-01-10Sa, Jan | 6 Beiträge am 2026-01-17Sa, | 3 Beiträge am 2026-01-24Sa, | 0 Beiträge am 2026-01-31Sa, | 10 Beiträge am 2026-02-07Sa, Feb | 7 Beiträge am 2026-02-14Sa, | 4 Beiträge am 2026-02-21Sa, | 1 Beiträge am 2026-02-28Sa, | 11 Beiträge am 2026-03-07Sa, Mär | 8 Beiträge am 2026-03-14Sa, | 5 Beiträge am 2026-03-21Sa, | 2 Beiträge am 2026-03-28Sa, | 12 Beiträge am 2026-04-04Sa, |
| So | 3 Beiträge am 2026-01-11So, Jan | 0 Beiträge am 2026-01-18So, | 10 Beiträge am 2026-01-25So, | 7 Beiträge am 2026-02-01So, | 4 Beiträge am 2026-02-08So, Feb | 1 Beiträge am 2026-02-15So, | 11 Beiträge am 2026-02-22So, | 8 Beiträge am 2026-03-01So, | 5 Beiträge am 2026-03-08So, Mär | 2 Beiträge am 2026-03-15So, | 12 Beiträge am 2026-03-22So, | 9 Beiträge am 2026-03-29So, | 6 Beiträge am 2026-04-05So, |
无障碍
- 这张网格是一个真正的 <table>,所以读屏软件走的结构和眼睛读的结构是同一个:一行表头、一列表头,以及一个会播报自己那条读数的单元格。
- 没有对应项的那一天播报的是「无数据」而不是一个数,所以对看不见这条色阶的读者来说,空缺和零是两回事。
- 读数是按单元格取的,所以日期会和计数一起播报出来——单单行头和列头,说的只是一个星期几和一个月份,不是某一天。