@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.
Files changed (133) hide show
  1. package/LICENSE +22 -0
  2. package/dist/index.cjs +0 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +0 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +63 -0
  8. package/src/common/config-entity.ts +80 -0
  9. package/src/common/entity-data.ts +171 -0
  10. package/src/common/entity-manager-contribution.ts +12 -0
  11. package/src/common/entity-manager.ts +475 -0
  12. package/src/common/entity.ts +482 -0
  13. package/src/common/index.ts +22 -0
  14. package/src/common/playground-context.ts +35 -0
  15. package/src/common/playground-decorator-helper.ts +113 -0
  16. package/src/common/playground-decorators.ts +85 -0
  17. package/src/common/playground-schedule.ts +30 -0
  18. package/src/common/protect-wheel-area.ts +11 -0
  19. package/src/common/schema/index.ts +14 -0
  20. package/src/common/schema/node.ts +15 -0
  21. package/src/common/schema/opacity-schema.ts +19 -0
  22. package/src/common/schema/origin-schema.ts +35 -0
  23. package/src/common/schema/position-schema.ts +35 -0
  24. package/src/common/schema/rotation-schema.ts +19 -0
  25. package/src/common/schema/scale-schema.ts +35 -0
  26. package/src/common/schema/size-schema.ts +42 -0
  27. package/src/common/schema/skew-schema.ts +35 -0
  28. package/src/common/schema/transform-schema.ts +613 -0
  29. package/src/common/utils/bounds.spec.ts +229 -0
  30. package/src/common/utils/bounds.ts +176 -0
  31. package/src/common/utils/index.ts +6 -0
  32. package/src/composables/index.ts +16 -0
  33. package/src/composables/use-config-entity.ts +31 -0
  34. package/src/composables/use-entities.ts +26 -0
  35. package/src/composables/use-entity-data-from-context.ts +34 -0
  36. package/src/composables/use-entity-from-context.ts +31 -0
  37. package/src/composables/use-listen-events.ts +22 -0
  38. package/src/composables/use-playground-container.ts +16 -0
  39. package/src/composables/use-playground-context.ts +15 -0
  40. package/src/composables/use-playground-drag.ts +31 -0
  41. package/src/composables/use-playground.ts +16 -0
  42. package/src/composables/use-refresh.ts +6 -0
  43. package/src/composables/use-service.ts +17 -0
  44. package/src/core/index.ts +8 -0
  45. package/src/core/layer/config/editor-state-config-entity.ts +180 -0
  46. package/src/core/layer/config/index.ts +7 -0
  47. package/src/core/layer/config/playground-config-entity.ts +604 -0
  48. package/src/core/layer/index.ts +8 -0
  49. package/src/core/layer/layer.ts +248 -0
  50. package/src/core/layer/playground-layer.ts +547 -0
  51. package/src/core/pipeline/index.ts +10 -0
  52. package/src/core/pipeline/pipeline-entities-selector.ts +200 -0
  53. package/src/core/pipeline/pipeline-entities.ts +230 -0
  54. package/src/core/pipeline/pipeline-registry.ts +344 -0
  55. package/src/core/pipeline/pipeline-renderer.ts +248 -0
  56. package/src/core/pipeline/pipeline-vue-utils.ts +96 -0
  57. package/src/core/pipeline/pipeline.ts +28 -0
  58. package/src/core/utils/index.ts +12 -0
  59. package/src/core/utils/inject-provider-decorators.ts +27 -0
  60. package/src/core/utils/lazy-inject-decorators.ts +38 -0
  61. package/src/core/utils/mouse-touch-event.ts +120 -0
  62. package/src/core/utils/playground-drag.ts +511 -0
  63. package/src/core/utils/playground-gesture.spec.ts +135 -0
  64. package/src/core/utils/playground-gesture.ts +118 -0
  65. package/src/core/utils/tween.ts +156 -0
  66. package/src/core/utils/use-gesture/core/Controller.ts +212 -0
  67. package/src/core/utils/use-gesture/core/EventStore.ts +46 -0
  68. package/src/core/utils/use-gesture/core/TimeoutStore.ts +28 -0
  69. package/src/core/utils/use-gesture/core/actions.ts +63 -0
  70. package/src/core/utils/use-gesture/core/config/commonConfigResolver.ts +86 -0
  71. package/src/core/utils/use-gesture/core/config/coordinatesConfigResolver.ts +48 -0
  72. package/src/core/utils/use-gesture/core/config/dragConfigResolver.ts +140 -0
  73. package/src/core/utils/use-gesture/core/config/hoverConfigResolver.ts +11 -0
  74. package/src/core/utils/use-gesture/core/config/moveConfigResolver.ts +11 -0
  75. package/src/core/utils/use-gesture/core/config/pinchConfigResolver.ts +59 -0
  76. package/src/core/utils/use-gesture/core/config/resolver.ts +70 -0
  77. package/src/core/utils/use-gesture/core/config/scrollConfigResolver.ts +8 -0
  78. package/src/core/utils/use-gesture/core/config/sharedConfigResolver.ts +28 -0
  79. package/src/core/utils/use-gesture/core/config/support.ts +52 -0
  80. package/src/core/utils/use-gesture/core/config/wheelConfigResolver.ts +8 -0
  81. package/src/core/utils/use-gesture/core/engines/CoordinatesEngine.ts +78 -0
  82. package/src/core/utils/use-gesture/core/engines/DragEngine.ts +403 -0
  83. package/src/core/utils/use-gesture/core/engines/Engine.ts +406 -0
  84. package/src/core/utils/use-gesture/core/engines/HoverEngine.ts +43 -0
  85. package/src/core/utils/use-gesture/core/engines/MoveEngine.ts +52 -0
  86. package/src/core/utils/use-gesture/core/engines/PinchEngine.ts +345 -0
  87. package/src/core/utils/use-gesture/core/engines/ScrollEngine.ts +42 -0
  88. package/src/core/utils/use-gesture/core/engines/WheelEngine.ts +48 -0
  89. package/src/core/utils/use-gesture/core/index.ts +7 -0
  90. package/src/core/utils/use-gesture/core/parser.ts +92 -0
  91. package/src/core/utils/use-gesture/core/types/action.ts +19 -0
  92. package/src/core/utils/use-gesture/core/types/config.ts +255 -0
  93. package/src/core/utils/use-gesture/core/types/handlers.ts +73 -0
  94. package/src/core/utils/use-gesture/core/types/index.ts +11 -0
  95. package/src/core/utils/use-gesture/core/types/internalConfig.ts +80 -0
  96. package/src/core/utils/use-gesture/core/types/state.ts +275 -0
  97. package/src/core/utils/use-gesture/core/types/utils.ts +204 -0
  98. package/src/core/utils/use-gesture/core/types.ts +8 -0
  99. package/src/core/utils/use-gesture/core/utils/events.ts +158 -0
  100. package/src/core/utils/use-gesture/core/utils/fn.ts +32 -0
  101. package/src/core/utils/use-gesture/core/utils/maths.ts +63 -0
  102. package/src/core/utils/use-gesture/core/utils/state.ts +24 -0
  103. package/src/core/utils/use-gesture/core/utils.ts +8 -0
  104. package/src/core/utils/use-gesture/index.ts +6 -0
  105. package/src/core/utils/use-gesture/vanilla/DragGesture.ts +27 -0
  106. package/src/core/utils/use-gesture/vanilla/Gesture.ts +45 -0
  107. package/src/core/utils/use-gesture/vanilla/HoverGesture.ts +27 -0
  108. package/src/core/utils/use-gesture/vanilla/MoveGesture.ts +27 -0
  109. package/src/core/utils/use-gesture/vanilla/PinchGesture.ts +27 -0
  110. package/src/core/utils/use-gesture/vanilla/Recognizer.ts +43 -0
  111. package/src/core/utils/use-gesture/vanilla/ScrollGesture.ts +27 -0
  112. package/src/core/utils/use-gesture/vanilla/WheelGesture.ts +27 -0
  113. package/src/core/utils/use-gesture/vanilla/createGesture.ts +18 -0
  114. package/src/core/utils/use-gesture/vanilla/index.ts +18 -0
  115. package/src/env.d.ts +10 -0
  116. package/src/index.ts +17 -0
  117. package/src/playground-config.ts +70 -0
  118. package/src/playground-container.ts +129 -0
  119. package/src/playground-contribution.ts +81 -0
  120. package/src/playground-mock-tools.ts +143 -0
  121. package/src/playground.ts +401 -0
  122. package/src/plugin/index.ts +6 -0
  123. package/src/plugin/plugin.ts +226 -0
  124. package/src/services/clipboard-service.ts +40 -0
  125. package/src/services/context-menu-service.ts +25 -0
  126. package/src/services/index.ts +10 -0
  127. package/src/services/logger-service.ts +49 -0
  128. package/src/services/selection-service.ts +54 -0
  129. package/src/services/storage-service.ts +65 -0
  130. package/src/vue/index.ts +8 -0
  131. package/src/vue/playground-vue-context.ts +22 -0
  132. package/src/vue/playground-vue-provider.ts +115 -0
  133. package/src/vue/playground-vue-renderer.vue +44 -0
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './playground-drag';
7
+ // export * from './adsorber';
8
+ export * from './tween';
9
+ export * from './playground-gesture';
10
+ export { injectByProvider } from './inject-provider-decorators';
11
+ export { lazyInject, LazyInjectContext } from './lazy-inject-decorators';
12
+ export { MouseTouchEvent } from './mouse-touch-event';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { inject, interfaces, optional } from 'inversify';
7
+ import 'reflect-metadata';
8
+
9
+ export const injectByProvider = (provider: interfaces.ServiceIdentifier) =>
10
+ function (target: any, propertyKey: string) {
11
+ const providerPropertyKey = `${propertyKey}Provider`;
12
+
13
+ inject(provider)(target, providerPropertyKey);
14
+ optional()(target, providerPropertyKey);
15
+
16
+ // decorate 会依赖 reflect-metadata,因此解除其依赖
17
+ // decorate(inject(provider), target, providerPropertyKey);
18
+ // decorate(optional(), target, providerPropertyKey);
19
+
20
+ return {
21
+ get() {
22
+ return this[providerPropertyKey]?.();
23
+ },
24
+ configurable: true,
25
+ enumerable: true,
26
+ } as any;
27
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { inject, interfaces } from 'inversify';
7
+
8
+ export const LazyInjectContext = Symbol('LazyInjectContext');
9
+ export const IS_LAZY_INJECT_CONTEXT_INJECTED = Symbol('IS_LAZY_INJECT_CONTEXT_INJECTED');
10
+
11
+ export const lazyInject = (serviceIdentifier: interfaces.ServiceIdentifier) =>
12
+ function (target: any, propertyKey: string) {
13
+ if (!serviceIdentifier) {
14
+ throw new Error(
15
+ `ServiceIdentifier ${serviceIdentifier} in @lazyInject is Empty, it might be caused by file circular dependency, please check it.`,
16
+ );
17
+ }
18
+
19
+ // 只依赖注入一次
20
+ if (!Reflect.hasMetadata(IS_LAZY_INJECT_CONTEXT_INJECTED, target)) {
21
+ inject(LazyInjectContext)(target, LazyInjectContext);
22
+ Reflect.defineMetadata(IS_LAZY_INJECT_CONTEXT_INJECTED, true, target);
23
+ }
24
+
25
+ const descriptor = {
26
+ get() {
27
+ const ctx = this[LazyInjectContext];
28
+ return ctx.get(serviceIdentifier);
29
+ },
30
+ set() {},
31
+ configurable: true,
32
+ enumerable: true,
33
+ } as any;
34
+
35
+ // Object.defineProperty(target, propertyKey, descriptor);
36
+
37
+ return descriptor;
38
+ };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export namespace MouseTouchEvent {
7
+ export const isTouchEvent = (event: Event | TouchEvent): event is TouchEvent =>
8
+ 'touches' in event;
9
+
10
+ export const touchToMouseEvent = (event: Event): MouseEvent | Event => {
11
+ if (!isTouchEvent(event as TouchEvent)) {
12
+ return event as MouseEvent;
13
+ }
14
+ const touchEvent = event as TouchEvent;
15
+
16
+ // 默认获取第一个触摸点
17
+ const touch = touchEvent.touches[0] || touchEvent.changedTouches[0];
18
+
19
+ if (touchEvent.type === 'touchmove') {
20
+ preventDefault(touchEvent);
21
+ }
22
+
23
+ // 确定对应的鼠标事件类型
24
+ const mouseEventType = {
25
+ touchstart: 'mousedown',
26
+ touchmove: 'mousemove',
27
+ touchend: 'mouseup',
28
+ touchcancel: 'mouseup',
29
+ }[touchEvent.type];
30
+
31
+ if (!mouseEventType) {
32
+ throw new Error(`Unknown touch event type: ${touchEvent.type}`);
33
+ }
34
+
35
+ // 创建新的鼠标事件
36
+ const mouseEvent = new MouseEvent(mouseEventType, {
37
+ bubbles: touchEvent.bubbles,
38
+ cancelable: touchEvent.cancelable,
39
+ view: touchEvent.view,
40
+ // 复制触摸点的位置信息
41
+ clientX: touch.clientX,
42
+ clientY: touch.clientY,
43
+ screenX: touch.screenX,
44
+ screenY: touch.screenY,
45
+ // 复制修饰键状态
46
+ ctrlKey: touchEvent.ctrlKey,
47
+ altKey: touchEvent.altKey,
48
+ shiftKey: touchEvent.shiftKey,
49
+ metaKey: touchEvent.metaKey,
50
+ });
51
+
52
+ return mouseEvent;
53
+ };
54
+ export const getEventCoord = (
55
+ e:
56
+ | MouseEvent
57
+ | TouchEvent
58
+ | {
59
+ clientX: number;
60
+ clientY: number;
61
+ }
62
+ ): {
63
+ clientX: number;
64
+ clientY: number;
65
+ } => {
66
+ if (isTouchEvent(e as TouchEvent)) {
67
+ const touchEvent = e as TouchEvent;
68
+ if (touchEvent.touches.length === 0) {
69
+ return {
70
+ clientX: 0,
71
+ clientY: 0,
72
+ };
73
+ }
74
+ return {
75
+ clientX: touchEvent.touches[0].clientX,
76
+ clientY: touchEvent.touches[0].clientY,
77
+ };
78
+ } else if (e instanceof MouseEvent) {
79
+ return {
80
+ clientX: e.clientX,
81
+ clientY: e.clientY,
82
+ };
83
+ }
84
+ return {
85
+ clientX: (e as MouseEvent).clientX,
86
+ clientY: (e as MouseEvent).clientY,
87
+ };
88
+ };
89
+
90
+ export const preventDefault = (e: Event | MouseEvent | TouchEvent) => {
91
+ if (e.cancelable) {
92
+ e.preventDefault();
93
+ }
94
+ };
95
+
96
+ export const onTouched = (
97
+ touchStartEvent: TouchEvent,
98
+ callback: (e: MouseEvent) => void
99
+ ) => {
100
+ const startTouch = touchStartEvent.changedTouches[0];
101
+
102
+ const handleTouchEnd = (touchEndEvent: TouchEvent) => {
103
+ const endTouch = touchEndEvent.changedTouches[0];
104
+ const deltaX = endTouch.clientX - startTouch.clientX;
105
+ const deltaY = endTouch.clientY - startTouch.clientY;
106
+ // 判断是拖拽还是点击
107
+ const delta = 5;
108
+ if (Math.abs(deltaX) < delta && Math.abs(deltaY) < delta) {
109
+ // 触发回调
110
+ const mouseEvent = touchToMouseEvent(touchEndEvent) as MouseEvent;
111
+ callback(mouseEvent);
112
+ }
113
+ document.removeEventListener('touchend', handleTouchEnd);
114
+ document.removeEventListener('touchcancel', handleTouchEnd);
115
+ };
116
+
117
+ document.addEventListener('touchend', handleTouchEnd);
118
+ document.addEventListener('touchcancel', handleTouchEnd);
119
+ };
120
+ }
@@ -0,0 +1,511 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import {
7
+ Disposable,
8
+ Emitter,
9
+ generateLocalId,
10
+ type LocalId,
11
+ type Rectangle,
12
+ } from '@flowgram-vue/utils';
13
+
14
+ import type { PlaygroundConfigEntity } from '../layer/config';
15
+ import type { PositionSchema } from '../../common/schema/position-schema';
16
+ import { MouseTouchEvent } from './mouse-touch-event';
17
+
18
+ /* istanbul ignore next */
19
+ const SCROLL_DELTA = 4;
20
+ /* istanbul ignore next */
21
+ const SCROLL_AUTO_DISTANCE = 20; // 自动滚动到边缘距离
22
+ /* istanbul ignore next */
23
+ const SCROLL_INTERVAL = 6;
24
+
25
+ /* istanbul ignore next */
26
+ function createMouseEvent(type: string, clientX: number, clientY: number): MouseEvent {
27
+ const event = document.createEvent('MouseEvent');
28
+ event.initMouseEvent(
29
+ type,
30
+ true,
31
+ true,
32
+ // @ts-ignore
33
+ undefined,
34
+ 0,
35
+ 0,
36
+ 0,
37
+ clientX,
38
+ clientY,
39
+ false,
40
+ false,
41
+ false,
42
+ false,
43
+ 0,
44
+ null
45
+ );
46
+ return event;
47
+ }
48
+
49
+ export interface PlaygroundDragEvent extends MouseEvent {
50
+ id: LocalId;
51
+ startPos: PositionSchema;
52
+ endPos: PositionSchema;
53
+ movingDelta: PositionSchema; // 移动的偏移量
54
+ scale: number;
55
+ isMoving: boolean;
56
+ isStart: boolean;
57
+ }
58
+
59
+ export interface PlaygroundDragOptions<T> {
60
+ onDragStart?: (e: PlaygroundDragEvent, context?: T) => void;
61
+ onDrag?: (e: PlaygroundDragEvent, context?: T) => void;
62
+ onDragEnd?: (e: PlaygroundDragEvent, context?: T) => void;
63
+ stopGlobalEventNames?: string[];
64
+ }
65
+
66
+ /* istanbul ignore next */
67
+ export class PlaygroundDrag<T = undefined> implements Disposable {
68
+ private onDragStartEmitter = new Emitter<PlaygroundDragEvent>();
69
+
70
+ private onDragEndEmitter = new Emitter<PlaygroundDragEvent>();
71
+
72
+ private onDragEmitter = new Emitter<PlaygroundDragEvent>();
73
+
74
+ private readonly _stopGlobalEventNames = [
75
+ 'mouseenter',
76
+ 'mouseleave',
77
+ 'mouseover',
78
+ 'mouseout',
79
+ 'contextmenu',
80
+ ];
81
+
82
+ private localId: LocalId;
83
+
84
+ protected context?: T;
85
+
86
+ readonly onDrag = this.onDragEmitter.event;
87
+
88
+ readonly onDragStart = this.onDragStartEmitter.event;
89
+
90
+ readonly onDragEnd = this.onDragEndEmitter.event;
91
+
92
+ constructor(options: PlaygroundDragOptions<T> = {}) {
93
+ if (options.onDragStart) this.onDragStart((e) => options.onDragStart!(e, this.context));
94
+ if (options.onDrag) this.onDrag((e) => options.onDrag!(e, this.context));
95
+ if (options.onDragEnd) this.onDragEnd((e) => options.onDragEnd!(e, this.context));
96
+ if (options.stopGlobalEventNames) this._stopGlobalEventNames = options.stopGlobalEventNames;
97
+ }
98
+
99
+ get isStarted(): boolean {
100
+ return !!this._promise;
101
+ }
102
+
103
+ start(
104
+ clientX: number,
105
+ clientY: number,
106
+ entity?: PlaygroundConfigEntity,
107
+ context?: T
108
+ ): Promise<void> {
109
+ if (this._disposed) {
110
+ return Promise.resolve();
111
+ }
112
+ if (this._promise) {
113
+ return this._promise;
114
+ }
115
+ this.context = context;
116
+ this.localId = generateLocalId();
117
+ this._addListeners();
118
+ this._promise = new Promise((resolve) => {
119
+ this._resolve = resolve;
120
+ });
121
+ this._playgroundConfigEntity = entity;
122
+ const mousedown = createMouseEvent('mousedown', clientX, clientY);
123
+ this._startPos = this.getRelativePos(mousedown);
124
+ this.onDragStartEmitter.fire(this.getDragEvent(mousedown));
125
+ return this._promise;
126
+ }
127
+
128
+ stop(clientX: number, clientY: number): void {
129
+ if (this._disposed || !this._promise) {
130
+ return;
131
+ }
132
+ const mouseup = createMouseEvent('mouseup', clientX, clientY);
133
+ this.handleEvent(mouseup);
134
+ }
135
+
136
+ dispose(): void {
137
+ if (this._disposed) return;
138
+ this._stopScrollX();
139
+ this._stopScrollY();
140
+ this._disposed = true;
141
+ this.onDragEmitter.dispose();
142
+ this.onDragStartEmitter.dispose();
143
+ this.onDragEndEmitter.dispose();
144
+ this._finalize();
145
+ }
146
+
147
+ handleEvent(_event: Event): void {
148
+ const event = MouseTouchEvent.touchToMouseEvent(_event);
149
+ switch (event.type) {
150
+ case 'mousemove':
151
+ this._evtMouseMove(event as MouseEvent);
152
+ break;
153
+ case 'mouseup':
154
+ this._stopScrollX();
155
+ this._stopScrollY();
156
+ this._evtMouseUp(event as MouseEvent);
157
+ break;
158
+ case 'keydown':
159
+ this._evtKeyDown(event as KeyboardEvent);
160
+ break;
161
+ // TODO 暂时屏蔽右键菜单
162
+ case 'contextmenu':
163
+ const mouseup = createMouseEvent(
164
+ 'mouseup',
165
+ (event as MouseEvent).clientX,
166
+ (event as MouseEvent).clientY
167
+ );
168
+ this._evtMouseUp(mouseup);
169
+ break;
170
+ default:
171
+ // Stop all other events during drag-drop.
172
+ MouseTouchEvent.preventDefault(event);
173
+ event.stopPropagation();
174
+ break;
175
+ }
176
+ }
177
+
178
+ get scale(): number {
179
+ return this._playgroundConfigEntity ? this._playgroundConfigEntity.finalScale : 1;
180
+ }
181
+
182
+ protected getRelativePos(event: MouseEvent): PositionSchema {
183
+ if (this._playgroundConfigEntity) {
184
+ return this._playgroundConfigEntity.getPosFromMouseEvent(event, false);
185
+ }
186
+ return {
187
+ x: event.clientX,
188
+ y: event.clientY,
189
+ };
190
+ }
191
+
192
+ private _lastPos: PositionSchema = { x: 0, y: 0 };
193
+
194
+ protected getDragEvent(event: MouseEvent): PlaygroundDragEvent {
195
+ const startPos = this._startPos!;
196
+ const { scale } = this;
197
+ switch (event.type) {
198
+ case 'mousedown':
199
+ this._lastPos = startPos;
200
+ return Object.assign(event, {
201
+ id: this.localId,
202
+ startPos,
203
+ endPos: startPos,
204
+ scale,
205
+ movingDelta: { x: 0, y: 0 },
206
+ isStart: true,
207
+ isMoving: false,
208
+ });
209
+ case 'mousemove':
210
+ const endPos = this.getRelativePos(event);
211
+ const movingDelta = {
212
+ x: endPos.x - this._lastPos.x,
213
+ y: endPos.y - this._lastPos.y,
214
+ };
215
+ this._lastPos = endPos;
216
+ return Object.assign(event, {
217
+ id: this.localId,
218
+ startPos,
219
+ endPos,
220
+ scale,
221
+ isStart: true,
222
+ movingDelta,
223
+ isMoving: true,
224
+ });
225
+ case 'mouseup':
226
+ this._lastPos = { x: 0, y: 0 };
227
+ return Object.assign(event, {
228
+ id: this.localId,
229
+ startPos,
230
+ endPos: this.getRelativePos(event),
231
+ movingDelta: { x: 0, y: 0 },
232
+ scale,
233
+ isStart: false,
234
+ isMoving: false,
235
+ });
236
+ default:
237
+ throw new Error('unknown event');
238
+ }
239
+ }
240
+
241
+ private _finalize(): void {
242
+ const resolve = this._resolve;
243
+ this._removeListeners();
244
+ this._startPos = undefined;
245
+ this._promise = undefined;
246
+ this._resolve = undefined;
247
+ if (resolve) {
248
+ resolve();
249
+ }
250
+ }
251
+
252
+ // 这个用于自动滚动时候使用
253
+ private _lastMouseMoveEvent?: MouseEvent;
254
+
255
+ /**
256
+ * Handle the `'mousemove'` event for the drag object.
257
+ */
258
+ private _evtMouseMove(event: MouseEvent): void {
259
+ // Stop all input events during drag-drop.
260
+ event.preventDefault();
261
+ event.stopPropagation();
262
+ this._lastMouseMoveEvent = event;
263
+ const dragEvent = this.getDragEvent(event);
264
+ // Update the playground scroller.
265
+ this._updateDragScroll(dragEvent);
266
+ this.onDragEmitter.fire(dragEvent);
267
+ }
268
+
269
+ /**
270
+ * Handle the `'mouseup'` event for the drag object.
271
+ */
272
+ private _evtMouseUp(event: MouseEvent): void {
273
+ this._lastMouseMoveEvent = undefined;
274
+ // Stop all input events during drag-drop.
275
+ event.preventDefault();
276
+ event.stopPropagation();
277
+
278
+ // Do nothing if the left or center button is not released.
279
+ if (event.button !== 0 && event.button !== 1) {
280
+ return;
281
+ }
282
+ this.onDragEndEmitter.fire(this.getDragEvent(event));
283
+ this._finalize();
284
+ }
285
+
286
+ /**
287
+ * Handle the `'keydown'` event for the drag object.
288
+ */
289
+ private _evtKeyDown(event: KeyboardEvent): void {
290
+ // Stop all input events during drag-drop.
291
+ event.preventDefault();
292
+ event.stopPropagation();
293
+
294
+ // Cancel the drag if `Escape` is pressed.
295
+ if (event.keyCode === 27) {
296
+ this.stop(NaN, NaN);
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Add the document event listeners for the drag object.
302
+ */
303
+ private _addListeners(): void {
304
+ // mouse
305
+ document.addEventListener('mousedown', this, true);
306
+ document.addEventListener('mousemove', this, true);
307
+ document.addEventListener('mouseup', this, true);
308
+
309
+ // touch
310
+ document.addEventListener('touchstart', this, true);
311
+ document.addEventListener('touchmove', this, { passive: false });
312
+ document.addEventListener('touchend', this, true);
313
+ document.addEventListener('touchcancel', this, true);
314
+
315
+ this._stopGlobalEventNames.forEach((_event) => {
316
+ document.addEventListener(_event, this, true);
317
+ });
318
+ }
319
+
320
+ /**
321
+ * Remove the document event listeners for the drag object.
322
+ */
323
+ private _removeListeners(): void {
324
+ // mouse
325
+ document.removeEventListener('mousedown', this, true);
326
+ document.removeEventListener('mousemove', this, true);
327
+ document.removeEventListener('mouseup', this, true);
328
+
329
+ // touch
330
+ document.removeEventListener('touchstart', this, true);
331
+ document.removeEventListener('touchmove', this);
332
+ document.removeEventListener('touchend', this, true);
333
+ document.removeEventListener('touchcancel', this, true);
334
+
335
+ this._stopGlobalEventNames.forEach((_event) => {
336
+ document.removeEventListener(_event, this, true);
337
+ });
338
+ }
339
+
340
+ /**
341
+ * 自动滚动画布
342
+ */
343
+ private _updateDragScroll = (event: PlaygroundDragEvent): void => {
344
+ if (!this._playgroundConfigEntity) return;
345
+ const playgroundConfig = this._playgroundConfigEntity.config;
346
+ const dragPos = event.endPos;
347
+ const { scrollX, width, height, scrollY } = playgroundConfig;
348
+ if (dragPos.x > width + scrollX - SCROLL_AUTO_DISTANCE) {
349
+ this._startScrollX(scrollX, true);
350
+ } else if (dragPos.x < scrollX + SCROLL_AUTO_DISTANCE) {
351
+ this._startScrollX(scrollX, false);
352
+ } else {
353
+ this._stopScrollX();
354
+ }
355
+ if (dragPos.y > height + scrollY - SCROLL_AUTO_DISTANCE) {
356
+ this._startScrollY(scrollY, true);
357
+ } else if (dragPos.y < scrollY + SCROLL_AUTO_DISTANCE) {
358
+ this._startScrollY(scrollY, false);
359
+ } else {
360
+ this._stopScrollY();
361
+ }
362
+ };
363
+
364
+ private _scrollXInterval: { interval: number; origin: number } | undefined;
365
+
366
+ private _scrollYInterval: { interval: number; origin: number } | undefined;
367
+
368
+ private _startScrollX(origin: number, added: boolean): void {
369
+ if (this._scrollXInterval) {
370
+ return;
371
+ }
372
+ const interval = window.setInterval(() => {
373
+ const current = this._scrollXInterval!;
374
+ if (!current) return;
375
+ this.fireScroll('scrollX', added);
376
+ }, SCROLL_INTERVAL);
377
+ this._scrollXInterval = { interval, origin };
378
+ }
379
+
380
+ private _stopScrollX(): void {
381
+ if (this._scrollXInterval) {
382
+ clearInterval(this._scrollXInterval.interval);
383
+ this._scrollXInterval = undefined;
384
+ }
385
+ }
386
+
387
+ private _startScrollY(origin: number, added: boolean): void {
388
+ if (this._scrollYInterval) {
389
+ return;
390
+ }
391
+ const interval = window.setInterval(() => {
392
+ this.fireScroll('scrollY', added);
393
+ }, SCROLL_INTERVAL);
394
+ this._scrollYInterval = { interval, origin };
395
+ }
396
+
397
+ private _stopScrollY(): void {
398
+ if (this._scrollYInterval) {
399
+ clearInterval(this._scrollYInterval.interval);
400
+ this._scrollYInterval = undefined;
401
+ }
402
+ }
403
+
404
+ /**
405
+ * 触发滚动
406
+ * @param scrollKey
407
+ * @param added
408
+ */
409
+ fireScroll(scrollKey: 'scrollY' | 'scrollX', added: boolean): void {
410
+ const current = scrollKey === 'scrollY' ? this._scrollYInterval : this._scrollXInterval;
411
+ if (!current) return;
412
+ const value = (current.origin = added
413
+ ? current.origin + SCROLL_DELTA
414
+ : current.origin - SCROLL_DELTA);
415
+ const oldScroll = this._playgroundConfigEntity!.config[scrollKey];
416
+ this._playgroundConfigEntity!.updateConfig({
417
+ [scrollKey]: value,
418
+ });
419
+ const newScroll = this._playgroundConfigEntity!.config[scrollKey];
420
+ if (newScroll !== oldScroll) {
421
+ const lastMouseMoveEvent = this._lastMouseMoveEvent!;
422
+ const delta = {
423
+ x: scrollKey === 'scrollX' ? newScroll - current.origin : 0,
424
+ y: scrollKey === 'scrollY' ? newScroll - current.origin : 0,
425
+ };
426
+ const mouseMove = createMouseEvent(
427
+ 'mousemove',
428
+ lastMouseMoveEvent.clientX + delta.x,
429
+ lastMouseMoveEvent.clientY + delta.y
430
+ );
431
+ const dragEvent = this.getDragEvent(mouseMove);
432
+ this.onDragEmitter.fire(dragEvent);
433
+ }
434
+ }
435
+
436
+ private _disposed = false;
437
+
438
+ private _promise?: Promise<void>;
439
+
440
+ private _resolve?: () => void;
441
+
442
+ private _startPos?: PositionSchema;
443
+
444
+ private _playgroundConfigEntity?: PlaygroundConfigEntity;
445
+ }
446
+
447
+ let dragCache: PlaygroundDrag<any> | undefined;
448
+
449
+ export interface PlaygroundDragEntitiesOpts<T> extends PlaygroundDragOptions<T> {
450
+ // entities?: Entity[]; // 需要拖动的实体,会自动修改 position
451
+ context?: T; // 上下文
452
+ config?: PlaygroundConfigEntity;
453
+ adsorbRefs?: Rectangle[]; // 需要吸附的矩形
454
+ // adsorbLines?: Adsorber.Line[]; // 需要吸附的线
455
+ }
456
+
457
+ /* istanbul ignore next */
458
+ export namespace PlaygroundDrag {
459
+ /**
460
+ * 拖拽实体
461
+ */
462
+ export function startDrag<T>(
463
+ clientX: number,
464
+ clientY: number,
465
+ opts: PlaygroundDragEntitiesOpts<T> = {}
466
+ ): Disposable {
467
+ if (dragCache) {
468
+ dragCache.stop(NaN, NaN);
469
+ }
470
+ // const { entities } = opts;
471
+ // const ableManager = entities && entities.length >= 1 ? entities[0].ableManager : undefined;
472
+ const dragger = (dragCache = new PlaygroundDrag<T>({
473
+ onDragStart(e, ctx): void {
474
+ // if (ableManager) {
475
+ // // 添加拖拽能力
476
+ // entities!.forEach(n => n.addAbles(Dragable));
477
+ // ableManager.dispatch<DragablePayload>(DragablePayload, e);
478
+ // }
479
+ if (opts.onDragStart) opts.onDragStart(e, ctx);
480
+ },
481
+ onDrag(e, ctx): void {
482
+ // if (ableManager) {
483
+ // ableManager.dispatch<DragablePayload>(DragablePayload, {
484
+ // ...e,
485
+ // adsorbRefs: opts.adsorbRefs,
486
+ // adsorbLines: opts.adsorbLines,
487
+ // });
488
+ // }
489
+ if (opts.onDrag) opts.onDrag(e, ctx);
490
+ },
491
+ onDragEnd(e, ctx): void {
492
+ // if (ableManager) {
493
+ // ableManager.dispatch<DragablePayload>(DragablePayload, e);
494
+ // // 去除拖拽能力
495
+ // entities!.forEach(n => n.removeAbles(Dragable));
496
+ // }
497
+ if (opts.onDragEnd) opts.onDragEnd(e, ctx);
498
+ dragger.dispose();
499
+ if (dragCache === dragger) dragCache = undefined;
500
+ },
501
+ }));
502
+ dragger.start(clientX, clientY, opts.config, opts.context);
503
+ return Disposable.create(() => {
504
+ dragger.stop(0, 0);
505
+ dragger.dispose();
506
+ if (dragCache === dragger) {
507
+ dragCache = undefined;
508
+ }
509
+ });
510
+ }
511
+ }