@lensui/lens-table 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,32 +104,32 @@ export function Example() {
104
104
  | `virtualized` | `boolean \| { enabled?: boolean; overscan?: number }` | `true` | 虚拟渲染配置。传`false` 时渲染全部行列;传对象时可通过 `enabled` 开关,并用 `overscan` 配置可见范围外的缓冲行列数量,默认 `4`。 |
105
105
  | `tooltip` | `boolean \| { header?: boolean; cell?: boolean }` | `true` | 文本 tooltip 总配置。传`true` 时表头和单元格 hover 都显示完整文本;传 `false` 可关闭;传对象可分别控制表头和单元格。 |
106
106
  | `summary` | `boolean \| { position?: 'top' \| 'bottom'; verticalBordered?: boolean; emptyValue?: ReactNode }` | `false` | 表格级合计行配置。默认不显示;传 `true` 或对象即开启合计行,对象可控制显示在表头下方或底部、是否显示内部竖线,以及无合计值单元格的填充内容。 |
107
- | `rowNumber` | `boolean` | `true` | 是否显示序号列。默认开启并自动生成左侧序号列,不需要在`columns` 中配置;传 `false` 可关闭。 |
108
- | `selectedCell` | `GridSelection \| null` | 非受控 | 受控单元格选中状态。 |
109
- | `defaultSelectedCell` | `GridSelection \| null` | `null` | 非受控模式下的默认选中单元格。 |
110
- | `onSelectedCellChange` | `(selection) => void` | - | 单元格选中变化回调。 |
107
+ | `rowNumber` | `boolean \| TableRowNumberConfig` | `true` | 是否显示序号列。默认开启并自动生成左侧序号列,不需要在`columns` 中配置;传 `false` 可关闭。传 `{ fixed: false }` 可取消序号列固定,`fixed` 默认 `true`。 |
108
+ | `selectedCell` | `GridSelectionTarget \| null` | 非受控 | 受控单元格选中状态。传 `columnKey`,行可使用 `rowKey` 或 `rowIndex` 定位。 |
109
+ | `defaultSelectedCell` | `GridSelectionTarget \| null` | `null` | 默认选中单元格。传 `columnKey`,行可使用 `rowKey` 或 `rowIndex` 定位。 |
110
+ | `onSelectedCellChange` | `(selection) => void` | - | 单元格选中变化回调,`selection.value` 为当前单元格的原始值。 |
111
111
  | `rangeSelection` | `boolean` | `false` | 是否允许鼠标拖动选择多个单元格。开启后支持范围复制、范围右键菜单和右下角拖拽扩展范围。 |
112
- | `rowSelection` | `boolean \| AxisSelectionConfig` | `false` | 开启行选择。可传`{ mode: 'single' \| 'multiple' }`;开启后组件自动生成左侧选择列,不需要在 `columns` 中配置。 |
112
+ | `rowSelection` | `boolean \| RowSelectionConfig` | `false` | 开启行选择。可传`{ mode: 'single' \| 'multiple', showCheckbox: false }` 隐藏选择框并通过点击数据单元格选择行;默认显示选择框。默认自动生成左侧选择列,不需要在 `columns` 中配置。 |
113
113
  | `selectedRowKeys` | `GridKey[]` | 非受控 | 受控行选择 key 列表。 |
114
114
  | `defaultSelectedRowKeys` | `GridKey[]` | `[]` | 非受控模式下默认选中的行 key。 |
115
- | `onSelectedRowKeysChange` | `(keys, rows, indices) => void` | - | 行选择变化回调。 |
115
+ | `onSelectedRowChange` | `(keys, rows, indices) => void` | - | 行选择变化回调。 |
116
116
  | `columnSelection` | `boolean \| AxisSelectionConfig` | `false` | 开启列选择。可传`{ mode: 'single' \| 'multiple' }`。 |
117
117
  | `selectedColumnKeys` | `string[]` | 非受控 | 受控列选择 key 列表。 |
118
118
  | `defaultSelectedColumnKeys` | `string[]` | `[]` | 非受控模式下默认选中的列 key。 |
119
- | `onSelectedColumnKeysChange` | `(keys) => void` | - | 列选择变化回调。 |
119
+ | `onSelectedColumnChange` | `(keys) => void` | - | 列选择变化回调。 |
120
120
  | `columnDraggable` | `boolean` | `true` | 是否允许拖拽调整列顺序。默认开启,普通列默认显示表头拖拽入口,传`false` 可关闭。 |
121
- | `columnResizable` | `boolean` | `false` | 是否允许拖拽调整列宽。 |
122
- | `onColumnOrderChange` | `(sourceIndex, targetIndex) => void` | - | 列拖拽排序回调。需要在外部更新`columns`。 |
123
- | `onColumnResize` | `(columnKey, width) => void` | - | 列宽变化回调。未传时组件会在内部维护列宽;传入后可用于持久化或受控同步。 |
121
+ | `onColumnsReorder` | `(columns, detail) => void` | - | 列拖拽重排完成回调,返回重排后的完整列配置和索引详情。 |
124
122
  | `rowDraggable` | `boolean` | `false` | 是否允许拖拽调整行顺序。开启后组件自动生成左侧拖拽手柄列,不需要在`columns` 中配置。 |
125
- | `onRowOrderChange` | `(sourceIndex, targetIndex) => void` | - | 行拖拽排序回调。需要在外部更新`rows`。 |
126
- | `onInsertRows` | `(event) => void` | - | 右键菜单插入行回调。 |
127
- | `onDeleteRows` | `(event) => void` | - | 右键菜单删除行回调。 |
128
- | `sortState` | `GridSortState \| null` | 非受控 | 受控排序状态。组件只管理 UI 状态,数据排序由外部完成。 |
129
- | `onSortStateChange` | `(state) => void` | - | 点击表头排序时触发。 |
130
- | `filterValues` | `Record<string, string>` | 非受控 | 受控筛选值。组件只管理输入值,数据过滤由外部完成。 |
131
- | `onFilterValuesChange` | `(values) => void` | - | 表头筛选值变化回调。 |
123
+ | `onRowsReorder` | `(rows, detail) => void` | - | 行拖拽重排完成回调,返回重排后的完整行数据和索引详情。 |
124
+ | `columnResizable` | `boolean` | `true` | 是否允许拖拽调整列宽。默认开启,传 `false` 可关闭。 |
125
+ | `onColumnResize` | `(columnKey, width) => void` | - | 列宽变化回调。未传时组件会在内部维护列宽;传入后可用于持久化或受控同步。 |
126
+ | `onInsertRows` | `(rows, insertedRows) => void` | - | 插入完成后返回新的完整行数组和本次插入的行。 |
127
+ | `onDeleteRows` | `(rows, deletedRows) => void` | - | 删除完成后返回新的完整行数组和本次删除的行。 |
128
+ | `onSortChange` | `(rows, state) => Row[]` | - | 自定义排序方法。排序状态由组件内部管理;返回排序后的行。 |
129
+ | `onFilterChange` | `(rows, values) => Row[]` | - | 自定义筛选方法。筛选值由组件内部管理;返回符合条件的行。 |
132
130
  | `onCellChange` | `(change) => void` | - | 单元格编辑提交回调。 |
131
+ | `onCellClick` | `(cell: TableCellContext<Row>, event: React.MouseEvent<HTMLElement>) => void` | - | 数据单元格单击回调;调用 `event.preventDefault()` 可阻止默认选择行为。 |
132
+ | `onCellDoubleClick` | `(cell: TableCellContext<Row>, event: React.MouseEvent<HTMLElement>) => void` | - | 数据单元格双击回调,在编辑前触发;调用 `event.preventDefault()` 可阻止进入编辑。 |
133
133
  | `onCellContextMenu` | `(event) => void` | - | 单元格右键回调,可用于扩展自定义菜单逻辑。 |
134
134
  | `contextMenu` | `boolean \| { header?, cell?, range? }` | `true` | `true` 开启默认右键菜单,`false` 关闭右键菜单,传对象可自定义菜单项、顺序和分割线。 |
135
135
  | `emptyContent` | `ReactNode` | 内置空状态 | 自定义空数据内容。 |
@@ -137,6 +137,17 @@ export function Example() {
137
137
  | `style` | `CSSProperties` | - | 根节点内联样式,可用于传入主题 CSS 变量。 |
138
138
  | `ariaLabel` | `string` | `Table` | 表格区域无障碍名称。 |
139
139
 
140
+ 插入和删除的位置计算、空行生成、乐观显示及目标行过滤均由组件处理,外部只需更新最终数组:
141
+
142
+ ```tsx
143
+ <Table
144
+ rows={rows}
145
+ columns={columns}
146
+ onInsertRows={setRows}
147
+ onDeleteRows={setRows}
148
+ />
149
+ ```
150
+
140
151
  ## 高亮和范围选择
141
152
 
142
153
  编辑高亮、插入行高亮和鼠标拖动多单元格选择默认都不开启。需要这些视觉反馈或批量选择能力时,通过表格级配置显式开启:
@@ -197,10 +208,10 @@ export function Example() {
197
208
  | `width` | `number` | `140` | 列宽,单位 px。最小宽度由内部布局保护。 |
198
209
  | `fixed` | `'left' \| 'right'` | - | 固定列位置。左/右固定列会覆盖滚动列并显示阴影。 |
199
210
  | `align` | `'left' \| 'center' \| 'right'` | `'left'` | 单元格文本和拖拽预览文本对齐方式。 |
200
- | `editable` | `boolean` | `false` | 是否允许双击或按 Enter 进入编辑。 |
211
+ | `editable` | `boolean \| ((value: unknown, row: Row, rowIndex: number) => boolean)` | `false` | 是否允许双击、Enter 或右键菜单进入编辑。函数按当前单元格动态判断,提交时再次检查权限。 |
201
212
  | `editor` | `EditorConfig` | `{ type: 'text' }` | 内置编辑器配置。详见下方编辑器表。未配置时默认使用文本编辑器。 |
202
- | `sortable` | `boolean` | `true` | 表头显示排序按钮,并触发`onSortStateChange`。普通数据列默认开启,传 `false` 可关闭。 |
203
- | `filterable` | `boolean` | `true` | 表头显示筛选按钮,并触发`onFilterValuesChange`。普通数据列默认开启,传 `false` 可关闭。 |
213
+ | `sortable` | `boolean` | `true` | 表头显示排序按钮。普通数据列默认开启,传 `false` 可关闭。 |
214
+ | `filterable` | `boolean` | `true` | 表头显示筛选按钮。普通数据列默认开启,传 `false` 可关闭。 |
204
215
  | `formatter` | `(value, row, rowIndex) => string` | - | 自定义展示文本。异常会被隔离并显示渲染错误文案。 |
205
216
  | `renderCell` | `(value, row, rowIndex) => ReactNode` | - | 自定义单元格 React 内容。适合标签、徽标、图标组合等内置编辑器无法表达的展示。 |
206
217
  | `summary` | `boolean \| ((rows, column) => ReactNode)` | `false` | 是否在合计行显示该列合计。传`true` 自动累加数字值;传函数可自定义合计内容。 |
@@ -457,60 +468,44 @@ const columns: GridColumn<Row>[] = [
457
468
 
458
469
  ## 排序和筛选
459
470
 
460
- 组件只负责展示排序/筛选交互,不会直接改变 `rows`。业务侧需要根据回调结果自行排序、过滤。
471
+ 排序和筛选状态均由组件内部管理。默认排序会比较数值或单元格显示文本;默认筛选按照显示文本执行不区分大小写的包含匹配。需要其他规则时,分别通过 `onSortChange` 和 `onFilterChange` 返回处理后的行。
461
472
 
462
473
  ```tsx
463
- const [sortState, setSortState] = useState<GridSortState | null>(null);
464
- const [filterValues, setFilterValues] = useState<Record<string, string>>({});
465
-
466
- const visibleRows = useMemo(() => {
467
- const filtered = rows.filter((row) =>
468
- Object.entries(filterValues).every(([key, value]) =>
469
- String(row[key as keyof Row] ?? '').includes(value),
470
- ),
471
- );
472
-
473
- if (!sortState) return filtered;
474
-
475
- return filtered.slice().sort((a, b) => {
476
- const left = a[sortState.columnKey as keyof Row];
477
- const right = b[sortState.columnKey as keyof Row];
478
- const result = String(left).localeCompare(String(right));
479
- return sortState.direction === 'asc' ? result : -result;
480
- });
481
- }, [rows, sortState, filterValues]);
482
-
483
474
  <Table
484
475
  columns={columns}
485
- rows={visibleRows}
486
- sortState={sortState}
487
- filterValues={filterValues}
488
- onSortStateChange={setSortState}
489
- onFilterValuesChange={setFilterValues}
476
+ rows={rows}
477
+ onSortChange={(currentRows, state) => {
478
+ const direction = state.direction === 'asc' ? 1 : -1;
479
+ return currentRows.slice().sort((left, right) =>
480
+ String(left[state.columnKey as keyof Row]).localeCompare(
481
+ String(right[state.columnKey as keyof Row]),
482
+ ) * direction,
483
+ );
484
+ }}
485
+ onFilterChange={(currentRows, values) =>
486
+ currentRows.filter((row) =>
487
+ Object.entries(values).every(([key, value]) =>
488
+ String(row[key as keyof Row] ?? '').startsWith(value),
489
+ ),
490
+ )
491
+ }
490
492
  />;
491
493
  ```
492
494
 
493
495
  ## 拖拽和列宽
494
496
 
495
- 拖拽排序采用外部受控数据模式:组件告诉你发生了什么,真正的 `rows` 或 `columns` 更新由你完成。列宽调整支持非受控使用,只传 `columnResizable` 即可拖动;如果需要持久化宽度,可以监听 `onColumnResize` 并同步到你的列配置。
497
+ 拖拽后组件会在内部维护新的行列顺序,因此排序回调不是必需的。需要持久化或同步结果时,可以通过回调直接取得排序后的 `rows` 或 `columns`。列宽调整默认开启并支持非受控使用,传 `columnResizable={false}` 可关闭。
496
498
 
497
- `columnDraggable` 默认开启,普通列会自动显示表头拖拽入口。行选择和行拖拽是表格级配置:开启 `rowSelection` 后自动生成左侧选择列,开启 `rowDraggable` 后自动生成左侧拖拽手柄列,不需要在 `columns` 里配置 `rowSelection` 或 `rowDragHandle`。
499
+ `columnDraggable` 默认开启,普通列会自动显示表头拖拽入口。行选择和行拖拽是表格级配置:开启 `rowSelection` 后默认自动生成左侧选择列(`showCheckbox: false` 时隐藏),开启 `rowDraggable` 后自动生成左侧拖拽手柄列,不需要在 `columns` 里配置 `rowSelection` 或 `rowDragHandle`。
498
500
 
499
501
  ```tsx
500
502
  <Table
501
503
  columns={columns}
502
504
  rows={rows}
503
505
  rowSelection={{ mode: 'multiple' }}
504
- columnResizable
505
506
  rowDraggable
506
- onColumnOrderChange={(sourceIndex, targetIndex) => {
507
- setColumns((current) => {
508
- const next = current.slice();
509
- const [column] = next.splice(sourceIndex, 1);
510
- next.splice(targetIndex, 0, column);
511
- return next;
512
- });
513
- }}
507
+ onColumnsReorder={(nextColumns) => saveColumns(nextColumns)}
508
+ onRowsReorder={(nextRows) => saveRows(nextRows)}
514
509
  onColumnResize={(columnKey, width) => {
515
510
  setColumns((current) =>
516
511
  current.map((column) =>
@@ -518,17 +513,23 @@ const visibleRows = useMemo(() => {
518
513
  ),
519
514
  );
520
515
  }}
521
- onRowOrderChange={(sourceIndex, targetIndex) => {
522
- setRows((current) => {
523
- const next = current.slice();
524
- const [row] = next.splice(sourceIndex, 1);
525
- next.splice(targetIndex, 0, row);
526
- return next;
527
- });
528
- }}
529
516
  />;
530
517
  ```
531
518
 
519
+ 隐藏选择框时不生成选择列,表头全选框也会隐藏;行选择状态、回调和右键菜单仍然可用。多选模式支持 Ctrl / Command 点击追加或取消选择,以及 Shift 点击连续选择。
520
+
521
+ ```tsx
522
+ <Table
523
+ columns={columns}
524
+ rows={rows}
525
+ rowNumber={{ fixed: false }}
526
+ rowSelection={{ mode: 'multiple', showCheckbox: false }}
527
+ onSelectedRowChange={(keys, selectedRows) => console.log(keys, selectedRows)}
528
+ />
529
+ ```
530
+
531
+ `rowNumber={true}` 或 `rowNumber={{ fixed: true }}` 固定序号列;`rowNumber={false}` 隐藏序号列。
532
+
532
533
  ## 虚拟渲染
533
534
 
534
535
  默认开启虚拟渲染,只绘制可见区域和少量缓冲区域。缓冲数量默认是 `4`,可以通过对象形式调整。
@@ -708,3 +709,76 @@ pnpm check
708
709
  ## License
709
710
 
710
711
  MIT
712
+
713
+ ## 单元格事件与动态编辑权限
714
+
715
+ ```tsx
716
+ const columns: TableColumn<MyRow>[] = [
717
+ {
718
+ key: 'name',
719
+ title: '姓名',
720
+ dataIndex: 'name',
721
+ editable: (_value, row, _rowIndex) => row.status === 'draft',
722
+ },
723
+ ];
724
+
725
+ <Table
726
+ columns={columns}
727
+ rows={rows}
728
+ onCellClick={(cell, event) => {
729
+ console.log(cell.row, cell.column, cell.value, cell.rowIndex, cell.columnIndex);
730
+ }}
731
+ onCellDoubleClick={(cell, event) => {
732
+ console.log(cell.rowKey, cell.columnKey);
733
+ // event.preventDefault(); // 阻止本次双击进入编辑
734
+ }}
735
+ />
736
+ ```
737
+
738
+ 事件只针对数据单元格,表头、序号列、选择列和拖拽手柄不触发;只读单元格也会触发。双击时先触发一次单击回调,再触发双击回调,不延迟单击。合并单元格返回其起始单元格,索引基于当前显示顺序,`columnIndex` 包含工具列。
739
+
740
+ `editable` 保持兼容布尔值;函数接收当前值、行数据和当前行索引,返回 `true` 才允许编辑。编辑期间权限变为 `false` 时,提交会关闭编辑器并丢弃草稿,不触发 `onCellChange`。
741
+
742
+ ## Ref 方法
743
+
744
+ 支持 React 18+ 的对象 ref 和回调 ref,保留行数据的泛型推导;`VelocityGrid` 别名同样支持。可导入 `TableRef<Row>`(或 `VelocityGridRef<Row>`)。
745
+
746
+ ```tsx
747
+ import { useRef } from 'react';
748
+ import { Table, type TableRef } from '@lensui/lens-table';
749
+
750
+ function Example() {
751
+ const tableRef = useRef<TableRef<MyRow>>(null);
752
+ return (
753
+ <>
754
+ <button onClick={() => tableRef.current?.scrollToCell({ rowKey: 1, columnKey: 'name' })}>
755
+ 定位
756
+ </button>
757
+ <button onClick={() => tableRef.current?.startEdit({ rowKey: 1, columnKey: 'name' })}>
758
+ 编辑
759
+ </button>
760
+ <Table ref={tableRef} columns={columns} rows={rows} rowSelection />
761
+ </>
762
+ );
763
+ }
764
+ ```
765
+
766
+ | 方法 | 说明 |
767
+ | --- | --- |
768
+ | `focus()` | 聚焦表格,启用键盘操作。 |
769
+ | `scrollToCell(target)` | 将目标数据单元格滚入可见区域,不改变选择;目标不存在时返回 `false`。 |
770
+ | `getSelectedCell()` | 返回当前单元格的 `TableCellContext<Row>`,未选择时返回 `null`。 |
771
+ | `setSelectedCell(target \| null)` | 设置或清除单元格选择,同时清除范围选择;无效目标返回 `false` 并保留原选择。 |
772
+ | `getSelectedRowKeys()` | 返回所选行 key 的数组副本。 |
773
+ | `getSelectedRows()` | 返回当前筛选和排序结果中选中的行数据,按显示顺序排列。 |
774
+ | `setSelectedRowKeys(keys)` | 设置行选择并触发现有回调;需要开启 `rowSelection`,单选模式只取第一个 key。 |
775
+ | `getSelectedColumnKeys()` | 返回所选列 key 的数组副本。 |
776
+ | `setSelectedColumnKeys(keys)` | 设置数据列选择,忽略无效列;需要开启 `columnSelection`,单选模式只取第一个有效 key。 |
777
+ | `clearSelection()` | 清空单元格、范围、行和列选择,并取消编辑。 |
778
+ | `startEdit(target?)` | 选择并编辑目标单元格;省略参数时编辑当前选择。目标无效或 `editable` 不允许时返回 `false`。 |
779
+ | `commitEdit()` | 提交当前草稿并关闭编辑器,遵循 `editable` 动态权限和 `onCellChange`。 |
780
+ | `cancelEdit()` | 丢弃当前草稿并关闭编辑器。 |
781
+
782
+ `target` 使用 `{ rowKey, columnKey }` 或 `{ rowIndex, columnKey }`;`rowKey` 优先,`rowIndex` 为当前显示顺序中的零基索引。工具列不能作为目标;合并单元格自动定位到起始单元格。
783
+
784
+ 选择方法遵守受控/非受控约定:传入 `selectedCell`、`selectedRowKeys` 或 `selectedColumnKeys` 后,需要在相应回调中更新这些 props。尤其是受控单元格选择下的 `startEdit`,外部需要接受新的选择才能保持编辑器打开。读取方法反映最近一次 React 渲染完成后的状态,不保证在调用设置方法的同一同步代码段内立即更新;返回的行和列对象是原数据引用。