@formicoidea/labre-framework-bpmn 0.36.0 → 0.38.1
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/actions.js +6 -8
- package/dist/export.d.ts +6 -5
- package/dist/export.js +5 -4
- package/dist/facts.d.ts +15 -23
- package/dist/facts.js +31 -40
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/presets.d.ts +19 -0
- package/dist/presets.js +24 -1
- package/dist/reading.d.ts +16 -0
- package/dist/reading.js +102 -0
- package/dist/rules.js +7 -7
- package/dist/templates/index.js +178 -136
- package/dist/translations.js +7 -1
- package/dist/view.js +11 -1
- package/package.json +2 -2
package/dist/actions.js
CHANGED
|
@@ -8,7 +8,7 @@ import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
|
|
|
8
8
|
import { ASSOCIATION_STROKE, ASSOCIATION_WIDTH, MESSAGE_STROKE, MESSAGE_WIDTH, NODE_LABEL, nodeLabelKey, NODE_SIZE, SEQUENCE_STROKE, SEQUENCE_WIDTH, } from './consts.js';
|
|
9
9
|
import { BPMN_FORMAT_ID } from './export.js';
|
|
10
10
|
import { BPMN_SVG_IMPORT, BPMN_XML_EXPORT, BPMN_XML_FORMAT, BPMN_XML_IMPORT, bpmnBoardFrom, bpmnSafeFilename, } from './interchange.js';
|
|
11
|
-
import { bpmnNodeProps } from './presets.js';
|
|
11
|
+
import { bpmnNodeProps, bpmnPoolProps } from './presets.js';
|
|
12
12
|
import { BPMN_ROLE } from './roles.js';
|
|
13
13
|
/**
|
|
14
14
|
* Standalone creation/activation actions for the BPMN toolbox — lifted out of
|
|
@@ -56,13 +56,11 @@ export function createBpmnPool(std) {
|
|
|
56
56
|
const w = 560;
|
|
57
57
|
const h = 200;
|
|
58
58
|
const { centerX: cx, centerY: cy } = gfx.viewport;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
|
|
65
|
-
});
|
|
59
|
+
// What a pool IS lives in one place (`./presets.ts`), beside what a node is
|
|
60
|
+
// and for the same reason: the shipped cards build their participants from it
|
|
61
|
+
// too, so a pool laid out by a template and one dropped from the toolbox
|
|
62
|
+
// cannot say different things. The gesture owns the BOX and nothing else.
|
|
63
|
+
const id = surface.addElement(bpmnPoolProps({ xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize() }));
|
|
66
64
|
finish(gfx, id);
|
|
67
65
|
}
|
|
68
66
|
/**
|
package/dist/export.d.ts
CHANGED
|
@@ -213,7 +213,7 @@ export declare const BPMN_SCOPE: {
|
|
|
213
213
|
* decides the FILENAME and nothing else.
|
|
214
214
|
*/
|
|
215
215
|
export interface BpmnExportBoard {
|
|
216
|
-
/** In document order
|
|
216
|
+
/** In document order; `bpmnPoolOf` breaks a tie on the smaller id, not on it. */
|
|
217
217
|
pools: readonly BpmnPoolElementModel[];
|
|
218
218
|
nodes: readonly BpmnNodeElementModel[];
|
|
219
219
|
connectors: readonly ConnectorElementModel[];
|
|
@@ -258,10 +258,11 @@ export interface BpmnExportOutcome {
|
|
|
258
258
|
* process; with a collaboration it must point at the collaboration, or most
|
|
259
259
|
* tools draw the flow and none of the pools (spec §12.3.2).
|
|
260
260
|
*
|
|
261
|
-
* Attribution is {@link bpmnPoolOf} and {@link bpmnLaneOf} —
|
|
262
|
-
* the pool
|
|
263
|
-
* same arithmetic the audit and the
|
|
264
|
-
*
|
|
261
|
+
* Attribution is {@link bpmnPoolOf} and {@link bpmnLaneOf} — WHOLE containment
|
|
262
|
+
* in the pool, then the CENTRE against the pool's plot for the lane, no
|
|
263
|
+
* nearest-pool fallback. Deliberately the same arithmetic the audit and the
|
|
264
|
+
* validation rules read, so a task the audit reports in "Back office" is in the
|
|
265
|
+
* `lane` named "Back office" here.
|
|
265
266
|
*/
|
|
266
267
|
export declare function exportBpmnXml(board: BpmnExportBoard, options?: BpmnExportOptions): string;
|
|
267
268
|
/**
|
package/dist/export.js
CHANGED
|
@@ -929,10 +929,11 @@ function edgeScope(element, source, target, ctx) {
|
|
|
929
929
|
* process; with a collaboration it must point at the collaboration, or most
|
|
930
930
|
* tools draw the flow and none of the pools (spec §12.3.2).
|
|
931
931
|
*
|
|
932
|
-
* Attribution is {@link bpmnPoolOf} and {@link bpmnLaneOf} —
|
|
933
|
-
* the pool
|
|
934
|
-
* same arithmetic the audit and the
|
|
935
|
-
*
|
|
932
|
+
* Attribution is {@link bpmnPoolOf} and {@link bpmnLaneOf} — WHOLE containment
|
|
933
|
+
* in the pool, then the CENTRE against the pool's plot for the lane, no
|
|
934
|
+
* nearest-pool fallback. Deliberately the same arithmetic the audit and the
|
|
935
|
+
* validation rules read, so a task the audit reports in "Back office" is in the
|
|
936
|
+
* `lane` named "Back office" here.
|
|
936
937
|
*/
|
|
937
938
|
export function exportBpmnXml(board, options = {}) {
|
|
938
939
|
return exportBpmnXmlWithWarnings(board, options).text;
|
package/dist/facts.d.ts
CHANGED
|
@@ -1,31 +1,19 @@
|
|
|
1
1
|
import type { BpmnLane, BpmnPoolElementModel } from '@formicoidea/labre-core/model';
|
|
2
2
|
import type { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
3
3
|
/**
|
|
4
|
-
* The pool
|
|
4
|
+
* The pool an artefact belongs to: the one that WHOLLY CONTAINS its bound
|
|
5
|
+
* (PF2.4), or `null`.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
+
* The pool's ELEMENT bound and not its plot: membership is one question with one
|
|
8
|
+
* answer across the product, and `containingFrame` is where it is answered — the
|
|
9
|
+
* same function `containingBackground` uses inside the validation engine, so a
|
|
10
|
+
* task the engine indicts for hanging off a pool is not reported as being in it.
|
|
11
|
+
* The name band therefore counts as part of the pool for MEMBERSHIP; it is only
|
|
12
|
+
* the lane arithmetic below that still subtracts it.
|
|
7
13
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* half only, with the audit's own first-match-in-document-order tie-break —
|
|
12
|
-
* which is the sanctioned reduction, and it is the right one here for two
|
|
13
|
-
* reasons.
|
|
14
|
-
*
|
|
15
|
-
* - The nearest-fallback never returns `null` while a single pool exists on the
|
|
16
|
-
* board. That is correct for an audit, which has to say something about every
|
|
17
|
-
* role-carrying element it reports; it is wrong for a fact query, whose whole
|
|
18
|
-
* value is telling "in this pool" apart from "beside it". A task dropped on
|
|
19
|
-
* bare canvas is not in a pool, and saying so is the answer.
|
|
20
|
-
* - Containment is tested on the CENTRE against the plot, not on the full
|
|
21
|
-
* element box against the element box, so this function and {@link bpmnLaneOf}
|
|
22
|
-
* are the same test at two scales. A laned pool therefore cannot answer "yes,
|
|
23
|
-
* this pool" and "no lane" for a lane set that covers the plot — the two are
|
|
24
|
-
* congruent by construction rather than by agreement.
|
|
25
|
-
*
|
|
26
|
-
* Ties — a centre inside two overlapping pools — go to the first pool in the
|
|
27
|
-
* order given, which for a surface is document order. The audit's `attribute()`
|
|
28
|
-
* returns on its first containing frame the same way.
|
|
14
|
+
* No nearest-pool fallback, unlike the audit's frame attribution: a task dropped
|
|
15
|
+
* beside a pool is not in it, and inventing one here would put a rule's finding
|
|
16
|
+
* on a participant the author never drew it on.
|
|
29
17
|
*/
|
|
30
18
|
export declare function bpmnPoolOf(pools: readonly BpmnPoolElementModel[], bound: Bound): BpmnPoolElementModel | null;
|
|
31
19
|
/**
|
|
@@ -41,6 +29,10 @@ export declare function bpmnPoolOf(pools: readonly BpmnPoolElementModel[], bound
|
|
|
41
29
|
* not paint. The `BpmnLane` handed back is the model's own row, matched by id,
|
|
42
30
|
* so a caller gets the thing it can rename or resize.
|
|
43
31
|
*
|
|
32
|
+
* The CENTRE, deliberately unlike {@link bpmnPoolOf}: a task must be wholly in a
|
|
33
|
+
* pool, but may straddle two lanes and is then read in the one its centre is in.
|
|
34
|
+
* See the module comment — a board is a container, a band is a reading.
|
|
35
|
+
*
|
|
44
36
|
* First match in declaration order, so a centre landing exactly ON a divider
|
|
45
37
|
* belongs to the band ABOVE it. Arbitrary in isolation and deliberate together:
|
|
46
38
|
* it is the tie `zoneAt` breaks, and the two must break it the same way.
|
package/dist/facts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { backgroundInstanceZones, backgroundPlot, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { backgroundInstanceZones, backgroundPlot, containingFrame, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { BPMN_POOL_BACKGROUND } from './background.js';
|
|
3
3
|
/**
|
|
4
4
|
* Where a BPMN artefact SITS — the facts, answerable without a `BlockStdScope`.
|
|
@@ -16,15 +16,21 @@ import { BPMN_POOL_BACKGROUND } from './background.js';
|
|
|
16
16
|
* call them, a test can call them with a stub, and the answer cannot depend on
|
|
17
17
|
* which extensions happen to be registered.
|
|
18
18
|
*
|
|
19
|
-
* ##
|
|
19
|
+
* ## Two conventions, and the asymmetry between them is the point (PF2.4)
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
21
|
+
* {@link bpmnPoolOf} asks about MEMBERSHIP and answers with WHOLE containment in
|
|
22
|
+
* the pool's element bound — the shared `containingFrame`, the same test the
|
|
23
|
+
* validation engine and the audit use, so one artefact never gets two answers
|
|
24
|
+
* about which participant it is on.
|
|
25
|
+
*
|
|
26
|
+
* {@link bpmnLaneOf} asks about POSITION inside a pool it is already in, and
|
|
27
|
+
* keeps the CENTRE against ratios of the PLOT, first matching zone in
|
|
28
|
+
* declaration order — the convention `plotRatios` and `zoneAt` use in the audit.
|
|
29
|
+
* A lane is a band drawn across a pool, and a task taller than a band still has
|
|
30
|
+
* to be in exactly one of them.
|
|
31
|
+
*
|
|
32
|
+
* So a task must be WHOLLY inside a pool, but may straddle two lanes. That is
|
|
33
|
+
* deliberate: a board is a container, a band is a reading.
|
|
28
34
|
*/
|
|
29
35
|
/**
|
|
30
36
|
* `lane` — the namespace the pool's instance zones report under.
|
|
@@ -59,42 +65,23 @@ function within(at, rect) {
|
|
|
59
65
|
at[1] >= rect.y &&
|
|
60
66
|
at[1] <= rect.y + rect.h);
|
|
61
67
|
}
|
|
62
|
-
/** The whole plot, as the ratios every zone rectangle is expressed in. */
|
|
63
|
-
const WHOLE_PLOT = { x: 0, y: 0, w: 1, h: 1 };
|
|
64
68
|
/**
|
|
65
|
-
* The pool
|
|
66
|
-
*
|
|
67
|
-
* ## Which attribution rule this is, and why it is not the audit's whole one
|
|
68
|
-
*
|
|
69
|
-
* The audit attributes in two halves (`attribute()` in `audit.ts`, and
|
|
70
|
-
* `attributeBackground()` in `validation.ts`): the frame that CONTAINS the
|
|
71
|
-
* element, failing that the NEAREST by edge-to-edge gap. This is the containment
|
|
72
|
-
* half only, with the audit's own first-match-in-document-order tie-break —
|
|
73
|
-
* which is the sanctioned reduction, and it is the right one here for two
|
|
74
|
-
* reasons.
|
|
69
|
+
* The pool an artefact belongs to: the one that WHOLLY CONTAINS its bound
|
|
70
|
+
* (PF2.4), or `null`.
|
|
75
71
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* element box against the element box, so this function and {@link bpmnLaneOf}
|
|
83
|
-
* are the same test at two scales. A laned pool therefore cannot answer "yes,
|
|
84
|
-
* this pool" and "no lane" for a lane set that covers the plot — the two are
|
|
85
|
-
* congruent by construction rather than by agreement.
|
|
72
|
+
* The pool's ELEMENT bound and not its plot: membership is one question with one
|
|
73
|
+
* answer across the product, and `containingFrame` is where it is answered — the
|
|
74
|
+
* same function `containingBackground` uses inside the validation engine, so a
|
|
75
|
+
* task the engine indicts for hanging off a pool is not reported as being in it.
|
|
76
|
+
* The name band therefore counts as part of the pool for MEMBERSHIP; it is only
|
|
77
|
+
* the lane arithmetic below that still subtracts it.
|
|
86
78
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
79
|
+
* No nearest-pool fallback, unlike the audit's frame attribution: a task dropped
|
|
80
|
+
* beside a pool is not in it, and inventing one here would put a rule's finding
|
|
81
|
+
* on a participant the author never drew it on.
|
|
90
82
|
*/
|
|
91
83
|
export function bpmnPoolOf(pools, bound) {
|
|
92
|
-
|
|
93
|
-
const at = plotRatios(pool, bound);
|
|
94
|
-
if (at !== null && within(at, WHOLE_PLOT))
|
|
95
|
-
return pool;
|
|
96
|
-
}
|
|
97
|
-
return null;
|
|
84
|
+
return containingFrame(bound, pools, pool => pool.elementBound);
|
|
98
85
|
}
|
|
99
86
|
/**
|
|
100
87
|
* The lane of `pool` the bound's centre falls in — `null` when the pool carries
|
|
@@ -109,6 +96,10 @@ export function bpmnPoolOf(pools, bound) {
|
|
|
109
96
|
* not paint. The `BpmnLane` handed back is the model's own row, matched by id,
|
|
110
97
|
* so a caller gets the thing it can rename or resize.
|
|
111
98
|
*
|
|
99
|
+
* The CENTRE, deliberately unlike {@link bpmnPoolOf}: a task must be wholly in a
|
|
100
|
+
* pool, but may straddle two lanes and is then read in the one its centre is in.
|
|
101
|
+
* See the module comment — a board is a container, a band is a reading.
|
|
102
|
+
*
|
|
112
103
|
* First match in declaration order, so a centre landing exactly ON a divider
|
|
113
104
|
* belongs to the band ABOVE it. Arbitrary in isolation and deliberate together:
|
|
114
105
|
* it is the tie `zoneAt` breaks, and the two must break it the same way.
|
package/dist/index.d.ts
CHANGED
|
@@ -12,3 +12,4 @@ export { BPMN_PROFILES } from './profiles.js';
|
|
|
12
12
|
export { BPMN_ROLE, BPMN_ROLE_OF_KIND, BPMN_ROLES, type BpmnRole, type BpmnRoleId, } from './roles.js';
|
|
13
13
|
export { BPMN_RULES, BPMN_SEQUENCE_MATRIX } from './rules.js';
|
|
14
14
|
export { bpmnTranslationEntries } from './translations.js';
|
|
15
|
+
export { BPMN_ANNOTATION_READING, BPMN_DATA_READING, BPMN_GROUP_READING, BPMN_READING, BPMN_READINGS, } from './reading.js';
|
package/dist/index.js
CHANGED
|
@@ -47,3 +47,4 @@ export { BPMN_PROFILES } from './profiles.js';
|
|
|
47
47
|
export { BPMN_ROLE, BPMN_ROLE_OF_KIND, BPMN_ROLES, } from './roles.js';
|
|
48
48
|
export { BPMN_RULES, BPMN_SEQUENCE_MATRIX } from './rules.js';
|
|
49
49
|
export { bpmnTranslationEntries } from './translations.js';
|
|
50
|
+
export { BPMN_ANNOTATION_READING, BPMN_DATA_READING, BPMN_GROUP_READING, BPMN_READING, BPMN_READINGS, } from './reading.js';
|
package/dist/presets.d.ts
CHANGED
|
@@ -124,6 +124,25 @@ export declare function bpmnNodeProps(kind: BpmnNodeKind, box: {
|
|
|
124
124
|
}): Record<string, unknown> & {
|
|
125
125
|
type: string;
|
|
126
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* One BPMN pool, as the props `surface.addElement` takes.
|
|
129
|
+
*
|
|
130
|
+
* The same argument {@link bpmnNodeProps} makes, one artefact over: the palette
|
|
131
|
+
* hands it a box centred on the viewport and a worked scene hands it the box the
|
|
132
|
+
* scene lays out, and the element that lands is the same either way. It used to
|
|
133
|
+
* be spelled out at each site, which is how the shipped cards came to write a
|
|
134
|
+
* `name` the creation gesture leaves to the model's own default.
|
|
135
|
+
*
|
|
136
|
+
* `name` is spread and never defaulted, for the reason `textVerticalAlign` is on
|
|
137
|
+
* a node: a pool drawn from the toolbox puts no participant name in its Y.Map,
|
|
138
|
+
* and a scene that names its participants says so explicitly.
|
|
139
|
+
*/
|
|
140
|
+
export declare function bpmnPoolProps(box: {
|
|
141
|
+
xywh: string;
|
|
142
|
+
name?: string;
|
|
143
|
+
}): Record<string, unknown> & {
|
|
144
|
+
type: string;
|
|
145
|
+
};
|
|
127
146
|
/**
|
|
128
147
|
* What a node's kind is worth to an element that ALREADY EXISTS — the same
|
|
129
148
|
* description as {@link bpmnNodeProps}, minus the three things a morph has no
|
package/dist/presets.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FontFamily, SHAPE_TEXT_PADDING, SHAPE_TEXT_VERTICAL_PADDING, ShapeStyle, StrokeStyle, TextAlign, TextFitMode, TextVerticalAlign, } from '@formicoidea/labre-core/model';
|
|
2
2
|
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
3
3
|
import { CALL_ACTIVITY_WIDTH, END_WIDTH, EVENT_END, EVENT_START, GROUP_RADIUS, GROUP_STROKE, INNER_FONT_SIZE, LABEL_INSET_RATIO, LABEL_MIN_FONT_SIZE, NEUTRAL_STROKE, NODE_FILL, NODE_SIZE, NODE_STROKE_WIDTH, START_WIDTH, TASK_RADIUS, } from './consts.js';
|
|
4
|
-
import { BPMN_ROLE_OF_KIND } from './roles.js';
|
|
4
|
+
import { BPMN_ROLE, BPMN_ROLE_OF_KIND } from './roles.js';
|
|
5
5
|
/** Per-kind native shape + accent presets (style C). */
|
|
6
6
|
export const NODE_PRESETS = {
|
|
7
7
|
// Events: one ellipse, and the ring weight says start or end. The message and
|
|
@@ -255,6 +255,29 @@ export function bpmnNodeProps(kind, box) {
|
|
|
255
255
|
xywh: box.xywh,
|
|
256
256
|
};
|
|
257
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* One BPMN pool, as the props `surface.addElement` takes.
|
|
260
|
+
*
|
|
261
|
+
* The same argument {@link bpmnNodeProps} makes, one artefact over: the palette
|
|
262
|
+
* hands it a box centred on the viewport and a worked scene hands it the box the
|
|
263
|
+
* scene lays out, and the element that lands is the same either way. It used to
|
|
264
|
+
* be spelled out at each site, which is how the shipped cards came to write a
|
|
265
|
+
* `name` the creation gesture leaves to the model's own default.
|
|
266
|
+
*
|
|
267
|
+
* `name` is spread and never defaulted, for the reason `textVerticalAlign` is on
|
|
268
|
+
* a node: a pool drawn from the toolbox puts no participant name in its Y.Map,
|
|
269
|
+
* and a scene that names its participants says so explicitly.
|
|
270
|
+
*/
|
|
271
|
+
export function bpmnPoolProps(box) {
|
|
272
|
+
return {
|
|
273
|
+
type: 'bpmnPool',
|
|
274
|
+
// The FRAME the flow objects are drawn in, and a role of its own: a rule
|
|
275
|
+
// written on the artefacts must never fall on the lane that holds them.
|
|
276
|
+
role: BPMN_ROLE.pool,
|
|
277
|
+
...(box.name === undefined ? {} : { name: box.name }),
|
|
278
|
+
xywh: box.xywh,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
258
281
|
/**
|
|
259
282
|
* The box {@link bpmnMorphProps} hands {@link bpmnNodeProps} and then throws
|
|
260
283
|
* away. Never written to a document: a morph keeps the geometry the element
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReadingProfile } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
/** Events, activities and gateways — the three families that ARE the process. */
|
|
3
|
+
export declare const BPMN_READING: ReadingProfile;
|
|
4
|
+
/**
|
|
5
|
+
* The paperwork: a data object and a data store.
|
|
6
|
+
*
|
|
7
|
+
* No relation — a data object is tied to the work by an ASSOCIATION, which
|
|
8
|
+
* declares no verb and no direction, so there is no sentence to read off it.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BPMN_DATA_READING: ReadingProfile;
|
|
11
|
+
/** A note on the picture. Readable, so a reader can link it to a record. */
|
|
12
|
+
export declare const BPMN_ANNOTATION_READING: ReadingProfile;
|
|
13
|
+
/** A lasso round part of the picture. Parent-less by spec, hence its own row. */
|
|
14
|
+
export declare const BPMN_GROUP_READING: ReadingProfile;
|
|
15
|
+
/** Every BPMN profile, in the order the view extension registers them. */
|
|
16
|
+
export declare const BPMN_READINGS: readonly ReadingProfile[];
|
package/dist/reading.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { BPMN_ROLE, BPMN_ROLES } from './roles.js';
|
|
2
|
+
/**
|
|
3
|
+
* What BPMN lets the tool read of an artefact (MF3, the reversed reading).
|
|
4
|
+
*
|
|
5
|
+
* DATA, like the rules and the profiles beside it: the engine
|
|
6
|
+
* (`@labre/affine-block-surface`) knows how to read a role and a typed edge — it
|
|
7
|
+
* knows nothing about BPMN. Everything below is already stated in `roles.ts` and
|
|
8
|
+
* is merely POINTED AT here.
|
|
9
|
+
*
|
|
10
|
+
* Registered from the FLAG-GATED view extension: reading a process is tooling,
|
|
11
|
+
* so a pool whose BPMN flag is off keeps every element it has and simply stops
|
|
12
|
+
* being read (ADR 0009).
|
|
13
|
+
*
|
|
14
|
+
* ## FOUR profiles, because BPMN has four parent-less node families
|
|
15
|
+
*
|
|
16
|
+
* The vocabulary is deliberately a forest and not a tree — `roles.ts` spells out
|
|
17
|
+
* why, family by family: the paperwork is not the work, commentary is never
|
|
18
|
+
* evidence, and BPMN 2.0.2 §10.4 exempts the group from every containment
|
|
19
|
+
* constraint there is. A single `appliesTo` would therefore have needed a
|
|
20
|
+
* `bpmn:artefact` root that the notation does not have, and inventing a role is
|
|
21
|
+
* the one thing ADR 0007 forbids: an id is forever, and a vocabulary is not a
|
|
22
|
+
* place to put a convenience.
|
|
23
|
+
*
|
|
24
|
+
* So each family declares its own profile, with an id of its own (the DI keys on
|
|
25
|
+
* it and throws on a duplicate). Only the pool is left out, and only because it
|
|
26
|
+
* is the FRAME: a reading is about an artefact, never about the lane it is drawn
|
|
27
|
+
* in.
|
|
28
|
+
*
|
|
29
|
+
* ## The sequence flow is the only relation read
|
|
30
|
+
*
|
|
31
|
+
* Of the three connecting objects, only `bpmn:sequence-flow` orders anything. A
|
|
32
|
+
* message flow says who told whom and a plain association says "this note is
|
|
33
|
+
* about that", both of which read identically from either end — and the
|
|
34
|
+
* association declares no `direction` for exactly that reason. Reading them
|
|
35
|
+
* under the sequence flow's two words would put a sentence in the panel that
|
|
36
|
+
* the notation never said.
|
|
37
|
+
*
|
|
38
|
+
* ## No nature, no phase
|
|
39
|
+
*
|
|
40
|
+
* BPMN ships no type-3 tag pack: its kinds ARE the roles, and the panel says
|
|
41
|
+
* them on the type line, chain included ("Message start event › Start event ›
|
|
42
|
+
* Event › Flow object"). And a pool's lanes are participants, not zones on a
|
|
43
|
+
* plotted axis, so there is nothing a position could be read against yet.
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* The two words a sequence flow is read with, from the subject's end.
|
|
47
|
+
*
|
|
48
|
+
* ADR 0010 tier 2 on this role: the verb is "is followed by", so the SOURCE is
|
|
49
|
+
* what happens first. An edge leaving the subject therefore names what comes
|
|
50
|
+
* next, and one arriving names what came before.
|
|
51
|
+
*/
|
|
52
|
+
const SEQUENCE_SIDES = {
|
|
53
|
+
consumer: {
|
|
54
|
+
labelKey: 'com.labre.bpmn.reading.relations.consumer',
|
|
55
|
+
labelFallback: 'Preceded by',
|
|
56
|
+
},
|
|
57
|
+
supplier: {
|
|
58
|
+
labelKey: 'com.labre.bpmn.reading.relations.supplier',
|
|
59
|
+
labelFallback: 'Followed by',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
/** Events, activities and gateways — the three families that ARE the process. */
|
|
63
|
+
export const BPMN_READING = {
|
|
64
|
+
id: 'bpmn',
|
|
65
|
+
framework: 'bpmn',
|
|
66
|
+
roles: BPMN_ROLES,
|
|
67
|
+
appliesTo: BPMN_ROLE.flowObject,
|
|
68
|
+
relation: { edgeRole: BPMN_ROLE.sequenceFlow, sides: SEQUENCE_SIDES },
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The paperwork: a data object and a data store.
|
|
72
|
+
*
|
|
73
|
+
* No relation — a data object is tied to the work by an ASSOCIATION, which
|
|
74
|
+
* declares no verb and no direction, so there is no sentence to read off it.
|
|
75
|
+
*/
|
|
76
|
+
export const BPMN_DATA_READING = {
|
|
77
|
+
id: 'bpmn-data',
|
|
78
|
+
framework: 'bpmn',
|
|
79
|
+
roles: BPMN_ROLES,
|
|
80
|
+
appliesTo: BPMN_ROLE.data,
|
|
81
|
+
};
|
|
82
|
+
/** A note on the picture. Readable, so a reader can link it to a record. */
|
|
83
|
+
export const BPMN_ANNOTATION_READING = {
|
|
84
|
+
id: 'bpmn-annotation',
|
|
85
|
+
framework: 'bpmn',
|
|
86
|
+
roles: BPMN_ROLES,
|
|
87
|
+
appliesTo: BPMN_ROLE.textAnnotation,
|
|
88
|
+
};
|
|
89
|
+
/** A lasso round part of the picture. Parent-less by spec, hence its own row. */
|
|
90
|
+
export const BPMN_GROUP_READING = {
|
|
91
|
+
id: 'bpmn-group',
|
|
92
|
+
framework: 'bpmn',
|
|
93
|
+
roles: BPMN_ROLES,
|
|
94
|
+
appliesTo: BPMN_ROLE.group,
|
|
95
|
+
};
|
|
96
|
+
/** Every BPMN profile, in the order the view extension registers them. */
|
|
97
|
+
export const BPMN_READINGS = [
|
|
98
|
+
BPMN_READING,
|
|
99
|
+
BPMN_DATA_READING,
|
|
100
|
+
BPMN_ANNOTATION_READING,
|
|
101
|
+
BPMN_GROUP_READING,
|
|
102
|
+
];
|
package/dist/rules.js
CHANGED
|
@@ -882,11 +882,11 @@ const activityDeadEnd = {
|
|
|
882
882
|
*
|
|
883
883
|
* ## What it needs the pool DECLARATION for
|
|
884
884
|
*
|
|
885
|
-
* "Inside the pool" is
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
*
|
|
885
|
+
* "Inside the pool" is WHOLE containment in the pool's element bound (PF2.4) —
|
|
886
|
+
* the same `containingFrame` `bpmnPoolOf` calls, so this rule and the fact query
|
|
887
|
+
* give one answer to "which pool is this on" instead of two. The declaration is
|
|
888
|
+
* carried all the same: it is what the zone half of the family reads, and it is
|
|
889
|
+
* where the lane geometry lives.
|
|
890
890
|
*
|
|
891
891
|
* ## Silence
|
|
892
892
|
*
|
|
@@ -912,8 +912,8 @@ const sequenceFlowStaysHome = {
|
|
|
912
912
|
reference: 'OMG BPMN 2.0.2 (ISO/IEC 19510) p.40, restated p.502 — Sequence Flows cannot cross a Pool boundary',
|
|
913
913
|
},
|
|
914
914
|
backgroundRole: BPMN_ROLE.pool,
|
|
915
|
-
// The declaration,
|
|
916
|
-
//
|
|
915
|
+
// The declaration, for the zone half of the family and the lane geometry.
|
|
916
|
+
// Membership itself is whole containment in the element bound (PF2.4).
|
|
917
917
|
background: BPMN_POOL_BACKGROUND,
|
|
918
918
|
locality: {
|
|
919
919
|
edgeRole: BPMN_ROLE.sequenceFlow,
|
package/dist/templates/index.js
CHANGED
|
@@ -1,74 +1,59 @@
|
|
|
1
|
-
import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
-
import { ConnectorMode,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { makeTemplateSnapshot, surfaceText, templateFromCommand, } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
import { ConnectorMode, PointStyle, StrokeStyle, } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { bpmnCommands } from '../commands.js';
|
|
4
|
+
import { MESSAGE_STROKE, MESSAGE_WIDTH, NODE_SIZE, SEQUENCE_STROKE, SEQUENCE_WIDTH, } from '../consts.js';
|
|
5
|
+
import { bpmnNodeProps, bpmnPoolProps } from '../presets.js';
|
|
6
|
+
import { BPMN_ROLE } from '../roles.js';
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
8
|
+
* The BPMN palette — DERIVED from the toolbox, one template per artefact
|
|
9
|
+
* command.
|
|
7
10
|
*
|
|
8
|
-
* Every
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Every single-artefact entry below is what its command actually draws, run once
|
|
12
|
+
* against a recording surface. It used to be a hand-written restatement of the
|
|
13
|
+
* same artefacts through a private `node()` builder, and it had drifted exactly
|
|
14
|
+
* as far as a copy drifts: none of the eleven nodes carried the
|
|
15
|
+
* `textFitMode: Overflow` the pack has written since #160, the events and
|
|
16
|
+
* gateways had lost their typography on an early return, the labels were English
|
|
17
|
+
* literals outside the translation seam (#192), and six kinds out of seventeen
|
|
18
|
+
* had a template at all. Derived, none of that can happen again — and
|
|
19
|
+
* `templates-parity.unit.spec.ts` re-runs each command and compares, so the day
|
|
20
|
+
* a creation site changes the palette changes with it.
|
|
21
|
+
*
|
|
22
|
+
* The two worked SCENES stay hand-composed: a process end to end and a
|
|
23
|
+
* two-party message exchange are arrangements of a dozen artefacts, which no
|
|
24
|
+
* single command draws. They are built on the same presets
|
|
25
|
+
* ({@link bpmnNodeProps}, {@link bpmnPoolProps}), and the same test checks their
|
|
26
|
+
* composition AND runs the rule pack over them — which is what caught the
|
|
27
|
+
* "Message exchange" card violating `bpmn.pool-start-without-end` at insertion.
|
|
28
|
+
*/
|
|
29
|
+
/** The command a derived template is the picture of. Throws rather than skips. */
|
|
30
|
+
function byId(id) {
|
|
31
|
+
const command = bpmnCommands.find(entry => entry.id === id);
|
|
32
|
+
if (!command)
|
|
33
|
+
throw new Error(`[bpmn] templates: no command "${id}"`);
|
|
34
|
+
return command;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A scene node, from the same description the toolbox draws from.
|
|
38
|
+
*
|
|
39
|
+
* The scene owns the BOX and the words and nothing else — every visual prop
|
|
40
|
+
* comes from {@link bpmnNodeProps}. Undefined keys are dropped because that is
|
|
41
|
+
* what `surface.addElement` stores, so a scene artefact stays comparable with a
|
|
42
|
+
* derived one.
|
|
15
43
|
*/
|
|
16
44
|
function node(kind, x, y, text) {
|
|
17
45
|
const { w, h } = NODE_SIZE[kind];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// one.
|
|
24
|
-
role: BPMN_ROLE_OF_KIND[kind],
|
|
25
|
-
filled: true,
|
|
26
|
-
fillColor: NODE_FILL,
|
|
27
|
-
shapeStyle: ShapeStyle.General,
|
|
28
|
-
roughness: 0,
|
|
29
|
-
xywh: `[${x},${y},${w},${h}]`,
|
|
30
|
-
};
|
|
31
|
-
if (kind === 'startEvent')
|
|
32
|
-
return {
|
|
33
|
-
...base,
|
|
34
|
-
shapeType: 'ellipse',
|
|
35
|
-
strokeColor: EVENT_START,
|
|
36
|
-
strokeWidth: START_WIDTH,
|
|
37
|
-
};
|
|
38
|
-
if (kind === 'endEvent')
|
|
39
|
-
return {
|
|
40
|
-
...base,
|
|
41
|
-
shapeType: 'ellipse',
|
|
42
|
-
strokeColor: EVENT_END,
|
|
43
|
-
strokeWidth: END_WIDTH,
|
|
44
|
-
};
|
|
45
|
-
if (kind === 'gatewayExclusive')
|
|
46
|
-
return {
|
|
47
|
-
...base,
|
|
48
|
-
shapeType: 'diamond',
|
|
49
|
-
strokeColor: NEUTRAL_STROKE,
|
|
50
|
-
strokeWidth: NODE_STROKE_WIDTH,
|
|
51
|
-
};
|
|
52
|
-
return {
|
|
53
|
-
...base,
|
|
54
|
-
shapeType: 'rect',
|
|
55
|
-
radius: TASK_RADIUS,
|
|
56
|
-
strokeColor: NEUTRAL_STROKE,
|
|
57
|
-
strokeWidth: NODE_STROKE_WIDTH,
|
|
58
|
-
text: surfaceText(text ?? 'Task'),
|
|
59
|
-
color: NEUTRAL_STROKE,
|
|
60
|
-
fontFamily: FontFamily.Inter,
|
|
61
|
-
fontSize: INNER_FONT_SIZE,
|
|
62
|
-
textAlign: 'center',
|
|
63
|
-
};
|
|
46
|
+
return defined({
|
|
47
|
+
...bpmnNodeProps(kind, { xywh: `[${x},${y},${w},${h}]` }),
|
|
48
|
+
// A snapshot carries a serialized `Y.Text`, never a bare string.
|
|
49
|
+
...(text === undefined ? {} : { text: surfaceText(text) }),
|
|
50
|
+
});
|
|
64
51
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
xywh: `[${x},${y},${w},${h}]`,
|
|
71
|
-
};
|
|
52
|
+
/** A scene participant, from the same description the toolbox draws from. */
|
|
53
|
+
const pool = (x, y, w, h, name) => defined(bpmnPoolProps({ xywh: `[${x},${y},${w},${h}]`, name }));
|
|
54
|
+
/** Drop the keys a preset left undefined — what `addElement` would store. */
|
|
55
|
+
function defined(props) {
|
|
56
|
+
return Object.fromEntries(Object.entries(props).filter(([, value]) => value !== undefined));
|
|
72
57
|
}
|
|
73
58
|
/** A sequence-flow connector; ids are remapped on insert. */
|
|
74
59
|
function seq(source, target) {
|
|
@@ -121,6 +106,10 @@ function msg(source, target) {
|
|
|
121
106
|
* that", and a stroke attached to neither end has no this and no that to say it
|
|
122
107
|
* about. Drawing it with the sequence-flow tool, or dropping this one and then
|
|
123
108
|
* attaching both of its ends, is what makes it a statement.
|
|
109
|
+
*
|
|
110
|
+
* Hand-written, and the one artefact card that cannot derive:
|
|
111
|
+
* `bpmn.sequenceFlowTool` ACTIVATES a tool rather than drawing anything, so
|
|
112
|
+
* there is nothing to record — the user draws it.
|
|
124
113
|
*/
|
|
125
114
|
function freeSeq() {
|
|
126
115
|
return {
|
|
@@ -135,86 +124,139 @@ function freeSeq() {
|
|
|
135
124
|
target: { position: [140, 0] },
|
|
136
125
|
};
|
|
137
126
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
127
|
+
/* ── Previews ─────────────────────────────────────────────────────────────
|
|
128
|
+
*
|
|
129
|
+
* One 135×80 sketch per card, in the toolbar glyphs' own shapes
|
|
130
|
+
* (`../toolbar/icons.ts`). A preview has one job, which is to look like what
|
|
131
|
+
* lands on the board.
|
|
132
|
+
*/
|
|
133
|
+
const A = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
|
|
134
|
+
/** An event: the ring weight says start or end, the glyph says what triggers it. */
|
|
135
|
+
const eventPreview = (stroke, width, glyph = '') => `<svg ${A} fill="none"><circle cx="67" cy="40" r="20" stroke="${stroke}" stroke-width="${width}"/>${glyph}</svg>`;
|
|
136
|
+
/** An activity: one rounded rectangle, and a marker tells the kinds apart. */
|
|
137
|
+
const taskPreview = (glyph = '', width = 2.4) => `<svg ${A} fill="none"><rect x="34" y="24" width="66" height="32" rx="6" stroke="#262626" stroke-width="${width}"/>${glyph}</svg>`;
|
|
138
|
+
/** A gateway: one diamond, one marker each. */
|
|
139
|
+
const gatewayPreview = (marker) => `<svg ${A} fill="none"><path d="M67 16 L92 40 L67 64 L42 40 Z" stroke="#262626" stroke-width="2.4" stroke-linejoin="round"/><path d="${marker}" stroke="#262626" stroke-width="2.2" stroke-linecap="round"/></svg>`;
|
|
140
|
+
const ENVELOPE = '<rect x="59" y="35" width="16" height="11" stroke="#262626" stroke-width="1.4"/><path d="M59 35 L67 42 L75 35" stroke="#262626" stroke-width="1.4" stroke-linejoin="round"/>';
|
|
141
|
+
const CLOCK = '<circle cx="67" cy="40" r="9" stroke="#262626" stroke-width="1.4"/><path d="M67 40 V33 M67 40 L72 43" stroke="#262626" stroke-width="1.4" stroke-linecap="round"/>';
|
|
142
|
+
/** The collapsed `[+]`, on the activity's bottom edge — sub-process and call alike. */
|
|
143
|
+
const COLLAPSED = '<rect x="61" y="44" width="12" height="12" stroke="#262626" stroke-width="1.4"/><path d="M67 47 V53 M64 50 H70" stroke="#262626" stroke-width="1.4" stroke-linecap="round"/>';
|
|
142
144
|
const previews = {
|
|
143
|
-
process: `<svg ${
|
|
144
|
-
startEvent:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
process: `<svg ${A} fill="none"><circle cx="16" cy="40" r="8" stroke="#43a06b" stroke-width="2"/><rect x="34" y="31" width="26" height="18" rx="3" stroke="#262626" stroke-width="1.6"/><path d="M78 31 L88 40 L78 49 L68 40 Z" stroke="#262626" stroke-width="1.4"/><path d="M73 37 L83 43 M83 37 L73 43" stroke="#262626" stroke-width="1.2"/><circle cx="118" cy="40" r="8" stroke="#cf5648" stroke-width="3"/><path d="M24 40 H34 M60 40 H68 M88 40 H110" stroke="#262626" stroke-width="1.2"/></svg>`,
|
|
146
|
+
startEvent: eventPreview('#43a06b', 3),
|
|
147
|
+
startEventMessage: eventPreview('#43a06b', 3, ENVELOPE),
|
|
148
|
+
startEventTimer: eventPreview('#43a06b', 3, CLOCK),
|
|
149
|
+
endEvent: eventPreview('#cf5648', 5),
|
|
150
|
+
endEventMessage: eventPreview('#cf5648', 5, ENVELOPE),
|
|
151
|
+
endEventTerminate: eventPreview('#cf5648', 5, '<circle cx="67" cy="40" r="8" fill="#262626"/>'),
|
|
152
|
+
task: taskPreview(),
|
|
153
|
+
taskUser: taskPreview('<circle cx="41" cy="31" r="2.6" stroke="#262626" stroke-width="1.2"/><path d="M37.4 38 a3.6 3.6 0 0 1 7.2 0" stroke="#262626" stroke-width="1.2"/>'),
|
|
154
|
+
taskService: taskPreview('<circle cx="41" cy="32" r="3.4" stroke="#262626" stroke-width="1.2"/><circle cx="41" cy="32" r="1" fill="#262626"/><path d="M41 27.4 V29 M41 35 V36.6 M36.4 32 H38 M44 32 H45.6" stroke="#262626" stroke-width="1.2" stroke-linecap="round"/>'),
|
|
155
|
+
subProcess: taskPreview(COLLAPSED),
|
|
156
|
+
// The thick border IS the distinction: this box stands for a process defined
|
|
157
|
+
// elsewhere (`presets.ts`, `CALL_ACTIVITY_WIDTH`).
|
|
158
|
+
callActivity: taskPreview(COLLAPSED, 4.5),
|
|
159
|
+
gateway: gatewayPreview('M58 31 L76 49 M76 31 L58 49'),
|
|
160
|
+
gatewayParallel: gatewayPreview('M67 30 V50 M57 40 H77'),
|
|
161
|
+
dataObject: `<svg ${A} fill="none"><path d="M52 14 H76 L88 26 V66 H52 Z" stroke="#262626" stroke-width="2.2" stroke-linejoin="round"/><path d="M76 14 V26 H88" stroke="#262626" stroke-width="1.8" stroke-linejoin="round"/></svg>`,
|
|
162
|
+
dataStore: `<svg ${A} fill="none"><path d="M48 22 V58 C48 62 56 65 67 65 C78 65 86 62 86 58 V22" stroke="#262626" stroke-width="2.2" stroke-linejoin="round"/><ellipse cx="67" cy="22" rx="19" ry="6" stroke="#262626" stroke-width="2.2"/></svg>`,
|
|
163
|
+
textAnnotation: `<svg ${A} fill="none"><path d="M46 16 H36 V64 H46" stroke="#262626" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/><path d="M54 28 H100 M54 40 H100 M54 52 H86" stroke="#262626" stroke-width="1.8" stroke-linecap="round"/></svg>`,
|
|
164
|
+
group: `<svg ${A} fill="none"><rect x="18" y="12" width="99" height="56" rx="10" stroke="#8e8d91" stroke-width="2.2" stroke-dasharray="7 5"/></svg>`,
|
|
165
|
+
sequence: `<svg ${A} fill="none"><path d="M24 40 H96" stroke="#262626" stroke-width="2.4" stroke-linecap="round"/><path d="M94 33 L108 40 L94 47 Z" fill="#262626"/></svg>`,
|
|
166
|
+
pool: `<svg ${A} fill="none"><rect x="14" y="20" width="107" height="40" rx="3" stroke="#262626" stroke-width="2"/><path d="M30 20 V60" stroke="#262626" stroke-width="1.8"/><rect x="14" y="20" width="16" height="40" fill="#f4f4f5"/><path d="M30 20 V60" stroke="#262626" stroke-width="1.8"/></svg>`,
|
|
150
167
|
// Two participants stacked, and the dashed line between them is the whole
|
|
151
168
|
// point of the card: a message flow is the one arrow that crosses a pool.
|
|
152
169
|
// Its source terminator is a FILLED disc, matching what `renderCircle`
|
|
153
170
|
// actually paints rather than the hollow ring the norm asks for — a preview
|
|
154
171
|
// has one job, which is to look like what lands on the board.
|
|
155
|
-
messageExchange: `<svg ${
|
|
172
|
+
messageExchange: `<svg ${A} fill="none"><rect x="14" y="8" width="107" height="27" rx="3" stroke="#262626" stroke-width="1.6"/><rect x="14" y="8" width="11" height="27" fill="#f4f4f5"/><path d="M25 8 V35" stroke="#262626" stroke-width="1.4"/><rect x="14" y="45" width="107" height="27" rx="3" stroke="#262626" stroke-width="1.6"/><rect x="14" y="45" width="11" height="27" fill="#f4f4f5"/><path d="M25 45 V72" stroke="#262626" stroke-width="1.4"/><circle cx="33" cy="21.5" r="4.5" stroke="#43a06b" stroke-width="1.6"/><rect x="46" y="14" width="26" height="15" rx="3" stroke="#262626" stroke-width="1.6"/><circle cx="88" cy="21.5" r="4.5" stroke="#cf5648" stroke-width="2.6"/><rect x="46" y="51" width="26" height="15" rx="3" stroke="#262626" stroke-width="1.6"/><path d="M41 21.5 H46 M72 21.5 H83" stroke="#262626" stroke-width="1.4"/><circle cx="59" cy="31.5" r="2.2" fill="#262626"/><path d="M59 34 V48" stroke="#262626" stroke-width="1.4" stroke-dasharray="3 2.4"/><path d="M56 46 L59 50 L62 46" stroke="#262626" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
173
|
+
};
|
|
174
|
+
/* ── The two worked scenes ────────────────────────────────────────────────── */
|
|
175
|
+
/** A hand-composed card — what is left once the artefacts are derived. */
|
|
176
|
+
const scene = (name, preview, elements) => ({
|
|
177
|
+
name,
|
|
178
|
+
type: 'template',
|
|
179
|
+
preview,
|
|
180
|
+
content: makeTemplateSnapshot(elements, name),
|
|
181
|
+
});
|
|
182
|
+
const process = {
|
|
183
|
+
pool: pool(0, 0, 640, 200, 'Process'),
|
|
184
|
+
start: node('startEvent', 40, 72),
|
|
185
|
+
task1: node('task', 116, 64, 'Submit request'),
|
|
186
|
+
gw: node('gatewayExclusive', 272, 64),
|
|
187
|
+
task2: node('task', 376, 20, 'Fulfil'),
|
|
188
|
+
task3: node('task', 376, 124, 'Reject'),
|
|
189
|
+
end: node('endEvent', 556, 72),
|
|
190
|
+
c1: seq('start', 'task1'),
|
|
191
|
+
c2: seq('task1', 'gw'),
|
|
192
|
+
c3: seq('gw', 'task2'),
|
|
193
|
+
c4: seq('gw', 'task3'),
|
|
194
|
+
c5: seq('task2', 'end'),
|
|
195
|
+
c6: seq('task3', 'end'),
|
|
156
196
|
};
|
|
157
197
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
198
|
+
* Two participants, and the one arrow that is allowed to cross between them.
|
|
199
|
+
*
|
|
200
|
+
* The card exists because the message flow is the piece of BPMN people get
|
|
201
|
+
* wrong first: a sequence flow may never leave its pool, and the thing that
|
|
202
|
+
* does leave is a different statement with a different line. Laying the two
|
|
203
|
+
* pools out one above the other and drawing both flows once is the shortest
|
|
204
|
+
* way to show that — the solid arrow stays home, the dashed one crosses.
|
|
205
|
+
*
|
|
206
|
+
* Stacked vertically with a 40-unit gutter, so the message flow is a straight
|
|
207
|
+
* orthogonal drop between two tasks that already line up.
|
|
208
|
+
*
|
|
209
|
+
* ## The end event, which is not decoration
|
|
210
|
+
*
|
|
211
|
+
* The customer pool shipped with a start event and no end, which is a verbatim
|
|
212
|
+
* violation of BPMN 2.0.2 p.246 and of `bpmn.pool-start-without-end` — a
|
|
213
|
+
* `warning` the moment anybody switched the card to the descriptive profile.
|
|
214
|
+
* Factory content teaching the mistake the rule pack reports is the worst
|
|
215
|
+
* possible first BPMN diagram, and nothing in the repository compared the two
|
|
216
|
+
* until this slice ran the rules over the shipped cards. The customer's process
|
|
217
|
+
* now closes where it should: order placed, then done.
|
|
160
218
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
c5: seq('task2', 'end'),
|
|
175
|
-
c6: seq('task3', 'end'),
|
|
176
|
-
};
|
|
177
|
-
/**
|
|
178
|
-
* Two participants, and the one arrow that is allowed to cross between them.
|
|
179
|
-
*
|
|
180
|
-
* The card exists because the message flow is the piece of BPMN people get
|
|
181
|
-
* wrong first: a sequence flow may never leave its pool, and the thing that
|
|
182
|
-
* does leave is a different statement with a different line. Laying the two
|
|
183
|
-
* pools out one above the other and drawing both flows once is the shortest
|
|
184
|
-
* way to show that — the solid arrow stays home, the dashed one crosses.
|
|
185
|
-
*
|
|
186
|
-
* Stacked vertically with a 40-unit gutter, so the message flow is a straight
|
|
187
|
-
* orthogonal drop between two tasks that already line up.
|
|
188
|
-
*/
|
|
189
|
-
const messageExchange = {
|
|
190
|
-
customer: pool(0, 0, 640, 200, 'Customer'),
|
|
191
|
-
supplier: pool(0, 240, 640, 200, 'Supplier'),
|
|
192
|
-
start: node('startEvent', 40, 72),
|
|
193
|
-
ask: node('taskUser', 140, 64, 'Place order'),
|
|
194
|
-
answer: node('taskService', 140, 304, 'Confirm order'),
|
|
195
|
-
// Inside the first participant: what happens, and in what order.
|
|
196
|
-
inside: seq('start', 'ask'),
|
|
197
|
-
// Across the two: who told whom.
|
|
198
|
-
across: msg('ask', 'answer'),
|
|
199
|
-
};
|
|
200
|
-
const t = (name, preview, elements) => ({
|
|
201
|
-
name,
|
|
202
|
-
type: 'template',
|
|
203
|
-
preview,
|
|
204
|
-
content: makeTemplateSnapshot(elements, name),
|
|
205
|
-
});
|
|
206
|
-
return [
|
|
207
|
-
t('Simple process', previews.process, process),
|
|
208
|
-
t('Message exchange', previews.messageExchange, messageExchange),
|
|
209
|
-
t('Start event', previews.startEvent, single(node('startEvent', 0, 0))),
|
|
210
|
-
t('End event', previews.endEvent, single(node('endEvent', 0, 0))),
|
|
211
|
-
t('Task', previews.task, single(node('task', 0, 0))),
|
|
212
|
-
t('Exclusive gateway', previews.gateway, single(node('gatewayExclusive', 0, 0))),
|
|
213
|
-
t('Sequence flow', previews.sequence, single(freeSeq())),
|
|
214
|
-
t('Pool', previews.pool, single(pool(0, 0, 560, 200))),
|
|
215
|
-
];
|
|
216
|
-
}
|
|
219
|
+
const messageExchange = {
|
|
220
|
+
customer: pool(0, 0, 640, 200, 'Customer'),
|
|
221
|
+
supplier: pool(0, 240, 640, 200, 'Supplier'),
|
|
222
|
+
start: node('startEvent', 40, 72),
|
|
223
|
+
ask: node('taskUser', 140, 64, 'Place order'),
|
|
224
|
+
done: node('endEvent', 320, 72),
|
|
225
|
+
answer: node('taskService', 140, 304, 'Confirm order'),
|
|
226
|
+
// Inside the first participant: what happens, and in what order.
|
|
227
|
+
inside: seq('start', 'ask'),
|
|
228
|
+
closes: seq('ask', 'done'),
|
|
229
|
+
// Across the two: who told whom.
|
|
230
|
+
across: msg('ask', 'answer'),
|
|
231
|
+
};
|
|
217
232
|
export const bpmnTemplateCategory = {
|
|
218
233
|
name: 'BPMN',
|
|
219
|
-
templates:
|
|
234
|
+
templates: [
|
|
235
|
+
scene('Simple process', previews.process, process),
|
|
236
|
+
scene('Message exchange', previews.messageExchange, messageExchange),
|
|
237
|
+
/* ── The core: a drawable process, from the first click ─────────────── */
|
|
238
|
+
templateFromCommand(byId('bpmn.addStartEvent'), previews.startEvent),
|
|
239
|
+
templateFromCommand(byId('bpmn.addEndEvent'), previews.endEvent),
|
|
240
|
+
templateFromCommand(byId('bpmn.addTask'), previews.task),
|
|
241
|
+
templateFromCommand(byId('bpmn.addExclusiveGateway'), previews.gateway),
|
|
242
|
+
scene('Sequence flow', previews.sequence, { a: freeSeq() }),
|
|
243
|
+
templateFromCommand(byId('bpmn.addPool'), previews.pool),
|
|
244
|
+
/* ── Activities ─────────────────────────────────────────────────────── */
|
|
245
|
+
templateFromCommand(byId('bpmn.addUserTask'), previews.taskUser),
|
|
246
|
+
templateFromCommand(byId('bpmn.addServiceTask'), previews.taskService),
|
|
247
|
+
templateFromCommand(byId('bpmn.addSubProcess'), previews.subProcess),
|
|
248
|
+
templateFromCommand(byId('bpmn.addCallActivity'), previews.callActivity),
|
|
249
|
+
/* ── The other gateway ──────────────────────────────────────────────── */
|
|
250
|
+
templateFromCommand(byId('bpmn.addParallelGateway'), previews.gatewayParallel),
|
|
251
|
+
/* ── Event variants ─────────────────────────────────────────────────── */
|
|
252
|
+
templateFromCommand(byId('bpmn.addMessageStartEvent'), previews.startEventMessage),
|
|
253
|
+
templateFromCommand(byId('bpmn.addTimerStartEvent'), previews.startEventTimer),
|
|
254
|
+
templateFromCommand(byId('bpmn.addMessageEndEvent'), previews.endEventMessage),
|
|
255
|
+
templateFromCommand(byId('bpmn.addTerminateEndEvent'), previews.endEventTerminate),
|
|
256
|
+
/* ── Data, and what an author writes ON the picture ──────────────────── */
|
|
257
|
+
templateFromCommand(byId('bpmn.addDataObject'), previews.dataObject),
|
|
258
|
+
templateFromCommand(byId('bpmn.addDataStore'), previews.dataStore),
|
|
259
|
+
templateFromCommand(byId('bpmn.addTextAnnotation'), previews.textAnnotation),
|
|
260
|
+
templateFromCommand(byId('bpmn.addGroup'), previews.group),
|
|
261
|
+
],
|
|
220
262
|
};
|
package/dist/translations.js
CHANGED
|
@@ -3,6 +3,7 @@ import { bpmnCommands } from './commands.js';
|
|
|
3
3
|
import { NODE_LABEL, nodeLabelKey } from './consts.js';
|
|
4
4
|
import { BPMN_IMPORT_REMARKS } from './import.js';
|
|
5
5
|
import { BPMN_PROFILES } from './profiles.js';
|
|
6
|
+
import { BPMN_READINGS } from './reading.js';
|
|
6
7
|
import { BPMN_ROLES } from './roles.js';
|
|
7
8
|
import { BPMN_RULES } from './rules.js';
|
|
8
9
|
/**
|
|
@@ -47,4 +48,9 @@ export const bpmnTranslationEntries = mergeTranslationEntries(commandTranslation
|
|
|
47
48
|
// registry names no framework category in the bundled distribution, so a
|
|
48
49
|
// host that composed core's manifest alone drew translated entries under
|
|
49
50
|
// English headers (#183).
|
|
50
|
-
commandCategoryTranslationEntries(bpmnCommands), collectTranslationKeys('role', BPMN_ROLES), collectTranslationKeys('rule', BPMN_RULES), collectTranslationKeys('profile', BPMN_PROFILES), seedEntries(), importRemarkEntries()
|
|
51
|
+
commandCategoryTranslationEntries(bpmnCommands), collectTranslationKeys('role', BPMN_ROLES), collectTranslationKeys('rule', BPMN_RULES), collectTranslationKeys('profile', BPMN_PROFILES), seedEntries(), importRemarkEntries(),
|
|
52
|
+
// LAST, and the order is load-bearing: a reading profile carries the
|
|
53
|
+
// framework's own `roles`, so walking it reaches every role key the `role`
|
|
54
|
+
// line above already named. `mergeTranslationEntries` keeps the FIRST
|
|
55
|
+
// occurrence, which is what makes each key report the source it comes from.
|
|
56
|
+
collectTranslationKeys('reading', BPMN_READINGS));
|
package/dist/view.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FrameworkBackgroundInteractionExtension, InterchangeExtension, morphToolbarConfig, ValidationProfileExtension, ValidationRuleExtension, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { FrameworkBackgroundInteractionExtension, InterchangeExtension, morphToolbarConfig, ReadingProfileExtension, ValidationProfileExtension, ValidationRuleExtension, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
|
|
3
3
|
import { extendTemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
4
4
|
import { ToolbarModuleExtension } from '@formicoidea/labre-core/shared/services';
|
|
@@ -10,6 +10,7 @@ import { effects } from './effects.js';
|
|
|
10
10
|
import { BPMN_INTERCHANGE } from './interchange.js';
|
|
11
11
|
import { BPMN_MORPH_SPEC } from './morph.js';
|
|
12
12
|
import { BPMN_PROFILES } from './profiles.js';
|
|
13
|
+
import { BPMN_READINGS } from './reading.js';
|
|
13
14
|
import { BPMN_ROLES } from './roles.js';
|
|
14
15
|
import { BPMN_RULES } from './rules.js';
|
|
15
16
|
import { bpmnTemplateCategory } from './templates/index.js';
|
|
@@ -114,6 +115,15 @@ export class BpmnViewExtension extends ViewExtensionProvider {
|
|
|
114
115
|
id: BlockFlavourIdentifier('affine:surface:bpmnNode'),
|
|
115
116
|
config: morphToolbarConfig(BPMN_MORPH_SPEC),
|
|
116
117
|
}));
|
|
118
|
+
// The reversed reading (MF3): what the process says about an artefact, on
|
|
119
|
+
// demand. Four profiles because BPMN has four parent-less node families
|
|
120
|
+
// (`reading.ts` says why the notation refuses a common root). The CLICK
|
|
121
|
+
// that triggers it is registered once by the surface and gated by the
|
|
122
|
+
// presence of a profile, so it goes with the flag without either side
|
|
123
|
+
// naming the other.
|
|
124
|
+
for (const reading of BPMN_READINGS) {
|
|
125
|
+
context.register(ReadingProfileExtension(reading));
|
|
126
|
+
}
|
|
117
127
|
context.register(bpmnSeniorTool);
|
|
118
128
|
context.register(CommandExtension(bpmnCommands, bpmnCommandIcons));
|
|
119
129
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formicoidea/labre-framework-bpmn",
|
|
3
3
|
"description": "Labre bpmn framework for @formicoidea/labre-core.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.38.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "lajola",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dist"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@formicoidea/labre-core": "0.
|
|
34
|
+
"@formicoidea/labre-core": "0.38.1",
|
|
35
35
|
"lit": "^3.2.0"
|
|
36
36
|
}
|
|
37
37
|
}
|