Skip to content

Commit b5d5319

Browse files
committed
WIP
1 parent 2e2953f commit b5d5319

File tree

4 files changed

+89
-63
lines changed

4 files changed

+89
-63
lines changed

docs/execution-visualizer/README.md

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,29 @@ toggleMode.toggleMode(false);
5050

5151
```javascript
5252
executionVisualizer.setExecutionState({
53-
executedElements: ['StartEvent_1', 'Flow_1', 'Task_1', 'Flow_2', 'Task_2', 'Flow_3'], // IDs of executed elements and flows
54-
activeElement: 'Task_3' // ID of currently active element
53+
completed: ['StartEvent_1', 'Flow_1', 'Task_1', 'Flow_2', 'Task_2', 'Flow_3'], // IDs of completed elements and flows
54+
active: 'Task_3' // ID(s) of currently active element(s)
5555
});
5656
```
5757

58-
- **executedElements**: Array of element IDs that have been executed, including both shapes and sequence flows (styled in gray)
59-
- **activeElement**: ID of the currently active element (styled in green)
58+
- **completed**: Array of element IDs that have been completed, including both shapes and sequence flows (styled in blue)
59+
- **active**: ID or array of IDs of the currently active element(s) (styled in blue with higher priority)
60+
61+
**Note**: `active` can be either a single string ID or an array of string IDs to support multiple active elements:
62+
63+
```javascript
64+
// Single active element
65+
executionVisualizer.setExecutionState({
66+
completed: ['StartEvent_1', 'Flow_1'],
67+
active: 'Task_1'
68+
});
69+
70+
// Multiple active elements (e.g., parallel execution)
71+
executionVisualizer.setExecutionState({
72+
completed: ['StartEvent_1', 'Flow_1', 'ParallelGateway_1'],
73+
active: ['Task_1', 'Task_2', 'Task_3']
74+
});
75+
```
6076

6177
#### Clear Visualization
6278

@@ -69,7 +85,7 @@ executionVisualizer.clear();
6985

7086
```javascript
7187
const state = executionVisualizer.getExecutionState();
72-
// Returns: { executedElements: [...], activeElement: '...' }
88+
// Returns: { completed: [...], active: [...] }
7389
```
7490

7591
## Visual Styling
@@ -87,8 +103,8 @@ const eventBus = viewer.get('eventBus');
87103

88104
// Fired when execution state changes
89105
eventBus.on('executionVisualizer.stateChanged', (event) => {
90-
console.log('Executed elements:', event.executedElements);
91-
console.log('Active element:', event.activeElement);
106+
console.log('Completed elements:', event.completed);
107+
console.log('Active elements:', event.active);
92108
});
93109

94110
// Fired when visualization is cleared

example/src/visualizer.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ function setupControls(toggleMode, executionVisualizer, eventBus) {
8181
toggleBtn.textContent = 'Mode: ON';
8282
}
8383
executionVisualizer.setExecutionState({
84-
executedElements: [],
85-
activeElement: 'StartEvent_0j9yk1o'
84+
completed: [],
85+
active: 'StartEvent_0j9yk1o'
8686
});
8787
});
8888

@@ -93,8 +93,8 @@ function setupControls(toggleMode, executionVisualizer, eventBus) {
9393
toggleBtn.textContent = 'Mode: ON';
9494
}
9595
executionVisualizer.setExecutionState({
96-
executedElements: ['StartEvent_0j9yk1o', 'SequenceFlow_1bpznq3'],
97-
activeElement: 'ParallelGateway_0s75uad'
96+
completed: ['StartEvent_0j9yk1o', 'SequenceFlow_1bpznq3'],
97+
active: 'ParallelGateway_0s75uad'
9898
});
9999
});
100100

@@ -105,8 +105,8 @@ function setupControls(toggleMode, executionVisualizer, eventBus) {
105105
toggleBtn.textContent = 'Mode: ON';
106106
}
107107
executionVisualizer.setExecutionState({
108-
executedElements: ['StartEvent_0j9yk1o', 'SequenceFlow_1bpznq3', 'ParallelGateway_0s75uad', 'SequenceFlow_10d6h3a'],
109-
activeElement: 'Task_1upmjgh'
108+
completed: ['StartEvent_0j9yk1o', 'SequenceFlow_1bpznq3', 'ParallelGateway_0s75uad', 'SequenceFlow_10d6h3a'],
109+
active: 'Task_1upmjgh'
110110
});
111111
});
112112

@@ -117,8 +117,8 @@ function setupControls(toggleMode, executionVisualizer, eventBus) {
117117
toggleBtn.textContent = 'Mode: ON';
118118
}
119119
executionVisualizer.setExecutionState({
120-
executedElements: ['StartEvent_0j9yk1o', 'SequenceFlow_1bpznq3', 'ParallelGateway_0s75uad', 'SequenceFlow_10d6h3a', 'Task_1upmjgh', 'SequenceFlow_1dzm18n'],
121-
activeElement: 'ParallelGateway_158jo5x'
120+
completed: ['StartEvent_0j9yk1o', 'SequenceFlow_1bpznq3', 'ParallelGateway_0s75uad', 'SequenceFlow_10d6h3a', 'Task_1upmjgh', 'SequenceFlow_1dzm18n'],
121+
active: ['ParallelGateway_158jo5x']
122122
});
123123
});
124124

lib/features/execution-visualizer/ExecutionVisualizer.js

Lines changed: 57 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ export default function ExecutionVisualizer(elementRegistry, elementColors, even
3131
this._eventBus = eventBus;
3232
this._overlays = overlays;
3333

34-
this._executedElements = new Set();
35-
this._activeElement = null;
36-
this._activeOverlayId = null;
34+
this._completed = new Set();
35+
this._active = new Set();
36+
this._activeOverlayIds = new Map();
3737

3838
// Clear execution state when mode is toggled off
3939
eventBus.on(TOGGLE_MODE_EVENT, event => {
@@ -51,94 +51,97 @@ ExecutionVisualizer.$inject = [
5151
];
5252

5353
/**
54-
* Set the execution state by providing executed elements and active element.
54+
* Set the execution state by providing completed elements and active elements.
5555
*
5656
* @param {Object} state
57-
* @param {string[]} [state.executedElements] - Array of element IDs that have been executed (including sequence flows)
58-
* @param {string} [state.activeElement] - ID of the currently active element
57+
* @param {string[]} [state.completed] - Array of element IDs that have been completed (including sequence flows)
58+
* @param {string|string[]} [state.active] - ID(s) of the currently active element(s)
5959
*/
6060
ExecutionVisualizer.prototype.setExecutionState = function(state) {
6161
const {
62-
executedElements = [],
63-
activeElement = null
62+
completed = [],
63+
active = []
6464
} = state;
6565

66+
// Normalize active to array
67+
const activeArray = Array.isArray(active) ? active : (active ? [active] : []);
68+
6669
// Clear previous active element styling
67-
if (this._activeElement) {
68-
const prevActiveEl = this._elementRegistry.get(this._activeElement);
69-
if (prevActiveEl) {
70-
this._elementColors.remove(prevActiveEl, 'active');
70+
this._active.forEach(id => {
71+
if (!activeArray.includes(id)) {
72+
const prevActiveEl = this._elementRegistry.get(id);
73+
if (prevActiveEl) {
74+
this._elementColors.remove(prevActiveEl, 'active');
75+
}
76+
this._removeActiveIndicator(id);
7177
}
72-
this._removeActiveIndicator();
73-
}
78+
});
7479

75-
// Clear previous executed elements that are no longer in the list
76-
this._executedElements.forEach(id => {
77-
if (!executedElements.includes(id)) {
80+
// Clear previous completed elements that are no longer in the list
81+
this._completed.forEach(id => {
82+
if (!completed.includes(id)) {
7883
const element = this._elementRegistry.get(id);
7984
if (element) {
8085
this._elementColors.remove(element, 'executed');
8186
}
8287
}
8388
});
8489

85-
// Update executed elements (including sequence flows)
86-
executedElements.forEach(id => {
90+
// Update completed elements (including sequence flows)
91+
completed.forEach(id => {
8792
const element = this._elementRegistry.get(id);
88-
if (element && id !== activeElement) {
93+
if (element && !activeArray.includes(id)) {
8994
this._elementColors.add(element, 'executed', {
9095
stroke: EXECUTED_STROKE_COLOR
9196
}, EXECUTED_PRIORITY);
9297
}
9398
});
9499

95-
// Update active element
96-
if (activeElement) {
97-
const activeEl = this._elementRegistry.get(activeElement);
100+
// Update active elements
101+
activeArray.forEach(id => {
102+
const activeEl = this._elementRegistry.get(id);
98103
if (activeEl) {
99104
this._elementColors.add(activeEl, 'active', {
100105
stroke: ACTIVE_STROKE_COLOR
101106
}, ACTIVE_PRIORITY);
102-
this._addActiveIndicator(activeEl);
107+
this._addActiveIndicator(activeEl, id);
103108
}
104-
}
109+
});
105110

106111
// Update internal state
107-
this._executedElements = new Set(executedElements);
108-
this._activeElement = activeElement;
112+
this._completed = new Set(completed);
113+
this._active = new Set(activeArray);
109114

110115
// Fire event for integrations
111116
this._eventBus.fire('executionVisualizer.stateChanged', {
112-
executedElements,
113-
activeElement
117+
completed,
118+
active: activeArray
114119
});
115120
};
116121

117122
/**
118123
* Clear all execution visualization state.
119124
*/
120125
ExecutionVisualizer.prototype.clear = function() {
121-
this._executedElements.forEach(id => {
126+
this._completed.forEach(id => {
122127
const element = this._elementRegistry.get(id);
123128
if (element) {
124129
this._elementColors.remove(element, 'executed');
125130
}
126131
});
127132

128-
// Remove active element color
129-
if (this._activeElement) {
130-
const activeEl = this._elementRegistry.get(this._activeElement);
133+
// Remove active element colors
134+
this._active.forEach(id => {
135+
const activeEl = this._elementRegistry.get(id);
131136
if (activeEl) {
132137
this._elementColors.remove(activeEl, 'active');
133138
}
134-
}
135-
136-
// Remove active indicator
137-
this._removeActiveIndicator();
139+
this._removeActiveIndicator(id);
140+
});
138141

139142
// Reset internal state
140-
this._executedElements.clear();
141-
this._activeElement = null;
143+
this._completed.clear();
144+
this._active.clear();
142145

143146
// Fire event
144147
this._eventBus.fire('executionVisualizer.cleared');
@@ -151,40 +154,47 @@ ExecutionVisualizer.prototype.clear = function() {
151154
*/
152155
ExecutionVisualizer.prototype.getExecutionState = function() {
153156
return {
154-
executedElements: Array.from(this._executedElements),
155-
activeElement: this._activeElement
157+
completed: Array.from(this._completed),
158+
active: Array.from(this._active)
156159
};
157160
};
158161

159162
/**
160163
* Add active indicator overlay to element.
161164
* @private
162165
*/
163-
ExecutionVisualizer.prototype._addActiveIndicator = function(element) {
166+
ExecutionVisualizer.prototype._addActiveIndicator = function(element, elementId) {
164167
if (is(element, 'bpmn:SequenceFlow') || is(element, 'bpmn:MessageFlow')) {
165168
return;
166169
}
167170

171+
// Skip if already exists
172+
if (this._activeOverlayIds.has(elementId)) {
173+
return;
174+
}
175+
168176
const html = domify('<div class="bts-active-indicator"></div>');
169177

170178
const position = { bottom: OFFSET_BOTTOM, right: OFFSET_LEFT };
171179

172-
this._activeOverlayId = this._overlays.add(element, 'bts-active-indicator', {
180+
const overlayId = this._overlays.add(element, 'bts-active-indicator', {
173181
position: position,
174182
html: html,
175183
show: {
176184
minZoom: 0.5
177185
}
178186
});
187+
188+
this._activeOverlayIds.set(elementId, overlayId);
179189
};
180190

181191
/**
182192
* Remove active indicator overlay.
183193
* @private
184194
*/
185-
ExecutionVisualizer.prototype._removeActiveIndicator = function() {
186-
if (this._activeOverlayId) {
187-
this._overlays.remove(this._activeOverlayId);
188-
this._activeOverlayId = null;
195+
ExecutionVisualizer.prototype._removeActiveIndicator = function(elementId) {
196+
if (this._activeOverlayIds.has(elementId)) {
197+
this._overlays.remove(this._activeOverlayIds.get(elementId));
198+
this._activeOverlayIds.delete(elementId);
189199
}
190200
};

lib/features/neutral-element-colors/NeutralElementColors.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default function NeutralElementColors(
2424
NeutralElementColors.prototype._setNeutralColors = function() {
2525
this._elementRegistry.forEach(element => {
2626
this._elementColors.add(element, ID, {
27-
stroke: '#aaa',
27+
stroke: '#212121',
2828
fill: '#fff'
2929
});
3030
});

0 commit comments

Comments
 (0)