@@ -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 */
6060ExecutionVisualizer . 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 */
120125ExecutionVisualizer . 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 */
152155ExecutionVisualizer . 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} ;
0 commit comments