mirror of
https://gitee.com/antv/g6.git
synced 2024-12-02 19:58:46 +08:00
feat: support history (visibility, state, position)
This commit is contained in:
parent
11a9a3016a
commit
3dd8625766
@ -330,13 +330,15 @@ export class DataController {
|
||||
const prevNodesAndCombos = userGraphCore.getAllNodes();
|
||||
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')) {
|
||||
// update the parentId
|
||||
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));
|
||||
}
|
||||
|
@ -282,6 +282,7 @@ export class ItemController {
|
||||
upsertAncestors?: boolean;
|
||||
action?: 'updatePosition';
|
||||
}) {
|
||||
debugger;
|
||||
const {
|
||||
changes,
|
||||
graphCore,
|
||||
@ -473,7 +474,7 @@ export class ItemController {
|
||||
}
|
||||
const parentItem = this.itemMap[current.parentId];
|
||||
if (current.parentId && parentItem?.model.data.collapsed) {
|
||||
this.graph.hideItem(innerModel.id);
|
||||
this.graph.hideItem(innerModel.id, false, false);
|
||||
}
|
||||
});
|
||||
updateRelates();
|
||||
@ -1097,7 +1098,8 @@ export class ItemController {
|
||||
const succeedIds: ID[] = [];
|
||||
// hide the succeeds
|
||||
graphComboTreeDfs(this.graph, [comboModel], (child) => {
|
||||
if (child.id !== comboModel.id) this.graph.hideItem(child.id);
|
||||
if (child.id !== comboModel.id)
|
||||
this.graph.hideItem(child.id, false, false);
|
||||
relatedEdges = relatedEdges.concat(graphCore.getRelatedEdges(child.id));
|
||||
succeedIds.push(child.id);
|
||||
});
|
||||
@ -1153,7 +1155,7 @@ export class ItemController {
|
||||
});
|
||||
if (child.id !== comboModel.id) {
|
||||
if (!graphCore.getNode(child.data.parentId).data.collapsed) {
|
||||
this.graph.showItem(child.id);
|
||||
this.graph.showItem(child.id, false, false);
|
||||
}
|
||||
// re-add collapsed succeeds' virtual edges by calling collapseCombo
|
||||
if (child.data._isCombo && child.data.collapsed) {
|
||||
|
@ -135,6 +135,18 @@ export class PluginController {
|
||||
}
|
||||
}
|
||||
|
||||
public hasPlugin(pluginKey: string): boolean {
|
||||
return this.pluginMap.has(pluginKey);
|
||||
}
|
||||
|
||||
public getPlugin(pluginKey: string): Plugin {
|
||||
const { plugin } = this.pluginMap.get(pluginKey);
|
||||
if (!plugin) {
|
||||
throw new Error('Plugin not found for key: ' + pluginKey);
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private addListeners = (key: string, plugin: Plugin) => {
|
||||
const events = plugin.getEvents();
|
||||
this.listenersMap[key] = {};
|
||||
|
@ -1,7 +1,19 @@
|
||||
import EventEmitter from '@antv/event-emitter';
|
||||
import { AABB, Canvas, DisplayObject, PointLike, runtime } from '@antv/g';
|
||||
import { GraphChange, ID } from '@antv/graphlib';
|
||||
import { isArray, isNil, isNumber, isObject, isString } from '@antv/util';
|
||||
import {
|
||||
each,
|
||||
groupBy,
|
||||
isArray,
|
||||
isBoolean,
|
||||
isEmpty,
|
||||
isEqual,
|
||||
isNil,
|
||||
isNumber,
|
||||
isObject,
|
||||
isString,
|
||||
map,
|
||||
} from '@antv/util';
|
||||
import {
|
||||
ComboUserModel,
|
||||
EdgeUserModel,
|
||||
@ -33,6 +45,8 @@ import {
|
||||
import { FitViewRules, GraphTransformOptions } from '../types/view';
|
||||
import { changeRenderer, createCanvas } from '../util/canvas';
|
||||
import { formatPadding } from '../util/shape';
|
||||
import History from '../stdlib/plugin/history';
|
||||
import CommandFactory from '../stdlib/plugin/history/command';
|
||||
import {
|
||||
DataController,
|
||||
ExtensionController,
|
||||
@ -44,7 +58,6 @@ import {
|
||||
} from './controller';
|
||||
import { PluginController } from './controller/plugin';
|
||||
import Hook from './hooks';
|
||||
|
||||
/**
|
||||
* Disable CSS parsing for better performance.
|
||||
*/
|
||||
@ -70,6 +83,9 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
// the tag indicates all the three canvases are all ready
|
||||
private canvasReady: boolean;
|
||||
private specification: Specification<B, T>;
|
||||
|
||||
private enableStack: boolean;
|
||||
|
||||
private dataController: DataController;
|
||||
private interactionController: InteractionController;
|
||||
private layoutController: LayoutController;
|
||||
@ -84,6 +100,10 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
type: 'spec',
|
||||
base: 'light',
|
||||
},
|
||||
enableStack: true,
|
||||
stackCfg: {
|
||||
ignoreStateChange: false,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(spec: Specification<B, T>) {
|
||||
@ -93,6 +113,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
this.initHooks();
|
||||
this.initCanvas();
|
||||
this.initControllers();
|
||||
this.initHistory();
|
||||
|
||||
this.hooks.init.emit({
|
||||
canvases: {
|
||||
@ -220,6 +241,16 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
});
|
||||
}
|
||||
|
||||
private initHistory() {
|
||||
this.enableStack = this.specification.enableStack;
|
||||
|
||||
if (this.enableStack) {
|
||||
const history = { type: 'history', key: 'history' };
|
||||
this.specification.plugins ||= [];
|
||||
this.specification.plugins.push(history);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the renderer at runtime.
|
||||
* @param type renderer name
|
||||
@ -891,7 +922,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
| NodeUserModel[]
|
||||
| EdgeUserModel[]
|
||||
| ComboUserModel[],
|
||||
stack?: boolean,
|
||||
stack = true,
|
||||
):
|
||||
| NodeModel
|
||||
| EdgeModel
|
||||
@ -911,6 +942,14 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
graphCore,
|
||||
theme: specification,
|
||||
});
|
||||
if (this.enableStack && stack) {
|
||||
const changes = event.changes;
|
||||
if (!isEmpty(changes)) {
|
||||
const cmd = CommandFactory.create(changes);
|
||||
const history = this.getHistoryPlugin();
|
||||
history.push(cmd);
|
||||
}
|
||||
}
|
||||
this.emit('afteritemchange', { type: itemType, action: 'add', models });
|
||||
});
|
||||
|
||||
@ -975,7 +1014,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
| Partial<EdgeUserModel>[]
|
||||
| Partial<ComboUserModel>[]
|
||||
>,
|
||||
stack?: boolean,
|
||||
stack = true,
|
||||
):
|
||||
| NodeModel
|
||||
| EdgeModel
|
||||
@ -1028,7 +1067,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
stack?: boolean,
|
||||
stack = true,
|
||||
) {
|
||||
return this.updatePosition('node', models, upsertAncestors, stack);
|
||||
}
|
||||
@ -1048,11 +1087,21 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
stack?: boolean,
|
||||
stack = true,
|
||||
) {
|
||||
return this.updatePosition('combo', models, upsertAncestors, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get history plugin instance
|
||||
*/
|
||||
private getHistoryPlugin(): History {
|
||||
if (this.enableStack) {
|
||||
return this.pluginController.getPlugin('history') as History;
|
||||
}
|
||||
throw new Error('History plugin is currently not configured.');
|
||||
}
|
||||
|
||||
private updatePosition(
|
||||
type: 'node' | 'combo',
|
||||
models:
|
||||
@ -1061,7 +1110,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
ComboUserModel | Partial<NodeUserModel>[] | Partial<ComboUserModel>[]
|
||||
>,
|
||||
upsertAncestors?: boolean,
|
||||
stack?: boolean,
|
||||
stack = true,
|
||||
) {
|
||||
const modelArr = isArray(models) ? models : [models];
|
||||
const { graphCore } = this.dataController;
|
||||
@ -1076,6 +1125,16 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
upsertAncestors,
|
||||
action: 'updatePosition',
|
||||
});
|
||||
if (this.enableStack && stack) {
|
||||
const changes = event.changes.filter(
|
||||
(change) => !isEqual(change.newValue, change.oldValue),
|
||||
);
|
||||
if (!isEmpty(changes)) {
|
||||
const cmd = CommandFactory.create(changes, 'updatePosition');
|
||||
const history = this.getHistoryPlugin();
|
||||
history.push(cmd);
|
||||
}
|
||||
}
|
||||
this.emit('afteritemchange', {
|
||||
type,
|
||||
action: 'updatePosition',
|
||||
@ -1099,19 +1158,51 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
return isArray(models) ? dataList : dataList[0];
|
||||
}
|
||||
|
||||
private getItemPreviousVisibility(ids: ID[]) {
|
||||
const objs = ids.map((id) => ({ id, visible: this.getItemVisible(id) }));
|
||||
const groupedByVisible = groupBy(objs, 'visible');
|
||||
|
||||
const values = [];
|
||||
for (const visible in groupedByVisible) {
|
||||
values.push({
|
||||
ids: map(groupedByVisible[visible], (item) => item.id),
|
||||
visible: Boolean(visible),
|
||||
});
|
||||
}
|
||||
console.log('values', values);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the item(s).
|
||||
* @param item the item to be shown
|
||||
* @returns
|
||||
* @group Item
|
||||
*/
|
||||
public showItem(ids: ID | ID[], disableAniamte?: boolean) {
|
||||
public showItem(ids: ID | ID[], disableAnimate?: boolean, stack = true) {
|
||||
const idArr = isArray(ids) ? ids : [ids];
|
||||
if (isEmpty(idArr)) return;
|
||||
if (this.enableStack && stack) {
|
||||
const changes = {
|
||||
newValue: [{ ids: idArr, visible: true }],
|
||||
oldValue: this.getItemPreviousVisibility(idArr),
|
||||
params: { disableAnimate },
|
||||
};
|
||||
const cmd = CommandFactory.create(changes, 'updateVisibility');
|
||||
const history = this.getHistoryPlugin();
|
||||
history.push(cmd);
|
||||
}
|
||||
this.hooks.itemvisibilitychange.emit({
|
||||
ids: idArr as ID[],
|
||||
value: true,
|
||||
graphCore: this.dataController.graphCore,
|
||||
animate: !disableAniamte,
|
||||
animate: !disableAnimate,
|
||||
});
|
||||
this.emit('afteritemvisibilitychange', {
|
||||
ids,
|
||||
value: true,
|
||||
animate: !disableAnimate,
|
||||
});
|
||||
}
|
||||
/**
|
||||
@ -1120,13 +1211,31 @@ 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, stack = true) {
|
||||
const idArr = isArray(ids) ? ids : [ids];
|
||||
if (isEmpty(idArr)) return;
|
||||
if (this.enableStack && stack) {
|
||||
const changes = {
|
||||
newValue: [{ ids: idArr, visible: false }],
|
||||
oldValue: this.getItemPreviousVisibility(idArr),
|
||||
params: { disableAnimate },
|
||||
};
|
||||
console.log('changes', changes);
|
||||
|
||||
const cmd = CommandFactory.create(changes, 'updateVisibility');
|
||||
const history = this.getHistoryPlugin();
|
||||
history.push(cmd);
|
||||
}
|
||||
this.hooks.itemvisibilitychange.emit({
|
||||
ids: idArr as ID[],
|
||||
value: false,
|
||||
graphCore: this.dataController.graphCore,
|
||||
animate: !disableAniamte,
|
||||
animate: !disableAnimate,
|
||||
});
|
||||
this.emit('afteritemvisibilitychange', {
|
||||
ids,
|
||||
value: false,
|
||||
animate: !disableAnimate,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1158,6 +1267,52 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
graphCore: this.dataController.graphCore,
|
||||
});
|
||||
}
|
||||
|
||||
private getItemPreviousStates(
|
||||
stateOptions: {
|
||||
ids: ID | ID[];
|
||||
states: string | string[];
|
||||
value: boolean;
|
||||
}[],
|
||||
) {
|
||||
return stateOptions
|
||||
.flatMap((option) => {
|
||||
const { ids, states } = option;
|
||||
const idArr = Array.isArray(ids) ? ids : [ids];
|
||||
const stateArr = Array.isArray(states) ? states : [states];
|
||||
if (isEmpty(idArr)) return;
|
||||
return idArr.flatMap((id) => {
|
||||
return stateArr.map((state) => ({
|
||||
ids: id,
|
||||
states: state,
|
||||
value: this.getItemState(id, state),
|
||||
}));
|
||||
});
|
||||
})
|
||||
.filter((option) => !isEmpty(option));
|
||||
}
|
||||
|
||||
public setItemStates(
|
||||
stateOptions: {
|
||||
ids: ID | ID[];
|
||||
states: string | string[];
|
||||
value: boolean;
|
||||
}[],
|
||||
stack = true,
|
||||
) {
|
||||
if (this.enableStack && stack) {
|
||||
const changes = {
|
||||
newValue: stateOptions,
|
||||
oldValue: this.getItemPreviousStates(stateOptions),
|
||||
};
|
||||
const cmds = CommandFactory.create(changes, 'updateState');
|
||||
const history = this.getHistoryPlugin();
|
||||
history.push(cmds);
|
||||
}
|
||||
return each(stateOptions, (option) =>
|
||||
this.setItemState(option.ids, option.states, option.value, false),
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Set state for the item.
|
||||
* @param item the item to be set
|
||||
@ -1170,9 +1325,22 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
ids: ID | ID[],
|
||||
states: string | string[],
|
||||
value: boolean,
|
||||
stack = true,
|
||||
) {
|
||||
const idArr = isArray(ids) ? ids : [ids];
|
||||
const stateArr = isArray(states) ? states : [states];
|
||||
if (this.enableStack && stack) {
|
||||
if (!isEmpty(idArr)) {
|
||||
const stateOptions = [{ ids: idArr, states: stateArr, value }];
|
||||
const changes = {
|
||||
newValue: stateOptions,
|
||||
oldValue: this.getItemPreviousStates(stateOptions),
|
||||
};
|
||||
const cmds = CommandFactory.create(changes, 'updateState');
|
||||
const history = this.getHistoryPlugin();
|
||||
history.push(cmds);
|
||||
}
|
||||
}
|
||||
this.hooks.itemstatechange.emit({
|
||||
ids: idArr as ID[],
|
||||
states: stateArr as string[],
|
||||
@ -1198,8 +1366,20 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
* @returns
|
||||
* @group Item
|
||||
*/
|
||||
public clearItemState(ids: ID | ID[], states?: string[]) {
|
||||
public clearItemState(ids: ID | ID[], states?: string[], stack = true) {
|
||||
const idArr = isArray(ids) ? ids : [ids];
|
||||
if (this.enableStack && stack) {
|
||||
if (!isEmpty(idArr)) {
|
||||
const stateOptions = [{ ids: idArr, states, value: false }];
|
||||
const changes = {
|
||||
newValue: stateOptions,
|
||||
oldValue: this.getItemPreviousStates(stateOptions),
|
||||
};
|
||||
const cmds = CommandFactory.create(changes, 'updateState');
|
||||
const history = this.getHistoryPlugin();
|
||||
history.push(cmds);
|
||||
}
|
||||
}
|
||||
this.hooks.itemstatechange.emit({
|
||||
ids: idArr as ID[],
|
||||
states,
|
||||
@ -1243,11 +1423,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
* @returns whether success
|
||||
* @group Combo
|
||||
*/
|
||||
public addCombo(
|
||||
model: ComboUserModel,
|
||||
childrenIds: ID[],
|
||||
stack?: boolean,
|
||||
): ComboModel {
|
||||
public addCombo(model: ComboUserModel, childrenIds: ID[]): ComboModel {
|
||||
const { graphCore } = this.dataController;
|
||||
const { specification } = this.themeController;
|
||||
graphCore.once('changed', (event) => {
|
||||
@ -1324,7 +1500,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
dx: number,
|
||||
dy: number,
|
||||
upsertAncestors?: boolean,
|
||||
stack?: boolean,
|
||||
stack = true,
|
||||
): ComboModel[] {
|
||||
const idArr = isArray(ids) ? ids : [ids];
|
||||
const { graphCore } = this.dataController;
|
||||
@ -1653,6 +1829,54 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
return this.itemController.getTransient(String(id));
|
||||
}
|
||||
|
||||
// ===== history operations =====
|
||||
|
||||
/**
|
||||
* Restore n operations that were last n reverted on the graph.
|
||||
* @param steps The number of operations to undo. Default to 1.
|
||||
* @returns
|
||||
*/
|
||||
public undo(steps?: number) {
|
||||
if (!this.pluginController.hasPlugin('history')) {
|
||||
console.warn('当前没有配置 history 插件,请先配置 enableStack 为 true');
|
||||
return;
|
||||
}
|
||||
const history = this.getHistoryPlugin();
|
||||
history.undo(steps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert recent n operation(s) performed on the graph.
|
||||
* @param steps The number of operations to redo. Default to 1.
|
||||
* @returns
|
||||
*/
|
||||
public redo(steps?: number) {
|
||||
if (!this.pluginController.hasPlugin('history')) {
|
||||
console.warn('当前没有配置 history 插件,请先配置 enableStack 为 true');
|
||||
return;
|
||||
}
|
||||
const history = this.getHistoryPlugin();
|
||||
history.redo(steps);
|
||||
}
|
||||
|
||||
public canUndo() {
|
||||
const history = this.getHistoryPlugin();
|
||||
return history.canUndo();
|
||||
}
|
||||
|
||||
public canRedo() {
|
||||
const history = this.getHistoryPlugin();
|
||||
return history.canRedo();
|
||||
}
|
||||
|
||||
public cleanHistory(type?: 'undo' | 'redo') {
|
||||
const history = this.getHistoryPlugin();
|
||||
if (!type) return history.clean();
|
||||
return type === 'undo'
|
||||
? history.cleanUndoStack()
|
||||
: history.cleanRedoStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the graph instance and remove the related canvases.
|
||||
* @returns
|
||||
|
@ -138,9 +138,13 @@ export default class ClickSelect extends Behavior {
|
||||
if (this.options.shouldUpdate(event)) {
|
||||
if (!multiple) {
|
||||
// Not multiple, clear all currently selected items
|
||||
this.graph.setItemState(this.selectedIds, state, false);
|
||||
this.graph.setItemStates([
|
||||
{ ids: this.selectedIds, states: state, value: false },
|
||||
{ ids: itemId, states: state, value: isSelectAction },
|
||||
]);
|
||||
} else {
|
||||
this.graph.setItemState(itemId, state, isSelectAction);
|
||||
}
|
||||
this.graph.setItemState(itemId, state, isSelectAction);
|
||||
if (isSelectAction) {
|
||||
this.selectedIds.push(itemId);
|
||||
} else {
|
||||
|
@ -137,7 +137,7 @@ export default class DragCanvas extends Behavior {
|
||||
.getAllEdgesData()
|
||||
.map((edge) => edge.id)
|
||||
.filter((id) => graph.getItemVisible(id) === true);
|
||||
graph.hideItem(this.hiddenEdgeIds, true);
|
||||
graph.hideItem(this.hiddenEdgeIds, true, false);
|
||||
this.hiddenNodeIds = graph
|
||||
.getAllNodesData()
|
||||
.map((node) => node.id)
|
||||
@ -148,7 +148,7 @@ export default class DragCanvas extends Behavior {
|
||||
onlyDrawKeyShape: true,
|
||||
});
|
||||
});
|
||||
graph.hideItem(this.hiddenNodeIds, true);
|
||||
graph.hideItem(this.hiddenNodeIds, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,13 +242,13 @@ export default class DragCanvas extends Behavior {
|
||||
const { graph } = this;
|
||||
if (this.options.enableOptimize) {
|
||||
if (this.hiddenEdgeIds) {
|
||||
graph.showItem(this.hiddenEdgeIds, true);
|
||||
graph.showItem(this.hiddenEdgeIds, true, false);
|
||||
}
|
||||
if (this.hiddenNodeIds) {
|
||||
this.hiddenNodeIds.forEach((id) => {
|
||||
this.graph.drawTransient('node', id, { action: 'remove' });
|
||||
});
|
||||
graph.showItem(this.hiddenNodeIds, true);
|
||||
graph.showItem(this.hiddenNodeIds, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,10 +250,12 @@ export default class DragCombo extends Behavior {
|
||||
this.graph.hideItem(
|
||||
this.hiddenEdges.map((edge) => edge.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.graph.hideItem(
|
||||
this.hiddenComboTreeRoots.map((child) => child.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@ -275,14 +277,16 @@ export default class DragCombo extends Behavior {
|
||||
});
|
||||
|
||||
// Hide original edges and nodes. They will be restored when pointerup.
|
||||
this.graph.hideItem(selectedComboIds, true);
|
||||
this.graph.hideItem(selectedComboIds, true, false);
|
||||
this.graph.hideItem(
|
||||
this.hiddenEdges.map((edge) => edge.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.graph.hideItem(
|
||||
this.hiddenComboTreeRoots.map((child) => child.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
this.graph.frontItem(selectedComboIds);
|
||||
@ -423,6 +427,7 @@ export default class DragCombo extends Behavior {
|
||||
this.graph.showItem(
|
||||
this.hiddenEdges.map((edge) => edge.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.hiddenEdges = [];
|
||||
}
|
||||
@ -430,6 +435,7 @@ export default class DragCombo extends Behavior {
|
||||
this.graph.showItem(
|
||||
this.hiddenComboTreeRoots.map((model) => model.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.hiddenComboTreeRoots = [];
|
||||
}
|
||||
@ -439,6 +445,7 @@ export default class DragCombo extends Behavior {
|
||||
this.graph.showItem(
|
||||
this.originPositions.map((position) => position.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -239,10 +239,12 @@ export default class DragNode extends Behavior {
|
||||
this.graph.hideItem(
|
||||
this.hiddenEdges.map((edge) => edge.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.graph.hideItem(
|
||||
this.hiddenComboTreeItems.map((child) => child.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@ -266,14 +268,16 @@ export default class DragNode extends Behavior {
|
||||
});
|
||||
|
||||
// Hide original edges and nodes. They will be restored when pointerup.
|
||||
this.graph.hideItem(selectedNodeIds, true);
|
||||
this.graph.hideItem(selectedNodeIds, true, false);
|
||||
this.graph.hideItem(
|
||||
this.hiddenEdges.map((edge) => edge.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.graph.hideItem(
|
||||
this.hiddenComboTreeItems.map((combo) => combo.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
this.graph.frontItem(selectedNodeIds);
|
||||
@ -413,6 +417,7 @@ export default class DragNode extends Behavior {
|
||||
this.graph.showItem(
|
||||
this.hiddenEdges.map((edge) => edge.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.hiddenEdges = [];
|
||||
}
|
||||
@ -420,6 +425,7 @@ export default class DragNode extends Behavior {
|
||||
this.graph.showItem(
|
||||
this.hiddenComboTreeItems.map((edge) => edge.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
this.hiddenComboTreeItems = [];
|
||||
}
|
||||
@ -429,6 +435,7 @@ export default class DragNode extends Behavior {
|
||||
this.graph.showItem(
|
||||
this.originPositions.map((position) => position.id),
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ export default class ZoomCanvas extends Behavior {
|
||||
.getAllEdgesData()
|
||||
.map((edge) => edge.id)
|
||||
.filter((id) => graph.getItemVisible(id) === true);
|
||||
graph.hideItem(this.hiddenEdgeIds);
|
||||
graph.hideItem(this.hiddenEdgeIds, false, false);
|
||||
this.hiddenNodeIds = graph
|
||||
.getAllNodesData()
|
||||
.map((node) => node.id)
|
||||
@ -130,7 +130,7 @@ export default class ZoomCanvas extends Behavior {
|
||||
onlyDrawKeyShape: true,
|
||||
});
|
||||
});
|
||||
graph.hideItem(this.hiddenNodeIds);
|
||||
graph.hideItem(this.hiddenNodeIds, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,13 +141,13 @@ export default class ZoomCanvas extends Behavior {
|
||||
if (enableOptimize) {
|
||||
// restore hidden items
|
||||
if (hiddenEdgeIds) {
|
||||
graph.showItem(hiddenEdgeIds);
|
||||
graph.showItem(hiddenEdgeIds, false, false);
|
||||
}
|
||||
if (hiddenNodeIds) {
|
||||
hiddenNodeIds.forEach((id) => {
|
||||
graph.drawTransient('node', id, { action: 'remove' });
|
||||
});
|
||||
graph.showItem(hiddenNodeIds);
|
||||
graph.showItem(hiddenNodeIds, false, false);
|
||||
}
|
||||
}
|
||||
this.hiddenEdgeIds = [];
|
||||
|
@ -23,6 +23,7 @@ import Legend from './plugin/legend';
|
||||
import Grid from './plugin/grid';
|
||||
import Tooltip from './plugin/tooltip';
|
||||
import Menu from './plugin/menu';
|
||||
import History from './plugin/history';
|
||||
import ZoomCanvas from './behavior/zoom-canvas';
|
||||
import ZoomCanvas3D from './behavior/zoom-canvas-3d';
|
||||
import RotateCanvas3D from './behavior/rotate-canvas-3d';
|
||||
@ -72,6 +73,7 @@ const stdLib = {
|
||||
grid: Grid,
|
||||
tooltip: Tooltip,
|
||||
menu: Menu,
|
||||
history: History,
|
||||
},
|
||||
nodes: {
|
||||
'circle-node': CircleNode,
|
||||
|
49
packages/g6/src/stdlib/plugin/history/command.ts
Normal file
49
packages/g6/src/stdlib/plugin/history/command.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { groupBy } from '@antv/util';
|
||||
import { IGraph } from '../../../types';
|
||||
import { ItemAddedCommand } from './item-added-command';
|
||||
import { StateUpdatedCommand } from './state-updated-command';
|
||||
import { PositionUpdatedCommand } from './position-updated-command';
|
||||
import { VisibilityUpdatedCommand } from './visibility-updated-command';
|
||||
|
||||
export interface Command {
|
||||
redo: (graph: IGraph) => void;
|
||||
undo: (graph: IGraph) => void;
|
||||
}
|
||||
|
||||
export default class CommandFactory {
|
||||
static create(
|
||||
options,
|
||||
action?: 'updatePosition' | 'updateState' | 'updateVisibility',
|
||||
) {
|
||||
const onlyMove = action === 'updatePosition';
|
||||
const groupedByType = groupBy(options, 'type');
|
||||
|
||||
const commands = [];
|
||||
|
||||
for (const type in groupedByType) {
|
||||
switch (type) {
|
||||
case 'NodeDataUpdated':
|
||||
if (onlyMove) {
|
||||
commands.push(new PositionUpdatedCommand(groupedByType[type]));
|
||||
break;
|
||||
}
|
||||
case 'NodeAdded': {
|
||||
commands.push(new ItemAddedCommand('node', groupedByType[type]));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'updateState') {
|
||||
commands.push(new StateUpdatedCommand(options));
|
||||
}
|
||||
|
||||
if (action === 'updateVisibility') {
|
||||
commands.push(new VisibilityUpdatedCommand(options));
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
}
|
91
packages/g6/src/stdlib/plugin/history/index.ts
Normal file
91
packages/g6/src/stdlib/plugin/history/index.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import type { IGraph } from 'types';
|
||||
import { each, mix } from '@antv/util';
|
||||
import { Plugin as Base, IPluginBaseConfig } from '../../../types/plugin';
|
||||
import { Command } from './command';
|
||||
|
||||
export interface HistoryConfig extends IPluginBaseConfig {
|
||||
/** Default to true */
|
||||
enableStack?: boolean;
|
||||
/** Default to 0 stands no limit */
|
||||
stackSize?: number;
|
||||
}
|
||||
|
||||
export default class History extends Base {
|
||||
public readonly cfg: Partial<HistoryConfig>;
|
||||
protected undoStack: Command[][] = []; // support batch
|
||||
protected redoStack: Command[][] = [];
|
||||
protected stackSize = 0;
|
||||
|
||||
constructor(options?: HistoryConfig) {
|
||||
super();
|
||||
this.cfg = mix({}, this.getDefaultCfgs(), options.cfg);
|
||||
}
|
||||
|
||||
public getDefaultCfgs(): HistoryConfig {
|
||||
return {
|
||||
enableStack: true,
|
||||
stackSize: 0, // 0: not limit
|
||||
};
|
||||
}
|
||||
|
||||
public init(graph: IGraph) {
|
||||
super.init(graph);
|
||||
this.clean();
|
||||
}
|
||||
|
||||
public clean() {
|
||||
this.cleanUndoStack();
|
||||
this.cleanRedoStack();
|
||||
return this;
|
||||
}
|
||||
|
||||
public cleanUndoStack() {
|
||||
this.undoStack = [];
|
||||
}
|
||||
|
||||
public cleanRedoStack() {
|
||||
this.redoStack = [];
|
||||
}
|
||||
|
||||
public isEnable() {
|
||||
return this.cfg.enableStack;
|
||||
}
|
||||
|
||||
public push(cmd: Command[]) {
|
||||
// Clear the redo stack when a new action is performed to maintain state consistency
|
||||
this.cleanRedoStack();
|
||||
this.undoStack.push(cmd);
|
||||
}
|
||||
|
||||
public undo(steps = 1) {
|
||||
if (this.isEnable()) {
|
||||
const cmds = this.undoStack.pop();
|
||||
if (cmds) {
|
||||
this.redoStack.push(cmds);
|
||||
each(cmds, (cmd) => cmd.undo(this.graph));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public redo(steps = 1) {
|
||||
if (this.isEnable()) {
|
||||
const cmds = this.redoStack.pop();
|
||||
if (cmds) {
|
||||
this.undoStack.push(cmds);
|
||||
for (let i = cmds.length - 1; i >= 0; i--) {
|
||||
cmds[i].redo(this.graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public canUndo() {
|
||||
return this.isEnable() && this.undoStack.length > 0;
|
||||
}
|
||||
|
||||
public canRedo() {
|
||||
return this.isEnable() && this.redoStack.length > 0;
|
||||
}
|
||||
}
|
24
packages/g6/src/stdlib/plugin/history/item-added-command.ts
Normal file
24
packages/g6/src/stdlib/plugin/history/item-added-command.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { ITEM_TYPE } from '../../../types/item';
|
||||
import type { IGraph } from '../../../types';
|
||||
import type { GroupedChanges } from '../../../util/event';
|
||||
import { Command } from './command';
|
||||
|
||||
export class ItemAddedCommand implements Command {
|
||||
private diffData: GroupedChanges['NodeAdded'];
|
||||
private type: ITEM_TYPE;
|
||||
|
||||
constructor(type, options) {
|
||||
this.type = type;
|
||||
this.diffData = options;
|
||||
}
|
||||
|
||||
undo(graph: IGraph) {
|
||||
const ids = this.diffData.map((data) => data.value.id);
|
||||
graph.removeData(this.type, ids, false);
|
||||
}
|
||||
|
||||
redo(graph: IGraph) {
|
||||
const models = this.diffData.map((data) => data.value);
|
||||
graph.addData(this.type, models, false);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { GroupedChanges } from 'util/event';
|
||||
import type { IGraph } from '../../../types';
|
||||
import { Command } from './command';
|
||||
|
||||
export class ItemDataUpdatedCommand implements Command {
|
||||
private diffData: GroupedChanges['NodeDataUpdated' | 'EdgeDataUpdated'];
|
||||
|
||||
constructor(options) {
|
||||
this.diffData = options;
|
||||
}
|
||||
|
||||
undo(graph: IGraph) {}
|
||||
|
||||
redo(graph: IGraph) {}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { GroupedChanges } from 'util/event';
|
||||
import type { IGraph } from '../../../types';
|
||||
import { Command } from './command';
|
||||
|
||||
export class PositionUpdatedCommand implements Command {
|
||||
private diffData: GroupedChanges['NodeDataUpdated'];
|
||||
|
||||
constructor(options) {
|
||||
this.diffData = options;
|
||||
}
|
||||
|
||||
undo(graph: IGraph) {
|
||||
const models = this.diffData.map((data) => ({
|
||||
id: data.id,
|
||||
data: data.oldValue,
|
||||
}));
|
||||
graph.updatePosition('node', models, false, false);
|
||||
}
|
||||
|
||||
redo(graph: IGraph) {
|
||||
const models = this.diffData.map((data) => ({
|
||||
id: data.id,
|
||||
data: data.newValue,
|
||||
}));
|
||||
graph.updatePosition('node', models, false, false);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import type { ID, IGraph } from '../../../types';
|
||||
import { Command } from './command';
|
||||
|
||||
interface StateOption {
|
||||
ids: ID | ID[];
|
||||
states: string | string[];
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
export class StateUpdatedCommand implements Command {
|
||||
private diffState: {
|
||||
newValue: StateOption[];
|
||||
oldValue: StateOption[];
|
||||
};
|
||||
|
||||
constructor(options) {
|
||||
this.diffState = options;
|
||||
}
|
||||
|
||||
undo(graph: IGraph) {
|
||||
graph.setItemStates(this.diffState.oldValue, false);
|
||||
}
|
||||
|
||||
redo(graph: IGraph) {
|
||||
graph.setItemStates(this.diffState.newValue, false);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
import { each } from '@antv/util';
|
||||
import type { ID, IGraph } from '../../../types';
|
||||
import { Command } from './command';
|
||||
|
||||
interface Option {
|
||||
ids: ID[];
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export class VisibilityUpdatedCommand implements Command {
|
||||
private diffState: {
|
||||
newValue: Option[];
|
||||
oldValue: Option[];
|
||||
params: {
|
||||
disableAnimate?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
constructor(options) {
|
||||
this.diffState = options;
|
||||
}
|
||||
|
||||
undo(graph: IGraph) {
|
||||
const {
|
||||
oldValue,
|
||||
params: { disableAnimate },
|
||||
} = this.diffState;
|
||||
|
||||
each(oldValue, (value) =>
|
||||
value.visible
|
||||
? graph.showItem(value.ids, disableAnimate, false)
|
||||
: graph.hideItem(value.ids, disableAnimate, false),
|
||||
);
|
||||
}
|
||||
|
||||
redo(graph: IGraph) {
|
||||
const {
|
||||
newValue,
|
||||
params: { disableAnimate },
|
||||
} = this.diffState;
|
||||
|
||||
each(newValue, (value) =>
|
||||
value.visible
|
||||
? graph.showItem(value.ids, disableAnimate, false)
|
||||
: graph.hideItem(value.ids, disableAnimate, false),
|
||||
);
|
||||
}
|
||||
}
|
@ -444,14 +444,14 @@ export interface IGraph<
|
||||
* @returns
|
||||
* @group Data
|
||||
*/
|
||||
showItem: (ids: ID | ID[], disableAniamte?: boolean) => void;
|
||||
showItem: (ids: ID | ID[], disableAnimate?: boolean, stack?: 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, stack?: boolean) => void;
|
||||
/**
|
||||
* Make the item(s) to the front.
|
||||
* @param ids the item id(s) to front
|
||||
@ -466,6 +466,11 @@ export interface IGraph<
|
||||
* @group Item
|
||||
*/
|
||||
backItem: (ids: ID | ID[]) => void;
|
||||
|
||||
setItemStates: (
|
||||
options: { ids: ID | ID[]; states: string | string[]; value: boolean }[],
|
||||
stack?: boolean,
|
||||
) => void;
|
||||
/**
|
||||
* Set state for the item(s).
|
||||
* @param ids the id(s) for the item(s) to be set
|
||||
@ -474,7 +479,12 @@ export interface IGraph<
|
||||
* @returns
|
||||
* @group Item
|
||||
*/
|
||||
setItemState: (ids: ID | ID[], state: string, value: boolean) => void;
|
||||
setItemState: (
|
||||
ids: ID | ID[],
|
||||
state: string,
|
||||
value: boolean,
|
||||
stack?: boolean,
|
||||
) => void;
|
||||
/**
|
||||
* Get the state value for an item.
|
||||
* @param id the id for the item
|
||||
@ -490,7 +500,7 @@ export interface IGraph<
|
||||
* @returns
|
||||
* @group Item
|
||||
*/
|
||||
clearItemState: (ids: ID | ID[], states?: string[]) => void;
|
||||
clearItemState: (ids: ID | ID[], states?: string[], stack?: boolean) => void;
|
||||
|
||||
/**
|
||||
* Get the rendering bbox for a node / edge / combo, or the graph (when the id is not assigned).
|
||||
@ -627,4 +637,18 @@ export interface IGraph<
|
||||
type: string;
|
||||
[cfgName: string]: unknown;
|
||||
}) => void;
|
||||
|
||||
/**
|
||||
* Revert the last n operation(s) on the graph.
|
||||
* @param {number} steps The number of steps to undo. Default to 1.
|
||||
* @returns
|
||||
*/
|
||||
undo: (steps?: number) => void;
|
||||
|
||||
/**
|
||||
* Restore the operation that was last n reverted on the graph.
|
||||
* @param {number} steps The number of steps to redo. Default to 1.
|
||||
* @returns
|
||||
*/
|
||||
redo: (steps?: number) => void;
|
||||
}
|
||||
|
@ -117,4 +117,6 @@ export interface Specification<
|
||||
|
||||
/** theme */
|
||||
theme?: ThemeOptionsOf<T>;
|
||||
|
||||
enableStack?: boolean;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ export const getContextMenuEventProps = (
|
||||
};
|
||||
};
|
||||
|
||||
type GroupedChanges = {
|
||||
export type GroupedChanges = {
|
||||
NodeRemoved: NodeRemoved<NodeModelData>[];
|
||||
EdgeRemoved: EdgeRemoved<EdgeModelData>[];
|
||||
NodeAdded: NodeAdded<NodeModelData>[];
|
||||
@ -121,19 +121,20 @@ export const getGroupedChanges = (
|
||||
} else if (changeType === 'TreeStructureChanged') {
|
||||
groupedChanges[changeType].push(change);
|
||||
return;
|
||||
} else {
|
||||
const { id: oid } = change.value;
|
||||
if (!graphCore.hasNode(oid) && !graphCore.hasEdge(oid)) {
|
||||
const nid = Number(oid);
|
||||
if ((!isNaN(nid) && graphCore.hasNode(nid)) || graphCore.hasEdge(nid)) {
|
||||
groupedChanges[changeType].push({
|
||||
...change,
|
||||
value: { ...change.value, id: nid },
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// const { id: oid } = change.value;
|
||||
// if (!graphCore.hasNode(oid) && !graphCore.hasEdge(oid)) {
|
||||
// const nid = Number(oid);
|
||||
// if ((!isNaN(nid) && graphCore.hasNode(nid)) || graphCore.hasEdge(nid)) {
|
||||
// groupedChanges[changeType].push({
|
||||
// ...change,
|
||||
// value: { ...change.value, id: nid },
|
||||
// });
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
groupedChanges[changeType].push(change);
|
||||
});
|
||||
return groupedChanges;
|
||||
|
@ -29,6 +29,7 @@ import cubic_edge from './item/edge/cubic-edge';
|
||||
import cubic_horizon_edge from './item/edge/cubic-horizon-edge';
|
||||
import cubic_vertical_edge from './item/edge/cubic-vertical-edge';
|
||||
import fisheye from './plugins/fisheye';
|
||||
import history from './plugins/history';
|
||||
import tooltip from './demo/tooltip';
|
||||
import comboBasic from './combo/combo-basic';
|
||||
import animations_node_build_in from './animations/node-build-in';
|
||||
@ -65,6 +66,7 @@ export {
|
||||
cubic_horizon_edge,
|
||||
cubic_vertical_edge,
|
||||
fisheye,
|
||||
history,
|
||||
tooltip,
|
||||
comboBasic,
|
||||
animations_node_build_in,
|
||||
|
191
packages/g6/tests/demo/plugins/history.ts
Normal file
191
packages/g6/tests/demo/plugins/history.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { height, width } from '../../datasets/const';
|
||||
|
||||
const createOperationContainer = (container: HTMLElement) => {
|
||||
const operationContainer = document.createElement('div');
|
||||
operationContainer.id = 'operation-bar';
|
||||
operationContainer.style.width = '100%';
|
||||
operationContainer.style.height = '50px';
|
||||
operationContainer.style.backgroundColor = '#eee';
|
||||
|
||||
container.appendChild(operationContainer);
|
||||
};
|
||||
|
||||
const createOperations = (graph): any => {
|
||||
const parentEle = document.getElementById('operation-bar');
|
||||
if (!parentEle) return;
|
||||
|
||||
// undo/redo
|
||||
const undoButton = document.createElement('button');
|
||||
undoButton.innerText = 'undo';
|
||||
undoButton.addEventListener('click', () => {
|
||||
graph.undo();
|
||||
});
|
||||
const redoButton = document.createElement('button');
|
||||
redoButton.innerText = 'redo';
|
||||
redoButton.addEventListener('click', () => {
|
||||
graph.redo();
|
||||
});
|
||||
|
||||
undoButton.disabled = graph.canUndo();
|
||||
redoButton.disabled = graph.canRedo();
|
||||
|
||||
parentEle.appendChild(undoButton);
|
||||
parentEle.appendChild(redoButton);
|
||||
|
||||
// clear item's state
|
||||
const clearStateButton = document.createElement('button');
|
||||
clearStateButton.innerText = 'clear nodes selected state';
|
||||
clearStateButton.addEventListener('click', () => {
|
||||
graph.clearItemState([1, 2], 'selected');
|
||||
});
|
||||
parentEle.appendChild(clearStateButton);
|
||||
|
||||
// add a new node on the map
|
||||
const addNodeButton = document.createElement('button');
|
||||
addNodeButton.innerText = 'add a node';
|
||||
addNodeButton.addEventListener('click', () => {
|
||||
graph.addData('node', {
|
||||
id: 'node3',
|
||||
data: {
|
||||
x: 300,
|
||||
y: 100,
|
||||
},
|
||||
});
|
||||
});
|
||||
parentEle.appendChild(addNodeButton);
|
||||
|
||||
// show/hide node
|
||||
const visibilityButton = document.createElement('button');
|
||||
visibilityButton.innerText = 'show/hide node';
|
||||
visibilityButton.addEventListener('click', () => {
|
||||
const visible = graph.getItemVisible(4);
|
||||
if (visible) {
|
||||
graph.hideItem(4);
|
||||
} else {
|
||||
graph.showItem(4);
|
||||
}
|
||||
});
|
||||
parentEle.appendChild(visibilityButton);
|
||||
|
||||
return { undoButton, redoButton };
|
||||
};
|
||||
|
||||
export default (context) => {
|
||||
const { container } = context;
|
||||
|
||||
// 1.create operation container
|
||||
createOperationContainer(container!);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 1,
|
||||
data: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
type: 'circle-node',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
data: {
|
||||
x: 200,
|
||||
y: 100,
|
||||
type: 'circle-node',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
data: {
|
||||
x: 200,
|
||||
y: 200,
|
||||
type: 'circle-node',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 1,
|
||||
target: 2,
|
||||
data: {
|
||||
type: 'quadratic-edge',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const edge: (data: any) => any = (edgeInnerModel: any) => {
|
||||
const { id, data } = edgeInnerModel;
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
keyShape: {
|
||||
controlPoints: [150, 100],
|
||||
// curvePosition: 0.5,
|
||||
curveOffset: [0, 20],
|
||||
stroke: 'blue',
|
||||
},
|
||||
// iconShape: {
|
||||
// // img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
// text: 'label',
|
||||
// fill: 'blue'
|
||||
// },
|
||||
labelShape: {
|
||||
text: 'label',
|
||||
position: 'middle',
|
||||
fill: 'blue',
|
||||
},
|
||||
labelBackgroundShape: {
|
||||
fill: 'white',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
type: 'graph',
|
||||
modes: {
|
||||
default: ['click-select', 'drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
node: (nodeInnerModel: any) => {
|
||||
const { id, data } = nodeInnerModel;
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
keyShape: {
|
||||
r: 16,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
edge,
|
||||
});
|
||||
|
||||
const { undoButton, redoButton } = createOperations(graph);
|
||||
|
||||
graph.on('afteritemchange', () => {
|
||||
undoButton.disabled = !graph.canUndo();
|
||||
redoButton.disabled = !graph.canRedo();
|
||||
});
|
||||
|
||||
graph.on('afteritemstatechange', () => {
|
||||
undoButton.disabled = !graph.canUndo();
|
||||
redoButton.disabled = !graph.canRedo();
|
||||
});
|
||||
|
||||
graph.on('afteritemvisibilitychange', () => {
|
||||
debugger;
|
||||
undoButton.disabled = !graph.canUndo();
|
||||
redoButton.disabled = !graph.canRedo();
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
@ -241,7 +241,7 @@ const showRoute = (nodeData) => {
|
||||
rangeData.nodes.forEach(node => {
|
||||
const showIdx = showRangeIds.indexOf(+node.id);
|
||||
if (showIdx > -1) {
|
||||
graph.showItem(node.id);
|
||||
graph.showItem(node.id, false, false);
|
||||
rangeLayoutNodes.push(node);
|
||||
}
|
||||
else graph.hideItem(node.id);
|
||||
@ -299,4 +299,4 @@ if (typeof window !== 'undefined')
|
||||
if (!graph || graph.get('destroyed')) return;
|
||||
if (!container || !container.scrollWidth || !container.scrollHeight) return;
|
||||
graph.changeSize(container.scrollWidth, container.scrollHeight);
|
||||
};
|
||||
};
|
||||
|
@ -982,10 +982,10 @@ const hideItems = (graph) => {
|
||||
|
||||
const showItems = (graph) => {
|
||||
graph.getNodes().forEach((node) => {
|
||||
if (!node.isVisible()) graph.showItem(node);
|
||||
if (!node.isVisible()) graph.showItem(node, false, false);
|
||||
});
|
||||
graph.getEdges().forEach((edge) => {
|
||||
if (!edge.isVisible()) edge.showItem(edge);
|
||||
if (!edge.isVisible()) edge.showItem(edge,false, false);
|
||||
});
|
||||
hiddenItemIds = [];
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user