@formicoidea/labre-framework-bpmn 0.58.0 → 0.60.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 +76 -6
- package/dist/actions.js +176 -46
- package/dist/background.js +26 -1
- package/dist/commands-manifest.js +16 -0
- package/dist/commands.js +35 -3
- package/dist/consts.d.ts +2 -2
- package/dist/consts.js +2 -2
- package/dist/element-renderer.d.ts +16 -8
- package/dist/element-renderer.js +67 -3
- package/dist/element-view.d.ts +30 -4
- package/dist/element-view.js +85 -7
- package/dist/import.js +13 -35
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/interchange-headless.d.ts +1 -0
- package/dist/interchange-headless.js +3 -0
- package/dist/pool-hit.d.ts +63 -4
- package/dist/pool-hit.js +57 -8
- package/dist/presets.d.ts +12 -0
- package/dist/presets.js +18 -2
- package/dist/toolbar/config.d.ts +18 -2
- package/dist/toolbar/config.js +11 -7
- package/dist/toolbar/icons.d.ts +4 -0
- package/dist/toolbar/icons.js +14 -0
- package/dist/toolbox.d.ts +45 -0
- package/dist/toolbox.js +129 -0
- package/package.json +2 -2
package/dist/actions.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ export declare function createBpmnPool(std: BlockStdScope): void;
|
|
|
19
19
|
/**
|
|
20
20
|
* Arm the native connector tool, pre-styled for a BPMN sequence flow:
|
|
21
21
|
* orthogonal, solid, with a filled triangle head. The user then draws from
|
|
22
|
-
* one node to another
|
|
22
|
+
* one node to another, attaching at the anchor of each outline they pick (a
|
|
23
|
+
* gateway's corners, an activity's side midpoints, an event's cardinal points;
|
|
24
|
+
* `BPMN_CENTRE_ANCHORED`).
|
|
23
25
|
*/
|
|
24
26
|
export declare function activateBpmnSequenceFlow(std: BlockStdScope): void;
|
|
25
27
|
/**
|
|
@@ -102,12 +104,14 @@ export declare function bpmnPoolsForLaneEdit(std: BlockStdScope): BpmnPoolElemen
|
|
|
102
104
|
*/
|
|
103
105
|
export declare function addBpmnLane(std: BlockStdScope): void;
|
|
104
106
|
/**
|
|
105
|
-
* Remove the
|
|
107
|
+
* Remove the FOCUSED lane of every selected pool, or its LAST lane when none is
|
|
108
|
+
* focused.
|
|
106
109
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
110
|
+
* Focused and not selected, because a lane is not selectable: it is a slice of
|
|
111
|
+
* the pool's plot, painted by the background primitive, with no element of its
|
|
112
|
+
* own. A single click on its title strip, in a selected pool, focuses it
|
|
113
|
+
* ({@link bpmnFocusedLaneIndex}). With nothing focused, removing from the end
|
|
114
|
+
* is the gesture that undoes the one that added it.
|
|
111
115
|
*
|
|
112
116
|
* ## Nothing moves
|
|
113
117
|
*
|
|
@@ -221,3 +225,69 @@ export declare function importBpmnSvgFile(std: BlockStdScope): Promise<void>;
|
|
|
221
225
|
* same thing that only the bytes can tell apart.
|
|
222
226
|
*/
|
|
223
227
|
export declare function renameBpmnLane(std: BlockStdScope, model: BpmnPoolElementModel, index: number, name: string): void;
|
|
228
|
+
/**
|
|
229
|
+
* Move the lane at `from` to `to` inside `pool`, carrying what is drawn in it.
|
|
230
|
+
*
|
|
231
|
+
* A lane is a slice of the pool's plot, and what is "in" it is decided by
|
|
232
|
+
* geometry: the centre of an artefact, read by {@link bpmnLaneOf}. Reordering
|
|
233
|
+
* the `lanes` array alone would leave every task where it was on the canvas and
|
|
234
|
+
* silently hand it to whichever lane now covers that spot — the user moves the
|
|
235
|
+
* Sales lane up and finds their sales tasks filed under Ops. So the gesture
|
|
236
|
+
* moves both: the array is reordered, and every unlocked artefact wholly inside
|
|
237
|
+
* the pool is translated by the vertical shift of the lane its centre was in.
|
|
238
|
+
* Connectors attached to them follow through the connector watcher, as on any
|
|
239
|
+
* other move.
|
|
240
|
+
*
|
|
241
|
+
* A GESTURE-time write, not a cascade: it runs once, on the author's client,
|
|
242
|
+
* from a command, and a peer receives the reorder and the moves as ordinary
|
|
243
|
+
* updates. No stored-format change either: the same `lanes` array, the same
|
|
244
|
+
* `xywh` props.
|
|
245
|
+
*
|
|
246
|
+
* One `captureSync`, then one `transact`, so the reorder and every move are ONE
|
|
247
|
+
* undo step. Refused — and nothing written, so no empty undo entry — on a
|
|
248
|
+
* read-only document, a locked pool, an index that names no lane, or a move to
|
|
249
|
+
* where the lane already is. Returns whether it wrote.
|
|
250
|
+
*
|
|
251
|
+
* ponytail: a connector attached to nothing, drawn inside a lane, stays where
|
|
252
|
+
* it is, and a locked artefact is left behind; the upgrade is to translate the
|
|
253
|
+
* free endpoints too, here and nowhere else.
|
|
254
|
+
*/
|
|
255
|
+
export declare function moveBpmnLane(std: BlockStdScope, pool: BpmnPoolElementModel, from: number, to: number): boolean;
|
|
256
|
+
/**
|
|
257
|
+
* Something that knows which lane of a pool is FOCUSED — the pool's view
|
|
258
|
+
* (`BpmnPoolView`) in an editor, a plain object in a test.
|
|
259
|
+
*
|
|
260
|
+
* A lane is not an element, so it cannot be selected; the lane commands need
|
|
261
|
+
* another way to know which lane the user means. The focus is set by a single
|
|
262
|
+
* click on a lane's title strip in a selected pool, and it is on-screen state
|
|
263
|
+
* and nothing more: held by the view, never stored, never synced, dropped when
|
|
264
|
+
* the pool is deselected. Another viewer of the same document has their own or
|
|
265
|
+
* none. An id rather than an index, so the focus follows the lane it names
|
|
266
|
+
* through a move.
|
|
267
|
+
*/
|
|
268
|
+
export interface BpmnLaneFocusHolder {
|
|
269
|
+
readonly focusedLaneId: string | null;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* The index of `pool`'s focused lane in this editor, or `null` when none is
|
|
273
|
+
* focused or the focused one is gone (removed by a peer, undone away).
|
|
274
|
+
*
|
|
275
|
+
* Read through {@link BpmnLaneFocusHolder} rather than the view class, so the
|
|
276
|
+
* commands do not import the view.
|
|
277
|
+
*/
|
|
278
|
+
export declare function bpmnFocusedLaneIndex(std: BlockStdScope, pool: BpmnPoolElementModel): number | null;
|
|
279
|
+
/**
|
|
280
|
+
* The selected pool whose focused lane can move one step `by` (`-1` up, `1`
|
|
281
|
+
* down), and the lane's index — `null` when no such pool is selected.
|
|
282
|
+
*
|
|
283
|
+
* The first one that qualifies: a lane is focused by clicking it, on one pool
|
|
284
|
+
* at a time, so a selection holding two pools still names at most one lane.
|
|
285
|
+
*/
|
|
286
|
+
export declare function bpmnLaneToMove(std: BlockStdScope, by: -1 | 1): {
|
|
287
|
+
pool: BpmnPoolElementModel;
|
|
288
|
+
index: number;
|
|
289
|
+
} | null;
|
|
290
|
+
/** Move the focused lane of the selected pool one step up. */
|
|
291
|
+
export declare function moveBpmnLaneUp(std: BlockStdScope): void;
|
|
292
|
+
/** Move the focused lane of the selected pool one step down. */
|
|
293
|
+
export declare function moveBpmnLaneDown(std: BlockStdScope): void;
|
package/dist/actions.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { DefaultTool, EdgelessCRUDIdentifier, generateElementId, materializeInterchangeImport, reportInterchangeImport, runInterchangeImportFile, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { backgroundInstanceZones, backgroundPlot, DefaultTool, EdgelessCRUDIdentifier, generateElementId, materializeInterchangeImport, reportInterchangeImport, runInterchangeImportFile, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { ConnectorTool } from '@formicoidea/labre-core/gfx/connector';
|
|
3
3
|
import { createGroupCommand } from '@formicoidea/labre-core/gfx/group';
|
|
4
|
-
import { BpmnPoolElementModel, ConnectorMode, } from '@formicoidea/labre-core/model';
|
|
4
|
+
import { BpmnPoolElementModel, ConnectorElementModel, ConnectorMode, } from '@formicoidea/labre-core/model';
|
|
5
5
|
import { NotificationProvider, translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
6
6
|
import { downloadBlob } from '@formicoidea/labre-core/shared/utils';
|
|
7
7
|
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
8
|
-
import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
|
|
9
|
-
import {
|
|
8
|
+
import { GfxControllerIdentifier, GfxGroupLikeElementModel, } from '@formicoidea/labre-core/std/gfx';
|
|
9
|
+
import { BPMN_POOL_BACKGROUND } from './background.js';
|
|
10
|
+
import { BPMN_EDGE_STYLE, LANE_NAME_FALLBACK, LANE_NAME_KEY, NODE_LABEL, nodeLabelKey, NODE_SIZE, POOL_NAME_FALLBACK, POOL_NAME_KEY, POOL_REF_HEIGHT, POOL_REF_WIDTH, } from './consts.js';
|
|
10
11
|
import { BPMN_FORMAT_ID, exportBpmnXmlWithWarnings, } from './export.js';
|
|
12
|
+
import { bpmnLaneOf } from './facts.js';
|
|
11
13
|
import { BPMN_SVG_IMPORT, BPMN_XML_EXPORT, BPMN_XML_FORMAT, BPMN_XML_IMPORT, bpmnBoardFrom, bpmnSafeFilename, } from './interchange.js';
|
|
12
|
-
import { bpmnLabelBoxFor, bpmnLabelProps, bpmnNodeProps, bpmnPoolProps, } from './presets.js';
|
|
13
14
|
import { BPMN_ROLE } from './roles.js';
|
|
15
|
+
import { bpmnNodeElements, bpmnPoolElement } from './toolbox.js';
|
|
14
16
|
/**
|
|
15
17
|
* Standalone creation/activation actions for the BPMN toolbox — lifted out of
|
|
16
18
|
* `toolbar/bpmn-menu.ts` by PF3 so the menu becomes a pure renderer over the
|
|
@@ -52,30 +54,29 @@ export function createBpmnNode(std, kind) {
|
|
|
52
54
|
return;
|
|
53
55
|
const { w, h } = NODE_SIZE[kind];
|
|
54
56
|
const { centerX: cx, centerY: cy } = gfx.viewport;
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// (`
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return;
|
|
57
|
+
// The elements come from the ONE assembly a host with no editor also calls
|
|
58
|
+
// (`toolbox.ts`, `docs/adr/0045`), on the presets the importer reads too
|
|
59
|
+
// (`presets.ts`): a task drawn here, one read from a `.bpmn` file and one a
|
|
60
|
+
// host places are one element type in the document, down to the stroke
|
|
61
|
+
// width. What only an editor has stays here: the viewport centre, and the
|
|
62
|
+
// caption translated HERE and once (`nodeLabelKey`, and #183 on why a French
|
|
63
|
+
// board should not start in English).
|
|
64
|
+
const elements = bpmnNodeElements(kind, { x: cx - w / 2, y: cy - h / 2 }, { name: translateKey(std, nodeLabelKey(kind), NODE_LABEL[kind]) });
|
|
65
|
+
const minted = new Map();
|
|
66
|
+
for (const { id: local, ...props } of elements) {
|
|
67
|
+
if (props.type === 'group') {
|
|
68
|
+
// Through the native group command and not `addElement`: the group's
|
|
69
|
+
// seed title is numbered against the groups already on the board, and
|
|
70
|
+
// its user layer is its highest member's (ADR 0031) — both the editor's.
|
|
71
|
+
const children = Object.keys(props.children);
|
|
72
|
+
finish(gfx, group(gfx, children.map(child => minted.get(child) ?? child)));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
minted.set(local, surface.addElement(props));
|
|
75
76
|
}
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const id = minted.get(`${kind}:node`);
|
|
78
|
+
if (id)
|
|
79
|
+
finish(gfx, id);
|
|
79
80
|
}
|
|
80
81
|
/** Create a pool (background container) centred on the viewport. */
|
|
81
82
|
export function createBpmnPool(std) {
|
|
@@ -83,29 +84,27 @@ export function createBpmnPool(std) {
|
|
|
83
84
|
const surface = gfx.surface;
|
|
84
85
|
if (!surface)
|
|
85
86
|
return;
|
|
86
|
-
const w = 560;
|
|
87
|
-
const h = 200;
|
|
88
87
|
const { centerX: cx, centerY: cy } = gfx.viewport;
|
|
89
|
-
// What a pool IS lives in one place (
|
|
90
|
-
// and for the same reason: the
|
|
91
|
-
//
|
|
92
|
-
//
|
|
88
|
+
// What a pool IS lives in one place (`bpmnPoolElement`, `toolbox.ts`, over
|
|
89
|
+
// `bpmnPoolProps`), beside what a node is and for the same reason: the
|
|
90
|
+
// shipped cards and a host build their participants from it too, so a pool
|
|
91
|
+
// laid out by a template, one a host places and one dropped from the toolbox
|
|
92
|
+
// cannot say different things. The gesture owns the centre and nothing else.
|
|
93
93
|
//
|
|
94
94
|
// The NAME is the one exception, and it is written HERE rather than left to
|
|
95
95
|
// the model's default (`BpmnPoolElementModel.name = 'Pool'`, a red-zone
|
|
96
96
|
// field: a document created before this key existed keeps its literal
|
|
97
97
|
// 'Pool' verbatim). Resolved at placement and never again — content from
|
|
98
98
|
// the moment it lands, exactly like a node's caption.
|
|
99
|
-
const id =
|
|
100
|
-
|
|
101
|
-
name: translateKey(std, POOL_NAME_KEY, POOL_NAME_FALLBACK),
|
|
102
|
-
}));
|
|
103
|
-
finish(gfx, id);
|
|
99
|
+
const { id: _local, ...props } = bpmnPoolElement({ x: cx - POOL_REF_WIDTH / 2, y: cy - POOL_REF_HEIGHT / 2 }, { name: translateKey(std, POOL_NAME_KEY, POOL_NAME_FALLBACK) });
|
|
100
|
+
finish(gfx, surface.addElement(props));
|
|
104
101
|
}
|
|
105
102
|
/**
|
|
106
103
|
* Arm the native connector tool, pre-styled for a BPMN sequence flow:
|
|
107
104
|
* orthogonal, solid, with a filled triangle head. The user then draws from
|
|
108
|
-
* one node to another
|
|
105
|
+
* one node to another, attaching at the anchor of each outline they pick (a
|
|
106
|
+
* gateway's corners, an activity's side midpoints, an event's cardinal points;
|
|
107
|
+
* `BPMN_CENTRE_ANCHORED`).
|
|
109
108
|
*/
|
|
110
109
|
export function activateBpmnSequenceFlow(std) {
|
|
111
110
|
gfxOf(std).tool.setTool(ConnectorTool, {
|
|
@@ -280,12 +279,14 @@ export function addBpmnLane(std) {
|
|
|
280
279
|
}
|
|
281
280
|
}
|
|
282
281
|
/**
|
|
283
|
-
* Remove the
|
|
282
|
+
* Remove the FOCUSED lane of every selected pool, or its LAST lane when none is
|
|
283
|
+
* focused.
|
|
284
284
|
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
285
|
+
* Focused and not selected, because a lane is not selectable: it is a slice of
|
|
286
|
+
* the pool's plot, painted by the background primitive, with no element of its
|
|
287
|
+
* own. A single click on its title strip, in a selected pool, focuses it
|
|
288
|
+
* ({@link bpmnFocusedLaneIndex}). With nothing focused, removing from the end
|
|
289
|
+
* is the gesture that undoes the one that added it.
|
|
289
290
|
*
|
|
290
291
|
* ## Nothing moves
|
|
291
292
|
*
|
|
@@ -304,7 +305,13 @@ export function removeBpmnLane(std) {
|
|
|
304
305
|
return;
|
|
305
306
|
std.store.captureSync();
|
|
306
307
|
for (const model of withLanes) {
|
|
307
|
-
|
|
308
|
+
const lanes = bpmnLanesOf(model);
|
|
309
|
+
// The FOCUSED lane when the user picked one (a click on its title strip),
|
|
310
|
+
// the last one otherwise — which is still the one the previous "Add lane"
|
|
311
|
+
// added.
|
|
312
|
+
const focused = bpmnFocusedLaneIndex(std, model);
|
|
313
|
+
const gone = focused ?? lanes.length - 1;
|
|
314
|
+
writeLanes(std, model, lanes.filter((_, i) => i !== gone));
|
|
308
315
|
}
|
|
309
316
|
}
|
|
310
317
|
/* ── Export (BPMN 2.0 XML) ────────────────────────────────────────────── */
|
|
@@ -510,3 +517,126 @@ export function renameBpmnLane(std, model, index, name) {
|
|
|
510
517
|
std.store.captureSync();
|
|
511
518
|
writeLanes(std, model, next);
|
|
512
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Move the lane at `from` to `to` inside `pool`, carrying what is drawn in it.
|
|
522
|
+
*
|
|
523
|
+
* A lane is a slice of the pool's plot, and what is "in" it is decided by
|
|
524
|
+
* geometry: the centre of an artefact, read by {@link bpmnLaneOf}. Reordering
|
|
525
|
+
* the `lanes` array alone would leave every task where it was on the canvas and
|
|
526
|
+
* silently hand it to whichever lane now covers that spot — the user moves the
|
|
527
|
+
* Sales lane up and finds their sales tasks filed under Ops. So the gesture
|
|
528
|
+
* moves both: the array is reordered, and every unlocked artefact wholly inside
|
|
529
|
+
* the pool is translated by the vertical shift of the lane its centre was in.
|
|
530
|
+
* Connectors attached to them follow through the connector watcher, as on any
|
|
531
|
+
* other move.
|
|
532
|
+
*
|
|
533
|
+
* A GESTURE-time write, not a cascade: it runs once, on the author's client,
|
|
534
|
+
* from a command, and a peer receives the reorder and the moves as ordinary
|
|
535
|
+
* updates. No stored-format change either: the same `lanes` array, the same
|
|
536
|
+
* `xywh` props.
|
|
537
|
+
*
|
|
538
|
+
* One `captureSync`, then one `transact`, so the reorder and every move are ONE
|
|
539
|
+
* undo step. Refused — and nothing written, so no empty undo entry — on a
|
|
540
|
+
* read-only document, a locked pool, an index that names no lane, or a move to
|
|
541
|
+
* where the lane already is. Returns whether it wrote.
|
|
542
|
+
*
|
|
543
|
+
* ponytail: a connector attached to nothing, drawn inside a lane, stays where
|
|
544
|
+
* it is, and a locked artefact is left behind; the upgrade is to translate the
|
|
545
|
+
* free endpoints too, here and nowhere else.
|
|
546
|
+
*/
|
|
547
|
+
export function moveBpmnLane(std, pool, from, to) {
|
|
548
|
+
if (std.store.readonly || pool.isLocked())
|
|
549
|
+
return false;
|
|
550
|
+
if (from === to)
|
|
551
|
+
return false;
|
|
552
|
+
const lanes = bpmnLanesOf(pool);
|
|
553
|
+
if (!lanes[from] || !lanes[to])
|
|
554
|
+
return false;
|
|
555
|
+
const next = [...lanes];
|
|
556
|
+
const [moved] = next.splice(from, 1);
|
|
557
|
+
next.splice(to, 0, moved);
|
|
558
|
+
// Each band's top, before and after, read off the painted partition: the
|
|
559
|
+
// normalisation the renderer and the audit use, so an artefact moves by
|
|
560
|
+
// exactly the distance its lane moves on the canvas.
|
|
561
|
+
const frame = pool.elementBound;
|
|
562
|
+
const plot = backgroundPlot(BPMN_POOL_BACKGROUND, frame.w, frame.h);
|
|
563
|
+
const topsOf = (rows) => new Map(backgroundInstanceZones(BPMN_POOL_BACKGROUND, { lanes: rows }).map(zone => [zone.id, zone.rect.y * plot.height]));
|
|
564
|
+
const before = topsOf(lanes);
|
|
565
|
+
const after = topsOf(next);
|
|
566
|
+
const prefix = BPMN_POOL_BACKGROUND.instanceZones?.idPrefix ?? 'lane';
|
|
567
|
+
// Membership BEFORE the write: once the array is reordered the bands are
|
|
568
|
+
// where the lanes are going, and a centre would read the wrong lane.
|
|
569
|
+
const moves = [];
|
|
570
|
+
for (const element of gfxOf(std).grid.search(frame, { strict: true })) {
|
|
571
|
+
if (element === pool || element.isLocked())
|
|
572
|
+
continue;
|
|
573
|
+
// A connector follows its endpoints; a group or a mind map derives its
|
|
574
|
+
// bound from its children, which are moved on their own.
|
|
575
|
+
if (element instanceof ConnectorElementModel)
|
|
576
|
+
continue;
|
|
577
|
+
if (element instanceof GfxGroupLikeElementModel)
|
|
578
|
+
continue;
|
|
579
|
+
const bound = Bound.deserialize(element.xywh);
|
|
580
|
+
const lane = bpmnLaneOf(pool, bound);
|
|
581
|
+
if (!lane)
|
|
582
|
+
continue;
|
|
583
|
+
const key = `${prefix}:${lane.id}`;
|
|
584
|
+
const dy = (after.get(key) ?? 0) - (before.get(key) ?? 0);
|
|
585
|
+
if (dy === 0)
|
|
586
|
+
continue;
|
|
587
|
+
moves.push({ id: element.id, xywh: bound.moveDelta(0, dy).serialize() });
|
|
588
|
+
}
|
|
589
|
+
std.store.captureSync();
|
|
590
|
+
std.store.transact(() => {
|
|
591
|
+
writeLanes(std, pool, next);
|
|
592
|
+
const crud = std.get(EdgelessCRUDIdentifier);
|
|
593
|
+
for (const { id, xywh } of moves)
|
|
594
|
+
crud.updateElement(id, { xywh });
|
|
595
|
+
});
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* The index of `pool`'s focused lane in this editor, or `null` when none is
|
|
600
|
+
* focused or the focused one is gone (removed by a peer, undone away).
|
|
601
|
+
*
|
|
602
|
+
* Read through {@link BpmnLaneFocusHolder} rather than the view class, so the
|
|
603
|
+
* commands do not import the view.
|
|
604
|
+
*/
|
|
605
|
+
export function bpmnFocusedLaneIndex(std, pool) {
|
|
606
|
+
const holder = gfxOf(std).view?.get(pool.id);
|
|
607
|
+
const id = holder?.focusedLaneId;
|
|
608
|
+
if (!id)
|
|
609
|
+
return null;
|
|
610
|
+
const index = bpmnLanesOf(pool).findIndex(lane => lane.id === id);
|
|
611
|
+
return index < 0 ? null : index;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* The selected pool whose focused lane can move one step `by` (`-1` up, `1`
|
|
615
|
+
* down), and the lane's index — `null` when no such pool is selected.
|
|
616
|
+
*
|
|
617
|
+
* The first one that qualifies: a lane is focused by clicking it, on one pool
|
|
618
|
+
* at a time, so a selection holding two pools still names at most one lane.
|
|
619
|
+
*/
|
|
620
|
+
export function bpmnLaneToMove(std, by) {
|
|
621
|
+
for (const pool of bpmnPoolsForLaneEdit(std)) {
|
|
622
|
+
const index = bpmnFocusedLaneIndex(std, pool);
|
|
623
|
+
if (index === null)
|
|
624
|
+
continue;
|
|
625
|
+
if (!bpmnLanesOf(pool)[index + by])
|
|
626
|
+
continue;
|
|
627
|
+
return { pool, index };
|
|
628
|
+
}
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
/** Move the focused lane of the selected pool one step up. */
|
|
632
|
+
export function moveBpmnLaneUp(std) {
|
|
633
|
+
const target = bpmnLaneToMove(std, -1);
|
|
634
|
+
if (target)
|
|
635
|
+
moveBpmnLane(std, target.pool, target.index, target.index - 1);
|
|
636
|
+
}
|
|
637
|
+
/** Move the focused lane of the selected pool one step down. */
|
|
638
|
+
export function moveBpmnLaneDown(std) {
|
|
639
|
+
const target = bpmnLaneToMove(std, 1);
|
|
640
|
+
if (target)
|
|
641
|
+
moveBpmnLane(std, target.pool, target.index, target.index + 1);
|
|
642
|
+
}
|
package/dist/background.js
CHANGED
|
@@ -26,7 +26,15 @@ import { BPMN_ROLE } from './roles.js';
|
|
|
26
26
|
*
|
|
27
27
|
* The left margin IS the name band: `margin.left` is both the room the flow
|
|
28
28
|
* area gives up and the width of the strip the participant name is written in
|
|
29
|
-
* (see `BackgroundSideBandDef`).
|
|
29
|
+
* (see `BackgroundSideBandDef`) — its MINIMUM width. The name WRAPS up the band
|
|
30
|
+
* (`fit: 'wrap'`), on words, into as many as three lines side by side, and the
|
|
31
|
+
* band thickens to hold them: one line fits the declared 28 units, every
|
|
32
|
+
* further line adds one line height. The PO preferred this to the ellipsis cut
|
|
33
|
+
* PR #532 shipped (2026-10-27): a band that grows with its name reads as a
|
|
34
|
+
* title, a name cut short reads as a bug. A name past three lines is still cut
|
|
35
|
+
* with "…" on the last one, so a pasted paragraph does not eat the pool. The
|
|
36
|
+
* thickness is derived at paint time (`backgroundBandWidths`) and never
|
|
37
|
+
* stored, and the stored name stays whole.
|
|
30
38
|
*
|
|
31
39
|
* ## The lanes
|
|
32
40
|
*
|
|
@@ -43,6 +51,10 @@ import { BPMN_ROLE } from './roles.js';
|
|
|
43
51
|
*
|
|
44
52
|
* Each lane wears its own title band inside the pool's — a narrower strip, no
|
|
45
53
|
* fill, one divider, the name turned on its side, exactly as BPMN 2.0 draws it.
|
|
54
|
+
* A lane name wraps the same way, up to two lines, and every lane's strip takes
|
|
55
|
+
* the thickness the most demanding name needs (PO, 2026-10-27): strips of
|
|
56
|
+
* different widths would stagger the lanes' leading edges, and lanes of one
|
|
57
|
+
* pool are siblings, so they share one edge.
|
|
46
58
|
* The strip is CHROME inside the lane and not a smaller lane: a task dropped on
|
|
47
59
|
* a lane's title band is in that lane, because in BPMN the band belongs to it.
|
|
48
60
|
*/
|
|
@@ -99,6 +111,13 @@ export const BPMN_POOL_BACKGROUND = {
|
|
|
99
111
|
// pool is the same stroke, so the lanes read as the frame continued.
|
|
100
112
|
divider: { color: '@frame', width: POOL_FRAME_WIDTH },
|
|
101
113
|
},
|
|
114
|
+
// Wrapped across the strip, which grows to hold the lines — every lane's
|
|
115
|
+
// strip to the same thickness. Two lines at most: a lane title is a
|
|
116
|
+
// label, and past two a third line would cost the flow area more than
|
|
117
|
+
// the name gains; past that it is cut with "…". The stored name stays
|
|
118
|
+
// whole, and the rename editor shows it in full.
|
|
119
|
+
fit: 'wrap',
|
|
120
|
+
maxLines: 2,
|
|
102
121
|
style: {
|
|
103
122
|
size: POOL_LANE_NAME_FONT_SIZE,
|
|
104
123
|
color: '@name',
|
|
@@ -151,6 +170,12 @@ export const BPMN_POOL_BACKGROUND = {
|
|
|
151
170
|
},
|
|
152
171
|
// Read bottom-to-top, as the spec draws a vertical pool name.
|
|
153
172
|
vertical: true,
|
|
173
|
+
// Wrapped up the band, which grows to hold the lines (three at
|
|
174
|
+
// most, the last cut with "…" if the name is longer still). The
|
|
175
|
+
// anchor stays half the DECLARED width in from the plot edge; the
|
|
176
|
+
// primitive keeps it in the middle of the grown band.
|
|
177
|
+
fit: 'wrap',
|
|
178
|
+
maxLines: 3,
|
|
154
179
|
},
|
|
155
180
|
},
|
|
156
181
|
],
|
|
@@ -199,6 +199,22 @@ export const bpmnCommandsManifest = [
|
|
|
199
199
|
scope: 'edgeless',
|
|
200
200
|
defaultKeys: { mac: [], other: [] },
|
|
201
201
|
},
|
|
202
|
+
{
|
|
203
|
+
id: 'bpmn.moveLaneUp',
|
|
204
|
+
owner: 'bpmn',
|
|
205
|
+
labelKey: 'com.labre.commands.bpmn.moveLaneUp',
|
|
206
|
+
labelFallback: 'Move lane up',
|
|
207
|
+
scope: 'edgeless',
|
|
208
|
+
defaultKeys: { mac: [], other: [] },
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: 'bpmn.moveLaneDown',
|
|
212
|
+
owner: 'bpmn',
|
|
213
|
+
labelKey: 'com.labre.commands.bpmn.moveLaneDown',
|
|
214
|
+
labelFallback: 'Move lane down',
|
|
215
|
+
scope: 'edgeless',
|
|
216
|
+
defaultKeys: { mac: [], other: [] },
|
|
217
|
+
},
|
|
202
218
|
{
|
|
203
219
|
id: 'bpmn.exportXml',
|
|
204
220
|
owner: 'bpmn',
|
package/dist/commands.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { StrokeStyle } from '@formicoidea/labre-core/model';
|
|
2
|
-
import { activateBpmnAssociation, activateBpmnMessageFlow, activateBpmnSequenceFlow, addBpmnLane, bpmnLanesOf, bpmnPoolsForLaneEdit, bpmnPoolsSelected, createBpmnNode, createBpmnPool, exportBpmnXmlFile, importBpmnSvgFile, importBpmnXmlFile, removeBpmnLane, } from './actions.js';
|
|
2
|
+
import { activateBpmnAssociation, activateBpmnMessageFlow, activateBpmnSequenceFlow, addBpmnLane, bpmnLanesOf, bpmnPoolsForLaneEdit, bpmnPoolsSelected, createBpmnNode, createBpmnPool, exportBpmnXmlFile, importBpmnSvgFile, importBpmnXmlFile, bpmnLaneToMove, moveBpmnLaneDown, moveBpmnLaneUp, removeBpmnLane, } from './actions.js';
|
|
3
3
|
import { BPMN_EDGE_STYLE, NODE_FILL, NODE_SIZE, } from './consts.js';
|
|
4
4
|
import { bpmnNodeProps } from './presets.js';
|
|
5
5
|
import { BPMN_ROLE, BPMN_ROLE_OF_KIND } from './roles.js';
|
|
6
|
-
import { bpmnAssociationIcon, bpmnCallActivityIcon, bpmnDataObjectIcon, bpmnDataStoreIcon, bpmnEndIcon, bpmnEndMessageIcon, bpmnEndTerminateIcon, bpmnExportXmlIcon, bpmnGatewayIcon, bpmnGatewayParallelIcon, bpmnGroupIcon, bpmnImportSvgIcon, bpmnImportXmlIcon, bpmnLaneAddIcon, bpmnLaneRemoveIcon, bpmnMessageIcon, bpmnPoolIcon, bpmnSequenceIcon, bpmnStartIcon, bpmnStartMessageIcon, bpmnStartTimerIcon, bpmnSubProcessIcon, bpmnTaskIcon, bpmnTaskServiceIcon, bpmnTaskUserIcon, bpmnTextAnnotationIcon, bpmnToolbarIcon, } from './toolbar/icons.js';
|
|
6
|
+
import { bpmnAssociationIcon, bpmnCallActivityIcon, bpmnDataObjectIcon, bpmnDataStoreIcon, bpmnEndIcon, bpmnEndMessageIcon, bpmnEndTerminateIcon, bpmnExportXmlIcon, bpmnGatewayIcon, bpmnGatewayParallelIcon, bpmnGroupIcon, bpmnImportSvgIcon, bpmnImportXmlIcon, bpmnLaneAddIcon, bpmnLaneDownIcon, bpmnLaneRemoveIcon, bpmnLaneUpIcon, bpmnMessageIcon, bpmnPoolIcon, bpmnSequenceIcon, bpmnStartIcon, bpmnStartMessageIcon, bpmnStartTimerIcon, bpmnSubProcessIcon, bpmnTaskIcon, bpmnTaskServiceIcon, bpmnTaskUserIcon, bpmnTextAnnotationIcon, bpmnToolbarIcon, } from './toolbar/icons.js';
|
|
7
7
|
/**
|
|
8
8
|
* What a spec DOES — its own gesture, or the one its {@link Spec.node} implies.
|
|
9
9
|
*
|
|
@@ -493,7 +493,7 @@ const laneCommands = [
|
|
|
493
493
|
labelKey: 'com.labre.commands.bpmn.removeLane',
|
|
494
494
|
labelFallback: 'Remove lane',
|
|
495
495
|
descriptionKey: 'com.labre.commands.bpmn.removeLane.description',
|
|
496
|
-
descriptionFallback: 'Remove the
|
|
496
|
+
descriptionFallback: 'Remove the focused lane of the selected pool, or its last lane. Nothing drawn in it moves.',
|
|
497
497
|
iconKey: 'bpmn.lane-remove',
|
|
498
498
|
order: SPECS.length + 1,
|
|
499
499
|
run: removeBpmnLane,
|
|
@@ -501,6 +501,36 @@ const laneCommands = [
|
|
|
501
501
|
// …and additionally: a pool with no lane has none to remove.
|
|
502
502
|
when: std => bpmnPoolsForLaneEdit(std).some(model => bpmnLanesOf(model).length > 0),
|
|
503
503
|
},
|
|
504
|
+
// The two REORDER commands act on the FOCUSED lane — the one a single click
|
|
505
|
+
// on its title strip picked in the selected pool (`bpmnFocusedLaneIndex`).
|
|
506
|
+
// A lane is not an element and cannot be selected, so without a focus there
|
|
507
|
+
// is no lane to move and the commands are not offered at all.
|
|
508
|
+
{
|
|
509
|
+
...LANE_COMMON,
|
|
510
|
+
id: 'bpmn.moveLaneUp',
|
|
511
|
+
labelKey: 'com.labre.commands.bpmn.moveLaneUp',
|
|
512
|
+
labelFallback: 'Move lane up',
|
|
513
|
+
descriptionKey: 'com.labre.commands.bpmn.moveLaneUp.description',
|
|
514
|
+
descriptionFallback: 'Move the focused lane above the one before it, with everything drawn in it.',
|
|
515
|
+
iconKey: 'bpmn.lane-up',
|
|
516
|
+
order: SPECS.length + 5,
|
|
517
|
+
run: moveBpmnLaneUp,
|
|
518
|
+
telemetry: { framework: 'bpmn', element: 'pool:lane-move-up' },
|
|
519
|
+
when: std => bpmnLaneToMove(std, -1) !== null,
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
...LANE_COMMON,
|
|
523
|
+
id: 'bpmn.moveLaneDown',
|
|
524
|
+
labelKey: 'com.labre.commands.bpmn.moveLaneDown',
|
|
525
|
+
labelFallback: 'Move lane down',
|
|
526
|
+
descriptionKey: 'com.labre.commands.bpmn.moveLaneDown.description',
|
|
527
|
+
descriptionFallback: 'Move the focused lane below the one after it, with everything drawn in it.',
|
|
528
|
+
iconKey: 'bpmn.lane-down',
|
|
529
|
+
order: SPECS.length + 6,
|
|
530
|
+
run: moveBpmnLaneDown,
|
|
531
|
+
telemetry: { framework: 'bpmn', element: 'pool:lane-move-down' },
|
|
532
|
+
when: std => bpmnLaneToMove(std, 1) !== null,
|
|
533
|
+
},
|
|
504
534
|
];
|
|
505
535
|
/**
|
|
506
536
|
* The EXPORT — the first BPMN command whose subject is the whole board.
|
|
@@ -721,6 +751,8 @@ export const bpmnCommandIcons = {
|
|
|
721
751
|
'bpmn.pool': bpmnPoolIcon,
|
|
722
752
|
'bpmn.lane-add': bpmnLaneAddIcon,
|
|
723
753
|
'bpmn.lane-remove': bpmnLaneRemoveIcon,
|
|
754
|
+
'bpmn.lane-up': bpmnLaneUpIcon,
|
|
755
|
+
'bpmn.lane-down': bpmnLaneDownIcon,
|
|
724
756
|
'bpmn.export-xml': bpmnExportXmlIcon,
|
|
725
757
|
'bpmn.import-xml': bpmnImportXmlIcon,
|
|
726
758
|
'bpmn.import-svg': bpmnImportSvgIcon,
|
package/dist/consts.d.ts
CHANGED
|
@@ -268,8 +268,8 @@ export declare const POOL_LANE_MIN_HEIGHT = 24;
|
|
|
268
268
|
* participant, and a second lane that matched the first one's height would
|
|
269
269
|
* claim room the process has not asked for.
|
|
270
270
|
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
271
|
+
* The toolbox draws its pool at them (`bpmnPoolElement`, which `createBpmnPool`
|
|
272
|
+
* calls); the templates lay their pools out at sizes of their own.
|
|
273
273
|
*/
|
|
274
274
|
export declare const POOL_REF_WIDTH = 560;
|
|
275
275
|
export declare const POOL_REF_HEIGHT = 200;
|
package/dist/consts.js
CHANGED
|
@@ -329,8 +329,8 @@ export const POOL_LANE_MIN_HEIGHT = 24;
|
|
|
329
329
|
* participant, and a second lane that matched the first one's height would
|
|
330
330
|
* claim room the process has not asked for.
|
|
331
331
|
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
332
|
+
* The toolbox draws its pool at them (`bpmnPoolElement`, which `createBpmnPool`
|
|
333
|
+
* calls); the templates lay their pools out at sizes of their own.
|
|
334
334
|
*/
|
|
335
335
|
export const POOL_REF_WIDTH = 560;
|
|
336
336
|
export const POOL_REF_HEIGHT = 200;
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { type ElementRenderer } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import type { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
|
|
3
3
|
/**
|
|
4
|
-
* Canvas renderer for the BPMN pool
|
|
4
|
+
* Canvas renderer for the BPMN pool: the declared background, then the outline
|
|
5
|
+
* of the FOCUSED lane, if this editor has one (`BpmnLaneFocusHolder`, held by
|
|
6
|
+
* the pool's view and never stored).
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The outline is the editor talking about the drawing — "this is the lane the
|
|
9
|
+
* reorder commands will move" — not part of it, so it is the theme's accent
|
|
10
|
+
* (`--affine-primary-color`, resolved through the renderer) rather than a
|
|
11
|
+
* notation colour. It is painted only onto the editor's OWN canvases: the SVG
|
|
12
|
+
* and PNG exports paint through this same renderer into a context of their
|
|
13
|
+
* own, and a file must not carry an on-screen focus. A renderer with no editor
|
|
14
|
+
* behind it (a bare unit test) paints none either, and a host that defines no
|
|
15
|
+
* accent gets none.
|
|
16
|
+
*
|
|
17
|
+
* Painted in the transform the primitive left on the context — element-local
|
|
18
|
+
* units, rotated about the centre like the rest of the pool.
|
|
11
19
|
*
|
|
12
20
|
* Exported as a function as well as an extension because the fidelity suite
|
|
13
21
|
* drives it directly with a canvas stub.
|
|
14
22
|
*/
|
|
15
23
|
export declare const bpmnPool: ElementRenderer<BpmnPoolElementModel>;
|
|
16
|
-
export declare const BpmnPoolRendererExtension: import("@formicoidea/labre-core/_pkgs/store").ExtensionType & {
|
|
17
|
-
identifier: import("@formicoidea/labre-core/_pkgs/global/di").ServiceIdentifier<ElementRenderer<BpmnPoolElementModel>>;
|
|
24
|
+
export declare const BpmnPoolRendererExtension: import("@formicoidea/labre-core/_pkgs/store/index.js").ExtensionType & {
|
|
25
|
+
identifier: import("@formicoidea/labre-core/_pkgs/global/di/types.js").ServiceIdentifier<ElementRenderer<BpmnPoolElementModel>>;
|
|
18
26
|
};
|