iter(g336): 表格行选择补读屏语义

This commit is contained in:
HIsheR
2026-10-27 02:10:09 +08:00
parent 74dacdc8f2
commit da09e29058
5 changed files with 18 additions and 9 deletions
+4 -4
View File
@@ -61,7 +61,7 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
const span = (): number => shown().length + (selectable ? 1 : 0) + (props.renderDetail ? 1 : 0); // 跨列数随可见列与展开列变化
// —— 表头全选框:checked / indeterminate 三态跟随当页选中情况 ——
const headCheck = h('input', { type: 'checkbox', class: 'ua-checkbox' }) as HTMLInputElement;
const headCheck = h('input', { type: 'checkbox', class: 'ua-checkbox', 'aria-label': '全选本页' }) as HTMLInputElement; // 无名字的复选框读屏只念「复选框」
headCheck.addEventListener('change', () => {
if (headCheck.checked) api.selectAllVisible();
else table.dispatch({ type: 'clearSelection' });
@@ -137,15 +137,15 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
return h('td', { class: col.align ? `ua-align-${col.align}` : null, onclick: custom ? (e: MouseEvent) => e.stopPropagation() : null }, custom ?? (col.format ? col.format(raw, row) : String(raw))); // 无 render 退回纯文本(免注入);格内点击不触发整行回调
});
if (selectable) {
const check = h('input', { type: 'checkbox', class: 'ua-checkbox' }) as HTMLInputElement; // 受控:选中态由内核 selection 决定
const check = h('input', { type: 'checkbox', class: 'ua-checkbox', 'aria-label': `选择第 ${i + 1} 行` }) as HTMLInputElement; // 受控:选中态由内核 selection 决定;名字带行号,读屏才分得清选哪行
check.checked = selected;
check.addEventListener('change', () => table.dispatch({ type: 'toggleSelect', key }));
cells.unshift(h('td', { class: 'ua-cell-check' }, check));
}
if (props.renderDetail) cells.unshift(expandCell(open, `第 ${i + 1} 行`, () => expanded(open ? expanded().filter((k) => k !== key) : [...expanded(), key]))); // 展开列在最前:与选择列并列但不互斥
const rowProps = props.onRowClick // 传了才把整行做成可聚焦按钮语义(Tab 进入、Enter / 空格触发)
? { tabindex: '0', class: `ua-row-clickable${selected ? ' ua-row-selected' : ''}`, onclick: () => props.onRowClick?.(row), onkeydown: (e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); props.onRowClick?.(row); } } }
: selected ? { class: 'ua-row-selected' } : null;
? { tabindex: '0', class: `ua-row-clickable${selected ? ' ua-row-selected' : ''}`, 'aria-selected': selectable ? String(selected) : null, onclick: () => props.onRowClick?.(row), onkeydown: (e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); props.onRowClick?.(row); } } }
: selected ? { class: 'ua-row-selected', 'aria-selected': 'true' } : null; // 淡底是视觉,aria-selected 才是读屏能听到的「这行被选中」
const tr = h('tr', rowProps, ...cells);
return props.renderDetail && open ? [tr, detailRow(span(), props.renderDetail(row))] : [tr];
});