@formicoidea/labre-framework-bpmn 0.41.0 → 0.43.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/LICENSE +384 -0
- package/dist/actions.js +7 -23
- package/dist/commands.js +201 -39
- package/dist/consts.d.ts +34 -2
- package/dist/consts.js +38 -2
- package/dist/element-view.js +1 -1
- package/dist/export.d.ts +13 -1
- package/dist/export.js +19 -5
- package/dist/import.d.ts +10 -0
- package/dist/import.js +26 -1
- package/dist/interchange.d.ts +34 -0
- package/dist/interchange.js +99 -7
- package/dist/node/node-renderer.js +22 -32
- package/dist/presets.js +4 -3
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +7 -6
- package/dist/toolbar/bpmn-senior-button.d.ts +2 -0
- package/dist/toolbar/bpmn-senior-button.js +6 -2
- package/dist/toolbar/config.d.ts +22 -11
- package/dist/toolbar/config.js +39 -39
- package/dist/toolbar/icons.d.ts +9 -2
- package/dist/toolbar/icons.js +9 -6
- package/dist/toolbar/palette.d.ts +23 -0
- package/dist/toolbar/palette.js +65 -0
- package/dist/translations.js +13 -2
- package/dist/view.js +18 -14
- package/package.json +2 -2
package/dist/interchange.d.ts
CHANGED
|
@@ -48,6 +48,40 @@ export declare const BPMN_XML_FORMAT: InterchangeFormat;
|
|
|
48
48
|
* gives a centre inside two overlapping pools to the FIRST one, and the audit's
|
|
49
49
|
* `attribute()` does the same. Sorting here would make the export disagree with
|
|
50
50
|
* the badge the user can see.
|
|
51
|
+
*
|
|
52
|
+
* ## The filter is the ROLE, not the class
|
|
53
|
+
*
|
|
54
|
+
* A `BpmnNodeElementModel` is a shape with a `kind`; the ROLE is the author's
|
|
55
|
+
* statement that this circle IS the start of the process (`docs/adr/0010`, and
|
|
56
|
+
* C4's `isC4Node` before it). The distinction became load-bearing the day the
|
|
57
|
+
* pool grew a legend: a legend swatch is a real `bpmnNode` with a real `kind`
|
|
58
|
+
* and deliberately NO role, so a class-based filter would have written eighteen
|
|
59
|
+
* ghost `<task>` / `<startEvent>` / `<dataObject>` elements into the
|
|
60
|
+
* participant's process and broken the bpmn.io round trip the pack is for. The
|
|
61
|
+
* connectors have always been filtered this way (`EDGE_ELEMENT[connector.role]`
|
|
62
|
+
* in `export.ts`); this is the nodes and the pools catching up.
|
|
63
|
+
*
|
|
64
|
+
* ## What it costs, and the PO took that cost
|
|
65
|
+
*
|
|
66
|
+
* The pack shipped on 2026-06-13 and roles on 2026-08-26, with nothing
|
|
67
|
+
* backfilled (`roles.ts` says so in as many words). A process drawn between
|
|
68
|
+
* those two dates carries no role on anything and therefore no longer exports.
|
|
69
|
+
* Arbitrated on 2026-09-17: the alternative is an interchange file that cannot
|
|
70
|
+
* be trusted, which is worse than one that is not written.
|
|
71
|
+
*
|
|
72
|
+
* ## …and what it now SAYS
|
|
73
|
+
*
|
|
74
|
+
* The same ruling of 2026-09-17 added the other half: everything inside a
|
|
75
|
+
* pool's perimeter leaves in the board's generic SVG export (ADR 0025, R34),
|
|
76
|
+
* and only the roled artefacts leave in the `.bpmn`. That asymmetry is wanted;
|
|
77
|
+
* being silent about it was not. So the role-less things drawn INSIDE a pool
|
|
78
|
+
* are counted here — this is the only place that sees what was not picked —
|
|
79
|
+
* and `exportBpmnXmlWithWarnings` turns the count into one warning.
|
|
80
|
+
*
|
|
81
|
+
* Connectors are deliberately NOT counted: a neutral arrow states nothing
|
|
82
|
+
* (`docs/adr/0010`), so there is nothing to lose, which is the reason
|
|
83
|
+
* `export.ts` has always dropped one in silence. Neither is a generated legend
|
|
84
|
+
* — see {@link legendMemberIds}.
|
|
51
85
|
*/
|
|
52
86
|
export declare function bpmnBoardFrom(elements: readonly GfxPrimitiveElementModel[]): BpmnExportBoard;
|
|
53
87
|
/**
|
package/dist/interchange.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { interchangeCapabilityId, parseSvgSketch, SVG_SKETCH_EXTENSION, SVG_SKETCH_FORMAT_ID, SVG_SKETCH_MIME, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { interchangeCapabilityId, LEGEND_ROLE, parseSvgSketch, SVG_SKETCH_EXTENSION, SVG_SKETCH_FORMAT_ID, SVG_SKETCH_MIME, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { BpmnNodeElementModel, BpmnPoolElementModel, ConnectorElementModel, } from '@formicoidea/labre-core/model';
|
|
3
3
|
import { fillPlaceholders } from '@formicoidea/labre-core/shared/services';
|
|
4
4
|
import { BPMN_FORMAT_ID, exportBpmnXmlWithWarnings, } from './export.js';
|
|
5
|
+
import { bpmnPoolOf } from './facts.js';
|
|
5
6
|
import { importBpmnXml } from './import.js';
|
|
7
|
+
import { BPMN_ROLE, BPMN_ROLE_OF_KIND } from './roles.js';
|
|
6
8
|
/**
|
|
7
9
|
* BPMN's entries in the interchange registry (`docs/adr/0012`, P1).
|
|
8
10
|
*
|
|
@@ -49,6 +51,41 @@ export const BPMN_XML_FORMAT = {
|
|
|
49
51
|
mime: BPMN_XML_MIME,
|
|
50
52
|
};
|
|
51
53
|
/* ── Pure board helpers ───────────────────────────────────────────────── */
|
|
54
|
+
/**
|
|
55
|
+
* Everything a generated legend is made of: the group the legend gesture stamps
|
|
56
|
+
* with {@link LEGEND_ROLE}, and everything under it.
|
|
57
|
+
*
|
|
58
|
+
* A legend is drawn INSIDE the board it documents (`createBoardLegend`) and is
|
|
59
|
+
* made, on purpose, of role-less shapes, texts and swatches — so without this
|
|
60
|
+
* it would be reported as fourteen things the `.bpmn` left out, which is
|
|
61
|
+
* exactly the surprise that warning exists to prevent. The group's ROLE is what
|
|
62
|
+
* it is read by: nothing here matches a title, a framework name or a box.
|
|
63
|
+
*
|
|
64
|
+
* Read defensively, like every other walk over stored data in this framework:
|
|
65
|
+
* the children came out of a Y.Map and are whatever a peer wrote.
|
|
66
|
+
*/
|
|
67
|
+
function legendMemberIds(elements) {
|
|
68
|
+
const byId = new Map(elements.map(element => [element.id, element]));
|
|
69
|
+
const members = new Set();
|
|
70
|
+
const walk = (element) => {
|
|
71
|
+
if (members.has(element.id))
|
|
72
|
+
return;
|
|
73
|
+
members.add(element.id);
|
|
74
|
+
const children = element.childIds;
|
|
75
|
+
if (!Array.isArray(children))
|
|
76
|
+
return;
|
|
77
|
+
for (const id of children) {
|
|
78
|
+
const child = typeof id === 'string' ? byId.get(id) : undefined;
|
|
79
|
+
if (child)
|
|
80
|
+
walk(child);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
for (const element of elements) {
|
|
84
|
+
if (element.role === LEGEND_ROLE)
|
|
85
|
+
walk(element);
|
|
86
|
+
}
|
|
87
|
+
return members;
|
|
88
|
+
}
|
|
52
89
|
/**
|
|
53
90
|
* The artefacts the exporter speaks about, picked out of a surface's elements
|
|
54
91
|
* and kept in the order they were given.
|
|
@@ -57,20 +94,75 @@ export const BPMN_XML_FORMAT = {
|
|
|
57
94
|
* gives a centre inside two overlapping pools to the FIRST one, and the audit's
|
|
58
95
|
* `attribute()` does the same. Sorting here would make the export disagree with
|
|
59
96
|
* the badge the user can see.
|
|
97
|
+
*
|
|
98
|
+
* ## The filter is the ROLE, not the class
|
|
99
|
+
*
|
|
100
|
+
* A `BpmnNodeElementModel` is a shape with a `kind`; the ROLE is the author's
|
|
101
|
+
* statement that this circle IS the start of the process (`docs/adr/0010`, and
|
|
102
|
+
* C4's `isC4Node` before it). The distinction became load-bearing the day the
|
|
103
|
+
* pool grew a legend: a legend swatch is a real `bpmnNode` with a real `kind`
|
|
104
|
+
* and deliberately NO role, so a class-based filter would have written eighteen
|
|
105
|
+
* ghost `<task>` / `<startEvent>` / `<dataObject>` elements into the
|
|
106
|
+
* participant's process and broken the bpmn.io round trip the pack is for. The
|
|
107
|
+
* connectors have always been filtered this way (`EDGE_ELEMENT[connector.role]`
|
|
108
|
+
* in `export.ts`); this is the nodes and the pools catching up.
|
|
109
|
+
*
|
|
110
|
+
* ## What it costs, and the PO took that cost
|
|
111
|
+
*
|
|
112
|
+
* The pack shipped on 2026-06-13 and roles on 2026-08-26, with nothing
|
|
113
|
+
* backfilled (`roles.ts` says so in as many words). A process drawn between
|
|
114
|
+
* those two dates carries no role on anything and therefore no longer exports.
|
|
115
|
+
* Arbitrated on 2026-09-17: the alternative is an interchange file that cannot
|
|
116
|
+
* be trusted, which is worse than one that is not written.
|
|
117
|
+
*
|
|
118
|
+
* ## …and what it now SAYS
|
|
119
|
+
*
|
|
120
|
+
* The same ruling of 2026-09-17 added the other half: everything inside a
|
|
121
|
+
* pool's perimeter leaves in the board's generic SVG export (ADR 0025, R34),
|
|
122
|
+
* and only the roled artefacts leave in the `.bpmn`. That asymmetry is wanted;
|
|
123
|
+
* being silent about it was not. So the role-less things drawn INSIDE a pool
|
|
124
|
+
* are counted here — this is the only place that sees what was not picked —
|
|
125
|
+
* and `exportBpmnXmlWithWarnings` turns the count into one warning.
|
|
126
|
+
*
|
|
127
|
+
* Connectors are deliberately NOT counted: a neutral arrow states nothing
|
|
128
|
+
* (`docs/adr/0010`), so there is nothing to lose, which is the reason
|
|
129
|
+
* `export.ts` has always dropped one in silence. Neither is a generated legend
|
|
130
|
+
* — see {@link legendMemberIds}.
|
|
60
131
|
*/
|
|
61
132
|
export function bpmnBoardFrom(elements) {
|
|
62
133
|
const pools = [];
|
|
63
134
|
const nodes = [];
|
|
64
135
|
const connectors = [];
|
|
136
|
+
const roleless = [];
|
|
65
137
|
for (const element of elements) {
|
|
66
|
-
if (element instanceof BpmnPoolElementModel)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
138
|
+
if (element instanceof BpmnPoolElementModel) {
|
|
139
|
+
if (element.role === BPMN_ROLE.pool)
|
|
140
|
+
pools.push(element);
|
|
141
|
+
else
|
|
142
|
+
roleless.push(element);
|
|
143
|
+
}
|
|
144
|
+
else if (element instanceof BpmnNodeElementModel) {
|
|
145
|
+
if (element.role === BPMN_ROLE_OF_KIND[element.kind])
|
|
146
|
+
nodes.push(element);
|
|
147
|
+
else
|
|
148
|
+
roleless.push(element);
|
|
149
|
+
}
|
|
150
|
+
else if (element instanceof ConnectorElementModel) {
|
|
71
151
|
connectors.push(element);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
roleless.push(element);
|
|
155
|
+
}
|
|
72
156
|
}
|
|
73
|
-
|
|
157
|
+
// Against the pools that are actually WRITTEN: "inside the pool" has to mean
|
|
158
|
+
// inside a pool the file has, or the sentence names a perimeter the reader of
|
|
159
|
+
// the `.bpmn` cannot see. `bpmnPoolOf` is the same whole-containment test the
|
|
160
|
+
// audit and the serializer use, so what is reported left out is what the rest
|
|
161
|
+
// of the framework agrees is in there.
|
|
162
|
+
const legend = legendMemberIds(elements);
|
|
163
|
+
const leftOut = roleless.filter(element => !legend.has(element.id) &&
|
|
164
|
+
bpmnPoolOf(pools, element.elementBound) !== null).length;
|
|
165
|
+
return { pools, nodes, connectors, ...(leftOut > 0 ? { leftOut } : {}) };
|
|
74
166
|
}
|
|
75
167
|
/**
|
|
76
168
|
* A name a file system will accept, minus the extension.
|
|
@@ -12,7 +12,6 @@ import { DefaultTheme, } from '@formicoidea/labre-core/model';
|
|
|
12
12
|
*
|
|
13
13
|
* - events — envelope (message), clock (timer), solid disc (terminate);
|
|
14
14
|
* - tasks — a person (user) or a gear (service) in the top-left corner;
|
|
15
|
-
* - activity — the `+` box at the bottom edge (sub-process, call activity);
|
|
16
15
|
* - gateway — the X (exclusive) or the `+` (parallel);
|
|
17
16
|
* - data — folded page, cylinder, open bracket.
|
|
18
17
|
*
|
|
@@ -34,6 +33,13 @@ import { DefaultTheme, } from '@formicoidea/labre-core/model';
|
|
|
34
33
|
* two and the one that is legible zoomed out.
|
|
35
34
|
* - The **timer** has no hour ticks and the **data store** no shelf lines: at
|
|
36
35
|
* the sizes this canvas draws them, both read as noise around the shape.
|
|
36
|
+
* - The **collapsed `[+]`** of the sub-process and the call activity is not
|
|
37
|
+
* drawn at all. In bpmn.io that boxed plus is a BUTTON — it unfolds the
|
|
38
|
+
* process the box stands for. Labre has no such gesture: a process defined
|
|
39
|
+
* elsewhere is a linked document, which carries a symbol of its own. Drawn
|
|
40
|
+
* here the marker would promise an action nothing performs, so the two kinds
|
|
41
|
+
* are bare and the call activity is told apart by its thick border, a
|
|
42
|
+
* creation-time preset (`CALL_ACTIVITY_WIDTH` in `consts.ts`).
|
|
37
43
|
*
|
|
38
44
|
* Mirrors the EDGY node renderer.
|
|
39
45
|
*/
|
|
@@ -41,13 +47,19 @@ import { DefaultTheme, } from '@formicoidea/labre-core/model';
|
|
|
41
47
|
* The artefacts BPMN draws BARE — a plain native shape with nothing on it.
|
|
42
48
|
* Everything else in the union is decorated here.
|
|
43
49
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
50
|
+
* Three of them are here because the notation puts no marker on them at all.
|
|
51
|
+
* The other three are here for reasons of their own:
|
|
52
|
+
*
|
|
53
|
+
* - `group` has a distinctive look — a dashed, rounded, unfilled rectangle —
|
|
54
|
+
* and it is bare because that look is expressible as a native shape's own
|
|
55
|
+
* properties (`strokeStyle: dash`, `radius`, `filled: false`). Drawing it by
|
|
56
|
+
* hand would have meant re-implementing dashes the shape renderer already
|
|
57
|
+
* does, and losing the editability that comes free with them.
|
|
58
|
+
* - `subProcess` and `callActivity` do carry a marker in the notation — the
|
|
59
|
+
* collapsed `[+]` — and Labre drops it on purpose, because here it opens
|
|
60
|
+
* nothing (see the note on simplifications above). What tells the call
|
|
61
|
+
* activity from the sub-process is its thick border, a creation-time preset
|
|
62
|
+
* rather than anything drawn here.
|
|
51
63
|
*
|
|
52
64
|
* Written as the short list rather than the long one, so that the glyph kinds
|
|
53
65
|
* are DERIVED from the model's union instead of restated beside it: a kind
|
|
@@ -61,6 +73,8 @@ const UNDECORATED_KINDS = {
|
|
|
61
73
|
startEvent: true,
|
|
62
74
|
endEvent: true,
|
|
63
75
|
task: true,
|
|
76
|
+
subProcess: true,
|
|
77
|
+
callActivity: true,
|
|
64
78
|
group: true,
|
|
65
79
|
};
|
|
66
80
|
const isUndecorated = (kind) => Object.hasOwn(UNDECORATED_KINDS, kind);
|
|
@@ -204,30 +218,6 @@ export const bpmnNode = (model, ctx, matrix, renderer, rc, bound) => {
|
|
|
204
218
|
ctx.stroke();
|
|
205
219
|
return;
|
|
206
220
|
}
|
|
207
|
-
if (kind === 'subProcess' || kind === 'callActivity') {
|
|
208
|
-
// The collapsed marker: a small boxed `+` on the bottom edge, saying
|
|
209
|
-
// "there is a whole process folded up in here". The call activity carries
|
|
210
|
-
// the SAME marker — what tells the two apart is its thick border, which is
|
|
211
|
-
// a creation-time preset rather than anything drawn here.
|
|
212
|
-
const side = unit * 0.2;
|
|
213
|
-
const bx = cx;
|
|
214
|
-
const by = h - unit * 0.1 - side / 2;
|
|
215
|
-
const half = side / 2;
|
|
216
|
-
const arm = side * 0.3;
|
|
217
|
-
ctx.lineWidth = Math.max(1, unit * 0.028);
|
|
218
|
-
ctx.beginPath();
|
|
219
|
-
ctx.moveTo(bx - half, by - half);
|
|
220
|
-
ctx.lineTo(bx + half, by - half);
|
|
221
|
-
ctx.lineTo(bx + half, by + half);
|
|
222
|
-
ctx.lineTo(bx - half, by + half);
|
|
223
|
-
ctx.lineTo(bx - half, by - half);
|
|
224
|
-
ctx.moveTo(bx - arm, by);
|
|
225
|
-
ctx.lineTo(bx + arm, by);
|
|
226
|
-
ctx.moveTo(bx, by - arm);
|
|
227
|
-
ctx.lineTo(bx, by + arm);
|
|
228
|
-
ctx.stroke();
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
221
|
// ── Data and artifacts: the glyph IS the body ────────────────────────
|
|
232
222
|
// The native rect under these three is unfilled and unstroked, so both the
|
|
233
223
|
// fill and the outline are drawn here — off the model's own colours, which
|
package/dist/presets.js
CHANGED
|
@@ -29,9 +29,10 @@ export const NODE_PRESETS = {
|
|
|
29
29
|
stroke: EVENT_END,
|
|
30
30
|
width: END_WIDTH,
|
|
31
31
|
},
|
|
32
|
-
// Activities: the same rounded rectangle, and a marker tells
|
|
33
|
-
//
|
|
34
|
-
//
|
|
32
|
+
// Activities: the same rounded rectangle, and a corner marker tells the two
|
|
33
|
+
// typed tasks apart. The sub-process wears nothing (its collapsed `+` opens
|
|
34
|
+
// nothing here, so it is not drawn) and the call activity's thick border IS
|
|
35
|
+
// its distinction.
|
|
35
36
|
task: {
|
|
36
37
|
shapeType: 'rect',
|
|
37
38
|
stroke: NEUTRAL_STROKE,
|
|
@@ -4,7 +4,7 @@ import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
|
4
4
|
* The seeds the two worked scenes write — declared once so the scene builder
|
|
5
5
|
* and the manifest ({@link ../translations.ts}) read the very same fallback,
|
|
6
6
|
* never a restated literal. `com.labre.bpmn.example.<scene>.<slug>`, per
|
|
7
|
-
* `docs/adr/
|
|
7
|
+
* `docs/adr/0023`.
|
|
8
8
|
*/
|
|
9
9
|
export declare const SIMPLE_PROCESS_SEED: {
|
|
10
10
|
readonly poolName: {
|
package/dist/templates/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { BPMN_ROLE } from '../roles.js';
|
|
|
9
9
|
* The seeds the two worked scenes write — declared once so the scene builder
|
|
10
10
|
* and the manifest ({@link ../translations.ts}) read the very same fallback,
|
|
11
11
|
* never a restated literal. `com.labre.bpmn.example.<scene>.<slug>`, per
|
|
12
|
-
* `docs/adr/
|
|
12
|
+
* `docs/adr/0023`.
|
|
13
13
|
*/
|
|
14
14
|
export const SIMPLE_PROCESS_SEED = {
|
|
15
15
|
poolName: {
|
|
@@ -211,8 +211,6 @@ const taskPreview = (glyph = '', width = 2.4) => `<svg ${A} fill="none"><rect x=
|
|
|
211
211
|
const gatewayPreview = (marker) => `<svg ${A} fill="none"><path d="M67 16 L92 40 L67 64 L42 40 Z" stroke="${NEUTRAL_STROKE}" stroke-width="2.4" stroke-linejoin="round"/><path d="${marker}" stroke="${NEUTRAL_STROKE}" stroke-width="2.2" stroke-linecap="round"/></svg>`;
|
|
212
212
|
const ENVELOPE = `<rect x="59" y="35" width="16" height="11" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M59 35 L67 42 L75 35" stroke="${NEUTRAL_STROKE}" stroke-width="1.4" stroke-linejoin="round"/>`;
|
|
213
213
|
const CLOCK = `<circle cx="67" cy="40" r="9" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M67 40 V33 M67 40 L72 43" stroke="${NEUTRAL_STROKE}" stroke-width="1.4" stroke-linecap="round"/>`;
|
|
214
|
-
/** The collapsed `[+]`, on the activity's bottom edge — sub-process and call alike. */
|
|
215
|
-
const COLLAPSED = `<rect x="61" y="44" width="12" height="12" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M67 47 V53 M64 50 H70" stroke="${NEUTRAL_STROKE}" stroke-width="1.4" stroke-linecap="round"/>`;
|
|
216
214
|
const previews = {
|
|
217
215
|
process: `<svg ${A} fill="none"><circle cx="16" cy="40" r="8" stroke="${EVENT_START}" stroke-width="2"/><rect x="34" y="31" width="26" height="18" rx="3" stroke="${NEUTRAL_STROKE}" stroke-width="1.6"/><path d="M78 31 L88 40 L78 49 L68 40 Z" stroke="${NEUTRAL_STROKE}" stroke-width="1.4"/><path d="M73 37 L83 43 M83 37 L73 43" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/><circle cx="118" cy="40" r="8" stroke="${EVENT_END}" stroke-width="3"/><path d="M24 40 H34 M60 40 H68 M88 40 H110" stroke="${SEQUENCE_STROKE}" stroke-width="1.2"/></svg>`,
|
|
218
216
|
startEvent: eventPreview(EVENT_START, 3),
|
|
@@ -224,10 +222,13 @@ const previews = {
|
|
|
224
222
|
task: taskPreview(),
|
|
225
223
|
taskUser: taskPreview(`<circle cx="41" cy="31" r="2.6" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/><path d="M37.4 38 a3.6 3.6 0 0 1 7.2 0" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/>`),
|
|
226
224
|
taskService: taskPreview(`<circle cx="41" cy="32" r="3.4" stroke="${NEUTRAL_STROKE}" stroke-width="1.2"/><circle cx="41" cy="32" r="1" fill="${NEUTRAL_STROKE}"/><path d="M41 27.4 V29 M41 35 V36.6 M36.4 32 H38 M44 32 H45.6" stroke="${NEUTRAL_STROKE}" stroke-width="1.2" stroke-linecap="round"/>`),
|
|
227
|
-
|
|
225
|
+
// Neither carries the notation's collapsed `[+]`: it opens nothing in Labre
|
|
226
|
+
// and the board does not paint it, so a card that did would be advertising a
|
|
227
|
+
// drawing the command never makes.
|
|
228
|
+
subProcess: taskPreview(),
|
|
228
229
|
// The thick border IS the distinction: this box stands for a process defined
|
|
229
230
|
// elsewhere (`presets.ts`, `CALL_ACTIVITY_WIDTH`).
|
|
230
|
-
callActivity: taskPreview(
|
|
231
|
+
callActivity: taskPreview('', 4.5),
|
|
231
232
|
gateway: gatewayPreview('M58 31 L76 49 M76 31 L58 49'),
|
|
232
233
|
gatewayParallel: gatewayPreview('M67 30 V50 M57 40 H77'),
|
|
233
234
|
dataObject: `<svg ${A} fill="none"><path d="M52 14 H76 L88 26 V66 H52 Z" stroke="${NEUTRAL_STROKE}" stroke-width="2.2" stroke-linejoin="round"/><path d="M76 14 V26 H88" stroke="${NEUTRAL_STROKE}" stroke-width="1.8" stroke-linejoin="round"/></svg>`,
|
|
@@ -250,7 +251,7 @@ const previews = {
|
|
|
250
251
|
* `build` takes the OPTIONAL inserting editor: called with none, at module
|
|
251
252
|
* load, for the English `content`; called again with the real `std` as
|
|
252
253
|
* {@link Template.localize}, so the two builds read the very same layout and
|
|
253
|
-
* differ only in the words a seed resolves to (`docs/adr/
|
|
254
|
+
* differ only in the words a seed resolves to (`docs/adr/0023`).
|
|
254
255
|
*/
|
|
255
256
|
const scene = (name, preview, build, nameKey) => ({
|
|
256
257
|
name,
|
|
@@ -10,6 +10,8 @@ export declare class EdgelessBpmnSeniorButton extends EdgelessBpmnSeniorButton_b
|
|
|
10
10
|
enableActiveBackground: boolean;
|
|
11
11
|
type: typeof EmptyTool;
|
|
12
12
|
private _toggleMenu;
|
|
13
|
+
/** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
|
|
14
|
+
private get _armed();
|
|
13
15
|
render(): import("lit-html").TemplateResult<1>;
|
|
14
16
|
}
|
|
15
17
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
3
3
|
import { translateKey } from '@formicoidea/labre-core/shared/services';
|
|
4
|
-
import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
4
|
+
import { armedArtefact, EdgelessToolbarToolMixin, } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
5
5
|
import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
|
|
6
6
|
import { css, html, LitElement } from 'lit';
|
|
7
7
|
import { bpmnToolbarIcon } from './icons.js';
|
|
@@ -72,6 +72,10 @@ export class EdgelessBpmnSeniorButton extends EdgelessToolbarToolMixin(SignalWat
|
|
|
72
72
|
const menu = this.createPopper('edgeless-bpmn-menu', this);
|
|
73
73
|
menu.element.edgeless = this.edgeless;
|
|
74
74
|
}
|
|
75
|
+
/** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
|
|
76
|
+
get _armed() {
|
|
77
|
+
return armedArtefact(this.gfx)?.owner === 'bpmn';
|
|
78
|
+
}
|
|
75
79
|
render() {
|
|
76
80
|
return html `<edgeless-toolbar-button
|
|
77
81
|
class="bpmn-button"
|
|
@@ -79,7 +83,7 @@ export class EdgelessBpmnSeniorButton extends EdgelessToolbarToolMixin(SignalWat
|
|
|
79
83
|
? ''
|
|
80
84
|
: translateKey(this.edgeless.std, 'com.labre.framework.bpmn', 'BPMN')}
|
|
81
85
|
.tooltipOffset=${4}
|
|
82
|
-
.active=${!!this.popper}
|
|
86
|
+
.active=${!!this.popper || this._armed}
|
|
83
87
|
@click=${this._toggleMenu}
|
|
84
88
|
>
|
|
85
89
|
<div class="bpmn-root">
|
package/dist/toolbar/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ChromeWording, type ToolbarContext, type ToolbarModuleConfig } from '@formicoidea/labre-core/shared/services';
|
|
2
2
|
import { type TemplateResult } from 'lit';
|
|
3
3
|
export declare const bpmnPoolToolbarConfig: {
|
|
4
4
|
readonly actions: [{
|
|
@@ -24,16 +24,27 @@ export declare const bpmnPoolToolbarConfig: {
|
|
|
24
24
|
tooltip: string;
|
|
25
25
|
run: (runCtx: ToolbarContext) => void;
|
|
26
26
|
};
|
|
27
|
-
},
|
|
28
|
-
id: string;
|
|
29
|
-
placement: ActionPlacement;
|
|
30
|
-
when: (ctx: ToolbarContext) => boolean;
|
|
31
|
-
generate: (ctx: ToolbarContext) => {
|
|
32
|
-
icon: TemplateResult;
|
|
33
|
-
label: string;
|
|
34
|
-
run: (runCtx: ToolbarContext) => void;
|
|
35
|
-
};
|
|
36
|
-
}];
|
|
27
|
+
}, import("@formicoidea/labre-core/shared/services").ToolbarActionGenerator];
|
|
37
28
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
38
29
|
};
|
|
39
30
|
export declare const bpmnPoolToolbarExtension: import("@formicoidea/labre-core/store/index.js").ExtensionType;
|
|
31
|
+
/**
|
|
32
|
+
* The pool's FLAG-GATED row: the legend and the validation dropdown, in one
|
|
33
|
+
* module because a flavour may carry exactly one (a second on the same key
|
|
34
|
+
* throws `DuplicateServiceDefinitionError` before the editor finishes setting
|
|
35
|
+
* up — the same merge C4's board makes for the same reason).
|
|
36
|
+
*
|
|
37
|
+
* `c.legend` and not `b.legend`: the toolbar sorts lexicographically by id
|
|
38
|
+
* after partitioning by placement, and `'b.add-lane' < 'b.legend' <
|
|
39
|
+
* 'b.remove-lane'` would wedge the button between the two lane gestures. `c.`
|
|
40
|
+
* puts it where it belongs — resize · add lane · remove lane · **Legend** ·
|
|
41
|
+
* Validation, with Export XML in the "⋮".
|
|
42
|
+
*
|
|
43
|
+
* Gated, because GENERATING a legend is tooling (ADR 0026, amending 0009). What
|
|
44
|
+
* the gesture already wrote is document content and keeps painting with the
|
|
45
|
+
* flag off: the box is plain shapes and text plus `bpmnNode`s the always-on
|
|
46
|
+
* renderer paints, and the pool keeps its resize handle and its lanes either
|
|
47
|
+
* way.
|
|
48
|
+
*/
|
|
49
|
+
export declare const bpmnPoolToolingToolbarConfig: ToolbarModuleConfig;
|
|
50
|
+
export declare const bpmnPoolToolingToolbarExtension: import("@formicoidea/labre-core/store/index.js").ExtensionType;
|
package/dist/toolbar/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { EdgelessCRUDIdentifier, legendToolbarAction, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
-
import {
|
|
3
|
+
import { BOARD_RESIZE_TOGGLE, commandMoreAction, ToolbarModuleExtension, translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
4
4
|
import { BlockFlavourIdentifier, getRegisteredCommands, runCommand, } from '@formicoidea/labre-core/std';
|
|
5
5
|
import { html } from 'lit';
|
|
6
6
|
import { bpmnExportXmlIcon, bpmnLaneAddIcon, bpmnLaneRemoveIcon, } from './icons.js';
|
|
@@ -84,43 +84,6 @@ function commandAction(id, commandId, labelKey, labelFallback, icon) {
|
|
|
84
84
|
},
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
-
/**
|
|
88
|
-
* The same thing, for the "⋮" menu instead of the row.
|
|
89
|
-
*
|
|
90
|
-
* Two differences, and the widget imposes both: a menu line is drawn from
|
|
91
|
-
* `label` (a tooltip on a line that is already words would be a second copy of
|
|
92
|
-
* them), and `placement: ActionPlacement.More` is what partitions it out of the
|
|
93
|
-
* row in the first place — `renderToolbar` splits on exactly that flag, and the
|
|
94
|
-
* entries it collects come from every module contributing to the element,
|
|
95
|
-
* so a framework can add to the "⋮" without owning it.
|
|
96
|
-
*/
|
|
97
|
-
function commandMoreAction(id, commandId, labelKey, labelFallback, icon) {
|
|
98
|
-
return {
|
|
99
|
-
id,
|
|
100
|
-
placement: ActionPlacement.More,
|
|
101
|
-
when: (ctx) => {
|
|
102
|
-
const command = findCommand(ctx, commandId);
|
|
103
|
-
return command !== undefined && (command.when?.(ctx.std) ?? true);
|
|
104
|
-
},
|
|
105
|
-
generate: (ctx) => ({
|
|
106
|
-
icon,
|
|
107
|
-
label: translateKey(ctx.std, labelKey, labelFallback),
|
|
108
|
-
run: (runCtx) => {
|
|
109
|
-
const command = findCommand(runCtx, commandId);
|
|
110
|
-
if (!command)
|
|
111
|
-
return;
|
|
112
|
-
// The same `source` the row's own entries report: the "⋮" is a
|
|
113
|
-
// degradation of the row, not a surface of its own, and the
|
|
114
|
-
// `ElementCreationSource` union deliberately names places rather than
|
|
115
|
-
// widths (`docs/adr/0008`).
|
|
116
|
-
runCommand(runCtx.std, command, {
|
|
117
|
-
surface: 'contextual-toolbar',
|
|
118
|
-
source: 'toolbar:general',
|
|
119
|
-
});
|
|
120
|
-
},
|
|
121
|
-
}),
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
87
|
const findCommand = (ctx, id) => getRegisteredCommands(ctx.std).find(candidate => candidate.id === id);
|
|
125
88
|
export const bpmnPoolToolbarConfig = {
|
|
126
89
|
actions: [
|
|
@@ -141,3 +104,40 @@ export const bpmnPoolToolbarExtension = ToolbarModuleExtension({
|
|
|
141
104
|
id: BlockFlavourIdentifier('affine:surface:bpmnPool'),
|
|
142
105
|
config: bpmnPoolToolbarConfig,
|
|
143
106
|
});
|
|
107
|
+
/**
|
|
108
|
+
* The pool's FLAG-GATED row: the legend and the validation dropdown, in one
|
|
109
|
+
* module because a flavour may carry exactly one (a second on the same key
|
|
110
|
+
* throws `DuplicateServiceDefinitionError` before the editor finishes setting
|
|
111
|
+
* up — the same merge C4's board makes for the same reason).
|
|
112
|
+
*
|
|
113
|
+
* `c.legend` and not `b.legend`: the toolbar sorts lexicographically by id
|
|
114
|
+
* after partitioning by placement, and `'b.add-lane' < 'b.legend' <
|
|
115
|
+
* 'b.remove-lane'` would wedge the button between the two lane gestures. `c.`
|
|
116
|
+
* puts it where it belongs — resize · add lane · remove lane · **Legend** ·
|
|
117
|
+
* Validation, with Export XML in the "⋮".
|
|
118
|
+
*
|
|
119
|
+
* Gated, because GENERATING a legend is tooling (ADR 0026, amending 0009). What
|
|
120
|
+
* the gesture already wrote is document content and keeps painting with the
|
|
121
|
+
* flag off: the box is plain shapes and text plus `bpmnNode`s the always-on
|
|
122
|
+
* renderer paints, and the pool keeps its resize handle and its lanes either
|
|
123
|
+
* way.
|
|
124
|
+
*/
|
|
125
|
+
export const bpmnPoolToolingToolbarConfig = {
|
|
126
|
+
actions: [
|
|
127
|
+
legendToolbarAction({
|
|
128
|
+
id: 'c.legend',
|
|
129
|
+
Model: BpmnPoolElementModel,
|
|
130
|
+
owner: 'bpmn',
|
|
131
|
+
framework: 'bpmn',
|
|
132
|
+
}),
|
|
133
|
+
// The generic dropdown, not a BPMN variant of it: the config names no
|
|
134
|
+
// framework — it reads the registered rules and profiles — so this is the
|
|
135
|
+
// very same object wardley, c4 and the context map register.
|
|
136
|
+
...validationToolbarConfig.actions,
|
|
137
|
+
],
|
|
138
|
+
when: bpmnPoolToolbarConfig.when,
|
|
139
|
+
};
|
|
140
|
+
export const bpmnPoolToolingToolbarExtension = ToolbarModuleExtension({
|
|
141
|
+
id: BlockFlavourIdentifier('custom:affine:surface:bpmnPool'),
|
|
142
|
+
config: bpmnPoolToolingToolbarConfig,
|
|
143
|
+
});
|
package/dist/toolbar/icons.d.ts
CHANGED
|
@@ -38,9 +38,16 @@ export declare const bpmnTaskIcon: import("lit-html").TemplateResult<2>;
|
|
|
38
38
|
export declare const bpmnTaskUserIcon: import("lit-html").TemplateResult<2>;
|
|
39
39
|
/** Service task — the rectangle with a gear in its top-left corner. */
|
|
40
40
|
export declare const bpmnTaskServiceIcon: import("lit-html").TemplateResult<2>;
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Sub-process — the plain rectangle, like the task it is drawn as.
|
|
43
|
+
*
|
|
44
|
+
* The notation puts a collapsed [+] on its bottom edge and the canvas does not
|
|
45
|
+
* paint one (see `node/node-renderer.ts`): that box is a button everywhere else
|
|
46
|
+
* and opens nothing here. A palette glyph says what lands on the board, so it
|
|
47
|
+
* has none either, and the menu label is what tells this entry from the task.
|
|
48
|
+
*/
|
|
42
49
|
export declare const bpmnSubProcessIcon: import("lit-html").TemplateResult<2>;
|
|
43
|
-
/** Call activity — the same
|
|
50
|
+
/** Call activity — the same bare rectangle, on the notation's THICKEST border. */
|
|
44
51
|
export declare const bpmnCallActivityIcon: import("lit-html").TemplateResult<2>;
|
|
45
52
|
/** Exclusive gateway — diamond with an X. */
|
|
46
53
|
export declare const bpmnGatewayIcon: import("lit-html").TemplateResult<2>;
|
package/dist/toolbar/icons.js
CHANGED
|
@@ -87,17 +87,20 @@ export const bpmnTaskServiceIcon = svg `<svg width="24" height="24" viewBox="0 0
|
|
|
87
87
|
<circle cx="7.6" cy="11" r="0.5" stroke="currentColor" stroke-width="0.8"/>
|
|
88
88
|
<path d="M7.6 8.9 V9.3 M7.6 12.7 V13.1 M5.5 11 H5.9 M9.3 11 H9.7 M6.1 9.5 L6.4 9.8 M8.8 12.2 L9.1 12.5 M9.1 9.5 L8.8 9.8 M6.4 12.2 L6.1 12.5" stroke="currentColor" stroke-width="1" stroke-linecap="round"/>
|
|
89
89
|
</svg>`;
|
|
90
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Sub-process — the plain rectangle, like the task it is drawn as.
|
|
92
|
+
*
|
|
93
|
+
* The notation puts a collapsed [+] on its bottom edge and the canvas does not
|
|
94
|
+
* paint one (see `node/node-renderer.ts`): that box is a button everywhere else
|
|
95
|
+
* and opens nothing here. A palette glyph says what lands on the board, so it
|
|
96
|
+
* has none either, and the menu label is what tells this entry from the task.
|
|
97
|
+
*/
|
|
91
98
|
export const bpmnSubProcessIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
92
99
|
<rect x="3.5" y="6.5" width="17" height="11" rx="2.5" stroke="currentColor" stroke-width="1.6"/>
|
|
93
|
-
<rect x="9.6" y="12.6" width="4.8" height="4.8" stroke="currentColor" stroke-width="1.1"/>
|
|
94
|
-
<path d="M12 13.8 V16.2 M10.8 15 H13.2" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/>
|
|
95
100
|
</svg>`;
|
|
96
|
-
/** Call activity — the same
|
|
101
|
+
/** Call activity — the same bare rectangle, on the notation's THICKEST border. */
|
|
97
102
|
export const bpmnCallActivityIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
98
103
|
<rect x="4" y="7" width="16" height="10" rx="2.5" stroke="currentColor" stroke-width="3"/>
|
|
99
|
-
<rect x="9.6" y="12.2" width="4.8" height="4.8" stroke="currentColor" stroke-width="1.1"/>
|
|
100
|
-
<path d="M12 13.4 V15.8 M10.8 14.6 H13.2" stroke="currentColor" stroke-width="1.1" stroke-linecap="round"/>
|
|
101
104
|
</svg>`;
|
|
102
105
|
/** Exclusive gateway — diamond with an X. */
|
|
103
106
|
export const bpmnGatewayIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FrameworkPalette, NamedPalette } from '@formicoidea/labre-core/components/color-picker';
|
|
2
|
+
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
3
|
+
/**
|
|
4
|
+
* The two BPMN swatches' own names, resolved by `resolvePaletteLabel`
|
|
5
|
+
* (`@labre/affine-components/color-picker`) through each swatch's own
|
|
6
|
+
* {@link NamedPalette.labelWording} — the mechanism `WARDLEY_PALETTE_WORDINGS`
|
|
7
|
+
* established, reused here without either package naming the other.
|
|
8
|
+
*
|
|
9
|
+
* Exported so `../translations.ts` can list them under source `chrome`: a
|
|
10
|
+
* swatch's name is toolbar chrome, never seeded into a document.
|
|
11
|
+
*/
|
|
12
|
+
export declare const BPMN_PALETTE_WORDING_START: ChromeWording;
|
|
13
|
+
export declare const BPMN_PALETTE_WORDING_END: ChromeWording;
|
|
14
|
+
export declare const BPMN_PALETTE_WORDINGS: readonly ChromeWording[];
|
|
15
|
+
/** The two notation hues, then the neutrals every drawing needs. */
|
|
16
|
+
export declare const BPMN_PALETTE_LIST: NamedPalette[];
|
|
17
|
+
/**
|
|
18
|
+
* BPMN's page of the colour pickers' carousel (`docs/adr/0027`). Registered
|
|
19
|
+
* from the FLAG-GATED view extension, because offering hues is TOOLING: a
|
|
20
|
+
* process drawn while the flag was on keeps every colour it was painted with
|
|
21
|
+
* when the flag goes off (`docs/adr/0009`).
|
|
22
|
+
*/
|
|
23
|
+
export declare const BPMN_FRAMEWORK_PALETTE: FrameworkPalette;
|