@bpmnkit/editor 0.0.30 → 0.0.32
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/dist/command-stack.d.ts +20 -5
- package/dist/command-stack.js +42 -15
- package/dist/css.d.ts +1 -1
- package/dist/css.js +40 -0
- package/dist/editor.d.ts +60 -0
- package/dist/editor.js +404 -55
- package/dist/hud.js +114 -41
- package/dist/i18n.d.ts +14 -0
- package/dist/i18n.js +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/modeling.d.ts +1 -0
- package/dist/modeling.js +143 -33
- package/dist/overlay.d.ts +1 -1
- package/dist/overlay.js +2 -2
- package/dist/rules.d.ts +19 -5
- package/dist/rules.js +101 -5
- package/dist/state-machine.d.ts +11 -1
- package/dist/state-machine.js +41 -9
- package/dist/types.d.ts +11 -0
- package/package.json +3 -3
package/dist/overlay.js
CHANGED
|
@@ -205,14 +205,14 @@ export class OverlayRenderer {
|
|
|
205
205
|
this._resizePreviewG.appendChild(rect);
|
|
206
206
|
}
|
|
207
207
|
// ── Ghost connection ───────────────────────────────────────────────
|
|
208
|
-
setGhostConnection(waypoints) {
|
|
208
|
+
setGhostConnection(waypoints, invalid = false) {
|
|
209
209
|
this._ghostConnG.innerHTML = "";
|
|
210
210
|
if (!waypoints || waypoints.length < 2)
|
|
211
211
|
return;
|
|
212
212
|
const points = waypoints.map((wp) => `${wp.x},${wp.y}`).join(" ");
|
|
213
213
|
const poly = svgEl("polyline");
|
|
214
214
|
attr(poly, {
|
|
215
|
-
class: "bpmnkit-ghost-conn",
|
|
215
|
+
class: invalid ? "bpmnkit-ghost-conn bpmnkit-ghost-conn-invalid" : "bpmnkit-ghost-conn",
|
|
216
216
|
points,
|
|
217
217
|
"marker-end": `url(#${this._markerId})`,
|
|
218
218
|
});
|
package/dist/rules.d.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
/** True when `type` is an activity (task family, sub-process, call activity, …). */
|
|
2
|
+
export declare function isActivity(type: string): boolean;
|
|
2
3
|
/**
|
|
3
4
|
* Returns true if a sequence flow from `sourceType` to `targetType` is valid.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* BPMN defaults: an end event has no outgoing flow, a start event has no
|
|
7
|
+
* incoming flow, boundary events attach (they are never a flow *target*), data
|
|
8
|
+
* elements and annotations use associations rather than sequence flows, and an
|
|
9
|
+
* event-based gateway may only target intermediate catch events or receive tasks.
|
|
8
10
|
*/
|
|
9
|
-
export declare function canConnect(sourceType:
|
|
11
|
+
export declare function canConnect(sourceType: string, targetType: string): boolean;
|
|
12
|
+
/** Returns true if a boundary event may attach to a host of `hostType` (activities only). */
|
|
13
|
+
export declare function canAttach(hostType: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Returns true if a `parentType` container may hold a `childType` element.
|
|
16
|
+
* Containers hold flow nodes; lanes and pools hold flow nodes but not other
|
|
17
|
+
* pools; data/annotation elements and flows are never containers.
|
|
18
|
+
*/
|
|
19
|
+
export declare function canContain(parentType: string, childType: string): boolean;
|
|
20
|
+
/** Returns true if an element of `type` supports interactive resizing. */
|
|
21
|
+
export declare function canResize(type: string): boolean;
|
|
22
|
+
/** Returns true if an element may be morphed from `fromType` into `toType` (same category). */
|
|
23
|
+
export declare function canMorph(fromType: string, toType: string): boolean;
|
|
10
24
|
//# sourceMappingURL=rules.d.ts.map
|
package/dist/rules.js
CHANGED
|
@@ -1,17 +1,113 @@
|
|
|
1
|
+
import { RESIZABLE_TYPES } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* BPMN modeling rules — a small, dependency-free rule set matching the intent of
|
|
4
|
+
* bpmn-js's `BpmnRules`. Each predicate answers a single yes/no question and is
|
|
5
|
+
* consulted by the editor (state machine + HUD) before enabling or committing an
|
|
6
|
+
* action, so illegal edits are refused both visually and at commit time.
|
|
7
|
+
*/
|
|
8
|
+
const ACTIVITY_TYPES = new Set([
|
|
9
|
+
"task",
|
|
10
|
+
"serviceTask",
|
|
11
|
+
"userTask",
|
|
12
|
+
"scriptTask",
|
|
13
|
+
"sendTask",
|
|
14
|
+
"receiveTask",
|
|
15
|
+
"businessRuleTask",
|
|
16
|
+
"manualTask",
|
|
17
|
+
"callActivity",
|
|
18
|
+
"subProcess",
|
|
19
|
+
"adHocSubProcess",
|
|
20
|
+
"eventSubProcess",
|
|
21
|
+
"transaction",
|
|
22
|
+
]);
|
|
23
|
+
const CONTAINER_TYPES = new Set([
|
|
24
|
+
"process",
|
|
25
|
+
"subProcess",
|
|
26
|
+
"adHocSubProcess",
|
|
27
|
+
"eventSubProcess",
|
|
28
|
+
"transaction",
|
|
29
|
+
"participant",
|
|
30
|
+
"lane",
|
|
31
|
+
]);
|
|
32
|
+
const DATA_TYPES = new Set([
|
|
33
|
+
"dataObject",
|
|
34
|
+
"dataObjectReference",
|
|
35
|
+
"dataStoreReference",
|
|
36
|
+
]);
|
|
37
|
+
/** True when `type` is an activity (task family, sub-process, call activity, …). */
|
|
38
|
+
export function isActivity(type) {
|
|
39
|
+
return ACTIVITY_TYPES.has(type);
|
|
40
|
+
}
|
|
41
|
+
function morphCategory(type) {
|
|
42
|
+
if (type.endsWith("Gateway"))
|
|
43
|
+
return "gateway";
|
|
44
|
+
if (type === "startEvent" || type.endsWith("StartEvent"))
|
|
45
|
+
return "startEvent";
|
|
46
|
+
if (type === "endEvent" || type.endsWith("EndEvent"))
|
|
47
|
+
return "endEvent";
|
|
48
|
+
if (type === "intermediateCatchEvent" ||
|
|
49
|
+
type === "intermediateThrowEvent" ||
|
|
50
|
+
type.endsWith("CatchEvent") ||
|
|
51
|
+
type.endsWith("ThrowEvent")) {
|
|
52
|
+
return "intermediateEvent";
|
|
53
|
+
}
|
|
54
|
+
if (ACTIVITY_TYPES.has(type))
|
|
55
|
+
return "activity";
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
1
58
|
/**
|
|
2
59
|
* Returns true if a sequence flow from `sourceType` to `targetType` is valid.
|
|
3
60
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
61
|
+
* BPMN defaults: an end event has no outgoing flow, a start event has no
|
|
62
|
+
* incoming flow, boundary events attach (they are never a flow *target*), data
|
|
63
|
+
* elements and annotations use associations rather than sequence flows, and an
|
|
64
|
+
* event-based gateway may only target intermediate catch events or receive tasks.
|
|
7
65
|
*/
|
|
8
66
|
export function canConnect(sourceType, targetType) {
|
|
9
|
-
|
|
67
|
+
// End events never emit, start events never receive.
|
|
68
|
+
if (sourceType === "endEvent" || sourceType.endsWith("EndEvent"))
|
|
10
69
|
return false;
|
|
11
|
-
if (targetType === "startEvent")
|
|
70
|
+
if (targetType === "startEvent" || targetType.endsWith("StartEvent"))
|
|
12
71
|
return false;
|
|
13
72
|
if (targetType === "boundaryEvent")
|
|
14
73
|
return false;
|
|
74
|
+
// Data elements and text annotations are not sequence-flow endpoints.
|
|
75
|
+
if (DATA_TYPES.has(sourceType) || DATA_TYPES.has(targetType))
|
|
76
|
+
return false;
|
|
77
|
+
if (sourceType === "textAnnotation" || targetType === "textAnnotation")
|
|
78
|
+
return false;
|
|
79
|
+
// Event-based gateway → intermediate catch event or receive task only.
|
|
80
|
+
if (sourceType === "eventBasedGateway") {
|
|
81
|
+
return targetType === "intermediateCatchEvent" || targetType === "receiveTask";
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
/** Returns true if a boundary event may attach to a host of `hostType` (activities only). */
|
|
86
|
+
export function canAttach(hostType) {
|
|
87
|
+
return isActivity(hostType);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Returns true if a `parentType` container may hold a `childType` element.
|
|
91
|
+
* Containers hold flow nodes; lanes and pools hold flow nodes but not other
|
|
92
|
+
* pools; data/annotation elements and flows are never containers.
|
|
93
|
+
*/
|
|
94
|
+
export function canContain(parentType, childType) {
|
|
95
|
+
if (!CONTAINER_TYPES.has(parentType))
|
|
96
|
+
return false;
|
|
97
|
+
// Pools/lanes and sub-processes never contain a participant (pool).
|
|
98
|
+
if (childType === "participant")
|
|
99
|
+
return false;
|
|
15
100
|
return true;
|
|
16
101
|
}
|
|
102
|
+
/** Returns true if an element of `type` supports interactive resizing. */
|
|
103
|
+
export function canResize(type) {
|
|
104
|
+
return RESIZABLE_TYPES.has(type);
|
|
105
|
+
}
|
|
106
|
+
/** Returns true if an element may be morphed from `fromType` into `toType` (same category). */
|
|
107
|
+
export function canMorph(fromType, toType) {
|
|
108
|
+
if (fromType === toType)
|
|
109
|
+
return false;
|
|
110
|
+
const from = morphCategory(fromType);
|
|
111
|
+
return from !== null && from === morphCategory(toType);
|
|
112
|
+
}
|
|
17
113
|
//# sourceMappingURL=rules.js.map
|
package/dist/state-machine.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ type SelectSub = {
|
|
|
69
69
|
isHoriz: boolean;
|
|
70
70
|
projPt: DiagPoint;
|
|
71
71
|
origin: DiagPoint;
|
|
72
|
+
nearMidpoint: boolean;
|
|
72
73
|
screenX: number;
|
|
73
74
|
screenY: number;
|
|
74
75
|
} | {
|
|
@@ -76,6 +77,12 @@ type SelectSub = {
|
|
|
76
77
|
edgeId: string;
|
|
77
78
|
segIdx: number;
|
|
78
79
|
origin: DiagPoint;
|
|
80
|
+
} | {
|
|
81
|
+
name: "dragging-edge-segment";
|
|
82
|
+
edgeId: string;
|
|
83
|
+
segIdx: number;
|
|
84
|
+
isHoriz: boolean;
|
|
85
|
+
origin: DiagPoint;
|
|
79
86
|
} | {
|
|
80
87
|
name: "pointing-edge-waypoint";
|
|
81
88
|
edgeId: string;
|
|
@@ -127,7 +134,7 @@ export interface Callbacks {
|
|
|
127
134
|
cancelTranslate(): void;
|
|
128
135
|
previewResize(bounds: BpmnBounds): void;
|
|
129
136
|
commitResize(id: string, bounds: BpmnBounds): void;
|
|
130
|
-
previewConnect(ghostEnd: DiagPoint): void;
|
|
137
|
+
previewConnect(ghostEnd: DiagPoint, targetId: string | null): void;
|
|
131
138
|
cancelConnect(): void;
|
|
132
139
|
commitConnect(sourceId: string, targetId: string): void;
|
|
133
140
|
previewRubberBand(origin: DiagPoint, current: DiagPoint): void;
|
|
@@ -148,6 +155,9 @@ export interface Callbacks {
|
|
|
148
155
|
previewWaypointMove(edgeId: string, wpIdx: number, pt: DiagPoint): void;
|
|
149
156
|
commitWaypointMove(edgeId: string, wpIdx: number, pt: DiagPoint): void;
|
|
150
157
|
cancelWaypointMove(): void;
|
|
158
|
+
previewSegmentMove(edgeId: string, segIdx: number, isHoriz: boolean, delta: number): void;
|
|
159
|
+
commitSegmentMove(edgeId: string, segIdx: number, isHoriz: boolean, delta: number): void;
|
|
160
|
+
cancelSegmentMove(): void;
|
|
151
161
|
showEdgeHoverDot(pt: DiagPoint): void;
|
|
152
162
|
hideEdgeHoverDot(): void;
|
|
153
163
|
showEdgeWaypointBalls(edgeId: string): void;
|
package/dist/state-machine.js
CHANGED
|
@@ -148,6 +148,7 @@ export class EditorStateMachine {
|
|
|
148
148
|
isHoriz: hit.isHoriz,
|
|
149
149
|
projPt: hit.projPt,
|
|
150
150
|
origin: diag,
|
|
151
|
+
nearMidpoint: hit.nearMidpoint,
|
|
151
152
|
screenX: e.clientX,
|
|
152
153
|
screenY: e.clientY,
|
|
153
154
|
});
|
|
@@ -290,13 +291,13 @@ export class EditorStateMachine {
|
|
|
290
291
|
sourceId: sub.sourceId,
|
|
291
292
|
ghostEnd: diag,
|
|
292
293
|
});
|
|
293
|
-
this._cb.previewConnect(diag);
|
|
294
|
+
this._cb.previewConnect(diag, hit.type === "shape" ? hit.id : null);
|
|
294
295
|
}
|
|
295
296
|
break;
|
|
296
297
|
}
|
|
297
298
|
case "connecting": {
|
|
298
299
|
this._mode = this._withSub({ ...sub, ghostEnd: diag });
|
|
299
|
-
this._cb.previewConnect(diag);
|
|
300
|
+
this._cb.previewConnect(diag, hit.type === "shape" ? hit.id : null);
|
|
300
301
|
break;
|
|
301
302
|
}
|
|
302
303
|
case "pointing-edge-endpoint": {
|
|
@@ -319,13 +320,28 @@ export class EditorStateMachine {
|
|
|
319
320
|
case "pointing-edge-segment": {
|
|
320
321
|
const dist = screenDist(e.clientX, e.clientY, sub.screenX, sub.screenY);
|
|
321
322
|
if (dist > DRAG_THRESHOLD) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
323
|
+
if (sub.nearMidpoint) {
|
|
324
|
+
// Near the segment midpoint → insert a new waypoint.
|
|
325
|
+
this._mode = this._withSub({
|
|
326
|
+
name: "dragging-edge-waypoint-new",
|
|
327
|
+
edgeId: sub.edgeId,
|
|
328
|
+
segIdx: sub.segIdx,
|
|
329
|
+
origin: sub.origin,
|
|
330
|
+
});
|
|
331
|
+
this._cb.previewWaypointInsert(sub.edgeId, sub.segIdx, diag);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// Elsewhere on the segment → move the whole segment orthogonally.
|
|
335
|
+
this._mode = this._withSub({
|
|
336
|
+
name: "dragging-edge-segment",
|
|
337
|
+
edgeId: sub.edgeId,
|
|
338
|
+
segIdx: sub.segIdx,
|
|
339
|
+
isHoriz: sub.isHoriz,
|
|
340
|
+
origin: sub.origin,
|
|
341
|
+
});
|
|
342
|
+
const delta = sub.isHoriz ? diag.y - sub.origin.y : diag.x - sub.origin.x;
|
|
343
|
+
this._cb.previewSegmentMove(sub.edgeId, sub.segIdx, sub.isHoriz, delta);
|
|
344
|
+
}
|
|
329
345
|
}
|
|
330
346
|
break;
|
|
331
347
|
}
|
|
@@ -333,6 +349,11 @@ export class EditorStateMachine {
|
|
|
333
349
|
this._cb.previewWaypointInsert(sub.edgeId, sub.segIdx, diag);
|
|
334
350
|
break;
|
|
335
351
|
}
|
|
352
|
+
case "dragging-edge-segment": {
|
|
353
|
+
const delta = sub.isHoriz ? diag.y - sub.origin.y : diag.x - sub.origin.x;
|
|
354
|
+
this._cb.previewSegmentMove(sub.edgeId, sub.segIdx, sub.isHoriz, delta);
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
336
357
|
case "pointing-edge-waypoint": {
|
|
337
358
|
const dist = screenDist(e.clientX, e.clientY, sub.screenX, sub.screenY);
|
|
338
359
|
if (dist > DRAG_THRESHOLD) {
|
|
@@ -482,6 +503,13 @@ export class EditorStateMachine {
|
|
|
482
503
|
this._mode = this._idle();
|
|
483
504
|
break;
|
|
484
505
|
}
|
|
506
|
+
case "dragging-edge-segment": {
|
|
507
|
+
this._cb.lockViewport(false);
|
|
508
|
+
const delta = sub.isHoriz ? diag.y - sub.origin.y : diag.x - sub.origin.x;
|
|
509
|
+
this._cb.commitSegmentMove(sub.edgeId, sub.segIdx, sub.isHoriz, delta);
|
|
510
|
+
this._mode = this._idle();
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
485
513
|
case "pointing-edge-waypoint": {
|
|
486
514
|
this._cb.lockViewport(false);
|
|
487
515
|
this._cb.setEdgeSelected(sub.edgeId);
|
|
@@ -557,6 +585,10 @@ export class EditorStateMachine {
|
|
|
557
585
|
this._cb.cancelWaypointMove();
|
|
558
586
|
this._cb.lockViewport(false);
|
|
559
587
|
}
|
|
588
|
+
else if (sub.name === "dragging-edge-segment") {
|
|
589
|
+
this._cb.cancelSegmentMove();
|
|
590
|
+
this._cb.lockViewport(false);
|
|
591
|
+
}
|
|
560
592
|
}
|
|
561
593
|
this._mode = { mode: "default", sub: { name: "idle", hoveredId: null } };
|
|
562
594
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CanvasEvents, CanvasOptions } from "@bpmnkit/canvas";
|
|
2
2
|
import type { BpmnDefinitions } from "@bpmnkit/core";
|
|
3
|
+
import type { Translate } from "./i18n.js";
|
|
3
4
|
export type CreateShapeType = "startEvent" | "messageStartEvent" | "timerStartEvent" | "conditionalStartEvent" | "signalStartEvent" | "endEvent" | "messageEndEvent" | "escalationEndEvent" | "errorEndEvent" | "compensationEndEvent" | "signalEndEvent" | "terminateEndEvent" | "intermediateThrowEvent" | "intermediateCatchEvent" | "messageCatchEvent" | "messageThrowEvent" | "timerCatchEvent" | "escalationThrowEvent" | "conditionalCatchEvent" | "linkCatchEvent" | "linkThrowEvent" | "compensationThrowEvent" | "signalCatchEvent" | "signalThrowEvent" | "task" | "serviceTask" | "userTask" | "scriptTask" | "sendTask" | "receiveTask" | "businessRuleTask" | "manualTask" | "callActivity" | "subProcess" | "adHocSubProcess" | "transaction" | "exclusiveGateway" | "parallelGateway" | "inclusiveGateway" | "eventBasedGateway" | "complexGateway" | "textAnnotation";
|
|
4
5
|
/** Element types that support resize handles. */
|
|
5
6
|
export declare const RESIZABLE_TYPES: ReadonlySet<string>;
|
|
@@ -13,6 +14,14 @@ export type EditorOptions = CanvasOptions & {
|
|
|
13
14
|
* localStorage automatically. The stored key is `"bpmnkit-theme"`.
|
|
14
15
|
*/
|
|
15
16
|
persistTheme?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Optional translation hook for the editor's HUD strings (palette labels,
|
|
19
|
+
* banners, keyboard-shortcut names, context-menu actions). Receives the
|
|
20
|
+
* English template as the key plus optional `{name}` interpolation vars and
|
|
21
|
+
* returns the localized string. Defaults to identity (English); return
|
|
22
|
+
* unlocalized keys unchanged.
|
|
23
|
+
*/
|
|
24
|
+
translate?: Translate;
|
|
16
25
|
};
|
|
17
26
|
export interface EditorEvents extends CanvasEvents {
|
|
18
27
|
"diagram:change": (defs: BpmnDefinitions) => void;
|
|
@@ -48,6 +57,8 @@ export type HitResult = {
|
|
|
48
57
|
segIdx: number;
|
|
49
58
|
isHoriz: boolean;
|
|
50
59
|
projPt: DiagPoint;
|
|
60
|
+
/** True when the press is near the segment midpoint (→ insert a waypoint). */
|
|
61
|
+
nearMidpoint: boolean;
|
|
51
62
|
} | {
|
|
52
63
|
type: "edge-waypoint";
|
|
53
64
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bpmnkit/editor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"dist/**/*.d.ts"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@bpmnkit/canvas": "0.0.
|
|
21
|
-
"@bpmnkit/core": "0.
|
|
20
|
+
"@bpmnkit/canvas": "0.0.29",
|
|
21
|
+
"@bpmnkit/core": "0.1.1"
|
|
22
22
|
},
|
|
23
23
|
"description": "Full-featured interactive BPMN editor with undo/redo, HUD, and side-dock UI",
|
|
24
24
|
"keywords": [
|