Skip to content

Commit aa26cb1

Browse files
authored
Merge pull request #4824 from VisActor/feat/extend-column-define-custom-type
Feat/extend column define custom type
2 parents e48ef56 + 81888f0 commit aa26cb1

5 files changed

Lines changed: 333 additions & 12 deletions

File tree

docs/assets/guide/en/cell_type/cellType.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,72 @@ The following shows an example of `cellType: ()=>{}`: (Please refer to [Example]
253253
keepAspectRatio:true,
254254
}
255255
```
256+
257+
## Extension: Custom Column Types (TypeScript)
258+
259+
In real-world applications, in addition to VTable’s built-in `cellType` values (such as `text`, `link`, `image`, `video`, `progressbar`, `sparkline`, and `chart`), it is often necessary to attach **business-specific configurations** to column definitions, for example:
260+
261+
- Permission or visibility control: `permissionKey`
262+
- Tracking and analytics metadata: `trackId` / `trackParams`
263+
- Unified rendering strategy flags: `highlightOnNegative` / `useTagStyle`
264+
- DSL fields used by higher-level wrapper components: `renderAs` / `variant`
265+
266+
These fields are usually **not consumed directly by VTable itself**. Instead, they are read and handled by business-layer abstractions, such as a unified `cellRender`, custom hooks, or column factory utilities.
267+
268+
To support this use case, VTable provides a **type-level extension mechanism** that allows developers to extend `ColumnDefine` via TypeScript _module augmentation_. This enables defining custom column types with full type inference, validation, and IDE autocomplete support.
269+
270+
### 1) Define a custom column type
271+
272+
```ts
273+
import type { HeaderDefine } from '@visactor/vtable';
274+
275+
// Custom column body definition (example: a customized button column)
276+
export interface IColoredButtonColumnBody {
277+
cellType: 'button';
278+
field: string;
279+
280+
/** Business-specific extension field: highlight when the value is negative */
281+
highlightOnNegative?: boolean;
282+
283+
text?: string;
284+
style?: any;
285+
}
286+
287+
// Full column definition (Header + Body)
288+
export type ColoredButtonColumnDefine = IColoredButtonColumnBody & HeaderDefine;
289+
```
290+
291+
### 2) Register the type via module augmentation
292+
293+
```ts
294+
declare module '@visactor/vtable/es/ts-types' {
295+
interface CustomColumnBodyDefineMap {
296+
coloredButton: IColoredButtonColumnBody;
297+
}
298+
299+
interface CustomColumnDefineMap {
300+
coloredButtonColumn: ColoredButtonColumnDefine;
301+
}
302+
}
303+
```
304+
305+
### 3) Use the custom column type in `columns`
306+
307+
```ts
308+
import type { ColumnsDefine } from '@visactor/vtable';
309+
310+
const columns: ColumnsDefine = [
311+
{
312+
field: 'value',
313+
title: 'Custom Button Column',
314+
cellType: 'button',
315+
text: 'check',
316+
highlightOnNegative: true // ✅ extended field from CustomColumnDefineMap
317+
}
318+
];
319+
```
320+
321+
> **Note:**
322+
> The example above demonstrates a **type-level extension** only.
323+
> Whether fields such as `highlightOnNegative` produce actual visual or behavioral effects depends on business-side rendering logic (e.g., custom `cellRender`, hooks, or column factories).
324+
> VTable itself does not interpret or handle these fields automatically.

docs/assets/guide/zh/cell_type/cellType.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,70 @@ VTable 所支持的数据类型共有 7 种,分别为:
254254
width:'auto',
255255
keepAspectRatio:true,
256256
}
257-
```
257+
```
258+
## 扩展:自定义列类型(TypeScript)
259+
260+
在实际业务中,除了 VTable 内置的 `cellType`(text/link/image/video/progressbar/sparkline/chart 等),我们常常还需要在列定义里附加一些 **业务侧专用配置**,例如:
261+
262+
- 权限/可见性控制:`permissionKey`
263+
- 埋点信息:`trackId` / `trackParams`
264+
- 统一渲染策略开关:`highlightOnNegative` / `useTagStyle`
265+
- 上层封装组件的 DSL 字段:`renderAs` / `variant`
266+
267+
这些字段通常不会被 VTable 内部直接消费,而是由业务侧封装层(如统一的 `cellRender`、hook、列工厂函数等)读取并实现对应行为。
268+
269+
为此,VTable 提供了一个 **类型扩展入口**,允许开发者通过 TypeScript 的 module augmentation(模块扩展)为 `ColumnDefine` 增加自定义列类型定义,从而获得完整的类型提示与校验能力。
270+
271+
### 1)定义自定义列类型
272+
273+
```ts
274+
import type { HeaderDefine } from '@visactor/vtable';
275+
276+
// 自定义列 body 配置(示例:自定义按钮列)
277+
export interface IColoredButtonColumnBody {
278+
cellType: 'button';
279+
field: string;
280+
281+
/** 业务侧扩展字段:当值为负数时高亮 */
282+
highlightOnNegative?: boolean;
283+
284+
text?: string;
285+
style?: any;
286+
}
287+
288+
// 完整列定义(Header + Body)
289+
export type ColoredButtonColumnDefine = IColoredButtonColumnBody & HeaderDefine;
290+
```
291+
292+
### 2)通过模块扩展注册类型
293+
294+
```ts
295+
declare module '@visactor/vtable/es/ts-types' {
296+
interface CustomColumnBodyDefineMap {
297+
coloredButton: IColoredButtonColumnBody;
298+
}
299+
300+
interface CustomColumnDefineMap {
301+
coloredButtonColumn: ColoredButtonColumnDefine;
302+
}
303+
}
304+
```
305+
306+
### 3)在 columns 中使用
307+
308+
```ts
309+
import type { ColumnsDefine } from '@visactor/vtable';
310+
311+
const columns: ColumnsDefine = [
312+
{
313+
field: 'value',
314+
title: '自定义按钮列',
315+
cellType: 'button',
316+
text: 'check',
317+
highlightOnNegative: true // ✅ 来自 CustomColumnDefineMap 的扩展字段
318+
}
319+
];
320+
```
321+
322+
> 注意:以上示例展示的是 **类型系统层面的扩展能力**
323+
> `highlightOnNegative` 这类字段是否产生实际效果,需要由业务侧封装的渲染逻辑(例如自定义 `cellRender`、hook 或列工厂)去读取并执行。VTable 本身不会自动解析该字段。

packages/vtable/examples/menu.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,10 @@ export const menus = [
563563
{
564564
path: 'type',
565565
name: 'button'
566+
},
567+
{
568+
path: 'type',
569+
name: 'custom'
566570
}
567571
]
568572
},
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* 单元格类型 - 自定义
3+
*
4+
* 通过 TypeScript module augmentation 扩展 ColumnDefine,
5+
* 为内置 button 单元格增加一个自定义配置:highlightOnNegative。
6+
* 注意:
7+
* - 当前运行时并不会使用 highlightOnNegative 配置。
8+
* - Demo 的目的只是说明:用户可以安全地为 ColumnDefine 增加自定义列类型及其专有字段。
9+
*/
10+
11+
import * as VTable from '../../src';
12+
import { bindDebugTool } from '../../src/scenegraph/debug-tool';
13+
14+
const ListTable = VTable.ListTable;
15+
const CONTAINER_ID = 'vTable';
16+
17+
/**
18+
* 这里演示库使用方如何通过模块扩展自定义列类型
19+
* 注意:这个路径在真实使用场景下通常是 '@visactor/vtable/es/ts-types'
20+
*/
21+
declare module '../../src/ts-types/list-table/define' {
22+
/**
23+
* 为 ColumnBodyDefine 增加一个自定义按钮列配置
24+
* - 在原有 button 能力基础上新增 highlightOnNegative
25+
*/
26+
interface CustomColumnBodyDefineMap {
27+
coloredButton: {
28+
cellType: 'button';
29+
field?: string;
30+
text?: string;
31+
disable?: boolean;
32+
/**
33+
* 当单元格对应 value 为负数时是否高亮
34+
* (运行时如何使用该配置由业务侧决定)
35+
*/
36+
highlightOnNegative?: boolean;
37+
style?: any;
38+
};
39+
}
40+
41+
/**
42+
* 给 ColumnDefine 增加一个完整的自定义列类型
43+
* - 通常是 Header 配置 + Body 配置的组合
44+
*/
45+
interface CustomColumnDefineMap {
46+
coloredButtonColumn: {
47+
// Header 相关字段(简化示例,实际可根据需要补充)
48+
title?: string;
49+
width?: number | 'auto';
50+
51+
// Body 相关字段
52+
field?: string;
53+
cellType: 'button';
54+
text?: string;
55+
disable?: boolean;
56+
highlightOnNegative?: boolean;
57+
style?: any;
58+
};
59+
}
60+
}
61+
62+
export function createTable() {
63+
const option: VTable.ListTableConstructorOptions = {
64+
container: document.getElementById(CONTAINER_ID),
65+
columns: [
66+
{
67+
field: 'percent',
68+
title: 'percent',
69+
width: 120
70+
},
71+
{
72+
field: 'value',
73+
title: 'value',
74+
width: 120
75+
},
76+
// ⬇️ 这里就是一个“自定义类型扩展后的按钮列”
77+
{
78+
field: 'value',
79+
title: 'custom button',
80+
width: 'auto',
81+
cellType: 'button',
82+
disable: false,
83+
text: 'check',
84+
// 这个属性是通过 CustomColumnDefineMap 扩展进来的,当前运行时并不会使用该配置。Demo 的目的只是说明:用户可以安全地为 ColumnDefine 增加自定义列类型及其专有字段。
85+
highlightOnNegative: true,
86+
style: {
87+
color: '#FFF'
88+
}
89+
}
90+
],
91+
showFrozenIcon: true,
92+
widthMode: 'standard',
93+
defaultRowHeight: 80,
94+
heightMode: 'autoHeight'
95+
};
96+
97+
const instance = new ListTable(option);
98+
99+
const records = [
100+
{ percent: '100%', value: 20 },
101+
{ percent: '80%', value: 18 },
102+
{ percent: '20%', value: 12 },
103+
{ percent: '0%', value: 10 },
104+
{ percent: '60%', value: 16 },
105+
{ percent: '40%', value: 14 },
106+
{ percent: '0%', value: -10 },
107+
{ percent: '0%', value: -10 }
108+
];
109+
110+
// 设置表格数据
111+
instance.setRecords(records);
112+
113+
bindDebugTool(instance.scenegraph.stage as any, {
114+
// customGrapicKeys: ['role', '_updateTag'],
115+
});
116+
117+
instance.on(VTable.ListTable.EVENT_TYPE.BUTTON_CLICK, e => {
118+
console.log(VTable.ListTable.EVENT_TYPE.BUTTON_CLICK, e.col, e.row, e.event);
119+
});
120+
121+
// 只为了方便控制台调试用,不要拷贝到正式代码
122+
(window as any).tableInstance = instance;
123+
}

packages/vtable/src/ts-types/list-table/define/index.ts

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import type { IButtonColumnBodyDefine } from './button-define';
1313

1414
export type HeaderDefine = IImageHeaderDefine | ILinkHeaderDefine | ICheckboxHeaderDefine | ITextHeaderDefine;
1515

16-
export type ColumnBodyDefine =
16+
/**
17+
* 内置列 body 类型联合
18+
* 保留原有导出,方便使用者直接引用
19+
*/
20+
export type BuiltinColumnBodyDefine =
1721
| ILinkColumnBodyDefine
1822
| IImageColumnBodyDefine
1923
| ISparklineColumnBodyDefine
@@ -24,6 +28,29 @@ export type ColumnBodyDefine =
2428
| ITextColumnBodyDefine
2529
| ISwitchColumnBodyDefine
2630
| IButtonColumnBodyDefine;
31+
32+
/**
33+
* 用户扩展的列 body 类型映射表。
34+
*
35+
* 使用者可以通过 module augmentation 扩展:
36+
* ```typescript
37+
* declare module '@visactor/vtable/es/ts-types' {
38+
* interface CustomColumnBodyDefineMap {
39+
* myCustom: IMyCustomColumnBodyDefine;
40+
* }
41+
* }
42+
* ```
43+
*/
44+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
45+
export interface CustomColumnBodyDefineMap {}
46+
47+
/**
48+
* 所有 ColumnBodyDefine 的最终联合:
49+
* - 内置列 body
50+
* - 用户通过 CustomColumnBodyDefineMap 扩展的列 body
51+
*/
52+
export type ColumnBodyDefine = BuiltinColumnBodyDefine | CustomColumnBodyDefineMap[keyof CustomColumnBodyDefineMap];
53+
2754
export type TextColumnDefine = ITextColumnBodyDefine & HeaderDefine;
2855
export type LinkColumnDefine = ILinkColumnBodyDefine & HeaderDefine;
2956
export type ImageColumnDefine = IImageColumnBodyDefine & HeaderDefine;
@@ -35,12 +62,11 @@ export type ChartColumnDefine = IChartColumnBodyDefine & HeaderDefine;
3562
export type CompositeColumnDefine = ICompositeColumnBodyDefine & HeaderDefine;
3663
export type SwitchColumnDefine = ISwitchColumnBodyDefine & HeaderDefine;
3764
export type ButtonColumnDefine = IButtonColumnBodyDefine & HeaderDefine;
38-
// export type GroupColumnDefine = IChartColumnBodyDefine & HeaderDefine;
39-
export type GroupColumnDefine = HeaderDefine & {
40-
columns: ColumnsDefine;
41-
hideColumnsSubHeader?: boolean;
42-
};
43-
export type ColumnDefine = Either<
65+
66+
/**
67+
* 内置的「普通列」定义联合,不含分组列
68+
*/
69+
export type BuiltinSimpleColumnDefine =
4470
| LinkColumnDefine
4571
| ImageColumnDefine
4672
| SparklineColumnDefine
@@ -51,10 +77,43 @@ export type ColumnDefine = Either<
5177
| TextColumnDefine
5278
| CompositeColumnDefine
5379
| SwitchColumnDefine
54-
| ButtonColumnDefine,
55-
GroupColumnDefine
56-
>;
80+
| ButtonColumnDefine;
81+
82+
/**
83+
* 用户扩展的完整列定义映射表。
84+
*
85+
* 使用者可以通过 module augmentation 扩展:
86+
* ```typescript
87+
* declare module '@visactor/vtable/es/ts-types' {
88+
* interface CustomColumnDefineMap {
89+
* myCustomColumn: MyCustomColumnDefine;
90+
* }
91+
* }
92+
* ```
93+
* 其中 MyCustomColumnDefine 一般形如:
94+
* ```typescript
95+
* type MyCustomColumnDefine = IMyCustomColumnBodyDefine & HeaderDefine;
96+
* ```
97+
*/
98+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
99+
export interface CustomColumnDefineMap {}
100+
101+
export type GroupColumnDefine = HeaderDefine & {
102+
columns: ColumnsDefine;
103+
hideColumnsSubHeader?: boolean;
104+
};
105+
106+
/**
107+
* 所有「普通列」(simple column) 的最终联合:
108+
* - 内置列定义
109+
* - 用户扩展列定义
110+
*/
111+
export type SimpleColumnDefine = BuiltinSimpleColumnDefine | CustomColumnDefineMap[keyof CustomColumnDefineMap];
57112

58-
// export type ColumnDefine = HeaderDefine & ColumnBodyDefine;
113+
/**
114+
* 最终导出的 ColumnDefine:
115+
* - Either<普通列, 分组列>
116+
*/
117+
export type ColumnDefine = Either<SimpleColumnDefine, GroupColumnDefine>;
59118

60119
export type ColumnsDefine = ColumnDefine[];

0 commit comments

Comments
 (0)