Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions docs/assets/guide/en/cell_type/cellType.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,72 @@ The following shows an example of `cellType: ()=>{}`: (Please refer to [Example]
keepAspectRatio:true,
}
```

## Extension: Custom Column Types (TypeScript)

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:

- Permission or visibility control: `permissionKey`
- Tracking and analytics metadata: `trackId` / `trackParams`
- Unified rendering strategy flags: `highlightOnNegative` / `useTagStyle`
- DSL fields used by higher-level wrapper components: `renderAs` / `variant`

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.

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.

### 1) Define a custom column type

```ts
import type { HeaderDefine } from '@visactor/vtable';

// Custom column body definition (example: a customized button column)
export interface IColoredButtonColumnBody {
cellType: 'button';
field: string;

/** Business-specific extension field: highlight when the value is negative */
highlightOnNegative?: boolean;

text?: string;
style?: any;
}

// Full column definition (Header + Body)
export type ColoredButtonColumnDefine = IColoredButtonColumnBody & HeaderDefine;
```

### 2) Register the type via module augmentation

```ts
declare module '@visactor/vtable/es/ts-types' {
interface CustomColumnBodyDefineMap {
coloredButton: IColoredButtonColumnBody;
}

interface CustomColumnDefineMap {
coloredButtonColumn: ColoredButtonColumnDefine;
}
}
```

### 3) Use the custom column type in `columns`

```ts
import type { ColumnsDefine } from '@visactor/vtable';

const columns: ColumnsDefine = [
{
field: 'value',
title: 'Custom Button Column',
cellType: 'button',
text: 'check',
highlightOnNegative: true // ✅ extended field from CustomColumnDefineMap
}
];
```

> **Note:**
> The example above demonstrates a **type-level extension** only.
> 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).
> VTable itself does not interpret or handle these fields automatically.
68 changes: 67 additions & 1 deletion docs/assets/guide/zh/cell_type/cellType.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,70 @@ VTable 所支持的数据类型共有 7 种,分别为:
width:'auto',
keepAspectRatio:true,
}
```
```
## 扩展:自定义列类型(TypeScript)

在实际业务中,除了 VTable 内置的 `cellType`(text/link/image/video/progressbar/sparkline/chart 等),我们常常还需要在列定义里附加一些 **业务侧专用配置**,例如:

- 权限/可见性控制:`permissionKey`
- 埋点信息:`trackId` / `trackParams`
- 统一渲染策略开关:`highlightOnNegative` / `useTagStyle`
- 上层封装组件的 DSL 字段:`renderAs` / `variant`

这些字段通常不会被 VTable 内部直接消费,而是由业务侧封装层(如统一的 `cellRender`、hook、列工厂函数等)读取并实现对应行为。

为此,VTable 提供了一个 **类型扩展入口**,允许开发者通过 TypeScript 的 module augmentation(模块扩展)为 `ColumnDefine` 增加自定义列类型定义,从而获得完整的类型提示与校验能力。

### 1)定义自定义列类型

```ts
import type { HeaderDefine } from '@visactor/vtable';

// 自定义列 body 配置(示例:自定义按钮列)
export interface IColoredButtonColumnBody {
cellType: 'button';
field: string;

/** 业务侧扩展字段:当值为负数时高亮 */
highlightOnNegative?: boolean;

text?: string;
style?: any;
}

// 完整列定义(Header + Body)
export type ColoredButtonColumnDefine = IColoredButtonColumnBody & HeaderDefine;
```

### 2)通过模块扩展注册类型

```ts
declare module '@visactor/vtable/es/ts-types' {
interface CustomColumnBodyDefineMap {
coloredButton: IColoredButtonColumnBody;
}

interface CustomColumnDefineMap {
coloredButtonColumn: ColoredButtonColumnDefine;
}
}
```

### 3)在 columns 中使用

```ts
import type { ColumnsDefine } from '@visactor/vtable';

const columns: ColumnsDefine = [
{
field: 'value',
title: '自定义按钮列',
cellType: 'button',
text: 'check',
highlightOnNegative: true // ✅ 来自 CustomColumnDefineMap 的扩展字段
}
];
```

> 注意:以上示例展示的是 **类型系统层面的扩展能力**。
> `highlightOnNegative` 这类字段是否产生实际效果,需要由业务侧封装的渲染逻辑(例如自定义 `cellRender`、hook 或列工厂)去读取并执行。VTable 本身不会自动解析该字段。
4 changes: 4 additions & 0 deletions packages/vtable/examples/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,10 @@ export const menus = [
{
path: 'type',
name: 'button'
},
{
path: 'type',
name: 'custom'
}
]
},
Expand Down
123 changes: 123 additions & 0 deletions packages/vtable/examples/type/custom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* 单元格类型 - 自定义
*
* 通过 TypeScript module augmentation 扩展 ColumnDefine,
* 为内置 button 单元格增加一个自定义配置:highlightOnNegative。
* 注意:
* - 当前运行时并不会使用 highlightOnNegative 配置。
* - Demo 的目的只是说明:用户可以安全地为 ColumnDefine 增加自定义列类型及其专有字段。
*/

import * as VTable from '../../src';
import { bindDebugTool } from '../../src/scenegraph/debug-tool';

const ListTable = VTable.ListTable;
const CONTAINER_ID = 'vTable';

/**
* 这里演示库使用方如何通过模块扩展自定义列类型
* 注意:这个路径在真实使用场景下通常是 '@visactor/vtable/es/ts-types'
*/
declare module '../../src/ts-types/list-table/define' {
/**
* 为 ColumnBodyDefine 增加一个自定义按钮列配置
* - 在原有 button 能力基础上新增 highlightOnNegative
*/
interface CustomColumnBodyDefineMap {
coloredButton: {
cellType: 'button';
field?: string;
text?: string;
disable?: boolean;
/**
* 当单元格对应 value 为负数时是否高亮
* (运行时如何使用该配置由业务侧决定)
*/
highlightOnNegative?: boolean;
style?: any;
};
}

/**
* 给 ColumnDefine 增加一个完整的自定义列类型
* - 通常是 Header 配置 + Body 配置的组合
*/
interface CustomColumnDefineMap {
coloredButtonColumn: {
// Header 相关字段(简化示例,实际可根据需要补充)
title?: string;
width?: number | 'auto';

// Body 相关字段
field?: string;
cellType: 'button';
text?: string;
disable?: boolean;
highlightOnNegative?: boolean;
style?: any;
};
}
}

export function createTable() {
const option: VTable.ListTableConstructorOptions = {
container: document.getElementById(CONTAINER_ID),
columns: [
{
field: 'percent',
title: 'percent',
width: 120
},
{
field: 'value',
title: 'value',
width: 120
},
// ⬇️ 这里就是一个“自定义类型扩展后的按钮列”
{
field: 'value',
title: 'custom button',
width: 'auto',
cellType: 'button',
disable: false,
text: 'check',
// 这个属性是通过 CustomColumnDefineMap 扩展进来的,当前运行时并不会使用该配置。Demo 的目的只是说明:用户可以安全地为 ColumnDefine 增加自定义列类型及其专有字段。
highlightOnNegative: true,
style: {
color: '#FFF'
}
}
],
showFrozenIcon: true,
widthMode: 'standard',
defaultRowHeight: 80,
heightMode: 'autoHeight'
};

const instance = new ListTable(option);

const records = [
{ percent: '100%', value: 20 },
{ percent: '80%', value: 18 },
{ percent: '20%', value: 12 },
{ percent: '0%', value: 10 },
{ percent: '60%', value: 16 },
{ percent: '40%', value: 14 },
{ percent: '0%', value: -10 },
{ percent: '0%', value: -10 }
];

// 设置表格数据
instance.setRecords(records);

bindDebugTool(instance.scenegraph.stage as any, {
// customGrapicKeys: ['role', '_updateTag'],
});

instance.on(VTable.ListTable.EVENT_TYPE.BUTTON_CLICK, e => {
console.log(VTable.ListTable.EVENT_TYPE.BUTTON_CLICK, e.col, e.row, e.event);
});

// 只为了方便控制台调试用,不要拷贝到正式代码
(window as any).tableInstance = instance;
}
81 changes: 70 additions & 11 deletions packages/vtable/src/ts-types/list-table/define/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import type { IButtonColumnBodyDefine } from './button-define';

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

export type ColumnBodyDefine =
/**
* 内置列 body 类型联合
* 保留原有导出,方便使用者直接引用
*/
export type BuiltinColumnBodyDefine =
| ILinkColumnBodyDefine
| IImageColumnBodyDefine
| ISparklineColumnBodyDefine
Expand All @@ -24,6 +28,29 @@ export type ColumnBodyDefine =
| ITextColumnBodyDefine
| ISwitchColumnBodyDefine
| IButtonColumnBodyDefine;

/**
* 用户扩展的列 body 类型映射表。
*
* 使用者可以通过 module augmentation 扩展:
* ```typescript
* declare module '@visactor/vtable/es/ts-types' {
* interface CustomColumnBodyDefineMap {
* myCustom: IMyCustomColumnBodyDefine;
* }
* }
* ```
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CustomColumnBodyDefineMap {}

/**
* 所有 ColumnBodyDefine 的最终联合:
* - 内置列 body
* - 用户通过 CustomColumnBodyDefineMap 扩展的列 body
*/
export type ColumnBodyDefine = BuiltinColumnBodyDefine | CustomColumnBodyDefineMap[keyof CustomColumnBodyDefineMap];

export type TextColumnDefine = ITextColumnBodyDefine & HeaderDefine;
export type LinkColumnDefine = ILinkColumnBodyDefine & HeaderDefine;
export type ImageColumnDefine = IImageColumnBodyDefine & HeaderDefine;
Expand All @@ -35,12 +62,11 @@ export type ChartColumnDefine = IChartColumnBodyDefine & HeaderDefine;
export type CompositeColumnDefine = ICompositeColumnBodyDefine & HeaderDefine;
export type SwitchColumnDefine = ISwitchColumnBodyDefine & HeaderDefine;
export type ButtonColumnDefine = IButtonColumnBodyDefine & HeaderDefine;
// export type GroupColumnDefine = IChartColumnBodyDefine & HeaderDefine;
export type GroupColumnDefine = HeaderDefine & {
columns: ColumnsDefine;
hideColumnsSubHeader?: boolean;
};
export type ColumnDefine = Either<

/**
* 内置的「普通列」定义联合,不含分组列
*/
export type BuiltinSimpleColumnDefine =
| LinkColumnDefine
| ImageColumnDefine
| SparklineColumnDefine
Expand All @@ -51,10 +77,43 @@ export type ColumnDefine = Either<
| TextColumnDefine
| CompositeColumnDefine
| SwitchColumnDefine
| ButtonColumnDefine,
GroupColumnDefine
>;
| ButtonColumnDefine;

/**
* 用户扩展的完整列定义映射表。
*
* 使用者可以通过 module augmentation 扩展:
* ```typescript
* declare module '@visactor/vtable/es/ts-types' {
* interface CustomColumnDefineMap {
* myCustomColumn: MyCustomColumnDefine;
* }
* }
* ```
* 其中 MyCustomColumnDefine 一般形如:
* ```typescript
* type MyCustomColumnDefine = IMyCustomColumnBodyDefine & HeaderDefine;
* ```
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CustomColumnDefineMap {}

export type GroupColumnDefine = HeaderDefine & {
columns: ColumnsDefine;
hideColumnsSubHeader?: boolean;
};

/**
* 所有「普通列」(simple column) 的最终联合:
* - 内置列定义
* - 用户扩展列定义
*/
export type SimpleColumnDefine = BuiltinSimpleColumnDefine | CustomColumnDefineMap[keyof CustomColumnDefineMap];

// export type ColumnDefine = HeaderDefine & ColumnBodyDefine;
/**
* 最终导出的 ColumnDefine:
* - Either<普通列, 分组列>
*/
export type ColumnDefine = Either<SimpleColumnDefine, GroupColumnDefine>;

export type ColumnsDefine = ColumnDefine[];
Loading