数据
BarList - 排行榜
一个排行榜——条形在名字后面,而不是旁边。
用法
什么时候用它
来自@misoto22/folio/charts
import { BarList } from '@misoto22/folio/charts'说明
一个排行榜——条形在名字后面,而不是旁边。
它回答的是「来源排行」「最慢的接口」「最大的客户」,而且比横向柱状图答得更好:横向柱状图会把三分之一的宽度花在一根重复标签的分类轴上,而这些标签本来就可以写进行里。从坐标轴上读一个名字再去对应一根柱子是两步;直接在柱子上读它是零步。
它是一个 <table>,因为它本来就是:两列、一行表头、一个条目一行。条形是名字那一格的背景,所以它永远不会变成读屏软件还要多走一遍的第二个元素。
当分类少而固定、坐标轴确实在干活时——读者需要照着刻度读数值,而不是扫一眼排名——改用 <BarChart orientation="horizontal">。
结构
| 部件 | 说明 |
|---|---|
| 表名必填 | label,作为表格真正的 caption——默认 sr-only,除非 showLabel 把它当眉题印在这些行的上方。读屏软件那边,这个列表的名字就是它。 |
| 表头行必填 | 一个 sr-only 的 thead,两个 th scope="col",Name 和 Value,所以哪怕这个列表从不显示表头,两列也都是有名字的。 |
| 名称单元格必填 | 一个 th scope="row",装着截成一行的行名;item.icon 排在它前面、作为 aria-hidden 的装饰;item.href 会把这个名字本身变成链接。 |
| 条形必填 | 它不是一个元素:名称单元格内层 span 上的一道 linear-gradient,在这一行占天花板的比例处硬停。要是画成一个兄弟 div,它就成了无障碍树上又一个什么都不说的空东西。 |
| 数值单元格必填 | 一个贴尾边的 td,等宽的表格数字、--ink-2,由 formatValue 写出来——除非调用处替掉它,否则用的就是坐标轴那套紧凑写法的默认值。 |
| Other 行 | limit 加出来的东西:最后一行叫 Other,装着尾巴的总和,所以显示出来的这几行,仍然交代得了自己是从哪个整体里切出来的。 |
| 空状态 | 没有东西可排时,label 下面是一个 ChartEmpty。一个 caption 压在空的 tbody 上,在读者看来就是一个加载失败的列表,而且刷新也不会改变什么。 |
实践建议
推荐
- 行是随时间陆续到来的,就要固定 max。天花板是当前显示的最大那一行,所以一个新的第一名会把它下面每一根条形重新缩放——昨天填了三分之二的轨道,今天数字一点没变却只填了四分之一,而读者看到的那次变动,其实发生在另一行身上。
- 要用 limit,不要在调用处自己切 items。尾巴是被加总进 Other 行、而不是被丢掉,所以显示的五行仍然凑得成整体;手工切出来的前五名会悄悄扔掉另外四十项,而列表里没有一句话说这件事。
- 名字要短到能扛过截断。名字是被截成一行的,还要和数值列分宽度,所以两个到第四十个字符之后才有区别的接口,渲染出来是同一行、同一个省略号。
- 顺序本身就是重点时,要传 sort={false}——一条漏斗、一组步骤、一批固定的地区。sort 默认为 true 并按降序排,等于一声不吭地把一个序列变成了一个排行。
避免
- 条形量的是这一行比上最大那一行,从来不是占总量的比例:第一名永远填满自己的轨道,所以占全部流量 3% 的五行,和占了全部流量的五行长得一模一样。整体要紧的话,那句话该写在 caption 里。
- 数值列里不要混着几种数字形状。它是贴尾边的等宽表格数字,只有在字符串形状一致时才能把位数对齐——而默认的格式化函数在 10,000 处切到紧凑写法,所以一个跨过这个门槛的列表会把 9,400 排在 1.2M 底下,竖着比就什么都比不成了。
- 两行不能同名。名字同时是这一行的标签和它的 React key,所以一个可能重复标签的查询建出来的列表会渲染出重复的 key——React 会为此告警,并且在列表一更新时就调和错。
示例
默认
这份排名列表最朴素的样子:八行,条形画在名字后面,而不是名字旁边。而这正是一张水平条形图做不到的事——它要拿三分之一的宽度去放分类轴,而把那根轴上的一个名字对应到它的条形是两步,直接从条形上读出名字则是零步。除非 sort 另有说明,各行按降序排列,而刻度的上限取自其中最大的那一个,所以这份列表自己读是可读的,和第二份对照着读就不是了。
| Name | Value |
|---|---|
| google.com | 42.1K |
| github.com | 18.8K |
| news.ycombinator.com | 9,400 |
| x.com | 6,120 |
| reddit.com | 3,280 |
| lobste.rs | 1,940 |
| bsky.app | 1,100 |
| linkedin.com | 640 |
条数上限
limit 保留前三行,并把余下的五行汇总成一行 Other,而不是把它们丢掉。一个悄悄丢掉长尾的「前三」歪曲了整体,还让读者没有任何办法知道这件事发生过;那行 Other 才是让这一列数字加起来还等于点什么的东西。领先的三行还带着 href,这让名字本身成为链接——条形是同一个单元格上的背景,所以给一行加链接,不会给读屏软件多添一个要走过去的东西。
| Name | Value |
|---|---|
| google.com | 42.1K |
| github.com | 18.8K |
| news.ycombinator.com | 9,400 |
| Other | 13.1K |
共用上限
两份必须对照着读的列表,所以两份都被给了同一个 max。放任它自己推导,每一份都会拿自己最大的那一行当上限,于是 Enterprise 在两边都填满了自己的轨道——一个翻了一倍还多的季度,画出来会和一个原地踏步的季度分毫不差,而页面上不会有任何东西说明这一点。formatValue 让这些数字变成金额;默认是坐标轴用的那套紧凑写法,那对一个访问次数是对的,而对一种货币则什么都没说。
| Name | Value |
|---|---|
| Enterprise | A$92,400 |
| Mid-market | A$61,200 |
| Self-serve | A$28,900 |
| Education | A$9,600 |
| Name | Value |
|---|---|
| Enterprise | A$41,800 |
| Mid-market | A$33,500 |
| Self-serve | A$26,100 |
| Education | A$11,200 |
无障碍
- 是真正的 <table>,两列、一行一个条目——因为排行榜本来就是这个结构。条形是名字那一格的背景,所以它不会变成读屏软件还要多走一遍的第二个元素。
- limit 会把尾巴加总成一行「Other」,而不是丢掉——一个悄悄扔掉另外四十项的「前五」是在错误陈述整体,而读者根本看不出来。
- 两个榜要并排比较时一定要固定 max:各自独立取刻度的话,两边的第一名都会填满整条轨道,两个差得很远的数字看起来一模一样。