mirror of
https://gitee.com/antv/g6.git
synced 2024-12-02 19:58:46 +08:00
feat: tree formatted data rendering; feat: collapse-expand-tree behavior;
This commit is contained in:
parent
11a9a3016a
commit
5ebc32f86c
@ -46,7 +46,7 @@
|
||||
"test": "jest",
|
||||
"test:integration": "node --expose-gc --max-old-space-size=4096 --unhandled-rejections=strict node_modules/jest/bin/jest tests/integration/ --config jest.node.config.js --coverage -i --logHeapUsage --detectOpenHandles",
|
||||
"size": "limit-size",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/edge-spec.ts",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/item-animate-spec.ts",
|
||||
"test-behavior": "DEBUG_MODE=1 jest --watch ./tests/unit/item-3d-spec.ts"
|
||||
},
|
||||
"lint-staged": {
|
||||
@ -58,7 +58,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"@antv/algorithm": "^0.1.25",
|
||||
"@antv/algorithm": "^0.1.26",
|
||||
"@antv/dom-util": "^2.0.4",
|
||||
"@antv/g": "^5.15.7",
|
||||
"@antv/g-canvas": "^1.9.28",
|
||||
@ -75,6 +75,7 @@
|
||||
"@antv/matrix-util": "^3.0.4",
|
||||
"@antv/util": "~2.0.5",
|
||||
"color": "^4.2.3",
|
||||
"stats-js": "^1.0.1",
|
||||
"tslib": "^2.5.0",
|
||||
"typedoc-plugin-markdown": "^3.14.0",
|
||||
"typescript": "^5.1.6"
|
||||
@ -115,6 +116,7 @@
|
||||
"rollup-plugin-polyfill-node": "^0.8.0",
|
||||
"rollup-plugin-typescript": "^1.0.1",
|
||||
"rollup-plugin-visualizer": "^5.6.0",
|
||||
"stats-js": "1.0.1",
|
||||
"stats.js": "^0.17.0",
|
||||
"ts-jest": "^28.0.8",
|
||||
"typedoc": "^0.24.0",
|
||||
|
@ -74,6 +74,7 @@ export default class Combo extends Node {
|
||||
displayModel: ComboDisplayModel,
|
||||
diffData?: { previous: ComboUserModelData; current: ComboUserModelData },
|
||||
diffState?: { previous: State[]; current: State[] },
|
||||
animate: boolean = true,
|
||||
onfinish: Function = () => {},
|
||||
) {
|
||||
if (displayModel.data.collapsed) {
|
||||
@ -90,7 +91,7 @@ export default class Combo extends Node {
|
||||
}
|
||||
});
|
||||
}
|
||||
super.draw(displayModel, diffData, diffState, onfinish);
|
||||
super.draw(displayModel, diffData, diffState, animate, onfinish);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ import { animateShapes } from '../util/animate';
|
||||
import { EdgeStyleSet } from '../types/theme';
|
||||
import Item from './item';
|
||||
import Node from './node';
|
||||
import Combo from './combo';
|
||||
|
||||
interface IProps {
|
||||
model: EdgeModel;
|
||||
@ -50,6 +51,7 @@ export default class Edge extends Item {
|
||||
displayModel: EdgeDisplayModel,
|
||||
diffData?: { previous: EdgeModelData; current: EdgeModelData },
|
||||
diffState?: { previous: State[]; current: State[] },
|
||||
animate: boolean = true,
|
||||
onfinish: Function = () => {},
|
||||
) {
|
||||
// get the end points
|
||||
@ -94,7 +96,7 @@ export default class Edge extends Item {
|
||||
|
||||
const timing = firstRendering ? 'buildIn' : 'update';
|
||||
// handle shape's animate
|
||||
if (!disableAnimate && usingAnimates[timing]?.length) {
|
||||
if (animate && !disableAnimate && usingAnimates[timing]?.length) {
|
||||
this.animations = animateShapes(
|
||||
usingAnimates,
|
||||
targetStyles, // targetStylesMap
|
||||
@ -103,7 +105,7 @@ export default class Edge extends Item {
|
||||
firstRendering ? 'buildIn' : 'update',
|
||||
this.changedStates,
|
||||
this.animateFrameListener,
|
||||
() => onfinish(displayModel.id),
|
||||
(canceled) => onfinish(displayModel.id, canceled),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -140,8 +142,8 @@ export default class Edge extends Item {
|
||||
|
||||
public clone(
|
||||
containerGroup: Group,
|
||||
sourceItem: Node,
|
||||
targetItem: Node,
|
||||
sourceItem: Node | Combo,
|
||||
targetItem: Node | Combo,
|
||||
onlyKeyShape?: boolean,
|
||||
disableAnimate?: boolean,
|
||||
) {
|
||||
|
@ -68,8 +68,10 @@ export default abstract class Item implements IItem {
|
||||
/** The flag of transient item. */
|
||||
public transient = false;
|
||||
public renderExtensions: any; // TODO
|
||||
/** Cache the animation instances to stop at next lifecycle. */
|
||||
/** Cache the shape animation instances to stop at next lifecycle. */
|
||||
public animations: IAnimation[];
|
||||
/** Cache the group animation instances to stop at next lifecycle. */
|
||||
public groupAnimations: IAnimation[];
|
||||
|
||||
public themeStyles: {
|
||||
default: ItemShapeStyles;
|
||||
@ -159,6 +161,7 @@ export default abstract class Item implements IItem {
|
||||
displayModel: ItemDisplayModel,
|
||||
diffData?: { previous: ItemModelData; current: ItemModelData },
|
||||
diffState?: { previous: State[]; current: State[] },
|
||||
animate: boolean = true,
|
||||
onfinish: Function = () => {},
|
||||
) {
|
||||
// call this.renderExt.draw in extend implementations
|
||||
@ -198,6 +201,7 @@ export default abstract class Item implements IItem {
|
||||
lodStrategy: LodStrategyObj;
|
||||
},
|
||||
onlyMove?: boolean,
|
||||
animate: boolean = true,
|
||||
onfinish?: Function,
|
||||
) {
|
||||
// 1. merge model into this model
|
||||
@ -218,7 +222,7 @@ export default abstract class Item implements IItem {
|
||||
: itemTheme?.lodStrategy || this.lodStrategy;
|
||||
|
||||
if (onlyMove) {
|
||||
this.updatePosition(displayModel, diffData, onfinish);
|
||||
this.updatePosition(displayModel, diffData, animate, onfinish);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -242,9 +246,9 @@ export default abstract class Item implements IItem {
|
||||
}
|
||||
// 3. call element update fn from useLib
|
||||
if (this.states?.length) {
|
||||
this.drawWithStates(this.states, onfinish);
|
||||
this.drawWithStates(this.states, animate, onfinish);
|
||||
} else {
|
||||
this.draw(this.displayModel, diffData, undefined, onfinish);
|
||||
this.draw(this.displayModel, diffData, undefined, animate, onfinish);
|
||||
}
|
||||
// 4. tag all the states with 'dirty', for state style regenerating when state changed
|
||||
this.stateDirtyMap = {};
|
||||
@ -261,6 +265,7 @@ export default abstract class Item implements IItem {
|
||||
public updatePosition(
|
||||
displayModel: ItemDisplayModel,
|
||||
diffData?: { previous: ItemModelData; current: ItemModelData },
|
||||
animate?: boolean,
|
||||
onfinish?: Function,
|
||||
) {}
|
||||
|
||||
@ -395,60 +400,63 @@ export default abstract class Item implements IItem {
|
||||
|
||||
/** Show the item. */
|
||||
public show(animate = true) {
|
||||
// TODO: utilize graphcore's view
|
||||
this.stopAnimations();
|
||||
|
||||
const { animates = {} } = this.displayModel.data;
|
||||
if (animate && animates.show?.length) {
|
||||
const showAnimateFieldsMap: any = {};
|
||||
Object.values(animates.show).forEach((animate) => {
|
||||
const { shapeId = 'group' } = animate;
|
||||
showAnimateFieldsMap[shapeId] = (
|
||||
showAnimateFieldsMap[shapeId] || []
|
||||
).concat(animate.fields);
|
||||
});
|
||||
const targetStyleMap = {};
|
||||
Object.keys(this.shapeMap).forEach((id) => {
|
||||
const shape = this.shapeMap[id];
|
||||
if (!this.cacheHiddenShape[id]) {
|
||||
// set the animate fields to initial value
|
||||
if (showAnimateFieldsMap[id]) {
|
||||
targetStyleMap[id] = targetStyleMap[id] || {};
|
||||
const beginStyle = getShapeAnimateBeginStyles(shape);
|
||||
showAnimateFieldsMap[id].forEach((field) => {
|
||||
if (beginStyle.hasOwnProperty(field)) {
|
||||
targetStyleMap[id][field] = shape.style[field];
|
||||
shape.style[field] = beginStyle[field];
|
||||
}
|
||||
});
|
||||
Promise.all(this.stopAnimations()).finally(() => {
|
||||
if (this.destroyed || this.visible) return;
|
||||
const { animates = {} } = this.displayModel.data;
|
||||
if (animate && animates.show?.length) {
|
||||
const showAnimateFieldsMap: any = {};
|
||||
Object.values(animates.show).forEach((animate) => {
|
||||
const { shapeId = 'group' } = animate;
|
||||
showAnimateFieldsMap[shapeId] = (
|
||||
showAnimateFieldsMap[shapeId] || []
|
||||
).concat(animate.fields);
|
||||
});
|
||||
const targetStyleMap = {};
|
||||
Object.keys(this.shapeMap).forEach((id) => {
|
||||
const shape = this.shapeMap[id];
|
||||
if (!this.cacheHiddenShape[id]) {
|
||||
// set the animate fields to initial value
|
||||
if (showAnimateFieldsMap[id]) {
|
||||
targetStyleMap[id] = targetStyleMap[id] || {};
|
||||
const beginStyle = getShapeAnimateBeginStyles(shape);
|
||||
showAnimateFieldsMap[id].forEach((field) => {
|
||||
if (beginStyle.hasOwnProperty(field)) {
|
||||
targetStyleMap[id][field] = shape.style[field];
|
||||
shape.style[field] = beginStyle[field];
|
||||
}
|
||||
});
|
||||
}
|
||||
shape.show();
|
||||
}
|
||||
shape.show();
|
||||
});
|
||||
if (showAnimateFieldsMap.group) {
|
||||
showAnimateFieldsMap.group.forEach((field) => {
|
||||
const usingField = field === 'size' ? 'transform' : field;
|
||||
if (GROUP_ANIMATE_STYLES[0].hasOwnProperty(usingField)) {
|
||||
this.group.style[usingField] =
|
||||
GROUP_ANIMATE_STYLES[0][usingField];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (showAnimateFieldsMap.group) {
|
||||
showAnimateFieldsMap.group.forEach((field) => {
|
||||
const usingField = field === 'size' ? 'transform' : field;
|
||||
if (GROUP_ANIMATE_STYLES[0].hasOwnProperty(usingField)) {
|
||||
this.group.style[usingField] = GROUP_ANIMATE_STYLES[0][usingField];
|
||||
}
|
||||
|
||||
this.animations = this.runWithAnimates(
|
||||
animates,
|
||||
'show',
|
||||
targetStyleMap,
|
||||
);
|
||||
} else {
|
||||
Object.keys(this.shapeMap).forEach((id) => {
|
||||
const shape = this.shapeMap[id];
|
||||
if (!this.cacheHiddenShape[id]) shape.show();
|
||||
});
|
||||
}
|
||||
|
||||
this.animations = this.runWithAnimates(animates, 'show', targetStyleMap);
|
||||
} else {
|
||||
Object.keys(this.shapeMap).forEach((id) => {
|
||||
const shape = this.shapeMap[id];
|
||||
if (!this.cacheHiddenShape[id]) shape.show();
|
||||
});
|
||||
}
|
||||
|
||||
this.visible = true;
|
||||
this.visible = true;
|
||||
});
|
||||
}
|
||||
|
||||
/** Hides the item. */
|
||||
public hide(animate = true) {
|
||||
// TODO: utilize graphcore's view
|
||||
this.stopAnimations();
|
||||
const func = () => {
|
||||
Object.keys(this.shapeMap).forEach((id) => {
|
||||
const shape = this.shapeMap[id];
|
||||
@ -457,15 +465,22 @@ export default abstract class Item implements IItem {
|
||||
shape.hide();
|
||||
});
|
||||
};
|
||||
const { animates = {} } = this.displayModel.data;
|
||||
if (animate && animates.hide?.length) {
|
||||
this.animations = this.runWithAnimates(animates, 'hide', undefined, func);
|
||||
} else {
|
||||
// 2. clear group and remove group
|
||||
func();
|
||||
}
|
||||
|
||||
this.visible = false;
|
||||
Promise.all(this.stopAnimations()).then(() => {
|
||||
if (this.destroyed || !this.visible) return;
|
||||
const { animates = {} } = this.displayModel.data;
|
||||
if (animate && animates.hide?.length) {
|
||||
this.animations = this.runWithAnimates(
|
||||
animates,
|
||||
'hide',
|
||||
undefined,
|
||||
func,
|
||||
);
|
||||
} else {
|
||||
// 2. clear group and remove group
|
||||
func();
|
||||
}
|
||||
this.visible = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns the visibility of the item. */
|
||||
@ -606,7 +621,11 @@ export default abstract class Item implements IItem {
|
||||
* @param previousStates previous states
|
||||
* @returns
|
||||
*/
|
||||
private drawWithStates(previousStates: State[], onfinish?: Function) {
|
||||
private drawWithStates(
|
||||
previousStates: State[],
|
||||
animate: boolean = true,
|
||||
onfinish?: Function,
|
||||
) {
|
||||
const { default: _, ...themeStateStyles } = this.themeStyles;
|
||||
const { data: displayModelData } = this.displayModel;
|
||||
let styles = {}; // merged styles
|
||||
@ -675,6 +694,7 @@ export default abstract class Item implements IItem {
|
||||
previous: previousStates,
|
||||
current: this.states,
|
||||
},
|
||||
animate,
|
||||
onfinish,
|
||||
);
|
||||
}
|
||||
@ -729,8 +749,27 @@ export default abstract class Item implements IItem {
|
||||
* Stop all the animations on the item.
|
||||
*/
|
||||
public stopAnimations() {
|
||||
this.animations?.forEach(stopAnimate);
|
||||
this.animations = [];
|
||||
let promises = [];
|
||||
if (this.animations?.length) {
|
||||
while (this.animations.length) {
|
||||
const animate = this.animations.pop();
|
||||
if (animate.playState !== 'running') break;
|
||||
promises.push(stopAnimate(animate));
|
||||
// @ts-ignore
|
||||
animate.onManualCancel?.();
|
||||
}
|
||||
}
|
||||
if (this.groupAnimations?.length) {
|
||||
while (this.groupAnimations.length) {
|
||||
const groupAnimate = this.groupAnimations.pop();
|
||||
if (groupAnimate.playState !== 'running') break;
|
||||
promises.push(stopAnimate(groupAnimate));
|
||||
// @ts-ignore
|
||||
groupAnimate.onManualCancel?.();
|
||||
}
|
||||
this.groupAnimations = [];
|
||||
}
|
||||
return promises;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,6 +45,7 @@ export default class Node extends Item {
|
||||
this.displayModel as NodeDisplayModel | ComboDisplayModel,
|
||||
undefined,
|
||||
undefined,
|
||||
!this.displayModel.data.disableAnimate,
|
||||
props.onfinish,
|
||||
);
|
||||
}
|
||||
@ -55,6 +56,7 @@ export default class Node extends Item {
|
||||
current: NodeModelData | ComboModelData;
|
||||
},
|
||||
diffState?: { previous: State[]; current: State[] },
|
||||
animate: boolean = true,
|
||||
onfinish: Function = () => {},
|
||||
) {
|
||||
const { group, renderExt, shapeMap: prevShapeMap, model } = this;
|
||||
@ -78,13 +80,13 @@ export default class Node extends Item {
|
||||
} else {
|
||||
// terminate previous animations
|
||||
this.stopAnimations();
|
||||
this.updatePosition(displayModel, diffData, onfinish);
|
||||
this.updatePosition(displayModel, diffData, animate, onfinish);
|
||||
}
|
||||
|
||||
const { haloShape } = this.shapeMap;
|
||||
haloShape?.toBack();
|
||||
|
||||
super.draw(displayModel, diffData, diffState, onfinish);
|
||||
super.draw(displayModel, diffData, diffState, animate, onfinish);
|
||||
this.anchorPointsCache = undefined;
|
||||
renderExt.updateCache(this.shapeMap);
|
||||
|
||||
@ -94,7 +96,7 @@ export default class Node extends Item {
|
||||
}
|
||||
|
||||
// handle shape's and group's animate
|
||||
if (!disableAnimate && animates) {
|
||||
if (animate && !disableAnimate && animates) {
|
||||
const animatesExcludePosition = getAnimatesExcludePosition(animates);
|
||||
this.animations = animateShapes(
|
||||
animatesExcludePosition, // animates
|
||||
@ -104,7 +106,7 @@ export default class Node extends Item {
|
||||
firstRendering ? 'buildIn' : 'update',
|
||||
this.changedStates,
|
||||
this.animateFrameListener,
|
||||
() => onfinish(model.id),
|
||||
(canceled) => onfinish(model.id, canceled),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -121,9 +123,18 @@ export default class Node extends Item {
|
||||
lodStrategy: LodStrategyObj;
|
||||
},
|
||||
onlyMove?: boolean,
|
||||
animate?: boolean,
|
||||
onfinish?: Function,
|
||||
) {
|
||||
super.update(model, diffData, isReplace, theme, onlyMove, onfinish);
|
||||
super.update(
|
||||
model,
|
||||
diffData,
|
||||
isReplace,
|
||||
theme,
|
||||
onlyMove,
|
||||
animate,
|
||||
onfinish,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,19 +147,20 @@ export default class Node extends Item {
|
||||
previous: NodeModelData | ComboModelData;
|
||||
current: NodeModelData | ComboModelData;
|
||||
},
|
||||
animate?: boolean,
|
||||
onfinish: Function = () => {},
|
||||
) {
|
||||
const { group } = this;
|
||||
const { x, y, z = 0, animates, disableAnimate } = displayModel.data;
|
||||
if (isNaN(x as number) || isNaN(y as number) || isNaN(z)) return;
|
||||
if (!disableAnimate && animates?.update) {
|
||||
if (isNaN(x as number) || isNaN(y as number) || isNaN(z as number)) return;
|
||||
if (animate && !disableAnimate && animates?.update) {
|
||||
const groupAnimates = animates.update.filter(
|
||||
({ shapeId, fields = [] }) =>
|
||||
(!shapeId || shapeId === 'group') &&
|
||||
(fields.includes('x') || fields.includes('y')),
|
||||
);
|
||||
if (groupAnimates.length) {
|
||||
animateShapes(
|
||||
const animations = animateShapes(
|
||||
{ update: groupAnimates },
|
||||
{ group: { x, y, z } } as any, // targetStylesMap
|
||||
this.shapeMap, // shapeMap
|
||||
@ -156,12 +168,14 @@ export default class Node extends Item {
|
||||
'update',
|
||||
[],
|
||||
this.animateFrameListener,
|
||||
() => onfinish(displayModel.id),
|
||||
(canceled) => onfinish(displayModel.id, canceled),
|
||||
);
|
||||
this.groupAnimations = animations;
|
||||
return;
|
||||
}
|
||||
}
|
||||
group.setLocalPosition(x, y, z);
|
||||
onfinish(displayModel.id, !animate);
|
||||
}
|
||||
|
||||
public clone(
|
||||
@ -294,6 +308,12 @@ export default class Node extends Item {
|
||||
}
|
||||
|
||||
public getPosition(): Point {
|
||||
const initiated =
|
||||
this.shapeMap.keyShape && this.group.attributes.x !== undefined;
|
||||
if (initiated) {
|
||||
const { center } = this.shapeMap.keyShape.getRenderBounds();
|
||||
return { x: center[0], y: center[1], z: center[2] };
|
||||
}
|
||||
const { x = 0, y = 0, z = 0 } = this.model.data;
|
||||
return { x: x as number, y: y as number, z: z as number };
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Graph as GraphLib, GraphView, ID } from '@antv/graphlib';
|
||||
import {
|
||||
clone,
|
||||
isArray,
|
||||
isFunction,
|
||||
isNumber,
|
||||
isObject,
|
||||
isString,
|
||||
} from '@antv/util';
|
||||
import { clone, isArray, isObject } from '@antv/util';
|
||||
import { registery as registry } from '../../stdlib';
|
||||
import { ComboModel, ComboUserModel, GraphData, IGraph } from '../../types';
|
||||
import { ComboUserModelData } from '../../types/combo';
|
||||
import { DataChangeType, GraphCore } from '../../types/data';
|
||||
import {
|
||||
DataChangeType,
|
||||
DataConfig,
|
||||
FetchDataConfig,
|
||||
GraphCore,
|
||||
InlineGraphDataConfig,
|
||||
InlineTreeDataConfig,
|
||||
} from '../../types/data';
|
||||
import {
|
||||
EdgeModel,
|
||||
EdgeModelData,
|
||||
@ -28,9 +28,10 @@ import { getExtension } from '../../util/extension';
|
||||
import {
|
||||
deconstructData,
|
||||
graphComboTreeDfs,
|
||||
graphCoreTreeDfs,
|
||||
isSucceed,
|
||||
graphData2TreeData,
|
||||
treeData2GraphData,
|
||||
validateComboStrucutre,
|
||||
traverse,
|
||||
} from '../../util/data';
|
||||
|
||||
/**
|
||||
@ -49,8 +50,6 @@ export class DataController {
|
||||
*/
|
||||
public graphCore: GraphCore;
|
||||
|
||||
private comboTreeView: GraphView<any, any>;
|
||||
|
||||
constructor(graph: IGraph<any, any>) {
|
||||
this.graph = graph;
|
||||
this.tap();
|
||||
@ -142,6 +141,9 @@ export class DataController {
|
||||
private tap() {
|
||||
this.extensions = this.getExtensions();
|
||||
this.graph.hooks.datachange.tap(this.onDataChange.bind(this));
|
||||
this.graph.hooks.treecollapseexpand.tap(
|
||||
this.onTreeCollapseExpand.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,24 +163,24 @@ export class DataController {
|
||||
* Listener of graph's datachange hook.
|
||||
* @param param contains new graph data and type of data change
|
||||
*/
|
||||
private onDataChange(param: { data: GraphData; type: DataChangeType }) {
|
||||
private onDataChange(param: { data: DataConfig; type: DataChangeType }) {
|
||||
const { data, type: changeType } = param;
|
||||
const change = () => {
|
||||
switch (changeType) {
|
||||
case 'remove':
|
||||
this.removeData(data);
|
||||
this.removeData(data as GraphData);
|
||||
break;
|
||||
case 'update':
|
||||
this.updateData(data);
|
||||
this.updateData(data as GraphData);
|
||||
break;
|
||||
case 'moveCombo':
|
||||
this.moveCombo(data);
|
||||
this.moveCombo(data as GraphData);
|
||||
break;
|
||||
case 'addCombo':
|
||||
this.addCombo(data);
|
||||
this.addCombo(data as GraphData);
|
||||
break;
|
||||
default:
|
||||
// 'replace' | 'mergeReplace' | 'union'
|
||||
// changeType is 'replace' | 'mergeReplace' | 'union'
|
||||
this.changeData(data, changeType);
|
||||
break;
|
||||
}
|
||||
@ -191,16 +193,30 @@ export class DataController {
|
||||
}
|
||||
}
|
||||
|
||||
private onTreeCollapseExpand(params: {
|
||||
ids: ID[];
|
||||
action: 'collapse' | 'expand';
|
||||
}) {
|
||||
const { ids, action } = params;
|
||||
ids.forEach((id) => {
|
||||
this.userGraphCore.mergeNodeData(id, {
|
||||
collapsed: action === 'collapse',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change data by replace, merge repalce, or union.
|
||||
* @param data new data
|
||||
* @param changeType type of data change, 'replace' means discard the old data. 'mergeReplace' means merge the common part. 'union' means merge whole sets of old and new one
|
||||
*/
|
||||
private changeData(
|
||||
data: GraphData,
|
||||
dataConfig: DataConfig,
|
||||
changeType: 'replace' | 'mergeReplace' | 'union',
|
||||
) {
|
||||
const { userGraphCore } = this;
|
||||
const { type: dataType, data } = this.formatData(dataConfig) || {};
|
||||
if (!dataType) return;
|
||||
|
||||
if (changeType === 'replace') {
|
||||
this.userGraphCore = new GraphLib<NodeUserModelData, EdgeUserModelData>({
|
||||
nodes: data.nodes.concat(
|
||||
@ -224,10 +240,6 @@ export class DataController {
|
||||
edges,
|
||||
});
|
||||
if (combos?.length) {
|
||||
this.comboTreeView = new GraphView<NodeModelData, EdgeModelData>({
|
||||
graph: this.graphCore,
|
||||
cache: 'manual',
|
||||
});
|
||||
this.graphCore.attachTreeStructure('combo');
|
||||
nodes.forEach((node) => {
|
||||
if (node.data.parentId) {
|
||||
@ -257,9 +269,10 @@ export class DataController {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const { userGraphCore } = this;
|
||||
const prevData = deconstructData({
|
||||
nodes: userGraphCore.getAllNodes(),
|
||||
edges: userGraphCore.getAllEdges(),
|
||||
edges: [],
|
||||
});
|
||||
const { nodes = [], edges = [], combos = [] } = data;
|
||||
const nodesAndCombos = nodes.concat(
|
||||
@ -294,6 +307,7 @@ export class DataController {
|
||||
}
|
||||
|
||||
// =========== edge ============
|
||||
prevData.edges = userGraphCore.getAllEdges();
|
||||
if (!prevData.edges.length) {
|
||||
userGraphCore.addEdges(edges);
|
||||
} else {
|
||||
@ -317,6 +331,15 @@ export class DataController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (data.edges?.length) {
|
||||
const { userGraphCore } = this;
|
||||
// convert and store tree structure to graphCore
|
||||
this.updateTreeGraph(dataType, {
|
||||
nodes: userGraphCore.getAllNodes(),
|
||||
edges: userGraphCore.getAllEdges(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,12 +354,14 @@ export class DataController {
|
||||
const prevEdges = userGraphCore.getAllEdges();
|
||||
if (prevNodesAndCombos.length && nodesAndCombos.length) {
|
||||
// update the parentId
|
||||
nodesAndCombos.forEach((item) => {
|
||||
const { parentId } = item.data;
|
||||
this.graphCore.getChildren(item.id, 'combo').forEach((child) => {
|
||||
userGraphCore.mergeNodeData(child.id, { parentId });
|
||||
if (this.graphCore.hasTreeStructure('combo')) {
|
||||
nodesAndCombos.forEach((item) => {
|
||||
const { parentId } = item.data;
|
||||
this.graphCore.getChildren(item.id, 'combo').forEach((child) => {
|
||||
userGraphCore.mergeNodeData(child.id, { parentId });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// remove the node
|
||||
userGraphCore.removeNodes(nodesAndCombos.map((node) => node.id));
|
||||
}
|
||||
@ -353,9 +378,11 @@ export class DataController {
|
||||
* Update part of old data.
|
||||
* @param data data to be updated which is part of old one
|
||||
*/
|
||||
private updateData(data: GraphData) {
|
||||
private updateData(dataConfig: DataConfig) {
|
||||
const { userGraphCore } = this;
|
||||
const { nodes = [], edges = [], combos = [] } = data;
|
||||
const { type: dataType, data } = this.formatData(dataConfig);
|
||||
if (!dataType) return;
|
||||
const { nodes = [], edges = [], combos = [] } = data as GraphData;
|
||||
const {
|
||||
nodes: prevNodes,
|
||||
edges: prevEdges,
|
||||
@ -453,6 +480,38 @@ export class DataController {
|
||||
userGraphCore.mergeNodeData(id, data);
|
||||
});
|
||||
}
|
||||
|
||||
if (edges.length) {
|
||||
// convert and store tree structure to graphCore
|
||||
this.updateTreeGraph(dataType, {
|
||||
nodes: this.userGraphCore.getAllNodes(),
|
||||
edges: this.userGraphCore.getAllEdges(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private formatData(dataConfig: DataConfig): {
|
||||
data: GraphData;
|
||||
type: 'graphData' | 'treeData' | 'fetch';
|
||||
} {
|
||||
const { type, value } = dataConfig as
|
||||
| InlineGraphDataConfig
|
||||
| InlineTreeDataConfig
|
||||
| FetchDataConfig;
|
||||
let data = value;
|
||||
if (!type) {
|
||||
data = dataConfig as GraphData;
|
||||
} else if (type === 'treeData') {
|
||||
data = treeData2GraphData(value);
|
||||
} else if (type === 'fetch') {
|
||||
// TODO: fetch
|
||||
} else {
|
||||
console.warn(
|
||||
'Input data type is invalid, the type shuold be "graphData", "treeData", or "fetch".',
|
||||
);
|
||||
return;
|
||||
}
|
||||
return { type: type || 'graphData', data: data as GraphData };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -592,9 +651,22 @@ export class DataController {
|
||||
const changeMap: {
|
||||
[id: string]: boolean;
|
||||
} = {};
|
||||
const treeChanges = [];
|
||||
event.changes.forEach((change) => {
|
||||
const { value, id } = change;
|
||||
changeMap[id || value.id] = true;
|
||||
const id = change.id || change.value?.id;
|
||||
if (id !== undefined) {
|
||||
changeMap[id] = true;
|
||||
return;
|
||||
}
|
||||
if (
|
||||
[
|
||||
'TreeStructureAttached',
|
||||
'TreeStructureChanged',
|
||||
'TreeStructureChanged',
|
||||
].includes(change.type)
|
||||
) {
|
||||
treeChanges.push(change);
|
||||
}
|
||||
});
|
||||
nodes.forEach((model) => {
|
||||
newModelMap[model.id] = { type: 'node', model };
|
||||
@ -633,6 +705,7 @@ export class DataController {
|
||||
const { model: newModel } = newModelMap[id] || {};
|
||||
// remove
|
||||
if (!newModel) {
|
||||
// remove a combo, put the children to upper parent
|
||||
if (prevModel.data._isCombo) {
|
||||
graphCore.getChildren(id, 'combo').forEach((child) => {
|
||||
parentMap[child.id] = {
|
||||
@ -641,9 +714,43 @@ export class DataController {
|
||||
};
|
||||
});
|
||||
}
|
||||
// if it has combo parent, remove it from the parent's children list
|
||||
if (prevModel.data.parentId) {
|
||||
graphCore.setParent(id, undefined, 'combo');
|
||||
}
|
||||
|
||||
// for tree graph view, show the succeed nodes and edges
|
||||
const succeedIds = [];
|
||||
graphCore.dfsTree(
|
||||
id,
|
||||
(child) => {
|
||||
succeedIds.push(child.id);
|
||||
},
|
||||
'tree',
|
||||
);
|
||||
const succeedEdgeIds = graphCore
|
||||
.getAllEdges()
|
||||
.filter(
|
||||
({ source, target }) =>
|
||||
succeedIds.includes(source) && succeedIds.includes(target),
|
||||
)
|
||||
.map((edge) => edge.id);
|
||||
this.graph.showItem(
|
||||
succeedIds
|
||||
.filter((succeedId) => succeedId !== id)
|
||||
.concat(succeedEdgeIds),
|
||||
);
|
||||
|
||||
// for tree graph view, remove the node from the parent's children list
|
||||
graphCore.setParent(id, undefined, 'tree');
|
||||
// for tree graph view, make the its children to be roots
|
||||
graphCore
|
||||
.getChildren(id, 'tree')
|
||||
.forEach((child) =>
|
||||
graphCore.setParent(child.id, undefined, 'tree'),
|
||||
);
|
||||
|
||||
// remove the node data
|
||||
graphCore.removeNode(id);
|
||||
delete parentMap[prevModel.id];
|
||||
}
|
||||
@ -682,6 +789,21 @@ export class DataController {
|
||||
graphCore.mergeNodeData(id, { parentId: parentMap[id].new });
|
||||
graphCore.setParent(id, parentMap[id].new, 'combo');
|
||||
});
|
||||
|
||||
// update tree structure
|
||||
treeChanges.forEach((change) => {
|
||||
const { type, treeKey, nodeId, newParentId } = change;
|
||||
if (type === 'TreeStructureAttached') {
|
||||
graphCore.attachTreeStructure(treeKey);
|
||||
return;
|
||||
} else if (type === 'TreeStructureChanged') {
|
||||
graphCore.setParent(nodeId, newParentId, treeKey);
|
||||
return;
|
||||
} else if (type === 'TreeStructureDetached') {
|
||||
graphCore.detachTreeStructure(treeKey);
|
||||
return;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// situation 2: idMaps is complete
|
||||
// calculate the final idMap which maps the ids from final transformed data to their comes from ids in userData
|
||||
@ -704,15 +826,17 @@ export class DataController {
|
||||
const { changes } = event;
|
||||
changes.forEach((change) => {
|
||||
const { value, id, type } = change;
|
||||
// TODO: temporary skip. how to handle tree change events?
|
||||
if (
|
||||
[
|
||||
'TreeStructureAttached',
|
||||
'TreeStructureDetached',
|
||||
'TreeStructureChanged',
|
||||
].includes(type)
|
||||
)
|
||||
if (type === 'TreeStructureAttached') {
|
||||
graphCore.attachTreeStructure(change.treeKey);
|
||||
return;
|
||||
} else if (type === 'TreeStructureChanged') {
|
||||
const { newParentId, nodeId, treeKey } = change;
|
||||
graphCore.setParent(nodeId, newParentId, treeKey);
|
||||
return;
|
||||
} else if (type === 'TreeStructureDetached') {
|
||||
graphCore.detachTreeStructure(change.treeKey);
|
||||
return;
|
||||
}
|
||||
const dataId = id || value.id;
|
||||
changeMap[dataId] = changeMap[dataId] || [];
|
||||
changeMap[dataId].push(type.toLawerCase());
|
||||
@ -727,15 +851,14 @@ export class DataController {
|
||||
const oldValue = prevModelMap[newId];
|
||||
const isNodeOrCombo = graphCore.hasNode(newId);
|
||||
if (newValue && !oldValue) {
|
||||
const addFunc = isNodeOrCombo
|
||||
? graphCore.addNode
|
||||
: graphCore.addEdge;
|
||||
addFunc(newValue);
|
||||
isNodeOrCombo
|
||||
? graphCore.addNode(newValue)
|
||||
: graphCore.addEdge(newValue);
|
||||
} else if (!newValue && oldValue) {
|
||||
const removeFunc = isNodeOrCombo
|
||||
? graphCore.removeNode
|
||||
: graphCore.removeEdge;
|
||||
removeFunc(newId);
|
||||
isNodeOrCombo
|
||||
? graphCore.removeNode(newId)
|
||||
: graphCore.removeEdge(newId);
|
||||
// TODO: update combo tree and tree graph
|
||||
} else {
|
||||
if (!comesFromIds?.length) {
|
||||
// no comesForm, find same id in userGraphCore to follow the change, if it not found, diff new and old data value of graphCore (inner data)
|
||||
@ -748,11 +871,9 @@ export class DataController {
|
||||
isNodeOrCombo,
|
||||
diff,
|
||||
);
|
||||
} else {
|
||||
} else if (changeMap[comesFromIds[0]]?.length) {
|
||||
// follow the corresponding data event in userGraphCore
|
||||
const comesFromChanges = changeMap[comesFromIds[0]];
|
||||
if (comesFromChanges?.length)
|
||||
syncUpdateToGraphCore(newId, newValue, oldValue, isNodeOrCombo);
|
||||
syncUpdateToGraphCore(newId, newValue, oldValue, isNodeOrCombo);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -771,7 +892,6 @@ export class DataController {
|
||||
graphCore.setParent(node.id, node.data.parentId as ID, 'combo');
|
||||
});
|
||||
}
|
||||
this.comboTreeView?.refreshCache();
|
||||
});
|
||||
}
|
||||
|
||||
@ -798,6 +918,34 @@ export class DataController {
|
||||
});
|
||||
return { data: dataCloned, idMaps };
|
||||
}
|
||||
|
||||
/**
|
||||
* convert and store tree structure to graphCore
|
||||
* @param dataType
|
||||
* @param data
|
||||
*/
|
||||
private updateTreeGraph(dataType, data) {
|
||||
this.userGraphCore.attachTreeStructure('tree');
|
||||
if (dataType === 'treeData') {
|
||||
// tree structure storing
|
||||
data.edges.forEach((edge) => {
|
||||
const { source, target } = edge;
|
||||
this.userGraphCore.setParent(target, source, 'tree');
|
||||
});
|
||||
} else {
|
||||
// graph data to tree structure and storing
|
||||
const rootIds = data.nodes
|
||||
.filter((node) => node.data.isRoot)
|
||||
.map((node) => node.id);
|
||||
graphData2TreeData({}, data, rootIds).forEach((tree) => {
|
||||
traverse(tree, (node) => {
|
||||
node.children?.forEach((child) => {
|
||||
this.userGraphCore.setParent(child.id, node.id, 'tree');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,8 +126,6 @@ export class ItemController {
|
||||
[id: string]: Node | Edge | Combo | Group;
|
||||
} = {};
|
||||
|
||||
// private comboTreeView: GraphView<any, any>;
|
||||
|
||||
constructor(graph: IGraph<any, any>) {
|
||||
this.graph = graph;
|
||||
// get mapper for node / edge / combo
|
||||
@ -168,6 +166,9 @@ export class ItemController {
|
||||
this.graph.hooks.transientupdate.tap(this.onTransientUpdate.bind(this));
|
||||
this.graph.hooks.viewportchange.tap(this.onViewportChange.bind(this));
|
||||
this.graph.hooks.themechange.tap(this.onThemeChange.bind(this));
|
||||
this.graph.hooks.treecollapseexpand.tap(
|
||||
this.onTreeCollapseExpand.bind(this),
|
||||
);
|
||||
this.graph.hooks.destroy.tap(this.onDestroy.bind(this));
|
||||
}
|
||||
|
||||
@ -258,6 +259,7 @@ export class ItemController {
|
||||
this.renderCombos(combos, theme.combo, graphCore);
|
||||
this.renderEdges(edges, theme.edge);
|
||||
this.sortByComboTree(graphCore);
|
||||
// collapse the combos which has 'collapsed' in initial data
|
||||
if (graphCore.hasTreeStructure('combo')) {
|
||||
graphCoreTreeDfs(
|
||||
graphCore,
|
||||
@ -266,8 +268,21 @@ export class ItemController {
|
||||
if (child.data.collapsed) this.collapseCombo(graphCore, child);
|
||||
},
|
||||
'BT',
|
||||
'combo',
|
||||
);
|
||||
}
|
||||
// collapse the sub tree which has 'collapsed' in initial data
|
||||
const collapseNodes = [];
|
||||
graphCoreTreeDfs(
|
||||
graphCore,
|
||||
graphCore.getRoots('tree'),
|
||||
(child) => {
|
||||
if (child.data.collapsed) collapseNodes.push(child);
|
||||
},
|
||||
'BT',
|
||||
'tree',
|
||||
);
|
||||
this.collapseSubTree(collapseNodes, graphCore, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,14 +295,21 @@ export class ItemController {
|
||||
graphCore: GraphCore;
|
||||
theme: ThemeSpecification;
|
||||
upsertAncestors?: boolean;
|
||||
animate?: boolean;
|
||||
action?: 'updatePosition';
|
||||
callback?: (
|
||||
model: NodeModel | EdgeModel | ComboModel,
|
||||
canceled?: boolean,
|
||||
) => void;
|
||||
}) {
|
||||
const {
|
||||
changes,
|
||||
graphCore,
|
||||
action,
|
||||
animate = true,
|
||||
upsertAncestors = true,
|
||||
theme = {},
|
||||
callback = () => {},
|
||||
} = param;
|
||||
const groupedChanges = getGroupedChanges(graphCore, changes);
|
||||
const { itemMap } = this;
|
||||
@ -298,10 +320,9 @@ export class ItemController {
|
||||
({ value }) => {
|
||||
const { id } = value;
|
||||
const item = itemMap[id];
|
||||
if (item) {
|
||||
item.destroy();
|
||||
delete itemMap[id];
|
||||
}
|
||||
if (!item) return;
|
||||
item.destroy();
|
||||
delete itemMap[id];
|
||||
},
|
||||
);
|
||||
|
||||
@ -381,8 +402,16 @@ export class ItemController {
|
||||
const onlyMove = action === 'updatePosition';
|
||||
const item = itemMap[id] as Node | Combo;
|
||||
const type = item.getType();
|
||||
if (onlyMove && type === 'node' && isNaN(current.x) && isNaN(current.y))
|
||||
const innerModel = graphCore.getNode(id);
|
||||
if (
|
||||
onlyMove &&
|
||||
type === 'node' &&
|
||||
isNaN(current.x) &&
|
||||
isNaN(current.y)
|
||||
) {
|
||||
callback(innerModel, true);
|
||||
return;
|
||||
}
|
||||
// update the theme if the dataType value is changed
|
||||
let itemTheme;
|
||||
if (
|
||||
@ -396,7 +425,6 @@ export class ItemController {
|
||||
nodeTheme,
|
||||
);
|
||||
}
|
||||
const innerModel = graphCore.getNode(id);
|
||||
const relatedEdgeInnerModels = graphCore.getRelatedEdges(id);
|
||||
const nodeRelatedIdsToUpdate: ID[] = [];
|
||||
relatedEdgeInnerModels.forEach((edge) => {
|
||||
@ -415,11 +443,13 @@ export class ItemController {
|
||||
isReplace,
|
||||
itemTheme,
|
||||
onlyMove,
|
||||
animate,
|
||||
// call after updating finished
|
||||
() => {
|
||||
item.onframe();
|
||||
(_, canceled) => {
|
||||
item.onframe?.();
|
||||
// @ts-ignore
|
||||
item.onframe = undefined;
|
||||
callback(innerModel, canceled);
|
||||
},
|
||||
);
|
||||
|
||||
@ -476,7 +506,7 @@ export class ItemController {
|
||||
this.graph.hideItem(innerModel.id);
|
||||
}
|
||||
});
|
||||
updateRelates();
|
||||
updateRelatesThrottle();
|
||||
}
|
||||
|
||||
// === 6. update edges' data ===
|
||||
@ -513,7 +543,15 @@ export class ItemController {
|
||||
}
|
||||
const item = itemMap[id];
|
||||
const innerModel = graphCore.getEdge(id);
|
||||
item.update(innerModel, { current, previous }, isReplace, itemTheme);
|
||||
item.update(
|
||||
innerModel,
|
||||
{ current, previous },
|
||||
isReplace,
|
||||
itemTheme,
|
||||
undefined,
|
||||
animate,
|
||||
(_, canceled) => callback(innerModel, canceled),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -538,9 +576,25 @@ export class ItemController {
|
||||
}
|
||||
|
||||
// === 8. combo tree structure change, resort the shapes ===
|
||||
if (groupedChanges.TreeStructureChanged.length) {
|
||||
if (groupedChanges.ComboStructureChanged.length) {
|
||||
this.sortByComboTree(graphCore);
|
||||
}
|
||||
|
||||
// === 9. tree data structure change, hide the new node and edge while one of the ancestor is collapsed ===
|
||||
if (groupedChanges.TreeStructureChanged.length) {
|
||||
groupedChanges.TreeStructureChanged.forEach((change) => {
|
||||
const { nodeId } = change;
|
||||
// hide it when an ancestor is collapsed
|
||||
let parent = graphCore.getParent(nodeId, 'tree');
|
||||
while (parent) {
|
||||
if (parent.data.collapsed) {
|
||||
this.graph.hideItem(nodeId, true);
|
||||
break;
|
||||
}
|
||||
parent = graphCore.getParent(parent.id, 'tree');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -593,21 +647,20 @@ export class ItemController {
|
||||
if (type === 'edge') {
|
||||
item.show(animate);
|
||||
} else {
|
||||
let anccestorCollapsed = false;
|
||||
if (graphCore.hasTreeStructure('combo')) {
|
||||
let anccestorCollapsed = false;
|
||||
traverseAncestors(graphCore, [item.model], (model) => {
|
||||
if (model.data.collapsed) anccestorCollapsed = true;
|
||||
return anccestorCollapsed;
|
||||
});
|
||||
if (anccestorCollapsed) return;
|
||||
}
|
||||
|
||||
const relatedEdges = graphCore.getRelatedEdges(id);
|
||||
|
||||
item.show(animate);
|
||||
relatedEdges.forEach(({ id: edgeId, source, target }) => {
|
||||
if (this.getItemVisible(source) && this.getItemVisible(target))
|
||||
this.itemMap[edgeId]?.show();
|
||||
this.itemMap[edgeId]?.show(animate);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -615,7 +668,7 @@ export class ItemController {
|
||||
if (type !== 'edge') {
|
||||
const relatedEdges = graphCore.getRelatedEdges(id);
|
||||
relatedEdges.forEach(({ id: edgeId }) => {
|
||||
this.itemMap[edgeId]?.hide();
|
||||
this.itemMap[edgeId]?.hide(animate);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -979,11 +1032,13 @@ export class ItemController {
|
||||
console.warn(
|
||||
`The source node ${source} is not exist in the graph for edge ${id}, please add the node first`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!targetItem) {
|
||||
console.warn(
|
||||
`The source node ${source} is not exist in the graph for edge ${id}, please add the node first`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// get the base styles from theme
|
||||
let dataType;
|
||||
@ -1164,6 +1219,134 @@ export class ItemController {
|
||||
// remove related virtual edges
|
||||
this.graph.removeData('edge', uniq(relatedVirtualEdgeIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse or expand a sub tree according to action
|
||||
* @param params
|
||||
*/
|
||||
private onTreeCollapseExpand(params: {
|
||||
ids: ID[];
|
||||
animate: boolean;
|
||||
action: 'collapse' | 'expand';
|
||||
graphCore: GraphCore;
|
||||
}) {
|
||||
const { ids, animate, action, graphCore } = params;
|
||||
const rootModels = ids.map((id) => graphCore.getNode(id));
|
||||
switch (action) {
|
||||
case 'collapse':
|
||||
this.collapseSubTree(rootModels, graphCore, animate);
|
||||
break;
|
||||
case 'expand':
|
||||
default:
|
||||
this.expandSubTree(rootModels, graphCore, animate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse sub tree(s).
|
||||
* @param rootModels The root node models of sub trees
|
||||
* @param graphCore
|
||||
* @param animate Whether enable animations for expanding, true by default
|
||||
* @returns
|
||||
*/
|
||||
private collapseSubTree(
|
||||
rootModels: NodeModel[],
|
||||
graphCore: GraphCore,
|
||||
animate: boolean = true,
|
||||
) {
|
||||
let positions = [];
|
||||
rootModels.forEach((root) => {
|
||||
let shouldCollapse = true;
|
||||
const nodes = [];
|
||||
graphCoreTreeDfs(
|
||||
graphCore,
|
||||
[root],
|
||||
(node) => {
|
||||
if (node.id === root.id) return;
|
||||
const neighbors = graphCore.getNeighbors(node.id);
|
||||
if (
|
||||
neighbors.length > 2 ||
|
||||
(!graphCore.getChildren(node.id, 'tree')?.length &&
|
||||
neighbors.length > 1)
|
||||
) {
|
||||
shouldCollapse = false;
|
||||
}
|
||||
nodes.push(node);
|
||||
},
|
||||
'TB',
|
||||
'tree',
|
||||
{
|
||||
stopAllFn: () => !shouldCollapse,
|
||||
},
|
||||
);
|
||||
if (shouldCollapse) {
|
||||
positions = positions.concat(
|
||||
nodes.map((node) => ({
|
||||
id: node.id,
|
||||
data: { x: root.data.x, y: root.data.y },
|
||||
})),
|
||||
);
|
||||
}
|
||||
});
|
||||
if (!positions.length) return;
|
||||
this.graph.updateNodePosition(
|
||||
positions,
|
||||
undefined,
|
||||
!animate,
|
||||
(model, canceled) => {
|
||||
this.graph.hideItem(model.id, canceled);
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand sub tree(s).
|
||||
* @param rootModels The root node models of sub trees.
|
||||
* @param graphCore
|
||||
* @param animate Whether enable animations for expanding, true by default.
|
||||
* @returns
|
||||
*/
|
||||
private expandSubTree(
|
||||
rootModels: NodeModel[],
|
||||
graphCore: GraphCore,
|
||||
animate: boolean = true,
|
||||
) {
|
||||
let allNodeIds = [];
|
||||
let allEdgeIds = [];
|
||||
rootModels.forEach((root) => {
|
||||
const nodeIds = [];
|
||||
graphCoreTreeDfs(
|
||||
graphCore,
|
||||
[root],
|
||||
(node) => nodeIds.push(node.id),
|
||||
'TB',
|
||||
'tree',
|
||||
{
|
||||
stopBranchFn: (node) => {
|
||||
const shouldStop =
|
||||
node.id !== root.id && (node.data.collapsed as boolean);
|
||||
if (shouldStop) nodeIds.push(node.id);
|
||||
return shouldStop;
|
||||
},
|
||||
},
|
||||
);
|
||||
allEdgeIds = allEdgeIds.concat(
|
||||
graphCore
|
||||
.getAllEdges()
|
||||
.filter(
|
||||
(edge) =>
|
||||
nodeIds.includes(edge.source) && nodeIds.includes(edge.target),
|
||||
)
|
||||
.map((edge) => edge.id),
|
||||
);
|
||||
allNodeIds = allNodeIds.concat(nodeIds.filter((id) => id !== root.id));
|
||||
});
|
||||
const ids = uniq(allNodeIds.concat(allEdgeIds));
|
||||
this.graph.showItem(ids, !animate);
|
||||
this.graph.layout(undefined, !animate);
|
||||
}
|
||||
}
|
||||
|
||||
const getItemTheme = (
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Animation, DisplayObject, IAnimationEffectTiming } from '@antv/g';
|
||||
import Hierarchy from '@antv/hierarchy';
|
||||
import { Graph as GraphLib } from '@antv/graphlib';
|
||||
import {
|
||||
isLayoutWithIterations,
|
||||
@ -17,6 +18,7 @@ import {
|
||||
} from '../../types';
|
||||
import { GraphCore } from '../../types/data';
|
||||
import { EdgeModelData } from '../../types/edge';
|
||||
import { layoutOneTree } from '../../util/layout';
|
||||
|
||||
/**
|
||||
* Manages layout extensions and graph layout.
|
||||
@ -48,6 +50,7 @@ export class LayoutController {
|
||||
private async onLayout(params: {
|
||||
graphCore: GraphCore;
|
||||
options: LayoutOptions;
|
||||
animate?: boolean;
|
||||
}) {
|
||||
/**
|
||||
* The final calculated result.
|
||||
@ -57,13 +60,15 @@ export class LayoutController {
|
||||
// Stop currentLayout if any.
|
||||
this.stopLayout();
|
||||
|
||||
const { graphCore, options } = params;
|
||||
const { graphCore, options, animate = true } = params;
|
||||
const layoutNodes = graphCore
|
||||
.getAllNodes()
|
||||
.filter((node) => node.data.visible !== false && !node.data._isCombo);
|
||||
.filter(
|
||||
(node) => this.graph.getItemVisible(node.id) && !node.data._isCombo,
|
||||
);
|
||||
const layoutNodesIdMap = {};
|
||||
layoutNodes.forEach((node) => (layoutNodesIdMap[node.id] = true));
|
||||
const layoutGraphCore = new GraphLib<NodeModelData, EdgeModelData>({
|
||||
const layoutData = {
|
||||
nodes: layoutNodes,
|
||||
edges: graphCore
|
||||
.getAllEdges()
|
||||
@ -71,7 +76,10 @@ export class LayoutController {
|
||||
(edge) =>
|
||||
layoutNodesIdMap[edge.source] && layoutNodesIdMap[edge.target],
|
||||
),
|
||||
});
|
||||
};
|
||||
const layoutGraphCore = new GraphLib<NodeModelData, EdgeModelData>(
|
||||
layoutData,
|
||||
);
|
||||
|
||||
this.graph.emit('startlayout');
|
||||
|
||||
@ -112,6 +120,19 @@ export class LayoutController {
|
||||
throw new Error(`Unknown layout algorithm: ${type}`);
|
||||
}
|
||||
|
||||
if (Hierarchy[type]) {
|
||||
// tree layout type
|
||||
await this.handleTreeLayout(
|
||||
type,
|
||||
options,
|
||||
animationEffectTiming,
|
||||
graphCore,
|
||||
layoutData,
|
||||
animate,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize layout.
|
||||
const layout = new layoutCtor(rest);
|
||||
this.currentLayout = layout;
|
||||
@ -170,7 +191,57 @@ export class LayoutController {
|
||||
this.graph.emit('endlayout');
|
||||
|
||||
// Update nodes' positions.
|
||||
this.updateNodesPosition(positions);
|
||||
this.updateNodesPosition(positions, animate);
|
||||
}
|
||||
|
||||
async handleTreeLayout(
|
||||
type,
|
||||
options,
|
||||
animationEffectTiming,
|
||||
graphCore,
|
||||
layoutData,
|
||||
animate,
|
||||
) {
|
||||
const { animated = false, rootIds = [], begin = [0, 0] } = options;
|
||||
const nodePositions = [];
|
||||
const nodeMap = {};
|
||||
// tree layout with tree data
|
||||
const trees = graphCore
|
||||
.getRoots('tree')
|
||||
.filter(
|
||||
(node) => !node.data._isCombo, // this.graph.getItemVisible(node.id) &&
|
||||
)
|
||||
.map((node) => ({ id: node.id, children: [] }));
|
||||
|
||||
trees.forEach((tree) => {
|
||||
nodeMap[tree.id] = tree;
|
||||
graphCore.dfsTree(
|
||||
tree.id,
|
||||
(node) => {
|
||||
nodeMap[node.id].children = graphCore
|
||||
.getChildren(node.id, 'tree')
|
||||
.filter((node) => !node.data._isCombo)
|
||||
.map((child) => {
|
||||
nodeMap[child.id] = { id: child.id, children: [] };
|
||||
return nodeMap[child.id];
|
||||
});
|
||||
},
|
||||
'tree',
|
||||
);
|
||||
layoutOneTree(tree, type, options, nodeMap, nodePositions, begin);
|
||||
});
|
||||
if (animated) {
|
||||
await this.animateLayoutWithoutIterations(
|
||||
{ nodes: nodePositions, edges: [] },
|
||||
animationEffectTiming,
|
||||
);
|
||||
}
|
||||
this.graph.emit('endlayout');
|
||||
this.updateNodesPosition(
|
||||
{ nodes: nodePositions, edges: [] },
|
||||
animated || animate,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
stopLayout() {
|
||||
@ -202,8 +273,11 @@ export class LayoutController {
|
||||
}
|
||||
}
|
||||
|
||||
private updateNodesPosition(positions: LayoutMapping) {
|
||||
this.graph.updateNodePosition(positions.nodes);
|
||||
private updateNodesPosition(
|
||||
positions: LayoutMapping,
|
||||
animate: boolean = true,
|
||||
) {
|
||||
this.graph.updateNodePosition(positions.nodes, undefined, !animate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,7 +14,7 @@ import { CameraAnimationOptions } from '../types/animate';
|
||||
import { BehaviorOptionsOf, BehaviorRegistry } from '../types/behavior';
|
||||
import { ComboModel } from '../types/combo';
|
||||
import { Padding, Point } from '../types/common';
|
||||
import { DataChangeType, GraphCore } from '../types/data';
|
||||
import { DataChangeType, DataConfig, GraphCore } from '../types/data';
|
||||
import { EdgeModel, EdgeModelData } from '../types/edge';
|
||||
import { Hooks, ViewportChangeHookParams } from '../types/hook';
|
||||
import { ITEM_TYPE, ShapeStyle, SHAPE_TYPE } from '../types/item';
|
||||
@ -250,6 +250,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
changes: GraphChange<NodeModelData, EdgeModelData>[];
|
||||
graphCore: GraphCore;
|
||||
theme: ThemeSpecification;
|
||||
animate?: boolean;
|
||||
upsertAncestors?: boolean;
|
||||
}>({ name: 'itemchange' }),
|
||||
render: new Hook<{
|
||||
@ -259,7 +260,11 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
}>({
|
||||
name: 'render',
|
||||
}),
|
||||
layout: new Hook<{ graphCore: GraphCore }>({ name: 'layout' }),
|
||||
layout: new Hook<{
|
||||
graphCore: GraphCore;
|
||||
options?: LayoutOptions;
|
||||
animate?: boolean;
|
||||
}>({ name: 'layout' }),
|
||||
viewportchange: new Hook<ViewportChangeHookParams>({ name: 'viewport' }),
|
||||
modechange: new Hook<{ mode: string }>({ name: 'modechange' }),
|
||||
behaviorchange: new Hook<{
|
||||
@ -309,6 +314,12 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
transient: Canvas;
|
||||
};
|
||||
}>({ name: 'init' }),
|
||||
treecollapseexpand: new Hook<{
|
||||
ids: ID[];
|
||||
animate: boolean;
|
||||
action: 'collapse' | 'expand';
|
||||
graphCore: GraphCore;
|
||||
}>({ name: 'treecollapseexpand' }),
|
||||
destroy: new Hook<{}>({ name: 'destroy' }),
|
||||
};
|
||||
}
|
||||
@ -355,7 +366,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
* @returns
|
||||
* @group Data
|
||||
*/
|
||||
public async read(data: GraphData) {
|
||||
public async read(data: DataConfig) {
|
||||
this.hooks.datachange.emit({ data, type: 'replace' });
|
||||
const emitRender = async () => {
|
||||
this.hooks.render.emit({
|
||||
@ -387,7 +398,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
* @group Data
|
||||
*/
|
||||
public async changeData(
|
||||
data: GraphData,
|
||||
data: DataConfig,
|
||||
type: 'replace' | 'mergeReplace' = 'mergeReplace',
|
||||
) {
|
||||
this.hooks.datachange.emit({ data, type });
|
||||
@ -1028,9 +1039,21 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
disableAnimate: boolean = false,
|
||||
callback?: (
|
||||
model: NodeModel | EdgeModel | ComboModel,
|
||||
canceled?: boolean,
|
||||
) => void,
|
||||
stack?: boolean,
|
||||
) {
|
||||
return this.updatePosition('node', models, upsertAncestors, stack);
|
||||
return this.updatePosition(
|
||||
'node',
|
||||
models,
|
||||
upsertAncestors,
|
||||
disableAnimate,
|
||||
callback,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1048,9 +1071,18 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
disableAnimate: boolean = false,
|
||||
callback?: (model: NodeModel | EdgeModel | ComboModel) => void,
|
||||
stack?: boolean,
|
||||
) {
|
||||
return this.updatePosition('combo', models, upsertAncestors, stack);
|
||||
return this.updatePosition(
|
||||
'combo',
|
||||
models,
|
||||
upsertAncestors,
|
||||
disableAnimate,
|
||||
callback,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
|
||||
private updatePosition(
|
||||
@ -1061,6 +1093,11 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
disableAnimate: boolean = false,
|
||||
callback?: (
|
||||
model: NodeModel | EdgeModel | ComboModel,
|
||||
canceled?: boolean,
|
||||
) => void,
|
||||
stack?: boolean,
|
||||
) {
|
||||
const modelArr = isArray(models) ? models : [models];
|
||||
@ -1075,6 +1112,8 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
theme: specification,
|
||||
upsertAncestors,
|
||||
action: 'updatePosition',
|
||||
animate: !disableAnimate,
|
||||
callback,
|
||||
});
|
||||
this.emit('afteritemchange', {
|
||||
type,
|
||||
@ -1105,13 +1144,13 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
* @returns
|
||||
* @group Item
|
||||
*/
|
||||
public showItem(ids: ID | ID[], disableAniamte?: boolean) {
|
||||
public showItem(ids: ID | ID[], disableAnimate: boolean = false) {
|
||||
const idArr = isArray(ids) ? ids : [ids];
|
||||
this.hooks.itemvisibilitychange.emit({
|
||||
ids: idArr as ID[],
|
||||
value: true,
|
||||
graphCore: this.dataController.graphCore,
|
||||
animate: !disableAniamte,
|
||||
animate: !disableAnimate,
|
||||
});
|
||||
}
|
||||
/**
|
||||
@ -1120,13 +1159,13 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
* @returns
|
||||
* @group Item
|
||||
*/
|
||||
public hideItem(ids: ID | ID[], disableAniamte?: boolean) {
|
||||
public hideItem(ids: ID | ID[], disableAnimate: boolean = false) {
|
||||
const idArr = isArray(ids) ? ids : [ids];
|
||||
this.hooks.itemvisibilitychange.emit({
|
||||
ids: idArr as ID[],
|
||||
value: false,
|
||||
graphCore: this.dataController.graphCore,
|
||||
animate: !disableAniamte,
|
||||
animate: !disableAnimate,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1364,7 +1403,10 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
/**
|
||||
* Layout the graph (with current configurations if cfg is not assigned).
|
||||
*/
|
||||
public async layout(options?: LayoutOptions) {
|
||||
public async layout(
|
||||
options?: LayoutOptions,
|
||||
disableAnimate: boolean = false,
|
||||
) {
|
||||
const { graphCore } = this.dataController;
|
||||
const formattedOptions = {
|
||||
...this.getSpecification().layout,
|
||||
@ -1400,6 +1442,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
await this.hooks.layout.emitLinearAsync({
|
||||
graphCore,
|
||||
options: formattedOptions,
|
||||
animate: !disableAnimate,
|
||||
});
|
||||
this.emit('afterlayout');
|
||||
}
|
||||
@ -1653,6 +1696,47 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
return this.itemController.getTransient(String(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse sub tree(s).
|
||||
* @param ids Root id(s) of the sub trees.
|
||||
* @param disableAnimate Whether disable the animations for this operation.
|
||||
* @param stack Whether push this operation to stack.
|
||||
* @returns
|
||||
* @group Tree
|
||||
*/
|
||||
public collapse(
|
||||
ids: ID | ID[],
|
||||
disableAnimate: boolean = false,
|
||||
stack?: boolean,
|
||||
) {
|
||||
this.hooks.treecollapseexpand.emit({
|
||||
ids: isArray(ids) ? ids : [ids],
|
||||
action: 'collapse',
|
||||
animate: !disableAnimate,
|
||||
graphCore: this.dataController.graphCore,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Expand sub tree(s).
|
||||
* @param ids Root id(s) of the sub trees.
|
||||
* @param disableAnimate Whether disable the animations for this operation.
|
||||
* @param stack Whether push this operation to stack.
|
||||
* @returns
|
||||
* @group Tree
|
||||
*/
|
||||
public expand(
|
||||
ids: ID | ID[],
|
||||
disableAnimate: boolean = false,
|
||||
stack?: boolean,
|
||||
) {
|
||||
this.hooks.treecollapseexpand.emit({
|
||||
ids: isArray(ids) ? ids : [ids],
|
||||
action: 'expand',
|
||||
animate: !disableAnimate,
|
||||
graphCore: this.dataController.graphCore,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the graph instance and remove the related canvases.
|
||||
* @returns
|
||||
|
106
packages/g6/src/stdlib/behavior/collapse-expand-tree.ts
Normal file
106
packages/g6/src/stdlib/behavior/collapse-expand-tree.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { Behavior } from '../../types/behavior';
|
||||
import { IG6GraphEvent } from '../../types/event';
|
||||
|
||||
const ALLOWED_TRIGGERS = ['click', 'dblclick'] as const;
|
||||
type Trigger = (typeof ALLOWED_TRIGGERS)[number];
|
||||
|
||||
interface Options {
|
||||
/**
|
||||
* The key to pressed with mouse click to apply multiple selection.
|
||||
* Defaults to `"shift"`.
|
||||
* Could be "shift", "ctrl", "alt", or "meta".
|
||||
*/
|
||||
trigger: Trigger;
|
||||
/**
|
||||
* The event name to trigger when select/unselect an item.
|
||||
*/
|
||||
eventName: string;
|
||||
/**
|
||||
* Whether disable the collapse / expand animation triggered by this behavior.
|
||||
*/
|
||||
disableAnimate: boolean;
|
||||
/**
|
||||
* Whether allow the behavior happen on the current item.
|
||||
*/
|
||||
shouldBegin: (event: IG6GraphEvent) => boolean;
|
||||
/**
|
||||
* Whether to update item state.
|
||||
* If it returns false, you may probably listen to `eventName` and
|
||||
* manage states or data manually
|
||||
*/
|
||||
shouldUpdate: (event: IG6GraphEvent) => boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS: Options = {
|
||||
trigger: 'click',
|
||||
eventName: '',
|
||||
disableAnimate: false,
|
||||
shouldBegin: () => true,
|
||||
shouldUpdate: () => true,
|
||||
};
|
||||
|
||||
export default class CollapseExpandTree extends Behavior {
|
||||
options: Options;
|
||||
private timeout: NodeJS.Timeout = undefined;
|
||||
|
||||
constructor(options: Partial<Options>) {
|
||||
super(Object.assign({}, DEFAULT_OPTIONS, options));
|
||||
// Validate options
|
||||
if (options.trigger && !ALLOWED_TRIGGERS.includes(options.trigger)) {
|
||||
console.warn(
|
||||
`G6: Invalid trigger option "${options.trigger}" for collapse-expand-tree behavior!`,
|
||||
);
|
||||
this.options.trigger = DEFAULT_OPTIONS.trigger;
|
||||
}
|
||||
}
|
||||
|
||||
getEvents = () => {
|
||||
return this.options.trigger === 'dblclick'
|
||||
? {
|
||||
'node:dblclick': this.onClick,
|
||||
}
|
||||
: {
|
||||
'node:click': this.onClickBesideDblClick,
|
||||
};
|
||||
};
|
||||
|
||||
public onClickBesideDblClick(event: IG6GraphEvent) {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = undefined;
|
||||
return;
|
||||
}
|
||||
this.timeout = setTimeout(() => {
|
||||
this.timeout = undefined;
|
||||
this.onClick(event);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
public onClick(event: IG6GraphEvent) {
|
||||
if (!this.options.shouldBegin(event)) return;
|
||||
const { itemId, itemType } = event;
|
||||
const { disableAnimate } = this.options;
|
||||
|
||||
const model = this.graph.getNodeData(itemId);
|
||||
if (!model) {
|
||||
console.warn(`Node with id ${itemId} is not exist`);
|
||||
return;
|
||||
}
|
||||
let action = 'expand';
|
||||
if (model.data.collapsed) {
|
||||
this.graph.expand(itemId, disableAnimate);
|
||||
} else {
|
||||
this.graph.collapse(itemId, disableAnimate);
|
||||
action = 'collapse';
|
||||
}
|
||||
|
||||
// Emit an event.
|
||||
if (this.options.eventName) {
|
||||
this.graph.emit(this.options.eventName, {
|
||||
action,
|
||||
itemId,
|
||||
itemType,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import { registry as layoutRegistry } from '@antv/layout';
|
||||
import Hierarchy from '@antv/hierarchy';
|
||||
import { Lib } from '../types/stdlib';
|
||||
import ActivateRelations from './behavior/activate-relations';
|
||||
import BrushSelect from './behavior/brush-select';
|
||||
@ -30,6 +31,7 @@ import TrackCanvas3D from './behavior/track-canvas-3d';
|
||||
import OrbitCanvas3D from './behavior/orbit-canvas-3d';
|
||||
import HoverActivate from './behavior/hover-activate';
|
||||
import CollapseExpandCombo from './behavior/collapse-expand-combo';
|
||||
import CollapseExpandTree from './behavior/collapse-expand-tree';
|
||||
import { CubicEdge } from './item/edge/cubic';
|
||||
import { CubicHorizonEdge } from './item/edge/cubic-horizon';
|
||||
import { CubicVerticalEdge } from './item/edge/cubic-vertical';
|
||||
@ -48,7 +50,10 @@ const stdLib = {
|
||||
spec: SpecThemeSolver,
|
||||
subject: SubjectThemeSolver,
|
||||
},
|
||||
layouts: layoutRegistry,
|
||||
layouts: {
|
||||
...layoutRegistry,
|
||||
...Hierarchy,
|
||||
},
|
||||
behaviors: {
|
||||
'activate-relations': ActivateRelations,
|
||||
'drag-canvas': DragCanvas,
|
||||
@ -57,6 +62,7 @@ const stdLib = {
|
||||
'drag-node': DragNode,
|
||||
'drag-combo': DragCombo,
|
||||
'collapse-expand-combo': CollapseExpandCombo,
|
||||
'collapse-expand-tree': CollapseExpandTree,
|
||||
'click-select': ClickSelect,
|
||||
'brush-select': BrushSelect,
|
||||
'lasso-select': LassoSelect,
|
||||
|
@ -35,8 +35,6 @@ export default class Grid extends Base {
|
||||
public init(graph: IGraph) {
|
||||
super.init(graph);
|
||||
const minZoom = graph.getZoom();
|
||||
// console.log('minZoom', minZoom);
|
||||
// console.log('graph', graph.canvas);
|
||||
const graphContainer = graph.container;
|
||||
const canvas = this.canvas || graphContainer.firstChild.nextSibling;
|
||||
const [width, height] = graph.getSize();
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Graph as GraphLib } from '@antv/graphlib';
|
||||
import { Graph as GraphLib, TreeData } from '@antv/graphlib';
|
||||
import { ComboUserModel } from './combo';
|
||||
import { NodeDisplayModelData, NodeModelData, NodeUserModel } from './node';
|
||||
import { EdgeDisplayModelData, EdgeModelData, EdgeUserModel } from './edge';
|
||||
import { NodeUserModelData } from './node';
|
||||
|
||||
export interface GraphData {
|
||||
nodes?: NodeUserModel[];
|
||||
@ -9,16 +10,26 @@ export interface GraphData {
|
||||
combos?: ComboUserModel[];
|
||||
}
|
||||
|
||||
export interface InlineDataConfig {
|
||||
type: 'inline';
|
||||
export interface InlineGraphDataConfig {
|
||||
type: 'graphData';
|
||||
value: GraphData;
|
||||
}
|
||||
export interface InlineTreeDataConfig {
|
||||
type: 'treeData';
|
||||
value: TreeData<NodeUserModelData> | TreeData<NodeUserModelData>[];
|
||||
}
|
||||
|
||||
export interface FetchDataConfig {
|
||||
type: 'fetch';
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type DataConfig =
|
||||
| GraphData
|
||||
| InlineGraphDataConfig
|
||||
| InlineTreeDataConfig
|
||||
| FetchDataConfig;
|
||||
|
||||
export type GraphCore = GraphLib<NodeModelData, EdgeModelData>;
|
||||
export type DisplayGraphCore = GraphLib<
|
||||
NodeDisplayModelData,
|
||||
|
@ -232,6 +232,11 @@ export interface IGraph<
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
disableAnimate?: boolean,
|
||||
callback?: (
|
||||
model: NodeModel | EdgeModel | ComboModel,
|
||||
canceled?: boolean,
|
||||
) => void,
|
||||
stack?: boolean,
|
||||
) => NodeModel | ComboModel | NodeModel[] | ComboModel[];
|
||||
|
||||
@ -249,6 +254,8 @@ export interface IGraph<
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
disableAnimate?: boolean,
|
||||
callback?: (model: NodeModel | EdgeModel | ComboModel) => void,
|
||||
stack?: boolean,
|
||||
) => NodeModel | ComboModel | NodeModel[] | ComboModel[];
|
||||
|
||||
@ -444,14 +451,14 @@ export interface IGraph<
|
||||
* @returns
|
||||
* @group Data
|
||||
*/
|
||||
showItem: (ids: ID | ID[], disableAniamte?: boolean) => void;
|
||||
showItem: (ids: ID | ID[], disableAnimate?: boolean) => void;
|
||||
/**
|
||||
* Hide the item(s).
|
||||
* @param ids the item id(s) to be hidden
|
||||
* @returns
|
||||
* @group Item
|
||||
*/
|
||||
hideItem: (ids: ID | ID[], disableAniamte?: boolean) => void;
|
||||
hideItem: (ids: ID | ID[], disableAnimate?: boolean) => void;
|
||||
/**
|
||||
* Make the item(s) to the front.
|
||||
* @param ids the item id(s) to front
|
||||
@ -543,7 +550,7 @@ export interface IGraph<
|
||||
/**
|
||||
* Layout the graph (with current configurations if cfg is not assigned).
|
||||
*/
|
||||
layout: (options?: LayoutOptions) => Promise<void>;
|
||||
layout: (options?: LayoutOptions, disableAnimate?: boolean) => Promise<void>;
|
||||
stopLayout: () => void;
|
||||
|
||||
// ===== interaction =====
|
||||
@ -627,4 +634,24 @@ export interface IGraph<
|
||||
type: string;
|
||||
[cfgName: string]: unknown;
|
||||
}) => void;
|
||||
|
||||
// ===== tree operations =====
|
||||
/**
|
||||
* Collapse sub tree(s).
|
||||
* @param ids Root id(s) of the sub trees.
|
||||
* @param disableAnimate Whether disable the animations for this operation.
|
||||
* @param stack Whether push this operation to stack.
|
||||
* @returns
|
||||
* @group Tree
|
||||
*/
|
||||
collapse: (ids: ID | ID[], disableAnimate?: boolean, stack?: boolean) => void;
|
||||
/**
|
||||
* Expand sub tree(s).
|
||||
* @param ids Root id(s) of the sub trees.
|
||||
* @param disableAnimate Whether disable the animations for this operation.
|
||||
* @param stack Whether push this operation to stack.
|
||||
* @returns
|
||||
* @group Tree
|
||||
*/
|
||||
expand: (ids: ID | ID[], disableAnimate?: boolean, stack?: boolean) => void;
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ import { Canvas } from '@antv/g';
|
||||
import { GraphChange, ID } from '@antv/graphlib';
|
||||
import { CameraAnimationOptions } from './animate';
|
||||
import { BehaviorOptionsOf } from './behavior';
|
||||
import { DataChangeType, GraphCore, GraphData } from './data';
|
||||
import { EdgeModelData } from './edge';
|
||||
import { DataChangeType, DataConfig, GraphCore } from './data';
|
||||
import { EdgeModel, EdgeModelData } from './edge';
|
||||
import { ITEM_TYPE, ShapeStyle, SHAPE_TYPE } from './item';
|
||||
import { LayoutOptions } from './layout';
|
||||
import { NodeModelData } from './node';
|
||||
import { NodeModel, NodeModelData } from './node';
|
||||
import { ThemeSpecification } from './theme';
|
||||
import { GraphTransformOptions } from './view';
|
||||
import { ComboModel } from './combo';
|
||||
|
||||
export interface IHook<T> {
|
||||
name: string;
|
||||
@ -35,7 +36,7 @@ export interface Hooks {
|
||||
// data
|
||||
datachange: IHook<{
|
||||
type: DataChangeType;
|
||||
data: GraphData;
|
||||
data: DataConfig;
|
||||
}>;
|
||||
itemchange: IHook<{
|
||||
type: ITEM_TYPE;
|
||||
@ -43,14 +44,20 @@ export interface Hooks {
|
||||
graphCore: GraphCore;
|
||||
theme: ThemeSpecification;
|
||||
upsertAncestors?: boolean;
|
||||
animate?: boolean;
|
||||
action?: 'updatePosition';
|
||||
callback?: (model: NodeModel | EdgeModel | ComboModel) => void;
|
||||
}>;
|
||||
render: IHook<{
|
||||
graphCore: GraphCore;
|
||||
theme: ThemeSpecification;
|
||||
transientCanvas: Canvas;
|
||||
}>; // TODO: define param template
|
||||
layout: IHook<{ graphCore: GraphCore; options?: LayoutOptions }>; // TODO: define param template
|
||||
layout: IHook<{
|
||||
graphCore: GraphCore;
|
||||
options?: LayoutOptions;
|
||||
animate?: boolean;
|
||||
}>; // TODO: define param template
|
||||
// 'updatelayout': IHook<any>; // TODO: define param template
|
||||
modechange: IHook<{ mode: string }>;
|
||||
behaviorchange: IHook<{
|
||||
@ -101,6 +108,11 @@ export interface Hooks {
|
||||
transient: Canvas;
|
||||
};
|
||||
}>;
|
||||
treecollapseexpand: IHook<{
|
||||
ids: ID[];
|
||||
action: 'collapse' | 'expand';
|
||||
graphCore: GraphCore;
|
||||
animate?: boolean;
|
||||
}>;
|
||||
destroy: IHook<{}>;
|
||||
// TODO: more timecycles here
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import {
|
||||
NodeShapeMap,
|
||||
NodeUserModel,
|
||||
} from './node';
|
||||
import { ComboStyleSet, EdgeStyleSet, NodeStyleSet } from './theme';
|
||||
|
||||
export type GShapeStyle = CircleStyleProps &
|
||||
RectStyleProps &
|
||||
@ -248,6 +249,8 @@ export interface IItem {
|
||||
displayModel: ItemDisplayModel,
|
||||
diffData?: { previous: ItemModelData; current: ItemModelData },
|
||||
diffState?: { previous: State[]; current: State[] },
|
||||
animate?: boolean,
|
||||
onfinish?: Function,
|
||||
) => void;
|
||||
/**
|
||||
* Updates the shapes.
|
||||
@ -256,7 +259,14 @@ export interface IItem {
|
||||
update: (
|
||||
model: ItemModel,
|
||||
diffData: { previous: ItemModelData; current: ItemModelData },
|
||||
isUpdate?: boolean,
|
||||
isReplace?: boolean,
|
||||
itemTheme?: {
|
||||
styles: NodeStyleSet | EdgeStyleSet | ComboStyleSet;
|
||||
lodStrategy: LodStrategyObj;
|
||||
},
|
||||
onlyMove?: boolean,
|
||||
animate?: boolean,
|
||||
onfinish?: Function,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
@ -269,6 +279,7 @@ export interface IItem {
|
||||
updatePosition: (
|
||||
displayModel: ItemDisplayModel,
|
||||
diffData?: { previous: ItemModelData; current: ItemModelData },
|
||||
animate?: boolean,
|
||||
onfinish?: Function,
|
||||
) => void;
|
||||
|
||||
|
@ -45,6 +45,10 @@ export interface NodeUserModelData extends PlainObject {
|
||||
* Reserved for combo.
|
||||
*/
|
||||
parentId?: ID;
|
||||
/**
|
||||
* Whether to be a root at when used as a tree.
|
||||
*/
|
||||
isRoot?: boolean;
|
||||
/**
|
||||
* The icon to show on the node.
|
||||
* More styles should be configured in node mapper.
|
||||
|
@ -1,12 +1,7 @@
|
||||
import { Canvas } from '@antv/g';
|
||||
import { AnimateCfg } from './animate';
|
||||
import { Point } from './common';
|
||||
import {
|
||||
FetchDataConfig,
|
||||
GraphData,
|
||||
InlineDataConfig,
|
||||
TransformerFn,
|
||||
} from './data';
|
||||
import { DataConfig, TransformerFn } from './data';
|
||||
import {
|
||||
EdgeDisplayModel,
|
||||
EdgeEncode,
|
||||
@ -36,7 +31,6 @@ export interface Specification<
|
||||
B extends BehaviorRegistry,
|
||||
T extends ThemeRegistry,
|
||||
> {
|
||||
type: 'graph' | 'tree';
|
||||
container?: string | HTMLElement;
|
||||
backgroundCanvas?: Canvas;
|
||||
canvas?: Canvas;
|
||||
@ -61,7 +55,7 @@ export interface Specification<
|
||||
optimizeThreshold?: number;
|
||||
|
||||
/** data */
|
||||
data: GraphData | InlineDataConfig | FetchDataConfig; // TODO: more
|
||||
data?: DataConfig;
|
||||
transform?:
|
||||
| string[]
|
||||
| {
|
||||
|
@ -248,7 +248,7 @@ const runAnimateGroupOnShapes = (
|
||||
maxDurationIdx = i;
|
||||
}
|
||||
if (animation) {
|
||||
animation.oncancel = () => {
|
||||
animation.onManualCancel = () => {
|
||||
hasCanceled = true;
|
||||
cancelAnimations();
|
||||
};
|
||||
@ -293,10 +293,37 @@ const runAnimateOnShape = (
|
||||
}
|
||||
});
|
||||
}
|
||||
if (JSON.stringify(animateArr[0]) === JSON.stringify(animateArr[1])) return;
|
||||
if (!checkFrames(animateArr, shape)) return;
|
||||
return shape.animate(animateArr, animateConfig);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check and format the frames. If the frames are same, return false. If frames contains undefined x or y, format them.
|
||||
* @param frames
|
||||
* @param shape
|
||||
* @returns
|
||||
*/
|
||||
const checkFrames = (frames, shape) => {
|
||||
if (JSON.stringify(frames[0]) === JSON.stringify(frames[1])) return false;
|
||||
['x', 'y'].forEach((dim) => {
|
||||
if (!frames[0].hasOwnProperty(dim)) return;
|
||||
let val;
|
||||
const formatted = [...frames];
|
||||
if (frames[0][dim] === undefined && frames[0][dim] !== frames[1][dim])
|
||||
val = frames[1][dim];
|
||||
if (frames[1][dim] === undefined && frames[0][dim] !== frames[1][dim])
|
||||
val = frames[1][dim];
|
||||
if (val !== undefined) {
|
||||
shape.style[dim] = val;
|
||||
delete formatted[0][dim];
|
||||
delete formatted[1][dim];
|
||||
}
|
||||
});
|
||||
if (JSON.stringify(frames[0]) === JSON.stringify(frames[1])) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle shape and group animations.
|
||||
* Should be called after canvas ready and shape appended.
|
||||
@ -318,7 +345,7 @@ export const animateShapes = (
|
||||
onAnimatesEnd: Function = () => {},
|
||||
): IAnimation[] => {
|
||||
if (!animates?.[timing]) {
|
||||
onAnimatesEnd();
|
||||
onAnimatesEnd(false);
|
||||
return;
|
||||
}
|
||||
const segmentedTiming =
|
||||
@ -335,7 +362,7 @@ export const animateShapes = (
|
||||
let canceled = false;
|
||||
const onfinish = () => {
|
||||
if (i >= groupKeys.length) {
|
||||
onAnimatesEnd();
|
||||
!canceled && onAnimatesEnd(canceled);
|
||||
return;
|
||||
}
|
||||
const groupAnimations = runAnimateGroupOnShapes(
|
||||
@ -345,7 +372,10 @@ export const animateShapes = (
|
||||
mergedStyles,
|
||||
timing,
|
||||
onfinish, // execute next order group
|
||||
() => (canceled = true),
|
||||
() => {
|
||||
canceled = true;
|
||||
onAnimatesEnd(canceled);
|
||||
},
|
||||
canceled,
|
||||
).filter(Boolean);
|
||||
groupAnimations.forEach((animation) => {
|
||||
@ -429,8 +459,9 @@ export const fadeOut = (id, shape, hiddenShape, animateConfig) => {
|
||||
* Make the animation to the end frame and clear it from the target shape.
|
||||
* @param animation
|
||||
*/
|
||||
export const stopAnimate = (animation) => {
|
||||
export const stopAnimate = (animation: IAnimation): Promise<any> => {
|
||||
const timing = animation.effect.getTiming();
|
||||
animation.currentTime = Number(timing.duration) + Number(timing.delay || 0);
|
||||
animation.cancel();
|
||||
animation.finish();
|
||||
return animation.finished;
|
||||
};
|
||||
|
@ -1,6 +1,14 @@
|
||||
import { NodeUserModel } from 'types';
|
||||
import { IGraph } from '../types/graph';
|
||||
import { GraphCore } from '../types/data';
|
||||
import { GraphCore, GraphData } from '../types/data';
|
||||
import { TreeData } from '@antv/graphlib';
|
||||
import { NodeUserModelData } from 'types/node';
|
||||
import { isArray } from '@antv/util';
|
||||
import {
|
||||
depthFirstSearch,
|
||||
breadthFirstSearch,
|
||||
connectedComponent,
|
||||
} from '@antv/algorithm';
|
||||
|
||||
/**
|
||||
* Deconstruct data and distinguish nodes and combos from graphcore data.
|
||||
@ -35,19 +43,30 @@ export const graphCoreTreeDfs = (
|
||||
nodes: NodeUserModel[],
|
||||
fn,
|
||||
mode: 'TB' | 'BT' = 'TB',
|
||||
treeKey = 'combo',
|
||||
stopFns: {
|
||||
stopBranchFn?: (node: NodeUserModel) => boolean;
|
||||
stopAllFn?: (node: NodeUserModel) => boolean;
|
||||
} = {},
|
||||
) => {
|
||||
if (!nodes?.length) return;
|
||||
nodes.forEach((node) => {
|
||||
if (!graphCore.hasNode(node.id)) return;
|
||||
const { stopBranchFn, stopAllFn } = stopFns;
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
if (!graphCore.hasNode(node.id)) continue;
|
||||
if (stopBranchFn?.(node)) continue; // Stop this branch
|
||||
if (stopAllFn?.(node)) return; // Stop all
|
||||
if (mode === 'TB') fn(node); // Traverse from top to bottom
|
||||
graphCoreTreeDfs(
|
||||
graphCore,
|
||||
graphCore.getChildren(node.id, 'combo'),
|
||||
graphCore.getChildren(node.id, treeKey),
|
||||
fn,
|
||||
mode,
|
||||
treeKey,
|
||||
stopFns,
|
||||
);
|
||||
if (mode !== 'TB') fn(node); // Traverse from bottom to top
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -176,3 +195,105 @@ export const validateComboStrucutre = (
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform tree graph data into graph data. Edges from parent-child structure.
|
||||
* @param treeData Tree structured data or an array of it.
|
||||
* @returns Graph formatted data object with nodes, edges and combos.
|
||||
*/
|
||||
export const treeData2GraphData = (
|
||||
treeData: TreeData<NodeUserModelData> | TreeData<NodeUserModelData>[],
|
||||
) => {
|
||||
const graphData = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
combos: [],
|
||||
};
|
||||
const trees = isArray(treeData) ? treeData : [treeData];
|
||||
trees.forEach((tree) => {
|
||||
traverse(tree, (child) => {
|
||||
graphData.nodes.push({
|
||||
id: child.id,
|
||||
data: child.data,
|
||||
});
|
||||
child.children?.forEach((subChild) => {
|
||||
graphData.edges.push({
|
||||
id: `tree-edge-${child.id}-${subChild.id}`,
|
||||
source: child.id,
|
||||
target: subChild.id,
|
||||
data: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return graphData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform graph data into tree graph data.
|
||||
* @param nodeMap
|
||||
* @param graphData Graph data.
|
||||
* @param propRootIds Ids of root nodes. There should be at least one node for each connected component, or the first node in a connected component will be added to the roots array.
|
||||
* @param algo
|
||||
* @returns
|
||||
*/
|
||||
export const graphData2TreeData = (
|
||||
nodeMap: { [id: string]: any },
|
||||
graphData: GraphData,
|
||||
propRootIds = [],
|
||||
) => {
|
||||
const trees = [];
|
||||
const connectedComponents = connectedComponent(graphData as any, false);
|
||||
const rootIds = [];
|
||||
const componentsNodeIds = [];
|
||||
connectedComponents.forEach((com, i) => {
|
||||
componentsNodeIds[i] = com.map((node) => node.id);
|
||||
if (propRootIds.length) {
|
||||
const root = componentsNodeIds[0].find((id) => propRootIds.includes(id));
|
||||
rootIds.push(root !== undefined ? root : com[0].id);
|
||||
} else {
|
||||
rootIds.push(com[0].id);
|
||||
}
|
||||
});
|
||||
|
||||
rootIds.forEach((id, i) => {
|
||||
nodeMap[id] = { id, children: [] };
|
||||
trees.push(nodeMap[id]);
|
||||
depthFirstSearch(
|
||||
graphData as any,
|
||||
id,
|
||||
{
|
||||
enter: ({ previous, current }) => {
|
||||
if (
|
||||
!previous ||
|
||||
current === id ||
|
||||
!componentsNodeIds[i].includes(current)
|
||||
)
|
||||
return;
|
||||
nodeMap[previous] = nodeMap[previous] || {
|
||||
id: previous,
|
||||
children: [],
|
||||
};
|
||||
nodeMap[current] = { id: current, children: [] };
|
||||
nodeMap[previous].children.push(nodeMap[current]);
|
||||
},
|
||||
},
|
||||
false,
|
||||
);
|
||||
});
|
||||
return trees;
|
||||
};
|
||||
|
||||
/**
|
||||
* Travere a tree data from top to bottom.
|
||||
* @param treeData
|
||||
* @param callback
|
||||
*/
|
||||
export const traverse = (treeData, callback) => {
|
||||
callback(treeData);
|
||||
if (treeData.children) {
|
||||
treeData.children.forEach((child) => {
|
||||
if (child) traverse(child, callback);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -83,6 +83,7 @@ type GroupedChanges = {
|
||||
EdgeUpdated: EdgeUpdated<EdgeModelData>[];
|
||||
EdgeDataUpdated: EdgeDataUpdated<EdgeModelData>[];
|
||||
TreeStructureChanged: TreeStructureChanged[];
|
||||
ComboStructureChanged: TreeStructureChanged[];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -104,6 +105,7 @@ export const getGroupedChanges = (
|
||||
EdgeUpdated: [],
|
||||
EdgeDataUpdated: [],
|
||||
TreeStructureChanged: [],
|
||||
ComboStructureChanged: [],
|
||||
};
|
||||
changes.forEach((change) => {
|
||||
const { type: changeType } = change;
|
||||
@ -119,7 +121,10 @@ export const getGroupedChanges = (
|
||||
return;
|
||||
}
|
||||
} else if (changeType === 'TreeStructureChanged') {
|
||||
groupedChanges[changeType].push(change);
|
||||
if (change.treeKey === 'combo')
|
||||
groupedChanges.ComboStructureChanged.push(change);
|
||||
else if (change.treeKey === 'tree')
|
||||
groupedChanges.TreeStructureChanged.push(change);
|
||||
return;
|
||||
} else {
|
||||
const { id: oid } = change.value;
|
||||
|
65
packages/g6/src/util/layout.ts
Normal file
65
packages/g6/src/util/layout.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import Hierarchy from '@antv/hierarchy';
|
||||
import { traverse } from './data';
|
||||
import { TreeGraphData } from '@antv/g6';
|
||||
|
||||
/**
|
||||
* Judge the direction according to options of a tree layout.
|
||||
* @param type Tree layout type.
|
||||
* @param options Tree layout options.
|
||||
* @returns
|
||||
*/
|
||||
export const isTreeLayoutHorizontal = (type, options) => {
|
||||
const { direction } = options;
|
||||
switch (type) {
|
||||
case 'compactBox':
|
||||
case 'dendrogram':
|
||||
return direction !== 'TB' && direction !== ' BT' && direction !== ' V';
|
||||
case 'indented':
|
||||
return true;
|
||||
case 'mindmap':
|
||||
return direction !== 'V';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Layout nodes on a single tree.
|
||||
* @param treeData
|
||||
* @param layoutType Tree layout type.
|
||||
* @param layoutOptions Tree layout options.
|
||||
* @param nodeMap
|
||||
* @param nodePositions An array to store the result.
|
||||
* @param begin The begin position for this tree, might be calculated from last tree.
|
||||
* @returns Positions array.
|
||||
*/
|
||||
export const layoutOneTree = (
|
||||
treeData: TreeGraphData,
|
||||
layoutType: string,
|
||||
layoutOptions,
|
||||
nodeMap,
|
||||
nodePositions,
|
||||
begin = [0, 0],
|
||||
) => {
|
||||
const { treeGap = 50 } = layoutOptions;
|
||||
const isHorizontal = isTreeLayoutHorizontal(layoutType, layoutOptions);
|
||||
const layoutData = Hierarchy[layoutType](treeData, layoutOptions);
|
||||
const range = [Infinity, -Infinity];
|
||||
const treeNodeIds = [];
|
||||
traverse(layoutData, (child) => {
|
||||
const { id, x, y } = child;
|
||||
treeNodeIds.push(id);
|
||||
const dim = isHorizontal ? 'y' : 'x';
|
||||
if (range[0] > child[dim]) range[0] = child[dim];
|
||||
if (range[1] < child[dim]) range[1] = child[dim];
|
||||
nodeMap[id].data = { x, y };
|
||||
});
|
||||
const diff = begin[isHorizontal ? 1 : 0] - range[0];
|
||||
treeNodeIds.forEach((id) => {
|
||||
const { x, y } = nodeMap[id].data;
|
||||
nodePositions.push({
|
||||
id,
|
||||
data: isHorizontal ? { x, y: y + diff } : { x: x + diff, y },
|
||||
});
|
||||
});
|
||||
begin[isHorizontal ? 1 : 0] += range[1] + diff + treeGap;
|
||||
return nodePositions;
|
||||
};
|
@ -5,7 +5,6 @@ export default () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
plugins: ['grid'],
|
||||
layout: {
|
||||
type: 'grid',
|
||||
|
@ -5,7 +5,6 @@ export default () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
|
@ -5,7 +5,6 @@ export default () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
|
@ -58,7 +58,6 @@ export default () => {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -124,7 +124,6 @@ const create2DGraph = (
|
||||
container: container as HTMLElement,
|
||||
width,
|
||||
height: 1400,
|
||||
type: 'graph',
|
||||
renderer: rendererType,
|
||||
data: dataFor2D,
|
||||
modes: {
|
||||
@ -223,7 +222,6 @@ const create3DGraph = async () => {
|
||||
container: container as HTMLDivElement,
|
||||
width,
|
||||
height: 1400,
|
||||
type: 'graph',
|
||||
renderer: 'webgl-3d',
|
||||
data: dataFor3D,
|
||||
// layout: {
|
||||
|
@ -44,7 +44,6 @@ export default () => {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -66,7 +66,6 @@ export default () => {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -51,7 +51,6 @@ export default () => {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -44,7 +44,6 @@ export default () => {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -32,6 +32,7 @@ import fisheye from './plugins/fisheye';
|
||||
import tooltip from './demo/tooltip';
|
||||
import comboBasic from './combo/combo-basic';
|
||||
import animations_node_build_in from './animations/node-build-in';
|
||||
import treeGraph from './tree/tree-graph';
|
||||
|
||||
export {
|
||||
behaviors_activateRelations,
|
||||
@ -68,4 +69,5 @@ export {
|
||||
tooltip,
|
||||
comboBasic,
|
||||
animations_node_build_in,
|
||||
treeGraph,
|
||||
};
|
||||
|
@ -231,7 +231,6 @@ export default () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
// supported behavior
|
||||
|
@ -234,7 +234,6 @@ export default () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
// 支持的 behavior
|
||||
|
@ -231,7 +231,6 @@ export default () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
// supported behavior
|
||||
|
@ -227,7 +227,6 @@ export default (context: TestCaseContext) => {
|
||||
// 2.create graph
|
||||
graph = new Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
// supported behavior
|
||||
|
@ -6,7 +6,6 @@ export default (context: TestCaseContext) => {
|
||||
const { width, height } = context;
|
||||
return new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'd3force',
|
||||
|
@ -17,7 +17,6 @@ export default async () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
renderer: 'webgl-3d',
|
||||
modes: {
|
||||
|
@ -13,7 +13,6 @@ export default async () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'force-wasm',
|
||||
|
@ -17,7 +17,6 @@ export default async () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'forceatlas2-wasm',
|
||||
|
@ -10,7 +10,6 @@ export default async () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'fruchterman-gpu',
|
||||
|
@ -17,7 +17,6 @@ export default async () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'fruchterman-wasm',
|
||||
|
@ -77,7 +77,6 @@ export default async () => {
|
||||
container: $container1,
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
type: 'graph',
|
||||
renderer: 'webgl-3d',
|
||||
modes: {
|
||||
default: [
|
||||
@ -144,7 +143,6 @@ export default async () => {
|
||||
container: $container2,
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
type: 'graph',
|
||||
renderer: 'webgl-3d',
|
||||
modes: {
|
||||
default: [
|
||||
|
@ -135,7 +135,6 @@ export default async () => {
|
||||
container: $container1,
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'force-wasm',
|
||||
@ -171,7 +170,6 @@ export default async () => {
|
||||
container: $container2,
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'force',
|
||||
|
@ -1944,7 +1944,6 @@ const createGraph = async () => {
|
||||
container: container as HTMLElement,
|
||||
width,
|
||||
height: 1200,
|
||||
type: 'graph',
|
||||
// renderer: 'webgl',
|
||||
data: { nodes, edges },
|
||||
layout: {
|
||||
|
@ -139,7 +139,6 @@ export default async () => {
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'force',
|
||||
rankdir: 'LR',
|
||||
|
266
packages/g6/tests/demo/tree/tree-graph.ts
Normal file
266
packages/g6/tests/demo/tree/tree-graph.ts
Normal file
@ -0,0 +1,266 @@
|
||||
import Stats from 'stats-js';
|
||||
import G6 from '../../../src/index';
|
||||
import { container, height, width } from '../../datasets/const';
|
||||
import { CanvasEvent } from '@antv/g';
|
||||
|
||||
const treeDataCfg = {
|
||||
type: 'treeData',
|
||||
value: [
|
||||
{
|
||||
id: 'root',
|
||||
data: {
|
||||
// collapsed: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
id: 'c1',
|
||||
data: {},
|
||||
children: [
|
||||
{
|
||||
id: 'c1-c1',
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'c2',
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'root2',
|
||||
data: {},
|
||||
children: [
|
||||
{
|
||||
id: 't2c1',
|
||||
data: {},
|
||||
children: [
|
||||
{
|
||||
id: 't2c1-c1',
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 't2c2',
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const graphDataCfg = {
|
||||
type: 'graphData',
|
||||
value: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { isRoot: true, collapsed: true } },
|
||||
{ id: 'node2', data: {} },
|
||||
{ id: 'node3', data: {} },
|
||||
{ id: 'node4', data: {} },
|
||||
{ id: 'node5', data: {} },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: 'node1', target: 'node2', data: {} },
|
||||
{ id: 'edge2', source: 'node1', target: 'node3', data: {} },
|
||||
{ id: 'edge3', source: 'node1', target: 'node4', data: {} },
|
||||
// { id: 'edge4', source: 'node2', target: 'node3', data: {} },
|
||||
// { id: 'edge5', source: 'node3', target: 'node4', data: {} },
|
||||
{ id: 'edge6', source: 'node4', target: 'node5', data: {} },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const dataGenerator = (nodeNum, edgeNum) => {
|
||||
const nodes: any = [];
|
||||
const edges: any = [];
|
||||
for (let i = 0; i < nodeNum; i++) {
|
||||
nodes.push({
|
||||
id: `node-${i}`,
|
||||
data: {
|
||||
x: Math.random() * 1000,
|
||||
y: Math.random() * 1000,
|
||||
},
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < edgeNum; i++) {
|
||||
edges.push({
|
||||
id: `edge-${i}`,
|
||||
source: nodes[Math.floor(Math.random() * nodeNum)].id,
|
||||
target: nodes[Math.floor(Math.random() * nodeNum)].id,
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
return { nodes, edges };
|
||||
};
|
||||
|
||||
export default async () => {
|
||||
// const data = dataGenerator(90000, 10000);
|
||||
// console.log('data', data);
|
||||
const graph = new G6.Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
layout: {
|
||||
type: 'compactBox',
|
||||
// type: 'grid',
|
||||
},
|
||||
node: (innerModel) => {
|
||||
const { x, y, labelShape } = innerModel.data;
|
||||
return {
|
||||
...innerModel,
|
||||
data: {
|
||||
x,
|
||||
y,
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['x', 'y'],
|
||||
duration: 500,
|
||||
shapeId: 'group',
|
||||
order: 0,
|
||||
},
|
||||
],
|
||||
hide: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
shapeId: 'labelShape',
|
||||
},
|
||||
],
|
||||
show: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 1000,
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 1000,
|
||||
shapeId: 'labelShape',
|
||||
},
|
||||
],
|
||||
},
|
||||
// animate in shapes, unrelated to each other, excuted parallely
|
||||
labelShape: {
|
||||
text: innerModel.id,
|
||||
...labelShape,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
edge: (innerModel) => {
|
||||
return {
|
||||
...innerModel,
|
||||
data: {
|
||||
...innerModel.data,
|
||||
animates: {
|
||||
// hide: [
|
||||
// {
|
||||
// fields: ['opacity'],
|
||||
// duration: 200,
|
||||
// shapeId: 'keyShape',
|
||||
// },
|
||||
// {
|
||||
// fields: ['opacity'],
|
||||
// duration: 200,
|
||||
// shapeId: 'labelShape',
|
||||
// },
|
||||
// ],
|
||||
// show: [
|
||||
// {
|
||||
// fields: ['opacity'],
|
||||
// duration: 1000,
|
||||
// shapeId: 'keyShape',
|
||||
// },
|
||||
// {
|
||||
// fields: ['opacity'],
|
||||
// duration: 1000,
|
||||
// shapeId: 'labelShape',
|
||||
// },
|
||||
// ],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
// node: {
|
||||
// animates: {
|
||||
// update: [
|
||||
// {
|
||||
// fields: ['x', 'y'],
|
||||
// duration: 2000,
|
||||
// shapeId: 'group',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// labelShape: {
|
||||
// text: {
|
||||
// fields: ['id'],
|
||||
// formatter: (model) => model.id,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
data: treeDataCfg,
|
||||
// data: graphDataCfg,
|
||||
modes: {
|
||||
default: [
|
||||
'drag-canvas',
|
||||
'zoom-canvas',
|
||||
{
|
||||
type: 'collapse-expand-tree',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const stats = new Stats();
|
||||
stats.showPanel(0);
|
||||
document.body.appendChild(stats.dom);
|
||||
|
||||
graph.canvas.addEventListener(CanvasEvent.AFTER_RENDER, () => {
|
||||
if (stats) {
|
||||
stats.update();
|
||||
}
|
||||
});
|
||||
|
||||
let collapsed = true;
|
||||
graph.translateTo({ x: 100, y: 100 });
|
||||
|
||||
graph.on('canvas:click', (e) => {
|
||||
/** === change graph data / tree data === */
|
||||
// graph.changeData(treeDataCfg);
|
||||
/** === change layout === */
|
||||
// graph.layout({ type: 'compactBox' });
|
||||
/** === collapse / expand === */
|
||||
const ids = ['root2', 'root']; //['root']; //['node1']; //
|
||||
collapsed ? graph.expand(ids) : graph.collapse(ids);
|
||||
collapsed = !collapsed;
|
||||
});
|
||||
|
||||
graph.on('canvas:contextmenu', (e) => {
|
||||
console.log('contextmenu');
|
||||
/** === updateData === */
|
||||
// graph.updateData('node', {
|
||||
// id: 'node2',
|
||||
// data: { labelShape: { text: 'updated!!' } },
|
||||
// });
|
||||
|
||||
/** === remove a child / subtree root === */
|
||||
// graph.removeData('node', ['node4']);
|
||||
|
||||
/** === add a child === */
|
||||
// graph.addData('node', [{ id: 'newnode', data: {} }]);
|
||||
// graph.addData('edge', [
|
||||
// { id: 'newedge', source: 'node1', target: 'newnode', data: {} },
|
||||
// ]);
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
@ -10,7 +10,6 @@ const createGraph = () => {
|
||||
container: container as HTMLElement,
|
||||
width,
|
||||
height: 1200,
|
||||
type: 'graph',
|
||||
// renderer: 'webgl',
|
||||
data: {
|
||||
nodes: [
|
||||
|
16
packages/g6/tests/intergration/layouts/circular.ts
Normal file
16
packages/g6/tests/intergration/layouts/circular.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { container, data, height, width } from '../../datasets/const';
|
||||
|
||||
export default () => {
|
||||
return new G6.Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
center: [250, 250],
|
||||
radius: 200,
|
||||
},
|
||||
});
|
||||
};
|
56
packages/g6/tests/intergration/layouts/force-3d.ts
Normal file
56
packages/g6/tests/intergration/layouts/force-3d.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { container, data, height, width } from '../../datasets/const';
|
||||
|
||||
export default () => {
|
||||
return new G6.Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
renderer: 'webgl-3d',
|
||||
modes: {
|
||||
default: [
|
||||
{
|
||||
type: 'orbit-canvas-3d',
|
||||
trigger: 'drag',
|
||||
},
|
||||
'zoom-canvas-3d',
|
||||
],
|
||||
},
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'force',
|
||||
dimensions: 3,
|
||||
iterations: 100,
|
||||
center: [width / 2, height / 2, 0],
|
||||
},
|
||||
edge: {
|
||||
type: 'line-edge',
|
||||
keyShape: {
|
||||
lineWidth: 2,
|
||||
stroke: 'grey',
|
||||
},
|
||||
},
|
||||
node: {
|
||||
type: 'sphere-node',
|
||||
keyShape: {
|
||||
opacity: 0.6,
|
||||
},
|
||||
// labelShape: {
|
||||
// text: 'node-label',
|
||||
// },
|
||||
// iconShape: {
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
// },
|
||||
},
|
||||
nodeState: {
|
||||
selected: {
|
||||
keyShape: {
|
||||
fill: '#f00',
|
||||
},
|
||||
labelShape: {
|
||||
fontSize: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
@ -8,7 +8,6 @@ describe('behavior', () => {
|
||||
it('behavior in spec, add / remove / update a behavior in defualt mode', () => {
|
||||
const graph = new G6.Graph({
|
||||
container,
|
||||
type: 'graph',
|
||||
data: { nodes: [], edges: [] },
|
||||
modes: {
|
||||
default: [
|
||||
@ -116,7 +115,6 @@ describe('behavior', () => {
|
||||
});
|
||||
const graph = new CustomGraph({
|
||||
container,
|
||||
type: 'graph',
|
||||
data: { nodes: [], edges: [] },
|
||||
modes: {
|
||||
default: [
|
||||
|
@ -9,7 +9,6 @@ describe('brush-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -142,7 +141,6 @@ describe('brush-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -253,7 +251,6 @@ describe('brush-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -368,7 +365,6 @@ describe('brush-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -527,7 +523,6 @@ describe('brush-select behavior with itemTypes', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -660,7 +655,6 @@ describe('brush-select behavior with itemTypes', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -746,7 +740,6 @@ describe('brush-select behavior with itemTypes', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -836,7 +829,6 @@ describe('brush-select behavior with trigger', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -937,7 +929,6 @@ describe('brush-select behavior with trigger', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -1074,7 +1065,6 @@ describe('brush-select behavior with shouldBegin and shouldUpdate', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -1210,7 +1200,6 @@ describe('brush-select behavior with brushStyle', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
|
@ -8,7 +8,6 @@ describe('drag-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -105,7 +104,6 @@ describe('drag-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -172,7 +170,6 @@ describe('drag-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -232,7 +229,6 @@ describe('drag-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -283,7 +279,6 @@ describe('drag-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -355,7 +350,6 @@ describe('drag-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -426,7 +420,6 @@ describe('drag-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
|
@ -9,7 +9,6 @@ describe('lasso-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -158,7 +157,6 @@ describe('lasso-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -277,7 +275,6 @@ describe('lasso-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -400,7 +397,6 @@ describe('lasso-select behavior with selectSetMode', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -579,7 +575,6 @@ describe('lasso-select behavior with itemTypes', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -724,7 +719,6 @@ describe('lasso-select behavior with itemTypes', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -818,7 +812,6 @@ describe('lasso-select behavior with itemTypes', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -912,7 +905,6 @@ describe('lasso-select behavior with trigger', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -1017,7 +1009,6 @@ describe('lasso-select behavior with trigger', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -1170,7 +1161,6 @@ describe('lasso-select behavior with shouldBegin and shouldUpdate', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -1303,7 +1293,6 @@ describe('lasso-select behavior with brushStyle', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
|
@ -8,7 +8,6 @@ describe('zoom-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -93,7 +92,6 @@ describe('zoom-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -149,7 +147,6 @@ describe('zoom-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
@ -226,7 +223,6 @@ describe('zoom-canvas behavior', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
|
@ -10,7 +10,6 @@ describe('click-select', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200, keyShape: { fill: '#0f0' } } },
|
||||
|
@ -24,7 +24,6 @@ describe('data', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data, // with data, graph will be rendered in constructor
|
||||
});
|
||||
graph.on('afterrender', () => {
|
||||
|
@ -11,7 +11,6 @@ const createGraph = (dragNodeOptions: DragNodeOptions): IGraph => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200, keyShape: { fill: '#0f0' } } },
|
||||
|
@ -25,7 +25,6 @@ describe('edge item', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['drag-node'],
|
||||
},
|
||||
@ -265,7 +264,6 @@ describe('edge mapper', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
};
|
||||
it('function mapper', (done) => {
|
||||
const graph = new G6.Graph({
|
||||
@ -373,7 +371,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -507,7 +504,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -769,7 +765,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -897,7 +892,6 @@ describe('cubic-edge unit test', () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
@ -1137,7 +1131,6 @@ describe('cubic-edge unit test', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: stateData,
|
||||
modes: {
|
||||
// Supported behavior
|
||||
@ -1272,7 +1265,6 @@ describe('cubic-horizon-edge unit test', () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
@ -1415,7 +1407,6 @@ describe('cubic-horizon-edge unit test', () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
@ -1654,7 +1645,6 @@ describe('cubic-horizon-edge unit test', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: stateData,
|
||||
modes: {
|
||||
default: ['activate-relations'],
|
||||
@ -1788,7 +1778,6 @@ describe('cubic-vertical-edge unit test', () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
@ -2027,7 +2016,6 @@ describe('cubic-vertical-edge unit test', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: stateData,
|
||||
modes: {
|
||||
default: ['activate-relations'],
|
||||
@ -2161,7 +2149,6 @@ describe('cubic-vertical-edge unit test', () => {
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
@ -2400,7 +2387,6 @@ describe('cubic-vertical-edge unit test', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: stateData,
|
||||
modes: {
|
||||
default: ['activate-relations'],
|
||||
|
@ -31,7 +31,6 @@ describe('node item', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
renderer: 'webgl-3d',
|
||||
// renderer: 'canvas',
|
||||
modes: {
|
||||
|
@ -124,7 +124,6 @@ const createGraph = (props) => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: clonedData,
|
||||
...props,
|
||||
});
|
||||
|
@ -12,7 +12,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
});
|
||||
|
||||
@ -35,7 +34,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -123,7 +121,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -147,7 +144,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -185,7 +181,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -224,7 +219,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -262,7 +256,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'd3force',
|
||||
@ -289,7 +282,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -342,7 +334,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -388,7 +379,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'd3force',
|
||||
@ -417,7 +407,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'd3force',
|
||||
@ -470,7 +459,6 @@ describe('layout', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'myCustomLayout',
|
||||
|
@ -26,7 +26,6 @@ describe('node item', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -138,7 +137,6 @@ describe('node mapper', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
};
|
||||
it('function mapper', (done) => {
|
||||
const graph = new G6.Graph({
|
||||
@ -323,7 +321,6 @@ describe('register node', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -465,7 +462,6 @@ describe('register node', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -533,7 +529,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -677,7 +672,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -920,7 +914,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
|
@ -10,7 +10,6 @@ const createGraph = (plugins) => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200, nodeType: 'a' } },
|
||||
@ -65,9 +64,9 @@ const createGraph = (plugins) => {
|
||||
describe('grid plugin', () => {
|
||||
test('grid with string config', () => {
|
||||
const graph = createGraph([
|
||||
{
|
||||
img: 'url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImdyaWQiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTSAwIDEwIEwgNDAgMTAgTSAxMCAwIEwgMTAgNDAgTSAwIDIwIEwgNDAgMjAgTSAyMCAwIEwgMjAgNDAgTSAwIDMwIEwgNDAgMzAgTSAzMCAwIEwgMzAgNDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2UwZTBlMCIgb3BhY2l0eT0iMC4yIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNIDQwIDAgTCAwIDAgMCA0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIiBzdHJva2Utd2lkdGg9IjEiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JpZCkiLz48L3N2Zz4=)'
|
||||
}
|
||||
{
|
||||
img: 'url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImdyaWQiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTSAwIDEwIEwgNDAgMTAgTSAxMCAwIEwgMTAgNDAgTSAwIDIwIEwgNDAgMjAgTSAyMCAwIEwgMjAgNDAgTSAwIDMwIEwgNDAgMzAgTSAzMCAwIEwgMzAgNDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2UwZTBlMCIgb3BhY2l0eT0iMC4yIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNIDQwIDAgTCAwIDAgMCA0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIiBzdHJva2Utd2lkdGg9IjEiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JpZCkiLz48L3N2Zz4=)',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -10,7 +10,6 @@ const createGraph = (plugins) => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200, nodeType: 'a' } },
|
||||
|
@ -9,7 +9,6 @@ const createGraph = (plugins) => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200 } },
|
||||
|
@ -9,7 +9,6 @@ const createGraph = (plugins) => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200 } },
|
||||
|
@ -9,7 +9,6 @@ const createGraph = (plugins) => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200 } },
|
||||
|
@ -82,7 +82,6 @@ describe('edge item', () => {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
@ -282,7 +281,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -416,7 +414,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -550,7 +547,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -684,7 +680,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
@ -818,7 +813,6 @@ describe('state', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: {
|
||||
nodes: [
|
||||
{
|
||||
|
@ -8,7 +8,6 @@ describe('behavior', () => {
|
||||
it('behavior in spec, add / remove / update a behavior in defualt mode', () => {
|
||||
const graph = new G6.Graph({
|
||||
container,
|
||||
type: 'graph',
|
||||
data: { nodes: [], edges: [] },
|
||||
modes: {
|
||||
default: ['drag-canvas', 'click-select', 'drag-canvas', 'zoom-canvas'],
|
||||
|
@ -1887,7 +1887,6 @@ const createGraph = (props) => {
|
||||
// renderer: 'webgl',
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: clonedData,
|
||||
// data: {
|
||||
// nodes: [clonedData.nodes[0], clonedData.nodes[10]],
|
||||
|
@ -88,7 +88,6 @@ describe('theme', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'grid',
|
||||
@ -203,7 +202,6 @@ describe('theme', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: clone(data),
|
||||
layout: {
|
||||
type: 'grid',
|
||||
@ -430,7 +428,6 @@ describe('theme', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data: clone(data),
|
||||
layout: {
|
||||
type: 'grid',
|
||||
@ -712,7 +709,6 @@ describe('theme', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'grid',
|
||||
|
@ -27,7 +27,6 @@ describe('theme', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'grid',
|
||||
@ -126,7 +125,6 @@ describe('theme', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'grid',
|
||||
@ -255,7 +253,6 @@ describe('theme', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'grid',
|
||||
|
@ -12,7 +12,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -42,7 +41,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -85,7 +83,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -131,7 +128,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -208,7 +204,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -259,7 +254,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -385,7 +379,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -422,7 +415,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -473,7 +465,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
@ -542,7 +533,6 @@ describe('viewport', () => {
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
|
Loading…
Reference in New Issue
Block a user