mirror of
https://gitee.com/antv/g6.git
synced 2024-11-30 18:58:34 +08:00
Merge pull request #4963 from antvis/v5-update-mapper
feat: update mapper
This commit is contained in:
commit
ed717d8507
@ -177,6 +177,7 @@ 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.mapperchange.tap(this.onMapperChange.bind(this));
|
||||
this.graph.hooks.treecollapseexpand.tap(
|
||||
this.onTreeCollapseExpand.bind(this),
|
||||
);
|
||||
@ -850,6 +851,16 @@ export class ItemController {
|
||||
});
|
||||
};
|
||||
|
||||
private onMapperChange = ({ type, mapper }) => {
|
||||
if (!mapper) return;
|
||||
this.itemMap.forEach((item) => {
|
||||
const itemTye = item.getType();
|
||||
if (itemTye !== type) return;
|
||||
item.mapper = mapper;
|
||||
item.update(item.model, undefined, false);
|
||||
});
|
||||
};
|
||||
|
||||
private onDestroy = () => {
|
||||
Object.values(this.itemMap).forEach((item) => item.destroy());
|
||||
// Fix OOM problem, since this map will hold all the refs of items.
|
||||
|
@ -55,6 +55,7 @@ import { FitViewRules, GraphTransformOptions } from '../types/view';
|
||||
import { changeRenderer, createCanvas } from '../util/canvas';
|
||||
import { formatPadding } from '../util/shape';
|
||||
import { Plugin as PluginBase } from '../types/plugin';
|
||||
import { ComboMapper, EdgeMapper, NodeMapper } from '../types/spec';
|
||||
import {
|
||||
DataController,
|
||||
ExtensionController,
|
||||
@ -357,7 +358,11 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
main: Canvas;
|
||||
transient: Canvas;
|
||||
};
|
||||
}>({ name: 'init' }),
|
||||
}>({ name: 'themechange' }),
|
||||
mapperchange: new Hook<{
|
||||
type: ITEM_TYPE;
|
||||
mapper: NodeMapper | EdgeMapper | ComboMapper;
|
||||
}>({ name: 'mapperchange' }),
|
||||
treecollapseexpand: new Hook<{
|
||||
ids: ID[];
|
||||
animate: boolean;
|
||||
@ -412,6 +417,32 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the item display mapper for a specific item type.
|
||||
* @param {ITEM_TYPE} type - The type of item (node, edge, or combo).
|
||||
* @param {NodeMapper | EdgeMapper | ComboMapper} mapper - The mapper to be updated.
|
||||
* */
|
||||
public updateMapper(
|
||||
type: ITEM_TYPE,
|
||||
mapper: NodeMapper | EdgeMapper | ComboMapper,
|
||||
) {
|
||||
switch (type) {
|
||||
case 'node':
|
||||
this.specification.node = mapper as NodeMapper;
|
||||
break;
|
||||
case 'edge':
|
||||
this.specification.edge = mapper as EdgeMapper;
|
||||
break;
|
||||
case 'combo':
|
||||
this.specification.combo = mapper as ComboMapper;
|
||||
break;
|
||||
}
|
||||
this.hooks.mapperchange.emit({
|
||||
type,
|
||||
mapper,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the copy of specs(configurations).
|
||||
* @returns graph specs
|
||||
@ -1054,19 +1085,21 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
graphCore.once('changed', (event) => {
|
||||
if (!event.changes.length) return;
|
||||
const changes = event.changes;
|
||||
const timingParameters = {
|
||||
type: itemType,
|
||||
action: 'add',
|
||||
models,
|
||||
apiName: 'addData',
|
||||
changes,
|
||||
};
|
||||
this.emit('beforeitemchange', timingParameters);
|
||||
this.hooks.itemchange.emit({
|
||||
type: itemType,
|
||||
changes: graphCore.reduceChanges(event.changes),
|
||||
graphCore,
|
||||
theme: specification,
|
||||
});
|
||||
this.emit('afteritemchange', {
|
||||
type: itemType,
|
||||
action: 'add',
|
||||
models,
|
||||
apiName: 'addData',
|
||||
changes,
|
||||
});
|
||||
this.emit('afteritemchange', timingParameters);
|
||||
});
|
||||
|
||||
const modelArr = isArray(models) ? models : [models];
|
||||
@ -1099,19 +1132,21 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
graphCore.once('changed', (event) => {
|
||||
if (!event.changes.length) return;
|
||||
const changes = event.changes;
|
||||
const timingParameters = {
|
||||
type: itemType,
|
||||
action: 'remove',
|
||||
ids: idArr,
|
||||
apiName: 'removeData',
|
||||
changes,
|
||||
};
|
||||
this.emit('beforeitemchange', timingParameters);
|
||||
this.hooks.itemchange.emit({
|
||||
type: itemType,
|
||||
changes: event.changes,
|
||||
graphCore,
|
||||
theme: specification,
|
||||
});
|
||||
this.emit('afteritemchange', {
|
||||
type: itemType,
|
||||
action: 'remove',
|
||||
ids: idArr,
|
||||
apiName: 'removeData',
|
||||
changes,
|
||||
});
|
||||
this.emit('afteritemchange', timingParameters);
|
||||
});
|
||||
this.hooks.datachange.emit({
|
||||
data,
|
||||
@ -1204,19 +1239,21 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
const { specification } = this.themeController;
|
||||
graphCore.once('changed', (event) => {
|
||||
const changes = this.extendChanges(clone(event.changes));
|
||||
const timingParameters = {
|
||||
type: itemType,
|
||||
action: 'update',
|
||||
models,
|
||||
apiName: 'updateData',
|
||||
changes,
|
||||
};
|
||||
this.emit('beforeitemchange', timingParameters);
|
||||
this.hooks.itemchange.emit({
|
||||
type: itemType,
|
||||
changes: event.changes,
|
||||
graphCore,
|
||||
theme: specification,
|
||||
});
|
||||
this.emit('afteritemchange', {
|
||||
type: itemType,
|
||||
action: 'update',
|
||||
models,
|
||||
apiName: 'updateData',
|
||||
changes,
|
||||
});
|
||||
this.emit('afteritemchange', timingParameters);
|
||||
});
|
||||
|
||||
this.hooks.datachange.emit({
|
||||
@ -1325,6 +1362,15 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
const changes = event.changes.filter(
|
||||
(change) => !isEqual(change.newValue, change.oldValue),
|
||||
);
|
||||
const timingParameters = {
|
||||
type,
|
||||
action: 'updatePosition',
|
||||
upsertAncestors,
|
||||
models,
|
||||
apiName: 'updatePosition',
|
||||
changes,
|
||||
};
|
||||
this.emit('beforeitemchange', timingParameters);
|
||||
this.hooks.itemchange.emit({
|
||||
type,
|
||||
changes: event.changes,
|
||||
@ -1335,14 +1381,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
animate: !disableAnimate,
|
||||
callback,
|
||||
});
|
||||
this.emit('afteritemchange', {
|
||||
type,
|
||||
action: 'updatePosition',
|
||||
upsertAncestors,
|
||||
models,
|
||||
apiName: 'updatePosition',
|
||||
changes,
|
||||
});
|
||||
this.emit('afteritemchange', timingParameters);
|
||||
});
|
||||
|
||||
this.hooks.datachange.emit({
|
||||
@ -1617,19 +1656,21 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
graphCore.once('changed', (event) => {
|
||||
if (!event.changes.length) return;
|
||||
const changes = event.changes;
|
||||
const timingParameters = {
|
||||
type: 'combo',
|
||||
action: 'add',
|
||||
models: [model],
|
||||
apiName: 'addCombo',
|
||||
changes,
|
||||
};
|
||||
this.emit('beforeitemchange', timingParameters);
|
||||
this.hooks.itemchange.emit({
|
||||
type: 'combo',
|
||||
changes: graphCore.reduceChanges(event.changes),
|
||||
graphCore,
|
||||
theme: specification,
|
||||
});
|
||||
this.emit('afteritemchange', {
|
||||
type: 'combo',
|
||||
action: 'add',
|
||||
models: [model],
|
||||
apiName: 'addCombo',
|
||||
changes,
|
||||
});
|
||||
this.emit('afteritemchange', timingParameters);
|
||||
});
|
||||
|
||||
const data = {
|
||||
@ -1715,6 +1756,17 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
graphCore.once('changed', (event) => {
|
||||
if (!event.changes.length) return;
|
||||
const changes = this.extendChanges(clone(event.changes));
|
||||
const timingParameters = {
|
||||
type: 'combo',
|
||||
ids: idArr,
|
||||
dx,
|
||||
dy,
|
||||
action: 'updatePosition',
|
||||
upsertAncestors,
|
||||
apiName: 'moveCombo',
|
||||
changes,
|
||||
};
|
||||
this.emit('beforeitemchange', timingParameters);
|
||||
this.hooks.itemchange.emit({
|
||||
type: 'combo',
|
||||
changes: event.changes,
|
||||
@ -1724,16 +1776,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
action: 'updatePosition',
|
||||
callback,
|
||||
});
|
||||
this.emit('afteritemchange', {
|
||||
type: 'combo',
|
||||
ids: idArr,
|
||||
dx,
|
||||
dy,
|
||||
action: 'updatePosition',
|
||||
upsertAncestors,
|
||||
apiName: 'moveCombo',
|
||||
changes,
|
||||
});
|
||||
this.emit('afteritemchange', timingParameters);
|
||||
});
|
||||
|
||||
this.hooks.datachange.emit({
|
||||
|
@ -54,6 +54,8 @@ export class Minimap extends Base {
|
||||
private dx: number;
|
||||
/** Distance from left of minimap graph to the left of minimap container. */
|
||||
private dy: number;
|
||||
/** Cache the visibility while items' visibility changed. And apply them onto the minimap with debounce. */
|
||||
private visibleCache: { [id: string]: boolean } = {};
|
||||
|
||||
constructor(options?: MiniMapConfig) {
|
||||
super(options);
|
||||
@ -80,11 +82,11 @@ export class Minimap extends Base {
|
||||
|
||||
public getEvents() {
|
||||
return {
|
||||
afterupdateitem: this.handleUpdateCanvas,
|
||||
afteritemstatechange: this.handleUpdateCanvas,
|
||||
afterlayout: this.handleUpdateCanvas,
|
||||
viewportchange: this.handleUpdateCanvas,
|
||||
afteritemchange: this.handleUpdateCanvas,
|
||||
afteritemvisibilitychange: this.handleVisibilityChange,
|
||||
};
|
||||
}
|
||||
|
||||
@ -115,12 +117,12 @@ export class Minimap extends Base {
|
||||
style='position:absolute;
|
||||
left:0;
|
||||
top:0;
|
||||
box-sizing:border-box;
|
||||
border: 2px solid #1980ff;
|
||||
box-sizing:border-box;
|
||||
background: rgba(0, 0, 255, 0.1);
|
||||
cursor:move'
|
||||
draggable=${isSafari || isFireFox ? false : true}
|
||||
</div>`);
|
||||
/>`);
|
||||
|
||||
// Last mouse x position
|
||||
let x = 0;
|
||||
@ -128,11 +130,25 @@ export class Minimap extends Base {
|
||||
let y = 0;
|
||||
// Whether in dragging status
|
||||
let dragging = false;
|
||||
let resizing = false;
|
||||
|
||||
const dragstartevent = isSafari || isFireFox ? 'mousedown' : 'dragstart';
|
||||
this.container.addEventListener('mousemove', (e) => {
|
||||
const moveAtBorder = getMoveAtBorder(viewport, e);
|
||||
if (moveAtBorder) {
|
||||
this.container.style.cursor = cursorMap[moveAtBorder];
|
||||
viewport.style.cursor = cursorMap[moveAtBorder];
|
||||
} else {
|
||||
this.container.style.cursor = 'unset';
|
||||
viewport.style.cursor = 'move';
|
||||
}
|
||||
});
|
||||
viewport.addEventListener(
|
||||
dragstartevent,
|
||||
((e: IG6GraphEvent) => {
|
||||
resizing = getMoveAtBorder(viewport, e);
|
||||
if (resizing) return;
|
||||
|
||||
if ((e as any).dataTransfer) {
|
||||
const img = new Image();
|
||||
img.src =
|
||||
@ -159,6 +175,30 @@ export class Minimap extends Base {
|
||||
);
|
||||
|
||||
const dragListener = (e: IG6GraphEvent) => {
|
||||
const { style } = viewport;
|
||||
const left = parseInt(style.left, 10);
|
||||
const top = parseInt(style.top, 10);
|
||||
const width = parseInt(style.width, 10);
|
||||
const height = parseInt(style.height, 10);
|
||||
|
||||
if (resizing) {
|
||||
const { clientX, clientY } = e;
|
||||
const afterResize = { left, top, width, height };
|
||||
if (resizing.includes('left')) {
|
||||
afterResize.left = `${clientX}px`;
|
||||
afterResize.width = `${left + width - clientX}px`;
|
||||
} else if (resizing.includes('right')) {
|
||||
afterResize.width = `${clientX - left}px`;
|
||||
}
|
||||
if (resizing.includes('top')) {
|
||||
afterResize.top = `${clientY}`;
|
||||
afterResize.height = `${top + height - clientY}`;
|
||||
} else if (resizing.includes('bottom')) {
|
||||
afterResize.height = `${clientY - top}`;
|
||||
}
|
||||
modifyCSS(viewport, afterResize);
|
||||
return;
|
||||
}
|
||||
if (!dragging || isNil(e.clientX) || isNil(e.clientY)) {
|
||||
return;
|
||||
}
|
||||
@ -168,12 +208,6 @@ export class Minimap extends Base {
|
||||
let dx = x - e.clientX;
|
||||
let dy = y - e.clientY;
|
||||
|
||||
const { style } = viewport;
|
||||
const left = parseInt(style.left, 10);
|
||||
const top = parseInt(style.top, 10);
|
||||
const width = parseInt(style.width, 10);
|
||||
const height = parseInt(style.height, 10);
|
||||
|
||||
// If the viewport is already on the left or right, stop moving x.
|
||||
if (left - dx < 0 || left - dx + width >= size[0]) {
|
||||
dx = 0;
|
||||
@ -201,6 +235,7 @@ export class Minimap extends Base {
|
||||
|
||||
const dragendListener = () => {
|
||||
dragging = false;
|
||||
resizing = false;
|
||||
this.options.refresh = true;
|
||||
};
|
||||
const dragendevent = isSafari || isFireFox ? 'mouseup' : 'dragend';
|
||||
@ -549,6 +584,34 @@ export class Minimap extends Base {
|
||||
false,
|
||||
);
|
||||
|
||||
private handleVisibilityChange = (params) => {
|
||||
const { ids, value } = params;
|
||||
ids.forEach((id) => {
|
||||
this.visibleCache[id] = value;
|
||||
});
|
||||
this.debounceCloneVisibility();
|
||||
};
|
||||
|
||||
private debounceCloneVisibility = debounce(
|
||||
() => {
|
||||
const nodeGroup = this.canvas.getRoot().getElementById('node-group');
|
||||
const edgeGroup = this.canvas.getRoot().getElementById('edge-group');
|
||||
nodeGroup.childNodes.concat(edgeGroup.childNodes).forEach((child) => {
|
||||
const id = child.getAttribute?.('data-item-id');
|
||||
if (this.visibleCache.hasOwnProperty(id)) {
|
||||
if (this.visibleCache[id]) {
|
||||
child.childNodes.forEach((shape) => shape.show());
|
||||
} else if (this.visibleCache[id] === false) {
|
||||
child.childNodes.forEach((shape) => shape.hide());
|
||||
}
|
||||
}
|
||||
});
|
||||
this.visibleCache = {};
|
||||
},
|
||||
50,
|
||||
false,
|
||||
);
|
||||
|
||||
public init(graph: IGraph) {
|
||||
super.init(graph);
|
||||
const promise = this.initContainer();
|
||||
@ -696,3 +759,45 @@ export class Minimap extends Base {
|
||||
if (container?.parentNode) container.parentNode.removeChild(container);
|
||||
}
|
||||
}
|
||||
|
||||
const getMoveAtBorder = (dom, evt) => {
|
||||
const bounds = dom.getBoundingClientRect();
|
||||
const { clientX, clientY } = evt;
|
||||
console.log('mosemove', bounds.x, clientX);
|
||||
if (Math.abs(clientX - bounds.x) < 4 && Math.abs(clientY - bounds.y) < 4) {
|
||||
return 'left-top';
|
||||
} else if (
|
||||
Math.abs(clientX - bounds.x) < 4 &&
|
||||
Math.abs(clientY - bounds.y - bounds.height) < 4
|
||||
) {
|
||||
return 'left-bottom';
|
||||
} else if (
|
||||
Math.abs(clientX - bounds.x - bounds.width) < 4 &&
|
||||
Math.abs(clientY - bounds.y) < 4
|
||||
) {
|
||||
return 'right-top';
|
||||
} else if (
|
||||
Math.abs(clientX - bounds.x - bounds.width) < 4 &&
|
||||
Math.abs(clientY - bounds.y - bounds.height) < 4
|
||||
) {
|
||||
return 'right-bottom';
|
||||
} else if (Math.abs(clientX - bounds.x) < 4) {
|
||||
return 'left';
|
||||
} else if (Math.abs(clientY - bounds.y) < 4) {
|
||||
return 'top';
|
||||
} else if (Math.abs(clientY - bounds.y - bounds.height) < 4) {
|
||||
return 'bottom';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const cursorMap = {
|
||||
'left-top': 'nwse-resize',
|
||||
'right-bottom': 'nwse-resize',
|
||||
'right-top': 'nesw-resize',
|
||||
'left-bottom': 'nesw-resize',
|
||||
left: 'ew-resize',
|
||||
right: 'ew-resize',
|
||||
top: 'ns-resize',
|
||||
bottom: 'ns-resize',
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ import { ITEM_TYPE, SHAPE_TYPE } from './item';
|
||||
import { LayoutOptions } from './layout';
|
||||
import { NodeModel, NodeUserModel } from './node';
|
||||
import { RendererName } from './render';
|
||||
import { Specification } from './spec';
|
||||
import { ComboMapper, EdgeMapper, NodeMapper, Specification } from './spec';
|
||||
import { ThemeOptionsOf, ThemeRegistry } from './theme';
|
||||
import { FitViewRules, GraphTransformOptions } from './view';
|
||||
|
||||
@ -45,6 +45,12 @@ export interface IGraph<
|
||||
* Update the theme specs (configurations).
|
||||
*/
|
||||
updateTheme: (theme: ThemeOptionsOf<T>) => void;
|
||||
/**
|
||||
* Update the item display mapper for a specific item type.
|
||||
* @param {ITEM_TYPE} type - The type of item (node, edge, or combo).
|
||||
* @param {NodeMapper | EdgeMapper | ComboMapper} mapper - The mapper to be updated.
|
||||
* */
|
||||
updateMapper(type: ITEM_TYPE, mapper: NodeMapper | EdgeMapper | ComboMapper);
|
||||
/**
|
||||
* Get the copy of specs(configurations).
|
||||
* @returns graph specs
|
||||
|
@ -11,6 +11,7 @@ import { ThemeSpecification } from './theme';
|
||||
import { GraphTransformOptions } from './view';
|
||||
import { ComboModel } from './combo';
|
||||
import { Plugin as PluginBase } from './plugin';
|
||||
import { ComboMapper, EdgeMapper, NodeMapper } from './spec';
|
||||
|
||||
export interface IHook<T> {
|
||||
name: string;
|
||||
@ -124,6 +125,10 @@ export interface Hooks {
|
||||
transient: Canvas;
|
||||
};
|
||||
}>;
|
||||
mapperchange: IHook<{
|
||||
type: ITEM_TYPE;
|
||||
mapper: NodeMapper | EdgeMapper | ComboMapper;
|
||||
}>;
|
||||
treecollapseexpand: IHook<{
|
||||
ids: ID[];
|
||||
action: 'collapse' | 'expand';
|
||||
|
@ -29,6 +29,12 @@ import { RendererName } from './render';
|
||||
import { StackCfg } from './history';
|
||||
import { Plugin } from './plugin';
|
||||
|
||||
export type NodeMapper = ((data: NodeModel) => NodeDisplayModel) | NodeEncode;
|
||||
export type EdgeMapper = ((data: EdgeModel) => EdgeDisplayModel) | EdgeEncode;
|
||||
export type ComboMapper =
|
||||
| ((data: ComboModel) => ComboDisplayModel)
|
||||
| ComboEncode;
|
||||
|
||||
export interface Specification<
|
||||
B extends BehaviorRegistry,
|
||||
T extends ThemeRegistry,
|
||||
@ -92,9 +98,9 @@ export interface Specification<
|
||||
| TransformerFn[];
|
||||
|
||||
/** item */
|
||||
node?: ((data: NodeModel) => NodeDisplayModel) | NodeEncode;
|
||||
edge?: ((data: EdgeModel) => EdgeDisplayModel) | EdgeEncode;
|
||||
combo?: ((data: ComboModel) => ComboDisplayModel) | ComboEncode;
|
||||
node?: NodeMapper;
|
||||
edge?: EdgeMapper;
|
||||
combo?: ComboMapper;
|
||||
|
||||
/** item state styles */
|
||||
nodeState?: {
|
||||
|
@ -61,11 +61,15 @@ import layouts_combocombined from './layouts/combo-combined';
|
||||
import hull from './plugins/hull';
|
||||
import legend from './plugins/legend';
|
||||
import snapline from './plugins/snapline';
|
||||
import mapper from './visual/mapper';
|
||||
import minimap from './plugins/minimap';
|
||||
|
||||
export { default as timebar_time } from './plugins/timebar-time';
|
||||
export { default as timebar_chart } from './plugins/timebar-chart';
|
||||
|
||||
export {
|
||||
minimap,
|
||||
mapper,
|
||||
anchor,
|
||||
animations_node_build_in,
|
||||
arrow,
|
||||
|
58
packages/g6/tests/demo/plugins/minimap.ts
Normal file
58
packages/g6/tests/demo/plugins/minimap.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { Graph, Extensions, extend } from '../../../src/index';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const ExtGraph = extend(Graph, {
|
||||
plugins: {
|
||||
minimap: Extensions.Minimap,
|
||||
},
|
||||
});
|
||||
|
||||
return new ExtGraph({
|
||||
...context,
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: 'node1', data: { x: 100, y: 200, nodeType: 'a' } },
|
||||
{ id: 'node2', data: { x: 200, y: 250, nodeType: 'b' } },
|
||||
{ id: 'node3', data: { x: 200, y: 350, nodeType: 'b' } },
|
||||
{ id: 'node4', data: { x: 300, y: 250, nodeType: 'c' } },
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
data: { edgeType: 'e1' },
|
||||
},
|
||||
{
|
||||
id: 'edge2',
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
data: { edgeType: 'e2' },
|
||||
},
|
||||
{
|
||||
id: 'edge3',
|
||||
source: 'node3',
|
||||
target: 'node4',
|
||||
data: { edgeType: 'e3' },
|
||||
},
|
||||
{
|
||||
id: 'edge4',
|
||||
source: 'node1',
|
||||
target: 'node4',
|
||||
data: { edgeType: 'e3' },
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: ['minimap'],
|
||||
modes: {
|
||||
default: [
|
||||
{
|
||||
type: 'drag-node',
|
||||
},
|
||||
'zoom-canvas',
|
||||
'drag-canvas',
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
103
packages/g6/tests/demo/visual/mapper.ts
Normal file
103
packages/g6/tests/demo/visual/mapper.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { Graph } from '../../../src/index';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const graph = new Graph({
|
||||
...context,
|
||||
data: {
|
||||
nodes: [
|
||||
{ id: '1', data: {} },
|
||||
{ id: '2', data: {} },
|
||||
{ id: '3', data: {} },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'edge1', source: '1', target: '2', data: {} },
|
||||
{ id: 'edge2', source: '1', target: '3', data: {} },
|
||||
{ id: 'edge4', source: '2', target: '3', data: {} },
|
||||
],
|
||||
},
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
node: {
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { container } = context;
|
||||
const jsonNodeMapperBtn = document.createElement('button');
|
||||
container.parentNode?.appendChild(jsonNodeMapperBtn);
|
||||
jsonNodeMapperBtn.innerHTML = '更改节点JSON映射';
|
||||
jsonNodeMapperBtn.id = 'change-node-json-mapper';
|
||||
jsonNodeMapperBtn.style.zIndex = 10;
|
||||
jsonNodeMapperBtn.addEventListener('click', (e) => {
|
||||
graph.updateMapper('node', {
|
||||
labelShape: {
|
||||
text: 'xxx',
|
||||
fontWeight: 800,
|
||||
fill: '#f00',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const funcNodeMapperBtn = document.createElement('button');
|
||||
container.parentNode?.appendChild(funcNodeMapperBtn);
|
||||
funcNodeMapperBtn.innerHTML = '更改节点函数映射';
|
||||
funcNodeMapperBtn.id = 'change-node-func-mapper';
|
||||
funcNodeMapperBtn.style.zIndex = 10;
|
||||
funcNodeMapperBtn.addEventListener('click', (e) => {
|
||||
graph.updateMapper('node', (model) => ({
|
||||
id: model.id,
|
||||
data: {
|
||||
labelShape: {
|
||||
text: `new-${model.id}`,
|
||||
fontWeight: 800,
|
||||
fill: '#0f0',
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
const jsonEdgeMapperBtn = document.createElement('button');
|
||||
container.parentNode?.appendChild(jsonEdgeMapperBtn);
|
||||
jsonEdgeMapperBtn.innerHTML = '更改边JSON映射';
|
||||
jsonEdgeMapperBtn.id = 'change-edge-json-mapper';
|
||||
jsonEdgeMapperBtn.style.zIndex = 10;
|
||||
jsonEdgeMapperBtn.addEventListener('click', (e) => {
|
||||
graph.updateMapper('edge', {
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
fontWeight: 800,
|
||||
fill: '#f00',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const funcEdgeMapperBtn = document.createElement('button');
|
||||
container.parentNode?.appendChild(funcEdgeMapperBtn);
|
||||
funcEdgeMapperBtn.innerHTML = '更改边函数映射';
|
||||
funcEdgeMapperBtn.id = 'change-edge-func-mapper';
|
||||
funcEdgeMapperBtn.style.zIndex = 10;
|
||||
funcEdgeMapperBtn.addEventListener('click', (e) => {
|
||||
graph.updateMapper('edge', (model) => ({
|
||||
id: model.id,
|
||||
data: {
|
||||
labelShape: {
|
||||
text: `new-${model.id}`,
|
||||
fontWeight: 800,
|
||||
fill: '#0f0',
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
72
packages/g6/tests/integration/api-update-mapper.spec.ts
Normal file
72
packages/g6/tests/integration/api-update-mapper.spec.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import mapper from '../demo/visual/mapper';
|
||||
import { createContext } from './utils';
|
||||
import './utils/useSnapshotMatchers';
|
||||
|
||||
describe('updateMapper API', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('node and edge mapper update', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = mapper({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(dir, 'api-update-mapper-init');
|
||||
|
||||
const $updateNodeJson = document.getElementById(
|
||||
'change-node-json-mapper',
|
||||
);
|
||||
$updateNodeJson?.click();
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'api-update-mapper-node-json',
|
||||
);
|
||||
|
||||
const $updateNodeFunc = document.getElementById(
|
||||
'change-node-func-mapper',
|
||||
);
|
||||
$updateNodeFunc?.click();
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'api-update-mapper-node-func',
|
||||
);
|
||||
|
||||
const $updateEdgeJson = document.getElementById(
|
||||
'change-edge-json-mapper',
|
||||
);
|
||||
$updateEdgeJson?.click();
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'api-update-mapper-edge-json',
|
||||
);
|
||||
|
||||
const $updateEdgeFunc = document.getElementById(
|
||||
'change-edge-func-mapper',
|
||||
);
|
||||
$updateEdgeFunc?.click();
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'api-update-mapper-edge-func',
|
||||
);
|
||||
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@ -12,19 +12,6 @@ const renderers = {
|
||||
};
|
||||
|
||||
const getDefaultNodeAnimates = (delay) => ({
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 1000,
|
||||
delay: delay === undefined ? 1000 + Math.random() * 1000 : delay,
|
||||
},
|
||||
],
|
||||
buildOut: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
},
|
||||
],
|
||||
update: [
|
||||
{
|
||||
fields: ['lineWidth', 'fill', 'r'],
|
||||
@ -40,33 +27,12 @@ const getDefaultNodeAnimates = (delay) => ({
|
||||
},
|
||||
],
|
||||
hide: [
|
||||
{
|
||||
fields: ['size'],
|
||||
duration: 200,
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
shapeId: 'keyShape',
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
shapeId: 'labelShape',
|
||||
},
|
||||
],
|
||||
show: [
|
||||
{
|
||||
fields: ['size'],
|
||||
duration: 200,
|
||||
},
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 200,
|
||||
shapeId: 'keyShape',
|
||||
order: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const getDefaultEdgeAnimates = (delay) => ({
|
||||
@ -244,6 +210,7 @@ const create3DGraph = async (data) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const create2DGraph = (renderer, data) => {
|
||||
return new ExtGraph({
|
||||
container,
|
||||
@ -260,7 +227,7 @@ const create2DGraph = (renderer, data) => {
|
||||
],
|
||||
modes: {
|
||||
default: [
|
||||
{ type: 'zoom-canvas', key: '123', triggerOnItems: true },
|
||||
{ type: 'zoom-canvas', key: '123', triggerOnItems: true, enableOptimize: true },
|
||||
'drag-node',
|
||||
'drag-canvas',
|
||||
'brush-select',
|
||||
|
Loading…
Reference in New Issue
Block a user