mirror of
https://gitee.com/antv/g6.git
synced 2024-12-02 11:48:29 +08:00
Merge pull request #1029 from antvis/g6-dev-20191209
feat: update types file
This commit is contained in:
commit
ccdeb3ae84
20
jest.config.js
Normal file
20
jest.config.js
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
module.exports = {
|
||||
runner: 'jest-electron/runner',
|
||||
testEnvironment: 'jest-electron/environment',
|
||||
preset: 'ts-jest',
|
||||
collectCoverage: false,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.{ts,js}',
|
||||
'!**/node_modules/**',
|
||||
'!**/vendor/**'
|
||||
],
|
||||
testRegex: '/tests/.*-spec\\.ts?$',
|
||||
moduleDirectories: [ 'node_modules', 'src' ],
|
||||
moduleFileExtensions: [ 'js', 'ts', 'json' ],
|
||||
moduleNameMapper: {
|
||||
'@g6/(.*)': '<rootDir>/src/$1',
|
||||
'@g6/types': '<rootDir>/types'
|
||||
}
|
||||
};
|
14
package.json
14
package.json
@ -26,7 +26,7 @@
|
||||
"clean": "rimraf esm lib dist",
|
||||
"lint": "lint-staged",
|
||||
"test": "jest",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/behavior/index-spec.ts",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/graph/controller/mode-spec.ts",
|
||||
"coverage": "jest --coverage",
|
||||
"ci": "run-s build coverage",
|
||||
"doc": "rimraf apis && typedoc",
|
||||
@ -81,18 +81,6 @@
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"runner": "jest-electron/runner",
|
||||
"testEnvironment": "jest-electron/environment",
|
||||
"preset": "ts-jest",
|
||||
"collectCoverage": false,
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{ts,js}",
|
||||
"!**/node_modules/**",
|
||||
"!**/vendor/**"
|
||||
],
|
||||
"testRegex": "/tests/.*-spec\\.ts?$"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/antvis/g6"
|
||||
|
@ -1,10 +1,10 @@
|
||||
import clone from '@antv/util/lib/clone'
|
||||
import { IBehaviorOpation } from '../../types';
|
||||
import { BehaviorOpation } from '@g6/types';
|
||||
import BehaviorOption from './behaviorOption'
|
||||
|
||||
export default class Behavior {
|
||||
private static types = {}
|
||||
public static registerBehavior<T, U>(type: string, behavior: IBehaviorOpation<U>) {
|
||||
public static registerBehavior<T, U>(type: string, behavior: BehaviorOpation<U>) {
|
||||
if(!behavior) {
|
||||
throw new Error(`please specify handler for this behavior: ${type}`)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import each from '@antv/util/lib/each'
|
||||
import wrapBehavior from '@antv/util/lib/wrap-behavior'
|
||||
import { G6Event } from '../../types';
|
||||
import { IGraph } from '../interface/graph'
|
||||
import { IGraph } from '@g6/interface/graph'
|
||||
import { G6Event } from '@g6/types';
|
||||
|
||||
export default class BehaviorOption {
|
||||
private _events = null
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { G6Event, IG6GraphEvent } from "../../types";
|
||||
import { cloneEvent } from '../util/base'
|
||||
import { G6Event, IG6GraphEvent } from "@g6/types";
|
||||
import { cloneEvent } from '@g6/util/base'
|
||||
const abs = Math.abs
|
||||
const DRAG_OFFSET = 10
|
||||
const ALLOW_EVENTS = [ 16, 17, 18 ]
|
||||
|
1
src/graph/controller/index.ts
Normal file
1
src/graph/controller/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Mode } from './mode'
|
175
src/graph/controller/mode.ts
Normal file
175
src/graph/controller/mode.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import each from '@antv/util/lib/each'
|
||||
import isArray from '@antv/util/lib/is-array'
|
||||
import isString from '@antv/util/lib/is-string'
|
||||
import Behavior from '@g6/behavior/behavior'
|
||||
import { IBehavior } from '@g6/interface/behavior';
|
||||
import { IGraph, IMode, IModeType } from '@g6/interface/graph';
|
||||
|
||||
export default class Mode {
|
||||
private graph: IGraph
|
||||
/**
|
||||
* modes = {
|
||||
* default: [ 'drag-node', 'zoom-canvas' ],
|
||||
* edit: [ 'drag-canvas', {
|
||||
* type: 'brush-select',
|
||||
* trigger: 'ctrl'
|
||||
* }]
|
||||
* }
|
||||
*
|
||||
* @private
|
||||
* @type {IMode}
|
||||
* @memberof Mode
|
||||
*/
|
||||
public modes: IMode
|
||||
|
||||
/**
|
||||
* mode = 'drag-node'
|
||||
*
|
||||
* @private
|
||||
* @type {string}
|
||||
* @memberof Mode
|
||||
*/
|
||||
public mode: string
|
||||
private currentBehaves: IBehavior[]
|
||||
constructor(graph: IGraph) {
|
||||
this.graph = graph
|
||||
this.modes = graph.get('modes') || {
|
||||
default: []
|
||||
}
|
||||
this.formatModes()
|
||||
|
||||
this.mode = graph.get('defaultMode') || 'default'
|
||||
this.currentBehaves = []
|
||||
|
||||
this.setMode(this.mode)
|
||||
}
|
||||
|
||||
private formatModes() {
|
||||
const modes = this.modes;
|
||||
each(modes, mode => {
|
||||
each(mode, (behavior, i) => {
|
||||
if (isString(behavior)) {
|
||||
mode[i] = { type: behavior };
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private setBehaviors(mode: string) {
|
||||
const graph = this.graph;
|
||||
const behaviors = this.modes[mode];
|
||||
const behaves: IBehavior[] = [];
|
||||
let behave: IBehavior;
|
||||
each(behaviors, behavior => {
|
||||
const BehaviorInstance = Behavior.getBehavior(behavior.type)
|
||||
if (!BehaviorInstance) {
|
||||
return;
|
||||
}
|
||||
behave = new BehaviorInstance(behavior);
|
||||
if(behave) {
|
||||
behave.bind(graph)
|
||||
behaves.push(behave);
|
||||
}
|
||||
});
|
||||
this.currentBehaves = behaves;
|
||||
}
|
||||
|
||||
private mergeBehaviors(modeBehaviors: IModeType[], behaviors: IModeType[]): IModeType[] {
|
||||
each(behaviors, behavior => {
|
||||
if (modeBehaviors.indexOf(behavior) < 0) {
|
||||
if (isString(behavior)) {
|
||||
behavior = { type: behavior };
|
||||
}
|
||||
modeBehaviors.push(behavior);
|
||||
}
|
||||
});
|
||||
return modeBehaviors;
|
||||
}
|
||||
|
||||
private filterBehaviors(modeBehaviors: IModeType[], behaviors: IModeType[]): IModeType[] {
|
||||
const result: IModeType[] = [];
|
||||
modeBehaviors.forEach(behavior => {
|
||||
let type: string = ''
|
||||
if(isString(behavior)) {
|
||||
type = behavior
|
||||
} else {
|
||||
type = behavior.type
|
||||
}
|
||||
if (behaviors.indexOf(type) < 0) {
|
||||
result.push(behavior);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public setMode(mode: string): Mode {
|
||||
const modes = this.modes;
|
||||
const graph = this.graph;
|
||||
const behaviors = modes[mode];
|
||||
if (!behaviors) {
|
||||
return;
|
||||
}
|
||||
graph.emit('beforemodechange', { mode });
|
||||
|
||||
each(this.currentBehaves, behave => {
|
||||
behave.unbind(graph);
|
||||
});
|
||||
|
||||
this.setBehaviors(mode);
|
||||
|
||||
graph.emit('aftermodechange', { mode });
|
||||
this.mode = mode;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态增加或删除 Behavior
|
||||
*
|
||||
* @param {IModeType[]} behaviors
|
||||
* @param {(IModeType[] | IModeType)} modes
|
||||
* @param {boolean} isAdd
|
||||
* @returns {Mode}
|
||||
* @memberof Mode
|
||||
*/
|
||||
public manipulateBehaviors(behaviors: IModeType[], modes: IModeType[] | IModeType, isAdd: boolean): Mode {
|
||||
const self = this
|
||||
if(!isArray(behaviors)) {
|
||||
behaviors = [ behaviors ]
|
||||
}
|
||||
|
||||
if(isArray(modes)) {
|
||||
each(modes, mode => {
|
||||
if (!self.modes[mode]) {
|
||||
if (isAdd) {
|
||||
self.modes[mode] = [].concat(behaviors);
|
||||
}
|
||||
} else {
|
||||
if (isAdd) {
|
||||
self.modes[mode] = this.mergeBehaviors(self.modes[mode], behaviors);
|
||||
} else {
|
||||
self.modes[mode] = this.filterBehaviors(self.modes[mode], behaviors);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
let currentMode: string = ''
|
||||
if(!modes) {
|
||||
currentMode = this.mode
|
||||
}
|
||||
|
||||
if (isAdd) {
|
||||
self.modes[currentMode] = this.mergeBehaviors(self.modes[currentMode], behaviors);
|
||||
} else {
|
||||
self.modes[currentMode] = this.filterBehaviors(self.modes[currentMode], behaviors);
|
||||
}
|
||||
|
||||
self.setMode(this.mode)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
13
src/graph/graph.ts
Normal file
13
src/graph/graph.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import EventEmitter from '@antv/event-emitter'
|
||||
import { GraphOptions, IGraph } from '@g6/interface/graph';
|
||||
|
||||
export default class Graph extends EventEmitter implements IGraph {
|
||||
private _cfg: GraphOptions
|
||||
constructor(cfg: GraphOptions) {
|
||||
super()
|
||||
this._cfg = cfg
|
||||
}
|
||||
public get(key: string) {
|
||||
return this._cfg[key]
|
||||
}
|
||||
}
|
0
src/graph/tree-graph.ts
Normal file
0
src/graph/tree-graph.ts
Normal file
@ -1,9 +1,19 @@
|
||||
|
||||
import GraphEvent from '@antv/g-base/lib/event/graph-event';
|
||||
import { IG6GraphEvent } from "../../types";
|
||||
import { DefaultBehaviorType, G6Event, IG6GraphEvent } from '@g6/types';
|
||||
import { IGraph } from './graph';
|
||||
import { IItem } from './item';
|
||||
|
||||
|
||||
export interface IBehavior {
|
||||
constructor: (cfg?: object) => void;
|
||||
getEvents: () => { [key in G6Event]?: string };
|
||||
shouldBegin: () => boolean;
|
||||
shouldUpdate: () => boolean;
|
||||
shouldEnd: () => boolean;
|
||||
bind: (graph: IGraph) => void;
|
||||
unbind: (graph: IGraph) => void;
|
||||
[key: string]: (...args: DefaultBehaviorType[]) => unknown;
|
||||
}
|
||||
|
||||
export class G6GraphEvent extends GraphEvent implements IG6GraphEvent {
|
||||
public item: IItem
|
||||
|
@ -1,7 +1,147 @@
|
||||
import { G6Event } from '../../types'
|
||||
import EventEmitter from '@antv/event-emitter';
|
||||
import { Easeing, ModelStyle, ShapeStyle } from '@g6/types'
|
||||
|
||||
export interface IGraph {
|
||||
on: (event: G6Event, handler: () => void) => void;
|
||||
off: (event: G6Event, handler: () => void) => void;
|
||||
export interface IModeOption {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export type IModeType = string | IModeOption
|
||||
|
||||
export interface IMode {
|
||||
default: IModeType[]
|
||||
[key: string]: IModeType[]
|
||||
}
|
||||
|
||||
export interface ILayoutOptions {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface GraphOptions {
|
||||
/**
|
||||
* 图的 DOM 容器,可以传入该 DOM 的 id 或者直接传入容器的 HTML 节点对象
|
||||
*/
|
||||
container: string | HTMLElement;
|
||||
/**
|
||||
* 指定画布宽度,单位为 'px'
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* 指定画布高度,单位为 'px'
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* 渲染引擎,支持canvas和svg。
|
||||
*/
|
||||
renderer?: 'canvas' | 'svg';
|
||||
|
||||
fitView?: boolean;
|
||||
|
||||
layout?: ILayoutOptions;
|
||||
|
||||
/**
|
||||
* 图适应画布时,指定四周的留白。
|
||||
* 可以是一个值, 例如:fitViewPadding: 20
|
||||
* 也可以是一个数组,例如:fitViewPadding: [20, 40, 50,20]
|
||||
* 当指定一个值时,四边的边距都相等,当指定数组时,数组内数值依次对应 上,右,下,左四边的边距。
|
||||
*/
|
||||
fitViewPadding?: number[] | number;
|
||||
/**
|
||||
* 各种元素是否在一个分组内,决定节点和边的层级问题,默认情况下所有的节点在一个分组中,所有的边在一个分组中,当这个参数为 false 时,节点和边的层级根据生成的顺序确定。
|
||||
* 默认值:true
|
||||
*/
|
||||
groupByTypes?: boolean;
|
||||
|
||||
groupStyle?: {
|
||||
style: {
|
||||
[key: string]: ShapeStyle
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 当图中元素更新,或视口变换时,是否自动重绘。建议在批量操作节点时关闭,以提高性能,完成批量操作后再打开,参见后面的 setAutoPaint() 方法。
|
||||
* 默认值:true
|
||||
*/
|
||||
autoPaint?: boolean;
|
||||
|
||||
/**
|
||||
* 设置画布的模式。详情可见G6中的Mode文档。
|
||||
*/
|
||||
modes?: IMode;
|
||||
|
||||
/**
|
||||
* 默认状态下节点的配置,比如 shape, size, color。会被写入的 data 覆盖。
|
||||
*/
|
||||
defaultNode?: {
|
||||
shape?: string,
|
||||
size?: string,
|
||||
color?: string,
|
||||
} & ModelStyle;
|
||||
|
||||
/**
|
||||
* 默认状态下边的配置,比如 shape, size, color。会被写入的 data 覆盖。
|
||||
*/
|
||||
defaultEdge?: {
|
||||
shape?: string,
|
||||
size?: string,
|
||||
color?: string,
|
||||
} & ModelStyle;
|
||||
|
||||
nodeStateStyles?: ModelStyle;
|
||||
|
||||
edgeStateStyles?: ModelStyle;
|
||||
|
||||
/**
|
||||
* 向 graph 注册插件。插件机制请见:plugin
|
||||
*/
|
||||
plugins?: any[];
|
||||
/**
|
||||
* 是否启用全局动画。
|
||||
*/
|
||||
animate?: boolean;
|
||||
|
||||
/**
|
||||
* 动画配置项,仅在animate为true时有效。
|
||||
*/
|
||||
animateCfg?: {
|
||||
/**
|
||||
* 回调函数,用于自定义节点运动路径。
|
||||
*/
|
||||
onFrame?: () => unknown;
|
||||
/**
|
||||
* 动画时长,单位为毫秒。
|
||||
*/
|
||||
duration?: number;
|
||||
/**
|
||||
* 动画动效。
|
||||
* 默认值:easeLinear
|
||||
*/
|
||||
easing?: Easeing;
|
||||
};
|
||||
/**
|
||||
* 最小缩放比例
|
||||
* 默认值 0.2
|
||||
*/
|
||||
minZoom?: number;
|
||||
/**
|
||||
* 最大缩放比例
|
||||
* 默认值 10
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/**
|
||||
* 像素比率
|
||||
* 默认值 1.0
|
||||
*/
|
||||
pixelRatio?: number;
|
||||
|
||||
groupType?: string;
|
||||
|
||||
/**
|
||||
* Edge 是否连接到节点中间
|
||||
*/
|
||||
linkCenter?: boolean;
|
||||
}
|
||||
|
||||
export interface IGraph extends EventEmitter {
|
||||
get<T>(key: string): T;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
import Group from "@antv/g-canvas/lib/group";
|
||||
import ShapeBase from "@antv/g-canvas/lib/shape/base";
|
||||
import { BBox } from "@antv/g-canvas/lib/types";
|
||||
import { IModelConfig, INodeConfig, IPoint, IShapeStyle } from '../../types'
|
||||
import { IBBox, IPoint, IShapeBase, ModelConfig, NodeConfig, ShapeStyle } from '@g6/types'
|
||||
|
||||
// item 的配置项
|
||||
interface IItemConfig {
|
||||
export type IItemConfig = Partial<{
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@ -18,7 +16,7 @@ interface IItemConfig {
|
||||
/**
|
||||
* data model
|
||||
*/
|
||||
model: IModelConfig;
|
||||
model: ModelConfig;
|
||||
|
||||
/**
|
||||
* G Group
|
||||
@ -46,23 +44,23 @@ interface IItemConfig {
|
||||
/**
|
||||
* key shape to calculate item's bbox
|
||||
*/
|
||||
keyShape: ShapeBase,
|
||||
keyShape: IShapeBase,
|
||||
/**
|
||||
* item's states, such as selected or active
|
||||
* @type Array
|
||||
*/
|
||||
states: string[];
|
||||
|
||||
}
|
||||
}>
|
||||
|
||||
export interface IItem {
|
||||
_cfg: IItemConfig;
|
||||
|
||||
destroyed: boolean;
|
||||
|
||||
isItem(): boolean;
|
||||
|
||||
getDefaultCfg(): IItemConfig;
|
||||
|
||||
getKeyShapeStyle(): IShapeStyle;
|
||||
getKeyShapeStyle(): ShapeStyle;
|
||||
|
||||
/**
|
||||
* 获取当前元素的所有状态
|
||||
@ -77,11 +75,11 @@ export interface IItem {
|
||||
*/
|
||||
hasState(state: string): boolean;
|
||||
|
||||
getStateStyle(state: string): IShapeStyle;
|
||||
getStateStyle(state: string): ShapeStyle;
|
||||
|
||||
getOriginStyle(): IShapeStyle;
|
||||
getOriginStyle(): ShapeStyle;
|
||||
|
||||
getCurrentStatesStyle(): IShapeStyle;
|
||||
getCurrentStatesStyle(): ShapeStyle;
|
||||
|
||||
/**
|
||||
* 更改元素状态, visible 不属于这个范畴
|
||||
@ -103,13 +101,13 @@ export interface IItem {
|
||||
* 节点的关键形状,用于计算节点大小,连线截距等
|
||||
* @return {G.Shape} 关键形状
|
||||
*/
|
||||
getKeyShape(): ShapeBase;
|
||||
getKeyShape(): IShapeBase;
|
||||
|
||||
/**
|
||||
* 节点数据模型
|
||||
* @return {Object} 数据模型
|
||||
*/
|
||||
getModel(): IModelConfig;
|
||||
getModel(): ModelConfig;
|
||||
|
||||
/**
|
||||
* 节点类型
|
||||
@ -117,7 +115,7 @@ export interface IItem {
|
||||
*/
|
||||
getType(): string;
|
||||
|
||||
getShapeCfg(model: IModelConfig): IModelConfig;
|
||||
getShapeCfg(model: ModelConfig): ModelConfig;
|
||||
|
||||
/**
|
||||
* 刷新一般用于处理几种情况
|
||||
@ -133,7 +131,7 @@ export interface IItem {
|
||||
* @internal 仅提供给 Graph 使用,外部直接调用 graph.update 接口
|
||||
* @param {Object} cfg 配置项,可以是增量信息
|
||||
*/
|
||||
update(cfg: IModelConfig): void;
|
||||
update(cfg: ModelConfig): void;
|
||||
|
||||
/**
|
||||
* 更新元素内容,样式
|
||||
@ -144,12 +142,7 @@ export interface IItem {
|
||||
* 更新位置,避免整体重绘
|
||||
* @param {object} cfg 待更新数据
|
||||
*/
|
||||
updatePosition(cfg: INodeConfig): void;
|
||||
|
||||
/**
|
||||
* 更新/刷新等操作后,清除 cache
|
||||
*/
|
||||
clearCache(): void;
|
||||
updatePosition(cfg: NodeConfig): void;
|
||||
|
||||
/**
|
||||
* 绘制元素
|
||||
@ -159,7 +152,7 @@ export interface IItem {
|
||||
/**
|
||||
* 获取元素的包围盒
|
||||
*/
|
||||
getBBox(): BBox;
|
||||
getBBox(): IBBox;
|
||||
|
||||
/**
|
||||
* 将元素放到最前面
|
||||
@ -195,6 +188,8 @@ export interface IItem {
|
||||
|
||||
isVisible(): boolean;
|
||||
|
||||
isOnlyMove(cfg: ModelConfig): boolean;
|
||||
|
||||
get<T>(key: string): T;
|
||||
set<T>(key: string, value: T): void;
|
||||
}
|
||||
@ -252,11 +247,15 @@ export interface INode extends IItem {
|
||||
*/
|
||||
removeEdge(edge: IEdge): void;
|
||||
|
||||
clearCache(): void;
|
||||
|
||||
/**
|
||||
* 获取锚点的定义
|
||||
* @return {array} anchorPoints, {x,y,...cfg}
|
||||
*/
|
||||
getAnchorPoints(): IPoint[];
|
||||
|
||||
hasLocked(): boolean;
|
||||
|
||||
lock(): void;
|
||||
|
||||
unlock(): void;
|
||||
}
|
217
src/item/edge.ts
Normal file
217
src/item/edge.ts
Normal file
@ -0,0 +1,217 @@
|
||||
import isNil from '@antv/util/lib/is-nil';
|
||||
import isPlainObject from '@antv/util/lib/is-plain-object'
|
||||
import { IEdge, INode } from "@g6/interface/item";
|
||||
import { EdgeConfig, IPoint, NodeConfig, SourceTarget } from '@g6/types';
|
||||
import Item from "./item";
|
||||
|
||||
const END_MAP = { source: 'start', target: 'end' };
|
||||
const ITEM_NAME_SUFFIX = 'Node'; // 端点的后缀,如 sourceNode, targetNode
|
||||
const POINT_NAME_SUFFIX = 'Point'; // 起点或者结束点的后缀,如 startPoint, endPoint
|
||||
const ANCHOR_NAME_SUFFIX = 'Anchor';
|
||||
|
||||
export default class Edge extends Item implements IEdge {
|
||||
protected getDefaultCfg() {
|
||||
return {
|
||||
type: 'edge',
|
||||
sourceNode: null,
|
||||
targetNode: null,
|
||||
startPoint: null,
|
||||
endPoint: null,
|
||||
linkCenter: false
|
||||
}
|
||||
}
|
||||
|
||||
private setEnd(name: string, value: INode) {
|
||||
const pointName = END_MAP[name] + POINT_NAME_SUFFIX;
|
||||
const itemName = name + ITEM_NAME_SUFFIX;
|
||||
const preItem = this.get(itemName);
|
||||
if(preItem) {
|
||||
// 如果之前存在节点,则移除掉边
|
||||
preItem.removeEdge(this)
|
||||
}
|
||||
|
||||
if (isPlainObject(value)) { // 如果设置成具体的点,则清理节点
|
||||
this.set(pointName, value);
|
||||
this.set(itemName, null);
|
||||
} else {
|
||||
value!.addEdge(this);
|
||||
this.set(itemName, value);
|
||||
this.set(pointName, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接点的坐标
|
||||
* @param name source | target
|
||||
* @param model 边的数据模型
|
||||
* @param controlPoints 控制点
|
||||
*/
|
||||
private getLinkPoint(name: SourceTarget, model: EdgeConfig, controlPoints: IPoint[]): IPoint {
|
||||
const pointName = END_MAP[name] + POINT_NAME_SUFFIX;
|
||||
const itemName = name + ITEM_NAME_SUFFIX;
|
||||
let point = this.get(pointName);
|
||||
if (!point) {
|
||||
const item = this.get(itemName);
|
||||
const anchorName = name + ANCHOR_NAME_SUFFIX;
|
||||
const prePoint = this.getPrePoint(name, controlPoints);
|
||||
const anchorIndex = model[anchorName];
|
||||
if (isNil(anchorIndex)) { // 如果有锚点,则使用锚点索引获取连接点
|
||||
point = item.getLinkPointByAnchor(anchorIndex);
|
||||
}
|
||||
// 如果锚点没有对应的点或者没有锚点,则直接计算连接点
|
||||
point = point || item.getLinkPoint(prePoint);
|
||||
if (isNil(point.index)) {
|
||||
this.set(name + 'AnchorIndex', point.index);
|
||||
}
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取同端点进行连接的点,计算交汇点
|
||||
* @param name
|
||||
* @param controlPoints
|
||||
*/
|
||||
private getPrePoint(name: SourceTarget, controlPoints: IPoint[]): NodeConfig | IPoint {
|
||||
if (controlPoints && controlPoints.length) {
|
||||
const index = name === 'source' ? 0 : controlPoints.length - 1;
|
||||
return controlPoints[index];
|
||||
}
|
||||
const oppositeName = name === 'source' ? 'target' : 'source'; // 取另一个节点的位置
|
||||
return this.getEndPoint(oppositeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取端点的位置
|
||||
* @param name
|
||||
*/
|
||||
private getEndPoint(name: string): NodeConfig | IPoint {
|
||||
const itemName = name + ITEM_NAME_SUFFIX;
|
||||
const pointName = END_MAP[name] + POINT_NAME_SUFFIX;
|
||||
const item = this.get(itemName);
|
||||
// 如果有端点,直接使用 model
|
||||
if (item) {
|
||||
return item.get('model');
|
||||
} // 否则直接使用点
|
||||
return this.get(pointName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过端点的中心获取控制点
|
||||
* @param model
|
||||
*/
|
||||
private getControlPointsByCenter(model) {
|
||||
const sourcePoint = this.getEndPoint('source');
|
||||
const targetPoint = this.getEndPoint('target');
|
||||
const shapeFactory = this.get('shapeFactory');
|
||||
return shapeFactory.getControlPoints(model.shape, {
|
||||
startPoint: sourcePoint,
|
||||
endPoint: targetPoint
|
||||
});
|
||||
}
|
||||
|
||||
private getEndCenter(name: string): IPoint {
|
||||
const itemName = name + ITEM_NAME_SUFFIX;
|
||||
const pointName = END_MAP[name] + POINT_NAME_SUFFIX;
|
||||
const item = this.get(itemName);
|
||||
// 如果有端点,直接使用 model
|
||||
if (item) {
|
||||
const bbox = item.getBBox();
|
||||
return {
|
||||
x: bbox.centerX,
|
||||
y: bbox.centerY
|
||||
};
|
||||
} // 否则直接使用点
|
||||
return this.get(pointName);
|
||||
}
|
||||
|
||||
protected init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
public getShapeCfg(model: EdgeConfig): EdgeConfig {
|
||||
const self = this;
|
||||
const linkCenter: boolean = self.get('linkCenter'); // 如果连接到中心,忽视锚点、忽视控制点
|
||||
const cfg: any = super.getShapeCfg(model);
|
||||
if (linkCenter) {
|
||||
cfg.startPoint = self.getEndCenter('source');
|
||||
cfg.endPoint = self.getEndCenter('target');
|
||||
} else {
|
||||
const controlPoints = cfg.controlPoints || self.getControlPointsByCenter(cfg);
|
||||
cfg.startPoint = self.getLinkPoint('source', model, controlPoints);
|
||||
cfg.endPoint = self.getLinkPoint('target', model, controlPoints);
|
||||
}
|
||||
cfg.sourceNode = self.get('sourceNode');
|
||||
cfg.targetNode = self.get('targetNode');
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取边的数据模型
|
||||
*/
|
||||
public getModel(): EdgeConfig {
|
||||
const model: EdgeConfig = this.get('model');
|
||||
const out = Object.assign({}, model);
|
||||
const sourceItem = this.get('source' + ITEM_NAME_SUFFIX);
|
||||
const targetItem = this.get('target' + ITEM_NAME_SUFFIX);
|
||||
if (sourceItem) {
|
||||
out.source = sourceItem.get('id');
|
||||
delete out['source' + ITEM_NAME_SUFFIX];
|
||||
} else {
|
||||
out.source = this.get('start' + POINT_NAME_SUFFIX);
|
||||
}
|
||||
if (targetItem) {
|
||||
out.target = targetItem.get('id');
|
||||
delete out['target' + ITEM_NAME_SUFFIX];
|
||||
} else {
|
||||
out.target = this.get('end' + POINT_NAME_SUFFIX);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public setSource(source: INode) {
|
||||
this.setEnd('source', source)
|
||||
this.set('source', source)
|
||||
}
|
||||
|
||||
public setTarget(target: INode) {
|
||||
this.setEnd('target', target)
|
||||
this.set('target', target)
|
||||
}
|
||||
|
||||
public getSource(): INode {
|
||||
return this.get('source')
|
||||
}
|
||||
|
||||
public getTarget(): INode {
|
||||
return this.get('target')
|
||||
}
|
||||
|
||||
public updatePosition() {}
|
||||
|
||||
/**
|
||||
* 边不需要重计算容器位置,直接重新计算 path 位置
|
||||
* @param {object} cfg 待更新数据
|
||||
*/
|
||||
public update(cfg: EdgeConfig) {
|
||||
const model: EdgeConfig = this.get('model');
|
||||
Object.assign(model, cfg);
|
||||
this.updateShape();
|
||||
this.afterUpdate();
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
const sourceItem: INode = this.get('source' + ITEM_NAME_SUFFIX);
|
||||
const targetItem: INode = this.get('target' + ITEM_NAME_SUFFIX);
|
||||
if(sourceItem && !sourceItem.destroyed) {
|
||||
sourceItem.removeEdge(this)
|
||||
}
|
||||
|
||||
if(targetItem && !targetItem.destroyed) {
|
||||
targetItem.removeEdge(this);
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
}
|
568
src/item/item.ts
Normal file
568
src/item/item.ts
Normal file
@ -0,0 +1,568 @@
|
||||
import Group from '@antv/g-canvas/lib/group';
|
||||
import each from '@antv/util/lib/each'
|
||||
import isNil from '@antv/util/lib/is-nil';
|
||||
import isPlainObject from '@antv/util/lib/is-plain-object'
|
||||
import isString from '@antv/util/lib/is-string'
|
||||
import uniqueId from '@antv/util/lib/unique-id'
|
||||
import { IItem, IItemConfig } from "@g6/interface/item";
|
||||
import { IBBox, IPoint, IShapeBase, ModelConfig, ModelStyle } from '@g6/types';
|
||||
import { getBBox } from '@g6/util/graphic';
|
||||
import { translate } from '@g6/util/math';
|
||||
|
||||
const CACHE_BBOX = 'bboxCache';
|
||||
|
||||
const RESERVED_STYLES = [ 'fillStyle', 'strokeStyle',
|
||||
'path', 'points', 'img', 'symbol' ];
|
||||
|
||||
export default class Item implements IItem {
|
||||
public _cfg: IItemConfig = {}
|
||||
private defaultCfg: IItemConfig = {
|
||||
/**
|
||||
* id
|
||||
* @type {string}
|
||||
*/
|
||||
id: null,
|
||||
|
||||
/**
|
||||
* 类型
|
||||
* @type {string}
|
||||
*/
|
||||
type: 'item',
|
||||
|
||||
/**
|
||||
* data model
|
||||
* @type {object}
|
||||
*/
|
||||
model: {} as ModelConfig,
|
||||
|
||||
/**
|
||||
* g group
|
||||
* @type {G.Group}
|
||||
*/
|
||||
group: null,
|
||||
|
||||
/**
|
||||
* is open animate
|
||||
* @type {boolean}
|
||||
*/
|
||||
animate: false,
|
||||
|
||||
/**
|
||||
* visible - not group visible
|
||||
* @type {boolean}
|
||||
*/
|
||||
visible: true,
|
||||
|
||||
/**
|
||||
* locked - lock node
|
||||
* @type {boolean}
|
||||
*/
|
||||
locked: false,
|
||||
/**
|
||||
* capture event
|
||||
* @type {boolean}
|
||||
*/
|
||||
event: true,
|
||||
/**
|
||||
* key shape to calculate item's bbox
|
||||
* @type object
|
||||
*/
|
||||
keyShape: null,
|
||||
/**
|
||||
* item's states, such as selected or active
|
||||
* @type Array
|
||||
*/
|
||||
states: []
|
||||
};
|
||||
|
||||
public destroyed: boolean = false
|
||||
|
||||
constructor(cfg: IItemConfig) {
|
||||
this._cfg = Object.assign(this.defaultCfg, this.getDefaultCfg(), cfg)
|
||||
const group = cfg.group
|
||||
group.set('item', this)
|
||||
|
||||
let id = this.get('model').id
|
||||
|
||||
if(!id) {
|
||||
id = uniqueId(this.get('type'))
|
||||
}
|
||||
|
||||
this.set('id', id)
|
||||
group.set('id', id)
|
||||
|
||||
this.init()
|
||||
this.draw()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 keyshape 计算包围盒
|
||||
*/
|
||||
private calculateBBox(): IBBox {
|
||||
const keyShape: IShapeBase = this.get('keyShape');
|
||||
const group: Group = this.get('group');
|
||||
// 因为 group 可能会移动,所以必须通过父元素计算才能计算出正确的包围盒
|
||||
const bbox = getBBox(keyShape, group);
|
||||
bbox.x = bbox.minX;
|
||||
bbox.y = bbox.minY;
|
||||
bbox.width = bbox.maxX - bbox.minX;
|
||||
bbox.height = bbox.maxY - bbox.minY;
|
||||
bbox.centerX = (bbox.minX + bbox.maxX) / 2;
|
||||
bbox.centerY = (bbox.minY + bbox.maxY) / 2;
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* draw shape
|
||||
*/
|
||||
private drawInner() {
|
||||
const self = this;
|
||||
const shapeFactory = self.get('shapeFactory');
|
||||
const group: Group = self.get('group');
|
||||
const model: ModelConfig = self.get('model');
|
||||
group.clear();
|
||||
|
||||
if (!shapeFactory) {
|
||||
return;
|
||||
}
|
||||
self.updatePosition(model);
|
||||
const cfg = self.getShapeCfg(model); // 可能会附加额外信息
|
||||
const shapeType: string = cfg.shape;
|
||||
const keyShape: IShapeBase = shapeFactory.draw(shapeType, cfg, group);
|
||||
if (keyShape) {
|
||||
keyShape.isKeyShape = true;
|
||||
self.set('keyShape', keyShape);
|
||||
self.set('originStyle', this.getKeyShapeStyle());
|
||||
}
|
||||
// 防止由于用户外部修改 model 中的 shape 导致 shape 不更新
|
||||
this.set('currentShape', shapeType);
|
||||
this.resetStates(shapeFactory, shapeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset shape states
|
||||
* @param shapeFactory
|
||||
* @param shapeType
|
||||
*/
|
||||
private resetStates(shapeFactory, shapeType: string) {
|
||||
const self = this;
|
||||
const states: string[] = self.get('states');
|
||||
each(states, state => {
|
||||
shapeFactory.setState(shapeType, state, true, self);
|
||||
});
|
||||
}
|
||||
|
||||
protected init() {
|
||||
// TODO 实例化工厂方法,需要等 Shape 重构完成
|
||||
// const shapeFactory = Shape.getFactory(this.get('type'));
|
||||
// this.set('shapeFactory', shapeFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性
|
||||
* @internal 仅内部类使用
|
||||
* @param {String} key 属性名
|
||||
* @return {object | string | number} 属性值
|
||||
*/
|
||||
public get(key: string) {
|
||||
return this._cfg[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性
|
||||
* @internal 仅内部类使用
|
||||
* @param {String|Object} key 属性名,也可以是对象
|
||||
* @param {object | string | number} val 属性值
|
||||
*/
|
||||
public set(key: string, val): void {
|
||||
if(isPlainObject(key)) {
|
||||
this._cfg = Object.assign({}, this._cfg, key)
|
||||
} else {
|
||||
this._cfg[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
protected getDefaultCfg() {
|
||||
return {}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新/刷新等操作后,清除 cache
|
||||
*/
|
||||
protected clearCache() {
|
||||
this.set(CACHE_BBOX, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染前的逻辑,提供给子类复写
|
||||
*/
|
||||
protected beforeDraw() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染后的逻辑,提供给子类复写
|
||||
*/
|
||||
protected afterDraw() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新后做一些工作
|
||||
*/
|
||||
protected afterUpdate() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* draw shape
|
||||
*/
|
||||
public draw() {
|
||||
this.beforeDraw()
|
||||
this.drawInner()
|
||||
this.afterDraw()
|
||||
}
|
||||
|
||||
public getKeyShapeStyle(): ModelStyle {
|
||||
const keyShape = this.getKeyShape();
|
||||
if (keyShape) {
|
||||
const styles: ModelStyle = {};
|
||||
each(keyShape.attr(), (val, key) => {
|
||||
if (RESERVED_STYLES.indexOf(key) < 0) {
|
||||
styles[key] = val;
|
||||
}
|
||||
});
|
||||
return styles;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 确定还是否需要该方法
|
||||
public getShapeCfg(model: ModelConfig): ModelConfig {
|
||||
const styles = this.get('styles');
|
||||
if (styles && styles.default) {
|
||||
// merge graph的item样式与数据模型中的样式
|
||||
const newModel = Object.assign({}, model);
|
||||
newModel.style = Object.assign({}, styles.default, model.style);
|
||||
return newModel;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定状态的样式,去除了全局样式
|
||||
* @param state 状态名称
|
||||
*/
|
||||
public getStateStyle(state: string) {
|
||||
const type: string = this.getType()
|
||||
const styles = this.get(`${type}StateStyles`);
|
||||
const stateStyle = styles && styles[state];
|
||||
return stateStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* get keyshape style
|
||||
*/
|
||||
public getOriginStyle(): ModelStyle {
|
||||
return this.get('originStyle');
|
||||
}
|
||||
|
||||
public getCurrentStatesStyle(): ModelStyle {
|
||||
const self = this;
|
||||
const originStyle = self.getOriginStyle();
|
||||
each(self.getStates(), state => {
|
||||
Object.assign(originStyle, self.getStateStyle(state));
|
||||
});
|
||||
return originStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改元素状态, visible 不属于这个范畴
|
||||
* @internal 仅提供内部类 graph 使用
|
||||
* @param {String} state 状态名
|
||||
* @param {Boolean} enable 节点状态值
|
||||
*/
|
||||
public setState(state: string, enable: boolean) {
|
||||
const states: string[] = this.get('states');
|
||||
const shapeFactory = this.get('shapeFactory');
|
||||
const index = states.indexOf(state);
|
||||
if (enable) {
|
||||
if (index > -1) {
|
||||
return;
|
||||
}
|
||||
states.push(state);
|
||||
} else if (index > -1) {
|
||||
states.splice(index, 1);
|
||||
}
|
||||
|
||||
if (shapeFactory) {
|
||||
const model: ModelConfig = this.get('model');
|
||||
shapeFactory.setState(model.shape, state, enable, this);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
public clearStates(states: string[]) {
|
||||
const self = this;
|
||||
const originStates = self.getStates();
|
||||
const shapeFactory = self.get('shapeFactory');
|
||||
const shape: string = self.get('model').shape;
|
||||
if (!states) {
|
||||
self.set('states', []);
|
||||
shapeFactory.setState(shape, originStates[0], false, self);
|
||||
return;
|
||||
}
|
||||
if (isString(states)) {
|
||||
states = [ states ];
|
||||
}
|
||||
const newStates = originStates.filter(state => {
|
||||
shapeFactory.setState(shape, state, false, self);
|
||||
if (states.indexOf(state) >= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
self.set('states', newStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点的图形容器
|
||||
* @return {G.Group} 图形容器
|
||||
*/
|
||||
public getContainer(): Group {
|
||||
return this.get('group');
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点的关键形状,用于计算节点大小,连线截距等
|
||||
* @return {G.Shape} 关键形状
|
||||
*/
|
||||
public getKeyShape(): IShapeBase {
|
||||
return this.get('keyShape');
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点数据模型
|
||||
* @return {Object} 数据模型
|
||||
*/
|
||||
public getModel(): ModelConfig {
|
||||
return this.get('model');
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点类型
|
||||
* @return {string} 节点的类型
|
||||
*/
|
||||
public getType(): string {
|
||||
return this.get('type');
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是 Item 对象,悬空边情况下进行判定
|
||||
*/
|
||||
public isItem(): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前元素的所有状态
|
||||
* @return {Array} 元素的所有状态
|
||||
*/
|
||||
public getStates(): string[] {
|
||||
return this.get('states');
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前元素是否处于某状态
|
||||
* @param {String} state 状态名
|
||||
* @return {Boolean} 是否处于某状态
|
||||
*/
|
||||
public hasState(state: string): boolean {
|
||||
const states = this.getStates()
|
||||
return states.indexOf(state) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新一般用于处理几种情况
|
||||
* 1. item model 在外部被改变
|
||||
* 2. 边的节点位置发生改变,需要重新计算边
|
||||
*
|
||||
* 因为数据从外部被修改无法判断一些属性是否被修改,直接走位置和 shape 的更新
|
||||
*/
|
||||
public refresh() {
|
||||
const model: ModelConfig = this.get('model');
|
||||
// 更新元素位置
|
||||
this.updatePosition(model);
|
||||
// 更新元素内容,样式
|
||||
this.updateShape();
|
||||
// 做一些更新之后的操作
|
||||
this.afterUpdate();
|
||||
// 清除缓存
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
public isOnlyMove(cfg?: ModelConfig): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 将更新应用到 model 上,刷新属性
|
||||
* @internal 仅提供给 Graph 使用,外部直接调用 graph.update 接口
|
||||
* @param {Object} cfg 配置项,可以是增量信息
|
||||
*/
|
||||
public update(cfg: ModelConfig) {
|
||||
const model: ModelConfig = this.get('model');
|
||||
const originPosition: IPoint = { x: model.x, y: model.y };
|
||||
|
||||
// 直接将更新合到原数据模型上,可以保证用户在外部修改源数据然后刷新时的样式符合期待。
|
||||
Object.assign(model, cfg);
|
||||
|
||||
// isOnlyMove 仅用于node
|
||||
const onlyMove = this.isOnlyMove(cfg);
|
||||
// 仅仅移动位置时,既不更新,也不重绘
|
||||
if (onlyMove) {
|
||||
this.updatePosition(model);
|
||||
} else {
|
||||
// 如果 x,y 有变化,先重置位置
|
||||
if (originPosition.x !== model.x || originPosition.y !== model.y) {
|
||||
this.updatePosition(model);
|
||||
}
|
||||
this.updateShape();
|
||||
}
|
||||
this.afterUpdate();
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新元素内容,样式
|
||||
*/
|
||||
public updateShape() {
|
||||
const shapeFactory = this.get('shapeFactory');
|
||||
const model = this.get('model');
|
||||
const shape = model.shape;
|
||||
// 判定是否允许更新
|
||||
// 1. 注册的节点允许更新
|
||||
// 2. 更新后的 shape 等于原先的 shape
|
||||
if (shapeFactory.shouldUpdate(shape) && shape === this.get('currentShape')) {
|
||||
const updateCfg = this.getShapeCfg(model);
|
||||
shapeFactory.update(shape, updateCfg, this);
|
||||
} else {
|
||||
// 如果不满足上面两种状态,重新绘制
|
||||
this.draw();
|
||||
}
|
||||
this.set('originStyle', this.getKeyShapeStyle());
|
||||
// 更新后重置节点状态
|
||||
this.resetStates(shapeFactory, shape);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新位置,避免整体重绘
|
||||
* @param {object} cfg 待更新数据
|
||||
*/
|
||||
public updatePosition(cfg: ModelConfig) {
|
||||
const model: ModelConfig = this.get('model');
|
||||
|
||||
const x = isNil(cfg.x) ? model.x : cfg.x;
|
||||
const y = isNil(cfg.y) ? model.y : cfg.y;
|
||||
|
||||
const group: Group = this.get('group');
|
||||
|
||||
if (isNil(x) || isNil(y)) {
|
||||
return;
|
||||
}
|
||||
group.resetMatrix();
|
||||
// G 4.0 element 中移除了矩阵相关方法,详见https://www.yuque.com/antv/blog/kxzk9g#4rMMV
|
||||
translate(group, { x, y });
|
||||
model.x = x;
|
||||
model.y = y;
|
||||
this.clearCache(); // 位置更新后需要清除缓存
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取元素的包围盒
|
||||
* @return {Object} 包含 x,y,width,height, centerX, centerY
|
||||
*/
|
||||
public getBBox(): IBBox {
|
||||
// 计算 bbox 开销有些大,缓存
|
||||
let bbox: IBBox = this.get(CACHE_BBOX);
|
||||
if (!bbox) {
|
||||
bbox = this.calculateBBox();
|
||||
this.set(CACHE_BBOX, bbox);
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素放到最前面
|
||||
*/
|
||||
public toFront() {
|
||||
this.get('group').toFront();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素放到最后面
|
||||
*/
|
||||
public toBack() {
|
||||
this.get('group').toBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示元素
|
||||
*/
|
||||
public show() {
|
||||
this.changeVisibility(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏元素
|
||||
*/
|
||||
public hide() {
|
||||
this.changeVisibility(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改是否显示
|
||||
* @param {Boolean} visible 是否显示
|
||||
*/
|
||||
public changeVisibility(visible: boolean) {
|
||||
const group: Group = this.get('group');
|
||||
if (visible) {
|
||||
group.show();
|
||||
} else {
|
||||
group.hide();
|
||||
}
|
||||
this.set('visible', visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素是否可见
|
||||
*/
|
||||
public isVisible(): boolean {
|
||||
return this.get('visible');
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否拾取及出发该元素的交互事件
|
||||
* @param {Boolean} enable 标识位
|
||||
*/
|
||||
public enableCapture(enable: boolean) {
|
||||
const group: Group = this.get('group');
|
||||
if(group) {
|
||||
group.attr('capture', enable)
|
||||
}
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
if(!this.destroyed) {
|
||||
const animate = this.get('animate');
|
||||
const group: Group = this.get('group');
|
||||
if (animate) {
|
||||
group.stopAnimate();
|
||||
}
|
||||
group.remove();
|
||||
this._cfg = null;
|
||||
this.destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
205
src/item/node.ts
Normal file
205
src/item/node.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import each from '@antv/util/lib/each'
|
||||
import isNil from '@antv/util/lib/is-nil';
|
||||
import mix from '@antv/util/lib/mix'
|
||||
import { IEdge, INode } from '@g6/interface/item';
|
||||
import { IPoint, IShapeBase, NodeConfig } from '@g6/types';
|
||||
import { distance, getCircleIntersectByPoint, getEllispeIntersectByPoint, getRectIntersectByPoint } from '@g6/util/math';
|
||||
import Item from './item'
|
||||
|
||||
const CACHE_ANCHOR_POINTS = 'anchorPointsCache'
|
||||
const CACHE_BBOX = 'bboxCache'
|
||||
|
||||
export default class Node extends Item implements INode {
|
||||
private getNearestPoint(points: IPoint[], curPoint: IPoint): IPoint {
|
||||
let index = 0;
|
||||
let nearestPoint = points[0];
|
||||
let minDistance = distance(points[0], curPoint);
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
const point = points[i];
|
||||
const dis = distance(point, curPoint);
|
||||
if (dis < minDistance) {
|
||||
nearestPoint = point;
|
||||
minDistance = dis;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
nearestPoint.anchorIndex = index;
|
||||
return nearestPoint;
|
||||
}
|
||||
public getDefaultCfg() {
|
||||
return {
|
||||
type: 'node',
|
||||
edges: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从节点关联的所有边
|
||||
*/
|
||||
public getEdges(): IEdge[] {
|
||||
return this.get('edges')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的入边
|
||||
*/
|
||||
public getInEdges(): IEdge[] {
|
||||
const self = this;
|
||||
return this.get('edges').filter((edge: IEdge) => {
|
||||
return edge.get('target') === self;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的出边
|
||||
*/
|
||||
public getOutEdges(): IEdge[] {
|
||||
const self = this;
|
||||
return this.get('edges').filter((edge: IEdge) => {
|
||||
return edge.get('source') === self;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据锚点的索引获取连接点
|
||||
* @param {Number} index 索引
|
||||
*/
|
||||
public getLinkPointByAnchor(index): IPoint {
|
||||
const anchorPoints = this.getAnchorPoints();
|
||||
return anchorPoints[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接点
|
||||
* @param point
|
||||
*/
|
||||
public getLinkPoint(point: IPoint): IPoint {
|
||||
const keyShape: IShapeBase = this.get('keyShape');
|
||||
const type: string = keyShape.get('type');
|
||||
const bbox = this.getBBox();
|
||||
const { centerX, centerY } = bbox;
|
||||
const anchorPoints = this.getAnchorPoints();
|
||||
let intersectPoint: IPoint;
|
||||
switch (type) {
|
||||
case 'circle':
|
||||
intersectPoint = getCircleIntersectByPoint({
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
r: bbox.width / 2
|
||||
}, point);
|
||||
break;
|
||||
case 'ellipse':
|
||||
intersectPoint = getEllispeIntersectByPoint({
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
rx: bbox.width / 2,
|
||||
ry: bbox.height / 2
|
||||
}, point);
|
||||
break;
|
||||
default:
|
||||
intersectPoint = getRectIntersectByPoint(bbox, point);
|
||||
}
|
||||
let linkPoint = intersectPoint;
|
||||
// 如果存在锚点,则使用交点计算最近的锚点
|
||||
if (anchorPoints.length) {
|
||||
if (!linkPoint) { // 如果计算不出交点
|
||||
linkPoint = point;
|
||||
}
|
||||
linkPoint = this.getNearestPoint(anchorPoints, linkPoint);
|
||||
}
|
||||
if (!linkPoint) { // 如果最终依然没法找到锚点和连接点,直接返回中心点
|
||||
linkPoint = { x: centerX, y: centerY };
|
||||
}
|
||||
return linkPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取锚点的定义
|
||||
* @return {array} anchorPoints
|
||||
*/
|
||||
public getAnchorPoints(): IPoint[] {
|
||||
let anchorPoints: IPoint[] = this.get(CACHE_ANCHOR_POINTS);
|
||||
if (!anchorPoints) {
|
||||
anchorPoints = [];
|
||||
const shapeFactory = this.get('shapeFactory');
|
||||
const bbox = this.getBBox();
|
||||
const model: NodeConfig = this.get('model');
|
||||
const shapeCfg = this.getShapeCfg(model);
|
||||
const points = shapeFactory.getAnchorPoints(model.shape, shapeCfg) || [];
|
||||
each(points, (pointArr, index) => {
|
||||
const point = mix({
|
||||
x: bbox.minX + pointArr[0] * bbox.width,
|
||||
y: bbox.minY + pointArr[1] * bbox.height
|
||||
}, pointArr[2], {
|
||||
index
|
||||
});
|
||||
anchorPoints.push(point);
|
||||
});
|
||||
this.set(CACHE_ANCHOR_POINTS, anchorPoints);
|
||||
}
|
||||
return anchorPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* add edge
|
||||
* @param edge Edge instance
|
||||
*/
|
||||
public addEdge(edge: IEdge) {
|
||||
this.get('edges').push(edge)
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定节点
|
||||
*/
|
||||
public lock() {
|
||||
this.set('locked', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁锁定的节点
|
||||
*/
|
||||
public unlock() {
|
||||
this.set('locked', false);
|
||||
}
|
||||
|
||||
public hasLocked(): boolean {
|
||||
return this.get('locked');
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除边
|
||||
* @param {Edge} edge 边
|
||||
*/
|
||||
public removeEdge(edge: IEdge) {
|
||||
const edges = this.getEdges();
|
||||
const index = edges.indexOf(edge);
|
||||
if (index > -1) {
|
||||
edges.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public clearCache() {
|
||||
this.set(CACHE_BBOX, null); // 清理缓存的 bbox
|
||||
this.set(CACHE_ANCHOR_POINTS, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否仅仅移动节点,其他属性没变化
|
||||
* @param cfg 节点数据模型
|
||||
*/
|
||||
public isOnlyMove(cfg: NodeConfig): boolean {
|
||||
if(!cfg) {
|
||||
return false
|
||||
}
|
||||
|
||||
const existX = isNil(cfg.x)
|
||||
const existY = isNil(cfg.y)
|
||||
|
||||
const keys = Object.keys(cfg)
|
||||
|
||||
// 仅有一个字段,包含 x 或者 包含 y
|
||||
// 两个字段,同时有 x,同时有 y
|
||||
return (keys.length === 1 && (existX || existY))
|
||||
|| (keys.length === 2 && existX && existY)
|
||||
}
|
||||
}
|
@ -1,17 +1,16 @@
|
||||
import GraphEvent from '@antv/g-base/lib/event/graph-event'
|
||||
import isArray from '@antv/util/lib/is-array'
|
||||
import isNil from '@antv/util/lib/is-nil'
|
||||
import isNumber from "@antv/util/lib/is-number";
|
||||
import isString from '@antv/util/lib/is-string'
|
||||
import { IG6GraphEvent, IPadding } from "../../types";
|
||||
import { G6GraphEvent } from '../interface/behavior';
|
||||
import { G6GraphEvent } from '@g6/interface/behavior';
|
||||
import { IG6GraphEvent, Padding } from '@g6/types';
|
||||
|
||||
/**
|
||||
* turn padding into [top, right, bottom, right]
|
||||
* @param {Number|Array} padding input padding
|
||||
* @return {array} output
|
||||
*/
|
||||
export const formatPadding = (padding: IPadding): number[] => {
|
||||
export const formatPadding = (padding: Padding): number[] => {
|
||||
let top = 0;
|
||||
let left = 0;
|
||||
let right = 0;
|
||||
|
@ -1,11 +1,9 @@
|
||||
import Group from "@antv/g-canvas/lib/group";
|
||||
import ShapeBase from "@antv/g-canvas/lib/shape/base";
|
||||
import { BBox } from "@antv/g-canvas/lib/types";
|
||||
import { vec2 } from "@antv/matrix-util";
|
||||
import each from '@antv/util/lib/each'
|
||||
import { IEdgeConfig, IPoint, ITreeGraphData } from "../../types";
|
||||
import Global from '../global'
|
||||
import { INode } from "../interface/item";
|
||||
import Global from '@g6/global'
|
||||
import { INode } from "@g6/interface/item";
|
||||
import { EdgeConfig, IBBox, IPoint, IShapeBase, TreeGraphData } from '@g6/types';
|
||||
import { applyMatrix } from "./math";
|
||||
|
||||
const PI: number = Math.PI
|
||||
@ -16,7 +14,7 @@ const cos: (x: number) => number = Math.cos
|
||||
const SELF_LINK_SIN: number = sin(PI / 8);
|
||||
const SELF_LINK_COS: number = cos(PI / 8);
|
||||
|
||||
export const getBBox = (element: ShapeBase, group: Group): BBox => {
|
||||
export const getBBox = (element: IShapeBase, group: Group): IBBox => {
|
||||
const bbox = element.getBBox();
|
||||
let leftTop: IPoint = {
|
||||
x: bbox.minX,
|
||||
@ -52,13 +50,13 @@ export const getBBox = (element: ShapeBase, group: Group): BBox => {
|
||||
* get loop edge config
|
||||
* @param cfg edge config
|
||||
*/
|
||||
export const getLoopCfgs = (cfg: IEdgeConfig): IEdgeConfig => {
|
||||
export const getLoopCfgs = (cfg: EdgeConfig): EdgeConfig => {
|
||||
const item: INode = cfg.sourceNode || cfg.targetNode
|
||||
const container: Group = item.get('group')
|
||||
const containerMatrix = container.getMatrix()
|
||||
|
||||
const keyShape: ShapeBase = item.getKeyShape()
|
||||
const bbox: BBox = keyShape.getBBox()
|
||||
const keyShape: IShapeBase = item.getKeyShape()
|
||||
const bbox: IBBox = keyShape.getBBox()
|
||||
|
||||
const loopCfg = cfg.loopCfg || {}
|
||||
// 距离keyShape边的最高距离
|
||||
@ -269,7 +267,7 @@ export const getLoopCfgs = (cfg: IEdgeConfig): IEdgeConfig => {
|
||||
// return result;
|
||||
// }
|
||||
|
||||
const traverse = (data: ITreeGraphData, fn: (param: ITreeGraphData) => boolean) => {
|
||||
const traverse = (data: TreeGraphData, fn: (param: TreeGraphData) => boolean) => {
|
||||
if(!fn(data)) {
|
||||
return
|
||||
}
|
||||
@ -278,7 +276,7 @@ const traverse = (data: ITreeGraphData, fn: (param: ITreeGraphData) => boolean)
|
||||
})
|
||||
}
|
||||
|
||||
export const traverseTree = (data: ITreeGraphData, fn: (param: ITreeGraphData) => boolean) => {
|
||||
export const traverseTree = (data: TreeGraphData, fn: (param: TreeGraphData) => boolean) => {
|
||||
if(typeof fn !== 'function') {
|
||||
return
|
||||
}
|
||||
@ -290,7 +288,7 @@ export const traverseTree = (data: ITreeGraphData, fn: (param: ITreeGraphData) =
|
||||
* @param data Tree graph data
|
||||
* @param layout
|
||||
*/
|
||||
export const radialLayout = (data: ITreeGraphData, layout?: string): ITreeGraphData => {
|
||||
export const radialLayout = (data: TreeGraphData, layout?: string): TreeGraphData => {
|
||||
// 布局方式有 H / V / LR / RL / TB / BT
|
||||
const VERTICAL_LAYOUTS: string[] = [ 'V', 'TB', 'BT' ];
|
||||
const min: IPoint = {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import groupBy, { ObjectType } from '@antv/util/lib/group-by'
|
||||
import { IGraphData, IGroupConfig, IGroupNodeIds } from '../../types';
|
||||
import { GraphData, GroupConfig, GroupNodeIds } from '@g6/types';
|
||||
|
||||
export const getAllNodeInGroups = (data: IGraphData): IGroupNodeIds => {
|
||||
const groupById: ObjectType<IGroupConfig> = groupBy(data.groups, 'id');
|
||||
const groupByParentId: ObjectType<IGroupConfig> = groupBy(data.groups, 'parentId');
|
||||
export const getAllNodeInGroups = (data: GraphData): GroupNodeIds => {
|
||||
const groupById: ObjectType<GroupConfig> = groupBy(data.groups, 'id');
|
||||
const groupByParentId: ObjectType<GroupConfig> = groupBy(data.groups, 'parentId');
|
||||
|
||||
const result = {};
|
||||
for (const parentId in groupByParentId) {
|
||||
@ -39,7 +39,7 @@ export const getAllNodeInGroups = (data: IGraphData): IGroupNodeIds => {
|
||||
}
|
||||
|
||||
// 缓存所有groupID对应的Node
|
||||
const groupNodes: IGroupNodeIds = {} as IGroupNodeIds;
|
||||
const groupNodes: GroupNodeIds = {} as GroupNodeIds;
|
||||
for (const groupId in groupIds) {
|
||||
if (!groupId || groupId === 'undefined') {
|
||||
continue;
|
||||
|
@ -1,5 +1,7 @@
|
||||
import Group from '@antv/g-canvas/lib/group';
|
||||
import { mat3, vec3 } from '@antv/matrix-util'
|
||||
import { ICircle, IEllipse, IGraphData, IMatrix, IPoint, IRect } from '../../types'
|
||||
import { transform } from '@antv/matrix-util'
|
||||
import { GraphData, ICircle, IEllipse, IMatrix, IPoint, IRect } from '@g6/types'
|
||||
|
||||
/**
|
||||
* 是否在区间内
|
||||
@ -271,7 +273,7 @@ export const floydWarshall = (adjMatrix: IMatrix[]): IMatrix[] => {
|
||||
* @param data graph data
|
||||
* @param directed whether it's a directed graph
|
||||
*/
|
||||
export const getAdjMatrix = (data: IGraphData, directed: boolean): IMatrix[] => {
|
||||
export const getAdjMatrix = (data: GraphData, directed: boolean): IMatrix[] => {
|
||||
const nodes = data.nodes;
|
||||
const edges = data.edges;
|
||||
const matrix: IMatrix[] = [];
|
||||
@ -294,4 +296,16 @@ export const getAdjMatrix = (data: IGraphData, directed: boolean): IMatrix[] =>
|
||||
}
|
||||
});
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平移group
|
||||
* @param group Group 实例
|
||||
* @param point 坐标
|
||||
*/
|
||||
export const translate = (group: Group, point: IPoint) => {
|
||||
const matrix: IMatrix = group.getMatrix()
|
||||
transform(matrix, [
|
||||
[ 't', point.x, point.y ]
|
||||
])
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { vec2 } from '@antv/matrix-util'
|
||||
import { catmullRom2Bezier } from '@antv/path-util'
|
||||
import { IPoint } from '../../types'
|
||||
import { IPoint } from '@g6/types'
|
||||
|
||||
/**
|
||||
* 替换字符串中的字段
|
||||
|
@ -1,6 +1,6 @@
|
||||
import '../../../src/behavior'
|
||||
import Behavior from '../../../src/behavior/behavior'
|
||||
import { IBehavior } from '../../../types';
|
||||
import { IBehavior } from '../../../src/interface/behavior';
|
||||
|
||||
describe('Behavior', () => {
|
||||
it('register signle behavior', () => {
|
||||
|
19
tests/unit/graph/controller/mode-spec.ts
Normal file
19
tests/unit/graph/controller/mode-spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Mode } from '../../../../src/graph/controller'
|
||||
import Graph from '../../../../src/graph/graph'
|
||||
import { GraphOptions, IGraph } from '../../../../src/interface/graph';
|
||||
|
||||
describe('Mode Controller', () => {
|
||||
it('signle mode', () => {
|
||||
const cfg: GraphOptions = {
|
||||
container: 'x',
|
||||
width: 200,
|
||||
height: 100
|
||||
}
|
||||
const graph: IGraph = new Graph(cfg)
|
||||
const modeController = new Mode(graph)
|
||||
expect(Object.keys(modeController.modes).length).toBe(1);
|
||||
expect(modeController.modes.default).not.toBe(undefined);
|
||||
expect(modeController.modes.default.length).toBe(0);
|
||||
expect(modeController.mode).toBe('default');
|
||||
})
|
||||
})
|
@ -11,7 +11,12 @@
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"],
|
||||
"types": ["jest"]
|
||||
"types": ["jest"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@g6/*": ["./src/*"],
|
||||
"@g6/types": ["./types"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"typedocOptions": {
|
||||
|
107
types/index.ts
107
types/index.ts
@ -1,17 +1,28 @@
|
||||
import GraphEvent from '@antv/g-base/lib/event/graph-event';
|
||||
import { BBox } from '@antv/g-base/lib/types';
|
||||
import ShapeBase from '@antv/g-canvas/lib/shape/base';
|
||||
import { IGraph } from '../src/interface/graph';
|
||||
import { IItem, INode } from '../src/interface/item'
|
||||
|
||||
// Math types
|
||||
export interface IPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
// 获取连接点时使用
|
||||
anchorIndex?: number;
|
||||
}
|
||||
|
||||
export type IMatrix = number[];
|
||||
|
||||
export type IPadding = number | string | number[];
|
||||
export interface IBBox extends BBox {
|
||||
centerX?: number;
|
||||
centerY?: number;
|
||||
}
|
||||
|
||||
export type IShapeStyle = Partial<{
|
||||
export type Padding = number | string | number[];
|
||||
|
||||
// Shape types
|
||||
export type ShapeStyle = Partial<{
|
||||
x: number;
|
||||
y: number;
|
||||
r: number;
|
||||
@ -27,6 +38,10 @@ export type IShapeStyle = Partial<{
|
||||
[key: string]: string | number | object | object[]
|
||||
}>
|
||||
|
||||
export interface IShapeBase extends ShapeBase {
|
||||
isKeyShape: boolean;
|
||||
}
|
||||
|
||||
export interface IRect extends IPoint {
|
||||
width: number;
|
||||
height: number;
|
||||
@ -41,12 +56,15 @@ export interface IEllipse extends IPoint {
|
||||
ry: number;
|
||||
}
|
||||
|
||||
type IModelStyle = Partial<{
|
||||
export type SourceTarget = 'source' | 'target'
|
||||
|
||||
// model types (node edge group)
|
||||
export type ModelStyle = Partial<{
|
||||
style: {
|
||||
[key: string]: IShapeStyle
|
||||
[key: string]: ShapeStyle
|
||||
};
|
||||
stateStyles: {
|
||||
[key: string]: IShapeStyle;
|
||||
[key: string]: ShapeStyle;
|
||||
};
|
||||
// loop edge config
|
||||
loopCfg: {
|
||||
@ -56,37 +74,35 @@ type IModelStyle = Partial<{
|
||||
clockwise?: boolean;
|
||||
};
|
||||
}>
|
||||
interface IModelStyle1 {
|
||||
style?: {
|
||||
[key: string]: IShapeStyle
|
||||
};
|
||||
stateStyles?: {
|
||||
[key: string]: IShapeStyle;
|
||||
};
|
||||
// loop edge config
|
||||
loopCfg?: {
|
||||
dist?: number;
|
||||
position?: string;
|
||||
// 如果逆时针画,交换起点和终点
|
||||
clockwise?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export type IModelConfig = INodeConfig | IEdgeConfig
|
||||
export type Easeing =
|
||||
'easeLinear'
|
||||
| 'easePolyIn'
|
||||
| 'easePolyOut'
|
||||
| 'easePolyInOut'
|
||||
| 'easeQuad'
|
||||
| 'easeQuadIn'
|
||||
| 'easeQuadOut'
|
||||
| 'easeQuadInOut'
|
||||
| string
|
||||
|
||||
export interface INodeConfig extends IModelStyle {
|
||||
id: string;
|
||||
|
||||
export interface ModelConfig extends ModelStyle {
|
||||
shape?: string;
|
||||
label?: string;
|
||||
groupId?: string;
|
||||
description?: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
}
|
||||
export interface NodeConfig extends ModelConfig {
|
||||
id: string;
|
||||
groupId?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface IEdgeConfig extends IModelStyle {
|
||||
export interface EdgeConfig extends ModelConfig {
|
||||
id?: string;
|
||||
source: string;
|
||||
target: string;
|
||||
label?: string;
|
||||
sourceNode?: INode;
|
||||
targetNode?: INode;
|
||||
startPoint?: IPoint;
|
||||
@ -94,28 +110,28 @@ export interface IEdgeConfig extends IModelStyle {
|
||||
controlPoints?: IPoint[];
|
||||
}
|
||||
|
||||
export interface IGroupConfig {
|
||||
export interface GroupConfig {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
[key: string]: string | IModelStyle;
|
||||
[key: string]: string | ModelStyle;
|
||||
}
|
||||
|
||||
export interface IGroupNodeIds {
|
||||
export interface GroupNodeIds {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface IGraphData {
|
||||
nodes?: INodeConfig[];
|
||||
edges?: IEdgeConfig[];
|
||||
groups?: IGroupConfig[];
|
||||
export interface GraphData {
|
||||
nodes?: NodeConfig[];
|
||||
edges?: EdgeConfig[];
|
||||
groups?: GroupConfig[];
|
||||
}
|
||||
|
||||
export interface ITreeGraphData {
|
||||
export interface TreeGraphData {
|
||||
id: string;
|
||||
label?: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
children?: ITreeGraphData[];
|
||||
children?: TreeGraphData[];
|
||||
}
|
||||
|
||||
// Behavior type file
|
||||
@ -160,12 +176,12 @@ type Unbind = 'unbind'
|
||||
|
||||
export type DefaultBehaviorType = IG6GraphEvent | string | number | object
|
||||
|
||||
export type IBehaviorOpation<U> = {
|
||||
export type BehaviorOpation<U> = {
|
||||
[T in keyof U]:
|
||||
T extends GetEvents ? () => { [key in G6Event]?: string } :
|
||||
T extends ShouldBegin ? (cfg?: IModelConfig) => boolean :
|
||||
T extends ShouldEnd ? (cfg?: IModelConfig) => boolean :
|
||||
T extends ShouldUpdate ? (cfg?: IModelConfig) => boolean :
|
||||
T extends ShouldBegin ? (cfg?: ModelConfig) => boolean :
|
||||
T extends ShouldEnd ? (cfg?: ModelConfig) => boolean :
|
||||
T extends ShouldUpdate ? (cfg?: ModelConfig) => boolean :
|
||||
T extends Bind ? (graph: IGraph) => void :
|
||||
T extends Unbind ? (graph: IGraph) => void :
|
||||
(...args: DefaultBehaviorType[]) => unknown;
|
||||
@ -176,14 +192,3 @@ export type IEvent = Record<G6Event, string>
|
||||
export interface IG6GraphEvent extends GraphEvent {
|
||||
item: IItem;
|
||||
}
|
||||
|
||||
export interface IBehavior {
|
||||
constructor: (cfg?: object) => void;
|
||||
getEvents: () => { [key in G6Event]?: string };
|
||||
shouldBegin: () => boolean;
|
||||
shouldUpdate: () => boolean;
|
||||
shouldEnd: () => boolean;
|
||||
bind: (graph: IGraph) => void;
|
||||
unbind: (graph: IGraph) => void;
|
||||
[key: string]: (...args: DefaultBehaviorType[]) => unknown;
|
||||
}
|
Loading…
Reference in New Issue
Block a user