@djangocfg/widget-diagram 0.1.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/LICENSE +21 -0
- package/README.md +84 -0
- package/package.json +77 -0
- package/src/FloatingToolbar/FloatingToolbar.css +5 -0
- package/src/FloatingToolbar/actions/CopyAction.tsx +31 -0
- package/src/FloatingToolbar/actions/DownloadAction.tsx +51 -0
- package/src/FloatingToolbar/actions/ExpandAction.tsx +33 -0
- package/src/FloatingToolbar/actions/FullscreenAction.tsx +38 -0
- package/src/FloatingToolbar/actions/index.ts +4 -0
- package/src/FloatingToolbar/hooks/useScrollIsolation.ts +62 -0
- package/src/FloatingToolbar/index.tsx +184 -0
- package/src/Mermaid.client.tsx +97 -0
- package/src/builders/FlowDiagram/FlowDiagram.ts +96 -0
- package/src/builders/FlowDiagram/functions/getEdges.ts +50 -0
- package/src/builders/FlowDiagram/functions/getNodes.ts +43 -0
- package/src/builders/FlowDiagram/functions/getStyles.ts +90 -0
- package/src/builders/FlowDiagram/functions/index.ts +8 -0
- package/src/builders/FlowDiagram/index.ts +16 -0
- package/src/builders/FlowDiagram/types.ts +130 -0
- package/src/builders/JourneyDiagram/JourneyDiagram.ts +88 -0
- package/src/builders/JourneyDiagram/index.ts +12 -0
- package/src/builders/JourneyDiagram/types.ts +48 -0
- package/src/builders/SequenceDiagram/SequenceDiagram.ts +158 -0
- package/src/builders/SequenceDiagram/functions/getActivations.ts +30 -0
- package/src/builders/SequenceDiagram/functions/getBlocks.ts +112 -0
- package/src/builders/SequenceDiagram/functions/getMessages.ts +85 -0
- package/src/builders/SequenceDiagram/functions/getNotes.ts +94 -0
- package/src/builders/SequenceDiagram/functions/index.ts +16 -0
- package/src/builders/SequenceDiagram/index.ts +18 -0
- package/src/builders/SequenceDiagram/types.ts +192 -0
- package/src/builders/core/DiagramStore.ts +138 -0
- package/src/builders/core/index.ts +8 -0
- package/src/builders/core/sanitize.ts +83 -0
- package/src/builders/core/theme.ts +42 -0
- package/src/builders/core/types.ts +183 -0
- package/src/builders/index.ts +96 -0
- package/src/components/MermaidCodeViewer.tsx +95 -0
- package/src/components/MermaidErrorPanel.tsx +31 -0
- package/src/components/MermaidFullscreenModal.tsx +201 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useMermaidCleanup.ts +70 -0
- package/src/hooks/useMermaidFullscreen.ts +46 -0
- package/src/hooks/useMermaidRenderer.ts +329 -0
- package/src/hooks/useMermaidValidation.ts +97 -0
- package/src/index.tsx +79 -0
- package/src/lazy.tsx +40 -0
- package/src/mermaid.stories.tsx +217 -0
- package/src/types.ts +28 -0
- package/src/utils/mermaid-helpers.ts +157 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SequenceDiagram Builder
|
|
3
|
+
* Declarative API for building Mermaid sequence diagrams
|
|
4
|
+
* @module Mermaid/builders/SequenceDiagram/SequenceDiagram
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { DiagramStore } from '../core/DiagramStore';
|
|
8
|
+
import type { ParticipantType } from '../core/types';
|
|
9
|
+
import { sanitizeLabel } from '../core/sanitize';
|
|
10
|
+
import { createMessageBuilder } from './functions/getMessages';
|
|
11
|
+
import { createNoteBuilder } from './functions/getNotes';
|
|
12
|
+
import { createActivationBuilder } from './functions/getActivations';
|
|
13
|
+
import {
|
|
14
|
+
createLoopBuilder,
|
|
15
|
+
createAltBuilder,
|
|
16
|
+
createParBuilder,
|
|
17
|
+
createRectBuilder,
|
|
18
|
+
createCriticalBuilder,
|
|
19
|
+
createBreakBuilder,
|
|
20
|
+
} from './functions/getBlocks';
|
|
21
|
+
import type { ParticipantsObject, SequenceDiagramOptions, SequenceDiagramBuilder, DynamicArrowType } from './types';
|
|
22
|
+
|
|
23
|
+
const DEFAULT_OPTIONS: Required<SequenceDiagramOptions> = {
|
|
24
|
+
autoNumber: false,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Normalize participant definition to { type, alias } format
|
|
29
|
+
*/
|
|
30
|
+
function normalizeParticipant(
|
|
31
|
+
value: ParticipantType | { type: ParticipantType; alias?: string },
|
|
32
|
+
): { type: ParticipantType; alias?: string } {
|
|
33
|
+
if (typeof value === 'string') {
|
|
34
|
+
return { type: value };
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a SequenceDiagram builder
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const { d, note, loop, rect } = SequenceDiagram({
|
|
45
|
+
* Alice: 'participant',
|
|
46
|
+
* Bob: 'actor',
|
|
47
|
+
* Charlie: { type: 'participant', alias: 'C' },
|
|
48
|
+
* }, { autoNumber: true });
|
|
49
|
+
*
|
|
50
|
+
* rect('rgb(200, 220, 255)', () => {
|
|
51
|
+
* d.Alice.sync.Bob.msg('Hello!');
|
|
52
|
+
* d.Bob.syncReply.Alice.msg('Hi there!');
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* loop('Every minute', () => {
|
|
56
|
+
* d.Alice.async.Charlie.msg('Ping');
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* note.over.Alice.Bob.msg('Handshake complete');
|
|
60
|
+
*
|
|
61
|
+
* console.log(toString());
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @param participants - Participant definitions
|
|
65
|
+
* @param options - Diagram options
|
|
66
|
+
* @returns SequenceDiagram builder instance
|
|
67
|
+
*/
|
|
68
|
+
export function SequenceDiagram<P extends string>(
|
|
69
|
+
participants: ParticipantsObject<P>,
|
|
70
|
+
options: SequenceDiagramOptions = {},
|
|
71
|
+
): SequenceDiagramBuilder<P> {
|
|
72
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
73
|
+
const store = new DiagramStore('sequenceDiagram');
|
|
74
|
+
|
|
75
|
+
// Add autonumber if requested
|
|
76
|
+
if (opts.autoNumber) {
|
|
77
|
+
store.add('autonumber');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Get participant keys
|
|
81
|
+
const participantKeys = Object.keys(participants) as P[];
|
|
82
|
+
|
|
83
|
+
// Declare participants
|
|
84
|
+
store.addBlank();
|
|
85
|
+
participantKeys.forEach((key) => {
|
|
86
|
+
const def = normalizeParticipant(participants[key]);
|
|
87
|
+
if (def.alias) {
|
|
88
|
+
store.add(`${def.type} ${key} as ${sanitizeLabel(def.alias)}`);
|
|
89
|
+
} else {
|
|
90
|
+
store.add(`${def.type} ${key}`);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
store.addBlank();
|
|
94
|
+
|
|
95
|
+
// Create builders
|
|
96
|
+
const messageBuilder = createMessageBuilder<P>(store, participantKeys);
|
|
97
|
+
const noteBuilder = createNoteBuilder<P>(store, participantKeys);
|
|
98
|
+
const activationBuilder = createActivationBuilder<P>(store, participantKeys);
|
|
99
|
+
|
|
100
|
+
// Arrow type to Mermaid syntax mapping
|
|
101
|
+
const arrowMap: Record<DynamicArrowType, string> = {
|
|
102
|
+
sync: '->>',
|
|
103
|
+
syncReply: '-->>',
|
|
104
|
+
async: '-)',
|
|
105
|
+
asyncReply: '--)',
|
|
106
|
+
solid: '->',
|
|
107
|
+
dotted: '-->',
|
|
108
|
+
cross: '-x',
|
|
109
|
+
crossDotted: '--x',
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
d: messageBuilder,
|
|
114
|
+
note: noteBuilder,
|
|
115
|
+
activate: activationBuilder,
|
|
116
|
+
|
|
117
|
+
// Dynamic access methods
|
|
118
|
+
message(from: string, to: string, text: string, arrow: DynamicArrowType = 'sync') {
|
|
119
|
+
const arrowSyntax = arrowMap[arrow] || '->>';
|
|
120
|
+
store.add(`${from}${arrowSyntax}${to}: ${sanitizeLabel(text)}`);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
noteOver(participant: string, text: string) {
|
|
124
|
+
store.add(`Note over ${participant}: ${sanitizeLabel(text)}`);
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
noteOverSpan(participant1: string, participant2: string, text: string) {
|
|
128
|
+
store.add(`Note over ${participant1},${participant2}: ${sanitizeLabel(text)}`);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
noteLeft(participant: string, text: string) {
|
|
132
|
+
store.add(`Note left of ${participant}: ${sanitizeLabel(text)}`);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
noteRight(participant: string, text: string) {
|
|
136
|
+
store.add(`Note right of ${participant}: ${sanitizeLabel(text)}`);
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
loop: createLoopBuilder(store),
|
|
140
|
+
alt: createAltBuilder(store),
|
|
141
|
+
par: createParBuilder(store),
|
|
142
|
+
rect: createRectBuilder(store),
|
|
143
|
+
critical: createCriticalBuilder(store),
|
|
144
|
+
break: createBreakBuilder(store),
|
|
145
|
+
|
|
146
|
+
comment(text: string) {
|
|
147
|
+
store.addComment(text);
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
blank() {
|
|
151
|
+
store.addBlank();
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
toString() {
|
|
155
|
+
return store.toString();
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activation builder functions for SequenceDiagram
|
|
3
|
+
* @module Mermaid/builders/SequenceDiagram/functions/getActivations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DiagramStore } from '../../core/DiagramStore';
|
|
7
|
+
import type { ActivationBuilder } from '../types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create the activation builder for all participants
|
|
11
|
+
*/
|
|
12
|
+
export function createActivationBuilder<P extends string>(
|
|
13
|
+
store: DiagramStore,
|
|
14
|
+
participants: readonly P[],
|
|
15
|
+
): ActivationBuilder<P> {
|
|
16
|
+
const builder = {} as ActivationBuilder<P>;
|
|
17
|
+
|
|
18
|
+
participants.forEach((p) => {
|
|
19
|
+
builder[p] = {
|
|
20
|
+
activate() {
|
|
21
|
+
store.add(`activate ${p}`);
|
|
22
|
+
},
|
|
23
|
+
deactivate() {
|
|
24
|
+
store.add(`deactivate ${p}`);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return builder;
|
|
30
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block builder functions for SequenceDiagram (loop, alt, par, rect, etc.)
|
|
3
|
+
* @module Mermaid/builders/SequenceDiagram/functions/getBlocks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DiagramStore } from '../../core/DiagramStore';
|
|
7
|
+
import { sanitizeLabel } from '../../core/sanitize';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create a loop block builder
|
|
11
|
+
*/
|
|
12
|
+
export function createLoopBuilder(store: DiagramStore) {
|
|
13
|
+
return (label: string, fn: () => void) => {
|
|
14
|
+
store.add(`loop ${sanitizeLabel(label)}`);
|
|
15
|
+
store.indent();
|
|
16
|
+
fn();
|
|
17
|
+
store.dedent();
|
|
18
|
+
store.add('end');
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create an alt (alternative/conditional) block builder
|
|
24
|
+
*/
|
|
25
|
+
export function createAltBuilder(store: DiagramStore) {
|
|
26
|
+
return (label: string, fn: () => void) => {
|
|
27
|
+
store.add(`alt ${sanitizeLabel(label)}`);
|
|
28
|
+
store.indent();
|
|
29
|
+
fn();
|
|
30
|
+
store.dedent();
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
else(elseLabel: string, elseFn: () => void) {
|
|
34
|
+
store.add(`else ${sanitizeLabel(elseLabel)}`);
|
|
35
|
+
store.indent();
|
|
36
|
+
elseFn();
|
|
37
|
+
store.dedent();
|
|
38
|
+
store.add('end');
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create a parallel (par) block builder
|
|
46
|
+
*/
|
|
47
|
+
export function createParBuilder(store: DiagramStore) {
|
|
48
|
+
return (label: string, fn: () => void) => {
|
|
49
|
+
store.add(`par ${sanitizeLabel(label)}`);
|
|
50
|
+
store.indent();
|
|
51
|
+
fn();
|
|
52
|
+
store.dedent();
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
and(andLabel: string, andFn: () => void) {
|
|
56
|
+
store.add(`and ${sanitizeLabel(andLabel)}`);
|
|
57
|
+
store.indent();
|
|
58
|
+
andFn();
|
|
59
|
+
store.dedent();
|
|
60
|
+
store.add('end');
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create a rect (colored box) block builder
|
|
68
|
+
*/
|
|
69
|
+
export function createRectBuilder(store: DiagramStore) {
|
|
70
|
+
return (color: string, fn: () => void) => {
|
|
71
|
+
store.add(`rect ${color}`);
|
|
72
|
+
store.indent();
|
|
73
|
+
fn();
|
|
74
|
+
store.dedent();
|
|
75
|
+
store.add('end');
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a critical region block builder
|
|
81
|
+
*/
|
|
82
|
+
export function createCriticalBuilder(store: DiagramStore) {
|
|
83
|
+
return (label: string, fn: () => void) => {
|
|
84
|
+
store.add(`critical ${sanitizeLabel(label)}`);
|
|
85
|
+
store.indent();
|
|
86
|
+
fn();
|
|
87
|
+
store.dedent();
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
option(optionLabel: string, optionFn: () => void) {
|
|
91
|
+
store.add(`option ${sanitizeLabel(optionLabel)}`);
|
|
92
|
+
store.indent();
|
|
93
|
+
optionFn();
|
|
94
|
+
store.dedent();
|
|
95
|
+
store.add('end');
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create a break block builder
|
|
103
|
+
*/
|
|
104
|
+
export function createBreakBuilder(store: DiagramStore) {
|
|
105
|
+
return (label: string, fn: () => void) => {
|
|
106
|
+
store.add(`break ${sanitizeLabel(label)}`);
|
|
107
|
+
store.indent();
|
|
108
|
+
fn();
|
|
109
|
+
store.dedent();
|
|
110
|
+
store.add('end');
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message builder functions for SequenceDiagram
|
|
3
|
+
* @module Mermaid/builders/SequenceDiagram/functions/getMessages
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DiagramStore } from '../../core/DiagramStore';
|
|
7
|
+
import { MESSAGE_ARROWS } from '../../core/types';
|
|
8
|
+
import { sanitizeLabel } from '../../core/sanitize';
|
|
9
|
+
import type { MessageBuilder, MessageAction, MessageTarget, MessageArrows } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create message actions for a specific from -> arrow -> to combination
|
|
13
|
+
*/
|
|
14
|
+
function createMessageAction(
|
|
15
|
+
store: DiagramStore,
|
|
16
|
+
from: string,
|
|
17
|
+
arrow: string,
|
|
18
|
+
to: string,
|
|
19
|
+
): MessageAction {
|
|
20
|
+
return {
|
|
21
|
+
msg(text: string) {
|
|
22
|
+
store.add(`${from}${arrow}${to}: ${sanitizeLabel(text)}`);
|
|
23
|
+
},
|
|
24
|
+
activate(text: string) {
|
|
25
|
+
store.add(`${from}${arrow}+${to}: ${sanitizeLabel(text)}`);
|
|
26
|
+
},
|
|
27
|
+
deactivate(text: string) {
|
|
28
|
+
store.add(`${from}${arrow}-${to}: ${sanitizeLabel(text)}`);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create message target for a specific from -> arrow combination
|
|
35
|
+
*/
|
|
36
|
+
function createMessageTarget<P extends string>(
|
|
37
|
+
store: DiagramStore,
|
|
38
|
+
from: string,
|
|
39
|
+
arrow: string,
|
|
40
|
+
participants: readonly P[],
|
|
41
|
+
): MessageTarget<P> {
|
|
42
|
+
const target = {} as MessageTarget<P>;
|
|
43
|
+
|
|
44
|
+
participants.forEach((to) => {
|
|
45
|
+
target[to] = createMessageAction(store, from, arrow, to);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return target;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create arrow selector for a specific from participant
|
|
53
|
+
*/
|
|
54
|
+
function createMessageArrows<P extends string>(
|
|
55
|
+
store: DiagramStore,
|
|
56
|
+
from: P,
|
|
57
|
+
participants: readonly P[],
|
|
58
|
+
): MessageArrows<P> {
|
|
59
|
+
return {
|
|
60
|
+
sync: createMessageTarget(store, from, MESSAGE_ARROWS.sync, participants),
|
|
61
|
+
syncReply: createMessageTarget(store, from, MESSAGE_ARROWS.syncReply, participants),
|
|
62
|
+
async: createMessageTarget(store, from, MESSAGE_ARROWS.async, participants),
|
|
63
|
+
asyncReply: createMessageTarget(store, from, MESSAGE_ARROWS.asyncReply, participants),
|
|
64
|
+
solid: createMessageTarget(store, from, MESSAGE_ARROWS.solid, participants),
|
|
65
|
+
dotted: createMessageTarget(store, from, MESSAGE_ARROWS.dotted, participants),
|
|
66
|
+
cross: createMessageTarget(store, from, MESSAGE_ARROWS.cross, participants),
|
|
67
|
+
crossDotted: createMessageTarget(store, from, MESSAGE_ARROWS.crossDotted, participants),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create the full message builder for all participants
|
|
73
|
+
*/
|
|
74
|
+
export function createMessageBuilder<P extends string>(
|
|
75
|
+
store: DiagramStore,
|
|
76
|
+
participants: readonly P[],
|
|
77
|
+
): MessageBuilder<P> {
|
|
78
|
+
const builder = {} as MessageBuilder<P>;
|
|
79
|
+
|
|
80
|
+
participants.forEach((from) => {
|
|
81
|
+
builder[from] = createMessageArrows(store, from, participants);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return builder;
|
|
85
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Note builder functions for SequenceDiagram
|
|
3
|
+
* @module Mermaid/builders/SequenceDiagram/functions/getNotes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DiagramStore } from '../../core/DiagramStore';
|
|
7
|
+
import { sanitizeLabel } from '../../core/sanitize';
|
|
8
|
+
import type { NoteBuilder, NoteAction, NoteSideTarget, NoteOverTarget } from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a single note action
|
|
12
|
+
*/
|
|
13
|
+
function createNoteAction(
|
|
14
|
+
store: DiagramStore,
|
|
15
|
+
position: string,
|
|
16
|
+
): NoteAction {
|
|
17
|
+
return {
|
|
18
|
+
msg(text: string) {
|
|
19
|
+
store.add(`Note ${position}: ${sanitizeLabel(text)}`);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create note targets for left/right positions
|
|
26
|
+
*/
|
|
27
|
+
function createNoteSideTarget<P extends string>(
|
|
28
|
+
store: DiagramStore,
|
|
29
|
+
position: 'left' | 'right',
|
|
30
|
+
participants: readonly P[],
|
|
31
|
+
): NoteSideTarget<P> {
|
|
32
|
+
const target = {} as NoteSideTarget<P>;
|
|
33
|
+
|
|
34
|
+
participants.forEach((p) => {
|
|
35
|
+
target[p] = createNoteAction(store, `${position} of ${p}`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return target;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create note targets for "over" position (can span multiple participants)
|
|
43
|
+
*/
|
|
44
|
+
function createNoteOverTarget<P extends string>(
|
|
45
|
+
store: DiagramStore,
|
|
46
|
+
participants: readonly P[],
|
|
47
|
+
): NoteOverTarget<P> {
|
|
48
|
+
const target = {} as NoteOverTarget<P>;
|
|
49
|
+
|
|
50
|
+
participants.forEach((first) => {
|
|
51
|
+
// Create object with msg for single participant note
|
|
52
|
+
const obj = {
|
|
53
|
+
msg(text: string) {
|
|
54
|
+
store.add(`Note over ${first}: ${sanitizeLabel(text)}`);
|
|
55
|
+
},
|
|
56
|
+
} as NoteAction & { [K in P]: NoteAction };
|
|
57
|
+
|
|
58
|
+
// Add targets for spanning to other participants
|
|
59
|
+
participants.forEach((second) => {
|
|
60
|
+
if (first !== second) {
|
|
61
|
+
(obj as Record<string, NoteAction>)[second] = {
|
|
62
|
+
msg(text: string) {
|
|
63
|
+
store.add(`Note over ${first},${second}: ${sanitizeLabel(text)}`);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
// Self-reference just does single note
|
|
68
|
+
(obj as Record<string, NoteAction>)[second] = {
|
|
69
|
+
msg(text: string) {
|
|
70
|
+
store.add(`Note over ${first}: ${sanitizeLabel(text)}`);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
target[first] = obj;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return target;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create the full note builder
|
|
84
|
+
*/
|
|
85
|
+
export function createNoteBuilder<P extends string>(
|
|
86
|
+
store: DiagramStore,
|
|
87
|
+
participants: readonly P[],
|
|
88
|
+
): NoteBuilder<P> {
|
|
89
|
+
return {
|
|
90
|
+
leftOf: createNoteSideTarget(store, 'left', participants),
|
|
91
|
+
rightOf: createNoteSideTarget(store, 'right', participants),
|
|
92
|
+
over: createNoteOverTarget(store, participants),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SequenceDiagram function exports
|
|
3
|
+
* @module Mermaid/builders/SequenceDiagram/functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { createMessageBuilder } from './getMessages';
|
|
7
|
+
export { createNoteBuilder } from './getNotes';
|
|
8
|
+
export { createActivationBuilder } from './getActivations';
|
|
9
|
+
export {
|
|
10
|
+
createLoopBuilder,
|
|
11
|
+
createAltBuilder,
|
|
12
|
+
createParBuilder,
|
|
13
|
+
createRectBuilder,
|
|
14
|
+
createCriticalBuilder,
|
|
15
|
+
createBreakBuilder,
|
|
16
|
+
} from './getBlocks';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SequenceDiagram builder exports
|
|
3
|
+
* @module Mermaid/builders/SequenceDiagram
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { SequenceDiagram } from './SequenceDiagram';
|
|
7
|
+
export type {
|
|
8
|
+
ParticipantsObject,
|
|
9
|
+
SequenceDiagramOptions,
|
|
10
|
+
SequenceDiagramBuilder,
|
|
11
|
+
MessageBuilder,
|
|
12
|
+
MessageArrows,
|
|
13
|
+
MessageTarget,
|
|
14
|
+
MessageAction,
|
|
15
|
+
NoteBuilder,
|
|
16
|
+
ActivationBuilder,
|
|
17
|
+
DynamicArrowType,
|
|
18
|
+
} from './types';
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for SequenceDiagram builder
|
|
3
|
+
* @module Mermaid/builders/SequenceDiagram/types
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ParticipantType, MessageArrowType } from '../core/types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Participants definition object
|
|
10
|
+
*/
|
|
11
|
+
export type ParticipantsObject<P extends string> = {
|
|
12
|
+
readonly [K in P]: ParticipantType | { type: ParticipantType; alias?: string };
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for SequenceDiagram builder
|
|
17
|
+
*/
|
|
18
|
+
export interface SequenceDiagramOptions {
|
|
19
|
+
/** Enable auto-numbering of messages */
|
|
20
|
+
autoNumber?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Message action builder
|
|
25
|
+
*/
|
|
26
|
+
export interface MessageAction {
|
|
27
|
+
/** Add a message with text */
|
|
28
|
+
msg(text: string): void;
|
|
29
|
+
/** Add a message that activates the target */
|
|
30
|
+
activate(text: string): void;
|
|
31
|
+
/** Add a message that deactivates the target */
|
|
32
|
+
deactivate(text: string): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Target participant selector for messages
|
|
37
|
+
*/
|
|
38
|
+
export type MessageTarget<P extends string> = {
|
|
39
|
+
[K in P]: MessageAction;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Arrow type selector for messages
|
|
44
|
+
*/
|
|
45
|
+
export type MessageArrows<P extends string> = {
|
|
46
|
+
/** Synchronous call ->> */
|
|
47
|
+
sync: MessageTarget<P>;
|
|
48
|
+
/** Synchronous reply -->> */
|
|
49
|
+
syncReply: MessageTarget<P>;
|
|
50
|
+
/** Async call -) */
|
|
51
|
+
async: MessageTarget<P>;
|
|
52
|
+
/** Async reply --) */
|
|
53
|
+
asyncReply: MessageTarget<P>;
|
|
54
|
+
/** Solid line -> */
|
|
55
|
+
solid: MessageTarget<P>;
|
|
56
|
+
/** Dotted line --> */
|
|
57
|
+
dotted: MessageTarget<P>;
|
|
58
|
+
/** Cross (failure) -x */
|
|
59
|
+
cross: MessageTarget<P>;
|
|
60
|
+
/** Dotted cross --x */
|
|
61
|
+
crossDotted: MessageTarget<P>;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Message builder - from participant to arrow type to target
|
|
66
|
+
*/
|
|
67
|
+
export type MessageBuilder<P extends string> = {
|
|
68
|
+
[K in P]: MessageArrows<P>;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Note position types
|
|
73
|
+
*/
|
|
74
|
+
export type NotePosition = 'leftOf' | 'rightOf' | 'over';
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Single note action
|
|
78
|
+
*/
|
|
79
|
+
export interface NoteAction {
|
|
80
|
+
msg(text: string): void;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Note target for "over" position (can span multiple participants)
|
|
85
|
+
*/
|
|
86
|
+
export type NoteOverTarget<P extends string> = {
|
|
87
|
+
[K in P]: NoteAction & {
|
|
88
|
+
/** Span to another participant */
|
|
89
|
+
[K2 in P]: NoteAction;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Note target for left/right positions
|
|
95
|
+
*/
|
|
96
|
+
export type NoteSideTarget<P extends string> = {
|
|
97
|
+
[K in P]: NoteAction;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Note builder
|
|
102
|
+
*/
|
|
103
|
+
export interface NoteBuilder<P extends string> {
|
|
104
|
+
leftOf: NoteSideTarget<P>;
|
|
105
|
+
rightOf: NoteSideTarget<P>;
|
|
106
|
+
over: NoteOverTarget<P>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Activation builder
|
|
111
|
+
*/
|
|
112
|
+
export type ActivationBuilder<P extends string> = {
|
|
113
|
+
[K in P]: {
|
|
114
|
+
activate(): void;
|
|
115
|
+
deactivate(): void;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Arrow type for dynamic message method
|
|
121
|
+
*/
|
|
122
|
+
export type DynamicArrowType = 'sync' | 'syncReply' | 'async' | 'asyncReply' | 'solid' | 'dotted' | 'cross' | 'crossDotted';
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Main SequenceDiagram builder result
|
|
126
|
+
*/
|
|
127
|
+
export interface SequenceDiagramBuilder<P extends string> {
|
|
128
|
+
/** Message builder (d.Alice.sync.Bob.msg("Hello")) */
|
|
129
|
+
d: MessageBuilder<P>;
|
|
130
|
+
/** Note builder (note.over.Alice.msg("Thinking")) */
|
|
131
|
+
note: NoteBuilder<P>;
|
|
132
|
+
/** Activation builder (activate.Alice.activate()) */
|
|
133
|
+
activate: ActivationBuilder<P>;
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// Dynamic access methods (for runtime participant names)
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Send a message dynamically (for runtime participant names)
|
|
141
|
+
* @example message('Alice', 'Bob', 'Hello!') // sync arrow
|
|
142
|
+
* @example message('Alice', 'Bob', 'Hello!', 'async')
|
|
143
|
+
*/
|
|
144
|
+
message(from: string, to: string, text: string, arrow?: DynamicArrowType): void;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Add a note over one participant dynamically
|
|
148
|
+
* @example noteOver('Alice', 'Thinking...')
|
|
149
|
+
*/
|
|
150
|
+
noteOver(participant: string, text: string): void;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Add a note spanning two participants dynamically
|
|
154
|
+
* @example noteOverSpan('Alice', 'Bob', 'Handshake')
|
|
155
|
+
*/
|
|
156
|
+
noteOverSpan(participant1: string, participant2: string, text: string): void;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Add a note to the left of a participant
|
|
160
|
+
* @example noteLeft('Alice', 'Waiting')
|
|
161
|
+
*/
|
|
162
|
+
noteLeft(participant: string, text: string): void;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Add a note to the right of a participant
|
|
166
|
+
* @example noteRight('Alice', 'Done')
|
|
167
|
+
*/
|
|
168
|
+
noteRight(participant: string, text: string): void;
|
|
169
|
+
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// Block methods
|
|
172
|
+
// ============================================================================
|
|
173
|
+
|
|
174
|
+
/** Loop block */
|
|
175
|
+
loop(label: string, fn: () => void): void;
|
|
176
|
+
/** Optional/alternative block */
|
|
177
|
+
alt(label: string, fn: () => void): { else(label: string, fn: () => void): void };
|
|
178
|
+
/** Parallel block */
|
|
179
|
+
par(label: string, fn: () => void): { and(label: string, fn: () => void): void };
|
|
180
|
+
/** Colored rectangle box */
|
|
181
|
+
rect(color: string, fn: () => void): void;
|
|
182
|
+
/** Critical region */
|
|
183
|
+
critical(label: string, fn: () => void): { option(label: string, fn: () => void): void };
|
|
184
|
+
/** Break */
|
|
185
|
+
break(label: string, fn: () => void): void;
|
|
186
|
+
/** Add a comment */
|
|
187
|
+
comment(text: string): void;
|
|
188
|
+
/** Add blank line */
|
|
189
|
+
blank(): void;
|
|
190
|
+
/** Get the Mermaid string */
|
|
191
|
+
toString(): string;
|
|
192
|
+
}
|