@formicoidea/labre-framework-bpmn 0.32.0 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +202 -6
- package/dist/actions.js +427 -43
- package/dist/background.d.ts +2 -0
- package/dist/background.js +158 -0
- package/dist/commands-manifest.d.ts +18 -0
- package/dist/commands-manifest.js +226 -0
- package/dist/commands.js +496 -5
- package/dist/consts.d.ts +195 -4
- package/dist/consts.js +230 -4
- package/dist/element-renderer.d.ts +10 -4
- package/dist/element-renderer.js +14 -55
- package/dist/element-view.d.ts +119 -8
- package/dist/element-view.js +274 -30
- package/dist/export.d.ts +277 -0
- package/dist/export.js +1802 -0
- package/dist/facts.d.ts +48 -0
- package/dist/facts.js +127 -0
- package/dist/import.d.ts +69 -0
- package/dist/import.js +1476 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +47 -0
- package/dist/interchange.d.ts +109 -0
- package/dist/interchange.js +191 -0
- package/dist/morph.d.ts +61 -0
- package/dist/morph.js +118 -0
- package/dist/node/node-renderer.d.ts +0 -9
- package/dist/node/node-renderer.js +294 -17
- package/dist/pool-hit.d.ts +98 -0
- package/dist/pool-hit.js +130 -0
- package/dist/presets.d.ts +168 -0
- package/dist/presets.js +327 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +189 -0
- package/dist/roles.d.ts +96 -0
- package/dist/roles.js +410 -0
- package/dist/rules.d.ts +199 -0
- package/dist/rules.js +1539 -0
- package/dist/templates/index.js +116 -9
- package/dist/toolbar/bpmn-senior-button.js +8 -2
- package/dist/toolbar/config.d.ts +28 -2
- package/dist/toolbar/config.js +93 -4
- package/dist/toolbar/icons.d.ts +67 -0
- package/dist/toolbar/icons.js +141 -0
- package/dist/toolbar/senior-tool.js +1 -0
- package/dist/translations.d.ts +3 -1
- package/dist/translations.js +38 -3
- package/dist/view.d.ts +6 -2
- package/dist/view.js +68 -5
- package/package.json +6 -2
package/dist/element-view.d.ts
CHANGED
|
@@ -1,21 +1,132 @@
|
|
|
1
1
|
import type { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
|
|
2
|
+
import type { EditorHost } from '@formicoidea/labre-core/std';
|
|
3
|
+
import type { PointTestOptions } from '@formicoidea/labre-core/std/gfx';
|
|
2
4
|
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
3
5
|
/**
|
|
4
|
-
* View for a BPMN pool.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* View for a BPMN pool. Three direct gestures live here:
|
|
7
|
+
*
|
|
8
|
+
* - **dblclick in the pool's own title band** — the left margin strip the
|
|
9
|
+
* participant name is written up — edits that name in place;
|
|
10
|
+
* - **dblclick in a lane's title band** edits THAT lane's name instead. A lane
|
|
11
|
+
* name is not in the declaration's hit-test walk (`backgroundLabelHits`,
|
|
12
|
+
* which Wardley uses): it is a function of the MODEL, not of the declaration,
|
|
13
|
+
* so the box comes from `backgroundInstanceZoneBand` — the very rectangle the
|
|
14
|
+
* renderer paints the name in, rather than a second set of metrics that would
|
|
15
|
+
* one day disagree with it;
|
|
16
|
+
* - **drag on an internal lane boundary** moves the separator, taking from one
|
|
17
|
+
* lane and giving to the other.
|
|
18
|
+
*
|
|
19
|
+
* Both renames are ZONED to their band (PO recette, 2026-08-26). The whole pool
|
|
20
|
+
* used to open the participant editor, which was right while a pool had one
|
|
21
|
+
* name; with a name per lane it would mean a double-click in the middle of the
|
|
22
|
+
* flow area renames the participant — neither of the two things the user could
|
|
23
|
+
* have meant, and the kind of write nobody notices until it is in a
|
|
24
|
+
* deliverable. A double-click on open canvas inside the pool now does nothing,
|
|
25
|
+
* and the `text` cursor over either band is what says where the names are.
|
|
26
|
+
*
|
|
27
|
+
* ## How the separator drag takes the gesture
|
|
28
|
+
*
|
|
29
|
+
* `GfxElementModelView.dispatch` reports a drag as handled whenever a handler
|
|
30
|
+
* is REGISTERED, without consulting what the handler returned, and the default
|
|
31
|
+
* tool stands down on that report. A permanently registered `dragstart` would
|
|
32
|
+
* therefore make a pool undraggable. So the drag handlers are ARMED — attached
|
|
33
|
+
* while the pointer is over a boundary of a selected pool, detached the moment
|
|
34
|
+
* it is not — and the pool moves normally everywhere else.
|
|
35
|
+
*
|
|
36
|
+
* Arming happens on `pointermove` AND on `pointerdown`, and neither is a hover
|
|
37
|
+
* requirement: a touch drag emits its first `pointermove` before the drag
|
|
38
|
+
* threshold is crossed (the move controller listens on the host, the drag
|
|
39
|
+
* controller on the document, so the host listener runs first), which is what
|
|
40
|
+
* makes the grab zone work with no hovering at all. The `ns-resize` cursor is a
|
|
41
|
+
* bonus for whoever has a mouse, never the affordance itself.
|
|
42
|
+
*
|
|
43
|
+
* ## The pool must be selected first
|
|
44
|
+
*
|
|
45
|
+
* Both the cursor and the grab are gated on the pool being selected. On an
|
|
46
|
+
* infinite canvas, dragging over an element means "move it"; silently turning a
|
|
47
|
+
* twelve-unit strip of an UNSELECTED pool into a resize handle would take that
|
|
48
|
+
* away from a user who never said they were working on this pool. One click
|
|
49
|
+
* first, and then the strip is live.
|
|
50
|
+
*
|
|
51
|
+
* ponytail: a ROTATED pool is not accounted for — the pointer is converted to
|
|
52
|
+
* element-local coordinates by subtraction, so every hit box here assumes an
|
|
53
|
+
* upright pool. Same reserve `backgroundAxisFacts` documents, for the same
|
|
54
|
+
* reason: nothing rotates a framework background today. Upgrade: rotate the
|
|
55
|
+
* local point by `-model.rotate` about the element centre, at the one function
|
|
56
|
+
* that has the element in hand (`_localPoint`), not a new declared field.
|
|
7
57
|
*/
|
|
8
58
|
export declare class BpmnPoolView extends GfxElementModelView<BpmnPoolElementModel> {
|
|
9
59
|
static type: string;
|
|
10
60
|
private _nameEditor;
|
|
61
|
+
/** The boundary the pointer is over, and the handlers armed for it. */
|
|
62
|
+
private _armed;
|
|
63
|
+
/** Everything a separator drag needs, frozen at `dragstart`. */
|
|
64
|
+
private _drag;
|
|
11
65
|
onCreated(): void;
|
|
12
66
|
onDestroyed(): void;
|
|
67
|
+
/** Hand the cursor back on the way out; nothing here owns it for long. */
|
|
68
|
+
private _leave;
|
|
69
|
+
/** The pointer, in element-local model units. */
|
|
70
|
+
private _localPoint;
|
|
71
|
+
/**
|
|
72
|
+
* The boxes the gestures below aim at, all of them delegated to `pool-hit.ts`.
|
|
73
|
+
*
|
|
74
|
+
* That module is pure, so every answer here can be asserted without an
|
|
75
|
+
* editor, a viewport or a canvas; and it derives each box from
|
|
76
|
+
* `backgroundInstanceZones` / `backgroundInstanceZoneBand`, the same two
|
|
77
|
+
* functions the renderer paints from and the audit reports from. The view
|
|
78
|
+
* supplies the pointer and the zoom and nothing else.
|
|
79
|
+
*/
|
|
80
|
+
private _bands;
|
|
81
|
+
private _boundaryAt;
|
|
82
|
+
/** Which name this point aims at, if any — lane strip first, see `pool-hit`. */
|
|
83
|
+
private _targetAt;
|
|
84
|
+
private get _editable();
|
|
85
|
+
/**
|
|
86
|
+
* A pool is SELECTED by its border and its painted title bands
|
|
87
|
+
* (`BpmnPoolElementModel.includesPoint`) — but two of this view's gestures
|
|
88
|
+
* live elsewhere on it and must still receive their pointer events:
|
|
89
|
+
*
|
|
90
|
+
* - the internal LANE SEPARATORS, twelve-unit strips in the middle of the
|
|
91
|
+
* flow area, which arm the drag that moves them;
|
|
92
|
+
* - the zoom-GROWN rename targets, which reach past the painted bands when
|
|
93
|
+
* the board is small on screen so a fingertip can still land on one.
|
|
94
|
+
*
|
|
95
|
+
* Neither is a selection zone — a click in the flow area still goes to
|
|
96
|
+
* whatever the user dropped there, because picking asks the MODEL. This
|
|
97
|
+
* override only decides where the pool's own gestures are heard, which is why
|
|
98
|
+
* it is declared here, beside the code that draws and handles them, rather
|
|
99
|
+
* than in `affine-model` where none of this geometry is reachable.
|
|
100
|
+
*/
|
|
101
|
+
includesPoint(x: number, y: number, options: PointTestOptions, host: EditorHost): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* One pass over the pointer, deciding both the cursor and whether a
|
|
104
|
+
* separator drag is armed.
|
|
105
|
+
*
|
|
106
|
+
* The order is the order the gestures win in. A separator sits INSIDE a lane
|
|
107
|
+
* title band at every lane boundary, so the two overlap and something has to
|
|
108
|
+
* give: the separator takes it, because it is a twelve-unit strip the user
|
|
109
|
+
* has to aim at deliberately, while the title band is the whole leading edge
|
|
110
|
+
* and has plenty left over. It is also already gated on the pool being
|
|
111
|
+
* selected, so on an unselected pool the band wins uncontested.
|
|
112
|
+
*/
|
|
113
|
+
private _updateHover;
|
|
114
|
+
private _disarm;
|
|
115
|
+
private _onDragStart;
|
|
116
|
+
private _onDragMove;
|
|
117
|
+
private _onDragEnd;
|
|
118
|
+
/**
|
|
119
|
+
* A double-click renames whatever TITLE BAND it landed in, and nothing
|
|
120
|
+
* otherwise (PO recette, 2026-08-26).
|
|
121
|
+
*
|
|
122
|
+
* The whole pool used to open the participant editor. That was right while a
|
|
123
|
+
* pool had one name; now that every lane carries one it would mean a
|
|
124
|
+
* double-click in the middle of the flow area renames the participant — not
|
|
125
|
+
* one of the things the user could have meant, and the kind of write nobody
|
|
126
|
+
* notices until it is in a deliverable. A double-click on open canvas inside
|
|
127
|
+
* the pool now does nothing, which is the honest answer.
|
|
128
|
+
*/
|
|
13
129
|
private _onDblClick;
|
|
14
130
|
private _openEditor;
|
|
15
131
|
private _closeEditor;
|
|
16
132
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
19
|
-
* true (toggled from the toolbar). Moving / selecting stays available.
|
|
20
|
-
*/
|
|
21
|
-
export declare const BpmnPoolInteraction: import("@formicoidea/labre-core/store").ExtensionType;
|
package/dist/element-view.js
CHANGED
|
@@ -1,33 +1,295 @@
|
|
|
1
1
|
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import { GfxElementModelView
|
|
2
|
+
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
+
import { bpmnLanesOf, renameBpmnLane } from './actions.js';
|
|
4
|
+
import { POOL_LANE_MIN_HEIGHT } from './consts.js';
|
|
5
|
+
import { bpmnLaneBoundaryAt, bpmnPoolBands, bpmnPoolTargetAt, } from './pool-hit.js';
|
|
3
6
|
/**
|
|
4
|
-
* View for a BPMN pool.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
+
* View for a BPMN pool. Three direct gestures live here:
|
|
8
|
+
*
|
|
9
|
+
* - **dblclick in the pool's own title band** — the left margin strip the
|
|
10
|
+
* participant name is written up — edits that name in place;
|
|
11
|
+
* - **dblclick in a lane's title band** edits THAT lane's name instead. A lane
|
|
12
|
+
* name is not in the declaration's hit-test walk (`backgroundLabelHits`,
|
|
13
|
+
* which Wardley uses): it is a function of the MODEL, not of the declaration,
|
|
14
|
+
* so the box comes from `backgroundInstanceZoneBand` — the very rectangle the
|
|
15
|
+
* renderer paints the name in, rather than a second set of metrics that would
|
|
16
|
+
* one day disagree with it;
|
|
17
|
+
* - **drag on an internal lane boundary** moves the separator, taking from one
|
|
18
|
+
* lane and giving to the other.
|
|
19
|
+
*
|
|
20
|
+
* Both renames are ZONED to their band (PO recette, 2026-08-26). The whole pool
|
|
21
|
+
* used to open the participant editor, which was right while a pool had one
|
|
22
|
+
* name; with a name per lane it would mean a double-click in the middle of the
|
|
23
|
+
* flow area renames the participant — neither of the two things the user could
|
|
24
|
+
* have meant, and the kind of write nobody notices until it is in a
|
|
25
|
+
* deliverable. A double-click on open canvas inside the pool now does nothing,
|
|
26
|
+
* and the `text` cursor over either band is what says where the names are.
|
|
27
|
+
*
|
|
28
|
+
* ## How the separator drag takes the gesture
|
|
29
|
+
*
|
|
30
|
+
* `GfxElementModelView.dispatch` reports a drag as handled whenever a handler
|
|
31
|
+
* is REGISTERED, without consulting what the handler returned, and the default
|
|
32
|
+
* tool stands down on that report. A permanently registered `dragstart` would
|
|
33
|
+
* therefore make a pool undraggable. So the drag handlers are ARMED — attached
|
|
34
|
+
* while the pointer is over a boundary of a selected pool, detached the moment
|
|
35
|
+
* it is not — and the pool moves normally everywhere else.
|
|
36
|
+
*
|
|
37
|
+
* Arming happens on `pointermove` AND on `pointerdown`, and neither is a hover
|
|
38
|
+
* requirement: a touch drag emits its first `pointermove` before the drag
|
|
39
|
+
* threshold is crossed (the move controller listens on the host, the drag
|
|
40
|
+
* controller on the document, so the host listener runs first), which is what
|
|
41
|
+
* makes the grab zone work with no hovering at all. The `ns-resize` cursor is a
|
|
42
|
+
* bonus for whoever has a mouse, never the affordance itself.
|
|
43
|
+
*
|
|
44
|
+
* ## The pool must be selected first
|
|
45
|
+
*
|
|
46
|
+
* Both the cursor and the grab are gated on the pool being selected. On an
|
|
47
|
+
* infinite canvas, dragging over an element means "move it"; silently turning a
|
|
48
|
+
* twelve-unit strip of an UNSELECTED pool into a resize handle would take that
|
|
49
|
+
* away from a user who never said they were working on this pool. One click
|
|
50
|
+
* first, and then the strip is live.
|
|
51
|
+
*
|
|
52
|
+
* ponytail: a ROTATED pool is not accounted for — the pointer is converted to
|
|
53
|
+
* element-local coordinates by subtraction, so every hit box here assumes an
|
|
54
|
+
* upright pool. Same reserve `backgroundAxisFacts` documents, for the same
|
|
55
|
+
* reason: nothing rotates a framework background today. Upgrade: rotate the
|
|
56
|
+
* local point by `-model.rotate` about the element centre, at the one function
|
|
57
|
+
* that has the element in hand (`_localPoint`), not a new declared field.
|
|
7
58
|
*/
|
|
8
59
|
export class BpmnPoolView extends GfxElementModelView {
|
|
9
60
|
constructor() {
|
|
10
61
|
super(...arguments);
|
|
11
62
|
this._nameEditor = null;
|
|
63
|
+
/** The boundary the pointer is over, and the handlers armed for it. */
|
|
64
|
+
this._armed = null;
|
|
65
|
+
/** Everything a separator drag needs, frozen at `dragstart`. */
|
|
66
|
+
this._drag = null;
|
|
12
67
|
}
|
|
13
68
|
static { this.type = 'bpmnPool'; }
|
|
14
69
|
onCreated() {
|
|
15
70
|
super.onCreated();
|
|
16
71
|
this.on('dblclick', e => this._onDblClick(e));
|
|
72
|
+
this.on('pointermove', e => this._updateHover(e));
|
|
73
|
+
this.on('pointerdown', e => this._updateHover(e));
|
|
74
|
+
this.on('pointerleave', () => this._leave());
|
|
17
75
|
}
|
|
18
76
|
onDestroyed() {
|
|
19
77
|
this._closeEditor();
|
|
78
|
+
this._leave();
|
|
20
79
|
super.onDestroyed();
|
|
21
80
|
}
|
|
81
|
+
/** Hand the cursor back on the way out; nothing here owns it for long. */
|
|
82
|
+
_leave() {
|
|
83
|
+
this._disarm();
|
|
84
|
+
if (!this._drag)
|
|
85
|
+
this.gfx.cursor$.value = 'default';
|
|
86
|
+
}
|
|
87
|
+
/* ── Lane geometry ─────────────────────────────────────────────────── */
|
|
88
|
+
/** The pointer, in element-local model units. */
|
|
89
|
+
_localPoint(e) {
|
|
90
|
+
const [mx, my] = this.gfx.viewport.toModelCoord(e.x, e.y);
|
|
91
|
+
const [ex, ey] = this.model.deserializedXYWH;
|
|
92
|
+
return [mx - ex, my - ey];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The boxes the gestures below aim at, all of them delegated to `pool-hit.ts`.
|
|
96
|
+
*
|
|
97
|
+
* That module is pure, so every answer here can be asserted without an
|
|
98
|
+
* editor, a viewport or a canvas; and it derives each box from
|
|
99
|
+
* `backgroundInstanceZones` / `backgroundInstanceZoneBand`, the same two
|
|
100
|
+
* functions the renderer paints from and the audit reports from. The view
|
|
101
|
+
* supplies the pointer and the zoom and nothing else.
|
|
102
|
+
*/
|
|
103
|
+
_bands() {
|
|
104
|
+
return bpmnPoolBands(this.model);
|
|
105
|
+
}
|
|
106
|
+
_boundaryAt(local) {
|
|
107
|
+
return bpmnLaneBoundaryAt(this.model, local);
|
|
108
|
+
}
|
|
109
|
+
/** Which name this point aims at, if any — lane strip first, see `pool-hit`. */
|
|
110
|
+
_targetAt(local) {
|
|
111
|
+
return bpmnPoolTargetAt(this.model, local, this.gfx.viewport.zoom);
|
|
112
|
+
}
|
|
113
|
+
get _editable() {
|
|
114
|
+
return !this.gfx.std.store.readonly && !this.model.isLocked();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A pool is SELECTED by its border and its painted title bands
|
|
118
|
+
* (`BpmnPoolElementModel.includesPoint`) — but two of this view's gestures
|
|
119
|
+
* live elsewhere on it and must still receive their pointer events:
|
|
120
|
+
*
|
|
121
|
+
* - the internal LANE SEPARATORS, twelve-unit strips in the middle of the
|
|
122
|
+
* flow area, which arm the drag that moves them;
|
|
123
|
+
* - the zoom-GROWN rename targets, which reach past the painted bands when
|
|
124
|
+
* the board is small on screen so a fingertip can still land on one.
|
|
125
|
+
*
|
|
126
|
+
* Neither is a selection zone — a click in the flow area still goes to
|
|
127
|
+
* whatever the user dropped there, because picking asks the MODEL. This
|
|
128
|
+
* override only decides where the pool's own gestures are heard, which is why
|
|
129
|
+
* it is declared here, beside the code that draws and handles them, rather
|
|
130
|
+
* than in `affine-model` where none of this geometry is reachable.
|
|
131
|
+
*/
|
|
132
|
+
includesPoint(x, y, options, host) {
|
|
133
|
+
if (super.includesPoint(x, y, options, host))
|
|
134
|
+
return true;
|
|
135
|
+
// Element-local by subtraction, like every other box in this view — see the
|
|
136
|
+
// rotation reserve in the class comment.
|
|
137
|
+
const [ex, ey] = this.model.deserializedXYWH;
|
|
138
|
+
const local = [x - ex, y - ey];
|
|
139
|
+
return this._boundaryAt(local) !== null || this._targetAt(local) !== null;
|
|
140
|
+
}
|
|
141
|
+
/* ── Hover: what this point would do ───────────────────────────────── */
|
|
142
|
+
/**
|
|
143
|
+
* One pass over the pointer, deciding both the cursor and whether a
|
|
144
|
+
* separator drag is armed.
|
|
145
|
+
*
|
|
146
|
+
* The order is the order the gestures win in. A separator sits INSIDE a lane
|
|
147
|
+
* title band at every lane boundary, so the two overlap and something has to
|
|
148
|
+
* give: the separator takes it, because it is a twelve-unit strip the user
|
|
149
|
+
* has to aim at deliberately, while the title band is the whole leading edge
|
|
150
|
+
* and has plenty left over. It is also already gated on the pool being
|
|
151
|
+
* selected, so on an unselected pool the band wins uncontested.
|
|
152
|
+
*/
|
|
153
|
+
_updateHover(e) {
|
|
154
|
+
const local = this._localPoint(e);
|
|
155
|
+
const selected = this.gfx.selection.selectedIds.includes(this.model.id);
|
|
156
|
+
if (this._editable && selected) {
|
|
157
|
+
const index = this._boundaryAt(local);
|
|
158
|
+
if (index !== null) {
|
|
159
|
+
// Reasserted on every move rather than only on arrival: the cursor is a
|
|
160
|
+
// signal shared with the resize handles and the tools, and whoever set
|
|
161
|
+
// it last wins — so the one that is still true says so again.
|
|
162
|
+
this.gfx.cursor$.value = 'ns-resize';
|
|
163
|
+
if (this._armed?.index !== index) {
|
|
164
|
+
this._disarm();
|
|
165
|
+
this._armed = {
|
|
166
|
+
index,
|
|
167
|
+
disposers: [
|
|
168
|
+
this.on('dragstart', evt => this._onDragStart(evt)),
|
|
169
|
+
this.on('dragmove', evt => this._onDragMove(evt)),
|
|
170
|
+
this.on('dragend', () => this._onDragEnd()),
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this._disarm();
|
|
178
|
+
// A title band announces itself, selected or not: finding out that a name
|
|
179
|
+
// can be changed should not cost a click first. Gated on `_editable` all
|
|
180
|
+
// the same — an I-beam over a locked pool would promise an editor that
|
|
181
|
+
// refuses to open, which is the sort of small lie the recette is against.
|
|
182
|
+
const overTitle = this._editable && this._targetAt(local) !== null;
|
|
183
|
+
this.gfx.cursor$.value = overTitle ? 'text' : 'default';
|
|
184
|
+
}
|
|
185
|
+
_disarm() {
|
|
186
|
+
// Never mid-gesture: the pointer leaves the boundary as soon as the drag
|
|
187
|
+
// starts moving, and disarming there would drop the drag on its first step.
|
|
188
|
+
if (this._drag)
|
|
189
|
+
return;
|
|
190
|
+
if (!this._armed)
|
|
191
|
+
return;
|
|
192
|
+
this._armed.disposers.forEach(dispose => dispose());
|
|
193
|
+
this._armed = null;
|
|
194
|
+
}
|
|
195
|
+
_onDragStart(_) {
|
|
196
|
+
const index = this._armed?.index;
|
|
197
|
+
const geometry = this._bands();
|
|
198
|
+
if (index === undefined || !geometry || !this._editable)
|
|
199
|
+
return;
|
|
200
|
+
const lanes = bpmnLanesOf(this.model);
|
|
201
|
+
if (!lanes[index - 1] || !lanes[index])
|
|
202
|
+
return;
|
|
203
|
+
const total = lanes.reduce((sum, lane) => sum + lane.size, 0);
|
|
204
|
+
if (!(total > 0))
|
|
205
|
+
return;
|
|
206
|
+
this._drag = {
|
|
207
|
+
index,
|
|
208
|
+
lanes: lanes.map(lane => ({ ...lane })),
|
|
209
|
+
total,
|
|
210
|
+
plotHeight: geometry.plot.height,
|
|
211
|
+
// The floor is a HEIGHT the user can see, so it is stated in the model
|
|
212
|
+
// units of a pool at its reference height and converted to a weight
|
|
213
|
+
// against this pool's own total — a pool stretched to twice the height
|
|
214
|
+
// keeps the same visible floor, which is the point of weights.
|
|
215
|
+
minWeight: (POOL_LANE_MIN_HEIGHT / geometry.plot.height) * total,
|
|
216
|
+
};
|
|
217
|
+
// Local writes until the release: the intermediate weights repaint the
|
|
218
|
+
// canvas but never reach the document, so the whole drag is ONE undo step.
|
|
219
|
+
this.model.stash('lanes');
|
|
220
|
+
}
|
|
221
|
+
_onDragMove(e) {
|
|
222
|
+
const drag = this._drag;
|
|
223
|
+
if (!drag)
|
|
224
|
+
return;
|
|
225
|
+
// The travel SINCE the drag started, in view pixels (`e.delta` is the step
|
|
226
|
+
// since the last move, which is not the same thing), converted to model
|
|
227
|
+
// units. Always recomputed from the FROZEN pair rather than nudged: a nudge
|
|
228
|
+
// would accumulate the clamp and drift away from the pointer.
|
|
229
|
+
const dy = (e.y - e.start.y) / (this.gfx.viewport.zoom || 1);
|
|
230
|
+
const perUnit = drag.total / drag.plotHeight;
|
|
231
|
+
const above = drag.lanes[drag.index - 1];
|
|
232
|
+
const below = drag.lanes[drag.index];
|
|
233
|
+
const pair = above.size + below.size;
|
|
234
|
+
// The pair's total is invariant: the separator takes from one and gives to
|
|
235
|
+
// the other, so no lane the user is not touching changes size.
|
|
236
|
+
const wanted = above.size + dy * perUnit;
|
|
237
|
+
const floor = Math.min(drag.minWeight, pair / 2);
|
|
238
|
+
const nextAbove = Math.max(floor, Math.min(pair - floor, wanted));
|
|
239
|
+
const next = drag.lanes.map((lane, i) => i === drag.index - 1
|
|
240
|
+
? { ...lane, size: nextAbove }
|
|
241
|
+
: i === drag.index
|
|
242
|
+
? { ...lane, size: pair - nextAbove }
|
|
243
|
+
: lane);
|
|
244
|
+
this.model.lanes = next;
|
|
245
|
+
}
|
|
246
|
+
_onDragEnd() {
|
|
247
|
+
const drag = this._drag;
|
|
248
|
+
this._drag = null;
|
|
249
|
+
if (!drag)
|
|
250
|
+
return;
|
|
251
|
+
// Before the commit, not after: `pop` writes straight into the Y.Map, and
|
|
252
|
+
// without a boundary here a separator moved within half a second of the
|
|
253
|
+
// previous edit would be undone together with it.
|
|
254
|
+
this.gfx.std.store.captureSync();
|
|
255
|
+
this.model.pop('lanes');
|
|
256
|
+
this._disarm();
|
|
257
|
+
}
|
|
258
|
+
/* ── In-place naming ───────────────────────────────────────────────── */
|
|
259
|
+
/**
|
|
260
|
+
* A double-click renames whatever TITLE BAND it landed in, and nothing
|
|
261
|
+
* otherwise (PO recette, 2026-08-26).
|
|
262
|
+
*
|
|
263
|
+
* The whole pool used to open the participant editor. That was right while a
|
|
264
|
+
* pool had one name; now that every lane carries one it would mean a
|
|
265
|
+
* double-click in the middle of the flow area renames the participant — not
|
|
266
|
+
* one of the things the user could have meant, and the kind of write nobody
|
|
267
|
+
* notices until it is in a deliverable. A double-click on open canvas inside
|
|
268
|
+
* the pool now does nothing, which is the honest answer.
|
|
269
|
+
*/
|
|
22
270
|
_onDblClick(e) {
|
|
23
|
-
if (this.
|
|
271
|
+
if (!this._editable)
|
|
272
|
+
return;
|
|
273
|
+
const target = this._targetAt(this._localPoint(e));
|
|
274
|
+
if (target === null)
|
|
24
275
|
return;
|
|
25
|
-
|
|
276
|
+
if (target.kind === 'lane') {
|
|
277
|
+
const { index } = target;
|
|
278
|
+
const lane = bpmnLanesOf(this.model)[index];
|
|
279
|
+
this._openEditor(e, lane?.name ?? '', value => renameBpmnLane(this.gfx.std, this.model, index, value));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
this._openEditor(e, String(this.model.name ?? ''), value => {
|
|
283
|
+
this.gfx.std.store.captureSync();
|
|
284
|
+
this.gfx.std
|
|
285
|
+
.get(EdgelessCRUDIdentifier)
|
|
286
|
+
.updateElement(this.model.id, { name: value });
|
|
287
|
+
});
|
|
26
288
|
}
|
|
27
|
-
_openEditor(e) {
|
|
289
|
+
_openEditor(e, initial, commit) {
|
|
28
290
|
this._closeEditor();
|
|
29
291
|
const input = document.createElement('input');
|
|
30
|
-
input.value =
|
|
292
|
+
input.value = initial;
|
|
31
293
|
Object.assign(input.style, {
|
|
32
294
|
position: 'fixed',
|
|
33
295
|
left: `${e.raw.clientX}px`,
|
|
@@ -51,28 +313,25 @@ export class BpmnPoolView extends GfxElementModelView {
|
|
|
51
313
|
this.gfx.selection.set({ elements: [this.model.id], editing: true });
|
|
52
314
|
input.focus();
|
|
53
315
|
input.select();
|
|
54
|
-
const
|
|
316
|
+
const onCommit = () => {
|
|
55
317
|
if (this._nameEditor !== input)
|
|
56
318
|
return;
|
|
57
319
|
const value = input.value;
|
|
58
320
|
this._closeEditor();
|
|
59
|
-
|
|
60
|
-
this.gfx.std
|
|
61
|
-
.get(EdgelessCRUDIdentifier)
|
|
62
|
-
.updateElement(this.model.id, { name: value });
|
|
321
|
+
commit(value);
|
|
63
322
|
};
|
|
64
323
|
input.addEventListener('keydown', ev => {
|
|
65
324
|
ev.stopPropagation();
|
|
66
325
|
if (ev.key === 'Enter') {
|
|
67
326
|
ev.preventDefault();
|
|
68
|
-
|
|
327
|
+
onCommit();
|
|
69
328
|
}
|
|
70
329
|
else if (ev.key === 'Escape') {
|
|
71
330
|
ev.preventDefault();
|
|
72
331
|
this._closeEditor();
|
|
73
332
|
}
|
|
74
333
|
});
|
|
75
|
-
input.addEventListener('blur',
|
|
334
|
+
input.addEventListener('blur', onCommit);
|
|
76
335
|
}
|
|
77
336
|
_closeEditor() {
|
|
78
337
|
if (!this._nameEditor)
|
|
@@ -85,18 +344,3 @@ export class BpmnPoolView extends GfxElementModelView {
|
|
|
85
344
|
}
|
|
86
345
|
}
|
|
87
346
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
90
|
-
* true (toggled from the toolbar). Moving / selecting stays available.
|
|
91
|
-
*/
|
|
92
|
-
export const BpmnPoolInteraction = GfxViewInteractionExtension(BpmnPoolView.type, {
|
|
93
|
-
handleResize({ model }) {
|
|
94
|
-
return {
|
|
95
|
-
beforeResize({ set }) {
|
|
96
|
-
if (!model.resizeEnabled) {
|
|
97
|
-
set({ allowedHandlers: [] });
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
});
|