@bpmnkit/editor 0.0.35 → 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/dist/chrome.d.ts +24 -0
- package/dist/chrome.js +75 -0
- package/dist/css.d.ts +5 -4
- package/dist/css.js +231 -357
- package/dist/dock.js +58 -80
- package/dist/editor.d.ts +30 -2
- package/dist/editor.js +123 -84
- package/dist/headless.d.ts +21 -0
- package/dist/headless.js +17 -0
- package/dist/id.d.ts +25 -0
- package/dist/id.js +39 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/inject.d.ts +3 -0
- package/dist/inject.js +12 -0
- package/dist/modal.js +33 -53
- package/dist/modeling.d.ts +7 -6
- package/dist/modeling.js +28 -28
- package/dist/ops.d.ts +158 -0
- package/dist/ops.js +82 -0
- package/dist/types.d.ts +16 -0
- package/package.json +7 -3
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's DOM-free surface: operations, and the ids they mint.
|
|
3
|
+
*
|
|
4
|
+
* `@bpmnkit/editor` itself reaches for `document` the moment it is imported —
|
|
5
|
+
* it is an editor. But the part that decides *what an edit does* never touches
|
|
6
|
+
* the DOM, and that part has to run in two more places: a Durable Object
|
|
7
|
+
* replaying a writer's op to verify it, and a watcher applying the same op
|
|
8
|
+
* without an editor loaded at all.
|
|
9
|
+
*
|
|
10
|
+
* So this entry exists to be importable from a Worker. Everything reachable
|
|
11
|
+
* from here is pure, and the subpath keeps it that way — importing the package
|
|
12
|
+
* root instead would pull the canvas in behind it.
|
|
13
|
+
*/
|
|
14
|
+
export { applyOp } from "./ops.js";
|
|
15
|
+
export type { EditorOp, OpResult, ShapeMove } from "./ops.js";
|
|
16
|
+
export { createIdFactory, genId, newIdSeed } from "./id.js";
|
|
17
|
+
export type { IdFactory } from "./id.js";
|
|
18
|
+
export { createEmptyDefinitions } from "./modeling.js";
|
|
19
|
+
export type { Clipboard } from "./modeling.js";
|
|
20
|
+
export type { CreateShapeType, PortDir } from "./types.js";
|
|
21
|
+
//# sourceMappingURL=headless.d.ts.map
|
package/dist/headless.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's DOM-free surface: operations, and the ids they mint.
|
|
3
|
+
*
|
|
4
|
+
* `@bpmnkit/editor` itself reaches for `document` the moment it is imported —
|
|
5
|
+
* it is an editor. But the part that decides *what an edit does* never touches
|
|
6
|
+
* the DOM, and that part has to run in two more places: a Durable Object
|
|
7
|
+
* replaying a writer's op to verify it, and a watcher applying the same op
|
|
8
|
+
* without an editor loaded at all.
|
|
9
|
+
*
|
|
10
|
+
* So this entry exists to be importable from a Worker. Everything reachable
|
|
11
|
+
* from here is pure, and the subpath keeps it that way — importing the package
|
|
12
|
+
* root instead would pull the canvas in behind it.
|
|
13
|
+
*/
|
|
14
|
+
export { applyOp } from "./ops.js";
|
|
15
|
+
export { createIdFactory, genId, newIdSeed } from "./id.js";
|
|
16
|
+
export { createEmptyDefinitions } from "./modeling.js";
|
|
17
|
+
//# sourceMappingURL=headless.js.map
|
package/dist/id.d.ts
CHANGED
|
@@ -1,2 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How new element ids are minted.
|
|
3
|
+
*
|
|
4
|
+
* Normally this is `Math.random()`, which is exactly right for one person
|
|
5
|
+
* editing one diagram. It is wrong the moment the same edit has to happen twice
|
|
6
|
+
* — replayed on a server, or on someone else's screen — because two runs would
|
|
7
|
+
* invent two different ids for the same new task.
|
|
8
|
+
*
|
|
9
|
+
* So the minting functions in `modeling.ts` take an optional {@link IdFactory}.
|
|
10
|
+
* An operation carries a seed, both sides build the same factory from it, and
|
|
11
|
+
* the same edit produces byte-identical XML wherever it runs.
|
|
12
|
+
*/
|
|
13
|
+
/** Mints an id for a new element, given the prefix its type wants. */
|
|
14
|
+
export type IdFactory = (prefix: string) => string;
|
|
15
|
+
/** The default: unique enough for one editor, different on every call. */
|
|
1
16
|
export declare function genId(prefix: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* A factory that yields the same sequence of ids for the same seed.
|
|
19
|
+
*
|
|
20
|
+
* Deterministic *in call order*, which is the contract an operation replay
|
|
21
|
+
* relies on: the same function, given the same arguments, asks for the same
|
|
22
|
+
* prefixes in the same order, so it gets the same ids back.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createIdFactory(seed: string): IdFactory;
|
|
25
|
+
/** A seed with enough entropy that two operations cannot mint the same ids. */
|
|
26
|
+
export declare function newIdSeed(): string;
|
|
2
27
|
//# sourceMappingURL=id.d.ts.map
|
package/dist/id.js
CHANGED
|
@@ -1,4 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How new element ids are minted.
|
|
3
|
+
*
|
|
4
|
+
* Normally this is `Math.random()`, which is exactly right for one person
|
|
5
|
+
* editing one diagram. It is wrong the moment the same edit has to happen twice
|
|
6
|
+
* — replayed on a server, or on someone else's screen — because two runs would
|
|
7
|
+
* invent two different ids for the same new task.
|
|
8
|
+
*
|
|
9
|
+
* So the minting functions in `modeling.ts` take an optional {@link IdFactory}.
|
|
10
|
+
* An operation carries a seed, both sides build the same factory from it, and
|
|
11
|
+
* the same edit produces byte-identical XML wherever it runs.
|
|
12
|
+
*/
|
|
13
|
+
/** The default: unique enough for one editor, different on every call. */
|
|
1
14
|
export function genId(prefix) {
|
|
2
15
|
return `${prefix}_${Math.random().toString(36).slice(2, 9)}`;
|
|
3
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* A factory that yields the same sequence of ids for the same seed.
|
|
19
|
+
*
|
|
20
|
+
* Deterministic *in call order*, which is the contract an operation replay
|
|
21
|
+
* relies on: the same function, given the same arguments, asks for the same
|
|
22
|
+
* prefixes in the same order, so it gets the same ids back.
|
|
23
|
+
*/
|
|
24
|
+
export function createIdFactory(seed) {
|
|
25
|
+
// FNV-1a over the seed, then xorshift32 per id. Small, dependency-free, and
|
|
26
|
+
// the output is shaped like `genId`'s so nothing downstream can tell them apart.
|
|
27
|
+
let state = 2166136261;
|
|
28
|
+
for (let i = 0; i < seed.length; i++) {
|
|
29
|
+
state ^= seed.charCodeAt(i);
|
|
30
|
+
state = Math.imul(state, 16777619);
|
|
31
|
+
}
|
|
32
|
+
return (prefix) => {
|
|
33
|
+
state ^= state << 13;
|
|
34
|
+
state ^= state >>> 17;
|
|
35
|
+
state ^= state << 5;
|
|
36
|
+
return `${prefix}_${(state >>> 0).toString(36).padStart(7, "0").slice(0, 7)}`;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** A seed with enough entropy that two operations cannot mint the same ids. */
|
|
40
|
+
export function newIdSeed() {
|
|
41
|
+
return Math.random().toString(36).slice(2, 12);
|
|
42
|
+
}
|
|
4
43
|
//# sourceMappingURL=id.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export { BpmnEditor } from "./editor.js";
|
|
2
|
+
export { CHROME_CSS, CHROME_STYLE_ID, injectChromeStyles } from "./chrome.js";
|
|
3
|
+
export { injectStyle } from "./inject.js";
|
|
2
4
|
export { createEmptyDefinitions } from "./modeling.js";
|
|
5
|
+
export { applyOp } from "./ops.js";
|
|
6
|
+
export type { EditorOp, OpResult, ShapeMove } from "./ops.js";
|
|
7
|
+
export { createIdFactory, genId, newIdSeed } from "./id.js";
|
|
8
|
+
export type { IdFactory } from "./id.js";
|
|
3
9
|
export type { EditorEvents, EditorOptions, LabelPosition, Tool, CreateShapeType, HandleDir, PortDir, } from "./types.js";
|
|
4
10
|
export { ELEMENT_GROUPS, ELEMENT_TYPE_LABELS, EXTERNAL_LABEL_TYPES, CONTEXTUAL_ADD_TYPES, getElementGroup, getValidLabelPositions, } from "./element-groups.js";
|
|
5
11
|
export type { ElementGroup } from "./element-groups.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { BpmnEditor } from "./editor.js";
|
|
2
|
+
export { CHROME_CSS, CHROME_STYLE_ID, injectChromeStyles } from "./chrome.js";
|
|
3
|
+
export { injectStyle } from "./inject.js";
|
|
2
4
|
export { createEmptyDefinitions } from "./modeling.js";
|
|
5
|
+
export { applyOp } from "./ops.js";
|
|
6
|
+
export { createIdFactory, genId, newIdSeed } from "./id.js";
|
|
3
7
|
export { ELEMENT_GROUPS, ELEMENT_TYPE_LABELS, EXTERNAL_LABEL_TYPES, CONTEXTUAL_ADD_TYPES, getElementGroup, getValidLabelPositions, } from "./element-groups.js";
|
|
4
8
|
export { initEditorHud } from "./hud.js";
|
|
5
9
|
export { createTranslationRecorder, defaultTranslate, interpolate } from "./i18n.js";
|
package/dist/inject.d.ts
ADDED
package/dist/inject.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Injects a CSS string into `<head>` exactly once, keyed by id. */
|
|
2
|
+
export function injectStyle(id, css) {
|
|
3
|
+
if (typeof document === "undefined")
|
|
4
|
+
return;
|
|
5
|
+
if (document.getElementById(id))
|
|
6
|
+
return;
|
|
7
|
+
const style = document.createElement("style");
|
|
8
|
+
style.id = id;
|
|
9
|
+
style.textContent = css;
|
|
10
|
+
document.head.appendChild(style);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=inject.js.map
|
package/dist/modal.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { injectChromeStyles } from "./chrome.js";
|
|
1
2
|
const MODAL_STYLE_ID = "bpmnkit-hud-modal-styles";
|
|
2
3
|
function injectModalStyles() {
|
|
4
|
+
injectChromeStyles();
|
|
3
5
|
if (document.getElementById(MODAL_STYLE_ID))
|
|
4
6
|
return;
|
|
5
7
|
const style = document.createElement("style");
|
|
@@ -7,76 +9,54 @@ function injectModalStyles() {
|
|
|
7
9
|
style.textContent = `
|
|
8
10
|
.bpmnkit-hud-modal-overlay {
|
|
9
11
|
position: fixed; inset: 0; z-index: 300;
|
|
10
|
-
background:
|
|
12
|
+
background: color-mix(in srgb, var(--bpmnkit-ds-dark, #14161a) 55%, transparent);
|
|
11
13
|
display: flex; align-items: center; justify-content: center;
|
|
12
14
|
}
|
|
13
15
|
.bpmnkit-hud-modal {
|
|
14
|
-
background:
|
|
15
|
-
border: 1px solid rgba(255,255,255,0.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
box-shadow: 0 16px 48px rgba(0,0,0,0.6);
|
|
22
|
-
display: flex; flex-direction: column; gap: 12px;
|
|
16
|
+
background: var(--bpmnkit-chrome-ground, #ffffff);
|
|
17
|
+
border: 1px solid var(--bpmnkit-chrome-line, rgba(255, 255, 255, 0.14));
|
|
18
|
+
padding: 24px 26px;
|
|
19
|
+
min-width: 320px; max-width: 90vw;
|
|
20
|
+
color: var(--bpmnkit-chrome-ink-2, #c8ccd2);
|
|
21
|
+
font-family: var(--bpmnkit-ds-font-sans, system-ui, -apple-system, sans-serif);
|
|
22
|
+
display: flex; flex-direction: column; gap: 14px;
|
|
23
23
|
}
|
|
24
24
|
.bpmnkit-hud-modal-title {
|
|
25
|
-
font-
|
|
26
|
-
|
|
25
|
+
font-family: var(--bpmnkit-ds-font-mono, ui-monospace, monospace);
|
|
26
|
+
font-size: var(--bpmnkit-ds-t-mono-micro, 10.5px); letter-spacing: 0.12em;
|
|
27
|
+
text-transform: uppercase; color: var(--bpmnkit-chrome-ink-4, #9aa1aa);
|
|
27
28
|
}
|
|
28
29
|
.bpmnkit-hud-modal-input {
|
|
29
|
-
width: 100%; padding:
|
|
30
|
-
background:
|
|
31
|
-
border: 1px solid rgba(255,255,255,0.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
font-size:
|
|
30
|
+
width: 100%; padding: 7px 9px;
|
|
31
|
+
background: transparent;
|
|
32
|
+
border: 1px solid var(--bpmnkit-chrome-line, rgba(255, 255, 255, 0.14));
|
|
33
|
+
color: var(--bpmnkit-chrome-ink, #f4f5f7);
|
|
34
|
+
font-family: var(--bpmnkit-ds-font-mono, ui-monospace, monospace);
|
|
35
|
+
font-size: 12.5px;
|
|
35
36
|
outline: none;
|
|
36
37
|
box-sizing: border-box;
|
|
37
38
|
}
|
|
38
|
-
.bpmnkit-hud-modal-input:focus { border-color:
|
|
39
|
-
.bpmnkit-hud-modal-actions { display: flex; gap:
|
|
39
|
+
.bpmnkit-hud-modal-input:focus { border-color: var(--bpmnkit-chrome-accent, #c9755c); }
|
|
40
|
+
.bpmnkit-hud-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
|
|
40
41
|
.bpmnkit-hud-modal-btn {
|
|
41
|
-
font-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
font-family: var(--bpmnkit-ds-font-mono, ui-monospace, monospace);
|
|
43
|
+
font-size: 12px; padding: 7px 16px;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
border: 1px solid var(--bpmnkit-chrome-line, rgba(255, 255, 255, 0.14));
|
|
46
|
+
background: transparent;
|
|
47
|
+
color: var(--bpmnkit-chrome-ink-2, #c8ccd2);
|
|
46
48
|
}
|
|
47
|
-
.bpmnkit-hud-modal-btn:hover { background: rgba(255,255,255,0.
|
|
49
|
+
.bpmnkit-hud-modal-btn:hover { background: var(--bpmnkit-chrome-hover, rgba(255,255,255,0.07)); color: var(--bpmnkit-chrome-ink, #f4f5f7); }
|
|
48
50
|
.bpmnkit-hud-modal-btn--primary {
|
|
49
|
-
background:
|
|
50
|
-
border-color:
|
|
51
|
+
background: var(--bpmnkit-ds-accent, #a8503a);
|
|
52
|
+
border-color: var(--bpmnkit-ds-accent, #a8503a);
|
|
51
53
|
color: #fff;
|
|
52
54
|
}
|
|
53
|
-
.bpmnkit-hud-modal-btn--primary:hover {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
background: rgba(252,252,254,0.98);
|
|
57
|
-
border-color: rgba(0,0,0,0.1);
|
|
58
|
-
color: rgba(0,0,0,0.8);
|
|
59
|
-
box-shadow: 0 8px 32px rgba(0,0,0,0.15);
|
|
60
|
-
}
|
|
61
|
-
[data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-title { color: rgba(0,0,0,0.88); }
|
|
62
|
-
[data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-input {
|
|
63
|
-
background: rgba(0,0,0,0.04);
|
|
64
|
-
border-color: rgba(0,0,0,0.15);
|
|
65
|
-
color: rgba(0,0,0,0.9);
|
|
66
|
-
}
|
|
67
|
-
[data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-input:focus { border-color: rgba(0,80,200,0.4); }
|
|
68
|
-
[data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn {
|
|
69
|
-
border-color: rgba(0,0,0,0.12);
|
|
70
|
-
background: rgba(0,0,0,0.04);
|
|
71
|
-
color: rgba(0,0,0,0.7);
|
|
72
|
-
}
|
|
73
|
-
[data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn:hover { background: rgba(0,0,0,0.08); }
|
|
74
|
-
[data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn--primary {
|
|
75
|
-
background: rgba(0,80,200,0.85);
|
|
76
|
-
border-color: rgba(0,80,200,0.9);
|
|
55
|
+
.bpmnkit-hud-modal-btn--primary:hover {
|
|
56
|
+
background: var(--bpmnkit-ds-accent-hover, #8f412e);
|
|
57
|
+
border-color: var(--bpmnkit-ds-accent-hover, #8f412e);
|
|
77
58
|
color: #fff;
|
|
78
59
|
}
|
|
79
|
-
[data-bpmnkit-hud-theme="light"] .bpmnkit-hud-modal-btn--primary:hover { background: rgba(0,80,200,1); }
|
|
80
60
|
`;
|
|
81
61
|
document.head.appendChild(style);
|
|
82
62
|
}
|
package/dist/modeling.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { BpmnBounds, BpmnDefinitions, BpmnDiEdge, BpmnDiShape, BpmnFlowElement, BpmnSequenceFlow, BpmnWaypoint, DiColor } from "@bpmnkit/core";
|
|
2
|
+
import { type IdFactory } from "./id.js";
|
|
2
3
|
import type { CreateShapeType, PortDir } from "./types.js";
|
|
3
4
|
/** Creates a minimal valid BpmnDefinitions with one process and one diagram. */
|
|
4
5
|
export declare function createEmptyDefinitions(): BpmnDefinitions;
|
|
5
|
-
export declare function createShape(defs: BpmnDefinitions, type: CreateShapeType, bounds: BpmnBounds, name?: string): {
|
|
6
|
+
export declare function createShape(defs: BpmnDefinitions, type: CreateShapeType, bounds: BpmnBounds, name?: string, ids?: IdFactory): {
|
|
6
7
|
defs: BpmnDefinitions;
|
|
7
8
|
id: string;
|
|
8
9
|
};
|
|
9
|
-
export declare function createBoundaryEvent(defs: BpmnDefinitions, hostId: string, eventDefType: string | null, bounds: BpmnBounds, cancelActivity?: boolean): {
|
|
10
|
+
export declare function createBoundaryEvent(defs: BpmnDefinitions, hostId: string, eventDefType: string | null, bounds: BpmnBounds, cancelActivity?: boolean, ids?: IdFactory): {
|
|
10
11
|
defs: BpmnDefinitions;
|
|
11
12
|
id: string;
|
|
12
13
|
};
|
|
13
|
-
export declare function createConnection(defs: BpmnDefinitions, sourceId: string, targetId: string, waypoints: BpmnWaypoint[]): {
|
|
14
|
+
export declare function createConnection(defs: BpmnDefinitions, sourceId: string, targetId: string, waypoints: BpmnWaypoint[], ids?: IdFactory): {
|
|
14
15
|
defs: BpmnDefinitions;
|
|
15
16
|
id: string;
|
|
16
17
|
};
|
|
@@ -74,16 +75,16 @@ export interface Clipboard {
|
|
|
74
75
|
edges: BpmnDiEdge[];
|
|
75
76
|
}
|
|
76
77
|
export declare function copyElements(defs: BpmnDefinitions, ids: string[]): Clipboard;
|
|
77
|
-
export declare function pasteElements(defs: BpmnDefinitions, clipboard: Clipboard, offsetX: number, offsetY: number): {
|
|
78
|
+
export declare function pasteElements(defs: BpmnDefinitions, clipboard: Clipboard, offsetX: number, offsetY: number, ids?: IdFactory): {
|
|
78
79
|
defs: BpmnDefinitions;
|
|
79
80
|
newIds: Map<string, string>;
|
|
80
81
|
topLevelIds: string[];
|
|
81
82
|
};
|
|
82
|
-
export declare function createAnnotation(defs: BpmnDefinitions, bounds: BpmnBounds, text?: string): {
|
|
83
|
+
export declare function createAnnotation(defs: BpmnDefinitions, bounds: BpmnBounds, text?: string, ids?: IdFactory): {
|
|
83
84
|
defs: BpmnDefinitions;
|
|
84
85
|
id: string;
|
|
85
86
|
};
|
|
86
|
-
export declare function createAnnotationWithLink(defs: BpmnDefinitions, bounds: BpmnBounds, sourceId: string, sourceBounds: BpmnBounds, text?: string): {
|
|
87
|
+
export declare function createAnnotationWithLink(defs: BpmnDefinitions, bounds: BpmnBounds, sourceId: string, sourceBounds: BpmnBounds, text?: string, ids?: IdFactory): {
|
|
87
88
|
defs: BpmnDefinitions;
|
|
88
89
|
annotationId: string;
|
|
89
90
|
associationId: string;
|
package/dist/modeling.js
CHANGED
|
@@ -338,9 +338,9 @@ function updateRefInElements(flowElements, id, updateFn) {
|
|
|
338
338
|
});
|
|
339
339
|
}
|
|
340
340
|
// ── Create shape ─────────────────────────────────────────────────────────────
|
|
341
|
-
export function createShape(defs, type, bounds, name) {
|
|
342
|
-
const id =
|
|
343
|
-
const shapeId =
|
|
341
|
+
export function createShape(defs, type, bounds, name, ids = genId) {
|
|
342
|
+
const id = ids(type);
|
|
343
|
+
const shapeId = ids(`${type}_di`);
|
|
344
344
|
const flowElement = makeFlowElement(type, id, name);
|
|
345
345
|
const process = defs.processes[0];
|
|
346
346
|
if (!process)
|
|
@@ -382,9 +382,9 @@ export function createShape(defs, type, bounds, name) {
|
|
|
382
382
|
return { defs: newDefs, id };
|
|
383
383
|
}
|
|
384
384
|
// ── Create boundary event ─────────────────────────────────────────────────────
|
|
385
|
-
export function createBoundaryEvent(defs, hostId, eventDefType, bounds, cancelActivity = true) {
|
|
386
|
-
const id =
|
|
387
|
-
const shapeId =
|
|
385
|
+
export function createBoundaryEvent(defs, hostId, eventDefType, bounds, cancelActivity = true, ids = genId) {
|
|
386
|
+
const id = ids("BoundaryEvent");
|
|
387
|
+
const shapeId = ids("BoundaryEvent_di");
|
|
388
388
|
const process = defs.processes[0];
|
|
389
389
|
if (!process)
|
|
390
390
|
return { defs, id };
|
|
@@ -434,9 +434,9 @@ export function createBoundaryEvent(defs, hostId, eventDefType, bounds, cancelAc
|
|
|
434
434
|
};
|
|
435
435
|
}
|
|
436
436
|
// ── Create connection ─────────────────────────────────────────────────────────
|
|
437
|
-
export function createConnection(defs, sourceId, targetId, waypoints) {
|
|
438
|
-
const id =
|
|
439
|
-
const edgeId =
|
|
437
|
+
export function createConnection(defs, sourceId, targetId, waypoints, ids = genId) {
|
|
438
|
+
const id = ids("Flow");
|
|
439
|
+
const edgeId = ids("Flow_di");
|
|
440
440
|
const process = defs.processes[0];
|
|
441
441
|
if (!process)
|
|
442
442
|
return { defs, id };
|
|
@@ -1373,19 +1373,19 @@ export function copyElements(defs, ids) {
|
|
|
1373
1373
|
return { elements, flows, shapes, edges };
|
|
1374
1374
|
}
|
|
1375
1375
|
/** Assigns a fresh id to `el` and every descendant / nested edge, recording the mapping. */
|
|
1376
|
-
function buildIdMap(el, map) {
|
|
1377
|
-
map.set(el.id,
|
|
1376
|
+
function buildIdMap(el, map, ids) {
|
|
1377
|
+
map.set(el.id, ids(el.type));
|
|
1378
1378
|
if (hasChildren(el)) {
|
|
1379
1379
|
for (const child of el.flowElements)
|
|
1380
|
-
buildIdMap(child, map);
|
|
1380
|
+
buildIdMap(child, map, ids);
|
|
1381
1381
|
for (const sf of el.sequenceFlows)
|
|
1382
|
-
map.set(sf.id,
|
|
1382
|
+
map.set(sf.id, ids("Flow"));
|
|
1383
1383
|
for (const ta of el.textAnnotations)
|
|
1384
|
-
map.set(ta.id,
|
|
1384
|
+
map.set(ta.id, ids("TextAnnotation"));
|
|
1385
1385
|
for (const a of el.associations)
|
|
1386
|
-
map.set(a.id,
|
|
1386
|
+
map.set(a.id, ids("Association"));
|
|
1387
1387
|
for (const g of el.groups)
|
|
1388
|
-
map.set(g.id,
|
|
1388
|
+
map.set(g.id, ids("Group"));
|
|
1389
1389
|
}
|
|
1390
1390
|
}
|
|
1391
1391
|
/** Deep-clones `el`, remapping its own id, refs, and all nested children through `map`. */
|
|
@@ -1424,13 +1424,13 @@ function remapFlow(sf, map) {
|
|
|
1424
1424
|
targetRef: map.get(sf.targetRef) ?? sf.targetRef,
|
|
1425
1425
|
};
|
|
1426
1426
|
}
|
|
1427
|
-
export function pasteElements(defs, clipboard, offsetX, offsetY) {
|
|
1427
|
+
export function pasteElements(defs, clipboard, offsetX, offsetY, ids = genId) {
|
|
1428
1428
|
const newIds = new Map();
|
|
1429
1429
|
// Generate new IDs for every element (recursively) and top-level flow.
|
|
1430
1430
|
for (const el of clipboard.elements)
|
|
1431
|
-
buildIdMap(el, newIds);
|
|
1431
|
+
buildIdMap(el, newIds, ids);
|
|
1432
1432
|
for (const sf of clipboard.flows)
|
|
1433
|
-
newIds.set(sf.id,
|
|
1433
|
+
newIds.set(sf.id, ids("Flow"));
|
|
1434
1434
|
const process = defs.processes[0];
|
|
1435
1435
|
const diagram = defs.diagrams[0];
|
|
1436
1436
|
if (!process || !diagram)
|
|
@@ -1442,7 +1442,7 @@ export function pasteElements(defs, clipboard, offsetX, offsetY) {
|
|
|
1442
1442
|
const newElId = newIds.get(s.bpmnElement) ?? s.bpmnElement;
|
|
1443
1443
|
return {
|
|
1444
1444
|
...s,
|
|
1445
|
-
id:
|
|
1445
|
+
id: ids(`${newElId}_di`),
|
|
1446
1446
|
bpmnElement: newElId,
|
|
1447
1447
|
bounds: {
|
|
1448
1448
|
...s.bounds,
|
|
@@ -1456,7 +1456,7 @@ export function pasteElements(defs, clipboard, offsetX, offsetY) {
|
|
|
1456
1456
|
const newFlowId = newIds.get(e.bpmnElement) ?? e.bpmnElement;
|
|
1457
1457
|
return {
|
|
1458
1458
|
...e,
|
|
1459
|
-
id:
|
|
1459
|
+
id: ids(`${newFlowId}_di`),
|
|
1460
1460
|
bpmnElement: newFlowId,
|
|
1461
1461
|
waypoints: e.waypoints.map((wp) => ({
|
|
1462
1462
|
x: wp.x + offsetX,
|
|
@@ -1492,9 +1492,9 @@ export function pasteElements(defs, clipboard, offsetX, offsetY) {
|
|
|
1492
1492
|
return { defs: newDefs, newIds, topLevelIds };
|
|
1493
1493
|
}
|
|
1494
1494
|
// ── Create text annotation ────────────────────────────────────────────────────
|
|
1495
|
-
export function createAnnotation(defs, bounds, text) {
|
|
1496
|
-
const id =
|
|
1497
|
-
const shapeId =
|
|
1495
|
+
export function createAnnotation(defs, bounds, text, ids = genId) {
|
|
1496
|
+
const id = ids("TextAnnotation");
|
|
1497
|
+
const shapeId = ids("TextAnnotation_di");
|
|
1498
1498
|
const annotation = { id, text, unknownAttributes: {} };
|
|
1499
1499
|
const diShape = { id: shapeId, bpmnElement: id, bounds, unknownAttributes: {} };
|
|
1500
1500
|
const process = defs.processes[0];
|
|
@@ -1521,11 +1521,11 @@ export function createAnnotation(defs, bounds, text) {
|
|
|
1521
1521
|
id,
|
|
1522
1522
|
};
|
|
1523
1523
|
}
|
|
1524
|
-
export function createAnnotationWithLink(defs, bounds, sourceId, sourceBounds, text) {
|
|
1525
|
-
const annotResult = createAnnotation(defs, bounds, text);
|
|
1524
|
+
export function createAnnotationWithLink(defs, bounds, sourceId, sourceBounds, text, ids = genId) {
|
|
1525
|
+
const annotResult = createAnnotation(defs, bounds, text, ids);
|
|
1526
1526
|
const annotationId = annotResult.id;
|
|
1527
|
-
const assocId =
|
|
1528
|
-
const edgeId =
|
|
1527
|
+
const assocId = ids("Association");
|
|
1528
|
+
const edgeId = ids("Association_di");
|
|
1529
1529
|
const assoc = {
|
|
1530
1530
|
id: assocId,
|
|
1531
1531
|
sourceRef: sourceId,
|
package/dist/ops.d.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's operations: what a change *was*, rather than what it produced.
|
|
3
|
+
*
|
|
4
|
+
* `diagram:change` hands out the whole new document, which is all a single
|
|
5
|
+
* editor needs. Sending that over a wire on every keystroke is not: a
|
|
6
|
+
* `moveShapes` op is a few hundred bytes where a serialised `BpmnDefinitions`
|
|
7
|
+
* is hundreds of kilobytes. So every edit also describes itself, and
|
|
8
|
+
* {@link applyOp} turns the description back into the edit.
|
|
9
|
+
*
|
|
10
|
+
* **`applyOp` is the only way the editor performs these edits**, locally as well
|
|
11
|
+
* as on replay. That is deliberate: if the editor composed the modeling calls
|
|
12
|
+
* itself and `applyOp` composed them again, the two could drift, and the whole
|
|
13
|
+
* point is that the same op yields byte-identical XML wherever it runs. Ids are
|
|
14
|
+
* the other half of that promise — creating ops carry a seed rather than
|
|
15
|
+
* inventing ids at random, so a replay mints the same ones (see `id.ts`).
|
|
16
|
+
*/
|
|
17
|
+
import type { BpmnBounds, BpmnDefinitions, BpmnWaypoint, DiColor } from "@bpmnkit/core";
|
|
18
|
+
import { type Clipboard } from "./modeling.js";
|
|
19
|
+
import type { CreateShapeType, PortDir } from "./types.js";
|
|
20
|
+
/** One shape's displacement. */
|
|
21
|
+
export interface ShapeMove {
|
|
22
|
+
id: string;
|
|
23
|
+
dx: number;
|
|
24
|
+
dy: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A single editor edit.
|
|
28
|
+
*
|
|
29
|
+
* `seed` appears on every op that creates something: it is what makes the new
|
|
30
|
+
* element's id a property of the op rather than of the machine that ran it.
|
|
31
|
+
*/
|
|
32
|
+
export type EditorOp = {
|
|
33
|
+
kind: "createShape";
|
|
34
|
+
type: CreateShapeType;
|
|
35
|
+
bounds: BpmnBounds;
|
|
36
|
+
name?: string;
|
|
37
|
+
onEdge?: string;
|
|
38
|
+
seed: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "createBoundaryEvent";
|
|
41
|
+
hostId: string;
|
|
42
|
+
eventDefType: string | null;
|
|
43
|
+
bounds: BpmnBounds;
|
|
44
|
+
seed: string;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "createAnnotation";
|
|
47
|
+
bounds: BpmnBounds;
|
|
48
|
+
text?: string;
|
|
49
|
+
seed: string;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "createAnnotationFor";
|
|
52
|
+
sourceId: string;
|
|
53
|
+
bounds: BpmnBounds;
|
|
54
|
+
sourceBounds: BpmnBounds;
|
|
55
|
+
seed: string;
|
|
56
|
+
} | {
|
|
57
|
+
kind: "createConnection";
|
|
58
|
+
sourceId: string;
|
|
59
|
+
targetId: string;
|
|
60
|
+
waypoints: BpmnWaypoint[];
|
|
61
|
+
seed: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A shape and the flow reaching it, as one edit — the "add element" affordance.
|
|
65
|
+
*
|
|
66
|
+
* Placement and routing are decided by the editor, from shapes only it can see,
|
|
67
|
+
* and travel in the op: a replay that recomputed them would need the same
|
|
68
|
+
* obstacles on screen to land in the same place.
|
|
69
|
+
*/
|
|
70
|
+
| {
|
|
71
|
+
kind: "createConnected";
|
|
72
|
+
sourceId: string;
|
|
73
|
+
type: CreateShapeType;
|
|
74
|
+
name?: string;
|
|
75
|
+
bounds: BpmnBounds;
|
|
76
|
+
waypoints: BpmnWaypoint[];
|
|
77
|
+
seed: string;
|
|
78
|
+
} | {
|
|
79
|
+
kind: "paste";
|
|
80
|
+
clipboard: Clipboard;
|
|
81
|
+
offsetX: number;
|
|
82
|
+
offsetY: number;
|
|
83
|
+
seed: string;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "move";
|
|
86
|
+
moves: ShapeMove[];
|
|
87
|
+
onEdge?: {
|
|
88
|
+
edgeId: string;
|
|
89
|
+
shapeId: string;
|
|
90
|
+
};
|
|
91
|
+
} | {
|
|
92
|
+
kind: "resize";
|
|
93
|
+
id: string;
|
|
94
|
+
bounds: BpmnBounds;
|
|
95
|
+
} | {
|
|
96
|
+
kind: "delete";
|
|
97
|
+
ids: string[];
|
|
98
|
+
} | {
|
|
99
|
+
kind: "rename";
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
} | {
|
|
103
|
+
kind: "labelPosition";
|
|
104
|
+
id: string;
|
|
105
|
+
bounds: BpmnBounds;
|
|
106
|
+
} | {
|
|
107
|
+
kind: "color";
|
|
108
|
+
id: string;
|
|
109
|
+
color: DiColor;
|
|
110
|
+
} | {
|
|
111
|
+
kind: "changeType";
|
|
112
|
+
id: string;
|
|
113
|
+
type: CreateShapeType;
|
|
114
|
+
} | {
|
|
115
|
+
kind: "reconnect";
|
|
116
|
+
edgeId: string;
|
|
117
|
+
isStart: boolean;
|
|
118
|
+
port: PortDir;
|
|
119
|
+
} | {
|
|
120
|
+
kind: "insertWaypoint";
|
|
121
|
+
edgeId: string;
|
|
122
|
+
segIdx: number;
|
|
123
|
+
point: BpmnWaypoint;
|
|
124
|
+
} | {
|
|
125
|
+
kind: "moveWaypoint";
|
|
126
|
+
edgeId: string;
|
|
127
|
+
wpIdx: number;
|
|
128
|
+
point: BpmnWaypoint;
|
|
129
|
+
} | {
|
|
130
|
+
kind: "moveSegment";
|
|
131
|
+
edgeId: string;
|
|
132
|
+
segIdx: number;
|
|
133
|
+
isHoriz: boolean;
|
|
134
|
+
delta: number;
|
|
135
|
+
} | {
|
|
136
|
+
kind: "autoLayout";
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The escape hatch: a whole document, for edits that have no description.
|
|
140
|
+
*
|
|
141
|
+
* `applyChange` takes an arbitrary function — the properties panel uses it —
|
|
142
|
+
* and a function cannot be replayed. Under a single writer a whole-document
|
|
143
|
+
* op is still correct, just larger on the wire, so nothing is unreplayable;
|
|
144
|
+
* it simply costs more.
|
|
145
|
+
*/
|
|
146
|
+
| {
|
|
147
|
+
kind: "snapshot";
|
|
148
|
+
defs: BpmnDefinitions;
|
|
149
|
+
};
|
|
150
|
+
/** The result of an op: the new document, and anything it brought into being. */
|
|
151
|
+
export interface OpResult {
|
|
152
|
+
defs: BpmnDefinitions;
|
|
153
|
+
/** Ids created by this op, in the order they were made. Empty for edits. */
|
|
154
|
+
created: string[];
|
|
155
|
+
}
|
|
156
|
+
/** Performs an op. The same op, on the same document, anywhere. */
|
|
157
|
+
export declare function applyOp(defs: BpmnDefinitions, op: EditorOp): OpResult;
|
|
158
|
+
//# sourceMappingURL=ops.d.ts.map
|