mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-10-31 02:44:17 +01:00 
			
		
		
		
	feat(sequence): actor creation and destruction #1838
This commit is contained in:
		| @@ -156,6 +156,81 @@ context('Sequence diagram', () => { | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render a sequence diagram with basic actor creation and destruction', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       sequenceDiagram | ||||
|       Alice ->> Bob: Hello Bob, how are you ? | ||||
|       Bob ->> Alice: Fine, thank you. And you? | ||||
|       create participant Polo | ||||
|       Alice ->> Polo: Hi Polo! | ||||
|       create actor Ola1 as Ola | ||||
|       Polo ->> Ola1: Hiii | ||||
|       Ola1 ->> Alice: Hi too | ||||
|       destroy Ola1 | ||||
|       Alice --x Ola1: Bye! | ||||
|       Alice ->> Bob: And now? | ||||
|       create participant Ola2 as Ola | ||||
|       Alice ->> Ola2: Hello again | ||||
|       destroy Alice | ||||
|       Alice --x Ola2: Bye for me! | ||||
|       destroy Bob | ||||
|       Ola2 --> Bob: The end | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
|   it('should render a sequence diagram with actor creation and destruction coupled with backgrounds, loops and notes', () => { | ||||
|     imgSnapshotTest( | ||||
|       ` | ||||
|       sequenceDiagram | ||||
| 			accTitle: test the accTitle | ||||
| 			accDescr: Test a description | ||||
|  | ||||
| 			participant Alice | ||||
|       participant Bob | ||||
| 			autonumber 10 10 | ||||
| 			rect rgb(200, 220, 100) | ||||
| 			rect rgb(200, 255, 200) | ||||
|  | ||||
| 			Alice ->> Bob: Hello Bob, how are you? | ||||
|       create participant John as John<br />Second Line | ||||
| 			Bob-->>John: How about you John? | ||||
| 			end | ||||
|  | ||||
| 			Bob--x Alice: I am good thanks! | ||||
| 			Bob-x John: I am good thanks! | ||||
| 			Note right of John: John thinks a long<br />long time, so long<br />that the text does<br />not fit on a row. | ||||
|  | ||||
| 			Bob-->Alice: Checking with John... | ||||
| 			Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit. | ||||
| 			Bob-x John: Hey John - we're still waiting to know<br />how you're doing | ||||
| 			Note over John:nowrap: John's trying hard not to break his train of thought. | ||||
|       destroy John | ||||
| 			Bob-x John: John! Cmon! | ||||
| 			Note over John: After a few more moments, John<br />finally snaps out of it. | ||||
| 			end | ||||
|  | ||||
| 			autonumber off | ||||
| 			alt either this | ||||
|       create actor Lola | ||||
| 			Alice->>+Lola: Yes | ||||
| 			Lola-->>-Alice: OK | ||||
| 			else or this | ||||
| 			autonumber | ||||
| 			Alice->>Lola: No | ||||
| 			else or this will happen | ||||
| 			Alice->Lola: Maybe | ||||
| 			end | ||||
| 			autonumber 200 | ||||
| 			par this happens in parallel | ||||
|       destroy Bob | ||||
| 			Alice -->> Bob: Parallel message 1 | ||||
| 			and | ||||
| 			Alice -->> Lola: Parallel message 2 | ||||
| 			end | ||||
|       ` | ||||
|     ); | ||||
|   }); | ||||
|   context('font settings', () => { | ||||
|     it('should render different note fonts when configured', () => { | ||||
|       imgSnapshotTest( | ||||
|   | ||||
| @@ -38,6 +38,8 @@ | ||||
| "box"															{ this.begin('LINE'); return 'box'; } | ||||
| "participant"                                                   { this.begin('ID'); return 'participant'; } | ||||
| "actor"                                                   		{ this.begin('ID'); return 'participant_actor'; } | ||||
| "create"                                                        return 'create'; | ||||
| "destroy"                                                       { this.begin('ID'); return 'destroy'; } | ||||
| <ID>[^\->:\n,;]+?([\-]*[^\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$)     { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; } | ||||
| <ALIAS>"as"                                                     { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; } | ||||
| <ALIAS>(?:)                                                     { this.popState(); this.popState(); return 'NEWLINE'; } | ||||
| @@ -138,6 +140,7 @@ directive | ||||
|  | ||||
| statement | ||||
| 	: participant_statement | ||||
| 	| 'create' participant_statement {$2.type='createParticipant'; $$=$2;} | ||||
| 	| 'box' restOfLine box_section end | ||||
| 	{ | ||||
| 		$3.unshift({type: 'boxStart', boxData:yy.parseBoxData($2) }); | ||||
| @@ -234,10 +237,11 @@ else_sections | ||||
| 	; | ||||
|  | ||||
| participant_statement | ||||
| 	: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;} | ||||
| 	| 'participant' actor 'NEWLINE' {$2.type='addParticipant';$$=$2;} | ||||
| 	| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.type='addActor';$2.description=yy.parseMessage($4); $$=$2;} | ||||
| 	| 'participant_actor' actor 'NEWLINE' {$2.type='addActor'; $$=$2;} | ||||
| 	: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;} | ||||
| 	| 'participant' actor 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant';$$=$2;} | ||||
| 	| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;} | ||||
| 	| 'participant_actor' actor 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant'; $$=$2;} | ||||
| 	| 'destroy' actor 'NEWLINE' {$2.type='destroyParticipant'; $$=$2;} | ||||
| 	; | ||||
|  | ||||
| note_statement | ||||
|   | ||||
| @@ -14,12 +14,16 @@ import { | ||||
|  | ||||
| let prevActor = undefined; | ||||
| let actors = {}; | ||||
| let createdActors = {}; | ||||
| let destroyedActors = {}; | ||||
| let boxes = []; | ||||
| let messages = []; | ||||
| const notes = []; | ||||
| let sequenceNumbersEnabled = false; | ||||
| let wrapEnabled; | ||||
| let currentBox = undefined; | ||||
| let lastCreated = undefined; | ||||
| let lastDestroyed = undefined; | ||||
|  | ||||
| export const parseDirective = function (statement, context, type) { | ||||
|   mermaidAPI.parseDirective(this, statement, context, type); | ||||
| @@ -165,6 +169,12 @@ export const getBoxes = function () { | ||||
| export const getActors = function () { | ||||
|   return actors; | ||||
| }; | ||||
| export const getCreatedActors = function () { | ||||
|   return createdActors; | ||||
| }; | ||||
| export const getDestroyedActors = function () { | ||||
|   return destroyedActors; | ||||
| }; | ||||
| export const getActor = function (id) { | ||||
|   return actors[id]; | ||||
| }; | ||||
| @@ -194,6 +204,8 @@ export const autoWrap = () => { | ||||
|  | ||||
| export const clear = function () { | ||||
|   actors = {}; | ||||
|   createdActors = {}; | ||||
|   destroyedActors = {}; | ||||
|   boxes = []; | ||||
|   messages = []; | ||||
|   sequenceNumbersEnabled = false; | ||||
| @@ -459,10 +471,21 @@ export const apply = function (param) { | ||||
|         }); | ||||
|         break; | ||||
|       case 'addParticipant': | ||||
|         addActor(param.actor, param.actor, param.description, 'participant'); | ||||
|         addActor(param.actor, param.actor, param.description, param.draw); | ||||
|         break; | ||||
|       case 'addActor': | ||||
|         addActor(param.actor, param.actor, param.description, 'actor'); | ||||
|       case 'createParticipant': | ||||
|         if (actors[param.actor]) { | ||||
|           throw new Error( | ||||
|             "It is not possible to have actors with the same id, even if one is destroyed before the next is created. Use 'AS' aliases to simulate the behavior" | ||||
|           ); | ||||
|         } | ||||
|         lastCreated = param.actor; | ||||
|         addActor(param.actor, param.actor, param.description, param.draw); | ||||
|         createdActors[param.actor] = messages.length; | ||||
|         break; | ||||
|       case 'destroyParticipant': | ||||
|         lastDestroyed = param.actor; | ||||
|         destroyedActors[param.actor] = messages.length; | ||||
|         break; | ||||
|       case 'activeStart': | ||||
|         addSignal(param.actor, undefined, undefined, param.signalType); | ||||
| @@ -486,6 +509,27 @@ export const apply = function (param) { | ||||
|         addDetails(param.actor, param.text); | ||||
|         break; | ||||
|       case 'addMessage': | ||||
|         if (lastCreated) { | ||||
|           if (param.to !== lastCreated) { | ||||
|             throw new Error( | ||||
|               'The created participant ' + | ||||
|                 lastCreated + | ||||
|                 ' does not have an associated creating message after its declaration. Please check the sequence diagram.' | ||||
|             ); | ||||
|           } else { | ||||
|             lastCreated = undefined; | ||||
|           } | ||||
|         } else if (lastDestroyed) { | ||||
|           if (param.to !== lastDestroyed && param.from !== lastDestroyed) { | ||||
|             throw new Error( | ||||
|               'The destroyed participant ' + | ||||
|                 lastDestroyed + | ||||
|                 ' does not have an associated destroying message after its declaration. Please check the sequence diagram.' | ||||
|             ); | ||||
|           } else { | ||||
|             lastDestroyed = undefined; | ||||
|           } | ||||
|         } | ||||
|         addSignal(param.from, param.to, param.msg, param.signalType); | ||||
|         break; | ||||
|       case 'boxStart': | ||||
| @@ -566,6 +610,8 @@ export default { | ||||
|   showSequenceNumbers, | ||||
|   getMessages, | ||||
|   getActors, | ||||
|   getCreatedActors, | ||||
|   getDestroyedActors, | ||||
|   getActor, | ||||
|   getActorKeys, | ||||
|   getActorProperty, | ||||
|   | ||||
| @@ -1404,6 +1404,62 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com | ||||
|     expect(boxes[0].actorKeys).toEqual(['a', 'b']); | ||||
|     expect(boxes[0].fill).toEqual('Aqua'); | ||||
|   }); | ||||
|  | ||||
|   it('should handle simple actor creation', async () => { | ||||
|     const str = ` | ||||
|   sequenceDiagram | ||||
|   participant a as Alice | ||||
|   a ->>b: Hello Bob? | ||||
|   create participant c | ||||
|   b-->>c: Hello c! | ||||
|   c ->> b: Hello b? | ||||
|   create actor d as Donald | ||||
|   a ->> d: Hello Donald? | ||||
|   `; | ||||
|     await mermaidAPI.parse(str); | ||||
|     const actors = diagram.db.getActors(); | ||||
|     const createdActors = diagram.db.getCreatedActors(); | ||||
|     expect(actors['c'].name).toEqual('c'); | ||||
|     expect(actors['c'].description).toEqual('c'); | ||||
|     expect(actors['c'].type).toEqual('participant'); | ||||
|     expect(createdActors['c']).toEqual(1); | ||||
|     expect(actors['d'].name).toEqual('d'); | ||||
|     expect(actors['d'].description).toEqual('Donald'); | ||||
|     expect(actors['d'].type).toEqual('actor'); | ||||
|     expect(createdActors['d']).toEqual(3); | ||||
|   }); | ||||
|   it('should handle simple actor destruction', async () => { | ||||
|     const str = ` | ||||
|   sequenceDiagram | ||||
|   participant a as Alice | ||||
|   a ->>b: Hello Bob? | ||||
|   destroy a | ||||
|   b-->>a: Hello Alice! | ||||
|   b ->> c: Where is Alice? | ||||
|   destroy c | ||||
|   b ->> c: Where are you? | ||||
|   `; | ||||
|     await mermaidAPI.parse(str); | ||||
|     const destroyedActors = diagram.db.getDestroyedActors(); | ||||
|     expect(destroyedActors['a']).toEqual(1); | ||||
|     expect(destroyedActors['c']).toEqual(3); | ||||
|   }); | ||||
|   it('should handle the creation and destruction of the same actor', async () => { | ||||
|     const str = ` | ||||
|   sequenceDiagram | ||||
|   a ->>b: Hello Bob? | ||||
|   create participant c | ||||
|   b ->>c: Hello c! | ||||
|   c ->> b: Hello b? | ||||
|   destroy c | ||||
|   b ->> c : Bye c ! | ||||
|   `; | ||||
|     await mermaidAPI.parse(str); | ||||
|     const createdActors = diagram.db.getCreatedActors(); | ||||
|     const destroyedActors = diagram.db.getDestroyedActors(); | ||||
|     expect(createdActors['c']).toEqual(1); | ||||
|     expect(destroyedActors['c']).toEqual(3); | ||||
|   }); | ||||
| }); | ||||
| describe('when checking the bounds in a sequenceDiagram', function () { | ||||
|   beforeAll(() => { | ||||
| @@ -1973,7 +2029,9 @@ participant Alice`; | ||||
|       expect(bounds.startx).toBe(0); | ||||
|       expect(bounds.starty).toBe(0); | ||||
|       expect(bounds.stopx).toBe(conf.width); | ||||
|       expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + conf.boxMargin); | ||||
|       expect(bounds.stopy).toBe( | ||||
|         models.lastActor().stopy + models.lastActor().height + conf.boxMargin | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -2025,7 +2083,7 @@ participant Alice | ||||
|     expect(bounds.startx).toBe(0); | ||||
|     expect(bounds.starty).toBe(0); | ||||
|     expect(bounds.stopy).toBe( | ||||
|       models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin | ||||
|       models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin | ||||
|     ); | ||||
|   }); | ||||
|   it('should handle one actor, when logLevel is 3 (dfg0)', async () => { | ||||
| @@ -2045,7 +2103,7 @@ participant Alice | ||||
|     expect(bounds.startx).toBe(0); | ||||
|     expect(bounds.starty).toBe(0); | ||||
|     expect(bounds.stopy).toBe( | ||||
|       models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin | ||||
|       models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin | ||||
|     ); | ||||
|   }); | ||||
|   it('should hide sequence numbers when autonumber is removed when autonumber is enabled', async () => { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| // @ts-nocheck TODO: fix file | ||||
| import { select, selectAll } from 'd3'; | ||||
| import svgDraw, { drawText, fixLifeLineHeights } from './svgDraw.js'; | ||||
| import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js'; | ||||
| import { log } from '../../logger.js'; | ||||
| import common from '../common/common.js'; | ||||
| import * as svgDrawCommon from '../common/svgDrawCommon'; | ||||
| @@ -478,29 +478,19 @@ const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Di | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export const drawActors = function ( | ||||
| const addActorRenderingData = function ( | ||||
|   diagram, | ||||
|   actors, | ||||
|   createdActors, | ||||
|   actorKeys, | ||||
|   verticalPos, | ||||
|   configuration, | ||||
|   messages, | ||||
|   isFooter | ||||
| ) { | ||||
|   if (configuration.hideUnusedParticipants === true) { | ||||
|     const newActors = new Set(); | ||||
|     messages.forEach((message) => { | ||||
|       newActors.add(message.from); | ||||
|       newActors.add(message.to); | ||||
|     }); | ||||
|     actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey)); | ||||
|   } | ||||
|  | ||||
|   // Draw the actors | ||||
|   let prevWidth = 0; | ||||
|   let prevMargin = 0; | ||||
|   let maxHeight = 0; | ||||
|   let prevBox = undefined; | ||||
|   let maxHeight = 0; | ||||
|  | ||||
|   for (const actorKey of actorKeys) { | ||||
|     const actor = actors[actorKey]; | ||||
| @@ -528,12 +518,16 @@ export const drawActors = function ( | ||||
|     actor.height = common.getMax(actor.height || conf.height, conf.height); | ||||
|     actor.margin = actor.margin || conf.actorMargin; | ||||
|  | ||||
|     actor.x = prevWidth + prevMargin; | ||||
|     actor.y = bounds.getVerticalPos(); | ||||
|     maxHeight = common.getMax(maxHeight, actor.height); | ||||
|  | ||||
|     // if the actor is created by a message, widen margin | ||||
|     if (createdActors[actor.name]) { | ||||
|       prevMargin += actor.width / 2; | ||||
|     } | ||||
|  | ||||
|     actor.x = prevWidth + prevMargin; | ||||
|     actor.starty = bounds.getVerticalPos(); | ||||
|  | ||||
|     // Draw the box with the attached line | ||||
|     const height = svgDraw.drawActor(diagram, actor, conf, isFooter); | ||||
|     maxHeight = common.getMax(maxHeight, height); | ||||
|     bounds.insert(actor.x, verticalPos, actor.x + actor.width, actor.height); | ||||
|  | ||||
|     prevWidth += actor.width + prevMargin; | ||||
| @@ -554,6 +548,28 @@ export const drawActors = function ( | ||||
|   bounds.bumpVerticalPos(maxHeight); | ||||
| }; | ||||
|  | ||||
| export const drawActors = function (diagram, actors, actorKeys, isFooter) { | ||||
|   if (!isFooter) { | ||||
|     for (const actorKey of actorKeys) { | ||||
|       const actor = actors[actorKey]; | ||||
|       // Draw the box with the attached line | ||||
|       svgDraw.drawActor(diagram, actor, conf, false); | ||||
|     } | ||||
|   } else { | ||||
|     let maxHeight = 0; | ||||
|     bounds.bumpVerticalPos(conf.boxMargin * 2); | ||||
|     for (const actorKey of actorKeys) { | ||||
|       const actor = actors[actorKey]; | ||||
|       if (!actor.stopy) { | ||||
|         actor.stopy = bounds.getVerticalPos(); | ||||
|       } | ||||
|       const height = svgDraw.drawActor(diagram, actor, conf, true); | ||||
|       maxHeight = common.getMax(maxHeight, height); | ||||
|     } | ||||
|     bounds.bumpVerticalPos(maxHeight + conf.boxMargin); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export const drawActorsPopup = function (diagram, actors, actorKeys, doc) { | ||||
|   let maxHeight = 0; | ||||
|   let maxWidth = 0; | ||||
| @@ -633,6 +649,95 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop | ||||
|   bounds.bumpVerticalPos(heightAdjust); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Adjust the msgModel and the actor for the rendering in case the latter is created or destroyed by the msg | ||||
|  * @param msg - the potentially creating or destroying message | ||||
|  * @param msgModel - the model associated with the message | ||||
|  * @param lineStartY - the y position of the message line | ||||
|  * @param index - the index of the current actor under consideration | ||||
|  * @param actors - the array of all actors | ||||
|  * @param createdActors - the array of actors created in the diagram | ||||
|  * @param destroyedActors - the array of actors destroyed in the diagram | ||||
|  */ | ||||
| function adjustCreatedDestroyedData( | ||||
|   msg, | ||||
|   msgModel, | ||||
|   lineStartY, | ||||
|   index, | ||||
|   actors, | ||||
|   createdActors, | ||||
|   destroyedActors | ||||
| ) { | ||||
|   function receiverAdjustment(actor, adjustment) { | ||||
|     if (actor.x < actors[msg.from].x) { | ||||
|       bounds.insert( | ||||
|         msgModel.stopx - adjustment, | ||||
|         msgModel.starty, | ||||
|         msgModel.startx, | ||||
|         msgModel.stopy + actor.height / 2 + conf.noteMargin | ||||
|       ); | ||||
|       msgModel.stopx = msgModel.stopx + adjustment; | ||||
|     } else { | ||||
|       bounds.insert( | ||||
|         msgModel.startx, | ||||
|         msgModel.starty, | ||||
|         msgModel.stopx + adjustment, | ||||
|         msgModel.stopy + actor.height / 2 + conf.noteMargin | ||||
|       ); | ||||
|       msgModel.stopx = msgModel.stopx - adjustment; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function senderAdjustment(actor, adjustment) { | ||||
|     if (actor.x < actors[msg.to].x) { | ||||
|       bounds.insert( | ||||
|         msgModel.startx - adjustment, | ||||
|         msgModel.starty, | ||||
|         msgModel.stopx, | ||||
|         msgModel.stopy + actor.height / 2 + conf.noteMargin | ||||
|       ); | ||||
|       msgModel.startx = msgModel.startx + adjustment; | ||||
|     } else { | ||||
|       bounds.insert( | ||||
|         msgModel.stopx, | ||||
|         msgModel.starty, | ||||
|         msgModel.startx + adjustment, | ||||
|         msgModel.stopy + actor.height / 2 + conf.noteMargin | ||||
|       ); | ||||
|       msgModel.startx = msgModel.startx - adjustment; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // if it is a create message | ||||
|   if (createdActors[msg.to] == index) { | ||||
|     const actor = actors[msg.to]; | ||||
|     const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3; | ||||
|     receiverAdjustment(actor, adjustment); | ||||
|     actor.starty = lineStartY - actor.height / 2; | ||||
|     bounds.bumpVerticalPos(actor.height / 2); | ||||
|   } | ||||
|   // if it is a destroy sender message | ||||
|   else if (destroyedActors[msg.from] == index) { | ||||
|     const actor = actors[msg.from]; | ||||
|     if (conf.mirrorActors) { | ||||
|       const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 : actor.width / 2; | ||||
|       senderAdjustment(actor, adjustment); | ||||
|     } | ||||
|     actor.stopy = lineStartY - actor.height / 2; | ||||
|     bounds.bumpVerticalPos(actor.height / 2); | ||||
|   } | ||||
|   // if it is a destroy receiver message | ||||
|   else if (destroyedActors[msg.to] == index) { | ||||
|     const actor = actors[msg.to]; | ||||
|     if (conf.mirrorActors) { | ||||
|       const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3; | ||||
|       receiverAdjustment(actor, adjustment); | ||||
|     } | ||||
|     actor.stopy = lineStartY - actor.height / 2; | ||||
|     bounds.bumpVerticalPos(actor.height / 2); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Draws a sequenceDiagram in the tag with id: id based on the graph definition in text. | ||||
|  * | ||||
| @@ -666,8 +771,10 @@ export const draw = function (_text: string, id: string, _version: string, diagO | ||||
|  | ||||
|   // Fetch data from the parsing | ||||
|   const actors = diagObj.db.getActors(); | ||||
|   const createdActors = diagObj.db.getCreatedActors(); | ||||
|   const destroyedActors = diagObj.db.getDestroyedActors(); | ||||
|   const boxes = diagObj.db.getBoxes(); | ||||
|   const actorKeys = diagObj.db.getActorKeys(); | ||||
|   let actorKeys = diagObj.db.getActorKeys(); | ||||
|   const messages = diagObj.db.getMessages(); | ||||
|   const title = diagObj.db.getDiagramTitle(); | ||||
|   const hasBoxes = diagObj.db.hasAtLeastOneBox(); | ||||
| @@ -686,7 +793,16 @@ export const draw = function (_text: string, id: string, _version: string, diagO | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   drawActors(diagram, actors, actorKeys, 0, conf, messages, false); | ||||
|   if (conf.hideUnusedParticipants === true) { | ||||
|     const newActors = new Set(); | ||||
|     messages.forEach((message) => { | ||||
|       newActors.add(message.from); | ||||
|       newActors.add(message.to); | ||||
|     }); | ||||
|     actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey)); | ||||
|   } | ||||
|  | ||||
|   addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false); | ||||
|   const loopWidths = calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj); | ||||
|  | ||||
|   // The arrow head definition is attached to the svg once | ||||
| @@ -720,7 +836,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO | ||||
|   let sequenceIndex = 1; | ||||
|   let sequenceIndexStep = 1; | ||||
|   const messagesToDraw = []; | ||||
|   messages.forEach(function (msg) { | ||||
|   const backgrounds = []; | ||||
|   messages.forEach(function (msg, index) { | ||||
|     let loopModel, noteModel, msgModel; | ||||
|  | ||||
|     switch (msg.type) { | ||||
| @@ -757,7 +874,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO | ||||
|         break; | ||||
|       case diagObj.db.LINETYPE.RECT_END: | ||||
|         loopModel = bounds.endLoop(); | ||||
|         svgDraw.drawBackgroundRect(diagram, loopModel); | ||||
|         backgrounds.push(loopModel); | ||||
|         bounds.models.addLoop(loopModel); | ||||
|         bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos()); | ||||
|         break; | ||||
| @@ -876,13 +993,20 @@ export const draw = function (_text: string, id: string, _version: string, diagO | ||||
|         break; | ||||
|       default: | ||||
|         try { | ||||
|           // lastMsg = msg | ||||
|           bounds.resetVerticalPos(); | ||||
|           msgModel = msg.msgModel; | ||||
|           msgModel.starty = bounds.getVerticalPos(); | ||||
|           msgModel.sequenceIndex = sequenceIndex; | ||||
|           msgModel.sequenceVisible = diagObj.db.showSequenceNumbers(); | ||||
|           const lineStartY = boundMessage(diagram, msgModel); | ||||
|           adjustCreatedDestroyedData( | ||||
|             msg, | ||||
|             msgModel, | ||||
|             lineStartY, | ||||
|             index, | ||||
|             actors, | ||||
|             createdActors, | ||||
|             destroyedActors | ||||
|           ); | ||||
|           messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY }); | ||||
|           bounds.models.addMessage(msgModel); | ||||
|         } catch (e) { | ||||
| @@ -907,15 +1031,16 @@ export const draw = function (_text: string, id: string, _version: string, diagO | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj)); | ||||
|   log.debug('createdActors', createdActors); | ||||
|   log.debug('destroyedActors', destroyedActors); | ||||
|  | ||||
|   drawActors(diagram, actors, actorKeys, false); | ||||
|   messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj)); | ||||
|   if (conf.mirrorActors) { | ||||
|     // Draw actors below diagram | ||||
|     bounds.bumpVerticalPos(conf.boxMargin * 2); | ||||
|     drawActors(diagram, actors, actorKeys, bounds.getVerticalPos(), conf, messages, true); | ||||
|     bounds.bumpVerticalPos(conf.boxMargin); | ||||
|     fixLifeLineHeights(diagram, bounds.getVerticalPos()); | ||||
|     drawActors(diagram, actors, actorKeys, true); | ||||
|   } | ||||
|   backgrounds.forEach((e) => svgDraw.drawBackgroundRect(diagram, e)); | ||||
|   fixLifeLineHeights(diagram, actors, actorKeys, conf); | ||||
|  | ||||
|   bounds.models.boxes.forEach(function (box) { | ||||
|     box.height = bounds.getVerticalPos() - box.y; | ||||
| @@ -937,11 +1062,6 @@ export const draw = function (_text: string, id: string, _version: string, diagO | ||||
|  | ||||
|   const { bounds: box } = bounds.getBounds(); | ||||
|  | ||||
|   // Adjust line height of actor lines now that the height of the diagram is known | ||||
|   log.debug('For line height fix Querying: #' + id + ' .actor-line'); | ||||
|   const actorLines = selectAll('#' + id + ' .actor-line'); | ||||
|   actorLines.attr('y2', box.stopy); | ||||
|  | ||||
|   // Make sure the height of the diagram supports long menus. | ||||
|   let boxHeight = box.stopy - box.starty; | ||||
|   if (boxHeight < requiredBoxSize.maxHeight) { | ||||
|   | ||||
| @@ -4,6 +4,8 @@ import { addFunction } from '../../interactionDb.js'; | ||||
| import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js'; | ||||
| import { sanitizeUrl } from '@braintree/sanitize-url'; | ||||
|  | ||||
| export const ACTOR_TYPE_WIDTH = 18 * 2; | ||||
|  | ||||
| export const drawRect = function (elem, rectData) { | ||||
|   return svgDrawCommon.drawRect(elem, rectData); | ||||
| }; | ||||
| @@ -294,14 +296,19 @@ export const drawLabel = function (elem, txtObject) { | ||||
|  | ||||
| let actorCnt = -1; | ||||
|  | ||||
| export const fixLifeLineHeights = (diagram, bounds) => { | ||||
|   if (!diagram.selectAll) { | ||||
| export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => { | ||||
|   if (!diagram.select) { | ||||
|     return; | ||||
|   } | ||||
|   diagram | ||||
|     .selectAll('.actor-line') | ||||
|     .attr('class', '200') | ||||
|     .attr('y2', bounds - 55); | ||||
|   actorKeys.forEach((actorKey) => { | ||||
|     const actor = actors[actorKey]; | ||||
|     const actorDOM = diagram.select('#actor' + actor.actorCnt); | ||||
|     if (!conf.mirrorActors && actor.stopy) { | ||||
|       actorDOM.attr('y2', actor.stopy + actor.height / 2); | ||||
|     } else if (conf.mirrorActors) { | ||||
|       actorDOM.attr('y2', actor.stopy); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** | ||||
| @@ -313,10 +320,11 @@ export const fixLifeLineHeights = (diagram, bounds) => { | ||||
|  * @param {boolean} isFooter - If the actor is the footer one | ||||
|  */ | ||||
| const drawActorTypeParticipant = function (elem, actor, conf, isFooter) { | ||||
|   const actorY = isFooter ? actor.stopy : actor.starty; | ||||
|   const center = actor.x + actor.width / 2; | ||||
|   const centerY = actor.y + 5; | ||||
|   const centerY = actorY + 5; | ||||
|  | ||||
|   const boxpluslineGroup = elem.append('g'); | ||||
|   const boxpluslineGroup = elem.append('g').lower(); | ||||
|   var g = boxpluslineGroup; | ||||
|  | ||||
|   if (!isFooter) { | ||||
| @@ -328,6 +336,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) { | ||||
|       .attr('x2', center) | ||||
|       .attr('y2', 2000) | ||||
|       .attr('class', 'actor-line') | ||||
|       .attr('class', '200') | ||||
|       .attr('stroke-width', '0.5px') | ||||
|       .attr('stroke', '#999'); | ||||
|  | ||||
| @@ -348,7 +357,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) { | ||||
|     rect.fill = '#eaeaea'; | ||||
|   } | ||||
|   rect.x = actor.x; | ||||
|   rect.y = actor.y; | ||||
|   rect.y = actorY; | ||||
|   rect.width = actor.width; | ||||
|   rect.height = actor.height; | ||||
|   rect.class = cssclass; | ||||
| @@ -388,8 +397,11 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) { | ||||
| }; | ||||
|  | ||||
| const drawActorTypeActor = function (elem, actor, conf, isFooter) { | ||||
|   const actorY = isFooter ? actor.stopy : actor.starty; | ||||
|   const center = actor.x + actor.width / 2; | ||||
|   const centerY = actor.y + 80; | ||||
|   const centerY = actorY + 80; | ||||
|  | ||||
|   elem.lower(); | ||||
|  | ||||
|   if (!isFooter) { | ||||
|     actorCnt++; | ||||
| @@ -401,15 +413,18 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) { | ||||
|       .attr('x2', center) | ||||
|       .attr('y2', 2000) | ||||
|       .attr('class', 'actor-line') | ||||
|       .attr('class', '200') | ||||
|       .attr('stroke-width', '0.5px') | ||||
|       .attr('stroke', '#999'); | ||||
|  | ||||
|     actor.actorCnt = actorCnt; | ||||
|   } | ||||
|   const actElem = elem.append('g'); | ||||
|   actElem.attr('class', 'actor-man'); | ||||
|  | ||||
|   const rect = svgDrawCommon.getNoteRect(); | ||||
|   rect.x = actor.x; | ||||
|   rect.y = actor.y; | ||||
|   rect.y = actorY; | ||||
|   rect.fill = '#eaeaea'; | ||||
|   rect.width = actor.width; | ||||
|   rect.height = actor.height; | ||||
| @@ -421,33 +436,33 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) { | ||||
|     .append('line') | ||||
|     .attr('id', 'actor-man-torso' + actorCnt) | ||||
|     .attr('x1', center) | ||||
|     .attr('y1', actor.y + 25) | ||||
|     .attr('y1', actorY + 25) | ||||
|     .attr('x2', center) | ||||
|     .attr('y2', actor.y + 45); | ||||
|     .attr('y2', actorY + 45); | ||||
|  | ||||
|   actElem | ||||
|     .append('line') | ||||
|     .attr('id', 'actor-man-arms' + actorCnt) | ||||
|     .attr('x1', center - 18) | ||||
|     .attr('y1', actor.y + 33) | ||||
|     .attr('x2', center + 18) | ||||
|     .attr('y2', actor.y + 33); | ||||
|     .attr('x1', center - ACTOR_TYPE_WIDTH / 2) | ||||
|     .attr('y1', actorY + 33) | ||||
|     .attr('x2', center + ACTOR_TYPE_WIDTH / 2) | ||||
|     .attr('y2', actorY + 33); | ||||
|   actElem | ||||
|     .append('line') | ||||
|     .attr('x1', center - 18) | ||||
|     .attr('y1', actor.y + 60) | ||||
|     .attr('x1', center - ACTOR_TYPE_WIDTH / 2) | ||||
|     .attr('y1', actorY + 60) | ||||
|     .attr('x2', center) | ||||
|     .attr('y2', actor.y + 45); | ||||
|     .attr('y2', actorY + 45); | ||||
|   actElem | ||||
|     .append('line') | ||||
|     .attr('x1', center) | ||||
|     .attr('y1', actor.y + 45) | ||||
|     .attr('x2', center + 16) | ||||
|     .attr('y2', actor.y + 60); | ||||
|     .attr('y1', actorY + 45) | ||||
|     .attr('x2', center + ACTOR_TYPE_WIDTH / 2 - 2) | ||||
|     .attr('y2', actorY + 60); | ||||
|  | ||||
|   const circle = actElem.append('circle'); | ||||
|   circle.attr('cx', actor.x + actor.width / 2); | ||||
|   circle.attr('cy', actor.y + 10); | ||||
|   circle.attr('cy', actorY + 10); | ||||
|   circle.attr('r', 15); | ||||
|   circle.attr('width', actor.width); | ||||
|   circle.attr('height', actor.height); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Laura Valentine Tscharner
					Laura Valentine Tscharner