@flowgram-vue/free-history-plugin 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/dist/index.cjs +681 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +194 -0
- package/dist/index.js +672 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/src/changes/add-line-change.ts +36 -0
- package/src/changes/add-node-change.ts +35 -0
- package/src/changes/change-line-data.ts +33 -0
- package/src/changes/delete-line-change.ts +36 -0
- package/src/changes/delete-node-change.ts +33 -0
- package/src/changes/index.ts +12 -0
- package/src/composables/index.ts +6 -0
- package/src/composables/use-undo-redo.ts +38 -0
- package/src/create-free-history-plugin.ts +38 -0
- package/src/env.d.ts +10 -0
- package/src/free-history-config.ts +73 -0
- package/src/free-history-manager.ts +127 -0
- package/src/free-history-registers.ts +18 -0
- package/src/handlers/change-content-handler.ts +48 -0
- package/src/handlers/drag-nodes-handler.ts +44 -0
- package/src/history-entity-manager.ts +44 -0
- package/src/hooks/index.ts +6 -0
- package/src/index.ts +11 -0
- package/src/operation-metas/add-line.ts +44 -0
- package/src/operation-metas/add-node.ts +46 -0
- package/src/operation-metas/base.ts +23 -0
- package/src/operation-metas/change-line-data.ts +58 -0
- package/src/operation-metas/delete-line.ts +41 -0
- package/src/operation-metas/delete-node.ts +44 -0
- package/src/operation-metas/drag-nodes.ts +43 -0
- package/src/operation-metas/index.ts +24 -0
- package/src/operation-metas/move-child-nodes.ts +42 -0
- package/src/operation-metas/reset-layout.ts +33 -0
- package/src/types.ts +173 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
7
|
+
import { injectable, inject } from 'inversify';
|
|
8
|
+
import { DisposableCollection } from '@flowgram-vue/utils';
|
|
9
|
+
import { HistoryService } from '@flowgram-vue/history';
|
|
10
|
+
import {
|
|
11
|
+
WorkflowDocument,
|
|
12
|
+
WorkflowResetLayoutService,
|
|
13
|
+
WorkflowDragService,
|
|
14
|
+
WorkflowOperationBaseService,
|
|
15
|
+
} from '@flowgram-vue/free-layout-core';
|
|
16
|
+
import { OperationType } from '@flowgram-vue/document';
|
|
17
|
+
import { type PluginContext, PositionData } from '@flowgram-vue/core';
|
|
18
|
+
|
|
19
|
+
import { DragNodeOperationValue, type FreeHistoryPluginOptions, FreeOperationType } from './types';
|
|
20
|
+
import { HistoryEntityManager } from './history-entity-manager';
|
|
21
|
+
import { DragNodesHandler } from './handlers/drag-nodes-handler';
|
|
22
|
+
import { ChangeContentHandler } from './handlers/change-content-handler';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 历史管理
|
|
26
|
+
*/
|
|
27
|
+
@injectable()
|
|
28
|
+
export class FreeHistoryManager {
|
|
29
|
+
@inject(DragNodesHandler)
|
|
30
|
+
private _dragNodesHandler: DragNodesHandler;
|
|
31
|
+
|
|
32
|
+
@inject(ChangeContentHandler)
|
|
33
|
+
private _changeContentHandler: ChangeContentHandler;
|
|
34
|
+
|
|
35
|
+
@inject(HistoryEntityManager)
|
|
36
|
+
private _entityManager: HistoryEntityManager;
|
|
37
|
+
|
|
38
|
+
private _toDispose: DisposableCollection = new DisposableCollection();
|
|
39
|
+
|
|
40
|
+
@inject(WorkflowOperationBaseService)
|
|
41
|
+
private _operationService: WorkflowOperationBaseService;
|
|
42
|
+
|
|
43
|
+
onInit(ctx: PluginContext, opts: FreeHistoryPluginOptions) {
|
|
44
|
+
const document = ctx.get<WorkflowDocument>(WorkflowDocument);
|
|
45
|
+
const historyService = ctx.get<HistoryService>(HistoryService);
|
|
46
|
+
const dragService = ctx.get<WorkflowDragService>(WorkflowDragService);
|
|
47
|
+
|
|
48
|
+
const resetLayoutService = ctx.get<WorkflowResetLayoutService>(WorkflowResetLayoutService);
|
|
49
|
+
|
|
50
|
+
if (opts?.limit) {
|
|
51
|
+
historyService.limit(opts.limit);
|
|
52
|
+
}
|
|
53
|
+
historyService.context.source = ctx;
|
|
54
|
+
|
|
55
|
+
this._toDispose.pushAll([
|
|
56
|
+
dragService.onNodesDrag(async (event) => {
|
|
57
|
+
if (event.type !== 'onDragEnd') {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this._dragNodesHandler.handle(event);
|
|
61
|
+
}),
|
|
62
|
+
document.onNodeCreate(({ node, data }) => {
|
|
63
|
+
const positionData = node.getData(PositionData);
|
|
64
|
+
if (positionData) {
|
|
65
|
+
this._entityManager.addEntityData(positionData);
|
|
66
|
+
}
|
|
67
|
+
}),
|
|
68
|
+
document.onContentChange((event) => {
|
|
69
|
+
this._changeContentHandler.handle(event, ctx);
|
|
70
|
+
}),
|
|
71
|
+
document.onReload((_event) => {
|
|
72
|
+
historyService.clear();
|
|
73
|
+
}),
|
|
74
|
+
resetLayoutService.onResetLayout((event) => {
|
|
75
|
+
historyService.pushOperation(
|
|
76
|
+
{
|
|
77
|
+
type: FreeOperationType.resetLayout,
|
|
78
|
+
value: {
|
|
79
|
+
ids: event.nodeIds,
|
|
80
|
+
value: event.positionMap,
|
|
81
|
+
oldValue: event.oldPositionMap,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{ noApply: true }
|
|
85
|
+
);
|
|
86
|
+
}),
|
|
87
|
+
this._operationService.onNodeMove(({ node, fromParent, fromIndex, toParent, toIndex }) => {
|
|
88
|
+
historyService.pushOperation(
|
|
89
|
+
{
|
|
90
|
+
type: OperationType.moveChildNodes,
|
|
91
|
+
value: {
|
|
92
|
+
fromParentId: fromParent.id,
|
|
93
|
+
fromIndex,
|
|
94
|
+
nodeIds: [node.id],
|
|
95
|
+
toParentId: toParent.id,
|
|
96
|
+
toIndex,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
noApply: true,
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}),
|
|
104
|
+
this._operationService.onNodePostionUpdate((event) => {
|
|
105
|
+
const value: DragNodeOperationValue = {
|
|
106
|
+
ids: [event.node.id],
|
|
107
|
+
value: [event.newPosition],
|
|
108
|
+
oldValue: [event.oldPosition],
|
|
109
|
+
};
|
|
110
|
+
historyService.pushOperation(
|
|
111
|
+
{
|
|
112
|
+
type: FreeOperationType.dragNodes,
|
|
113
|
+
value,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
noApply: true,
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}),
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
dispose() {
|
|
124
|
+
this._entityManager.dispose();
|
|
125
|
+
this._toDispose.dispose();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { injectable } from 'inversify';
|
|
7
|
+
import { type OperationContribution, type OperationRegistry } from '@flowgram-vue/history';
|
|
8
|
+
|
|
9
|
+
import { operationMetas } from './operation-metas';
|
|
10
|
+
|
|
11
|
+
@injectable()
|
|
12
|
+
export class FreeHistoryRegisters implements OperationContribution {
|
|
13
|
+
registerOperationMeta(operationRegistry: OperationRegistry): void {
|
|
14
|
+
operationMetas.forEach(operationMeta => {
|
|
15
|
+
operationRegistry.registerOperationMeta(operationMeta);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
7
|
+
import { injectable, inject } from 'inversify';
|
|
8
|
+
import { HistoryService, Operation } from '@flowgram-vue/history';
|
|
9
|
+
import {
|
|
10
|
+
type WorkflowContentChangeEvent,
|
|
11
|
+
WorkflowContentChangeType,
|
|
12
|
+
} from '@flowgram-vue/free-layout-core';
|
|
13
|
+
import { type PluginContext } from '@flowgram-vue/core';
|
|
14
|
+
|
|
15
|
+
import { type IHandler } from '../types';
|
|
16
|
+
import { FreeHistoryConfig } from '../free-history-config';
|
|
17
|
+
import changes from '../changes';
|
|
18
|
+
|
|
19
|
+
@injectable()
|
|
20
|
+
export class ChangeContentHandler implements IHandler<WorkflowContentChangeEvent> {
|
|
21
|
+
@inject(HistoryService)
|
|
22
|
+
private _historyService: HistoryService;
|
|
23
|
+
|
|
24
|
+
@inject(FreeHistoryConfig) private _historyConfig: FreeHistoryConfig;
|
|
25
|
+
|
|
26
|
+
handle(event: WorkflowContentChangeEvent, ctx: PluginContext) {
|
|
27
|
+
if (!this._historyService.undoRedoService.canPush()) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
!this._historyConfig.enableChangeLineData &&
|
|
32
|
+
event.type === WorkflowContentChangeType.LINE_DATA_CHANGE
|
|
33
|
+
) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const change = changes.find((c) => c.type === event.type);
|
|
38
|
+
if (!change) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const operation: Operation | undefined = change.toOperation(event, ctx);
|
|
42
|
+
if (!operation) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this._historyService.pushOperation(operation, { noApply: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
7
|
+
import { injectable, inject } from 'inversify';
|
|
8
|
+
import { HistoryService } from '@flowgram-vue/history';
|
|
9
|
+
import { type NodesDragEndEvent } from '@flowgram-vue/free-layout-core';
|
|
10
|
+
import { TransformData } from '@flowgram-vue/core';
|
|
11
|
+
|
|
12
|
+
import { FreeOperationType, type IHandler } from '../types';
|
|
13
|
+
|
|
14
|
+
@injectable()
|
|
15
|
+
export class DragNodesHandler implements IHandler<NodesDragEndEvent> {
|
|
16
|
+
@inject(HistoryService)
|
|
17
|
+
private _historyService: HistoryService;
|
|
18
|
+
|
|
19
|
+
handle(event: NodesDragEndEvent) {
|
|
20
|
+
if (event.type === 'onDragEnd') {
|
|
21
|
+
this._dragNode(event);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private _dragNode(event: NodesDragEndEvent) {
|
|
26
|
+
this._historyService.pushOperation(
|
|
27
|
+
{
|
|
28
|
+
type: FreeOperationType.dragNodes,
|
|
29
|
+
value: {
|
|
30
|
+
ids: event.nodes.map((node) => node.id),
|
|
31
|
+
value: event.nodes.map((node) => {
|
|
32
|
+
const { x, y } = node.getData(TransformData).position;
|
|
33
|
+
return {
|
|
34
|
+
x,
|
|
35
|
+
y,
|
|
36
|
+
};
|
|
37
|
+
}),
|
|
38
|
+
oldValue: event.startPositions,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{ noApply: true }
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
7
|
+
import { cloneDeep, isEqual } from 'lodash-es';
|
|
8
|
+
import { injectable } from 'inversify';
|
|
9
|
+
import { type Disposable, DisposableCollection } from '@flowgram-vue/utils';
|
|
10
|
+
import { type EntityData } from '@flowgram-vue/core';
|
|
11
|
+
|
|
12
|
+
@injectable()
|
|
13
|
+
export class HistoryEntityManager implements Disposable {
|
|
14
|
+
private _entityDataValues: Map<EntityData, unknown> = new Map();
|
|
15
|
+
|
|
16
|
+
private _toDispose: DisposableCollection = new DisposableCollection();
|
|
17
|
+
|
|
18
|
+
addEntityData(entityData: EntityData) {
|
|
19
|
+
this._entityDataValues.set(entityData, cloneDeep(entityData.toJSON()));
|
|
20
|
+
this._toDispose.push(
|
|
21
|
+
entityData.onWillChange((event) => {
|
|
22
|
+
const value = event.toJSON();
|
|
23
|
+
const oldValue = this._entityDataValues.get(entityData);
|
|
24
|
+
if (isEqual(value, oldValue)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this._entityDataValues.set(entityData, cloneDeep(value));
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getValue(entityData: EntityData) {
|
|
33
|
+
return this._entityDataValues.get(entityData);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setValue(entityData: EntityData, value: unknown) {
|
|
37
|
+
return this._entityDataValues.set(entityData, value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
dispose() {
|
|
41
|
+
this._entityDataValues.clear();
|
|
42
|
+
this._toDispose.dispose();
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from './create-free-history-plugin';
|
|
7
|
+
export * from './types';
|
|
8
|
+
export * from '@flowgram-vue/history';
|
|
9
|
+
export * from './free-history-config';
|
|
10
|
+
export * from './hooks';
|
|
11
|
+
export * from './free-history-registers';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type PluginContext } from '@flowgram-vue/core';
|
|
7
|
+
import { WorkflowLinesManager } from '@flowgram-vue/free-layout-core';
|
|
8
|
+
import { type OperationMeta } from '@flowgram-vue/history';
|
|
9
|
+
|
|
10
|
+
import { type AddOrDeleteLineOperationValue, FreeOperationType } from '../types';
|
|
11
|
+
import { FreeHistoryConfig } from '../free-history-config';
|
|
12
|
+
import { baseOperationMeta } from './base';
|
|
13
|
+
|
|
14
|
+
export const addLineOperationMeta: OperationMeta<
|
|
15
|
+
AddOrDeleteLineOperationValue,
|
|
16
|
+
PluginContext,
|
|
17
|
+
void
|
|
18
|
+
> = {
|
|
19
|
+
...baseOperationMeta,
|
|
20
|
+
type: FreeOperationType.addLine,
|
|
21
|
+
inverse: op => ({
|
|
22
|
+
...op,
|
|
23
|
+
type: FreeOperationType.deleteLine,
|
|
24
|
+
}),
|
|
25
|
+
apply: (operation, ctx: PluginContext) => {
|
|
26
|
+
const linesManager = ctx.get<WorkflowLinesManager>(WorkflowLinesManager);
|
|
27
|
+
linesManager.createLine({
|
|
28
|
+
...operation.value,
|
|
29
|
+
key: operation.value.id,
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
getLabel: (op, ctx) => 'Create Line',
|
|
33
|
+
getDescription: (op, ctx) => {
|
|
34
|
+
const config = ctx.get<FreeHistoryConfig>(FreeHistoryConfig);
|
|
35
|
+
const { value } = op;
|
|
36
|
+
if (!value.from || !value.to) {
|
|
37
|
+
return 'Create Line';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fromName = config.getNodeLabelById(value.from);
|
|
41
|
+
const toName = config.getNodeLabelById(value.to);
|
|
42
|
+
return `Create Line from ${fromName} to ${toName}`;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { cloneDeep } from 'lodash-es';
|
|
7
|
+
import { type OperationMeta } from '@flowgram-vue/history';
|
|
8
|
+
import { WorkflowDocument, type WorkflowNodeJSON } from '@flowgram-vue/free-layout-core';
|
|
9
|
+
import { type PluginContext } from '@flowgram-vue/core';
|
|
10
|
+
|
|
11
|
+
import { type AddOrDeleteWorkflowNodeOperationValue, FreeOperationType } from '../types';
|
|
12
|
+
import { FreeHistoryConfig } from '../free-history-config';
|
|
13
|
+
import { baseOperationMeta } from './base';
|
|
14
|
+
|
|
15
|
+
export const addNodeOperationMeta: OperationMeta<
|
|
16
|
+
AddOrDeleteWorkflowNodeOperationValue,
|
|
17
|
+
PluginContext,
|
|
18
|
+
void
|
|
19
|
+
> = {
|
|
20
|
+
...baseOperationMeta,
|
|
21
|
+
type: FreeOperationType.addNode,
|
|
22
|
+
inverse: (op) => ({
|
|
23
|
+
...op,
|
|
24
|
+
type: FreeOperationType.deleteNode,
|
|
25
|
+
}),
|
|
26
|
+
apply: async (operation, ctx: PluginContext) => {
|
|
27
|
+
const document = ctx.get<WorkflowDocument>(WorkflowDocument);
|
|
28
|
+
await document.createWorkflowNode(
|
|
29
|
+
cloneDeep(operation.value.node) as WorkflowNodeJSON,
|
|
30
|
+
false,
|
|
31
|
+
operation.value.parentID
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
getLabel: (op, ctx) => {
|
|
35
|
+
const config = ctx.get<FreeHistoryConfig>(FreeHistoryConfig);
|
|
36
|
+
return `Create Node ${config.getNodeLabel(op.value.node)}`;
|
|
37
|
+
},
|
|
38
|
+
getDescription: (op, ctx) => {
|
|
39
|
+
const config = ctx.get<FreeHistoryConfig>(FreeHistoryConfig);
|
|
40
|
+
let desc = `Create Node ${config.getNodeLabel(op.value.node)}`;
|
|
41
|
+
if (op.value.node.meta?.position) {
|
|
42
|
+
desc += ` at ${op.value.node.meta.position.x},${op.value.node.meta.position.y}`;
|
|
43
|
+
}
|
|
44
|
+
return desc;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type OperationMeta } from '@flowgram-vue/history';
|
|
7
|
+
|
|
8
|
+
export const baseOperationMeta: Partial<OperationMeta> = {
|
|
9
|
+
shouldMerge: (_op, prev, element) => {
|
|
10
|
+
if (!prev) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
// 合并500ms内的操作, 如删除节点会联动删除线条
|
|
16
|
+
Date.now() - element.getTimestamp() <
|
|
17
|
+
500
|
|
18
|
+
) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type OperationMeta } from '@flowgram-vue/history';
|
|
7
|
+
import { WorkflowLinesManager } from '@flowgram-vue/free-layout-core';
|
|
8
|
+
import { type PluginContext } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
import { FreeOperationType, type ChangeLineDataValue } from '../types';
|
|
11
|
+
import { baseOperationMeta } from './base';
|
|
12
|
+
|
|
13
|
+
export const changeLineDataOperationMeta: OperationMeta<ChangeLineDataValue, PluginContext, void> =
|
|
14
|
+
{
|
|
15
|
+
...baseOperationMeta,
|
|
16
|
+
type: FreeOperationType.changeLineData,
|
|
17
|
+
inverse: (op) => ({
|
|
18
|
+
...op,
|
|
19
|
+
value: {
|
|
20
|
+
...op.value,
|
|
21
|
+
newValue: op.value.oldValue,
|
|
22
|
+
oldValue: op.value.newValue,
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
apply: (op, ctx: PluginContext) => {
|
|
26
|
+
const linesManager = ctx.get<WorkflowLinesManager>(WorkflowLinesManager);
|
|
27
|
+
const line = linesManager.getLineById(op.value.id);
|
|
28
|
+
|
|
29
|
+
if (!line) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
line.lineData = op.value.newValue;
|
|
34
|
+
},
|
|
35
|
+
shouldMerge: (op, prev, element) => {
|
|
36
|
+
if (!prev) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (Date.now() - element.getTimestamp() < 500) {
|
|
41
|
+
if (
|
|
42
|
+
op.type === prev.type && // 相同类型
|
|
43
|
+
op.value.id === prev.value.id // 相同节点
|
|
44
|
+
) {
|
|
45
|
+
return {
|
|
46
|
+
type: op.type,
|
|
47
|
+
value: {
|
|
48
|
+
...op.value,
|
|
49
|
+
newValue: op.value.newValue,
|
|
50
|
+
oldValue: prev.value.oldValue,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type OperationMeta } from '@flowgram-vue/history';
|
|
7
|
+
import { WorkflowDocument } from '@flowgram-vue/free-layout-core';
|
|
8
|
+
import { type PluginContext } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
import { type AddOrDeleteLineOperationValue, FreeOperationType } from '../types';
|
|
11
|
+
import { FreeHistoryConfig } from '../free-history-config';
|
|
12
|
+
import { baseOperationMeta } from './base';
|
|
13
|
+
|
|
14
|
+
export const deleteLineOperationMeta: OperationMeta<
|
|
15
|
+
AddOrDeleteLineOperationValue,
|
|
16
|
+
PluginContext,
|
|
17
|
+
void
|
|
18
|
+
> = {
|
|
19
|
+
...baseOperationMeta,
|
|
20
|
+
type: FreeOperationType.deleteLine,
|
|
21
|
+
inverse: (op) => ({
|
|
22
|
+
...op,
|
|
23
|
+
type: FreeOperationType.addLine,
|
|
24
|
+
}),
|
|
25
|
+
apply: (operation, ctx: PluginContext) => {
|
|
26
|
+
const document = ctx.get<WorkflowDocument>(WorkflowDocument);
|
|
27
|
+
document.removeNode(operation.value.id);
|
|
28
|
+
},
|
|
29
|
+
getLabel: (op, ctx) => 'Delete Line',
|
|
30
|
+
getDescription: (op, ctx) => {
|
|
31
|
+
const config = ctx.get<FreeHistoryConfig>(FreeHistoryConfig);
|
|
32
|
+
const { value } = op;
|
|
33
|
+
if (!value.from || !value.to) {
|
|
34
|
+
return 'Delete Line';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fromName = config.getNodeLabelById(value.from);
|
|
38
|
+
const toName = config.getNodeLabelById(value.to);
|
|
39
|
+
return `Delete Line from ${fromName} to ${toName}`;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type PluginContext } from '@flowgram-vue/core';
|
|
7
|
+
import { WorkflowDocument } from '@flowgram-vue/free-layout-core';
|
|
8
|
+
import { type OperationMeta } from '@flowgram-vue/history';
|
|
9
|
+
|
|
10
|
+
import { type AddOrDeleteWorkflowNodeOperationValue, FreeOperationType } from '../types';
|
|
11
|
+
import { FreeHistoryConfig } from '../free-history-config';
|
|
12
|
+
import { baseOperationMeta } from './base';
|
|
13
|
+
|
|
14
|
+
export const deleteNodeOperationMeta: OperationMeta<
|
|
15
|
+
AddOrDeleteWorkflowNodeOperationValue,
|
|
16
|
+
PluginContext,
|
|
17
|
+
void
|
|
18
|
+
> = {
|
|
19
|
+
...baseOperationMeta,
|
|
20
|
+
type: FreeOperationType.deleteNode,
|
|
21
|
+
inverse: op => ({
|
|
22
|
+
...op,
|
|
23
|
+
type: FreeOperationType.addNode,
|
|
24
|
+
}),
|
|
25
|
+
apply: (operation, ctx: PluginContext) => {
|
|
26
|
+
const document = ctx.get<WorkflowDocument>(WorkflowDocument);
|
|
27
|
+
const node = document.getNode(operation.value.node.id);
|
|
28
|
+
if (node) {
|
|
29
|
+
node.dispose();
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
getLabel: (op, ctx) => {
|
|
33
|
+
const config = ctx.get<FreeHistoryConfig>(FreeHistoryConfig);
|
|
34
|
+
return `Delete Node ${config.getNodeLabel(op.value.node)}`;
|
|
35
|
+
},
|
|
36
|
+
getDescription: (op, ctx) => {
|
|
37
|
+
const config = ctx.get<FreeHistoryConfig>(FreeHistoryConfig);
|
|
38
|
+
let desc = `Delete Node ${config.getNodeLabel(op.value.node)}`;
|
|
39
|
+
if (op.value.node.meta?.position) {
|
|
40
|
+
desc += ` at ${op.value.node.meta.position.x},${op.value.node.meta.position.y}`;
|
|
41
|
+
}
|
|
42
|
+
return desc;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type OperationMeta } from '@flowgram-vue/history';
|
|
7
|
+
import { WorkflowDocument } from '@flowgram-vue/free-layout-core';
|
|
8
|
+
import { type PluginContext, TransformData } from '@flowgram-vue/core';
|
|
9
|
+
|
|
10
|
+
import { type DragNodeOperationValue, FreeOperationType } from '../types';
|
|
11
|
+
import { baseOperationMeta } from './base';
|
|
12
|
+
|
|
13
|
+
export const dragNodesOperationMeta: OperationMeta<DragNodeOperationValue, PluginContext, void> = {
|
|
14
|
+
...baseOperationMeta,
|
|
15
|
+
type: FreeOperationType.dragNodes,
|
|
16
|
+
inverse: (op) => ({
|
|
17
|
+
...op,
|
|
18
|
+
value: {
|
|
19
|
+
...op.value,
|
|
20
|
+
value: op.value.oldValue,
|
|
21
|
+
oldValue: op.value.value,
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
apply: (operation, ctx) => {
|
|
25
|
+
operation.value.ids.forEach((id, index) => {
|
|
26
|
+
const document = ctx.get<WorkflowDocument>(WorkflowDocument);
|
|
27
|
+
const node = document.getNode(id);
|
|
28
|
+
if (!node) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const transform = node.getData(TransformData);
|
|
33
|
+
const point = operation.value.value[index];
|
|
34
|
+
transform.update({
|
|
35
|
+
position: {
|
|
36
|
+
x: point.x,
|
|
37
|
+
y: point.y,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
document.layout.updateAffectedTransform(node);
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resetLayoutOperationMeta } from './reset-layout';
|
|
7
|
+
import { moveChildNodesOperationMeta } from './move-child-nodes';
|
|
8
|
+
import { dragNodesOperationMeta } from './drag-nodes';
|
|
9
|
+
import { deleteNodeOperationMeta } from './delete-node';
|
|
10
|
+
import { deleteLineOperationMeta } from './delete-line';
|
|
11
|
+
import { changeLineDataOperationMeta } from './change-line-data';
|
|
12
|
+
import { addNodeOperationMeta } from './add-node';
|
|
13
|
+
import { addLineOperationMeta } from './add-line';
|
|
14
|
+
|
|
15
|
+
export const operationMetas = [
|
|
16
|
+
addLineOperationMeta,
|
|
17
|
+
deleteLineOperationMeta,
|
|
18
|
+
addNodeOperationMeta,
|
|
19
|
+
deleteNodeOperationMeta,
|
|
20
|
+
resetLayoutOperationMeta,
|
|
21
|
+
dragNodesOperationMeta,
|
|
22
|
+
moveChildNodesOperationMeta,
|
|
23
|
+
changeLineDataOperationMeta,
|
|
24
|
+
];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { OperationMeta } from '@flowgram-vue/history';
|
|
7
|
+
import { WorkflowDocument } from '@flowgram-vue/free-layout-core';
|
|
8
|
+
import { MoveChildNodesOperationValue, OperationType } from '@flowgram-vue/document';
|
|
9
|
+
import { FlowNodeBaseType } from '@flowgram-vue/document';
|
|
10
|
+
import { PluginContext, TransformData } from '@flowgram-vue/core';
|
|
11
|
+
|
|
12
|
+
import { baseOperationMeta } from './base';
|
|
13
|
+
|
|
14
|
+
export const moveChildNodesOperationMeta: OperationMeta<
|
|
15
|
+
MoveChildNodesOperationValue,
|
|
16
|
+
PluginContext,
|
|
17
|
+
void
|
|
18
|
+
> = {
|
|
19
|
+
...baseOperationMeta,
|
|
20
|
+
type: OperationType.moveChildNodes,
|
|
21
|
+
inverse: (op) => ({
|
|
22
|
+
...op,
|
|
23
|
+
value: {
|
|
24
|
+
...op.value,
|
|
25
|
+
fromIndex: op.value.toIndex,
|
|
26
|
+
toIndex: op.value.fromIndex,
|
|
27
|
+
fromParentId: op.value.toParentId,
|
|
28
|
+
toParentId: op.value.fromParentId,
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
apply: (operation, ctx: PluginContext) => {
|
|
32
|
+
const document = ctx.get<WorkflowDocument>(WorkflowDocument);
|
|
33
|
+
document.moveChildNodes(operation.value);
|
|
34
|
+
const fromContainer = document.getNode(operation.value.fromParentId);
|
|
35
|
+
requestAnimationFrame(() => {
|
|
36
|
+
if (fromContainer && fromContainer.flowNodeType !== FlowNodeBaseType.ROOT) {
|
|
37
|
+
const fromContainerTransformData = fromContainer.getData(TransformData);
|
|
38
|
+
fromContainerTransformData.fireChange();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
};
|