From 4e30dee3c63af3bc7007a641ed4518f353c0d20e Mon Sep 17 00:00:00 2001 From: imsunhao Date: Wed, 10 Dec 2025 17:24:10 +0800 Subject: [PATCH 1/3] feat(types): add extensible custom column define support via module augmentation --- .../src/ts-types/list-table/define/index.ts | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/vtable/src/ts-types/list-table/define/index.ts b/packages/vtable/src/ts-types/list-table/define/index.ts index 221e6ddae0..42897cf308 100644 --- a/packages/vtable/src/ts-types/list-table/define/index.ts +++ b/packages/vtable/src/ts-types/list-table/define/index.ts @@ -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 @@ -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; @@ -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 @@ -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; export type ColumnsDefine = ColumnDefine[]; From dca64ae471bc643c2834f5b22243ff538a9ae233 Mon Sep 17 00:00:00 2001 From: imsunhao Date: Thu, 11 Dec 2025 10:34:16 +0800 Subject: [PATCH 2/3] docs(demo): add custom column type demo showing TypeScript extension (no runtime changes) --- packages/vtable/examples/menu.ts | 4 + packages/vtable/examples/type/custom.ts | 123 ++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 packages/vtable/examples/type/custom.ts diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index d973e2cec7..fc087250ab 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -563,6 +563,10 @@ export const menus = [ { path: 'type', name: 'button' + }, + { + path: 'type', + name: 'custom' } ] }, diff --git a/packages/vtable/examples/type/custom.ts b/packages/vtable/examples/type/custom.ts new file mode 100644 index 0000000000..38cffbf50f --- /dev/null +++ b/packages/vtable/examples/type/custom.ts @@ -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; +} From 4dd782e1ae1df511f863d86316ce585f09c57090 Mon Sep 17 00:00:00 2001 From: imsunhao Date: Fri, 12 Dec 2025 16:31:57 +0800 Subject: [PATCH 3/3] docs: add guide for extending custom column types via TypeScript --- docs/assets/guide/en/cell_type/cellType.md | 69 ++++++++++++++++++++++ docs/assets/guide/zh/cell_type/cellType.md | 68 ++++++++++++++++++++- 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/docs/assets/guide/en/cell_type/cellType.md b/docs/assets/guide/en/cell_type/cellType.md index ac47c97a43..961298f3c5 100644 --- a/docs/assets/guide/en/cell_type/cellType.md +++ b/docs/assets/guide/en/cell_type/cellType.md @@ -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. diff --git a/docs/assets/guide/zh/cell_type/cellType.md b/docs/assets/guide/zh/cell_type/cellType.md index 92abf612c4..c3d61bf7f6 100644 --- a/docs/assets/guide/zh/cell_type/cellType.md +++ b/docs/assets/guide/zh/cell_type/cellType.md @@ -254,4 +254,70 @@ VTable 所支持的数据类型共有 7 种,分别为: width:'auto', keepAspectRatio:true, } -``` \ No newline at end of file +``` +## 扩展:自定义列类型(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 本身不会自动解析该字段。