46 lines
1.9 KiB
TypeScript
46 lines
1.9 KiB
TypeScript
import { effect, h } from '@ui-arch/frame';
|
|
import type { ProgressProps } from './types.js';
|
|
|
|
/*** 进度条:value 信号驱动 0-100 定量态,缺省 value 则退化为不确定态(无限往复);满格切 success 色并停条纹 ***/
|
|
export function progress(props: ProgressProps = {}): HTMLElement {
|
|
// —— 状态集中声明 ——
|
|
const value = props.value; // 可选:不传即不确定态
|
|
const fill = h('div', { class: 'ua-progress-fill' });
|
|
const track = h('div', { class: 'ua-progress-track' }, fill);
|
|
const text = h('span', { class: 'ua-progress-text' });
|
|
const withText = props.showText !== false && !!value; // 不确定态没有百分比可显示
|
|
const label = props.label; // 可访问名:读屏念「上传进度 50%」而不是干巴巴的百分比
|
|
const variant = props.variant ?? 'default'; // 语义色:default 之外都带类名,满格时仍以 variant 为准
|
|
const cls = `ua-progress${variant === 'default' ? '' : ` ua-progress-${variant}`}`;
|
|
|
|
if (!value) {
|
|
fill.classList.add('ua-indeterminate'); // 宽度与往复动画交给 CSS
|
|
return h('div', { class: cls, role: 'progressbar', 'aria-label': label ?? '进行中' }, track);
|
|
}
|
|
effect(() => {
|
|
const v = clamp(value()); // 依赖收集:信号变化自动写回
|
|
fill.style.width = `${v}%`;
|
|
fill.classList.toggle('ua-done', v >= 100); // 满格切语义色
|
|
text.textContent = `${Math.round(v)}%`;
|
|
});
|
|
return h(
|
|
'div',
|
|
{
|
|
class: cls,
|
|
role: 'progressbar',
|
|
'aria-label': label,
|
|
'aria-valuemin': '0',
|
|
'aria-valuemax': '100',
|
|
'aria-valuenow': () => String(Math.round(clamp(value()))),
|
|
},
|
|
track,
|
|
withText ? text : null,
|
|
);
|
|
}
|
|
|
|
/*** 进度钳制到 0-100 ***/
|
|
function clamp(v: number): number {
|
|
if (Number.isNaN(v)) return 0; // 卫语句:非法值归零
|
|
return Math.min(100, Math.max(0, v));
|
|
}
|