diff --git a/packages/g6/src/elements/combos/base-combo.ts b/packages/g6/src/elements/combos/base-combo.ts
index 827337eb05a..2443d9ebe6e 100644
--- a/packages/g6/src/elements/combos/base-combo.ts
+++ b/packages/g6/src/elements/combos/base-combo.ts
@@ -23,14 +23,15 @@ import type { BaseNodeStyleProps } from '../nodes';
import { BaseNode } from '../nodes';
import { Icon, IconStyleProps } from '../shapes';
import { connectImage, dispatchPositionChange } from '../shapes/image';
+import { UnknownStruct } from '../../types/utility';
/**
* 组合通用样式配置项
*
* Common style props for combo
*/
-export interface BaseComboStyleProps
- extends BaseNodeStyleProps,
+export interface BaseComboStyleProps
+ extends Omit, 'childrenData'>,
Prefix<'collapsed', BaseStyleProps>,
Prefix<'collapsedMarker', CollapsedMarkerStyleProps> {
/**
@@ -60,7 +61,7 @@ export interface BaseComboStyleProps
*
* If the combo is collapsed, children may be empty, and the complete child element data can be obtained through childrenData
*/
- childrenData?: NodeLikeData[];
+ childrenData?: NodeLikeData[];
/**
* 组合的内边距,只在展开状态下生效
*
diff --git a/packages/g6/src/elements/combos/circle.ts b/packages/g6/src/elements/combos/circle.ts
index a1340cb6505..02d4f652596 100644
--- a/packages/g6/src/elements/combos/circle.ts
+++ b/packages/g6/src/elements/combos/circle.ts
@@ -7,29 +7,30 @@ import { subStyleProps } from '../../utils/prefix';
import { parseSize } from '../../utils/size';
import type { BaseComboStyleProps } from './base-combo';
import { BaseCombo } from './base-combo';
+import { UnknownStruct } from '../../types/utility';
/**
* 圆形组合样式配置项
*
* Circle combo style props
*/
-export interface CircleComboStyleProps extends BaseComboStyleProps {}
+export interface CircleComboStyleProps extends BaseComboStyleProps {}
/**
* 圆形组合
*
* Circle combo
*/
-export class CircleCombo extends BaseCombo {
- constructor(options: DisplayObjectConfig) {
+export class CircleCombo extends BaseCombo> {
+ constructor(options: DisplayObjectConfig>) {
super(options);
}
- protected drawKeyShape(attributes: Required, container: Group): GCircle | undefined {
+ protected drawKeyShape(attributes: Required>, container: Group): GCircle | undefined {
return this.upsert('key', GCircle, this.getKeyStyle(attributes), container);
}
- protected getKeyStyle(attributes: Required): GCircleStyleProps {
+ protected getKeyStyle(attributes: Required>): GCircleStyleProps {
const { collapsed } = attributes;
const keyStyle = super.getKeyStyle(attributes);
@@ -41,13 +42,13 @@ export class CircleCombo extends BaseCombo {
};
}
- protected getCollapsedKeySize(attributes: Required): STDSize {
+ protected getCollapsedKeySize(attributes: Required>): STDSize {
const [collapsedWidth, collapsedHeight] = parseSize(attributes.collapsedSize);
const collapsedR = Math.max(collapsedWidth, collapsedHeight) / 2;
return [collapsedR * 2, collapsedR * 2, 0];
}
- protected getExpandedKeySize(attributes: Required): STDSize {
+ protected getExpandedKeySize(attributes: Required>): STDSize {
const contentBBox = this.getContentBBox(attributes);
const [width, height] = getBBoxSize(contentBBox);
const expandedR = Math.sqrt(width ** 2 + height ** 2) / 2;
diff --git a/packages/g6/src/elements/combos/rect.ts b/packages/g6/src/elements/combos/rect.ts
index 9e6abc37beb..642028a4472 100644
--- a/packages/g6/src/elements/combos/rect.ts
+++ b/packages/g6/src/elements/combos/rect.ts
@@ -3,29 +3,30 @@ import { Rect as GRect, Group } from '@antv/g';
import { subStyleProps } from '../../utils/prefix';
import type { BaseComboStyleProps } from './base-combo';
import { BaseCombo } from './base-combo';
+import { UnknownStruct } from '../../types/utility';
/**
* 矩形组合样式配置项
*
* Rect combo style props
*/
-export interface RectComboStyleProps extends BaseComboStyleProps {}
+export interface RectComboStyleProps extends BaseComboStyleProps {}
/**
* 矩形组合
*
* Rect combo
*/
-export class RectCombo extends BaseCombo {
- constructor(options: DisplayObjectConfig) {
+export class RectCombo extends BaseCombo> {
+ constructor(options: DisplayObjectConfig>) {
super(options);
}
- protected drawKeyShape(attributes: Required, container: Group): GRect | undefined {
+ protected drawKeyShape(attributes: Required>, container: Group): GRect | undefined {
return this.upsert('key', GRect, this.getKeyStyle(attributes), container);
}
- protected getKeyStyle(attributes: Required): GRectStyleProps {
+ protected getKeyStyle(attributes: Required>): GRectStyleProps {
const keyStyle = super.getKeyStyle(attributes);
const [width, height] = this.getKeySize(attributes);
diff --git a/packages/g6/src/elements/nodes/base-node.ts b/packages/g6/src/elements/nodes/base-node.ts
index 4ffc0bc155d..6a193dc0fe6 100644
--- a/packages/g6/src/elements/nodes/base-node.ts
+++ b/packages/g6/src/elements/nodes/base-node.ts
@@ -29,13 +29,15 @@ import { BaseElement } from '../base-element';
import type { BadgeStyleProps, BaseShapeStyleProps, IconStyleProps, LabelStyleProps } from '../shapes';
import { Badge, Icon, Label } from '../shapes';
import { connectImage, dispatchPositionChange } from '../shapes/image';
+import { UnknownStruct } from '../../types/utility';
/**
* 节点通用样式配置项
*
* Base node style props
*/
-export interface BaseNodeStyleProps
+export interface BaseNodeStyleProps<
+ NodeType extends UnknownStruct = UnknownStruct>
extends BaseShapeStyleProps,
Prefix<'label', NodeLabelStyleProps>,
Prefix<'halo', BaseStyleProps>,
@@ -97,7 +99,7 @@ export interface BaseNodeStyleProps
* Only valid in the tree graph. If the current node is collapsed, children may be empty, and the complete child element data can be obtained through childrenData
* @ignore
*/
- childrenData?: NodeData[];
+ childrenData?: NodeData[];
/**
* 是否显示节点标签
*
diff --git a/packages/g6/src/runtime/data.ts b/packages/g6/src/runtime/data.ts
index ffb399206a7..98abc3abe44 100644
--- a/packages/g6/src/runtime/data.ts
+++ b/packages/g6/src/runtime/data.ts
@@ -29,9 +29,10 @@ import { positionOf } from '../utils/position';
import { format, print } from '../utils/print';
import { dfs } from '../utils/traverse';
import { add } from '../utils/vector';
+import { InferGraphDataTypes } from '../spec/graph';
-export class DataController {
- public model: GraphLib;
+export class DataController {
+ public model: GraphLib['node'], InferGraphDataTypes['combo']>, EdgeData['edge']>>;
/**
* 最近一次删除的 combo 的 id
@@ -132,13 +133,13 @@ export class DataController {
}
public getNodeData(ids?: ID[]) {
- return this.model.getAllNodes().reduce((acc, node) => {
- const data = toG6Data(node);
+ return this.model.getAllNodes().reduce['node']>[]>((acc, node) => {
+ const data = toG6Data(node) as NodeData['node']>;
if (this.isCombo(idOf(data))) return acc;
if (ids === undefined) acc.push(data);
else ids.includes(idOf(data)) && acc.push(data);
return acc;
- }, [] as NodeData[]);
+ }, []);
}
public getEdgeDatum(id: ID) {
@@ -146,23 +147,23 @@ export class DataController {
}
public getEdgeData(ids?: ID[]) {
- return this.model.getAllEdges().reduce((acc, edge) => {
+ return this.model.getAllEdges().reduce['edge']>[]>((acc, edge) => {
const data = toG6Data(edge);
if (ids === undefined) acc.push(data);
else ids.includes(idOf(data)) && acc.push(data);
return acc;
- }, [] as EdgeData[]);
+ }, []);
}
public getComboData(ids?: ID[]) {
- return this.model.getAllNodes().reduce((acc, combo) => {
+ return this.model.getAllNodes().reduce['combo']>[]>((acc, combo) => {
const data = toG6Data(combo);
if (!this.isCombo(idOf(data))) return acc;
- if (ids === undefined) acc.push(data as ComboData);
- else ids.includes(idOf(data)) && acc.push(data as ComboData);
+ if (ids === undefined) acc.push(data);
+ else ids.includes(idOf(data)) && acc.push(data);
return acc;
- }, [] as ComboData[]);
+ }, []);
}
public getRootsData(hierarchyKey: HierarchyKey = TREE_KEY) {
@@ -200,11 +201,11 @@ export class DataController {
return parent ? toG6Data(parent) : undefined;
}
- public getChildrenData(id: ID): NodeLikeData[] {
+ public getChildrenData(id: ID): NodeLikeData['node'], InferGraphDataTypes['combo']>[] {
const structureKey = this.getElementType(id) === 'node' ? TREE_KEY : COMBO_KEY;
const { model } = this;
if (!model.hasNode(id) || !model.hasTreeStructure(structureKey)) return [];
- return model.getChildren(id, structureKey).map(toG6Data);
+ return model.getChildren(id, structureKey).map(toG6Data) as NodeLikeData['node'], InferGraphDataTypes['combo']>[];
}
/**
@@ -336,7 +337,7 @@ export class DataController {
this.computeZIndex(data, 'add');
}
- public addNodeData(nodes: NodeData[] = []) {
+ public addNodeData(nodes: NodeData['node']>[] = []) {
if (!nodes.length) return;
this.model.addNodes(
nodes.map((node) => {
@@ -349,7 +350,7 @@ export class DataController {
this.computeZIndex({ nodes }, 'add');
}
- public addEdgeData(edges: EdgeData[] = []) {
+ public addEdgeData(edges: EdgeData['edge']>[] = []) {
if (!edges.length) return;
this.model.addEdges(
edges.map((edge) => {
@@ -361,7 +362,7 @@ export class DataController {
this.computeZIndex({ edges }, 'add');
}
- public addComboData(combos: ComboData[] = []) {
+ public addComboData(combos: ComboData['combo']>[] = []) {
if (!combos.length) return;
const { model } = this;
@@ -382,8 +383,8 @@ export class DataController {
this.computeZIndex({ combos }, 'add');
}
- public addChildrenData(parentId: ID, childrenData: NodeData[]) {
- const parentData = this.getNodeLikeDatum(parentId) as NodeData;
+ public addChildrenData(parentId: ID, childrenData: NodeData['node']>[]) {
+ const parentData = this.getNodeLikeDatum(parentId) as NodeData['node']>;
const childrenId = childrenData.map(idOf);
this.addNodeData(childrenData);
this.updateNodeData([{ id: parentId, children: [...(parentData.children || []), ...childrenId] }]);
@@ -562,17 +563,17 @@ export class DataController {
this.computeZIndex(data, 'update');
}
- public updateNodeData(nodes: PartialNodeLikeData[] = []) {
+ public updateNodeData(nodes: PartialNodeLikeData['node']>>[] = []) {
if (!nodes.length) return;
const { model } = this;
this.batch(() => {
- const modifiedNodes: NodeData[] = [];
+ const modifiedNodes: NodeData['node']>[] = [];
nodes.forEach((modifiedNode) => {
const id = idOf(modifiedNode);
- const originalNode = toG6Data(model.getNode(id));
+ const originalNode = toG6Data(model.getNode(id)) as NodeData['node']>;
if (isElementDataEqual(originalNode, modifiedNode)) return;
- const value = mergeElementsData(originalNode, modifiedNode);
+ const value = mergeElementsData['node']>>(originalNode, modifiedNode);
this.pushChange({ value, original: originalNode, type: ChangeType.NodeUpdated });
model.mergeNodeData(id, value);
modifiedNodes.push(value);
@@ -787,12 +788,12 @@ export class DataController {
const dy = ty - comboY;
const dz = tz - comboZ;
- dfs(
+ dfs['node'], InferGraphDataTypes['combo']>>(
combo,
(succeed) => {
const succeedId = idOf(succeed);
const [x, y, z] = positionOf(succeed);
- const value = mergeElementsData(succeed, {
+ const value = mergeElementsData['node']>>(succeed, {
style: { x: x + dx, y: y + dy, z: z + dz },
});
this.pushChange({
@@ -875,7 +876,7 @@ export class DataController {
const childData = toG6Data(child);
const childId = idOf(childData);
this.setParent(idOf(childData), grandParent, COMBO_KEY, false);
- const value = mergeElementsData(childData, {
+ const value = mergeElementsData['node']>>(childData, {
id: idOf(childData),
combo: grandParent,
});
diff --git a/packages/g6/src/runtime/graph.ts b/packages/g6/src/runtime/graph.ts
index 83d868d741c..4d6bf29b6d4 100644
--- a/packages/g6/src/runtime/graph.ts
+++ b/packages/g6/src/runtime/graph.ts
@@ -62,9 +62,10 @@ import { PluginController } from './plugin';
import { TransformController } from './transform';
import { RuntimeContext } from './types';
import { ViewportController } from './viewport';
+import { InferGraphDataTypes } from '../spec/graph';
-export class Graph extends EventEmitter {
- private options: GraphOptions = {};
+export class Graph extends EventEmitter {
+ private options: GraphOptions = {};
/**
* @internal
@@ -96,7 +97,7 @@ export class Graph extends EventEmitter {
model: new DataController(),
};
- constructor(options: GraphOptions) {
+ constructor(options: GraphOptions) {
super();
this._setOptions(Object.assign({}, Graph.defaultOptions, options), true);
this.context.graph = this;
@@ -112,7 +113,7 @@ export class Graph extends EventEmitter {
* @returns 配置项 | options
* @apiCategory option
*/
- public getOptions(): GraphOptions {
+ public getOptions(): GraphOptions {
return this.options;
}
@@ -127,11 +128,11 @@ export class Graph extends EventEmitter {
* To update devicePixelRatio and container properties, please destroy and recreate the instance
* @apiCategory option
*/
- public setOptions(options: GraphOptions): void {
+ public setOptions(options: GraphOptions): void {
this._setOptions(options, false);
}
- private _setOptions(options: GraphOptions, isInit: boolean) {
+ private _setOptions(options: GraphOptions, isInit: boolean) {
this.updateCanvas(options);
Object.assign(this.options, inferOptions(options));
@@ -186,7 +187,7 @@ export class Graph extends EventEmitter {
* @param zoomRange - 缩放区间 | zoom range
* @apiCategory viewport
*/
- public setZoomRange(zoomRange: GraphOptions['zoomRange']): void {
+ public setZoomRange(zoomRange: GraphOptions['zoomRange']): void {
this.options.zoomRange = zoomRange;
}
@@ -197,7 +198,7 @@ export class Graph extends EventEmitter {
* @returns 缩放区间 | zoom range
* @apiCategory viewport
*/
- public getZoomRange(): GraphOptions['zoomRange'] {
+ public getZoomRange(): GraphOptions['zoomRange'] {
return this.options.zoomRange;
}
@@ -244,7 +245,7 @@ export class Graph extends EventEmitter {
* The value of `options.combo`
* @apiCategory element
*/
- public setCombo(combo: ComboOptions): void {
+ public setCombo(combo: ComboOptions['node'], InferGraphDataTypes['combo']>): void {
this.options.combo = combo;
this.context.model.refreshData();
}
diff --git a/packages/g6/src/runtime/options.ts b/packages/g6/src/runtime/options.ts
index 0f328e7952d..69c82834ccb 100644
--- a/packages/g6/src/runtime/options.ts
+++ b/packages/g6/src/runtime/options.ts
@@ -1,4 +1,4 @@
-import type { GraphOptions } from '../spec';
+import type { GraphData, GraphOptions } from '../spec';
/**
* 基于用户传入的配置,推断出最终的配置
@@ -7,7 +7,7 @@ import type { GraphOptions } from '../spec';
* @param options - 用户传入的配置 | Configuration passed by the user
* @returns 最终的配置 | Final configuration
*/
-export function inferOptions(options: GraphOptions): GraphOptions {
+export function inferOptions(options: GraphOptions): GraphOptions {
const flow = [inferLayoutOptions];
return flow.reduce((acc, infer) => infer(acc), options);
}
@@ -19,7 +19,7 @@ export function inferOptions(options: GraphOptions): GraphOptions {
* @param options - 用户传入的配置 | Configuration passed by the user
* @returns 最终的配置 | Final configuration
*/
-function inferLayoutOptions(options: GraphOptions): GraphOptions {
+function inferLayoutOptions(options: GraphOptions): GraphOptions {
if (!options.layout) return options;
if (Array.isArray(options.layout)) return options;
if ('preLayout' in options.layout) return options;
diff --git a/packages/g6/src/spec/data.ts b/packages/g6/src/spec/data.ts
index 057c69c68a4..53e2844bfc1 100644
--- a/packages/g6/src/spec/data.ts
+++ b/packages/g6/src/spec/data.ts
@@ -2,6 +2,8 @@ import type { ID, State } from '../types';
import type { ComboStyle } from './element/combo';
import type { EdgeStyle } from './element/edge';
import type { NodeStyle } from './element/node';
+import type { UnknownStruct } from '../types/utility';
+
/**
* 图数据
*
@@ -26,25 +28,29 @@ import type { NodeStyle } from './element/node';
* }
* ```
*/
-export interface GraphData {
+export interface GraphData<
+ N extends UnknownStruct = UnknownStruct,
+ E extends UnknownStruct = UnknownStruct,
+ C extends UnknownStruct = UnknownStruct,
+> {
/**
* 节点数据
*
* node data
*/
- nodes?: NodeData[];
+ nodes?: NodeData[];
/**
* 边数据
*
* edge data
*/
- edges?: EdgeData[];
+ edges?: EdgeData[];
/**
* Combo 数据
*
* combo data
*/
- combos?: ComboData[];
+ combos?: ComboData[];
}
/**
@@ -52,7 +58,7 @@ export interface GraphData {
*
* Node data
*/
-export interface NodeData {
+export interface NodeData {
/**
* 节点 ID
*
@@ -74,7 +80,7 @@ export interface NodeData {
*
* Used to store custom data of the node, which can be obtained through callback functions in the style mapping
*/
- data?: Record;
+ data?: T;
/**
* 节点样式
*
@@ -121,7 +127,7 @@ export interface NodeData {
*
* Combo data
*/
-export interface ComboData {
+export interface ComboData {
/**
* Combo ID
*
@@ -143,7 +149,7 @@ export interface ComboData {
*
* Used to store custom data of the Combo, which can be obtained through callback functions in the style mapping
*/
- data?: Record;
+ data?: T;
/**
* Combo 样式
*
@@ -170,7 +176,7 @@ export interface ComboData {
*
* Edge data
*/
-export interface EdgeData {
+export interface EdgeData {
/**
* 边 ID
*
@@ -204,7 +210,7 @@ export interface EdgeData {
*
* Used to store custom data of the edge, which can be obtained through callback functions in the style mapping
*/
- data?: Record;
+ data?: T;
/**
* 边样式
*
diff --git a/packages/g6/src/spec/element/combo.ts b/packages/g6/src/spec/element/combo.ts
index 5f9d48bc67c..3e43b5da2e4 100644
--- a/packages/g6/src/spec/element/combo.ts
+++ b/packages/g6/src/spec/element/combo.ts
@@ -1,59 +1,101 @@
import type { AnimationOptions } from '../../animations/types';
-import type { BaseComboStyleProps } from '../../elements/combos';
+import type { BaseComboStyleProps, CircleComboStyleProps, RectComboStyleProps } from '../../elements/combos';
import type { Graph } from '../../runtime/graph';
+import { UnknownStruct } from '../../types/utility';
import type { ComboData } from '../data';
import type { AnimationStage } from './animation';
import type { PaletteOptions } from './palette';
+type ComboDataFn = (this: Graph, datum: ComboData) => R;
+
+type StyleFieldComboDataFn = {
+ [K in keyof S]?: S[K] | ComboDataFn;
+};
+
+type ComboOptionsBuilder<
+ NodeType extends UnknownStruct = UnknownStruct,
+ ComboType extends UnknownStruct = UnknownStruct,
+ Type extends string = string,
+ Style extends Partial> = Partial>
+> = {
+ /**
+ * 组合类型
+ *
+ * Combo type
+ */
+ type?: Type | ComboDataFn;
+ /**
+ * 组合样式
+ *
+ * Combo style
+ */
+ style?:
+ | Style
+ | ComboDataFn