进度

只要你知道还剩多少,就用确定进度条——只有它才真的告诉了读者什么。省略 value,进度条就会改成动画,那只说明「还在忙」,别的什么也没说。

确定进度

stack({ gap: 'lg' },
  progress({ value: 25 }),
  progress({ value: 60 }),
  progress({ value: 100 }),
)

不确定进度

progress()

没有 label 的进度条会被标上 aria-hidden——一个没有无障碍名称的 progressbar 角色对屏幕阅读器毫无意义,所以无标签的进度条一律当作装饰。凡是希望读者跟进的,都要给标签。

标签

标签说明正在发生什么;showValue 会在右侧加上百分比。

正在渲染页面72%
正在优化图片50%
等待部署
stack({ gap: 'lg' },
  progress({ value: 72, label: '正在渲染页面', showValue: true }),
  progress({ value: 30, max: 60, label: '正在优化图片', showValue: true }),
  progress({ label: '等待部署' }),
)

颜色与高度

通过80%
降级45%
失败中20%
stack({ gap: 'lg' },
  progress({ value: 80, color: 'success', label: '通过', showValue: true }),
  progress({ value: 45, color: 'warning', label: '降级', showValue: true }),
  progress({ value: 20, color: 'danger', label: '失败中', showValue: true }),
  progress({ value: 60, color: 'neutral', height: 'xs' }),
  progress({ value: 60, color: 'primary', height: '1rem' }),
)

不是 100 的刻度

max 让你直接传原始数字——已构建页数对总页数——而不必先算出百分比。

169 个页面中的第 118 个70%
progress({ value: 118, max: 169, label: '169 个页面中的第 118 个', showValue: true })

从浏览器里推动它

进度条是服务端渲染出来的 HTML:百分比是填充上的一个自定义属性,数值在 aria-valuenow 里,页面上没有任何东西会自己去改动它们。给它一个 id,setProgress 就会把这几处一起改掉——填充、播报出来的值,以及标签旁边的百分比。

import { setProgress } from 'sitelo/ui/client'

const request = new XMLHttpRequest()

request.upload.addEventListener('progress', (event) => {
  setProgress('upload', event.loaded, { max: event.total })
})

最大值会被记住,所以后面的调用只要传值就够了。或者像组件找到自己那份运行时一样直接取用这个模块,完全绕开打包:

button({ onclick: "import('/su/progress.js').then(m=>m.set('upload',100))" }, '完成')

传 null——或者任何不是有限数字的东西——会把进度条交还给不确定动画,所以不再报数的任务不需要单独处理。getProgress() 则按进度条自己的刻度把当前值读回来。

试一试

这个页面加载了运行时,所以下面的按钮是真的在推动这条进度条。

上传中0%
stack({ gap: 'md' },
  progress({ id: 'demo-progress', value: 0, label: '上传中', showValue: true }),
  stack({ direction: 'row', gap: 'sm', wrap: true },
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',0))" }, '重置'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',35))" }, '35%'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',80))" }, '80%'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',100))" }, '完成'),
    button({ size: 'sm', variant: 'ghost', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',null))" }, '未知'),
  ),
)

没有标签的进度条同样会动,但它仍然是 aria-hidden——它当初就是故意不带名字渲染的,现在给它播报一个数值,只会往无障碍树里塞一个没有名字的 progressbar。

加载转圈

没有单独的加载转圈组件——它就是一个图标,让它转起来的是 spin。和所有图标一样,它按 em 定尺寸,所以不用告诉它大小,它自己就能配上旁边的文字。

stack({ direction: 'row', gap: 'lg', align: 'center' },
  icon('spinner', { spin: true, size: 'sm' }),
  icon('spinner', { spin: true }),
  icon('spinner', { spin: true, size: 'lg' }),
)

放进上下文里

单独出现的加载转圈要给一个 label,这样它才会被播报。按钮里的那个不需要——按钮本身已经说了它在干什么。

正在获取最新的构建…

stack({ gap: 'md' },
  stack({ direction: 'row', gap: 'sm', align: 'center' },
    icon('spinner', { spin: true, label: '加载中' }),
    text({ variant: 'small', tone: 'muted' }, '正在获取最新的构建…'),
  ),
  stack({ direction: 'row', gap: 'sm' },
    button({ loading: true }, '部署中'),
    button({ variant: 'outline', loading: true }, '检查链接中'),
  ),
)

属性

progress()——也以 progressBar 之名导出:

属性类型默认值说明
valuenumber—进行到哪儿了。省略它就用不确定动画。
maxnumber100哪个值算作完成。
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''primary'填充颜色。
labelChild—进度条上方的文字;同时也是它的无障碍名称。
showValuebooleanfalse在标签旁显示百分比。
heightSpace'0.5rem'进度条的粗细。

setProgress(),来自 sitelo/ui/client:

属性类型默认值说明
targetElement | string—进度条本身,或者它的 id。没有元素用这个 id 时,会当作选择器再试一次。
valuenumber | null—要移动到哪里。null 会把它交还给不确定动画。
options.maxnumber100什么算完成。会被记住,供后续调用使用。

加载转圈没有自己的属性——它就是 icon('spinner', { spin: true }),icon() 收什么它就收什么。