iter(g84): 数据表定宽列支持拖拽调宽

This commit is contained in:
HIsheR
2026-10-26 13:02:41 +08:00
parent f5db24a52f
commit db5d38c5f4
5 changed files with 97 additions and 7 deletions
+51
View File
@@ -0,0 +1,51 @@
import { h } from '@ui-arch/frame';
const MIN = 56; // 列宽下限:小于这个值表头文字会被压成竖排
/*** 列宽手柄:右缘拖动改 <col> 宽度(双击复位),← → 每次微调 8px,两者共用 apply 收口 ***/
export function attachResize(th: HTMLElement, col: HTMLElement, start: number, label: string): void {
// —— 状态集中声明 ——
let dragging = false; // 是否处于拖拽中
let originX = 0; // 按下时的指针横坐标
let originW = 0; // 按下时的实际列宽
/*** 唯一写宽入口:夹取下限后写回 <col> ***/
const apply = (w: number): void => { col.style.width = `${Math.max(MIN, Math.round(w))}px`; };
const grip = h('span', {
class: 'ua-col-grip',
role: 'separator', // 可调节分隔符语义:读屏可识别为「可调整」
'aria-orientation': 'vertical',
'aria-label': `调整「${label}」列宽`,
tabindex: '0',
onpointerdown: (e: PointerEvent) => {
dragging = true;
originX = e.clientX;
originW = col.getBoundingClientRect().width; // 取实际渲染宽度,避免与 min-width 打架
grip.setPointerCapture(e.pointerId); // 捕获指针:拖出表头也继续跟手
e.preventDefault(); // 不触发列头排序
e.stopPropagation();
document.body.classList.add('ua-col-resizing'); // 拖拽期间全局禁用文本选中
},
onpointermove: (e: PointerEvent) => { if (dragging) apply(originW + e.clientX - originX); },
onpointerup: stop,
onpointercancel: stop,
onclick: (e: MouseEvent) => e.stopPropagation(), // 点手柄不该触发排序
ondblclick: (e: MouseEvent) => { e.stopPropagation(); apply(start); }, // 双击回到声明宽度
onkeydown: (e: KeyboardEvent) => {
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; // 卫语句:只接管方向键
e.preventDefault();
e.stopPropagation(); // 不冒泡到表格的行选择等外层快捷键
apply(col.getBoundingClientRect().width + (e.key === 'ArrowRight' ? 8 : -8));
},
}) as HTMLElement;
/*** 拖拽收尾:复位标志与全局样式 ***/
function stop(): void {
if (!dragging) return; // 卫语句:不在拖拽中
dragging = false;
document.body.classList.remove('ua-col-resizing');
}
th.append(grip);
}
+4 -6
View File
@@ -7,6 +7,7 @@ import {
} from '@ui-arch/table-core';
import type { PageInfo, RowKey, TableInstance } from '@ui-arch/table-core';
import { computed, effect, h, renderList, signal } from '@ui-arch/frame';
import { attachResize } from './columnResize.js';
import { button } from './button.js';
import { skeleton } from './skeleton.js';
import { textField } from './textField.js';
@@ -118,6 +119,7 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
col.header ?? col.key,
col.sortable ? sortMark(col.key) : null,
);
if (typeof col.width === 'number') attachResize(el, cols[props.columns.indexOf(col)], col.width, String(col.header ?? col.key)); // 定宽列可拖拽
if (col.sortable) {
effect(() => {
const rule = state().sorting.find((r) => r.key === col.key); // 与视觉箭头同源
@@ -161,12 +163,8 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
});
// —— 列宽:数字落成 <col> 固定宽度,'flex' 或未设置交给浏览器自适应 ——
const colgroup = h(
'colgroup',
null,
selectable ? h('col', { style: 'width:44px' }) : null,
...props.columns.map((col) => (typeof col.width === 'number' ? h('col', { style: `width:${col.width}px` }) : h('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 filterInput = textField({
placeholder: `筛选 ${filterKey}…`,