@formicoidea/labre-framework-bpmn 0.43.1 → 0.46.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/actions.d.ts +11 -1
- package/dist/actions.js +41 -11
- package/dist/consts.d.ts +72 -27
- package/dist/consts.js +106 -42
- package/dist/element-renderer.d.ts +1 -1
- package/dist/export.d.ts +12 -0
- package/dist/export.js +3 -1
- package/dist/import.d.ts +0 -11
- package/dist/import.js +98 -30
- package/dist/interchange.d.ts +7 -12
- package/dist/interchange.js +61 -22
- package/dist/morph.d.ts +37 -1
- package/dist/morph.js +124 -2
- package/dist/node/node-renderer.d.ts +1 -1
- package/dist/node/node-view.d.ts +24 -3
- package/dist/node/node-view.js +31 -4
- package/dist/presets.d.ts +32 -1
- package/dist/presets.js +44 -3
- package/dist/reading.js +41 -3
- package/dist/roles.d.ts +2 -1
- package/dist/roles.js +23 -0
- package/dist/rules.d.ts +4 -1
- package/dist/rules.js +24 -8
- package/dist/templates/index.d.ts +20 -0
- package/dist/templates/index.js +74 -14
- package/dist/toolbar/config.d.ts +2 -2
- package/dist/toolbar/node-config.d.ts +21 -0
- package/dist/toolbar/node-config.js +88 -0
- package/dist/toolbar/senior-tool.d.ts +1 -1
- package/dist/view.js +38 -16
- package/package.json +2 -2
package/dist/presets.d.ts
CHANGED
|
@@ -67,7 +67,9 @@ export interface BpmnLabelFit {
|
|
|
67
67
|
*
|
|
68
68
|
* Every value the creation builder writes is calibrated against
|
|
69
69
|
* {@link NODE_SIZE}: an 18-unit font and the shape's native 20-unit horizontal
|
|
70
|
-
* inset are comfortable in a 120-unit task
|
|
70
|
+
* inset are comfortable in a 120-unit task — the box the palette drew when
|
|
71
|
+
* this was written, kept as the activities' fit reference
|
|
72
|
+
* (`ACTIVITY_FIT_REF`) now that it draws 180×108.
|
|
71
73
|
* A file draws to its author's scale, and bpmn.io's normative sizes — the ones
|
|
72
74
|
* nearly every `.bpmn` in the wild carries — are a 100×80 task and a 36-unit
|
|
73
75
|
* event. In a 100-unit task the native inset leaves 60 units of line, which is
|
|
@@ -124,6 +126,35 @@ export declare function bpmnNodeProps(kind: BpmnNodeKind, box: {
|
|
|
124
126
|
}): Record<string, unknown> & {
|
|
125
127
|
type: string;
|
|
126
128
|
};
|
|
129
|
+
/**
|
|
130
|
+
* Where an external label's box goes for a symbol centred on `(cx, cy)`:
|
|
131
|
+
* centred on the symbol's vertical axis, {@link BPMN_LABEL_GAP} under its
|
|
132
|
+
* bottom edge — the placement BPMN's own cartography uses for an event's, a
|
|
133
|
+
* gateway's or a data shape's name.
|
|
134
|
+
*
|
|
135
|
+
* Reads the kind's canonical size, so the creation gesture and a template scene
|
|
136
|
+
* agree; an importer that keeps a file's own box passes that box's bottom
|
|
137
|
+
* through {@link bpmnLabelBoxUnder} instead.
|
|
138
|
+
*/
|
|
139
|
+
export declare function bpmnLabelBoxFor(kind: BpmnNodeKind, cx: number, cy: number): {
|
|
140
|
+
x: number;
|
|
141
|
+
y: number;
|
|
142
|
+
};
|
|
143
|
+
/** The label box for a symbol whose vertical axis is `cx` and bottom edge `bottom`. */
|
|
144
|
+
export declare function bpmnLabelBoxUnder(cx: number, bottom: number): {
|
|
145
|
+
x: number;
|
|
146
|
+
y: number;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* A gravitating label, as props — a free text element in the pack's own type,
|
|
150
|
+
* centred, carrying the role that makes it a NAME rather than a note.
|
|
151
|
+
*
|
|
152
|
+
* Mirrors `wardleyLabelProps`; one builder so the palette, the scenes and the
|
|
153
|
+
* importer write the same element.
|
|
154
|
+
*/
|
|
155
|
+
export declare function bpmnLabelProps(text: string, x: number, y: number): Record<string, unknown> & {
|
|
156
|
+
type: string;
|
|
157
|
+
};
|
|
127
158
|
/**
|
|
128
159
|
* One BPMN pool, as the props `surface.addElement` takes.
|
|
129
160
|
*
|
package/dist/presets.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
|
-
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';
|
|
3
|
+
import { ACTIVITY_FIT_REF, BPMN_LABEL_GAP, BPMN_LABEL_H, BPMN_LABEL_W, CALL_ACTIVITY_WIDTH, END_WIDTH, EVENT_END, EVENT_START, GROUP_RADIUS, GROUP_STROKE, INNER_FONT_SIZE, isBpmnActivityKind, LABEL_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
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 = {
|
|
@@ -141,7 +141,9 @@ const insetOf = (extent, native) => Number.isFinite(extent) && extent > 0
|
|
|
141
141
|
*
|
|
142
142
|
* Every value the creation builder writes is calibrated against
|
|
143
143
|
* {@link NODE_SIZE}: an 18-unit font and the shape's native 20-unit horizontal
|
|
144
|
-
* inset are comfortable in a 120-unit task
|
|
144
|
+
* inset are comfortable in a 120-unit task — the box the palette drew when
|
|
145
|
+
* this was written, kept as the activities' fit reference
|
|
146
|
+
* (`ACTIVITY_FIT_REF`) now that it draws 180×108.
|
|
145
147
|
* A file draws to its author's scale, and bpmn.io's normative sizes — the ones
|
|
146
148
|
* nearly every `.bpmn` in the wild carries — are a 100×80 task and a 36-unit
|
|
147
149
|
* event. In a 100-unit task the native inset leaves 60 units of line, which is
|
|
@@ -171,7 +173,9 @@ const insetOf = (extent, native) => Number.isFinite(extent) && extent > 0
|
|
|
171
173
|
* box was never going to hold.
|
|
172
174
|
*/
|
|
173
175
|
export function bpmnLabelFit(kind, w, h) {
|
|
174
|
-
|
|
176
|
+
// An activity is fitted against the box its type was calibrated in, not the
|
|
177
|
+
// box the palette now draws (see `ACTIVITY_FIT_REF`).
|
|
178
|
+
const size = isBpmnActivityKind(kind) ? ACTIVITY_FIT_REF : NODE_SIZE[kind];
|
|
175
179
|
const scale = Math.min(1, ratioOf(w, size.w), ratioOf(h, size.h));
|
|
176
180
|
return {
|
|
177
181
|
fontSize: Math.max(LABEL_MIN_FONT_SIZE, Math.round(INNER_FONT_SIZE * scale)),
|
|
@@ -256,6 +260,43 @@ export function bpmnNodeProps(kind, box) {
|
|
|
256
260
|
xywh: box.xywh,
|
|
257
261
|
};
|
|
258
262
|
}
|
|
263
|
+
/* ── The gravitating label (R38) ───────────────────────────────────────── */
|
|
264
|
+
/**
|
|
265
|
+
* Where an external label's box goes for a symbol centred on `(cx, cy)`:
|
|
266
|
+
* centred on the symbol's vertical axis, {@link BPMN_LABEL_GAP} under its
|
|
267
|
+
* bottom edge — the placement BPMN's own cartography uses for an event's, a
|
|
268
|
+
* gateway's or a data shape's name.
|
|
269
|
+
*
|
|
270
|
+
* Reads the kind's canonical size, so the creation gesture and a template scene
|
|
271
|
+
* agree; an importer that keeps a file's own box passes that box's bottom
|
|
272
|
+
* through {@link bpmnLabelBoxUnder} instead.
|
|
273
|
+
*/
|
|
274
|
+
export function bpmnLabelBoxFor(kind, cx, cy) {
|
|
275
|
+
return bpmnLabelBoxUnder(cx, cy + NODE_SIZE[kind].h / 2);
|
|
276
|
+
}
|
|
277
|
+
/** The label box for a symbol whose vertical axis is `cx` and bottom edge `bottom`. */
|
|
278
|
+
export function bpmnLabelBoxUnder(cx, bottom) {
|
|
279
|
+
return { x: cx - BPMN_LABEL_W / 2, y: bottom + BPMN_LABEL_GAP };
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* A gravitating label, as props — a free text element in the pack's own type,
|
|
283
|
+
* centred, carrying the role that makes it a NAME rather than a note.
|
|
284
|
+
*
|
|
285
|
+
* Mirrors `wardleyLabelProps`; one builder so the palette, the scenes and the
|
|
286
|
+
* importer write the same element.
|
|
287
|
+
*/
|
|
288
|
+
export function bpmnLabelProps(text, x, y) {
|
|
289
|
+
return {
|
|
290
|
+
type: 'text',
|
|
291
|
+
text,
|
|
292
|
+
role: BPMN_ROLE.label,
|
|
293
|
+
fontFamily: FontFamily.Inter,
|
|
294
|
+
fontSize: LABEL_FONT_SIZE,
|
|
295
|
+
color: NEUTRAL_STROKE,
|
|
296
|
+
textAlign: TextAlign.Center,
|
|
297
|
+
xywh: new Bound(x, y, BPMN_LABEL_W, BPMN_LABEL_H).serialize(),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
259
300
|
/**
|
|
260
301
|
* One BPMN pool, as the props `surface.addElement` takes.
|
|
261
302
|
*
|
package/dist/reading.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BPMN_POOL_BACKGROUND } from './background.js';
|
|
1
2
|
import { BPMN_ROLE, BPMN_ROLES } from './roles.js';
|
|
2
3
|
/**
|
|
3
4
|
* What BPMN lets the tool read of an artefact (MF3, the reversed reading).
|
|
@@ -35,13 +36,44 @@ import { BPMN_ROLE, BPMN_ROLES } from './roles.js';
|
|
|
35
36
|
* under the sequence flow's two words would put a sentence in the panel that
|
|
36
37
|
* the notation never said.
|
|
37
38
|
*
|
|
38
|
-
* ##
|
|
39
|
+
* ## A name inscribed, or a name beside
|
|
40
|
+
*
|
|
41
|
+
* Since R38 (`docs/adr/0029`) an event, a gateway and a data shape carry their
|
|
42
|
+
* name on a free `bpmn:label` text grouped with them, not as inner text. The
|
|
43
|
+
* two profiles that read them therefore declare `labelRole`, which the engine
|
|
44
|
+
* falls back to when the subject's own text is empty — Wardley's reading does
|
|
45
|
+
* the same. An activity's inscribed name still wins, because own text is read
|
|
46
|
+
* first; the annotation and the group ARE their text and need no fallback.
|
|
47
|
+
*
|
|
48
|
+
* ## No nature; a lane, not a phase
|
|
39
49
|
*
|
|
40
50
|
* BPMN ships no type-3 tag pack: its kinds ARE the roles, and the panel says
|
|
41
51
|
* them on the type line, chain included ("Message start event › Start event ›
|
|
42
|
-
* Event › Flow object").
|
|
43
|
-
*
|
|
52
|
+
* Event › Flow object").
|
|
53
|
+
*
|
|
54
|
+
* A pool's lanes are not an ordered axis, but they ARE where a flow object or a
|
|
55
|
+
* data shape is drawn: the lane its centre falls in, read in two dimensions (no
|
|
56
|
+
* `axis`) off the pool's own `lanes` and named by the name the user gave it —
|
|
57
|
+
* the same lane `bpmnLaneOf` and the audit report. The annotation and the group
|
|
58
|
+
* are left without a frame: commentary is about the picture, not in a lane, and
|
|
59
|
+
* §10.4 lets a group cross every lane there is.
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* The pool, read for its lanes. Shared by the two profiles that sit IN a lane,
|
|
63
|
+
* so a task and the data object beside it are placed by one declaration.
|
|
44
64
|
*/
|
|
65
|
+
const BPMN_LANE_FRAME = {
|
|
66
|
+
backgroundRole: BPMN_ROLE.pool,
|
|
67
|
+
background: BPMN_POOL_BACKGROUND,
|
|
68
|
+
label: {
|
|
69
|
+
labelKey: 'com.labre.bpmn.reading.field.lane',
|
|
70
|
+
labelFallback: 'Lane',
|
|
71
|
+
},
|
|
72
|
+
none: {
|
|
73
|
+
labelKey: 'com.labre.bpmn.reading.lane.none',
|
|
74
|
+
labelFallback: 'Not in a lane of a pool.',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
45
77
|
/**
|
|
46
78
|
* The two words a sequence flow is read with, from the subject's end.
|
|
47
79
|
*
|
|
@@ -65,7 +97,10 @@ export const BPMN_READING = {
|
|
|
65
97
|
framework: 'bpmn',
|
|
66
98
|
roles: BPMN_ROLES,
|
|
67
99
|
appliesTo: BPMN_ROLE.flowObject,
|
|
100
|
+
// An event's or a gateway's name gravitates (R38): see the module comment.
|
|
101
|
+
labelRole: BPMN_ROLE.label,
|
|
68
102
|
relation: { edgeRole: BPMN_ROLE.sequenceFlow, sides: SEQUENCE_SIDES },
|
|
103
|
+
frame: BPMN_LANE_FRAME,
|
|
69
104
|
};
|
|
70
105
|
/**
|
|
71
106
|
* The paperwork: a data object and a data store.
|
|
@@ -78,6 +113,9 @@ export const BPMN_DATA_READING = {
|
|
|
78
113
|
framework: 'bpmn',
|
|
79
114
|
roles: BPMN_ROLES,
|
|
80
115
|
appliesTo: BPMN_ROLE.data,
|
|
116
|
+
// Both data shapes are named beside the symbol (R38).
|
|
117
|
+
labelRole: BPMN_ROLE.label,
|
|
118
|
+
frame: BPMN_LANE_FRAME,
|
|
81
119
|
};
|
|
82
120
|
/** A note on the picture. Readable, so a reader can link it to a record. */
|
|
83
121
|
export const BPMN_ANNOTATION_READING = {
|
package/dist/roles.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ import type { RoleDefs, RoleId } from '@formicoidea/labre-core/std/gfx';
|
|
|
51
51
|
* same promise every role in this library has made (PRD principle 8).
|
|
52
52
|
*/
|
|
53
53
|
/** Every role this framework declares. */
|
|
54
|
-
export type BpmnRole = 'flow-object' | 'event' | 'start-event' | 'message-start-event' | 'timer-start-event' | 'end-event' | 'message-end-event' | 'terminate-end-event' | 'activity' | 'task' | 'user-task' | 'service-task' | 'sub-process' | 'call-activity' | 'gateway' | 'gateway-exclusive' | 'parallel-gateway' | 'data' | 'data-object' | 'data-store' | 'text-annotation' | 'group' | 'pool' | 'sequence-flow' | 'message-flow' | 'association';
|
|
54
|
+
export type BpmnRole = 'flow-object' | 'event' | 'start-event' | 'message-start-event' | 'timer-start-event' | 'end-event' | 'message-end-event' | 'terminate-end-event' | 'activity' | 'task' | 'user-task' | 'service-task' | 'sub-process' | 'call-activity' | 'gateway' | 'gateway-exclusive' | 'parallel-gateway' | 'data' | 'data-object' | 'data-store' | 'text-annotation' | 'group' | 'pool' | 'sequence-flow' | 'message-flow' | 'association' | 'label';
|
|
55
55
|
export type BpmnRoleId = `bpmn:${BpmnRole}`;
|
|
56
56
|
/** Role ids, keyed by the `kind` used at the creation sites. */
|
|
57
57
|
export declare const BPMN_ROLE: {
|
|
@@ -81,6 +81,7 @@ export declare const BPMN_ROLE: {
|
|
|
81
81
|
readonly sequenceFlow: "bpmn:sequence-flow";
|
|
82
82
|
readonly messageFlow: "bpmn:message-flow";
|
|
83
83
|
readonly association: "bpmn:association";
|
|
84
|
+
readonly label: "bpmn:label";
|
|
84
85
|
};
|
|
85
86
|
export declare const BPMN_ROLES: RoleDefs;
|
|
86
87
|
/**
|
package/dist/roles.js
CHANGED
|
@@ -40,6 +40,10 @@ export const BPMN_ROLE = {
|
|
|
40
40
|
sequenceFlow: 'bpmn:sequence-flow',
|
|
41
41
|
messageFlow: 'bpmn:message-flow',
|
|
42
42
|
association: 'bpmn:association',
|
|
43
|
+
// The gravitating name: a free text grouped under an event, a gateway or a
|
|
44
|
+
// data shape. Same call as `wardley:label` — the role is the only thing that
|
|
45
|
+
// tells a name from a free text the author typed beside the process.
|
|
46
|
+
label: 'bpmn:label',
|
|
43
47
|
};
|
|
44
48
|
const _everyRoleIsMapped = true;
|
|
45
49
|
void _everyRoleIsMapped;
|
|
@@ -369,12 +373,31 @@ const FLOW_DEFS = [
|
|
|
369
373
|
labelFallback: 'Association',
|
|
370
374
|
},
|
|
371
375
|
];
|
|
376
|
+
/**
|
|
377
|
+
* The gravitating label (R38): the text element grouped under an event, a
|
|
378
|
+
* gateway or a data shape, whose words are that artefact's NAME.
|
|
379
|
+
*
|
|
380
|
+
* Kind `text`, parent-less: it is not a flow object, not data and not an
|
|
381
|
+
* artifact, and no rule about the process may fall on the words that name a
|
|
382
|
+
* step. It exists so the export, the reading panel and the board picker can
|
|
383
|
+
* tell a name from a free text the author typed beside the process — exactly
|
|
384
|
+
* what `wardley:label` does for a component.
|
|
385
|
+
*/
|
|
386
|
+
const LABEL_DEFS = [
|
|
387
|
+
{
|
|
388
|
+
id: BPMN_ROLE.label,
|
|
389
|
+
kind: 'text',
|
|
390
|
+
labelKey: roleKey(BPMN_ROLE.label),
|
|
391
|
+
labelFallback: 'Label',
|
|
392
|
+
},
|
|
393
|
+
];
|
|
372
394
|
const DEFS = [
|
|
373
395
|
...FLOW_OBJECT_DEFS,
|
|
374
396
|
...DATA_DEFS,
|
|
375
397
|
...ARTIFACT_DEFS,
|
|
376
398
|
...POOL_DEFS,
|
|
377
399
|
...FLOW_DEFS,
|
|
400
|
+
...LABEL_DEFS,
|
|
378
401
|
];
|
|
379
402
|
// Null prototype: this is a lookup table keyed by ids that may one day come
|
|
380
403
|
// from host-supplied packs, so `defs['toString']` must not resolve.
|
package/dist/rules.d.ts
CHANGED
|
@@ -147,7 +147,10 @@ import type { EndpointTriplet, ValidationRule } from '@formicoidea/labre-core/bl
|
|
|
147
147
|
* not a bound that failed and its sentence never reads like one;
|
|
148
148
|
* - `label-presence` reads the subject's OWN `text`. A framework whose artefacts
|
|
149
149
|
* are named by a separate element beside them is asking a different question,
|
|
150
|
-
* and this family cannot answer it. BPMN names its
|
|
150
|
+
* and this family cannot answer it. BPMN names its ACTIVITIES in place, so it
|
|
151
|
+
* can ask it of them; since R38 its events, gateways and data shapes are
|
|
152
|
+
* named by a grouped `bpmn:label` instead, and are not asked (see
|
|
153
|
+
* {@link unlabeledStep}).
|
|
151
154
|
*/
|
|
152
155
|
/**
|
|
153
156
|
* `eitherMin` was also shipped, and this pack deliberately does not use it.
|
package/dist/rules.js
CHANGED
|
@@ -148,7 +148,10 @@ import { BPMN_ROLE, BPMN_ROLES } from './roles.js';
|
|
|
148
148
|
* not a bound that failed and its sentence never reads like one;
|
|
149
149
|
* - `label-presence` reads the subject's OWN `text`. A framework whose artefacts
|
|
150
150
|
* are named by a separate element beside them is asking a different question,
|
|
151
|
-
* and this family cannot answer it. BPMN names its
|
|
151
|
+
* and this family cannot answer it. BPMN names its ACTIVITIES in place, so it
|
|
152
|
+
* can ask it of them; since R38 its events, gateways and data shapes are
|
|
153
|
+
* named by a grouped `bpmn:label` instead, and are not asked (see
|
|
154
|
+
* {@link unlabeledStep}).
|
|
152
155
|
*/
|
|
153
156
|
/**
|
|
154
157
|
* `eitherMin` was also shipped, and this pack deliberately does not use it.
|
|
@@ -1452,23 +1455,36 @@ const singleBlankStart = {
|
|
|
1452
1455
|
* **`audit`**, because an unnamed step is a diagram that is not done rather than
|
|
1453
1456
|
* a diagram that is wrong, and the panel is where "not done" belongs.
|
|
1454
1457
|
*
|
|
1455
|
-
* Written on `bpmn:
|
|
1456
|
-
*
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
1459
|
-
*
|
|
1458
|
+
* Written on `bpmn:activity`, and only there. The `label-presence` family
|
|
1459
|
+
* reads the subject's OWN `text`, and since R38 (`docs/adr/0029`) only an
|
|
1460
|
+
* activity keeps its name there: an event, a gateway or a data shape is named
|
|
1461
|
+
* by a free `bpmn:label` text grouped with it, which the family cannot see
|
|
1462
|
+
* (`packages/affine/blocks/surface/src/extensions/validation.ts`, the
|
|
1463
|
+
* label-presence evaluator). Left on `bpmn:flow-object`, the rule would report
|
|
1464
|
+
* every NAMED event and gateway as unnamed.
|
|
1465
|
+
*
|
|
1466
|
+
* Losing them costs little: BPMN practice commonly leaves an event or a
|
|
1467
|
+
* converging gateway unnamed, while an activity is a box whose whole content is
|
|
1468
|
+
* its name — an empty one says nothing at all. The rule id is kept (ids are
|
|
1469
|
+
* forever); it was shipped as "step", and an activity is the step par
|
|
1470
|
+
* excellence.
|
|
1471
|
+
*
|
|
1472
|
+
* ponytail: the ceiling is "a gateway that asks no question is not reported".
|
|
1473
|
+
* The upgrade path is a label-presence mode that also reads the `labelRole`
|
|
1474
|
+
* member of the subject's group, the way `readName` does for the reading
|
|
1475
|
+
* panel — an engine change, not a pack one.
|
|
1460
1476
|
*/
|
|
1461
1477
|
const unlabeledStep = {
|
|
1462
1478
|
id: 'bpmn.unlabeled-step',
|
|
1463
1479
|
framework: 'bpmn',
|
|
1464
1480
|
family: 'label-presence',
|
|
1465
1481
|
severity: 'audit',
|
|
1466
|
-
appliesTo: BPMN_ROLE.
|
|
1482
|
+
appliesTo: BPMN_ROLE.activity,
|
|
1467
1483
|
roles: BPMN_ROLES,
|
|
1468
1484
|
messageKey: 'com.labre.bpmn.validation.unlabeled-step',
|
|
1469
1485
|
messageFallback: 'This step has no name.',
|
|
1470
1486
|
suggestionKey: 'com.labre.bpmn.validation.unlabeled-step.suggestion',
|
|
1471
|
-
suggestionFallback: 'Name it in a verb phrase a reader outside the room would understand — "Check the credit limit" rather than "Step 3".
|
|
1487
|
+
suggestionFallback: 'Name it in a verb phrase a reader outside the room would understand — "Check the credit limit" rather than "Step 3".',
|
|
1472
1488
|
version: 1,
|
|
1473
1489
|
// A name is nowhere required by the specification: an unnamed step is a
|
|
1474
1490
|
// diagram that is not done, never one that is wrong.
|
|
@@ -23,6 +23,18 @@ export declare const SIMPLE_PROCESS_SEED: {
|
|
|
23
23
|
readonly key: "com.labre.bpmn.example.simple-process.reject";
|
|
24
24
|
readonly fallback: "Reject";
|
|
25
25
|
};
|
|
26
|
+
readonly requestReceived: {
|
|
27
|
+
readonly key: "com.labre.bpmn.example.simple-process.request-received";
|
|
28
|
+
readonly fallback: "Request received";
|
|
29
|
+
};
|
|
30
|
+
readonly approved: {
|
|
31
|
+
readonly key: "com.labre.bpmn.example.simple-process.approved";
|
|
32
|
+
readonly fallback: "Approved?";
|
|
33
|
+
};
|
|
34
|
+
readonly requestHandled: {
|
|
35
|
+
readonly key: "com.labre.bpmn.example.simple-process.request-handled";
|
|
36
|
+
readonly fallback: "Request handled";
|
|
37
|
+
};
|
|
26
38
|
};
|
|
27
39
|
/**
|
|
28
40
|
* The two worked scenes' and the free-standing "Sequence flow" swatch's own
|
|
@@ -53,5 +65,13 @@ export declare const MESSAGE_EXCHANGE_SEED: {
|
|
|
53
65
|
readonly key: "com.labre.bpmn.example.message-exchange.confirm-order";
|
|
54
66
|
readonly fallback: "Confirm order";
|
|
55
67
|
};
|
|
68
|
+
readonly orderNeeded: {
|
|
69
|
+
readonly key: "com.labre.bpmn.example.message-exchange.order-needed";
|
|
70
|
+
readonly fallback: "Order needed";
|
|
71
|
+
};
|
|
72
|
+
readonly orderPlaced: {
|
|
73
|
+
readonly key: "com.labre.bpmn.example.message-exchange.order-placed";
|
|
74
|
+
readonly fallback: "Order placed";
|
|
75
|
+
};
|
|
56
76
|
};
|
|
57
77
|
export declare const bpmnTemplateCategory: TemplateCategory;
|
package/dist/templates/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { makeTemplateSnapshot, surfaceText, templateFromCommand, } from '@formicoidea/labre-core/gfx/template';
|
|
1
|
+
import { makeTemplateSnapshot, surfaceText, surfaceYMap, templateFromCommand, } from '@formicoidea/labre-core/gfx/template';
|
|
2
2
|
import { ConnectorMode, PointStyle, StrokeStyle, } from '@formicoidea/labre-core/model';
|
|
3
3
|
import { translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
4
4
|
import { bpmnCommands } from '../commands.js';
|
|
5
5
|
import { EVENT_END, EVENT_START, GROUP_STROKE, MESSAGE_STROKE, MESSAGE_WIDTH, NEUTRAL_STROKE, NODE_SIZE, POOL_BAND_FILL, POOL_FRAME_COLOR, SEQUENCE_STROKE, SEQUENCE_WIDTH, } from '../consts.js';
|
|
6
|
-
import { bpmnNodeProps, bpmnPoolProps } from '../presets.js';
|
|
6
|
+
import { bpmnLabelBoxFor, bpmnLabelProps, bpmnNodeProps, bpmnPoolProps, } from '../presets.js';
|
|
7
7
|
import { BPMN_ROLE } from '../roles.js';
|
|
8
8
|
/**
|
|
9
9
|
* The seeds the two worked scenes write — declared once so the scene builder
|
|
@@ -28,6 +28,22 @@ export const SIMPLE_PROCESS_SEED = {
|
|
|
28
28
|
key: 'com.labre.bpmn.example.simple-process.reject',
|
|
29
29
|
fallback: 'Reject',
|
|
30
30
|
},
|
|
31
|
+
// The three names the scene's events and gateway carry since they gravitate
|
|
32
|
+
// (R38): a worked example names its punctual symbols the way BPMN practice
|
|
33
|
+
// does — a state for an event, a question for a gateway — rather than
|
|
34
|
+
// repeating the artefact's kind under it.
|
|
35
|
+
requestReceived: {
|
|
36
|
+
key: 'com.labre.bpmn.example.simple-process.request-received',
|
|
37
|
+
fallback: 'Request received',
|
|
38
|
+
},
|
|
39
|
+
approved: {
|
|
40
|
+
key: 'com.labre.bpmn.example.simple-process.approved',
|
|
41
|
+
fallback: 'Approved?',
|
|
42
|
+
},
|
|
43
|
+
requestHandled: {
|
|
44
|
+
key: 'com.labre.bpmn.example.simple-process.request-handled',
|
|
45
|
+
fallback: 'Request handled',
|
|
46
|
+
},
|
|
31
47
|
};
|
|
32
48
|
/**
|
|
33
49
|
* The two worked scenes' and the free-standing "Sequence flow" swatch's own
|
|
@@ -67,6 +83,15 @@ export const MESSAGE_EXCHANGE_SEED = {
|
|
|
67
83
|
key: 'com.labre.bpmn.example.message-exchange.confirm-order',
|
|
68
84
|
fallback: 'Confirm order',
|
|
69
85
|
},
|
|
86
|
+
// The customer's two events, named since they gravitate (R38).
|
|
87
|
+
orderNeeded: {
|
|
88
|
+
key: 'com.labre.bpmn.example.message-exchange.order-needed',
|
|
89
|
+
fallback: 'Order needed',
|
|
90
|
+
},
|
|
91
|
+
orderPlaced: {
|
|
92
|
+
key: 'com.labre.bpmn.example.message-exchange.order-placed',
|
|
93
|
+
fallback: 'Order placed',
|
|
94
|
+
},
|
|
70
95
|
};
|
|
71
96
|
/**
|
|
72
97
|
* One seed's resolved text — the host's catalogue when `std` is a real
|
|
@@ -121,6 +146,30 @@ function node(kind, x, y, text) {
|
|
|
121
146
|
...(text === undefined ? {} : { text: surfaceText(text) }),
|
|
122
147
|
});
|
|
123
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* A scene node whose name GRAVITATES (R38, `bpmnLabelMode`): the symbol with no
|
|
151
|
+
* inner text, its label centred under it, and the native group that binds the
|
|
152
|
+
* two — the three elements `createBpmnNode` draws for the same kind, and the
|
|
153
|
+
* pattern Wardley's shipped maps use (`pair()` in `gfx/wardley`).
|
|
154
|
+
*
|
|
155
|
+
* Spread into a scene: `<id>` is the symbol, so connectors keep binding to it,
|
|
156
|
+
* `<id>Label` its name and `<id>Group` the pair.
|
|
157
|
+
*/
|
|
158
|
+
function named(id, kind, x, y, text) {
|
|
159
|
+
const { w, h } = NODE_SIZE[kind];
|
|
160
|
+
const at = bpmnLabelBoxFor(kind, x + w / 2, y + h / 2);
|
|
161
|
+
const label = `${id}Label`;
|
|
162
|
+
return {
|
|
163
|
+
[id]: node(kind, x, y),
|
|
164
|
+
// A snapshot carries a serialized `Y.Text`, where `addElement` takes a
|
|
165
|
+
// plain string.
|
|
166
|
+
[label]: { ...bpmnLabelProps(text, at.x, at.y), text: surfaceText(text) },
|
|
167
|
+
[`${id}Group`]: {
|
|
168
|
+
type: 'group',
|
|
169
|
+
children: surfaceYMap({ [id]: true, [label]: true }),
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
124
173
|
/** A scene participant, from the same description the toolbox draws from. */
|
|
125
174
|
const pool = (x, y, w, h, name) => defined(bpmnPoolProps({ xywh: `[${x},${y},${w},${h}]`, name }));
|
|
126
175
|
/** Drop the keys a preset left undefined — what `addElement` would store. */
|
|
@@ -261,15 +310,24 @@ const scene = (name, preview, build, nameKey) => ({
|
|
|
261
310
|
content: makeTemplateSnapshot(build(), name),
|
|
262
311
|
localize: std => makeTemplateSnapshot(build(std), name),
|
|
263
312
|
});
|
|
313
|
+
/**
|
|
314
|
+
* Start, a task, an exclusive split into two tasks, and the end they both reach.
|
|
315
|
+
*
|
|
316
|
+
* Laid out for the 180×108 activity (R38) on one horizontal axis at y = 160:
|
|
317
|
+
* the two branches sit 40 units apart with 32 units of margin to the pool's
|
|
318
|
+
* edges, and every symbol's gravitating label lands inside the pool as well —
|
|
319
|
+
* `bpmnPoolOf` judges whole containment, and a name hanging out of its
|
|
320
|
+
* participant would read as belonging to no one.
|
|
321
|
+
*/
|
|
264
322
|
function process(std) {
|
|
265
323
|
return {
|
|
266
|
-
pool: pool(0, 0,
|
|
267
|
-
start
|
|
268
|
-
task1: node('task',
|
|
269
|
-
gw
|
|
270
|
-
task2: node('task',
|
|
271
|
-
task3: node('task',
|
|
272
|
-
end
|
|
324
|
+
pool: pool(0, 0, 840, 320, seedText(std, SIMPLE_PROCESS_SEED.poolName)),
|
|
325
|
+
...named('start', 'startEvent', 72, 132, seedText(std, SIMPLE_PROCESS_SEED.requestReceived)),
|
|
326
|
+
task1: node('task', 168, 106, seedText(std, SIMPLE_PROCESS_SEED.submitRequest)),
|
|
327
|
+
...named('gw', 'gatewayExclusive', 388, 124, seedText(std, SIMPLE_PROCESS_SEED.approved)),
|
|
328
|
+
task2: node('task', 500, 32, seedText(std, SIMPLE_PROCESS_SEED.fulfil)),
|
|
329
|
+
task3: node('task', 500, 180, seedText(std, SIMPLE_PROCESS_SEED.reject)),
|
|
330
|
+
...named('end', 'endEvent', 720, 132, seedText(std, SIMPLE_PROCESS_SEED.requestHandled)),
|
|
273
331
|
c1: seq('start', 'task1'),
|
|
274
332
|
c2: seq('task1', 'gw'),
|
|
275
333
|
c3: seq('gw', 'task2'),
|
|
@@ -288,7 +346,9 @@ function process(std) {
|
|
|
288
346
|
* way to show that — the solid arrow stays home, the dashed one crosses.
|
|
289
347
|
*
|
|
290
348
|
* Stacked vertically with a 40-unit gutter, so the message flow is a straight
|
|
291
|
-
* orthogonal drop between two tasks that already line up.
|
|
349
|
+
* orthogonal drop between two tasks that already line up. Each 200-high pool
|
|
350
|
+
* centres its 108-high activity (R38), and the customer's two events keep
|
|
351
|
+
* their gravitating names inside the pool.
|
|
292
352
|
*
|
|
293
353
|
* ## The end event, which is not decoration
|
|
294
354
|
*
|
|
@@ -304,10 +364,10 @@ function messageExchange(std) {
|
|
|
304
364
|
return {
|
|
305
365
|
customer: pool(0, 0, 640, 200, seedText(std, MESSAGE_EXCHANGE_SEED.customer)),
|
|
306
366
|
supplier: pool(0, 240, 640, 200, seedText(std, MESSAGE_EXCHANGE_SEED.supplier)),
|
|
307
|
-
start
|
|
308
|
-
ask: node('taskUser',
|
|
309
|
-
done
|
|
310
|
-
answer: node('taskService',
|
|
367
|
+
...named('start', 'startEvent', 72, 72, seedText(std, MESSAGE_EXCHANGE_SEED.orderNeeded)),
|
|
368
|
+
ask: node('taskUser', 168, 46, seedText(std, MESSAGE_EXCHANGE_SEED.placeOrder)),
|
|
369
|
+
...named('done', 'endEvent', 388, 72, seedText(std, MESSAGE_EXCHANGE_SEED.orderPlaced)),
|
|
370
|
+
answer: node('taskService', 168, 286, seedText(std, MESSAGE_EXCHANGE_SEED.confirmOrder)),
|
|
311
371
|
// Inside the first participant: what happens, and in what order.
|
|
312
372
|
inside: seq('start', 'ask'),
|
|
313
373
|
closes: seq('ask', 'done'),
|
package/dist/toolbar/config.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare const bpmnPoolToolbarConfig: {
|
|
|
27
27
|
}, import("@formicoidea/labre-core/shared/services").ToolbarActionGenerator];
|
|
28
28
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
29
29
|
};
|
|
30
|
-
export declare const bpmnPoolToolbarExtension: import("@formicoidea/labre-core/store/index.js").ExtensionType;
|
|
30
|
+
export declare const bpmnPoolToolbarExtension: import("@formicoidea/labre-core/_pkgs/store/index.js").ExtensionType;
|
|
31
31
|
/**
|
|
32
32
|
* The pool's FLAG-GATED row: the legend and the validation dropdown, in one
|
|
33
33
|
* module because a flavour may carry exactly one (a second on the same key
|
|
@@ -47,4 +47,4 @@ export declare const bpmnPoolToolbarExtension: import("@formicoidea/labre-core/s
|
|
|
47
47
|
* way.
|
|
48
48
|
*/
|
|
49
49
|
export declare const bpmnPoolToolingToolbarConfig: ToolbarModuleConfig;
|
|
50
|
-
export declare const bpmnPoolToolingToolbarExtension: import("@formicoidea/labre-core/store/index.js").ExtensionType;
|
|
50
|
+
export declare const bpmnPoolToolingToolbarExtension: import("@formicoidea/labre-core/_pkgs/store/index.js").ExtensionType;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ToolbarContext, type ToolbarModuleConfig } from '@formicoidea/labre-core/shared/services';
|
|
2
|
+
/**
|
|
3
|
+
* Every selected BPMN node wears its name INSIDE the shape.
|
|
4
|
+
*
|
|
5
|
+
* An event, a gateway or a data shape names itself with a grouped `bpmn:label`
|
|
6
|
+
* text under the symbol (R38), so "Add text" on one would write a second,
|
|
7
|
+
* inner name the notation never draws there. The gesture exists for the
|
|
8
|
+
* inscribed kinds only — the same gate `bpmnNodeEditsInnerText` puts on the
|
|
9
|
+
* double-click, minus the legacy exception: a legacy event's inner text is
|
|
10
|
+
* already there, and "Add text" only ever shows on a node with none.
|
|
11
|
+
*/
|
|
12
|
+
export declare function bpmnSelectionIsInscribed(ctx: ToolbarContext): boolean;
|
|
13
|
+
export declare const bpmnNodeToolbarConfig: ToolbarModuleConfig;
|
|
14
|
+
/**
|
|
15
|
+
* The base shape features on a selected BPMN node, on the BARE flavour key.
|
|
16
|
+
* Registered from the ALWAYS-ON view extension: recolouring or re-typesetting
|
|
17
|
+
* a stored element is editing content, not tooling (`docs/adr/0009`). The
|
|
18
|
+
* "Change type" morph, which IS tooling, takes an owner-suffixed key beside
|
|
19
|
+
* it (`view.ts`).
|
|
20
|
+
*/
|
|
21
|
+
export declare const bpmnNodeToolbarExtension: import("@formicoidea/labre-core/_pkgs/store/index.js").ExtensionType;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { paletteColorAction, shapeToolbarConfig, } from '@formicoidea/labre-core/gfx/shape';
|
|
2
|
+
import { BpmnNodeElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
5
|
+
import { bpmnLabelMode } from '../consts.js';
|
|
6
|
+
import { BPMN_PALETTE_LIST } from './palette.js';
|
|
7
|
+
/**
|
|
8
|
+
* The shape toolbar's actions a BPMN node keeps.
|
|
9
|
+
*
|
|
10
|
+
* A BPMN node IS a `ShapeElementModel`, so the shape actions (which target
|
|
11
|
+
* `getSurfaceModelsByType(ShapeElementModel)`) operate on it directly — but
|
|
12
|
+
* `renderToolbar` merges by flavour KEY, so none of them reach a node until a
|
|
13
|
+
* module is registered on `affine:surface:bpmnNode`. This is that module, and
|
|
14
|
+
* it keeps what Edgy keeps (the reference the PO cited):
|
|
15
|
+
*
|
|
16
|
+
* - `d.style` — the scribbled / general line style;
|
|
17
|
+
* - `f.text` — "Add text", narrowed below to inscribed kinds;
|
|
18
|
+
* - `f2.text-fit` and every `g.text-*` — they already hide themselves on a
|
|
19
|
+
* node with no inner text, which is every external kind drawn since R38.
|
|
20
|
+
*
|
|
21
|
+
* Dropped: `c.switch-type` (the KIND decides the native shape — a diamond is a
|
|
22
|
+
* gateway because it is a gateway; turning it into an ellipse would paint a
|
|
23
|
+
* start event's outline with a gateway's marker; "Change type" is the morph
|
|
24
|
+
* module's job) and `f1.edit-vertices` (no BPMN symbol is a free polygon).
|
|
25
|
+
*/
|
|
26
|
+
const KEEP_FROM_SHAPE = (id) => id === 'd.style' ||
|
|
27
|
+
id === 'f.text' ||
|
|
28
|
+
id === 'f2.text-fit' ||
|
|
29
|
+
id.startsWith('g.text-');
|
|
30
|
+
/**
|
|
31
|
+
* Every selected BPMN node wears its name INSIDE the shape.
|
|
32
|
+
*
|
|
33
|
+
* An event, a gateway or a data shape names itself with a grouped `bpmn:label`
|
|
34
|
+
* text under the symbol (R38), so "Add text" on one would write a second,
|
|
35
|
+
* inner name the notation never draws there. The gesture exists for the
|
|
36
|
+
* inscribed kinds only — the same gate `bpmnNodeEditsInnerText` puts on the
|
|
37
|
+
* double-click, minus the legacy exception: a legacy event's inner text is
|
|
38
|
+
* already there, and "Add text" only ever shows on a node with none.
|
|
39
|
+
*/
|
|
40
|
+
export function bpmnSelectionIsInscribed(ctx) {
|
|
41
|
+
const models = ctx.getSurfaceModelsByType(BpmnNodeElementModel);
|
|
42
|
+
return (models.length > 0 &&
|
|
43
|
+
models.every(model => bpmnLabelMode(model.kind) === 'inscribed'));
|
|
44
|
+
}
|
|
45
|
+
/** Narrow a shape action's own `when` with {@link bpmnSelectionIsInscribed}. */
|
|
46
|
+
function inscribedOnly(action) {
|
|
47
|
+
const when = action.when;
|
|
48
|
+
return {
|
|
49
|
+
...action,
|
|
50
|
+
when: (ctx) => bpmnSelectionIsInscribed(ctx) &&
|
|
51
|
+
(typeof when === 'function' ? when(ctx) : when !== false),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fill, stroke colour, stroke width and stroke style — the picker Edgy and
|
|
56
|
+
* Wardley use, seeded with BPMN's two notation hues and the neutrals.
|
|
57
|
+
* `paletteColorAction` and the shape's own `e.color` expose the same four
|
|
58
|
+
* controls (both render `edgeless-shape-color-picker`, which carries the
|
|
59
|
+
* width / style panel); the palette one is taken because it is Edgy's.
|
|
60
|
+
*
|
|
61
|
+
* On the glyph-bodied kinds (`dataObject`, `dataStore`, `textAnnotation`) the
|
|
62
|
+
* native shape is born with `strokeStyle: None` and the renderer's glyph draws
|
|
63
|
+
* the silhouette from `strokeColor` / `fillColor`. Picking a visible stroke
|
|
64
|
+
* style there makes the native rectangle paint its outline around the glyph —
|
|
65
|
+
* an honest result of the author's pick, so it is left to the author rather
|
|
66
|
+
* than filtered out here.
|
|
67
|
+
*/
|
|
68
|
+
const bpmnColorAction = paletteColorAction('e.color', BPMN_PALETTE_LIST);
|
|
69
|
+
export const bpmnNodeToolbarConfig = {
|
|
70
|
+
actions: [
|
|
71
|
+
...shapeToolbarConfig.actions
|
|
72
|
+
.filter(action => KEEP_FROM_SHAPE(action.id))
|
|
73
|
+
.map(action => (action.id === 'f.text' ? inscribedOnly(action) : action)),
|
|
74
|
+
bpmnColorAction,
|
|
75
|
+
],
|
|
76
|
+
when: shapeToolbarConfig.when,
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* The base shape features on a selected BPMN node, on the BARE flavour key.
|
|
80
|
+
* Registered from the ALWAYS-ON view extension: recolouring or re-typesetting
|
|
81
|
+
* a stored element is editing content, not tooling (`docs/adr/0009`). The
|
|
82
|
+
* "Change type" morph, which IS tooling, takes an owner-suffixed key beside
|
|
83
|
+
* it (`view.ts`).
|
|
84
|
+
*/
|
|
85
|
+
export const bpmnNodeToolbarExtension = ToolbarModuleExtension({
|
|
86
|
+
id: BlockFlavourIdentifier('affine:surface:bpmnNode'),
|
|
87
|
+
config: bpmnNodeToolbarConfig,
|
|
88
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const bpmnSeniorTool: import("@formicoidea/labre-core/store").ExtensionType;
|
|
1
|
+
export declare const bpmnSeniorTool: import("@formicoidea/labre-core/_pkgs/store").ExtensionType;
|