@flowgram-vue/core 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 +0 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +0 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/src/common/config-entity.ts +80 -0
- package/src/common/entity-data.ts +171 -0
- package/src/common/entity-manager-contribution.ts +12 -0
- package/src/common/entity-manager.ts +475 -0
- package/src/common/entity.ts +482 -0
- package/src/common/index.ts +22 -0
- package/src/common/playground-context.ts +35 -0
- package/src/common/playground-decorator-helper.ts +113 -0
- package/src/common/playground-decorators.ts +85 -0
- package/src/common/playground-schedule.ts +30 -0
- package/src/common/protect-wheel-area.ts +11 -0
- package/src/common/schema/index.ts +14 -0
- package/src/common/schema/node.ts +15 -0
- package/src/common/schema/opacity-schema.ts +19 -0
- package/src/common/schema/origin-schema.ts +35 -0
- package/src/common/schema/position-schema.ts +35 -0
- package/src/common/schema/rotation-schema.ts +19 -0
- package/src/common/schema/scale-schema.ts +35 -0
- package/src/common/schema/size-schema.ts +42 -0
- package/src/common/schema/skew-schema.ts +35 -0
- package/src/common/schema/transform-schema.ts +613 -0
- package/src/common/utils/bounds.spec.ts +229 -0
- package/src/common/utils/bounds.ts +176 -0
- package/src/common/utils/index.ts +6 -0
- package/src/composables/index.ts +16 -0
- package/src/composables/use-config-entity.ts +31 -0
- package/src/composables/use-entities.ts +26 -0
- package/src/composables/use-entity-data-from-context.ts +34 -0
- package/src/composables/use-entity-from-context.ts +31 -0
- package/src/composables/use-listen-events.ts +22 -0
- package/src/composables/use-playground-container.ts +16 -0
- package/src/composables/use-playground-context.ts +15 -0
- package/src/composables/use-playground-drag.ts +31 -0
- package/src/composables/use-playground.ts +16 -0
- package/src/composables/use-refresh.ts +6 -0
- package/src/composables/use-service.ts +17 -0
- package/src/core/index.ts +8 -0
- package/src/core/layer/config/editor-state-config-entity.ts +180 -0
- package/src/core/layer/config/index.ts +7 -0
- package/src/core/layer/config/playground-config-entity.ts +604 -0
- package/src/core/layer/index.ts +8 -0
- package/src/core/layer/layer.ts +248 -0
- package/src/core/layer/playground-layer.ts +547 -0
- package/src/core/pipeline/index.ts +10 -0
- package/src/core/pipeline/pipeline-entities-selector.ts +200 -0
- package/src/core/pipeline/pipeline-entities.ts +230 -0
- package/src/core/pipeline/pipeline-registry.ts +344 -0
- package/src/core/pipeline/pipeline-renderer.ts +248 -0
- package/src/core/pipeline/pipeline-vue-utils.ts +96 -0
- package/src/core/pipeline/pipeline.ts +28 -0
- package/src/core/utils/index.ts +12 -0
- package/src/core/utils/inject-provider-decorators.ts +27 -0
- package/src/core/utils/lazy-inject-decorators.ts +38 -0
- package/src/core/utils/mouse-touch-event.ts +120 -0
- package/src/core/utils/playground-drag.ts +511 -0
- package/src/core/utils/playground-gesture.spec.ts +135 -0
- package/src/core/utils/playground-gesture.ts +118 -0
- package/src/core/utils/tween.ts +156 -0
- package/src/core/utils/use-gesture/core/Controller.ts +212 -0
- package/src/core/utils/use-gesture/core/EventStore.ts +46 -0
- package/src/core/utils/use-gesture/core/TimeoutStore.ts +28 -0
- package/src/core/utils/use-gesture/core/actions.ts +63 -0
- package/src/core/utils/use-gesture/core/config/commonConfigResolver.ts +86 -0
- package/src/core/utils/use-gesture/core/config/coordinatesConfigResolver.ts +48 -0
- package/src/core/utils/use-gesture/core/config/dragConfigResolver.ts +140 -0
- package/src/core/utils/use-gesture/core/config/hoverConfigResolver.ts +11 -0
- package/src/core/utils/use-gesture/core/config/moveConfigResolver.ts +11 -0
- package/src/core/utils/use-gesture/core/config/pinchConfigResolver.ts +59 -0
- package/src/core/utils/use-gesture/core/config/resolver.ts +70 -0
- package/src/core/utils/use-gesture/core/config/scrollConfigResolver.ts +8 -0
- package/src/core/utils/use-gesture/core/config/sharedConfigResolver.ts +28 -0
- package/src/core/utils/use-gesture/core/config/support.ts +52 -0
- package/src/core/utils/use-gesture/core/config/wheelConfigResolver.ts +8 -0
- package/src/core/utils/use-gesture/core/engines/CoordinatesEngine.ts +78 -0
- package/src/core/utils/use-gesture/core/engines/DragEngine.ts +403 -0
- package/src/core/utils/use-gesture/core/engines/Engine.ts +406 -0
- package/src/core/utils/use-gesture/core/engines/HoverEngine.ts +43 -0
- package/src/core/utils/use-gesture/core/engines/MoveEngine.ts +52 -0
- package/src/core/utils/use-gesture/core/engines/PinchEngine.ts +345 -0
- package/src/core/utils/use-gesture/core/engines/ScrollEngine.ts +42 -0
- package/src/core/utils/use-gesture/core/engines/WheelEngine.ts +48 -0
- package/src/core/utils/use-gesture/core/index.ts +7 -0
- package/src/core/utils/use-gesture/core/parser.ts +92 -0
- package/src/core/utils/use-gesture/core/types/action.ts +19 -0
- package/src/core/utils/use-gesture/core/types/config.ts +255 -0
- package/src/core/utils/use-gesture/core/types/handlers.ts +73 -0
- package/src/core/utils/use-gesture/core/types/index.ts +11 -0
- package/src/core/utils/use-gesture/core/types/internalConfig.ts +80 -0
- package/src/core/utils/use-gesture/core/types/state.ts +275 -0
- package/src/core/utils/use-gesture/core/types/utils.ts +204 -0
- package/src/core/utils/use-gesture/core/types.ts +8 -0
- package/src/core/utils/use-gesture/core/utils/events.ts +158 -0
- package/src/core/utils/use-gesture/core/utils/fn.ts +32 -0
- package/src/core/utils/use-gesture/core/utils/maths.ts +63 -0
- package/src/core/utils/use-gesture/core/utils/state.ts +24 -0
- package/src/core/utils/use-gesture/core/utils.ts +8 -0
- package/src/core/utils/use-gesture/index.ts +6 -0
- package/src/core/utils/use-gesture/vanilla/DragGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/Gesture.ts +45 -0
- package/src/core/utils/use-gesture/vanilla/HoverGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/MoveGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/PinchGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/Recognizer.ts +43 -0
- package/src/core/utils/use-gesture/vanilla/ScrollGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/WheelGesture.ts +27 -0
- package/src/core/utils/use-gesture/vanilla/createGesture.ts +18 -0
- package/src/core/utils/use-gesture/vanilla/index.ts +18 -0
- package/src/env.d.ts +10 -0
- package/src/index.ts +17 -0
- package/src/playground-config.ts +70 -0
- package/src/playground-container.ts +129 -0
- package/src/playground-contribution.ts +81 -0
- package/src/playground-mock-tools.ts +143 -0
- package/src/playground.ts +401 -0
- package/src/plugin/index.ts +6 -0
- package/src/plugin/plugin.ts +226 -0
- package/src/services/clipboard-service.ts +40 -0
- package/src/services/context-menu-service.ts +25 -0
- package/src/services/index.ts +10 -0
- package/src/services/logger-service.ts +49 -0
- package/src/services/selection-service.ts +54 -0
- package/src/services/storage-service.ts +65 -0
- package/src/vue/index.ts +8 -0
- package/src/vue/playground-vue-context.ts +22 -0
- package/src/vue/playground-vue-provider.ts +115 -0
- package/src/vue/playground-vue-renderer.vue +44 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { inject, injectable } from 'inversify';
|
|
7
|
+
import {
|
|
8
|
+
ConflatableMessage,
|
|
9
|
+
type IMessageHandler,
|
|
10
|
+
type Message,
|
|
11
|
+
MessageLoop,
|
|
12
|
+
} from '@phosphor/messaging';
|
|
13
|
+
import { Disposable, DisposableCollection, Emitter } from '@flowgram-vue/utils';
|
|
14
|
+
|
|
15
|
+
import { type Layer, type LayerRegistry, PlaygroundConfigEntity } from '../layer';
|
|
16
|
+
import {
|
|
17
|
+
// AbleManager,
|
|
18
|
+
ConfigEntity,
|
|
19
|
+
Entity,
|
|
20
|
+
EntityManager,
|
|
21
|
+
// getAbleMetadata,
|
|
22
|
+
getEntityDatasMetadata,
|
|
23
|
+
getEntityMetadata,
|
|
24
|
+
injectPlaygroundContext,
|
|
25
|
+
PlaygroundContext,
|
|
26
|
+
} from '../../common';
|
|
27
|
+
import { PipelineRenderer } from './pipeline-renderer';
|
|
28
|
+
import { PipelineEntitiesSelector } from './pipeline-entities-selector';
|
|
29
|
+
import { PipelineEntitiesImpl } from './pipeline-entities';
|
|
30
|
+
// import { PipelineDispatcher } from './pipeline-dispatcher';
|
|
31
|
+
import {
|
|
32
|
+
type PipeEventName,
|
|
33
|
+
type PipelineDimension,
|
|
34
|
+
type PipelineEventHandler,
|
|
35
|
+
type PipelineEventRegsiter,
|
|
36
|
+
PipelineLayerFactory,
|
|
37
|
+
type PipeSupportEvent,
|
|
38
|
+
} from './pipeline';
|
|
39
|
+
|
|
40
|
+
export enum PipelineMessage {
|
|
41
|
+
ZOOM = 'PIPELINE_ZOOM',
|
|
42
|
+
SCROLL = 'PIPELINE_SCROLL',
|
|
43
|
+
}
|
|
44
|
+
const zoomMessage = new ConflatableMessage(PipelineMessage.ZOOM);
|
|
45
|
+
const scrollMessage = new ConflatableMessage(PipelineMessage.SCROLL);
|
|
46
|
+
/**
|
|
47
|
+
* pipeline 注册器,用于注册一些事件
|
|
48
|
+
*/
|
|
49
|
+
@injectable()
|
|
50
|
+
export class PipelineRegistry implements Disposable, IMessageHandler {
|
|
51
|
+
private _isFocused = false;
|
|
52
|
+
|
|
53
|
+
protected toDispose = new DisposableCollection();
|
|
54
|
+
|
|
55
|
+
protected allLayersMap = new Map<LayerRegistry, Layer>();
|
|
56
|
+
|
|
57
|
+
readonly onResizeEmitter = new Emitter<PipelineDimension>();
|
|
58
|
+
|
|
59
|
+
readonly onFocusEmitter = new Emitter<void>();
|
|
60
|
+
|
|
61
|
+
readonly onBlurEmitter = new Emitter<void>();
|
|
62
|
+
|
|
63
|
+
readonly onZoomEmitter = new Emitter<number>();
|
|
64
|
+
|
|
65
|
+
readonly onScrollEmitter = new Emitter<{ scrollX: number; scrollY: number }>();
|
|
66
|
+
|
|
67
|
+
readonly onFocus = this.onFocusEmitter.event;
|
|
68
|
+
|
|
69
|
+
readonly onBlur = this.onBlurEmitter.event;
|
|
70
|
+
|
|
71
|
+
readonly onZoom = this.onZoomEmitter.event;
|
|
72
|
+
|
|
73
|
+
readonly onScroll = this.onScrollEmitter.event;
|
|
74
|
+
|
|
75
|
+
constructor() {
|
|
76
|
+
this.toDispose.pushAll([
|
|
77
|
+
this.onResizeEmitter,
|
|
78
|
+
this.onFocusEmitter,
|
|
79
|
+
this.onZoomEmitter,
|
|
80
|
+
this.onBlurEmitter,
|
|
81
|
+
this.onScrollEmitter,
|
|
82
|
+
]);
|
|
83
|
+
this.onFocusEmitter.event(() => {
|
|
84
|
+
this._isFocused = true;
|
|
85
|
+
});
|
|
86
|
+
this.onBlurEmitter.event(() => {
|
|
87
|
+
this._isFocused = false;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// @inject(PipelineDispatcher) dispatcher: PipelineDispatcher;
|
|
92
|
+
|
|
93
|
+
@inject(PipelineRenderer) renderer: PipelineRenderer;
|
|
94
|
+
|
|
95
|
+
@inject(PipelineEntitiesSelector) selector: PipelineEntitiesSelector;
|
|
96
|
+
|
|
97
|
+
@inject(EntityManager) entityManager: EntityManager;
|
|
98
|
+
|
|
99
|
+
// @inject(AbleManager) ableManager: AbleManager;
|
|
100
|
+
|
|
101
|
+
@injectPlaygroundContext() context: PlaygroundContext;
|
|
102
|
+
|
|
103
|
+
@inject(PipelineLayerFactory) layerFactory: PipelineLayerFactory;
|
|
104
|
+
|
|
105
|
+
// @inject(SelectionService) @optional() selectionService?: SelectionService;
|
|
106
|
+
protected playgroundEvents: {
|
|
107
|
+
[key: string]: { handlers: PipelineEventRegsiter[] } & Disposable;
|
|
108
|
+
} = {};
|
|
109
|
+
|
|
110
|
+
protected globalEvents: {
|
|
111
|
+
[key: string]: { handlers: PipelineEventRegsiter[] } & Disposable;
|
|
112
|
+
} = {};
|
|
113
|
+
|
|
114
|
+
_listenEvent(
|
|
115
|
+
name: PipeEventName,
|
|
116
|
+
handle: PipelineEventHandler,
|
|
117
|
+
isGlobal: boolean,
|
|
118
|
+
priority = 0,
|
|
119
|
+
options?: AddEventListenerOptions
|
|
120
|
+
): Disposable {
|
|
121
|
+
const eventsCache = isGlobal ? this.globalEvents : this.playgroundEvents;
|
|
122
|
+
const domNode = isGlobal ? document : this.renderer.node.parentNode!;
|
|
123
|
+
let eventRegister = eventsCache[name];
|
|
124
|
+
if (!eventRegister) {
|
|
125
|
+
const realHandler = {
|
|
126
|
+
handleEvent: (e: PipeSupportEvent) => {
|
|
127
|
+
const list = eventRegister.handlers;
|
|
128
|
+
for (let i = 0, len = list.length; i < len; i++) {
|
|
129
|
+
const prevent = list[i].handle(e);
|
|
130
|
+
/* v8 ignore next 1 */
|
|
131
|
+
if (prevent) return;
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
domNode.addEventListener(name, realHandler, options);
|
|
136
|
+
eventRegister = eventsCache[name] = {
|
|
137
|
+
handlers: [],
|
|
138
|
+
dispose: () => {
|
|
139
|
+
domNode.removeEventListener(name, realHandler);
|
|
140
|
+
delete eventsCache[name];
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const { handlers } = eventRegister;
|
|
145
|
+
const item = { handle, priority };
|
|
146
|
+
/**
|
|
147
|
+
* handlers 排序:
|
|
148
|
+
* 1. 后注册先执行 (符合冒泡规则)
|
|
149
|
+
* 2. 按 priority 排序
|
|
150
|
+
*/
|
|
151
|
+
handlers.unshift(item);
|
|
152
|
+
handlers.sort((a, b) => b.priority - a.priority);
|
|
153
|
+
const dispose = Disposable.create(() => {
|
|
154
|
+
const index = eventRegister.handlers.indexOf(item);
|
|
155
|
+
if (index !== -1) eventRegister.handlers.splice(index, 1);
|
|
156
|
+
if (eventRegister.handlers.length === 0) {
|
|
157
|
+
eventRegister.dispose();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
this.toDispose.push(dispose);
|
|
161
|
+
return dispose;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 监听画布上的浏览器事件
|
|
166
|
+
*/
|
|
167
|
+
listenPlaygroundEvent(
|
|
168
|
+
name: PipeEventName,
|
|
169
|
+
handle: (event: PipeSupportEvent) => boolean | undefined,
|
|
170
|
+
priority?: number,
|
|
171
|
+
options?: AddEventListenerOptions
|
|
172
|
+
): Disposable {
|
|
173
|
+
return this._listenEvent(name, handle, false, priority, options);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 监听全局的事件
|
|
178
|
+
* @param name
|
|
179
|
+
* @param handle
|
|
180
|
+
*/
|
|
181
|
+
listenGlobalEvent(
|
|
182
|
+
name: PipeEventName,
|
|
183
|
+
handle: (event: PipeSupportEvent) => boolean | undefined,
|
|
184
|
+
priority?: number,
|
|
185
|
+
options?: AddEventListenerOptions
|
|
186
|
+
): Disposable {
|
|
187
|
+
return this._listenEvent(name, handle, true, priority, options);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 注册 layer
|
|
192
|
+
* @param layerRegistry
|
|
193
|
+
* @param layerOptions 配置
|
|
194
|
+
*/
|
|
195
|
+
registerLayer<P extends Layer = Layer>(
|
|
196
|
+
layerRegistry: LayerRegistry<P>,
|
|
197
|
+
layerOptions?: P['options']
|
|
198
|
+
): void {
|
|
199
|
+
// layer 不允许重复添加
|
|
200
|
+
if (this.allLayersMap.has(layerRegistry)) return;
|
|
201
|
+
const layer = this.layerFactory(layerRegistry, layerOptions);
|
|
202
|
+
this.allLayersMap.set(layerRegistry, layer);
|
|
203
|
+
// const ableRegistries = getAbleMetadata(layerRegistry);
|
|
204
|
+
const entityRegistries = getEntityMetadata(layerRegistry);
|
|
205
|
+
const entityDataRegistries = getEntityDatasMetadata(layerRegistry);
|
|
206
|
+
// ableRegistries.forEach(r => this.ableManager.registerAble(r));
|
|
207
|
+
entityRegistries.forEach((r) => {
|
|
208
|
+
this.entityManager.registerEntity(r);
|
|
209
|
+
if (Entity.isRegistryOf(r, ConfigEntity)) {
|
|
210
|
+
// 自动创建注册的 entity
|
|
211
|
+
this.entityManager.createEntity(r);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
entityDataRegistries.forEach((r) => {
|
|
215
|
+
this.entityManager.registerEntity(r.entity);
|
|
216
|
+
this.entityManager.registerEntityData(r.data);
|
|
217
|
+
});
|
|
218
|
+
// this.selector.subscribeAVbles(layer, ableRegistries);
|
|
219
|
+
this.selector.subscribeEntities(layer, entityRegistries);
|
|
220
|
+
entityDataRegistries.forEach((r) =>
|
|
221
|
+
this.selector.subscribleEntityByData(layer, r.entity, r.data)
|
|
222
|
+
);
|
|
223
|
+
layer.observeManager = new PipelineEntitiesImpl(this.entityManager);
|
|
224
|
+
// layer.commands = this.commands;
|
|
225
|
+
// layer.menus = this.menus;
|
|
226
|
+
// layer.keybindings = this.keybindings;
|
|
227
|
+
// layer.selectionService = this.selectionService;
|
|
228
|
+
layer.reloadEntities = () => {
|
|
229
|
+
const result = this.selector.getLayerData(layer);
|
|
230
|
+
if (result.changed) {
|
|
231
|
+
(layer.observeManager as PipelineEntitiesImpl).load(
|
|
232
|
+
result.observeEntities,
|
|
233
|
+
result.observeDatas
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return result.changed;
|
|
237
|
+
};
|
|
238
|
+
// layer.dispatch = (payloadKey: string | symbol, payloadValue: object, cb?: () => void) => {
|
|
239
|
+
// const changedEntities = this.dispatcher.dispatch(payloadKey, payloadValue);
|
|
240
|
+
// if (cb) {
|
|
241
|
+
// if (changedEntities.length > 0) {
|
|
242
|
+
// const changed = layer.reloadEntities();
|
|
243
|
+
// if (changed && layer.autorun) layer.autorun();
|
|
244
|
+
// }
|
|
245
|
+
// cb();
|
|
246
|
+
// }
|
|
247
|
+
// };
|
|
248
|
+
// @ts-ignore
|
|
249
|
+
layer.listenPlaygroundEvent = this.listenPlaygroundEvent.bind(this);
|
|
250
|
+
// @ts-ignore
|
|
251
|
+
layer.listenGlobalEvent = this.listenGlobalEvent.bind(this);
|
|
252
|
+
layer.config = this.configEntity;
|
|
253
|
+
layer.getOtherLayer = this.getLayer.bind(this);
|
|
254
|
+
Object.defineProperty(layer, 'isFocused', {
|
|
255
|
+
get: () => this._isFocused,
|
|
256
|
+
});
|
|
257
|
+
if (layer.onResize) {
|
|
258
|
+
this.onResize(layer.onResize.bind(layer));
|
|
259
|
+
}
|
|
260
|
+
if (layer.onBlur) {
|
|
261
|
+
this.onBlurEmitter.event(layer.onBlur.bind(layer));
|
|
262
|
+
}
|
|
263
|
+
if (layer.onFocus) {
|
|
264
|
+
this.onFocusEmitter.event(layer.onFocus.bind(layer));
|
|
265
|
+
}
|
|
266
|
+
if (layer.onZoom) {
|
|
267
|
+
this.onZoomEmitter.event(layer.onZoom.bind(layer));
|
|
268
|
+
}
|
|
269
|
+
if (layer.onScroll) {
|
|
270
|
+
this.onScrollEmitter.event(layer.onScroll.bind(layer));
|
|
271
|
+
}
|
|
272
|
+
if (layer.onViewportChange) {
|
|
273
|
+
const viewportChange = layer.onViewportChange.bind(layer);
|
|
274
|
+
this.onResize(viewportChange);
|
|
275
|
+
this.onZoomEmitter.event(viewportChange);
|
|
276
|
+
this.onScrollEmitter.event(viewportChange);
|
|
277
|
+
}
|
|
278
|
+
if (layer.onReadonlyOrDisabledChange) {
|
|
279
|
+
this.configEntity.onReadonlyOrDisabledChange(layer.onReadonlyOrDisabledChange.bind(layer));
|
|
280
|
+
}
|
|
281
|
+
this.renderer.addLayer(layer);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 获取 layer
|
|
286
|
+
*/
|
|
287
|
+
getLayer<T extends Layer>(layerRegistry: LayerRegistry<T>): T | undefined {
|
|
288
|
+
return this.allLayersMap.get(layerRegistry) as T;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
get configEntity(): PlaygroundConfigEntity {
|
|
292
|
+
return this.entityManager.getEntity<PlaygroundConfigEntity>(PlaygroundConfigEntity, true)!;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
ready(): void {
|
|
296
|
+
const config = this.configEntity;
|
|
297
|
+
let lastScale = config.finalScale;
|
|
298
|
+
let lastScroll = config.scrollData;
|
|
299
|
+
// 监听 zoom
|
|
300
|
+
config.onConfigChanged(() => {
|
|
301
|
+
/* v8 ignore next 10 */
|
|
302
|
+
const newScale = config.finalScale;
|
|
303
|
+
const newScroll = config.scrollData;
|
|
304
|
+
if (newScale !== lastScale) {
|
|
305
|
+
lastScale = newScale;
|
|
306
|
+
if (process.env.NODE_ENV === 'test') {
|
|
307
|
+
this.processMessage(zoomMessage);
|
|
308
|
+
} else {
|
|
309
|
+
MessageLoop.postMessage(this, zoomMessage);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (lastScroll.scrollX !== newScroll.scrollX || lastScroll.scrollY !== newScroll.scrollY) {
|
|
313
|
+
lastScroll = newScroll;
|
|
314
|
+
if (process.env.NODE_ENV === 'test') {
|
|
315
|
+
this.processMessage(scrollMessage);
|
|
316
|
+
} else {
|
|
317
|
+
MessageLoop.postMessage(this, scrollMessage);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
processMessage(msg: Message): void {
|
|
324
|
+
const config = this.configEntity;
|
|
325
|
+
switch (msg.type) {
|
|
326
|
+
case PipelineMessage.SCROLL:
|
|
327
|
+
this.onScrollEmitter.fire(config.scrollData);
|
|
328
|
+
break;
|
|
329
|
+
case PipelineMessage.ZOOM:
|
|
330
|
+
this.onZoomEmitter.fire(config.finalScale);
|
|
331
|
+
break;
|
|
332
|
+
default:
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* pipline 大小变化时候会触发
|
|
338
|
+
*/
|
|
339
|
+
readonly onResize = this.onResizeEmitter.event;
|
|
340
|
+
|
|
341
|
+
dispose(): void {
|
|
342
|
+
this.toDispose.dispose();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { inject, injectable } from 'inversify';
|
|
7
|
+
import {
|
|
8
|
+
ConflatableMessage,
|
|
9
|
+
type IMessageHandler,
|
|
10
|
+
type Message,
|
|
11
|
+
MessageLoop,
|
|
12
|
+
} from '@phosphor/messaging';
|
|
13
|
+
import { Disposable, DisposableCollection, domUtils, Emitter } from '@flowgram-vue/utils';
|
|
14
|
+
import { defineComponent, type Component } from 'vue';
|
|
15
|
+
|
|
16
|
+
import { type Layer } from '../layer';
|
|
17
|
+
import { LoggerService } from '../../services';
|
|
18
|
+
import { EntityManager } from '../../common';
|
|
19
|
+
import { createLayerVueAutorun } from './pipeline-vue-utils';
|
|
20
|
+
import { PipelineEntitiesSelector } from './pipeline-entities-selector';
|
|
21
|
+
import { type PipelineEntitiesImpl } from './pipeline-entities';
|
|
22
|
+
|
|
23
|
+
export const FLUSH_LAYER_REQUEST = 'flush-layer-request';
|
|
24
|
+
|
|
25
|
+
let id = 0;
|
|
26
|
+
|
|
27
|
+
export class FlushLayerMessage extends ConflatableMessage {
|
|
28
|
+
constructor(readonly layer: Layer) {
|
|
29
|
+
super(`${FLUSH_LAYER_REQUEST}_layer${id++}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* pipeline 渲染器
|
|
35
|
+
*/
|
|
36
|
+
@injectable()
|
|
37
|
+
export class PipelineRenderer implements Disposable, IMessageHandler {
|
|
38
|
+
public isReady = false;
|
|
39
|
+
|
|
40
|
+
protected onAllLayersRenderedEmitter = new Emitter<void>();
|
|
41
|
+
|
|
42
|
+
protected toDispose = new DisposableCollection();
|
|
43
|
+
|
|
44
|
+
readonly layers: Layer[] = [];
|
|
45
|
+
|
|
46
|
+
protected forceUpdates: Set<Layer> = new Set();
|
|
47
|
+
|
|
48
|
+
readonly layerAutorunMap: Map<Layer, () => void> = new Map();
|
|
49
|
+
|
|
50
|
+
readonly layerRenderedMap: Map<Layer, boolean> = new Map();
|
|
51
|
+
|
|
52
|
+
readonly layerFlushMessages: Map<Layer, FlushLayerMessage> = new Map();
|
|
53
|
+
|
|
54
|
+
readonly node = domUtils.createDivWithClass('gedit-playground-pipeline');
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 所有 Layer 第一次渲染完成后触发
|
|
58
|
+
*/
|
|
59
|
+
readonly onAllLayersRendered = this.onAllLayersRenderedEmitter.event;
|
|
60
|
+
|
|
61
|
+
@inject(LoggerService) protected readonly loggerService: LoggerService;
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
@inject(PipelineEntitiesSelector)
|
|
65
|
+
protected readonly selector: PipelineEntitiesSelector,
|
|
66
|
+
@inject(EntityManager) entityManager: EntityManager
|
|
67
|
+
// @inject(AbleManager) ableManager: AbleManager,
|
|
68
|
+
) {
|
|
69
|
+
/**
|
|
70
|
+
* entity 修改触发 layer 更新
|
|
71
|
+
*/
|
|
72
|
+
this.toDispose.push(
|
|
73
|
+
entityManager.onEntityChange((entityType: string) => {
|
|
74
|
+
const layers = this.selector.entityLayerMap.get(entityType);
|
|
75
|
+
if (layers) layers.forEach((layer) => this.updateLayer(layer));
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
this.toDispose.push(this.onAllLayersRenderedEmitter);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
reportLayerRendered(layer: Layer): void {
|
|
82
|
+
this.layerRenderedMap.set(layer, true);
|
|
83
|
+
const allLayersRendered = Array.from(this.layerRenderedMap.values()).every((v) => v);
|
|
84
|
+
if (allLayersRendered) {
|
|
85
|
+
// logger 事件点位上报
|
|
86
|
+
this.loggerService.onAllLayersRendered();
|
|
87
|
+
this.onAllLayersRenderedEmitter.fire();
|
|
88
|
+
// e2e 性能测试时会注入以下全局变量
|
|
89
|
+
if ((window as any).REPORT_TTI_FOR_E2E) {
|
|
90
|
+
(window as any).REPORT_TTI_FOR_E2E(
|
|
91
|
+
// 由于 e2e 环境,performance 耗时即为 tti 的完整耗时。
|
|
92
|
+
performance.now(),
|
|
93
|
+
performance.getEntriesByType('resource')
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
addLayer(layer: Layer): void {
|
|
100
|
+
this.layers.push(layer);
|
|
101
|
+
this.toDispose.push(layer);
|
|
102
|
+
this.layerFlushMessages.set(layer, new FlushLayerMessage(layer));
|
|
103
|
+
layer.pipelineNode = this.node;
|
|
104
|
+
layer.playgroundNode = this.node.parentElement!;
|
|
105
|
+
// Auto create node
|
|
106
|
+
if ((layer.autorun || layer.render) && !layer.node) {
|
|
107
|
+
layer.node = document.createElement('div');
|
|
108
|
+
}
|
|
109
|
+
// 把 layer 加到父亲节点上
|
|
110
|
+
if (layer.node) {
|
|
111
|
+
this.node.appendChild(layer.node);
|
|
112
|
+
layer.node.classList.add('gedit-playground-layer');
|
|
113
|
+
}
|
|
114
|
+
if (layer.autorun) {
|
|
115
|
+
const autorun = layer.autorun.bind(layer);
|
|
116
|
+
this.layerAutorunMap.set(layer, autorun);
|
|
117
|
+
// 重载 layer autorun
|
|
118
|
+
layer.autorun = () => {
|
|
119
|
+
this.updateLayer(layer, true);
|
|
120
|
+
};
|
|
121
|
+
} else if (layer.render) {
|
|
122
|
+
this.layerRenderedMap.set(layer, false);
|
|
123
|
+
const render = layer.render.bind(layer);
|
|
124
|
+
const autorun = createLayerVueAutorun(
|
|
125
|
+
layer,
|
|
126
|
+
render,
|
|
127
|
+
this.reportLayerRendered.bind(this),
|
|
128
|
+
this
|
|
129
|
+
);
|
|
130
|
+
this.layerAutorunMap.set(layer, autorun.autorun);
|
|
131
|
+
this.toDispose.push(Disposable.create(() => autorun.dispose()));
|
|
132
|
+
if (process.env.NODE_ENV === 'test') {
|
|
133
|
+
// 不对外暴露_render 字段
|
|
134
|
+
(layer as any)._render = layer.render;
|
|
135
|
+
}
|
|
136
|
+
// 重载 layer.render:外部调用时走 flush,而不是直接画
|
|
137
|
+
layer.render = () => {
|
|
138
|
+
this.updateLayer(layer, true);
|
|
139
|
+
return null;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
flush(forceUpdate?: boolean): void {
|
|
145
|
+
this.layers.forEach((layer) => {
|
|
146
|
+
this.updateLayer(layer, forceUpdate);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ready(): void {
|
|
151
|
+
this.layers.forEach((layer) => {
|
|
152
|
+
// 先加载一次数据,保证 ready 时候能运行
|
|
153
|
+
this.loadLayerEntities(layer);
|
|
154
|
+
if (layer.onReady) layer.onReady();
|
|
155
|
+
});
|
|
156
|
+
this.isReady = true;
|
|
157
|
+
// 第一次先渲染一次
|
|
158
|
+
this.flush(true);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
dispose(): void {
|
|
162
|
+
this.toDispose.dispose();
|
|
163
|
+
this.node.remove();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
processMessage(msg: Message): void {
|
|
167
|
+
if (msg instanceof FlushLayerMessage) {
|
|
168
|
+
this.onFlushRequest(msg.layer);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected loadLayerEntities(layer: Layer): boolean {
|
|
173
|
+
const result = this.selector.getLayerData(layer);
|
|
174
|
+
if (result.changed) {
|
|
175
|
+
// 这里更新 layer 的 entities
|
|
176
|
+
(layer.observeManager as PipelineEntitiesImpl).load(
|
|
177
|
+
result.observeEntities,
|
|
178
|
+
result.observeDatas
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return result.changed;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
protected onFlushRequest(layer: Layer): boolean {
|
|
185
|
+
// 只有 ready 之后才能执行 autorun
|
|
186
|
+
if (!this.isReady || this.toDispose.disposed) return false;
|
|
187
|
+
const startRenderTime = performance.now();
|
|
188
|
+
const trackRenderPerformance = () => {
|
|
189
|
+
const renderDuration = performance.now() - startRenderTime;
|
|
190
|
+
// 小于 4ms 的渲染时间不需要记录
|
|
191
|
+
if (renderDuration < 4) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.loggerService.onFlushRequest(renderDuration);
|
|
195
|
+
};
|
|
196
|
+
const autorun = this.layerAutorunMap.get(layer);
|
|
197
|
+
const changed = this.loadLayerEntities(layer);
|
|
198
|
+
// 只有修改或强制刷新才会刷新
|
|
199
|
+
if (autorun && (changed || this.forceUpdates.has(layer))) {
|
|
200
|
+
this.forceUpdates.delete(layer);
|
|
201
|
+
try {
|
|
202
|
+
// console.time(`layer ${layer.constructor.name}`)
|
|
203
|
+
autorun();
|
|
204
|
+
// console.timeEnd(`layer ${layer.constructor.name}`)
|
|
205
|
+
/* v8 ignore next 3 */
|
|
206
|
+
} catch (e) {
|
|
207
|
+
console.error(e);
|
|
208
|
+
}
|
|
209
|
+
trackRenderPerformance();
|
|
210
|
+
return true;
|
|
211
|
+
/* v8 ignore next 2 */
|
|
212
|
+
}
|
|
213
|
+
trackRenderPerformance();
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 1. PostMessage: 会将消息在 nextTick 执行
|
|
219
|
+
* 2. ConflatableMessage: 当多个消息进来会在下一个 nextTick 做合并
|
|
220
|
+
* 3. 图层相互隔离,即时一层挂了也不受影响
|
|
221
|
+
*/
|
|
222
|
+
updateLayer(layer: Layer, forceUpdate?: boolean): void {
|
|
223
|
+
if (forceUpdate) {
|
|
224
|
+
this.forceUpdates.add(layer);
|
|
225
|
+
}
|
|
226
|
+
if (process.env.NODE_ENV === 'test') {
|
|
227
|
+
this.onFlushRequest(layer);
|
|
228
|
+
} else {
|
|
229
|
+
MessageLoop.postMessage(this, this.layerFlushMessages.get(layer)!);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private vueComp?: Component;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 转成 Vue 组件。Layer 已通过 createApp 挂到 layer.node,这里仅保留 API 兼容。
|
|
237
|
+
*/
|
|
238
|
+
toVueComponent(): Component {
|
|
239
|
+
if (this.vueComp) return this.vueComp;
|
|
240
|
+
this.vueComp = defineComponent({
|
|
241
|
+
name: 'PipelineVueLayers',
|
|
242
|
+
setup() {
|
|
243
|
+
return () => null;
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
return this.vueComp;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { NOOP } from '@flowgram-vue/utils';
|
|
7
|
+
import {
|
|
8
|
+
createApp,
|
|
9
|
+
defineComponent,
|
|
10
|
+
h,
|
|
11
|
+
isVNode,
|
|
12
|
+
nextTick,
|
|
13
|
+
onMounted,
|
|
14
|
+
shallowRef,
|
|
15
|
+
type App,
|
|
16
|
+
type Component,
|
|
17
|
+
type VNode,
|
|
18
|
+
} from 'vue';
|
|
19
|
+
|
|
20
|
+
import type { Layer } from '../layer';
|
|
21
|
+
import { type PipelineRenderer } from './pipeline-renderer';
|
|
22
|
+
|
|
23
|
+
export type LayerRenderResult = Component | VNode | null | void;
|
|
24
|
+
|
|
25
|
+
export interface LayerVueAutorun {
|
|
26
|
+
autorun: () => void;
|
|
27
|
+
dispose: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createLayerVueAutorun(
|
|
31
|
+
layer: Layer,
|
|
32
|
+
originRenderer: () => LayerRenderResult,
|
|
33
|
+
renderedCb: (layer: Layer) => void,
|
|
34
|
+
pipelineRenderer: PipelineRenderer
|
|
35
|
+
): LayerVueAutorun {
|
|
36
|
+
let app: App | null = null;
|
|
37
|
+
let update = NOOP;
|
|
38
|
+
|
|
39
|
+
const Root = defineComponent({
|
|
40
|
+
name: `PlaygroundVueLayer_${layer.constructor.name}`,
|
|
41
|
+
setup() {
|
|
42
|
+
const version = shallowRef(0);
|
|
43
|
+
update = () => {
|
|
44
|
+
version.value += 1;
|
|
45
|
+
};
|
|
46
|
+
onMounted(() => {
|
|
47
|
+
// 等 ready() 栈(含 contribution onReady)结束后再上报
|
|
48
|
+
nextTick(() => renderedCb(layer));
|
|
49
|
+
});
|
|
50
|
+
return () => {
|
|
51
|
+
void version.value;
|
|
52
|
+
if (!pipelineRenderer.isReady) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const result = originRenderer();
|
|
57
|
+
if (result == null) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (isVNode(result)) {
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
return h(result as Component);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error(`Render Layer "${layer.constructor.name}" error `, e);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
function ensureMounted(): void {
|
|
73
|
+
if (app || !layer.node) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
app = createApp(Root);
|
|
77
|
+
app.mount(layer.node);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
autorun: () => {
|
|
82
|
+
if (!app) {
|
|
83
|
+
ensureMounted();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
update();
|
|
87
|
+
},
|
|
88
|
+
dispose: () => {
|
|
89
|
+
if (app) {
|
|
90
|
+
app.unmount();
|
|
91
|
+
app = null;
|
|
92
|
+
}
|
|
93
|
+
update = NOOP;
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type Layer, type LayerRegistry } from '../layer';
|
|
7
|
+
|
|
8
|
+
export type PipeSupportEvent = MouseEvent | DragEvent | KeyboardEvent | UIEvent | TouchEvent | any;
|
|
9
|
+
export type PipeEventName = string;
|
|
10
|
+
export interface PipelineDimension {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type PipelineEventHandler = (event: PipeSupportEvent) => boolean | undefined;
|
|
16
|
+
|
|
17
|
+
export interface PipelineEventRegsiter {
|
|
18
|
+
handle: PipelineEventHandler;
|
|
19
|
+
priority: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export enum PipelineLayerPriority {
|
|
23
|
+
BASE_LAYER = -2, // 优先级最低
|
|
24
|
+
TOOL_LAYER = -1, // 工具层
|
|
25
|
+
NORMAL_LAYER, // 使用层
|
|
26
|
+
}
|
|
27
|
+
export const PipelineLayerFactory = Symbol('PipelineLayerFactory');
|
|
28
|
+
export type PipelineLayerFactory = (layerRegistry: LayerRegistry, layerOptions?: any) => Layer;
|