iter(g140): 表格支持整行点击
This commit is contained in:
@@ -1476,6 +1476,14 @@
|
|||||||
"acceptance": "position 新增 flipSide(四向判定,水平按宽度算左右空间),tooltip placement 支持 left/right 并复用自动翻转;CSS 四向定位 + 出场位移分离;文档 props/steps 与演示同步;npm run check 全绿",
|
"acceptance": "position 新增 flipSide(四向判定,水平按宽度算左右空间),tooltip placement 支持 left/right 并复用自动翻转;CSS 四向定位 + 出场位移分离;文档 props/steps 与演示同步;npm run check 全绿",
|
||||||
"notes": "自补:既有组件可用性细节",
|
"notes": "自补:既有组件可用性细节",
|
||||||
"status": "done"
|
"status": "done"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "g140",
|
||||||
|
"title": "表格支持整行点击",
|
||||||
|
"motivation": "「点行进详情」是表格最常用交互,dataTable 只能把跳转塞进某一列的按钮:点击区域小、键盘不可达,行本身对读屏也只是文本",
|
||||||
|
"acceptance": "dataTable 新增 onRowClick:传了则该行 tabindex=0 并响应点击 / Enter / 空格,未传时行为与现在完全一致(不进 Tab 序列);文档 props/steps 与演示同步;单文件仍 ≤200 行;npm run check 全绿",
|
||||||
|
"notes": "自补:既有组件可用性细节(行数恰好压到 200 行上限,靠合并注释行达标)",
|
||||||
|
"status": "open"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T07:32:30.909Z",
|
"generatedAt": "2026-10-26T07:33:59.912Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -1922,6 +1922,12 @@
|
|||||||
"n": "exportName",
|
"n": "exportName",
|
||||||
"t": "string",
|
"t": "string",
|
||||||
"d": "导出文件名;缺省 table.csv"
|
"d": "导出文件名;缺省 table.csv"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onRowClick",
|
||||||
|
"t": "(row: T) => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "整行点击 / Enter / 空格的回调;传了行才可聚焦可点(未传时行保持纯展示,不抢 Tab 焦点)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
@@ -1938,7 +1944,8 @@
|
|||||||
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
||||||
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
||||||
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",
|
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",
|
||||||
"需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出"
|
"需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出",
|
||||||
|
"整行可点(进详情、选中一行做后续操作)传 onRowClick:行会变成可聚焦项,Enter / 空格与点击等价;不传时行不进 Tab 序列,纯展示表不会被读屏当成按钮"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1214,6 +1214,7 @@ const content = h(
|
|||||||
],
|
],
|
||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
selected: picked,
|
selected: picked,
|
||||||
|
onRowClick: (row) => toast.info(`打开 ${row.name} 的详情`), // 整行可点:Tab 进入 + Enter 等价
|
||||||
}), h(
|
}), h(
|
||||||
'div',
|
'div',
|
||||||
{ class: 'row' },
|
{ class: 'row' },
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T07:32:30.909Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T07:33:59.912Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -975,6 +975,7 @@ const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label:
|
|||||||
- selected:Signal<RowKey[]> —— 受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写
|
- selected:Signal<RowKey[]> —— 受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写
|
||||||
- exportable:boolean —— 工具栏显示「导出 CSV」:导出筛选 + 排序后的全量行(不分页);缺省 false
|
- exportable:boolean —— 工具栏显示「导出 CSV」:导出筛选 + 排序后的全量行(不分页);缺省 false
|
||||||
- exportName:string —— 导出文件名;缺省 table.csv
|
- exportName:string —— 导出文件名;缺省 table.csv
|
||||||
|
- onRowClick(回调):(row: T) => void —— 整行点击 / Enter / 空格的回调;传了行才可聚焦可点(未传时行保持纯展示,不抢 Tab 焦点)
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -997,6 +998,7 @@ dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓
|
|||||||
11. 需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)
|
11. 需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)
|
||||||
12. 可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充
|
12. 可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充
|
||||||
13. 需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出
|
13. 需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出
|
||||||
|
14. 整行可点(进详情、选中一行做后续操作)传 onRowClick:行会变成可聚焦项,Enter / 空格与点击等价;不传时行不进 Tab 序列,纯展示表不会被读屏当成按钮
|
||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-table
|
现场演示:apps/docs/demo.html#sec-table
|
||||||
|
|
||||||
|
|||||||
@@ -151,15 +151,17 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
|||||||
return h('td', col.align ? { class: `ua-align-${col.align}` } : null, text);
|
return h('td', col.align ? { class: `ua-align-${col.align}` } : null, text);
|
||||||
});
|
});
|
||||||
if (selectable) {
|
if (selectable) {
|
||||||
const check = h('input', { type: 'checkbox', class: 'ua-checkbox' }) as HTMLInputElement;
|
const check = h('input', { type: 'checkbox', class: 'ua-checkbox' }) as HTMLInputElement; // 受控:选中态由内核 selection 决定
|
||||||
check.checked = selected;
|
check.checked = selected;
|
||||||
check.addEventListener('change', () => table.dispatch({ type: 'toggleSelect', key }));
|
check.addEventListener('change', () => table.dispatch({ type: 'toggleSelect', key }));
|
||||||
cells.unshift(h('td', { class: 'ua-cell-check' }, check));
|
cells.unshift(h('td', { class: 'ua-cell-check' }, check));
|
||||||
}
|
}
|
||||||
return h('tr', selected ? { class: 'ua-row-selected' } : null, ...cells);
|
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;
|
||||||
|
return h('tr', rowProps, ...cells);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// —— 列宽:数字落成 <col> 固定宽度,'flex' 或未设置交给浏览器自适应 ——
|
// —— 列宽:数字落成 <col> 固定宽度,'flex' 或未设置交给浏览器自适应 ——
|
||||||
const cols = props.columns.map((col) => (typeof col.width === 'number' ? h('col', { style: `width:${col.width}px` }) : h('col'))); // 每个数据列一个 <col>,拖拽直接改它
|
const cols = props.columns.map((col) => (typeof col.width === 'number' ? h('col', { style: `width:${col.width}px` }) : h('col'))); // 每个数据列一个 <col>,拖拽直接改它
|
||||||
const colgroup = h('colgroup', null, selectable ? h('col', { style: 'width:44px' }) : null, ...cols);
|
const colgroup = h('colgroup', null, selectable ? h('col', { style: 'width:44px' }) : null, ...cols);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export interface DataTableProps<T> {
|
|||||||
selected?: Signal<RowKey[]>; // 受控选中行信号:与内核选择双向同步,供批量操作读取
|
selected?: Signal<RowKey[]>; // 受控选中行信号:与内核选择双向同步,供批量操作读取
|
||||||
exportable?: boolean; // 工具栏显示导出按钮:导出筛选 + 排序后的全量行(不分页);缺省 false
|
exportable?: boolean; // 工具栏显示导出按钮:导出筛选 + 排序后的全量行(不分页);缺省 false
|
||||||
exportName?: string; // 导出文件名;缺省 table.csv
|
exportName?: string; // 导出文件名;缺省 table.csv
|
||||||
|
onRowClick?: (row: T) => void; // 整行点击 / Enter / 空格的回调;传了行才可聚焦(未传时行不可交互)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 选项条目:select / radioGroup / tabs / segmented 共用的选项形状 ***/
|
/*** 选项条目:select / radioGroup / tabs / segmented 共用的选项形状 ***/
|
||||||
|
|||||||
@@ -1793,6 +1793,12 @@
|
|||||||
"n": "exportName",
|
"n": "exportName",
|
||||||
"t": "string",
|
"t": "string",
|
||||||
"d": "导出文件名;缺省 table.csv"
|
"d": "导出文件名;缺省 table.csv"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onRowClick",
|
||||||
|
"t": "(row: T) => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "整行点击 / Enter / 空格的回调;传了行才可聚焦可点(未传时行保持纯展示,不抢 Tab 焦点)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
@@ -1809,7 +1815,8 @@
|
|||||||
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
||||||
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
||||||
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",
|
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",
|
||||||
"需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出"
|
"需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出",
|
||||||
|
"整行可点(进详情、选中一行做后续操作)传 onRowClick:行会变成可聚焦项,Enter / 空格与点击等价;不传时行不进 Tab 序列,纯展示表不会被读屏当成按钮"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4387,3 +4387,15 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
|
|||||||
.ua-datepicker-day.is-in-range:hover {
|
.ua-datepicker-day.is-in-range:hover {
|
||||||
background: color-mix(in srgb, var(--ua-color-accent) 20%, transparent);
|
background: color-mix(in srgb, var(--ua-color-accent) 20%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* —— 可点击行:指针手型 + 键盘聚焦环,与选中行底色叠加不冲突 —— */
|
||||||
|
.ua-row-clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.ua-row-clickable:hover td {
|
||||||
|
background: color-mix(in srgb, var(--ua-color-accent) 8%, transparent);
|
||||||
|
}
|
||||||
|
.ua-row-clickable:focus-visible {
|
||||||
|
outline: 2px solid var(--ua-color-accent);
|
||||||
|
outline-offset: -2px; /* 环画在行内,避免被表格滚动容器裁掉 */
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user