@djangocfg/ui-tools 2.1.158 → 2.1.160
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/README.md +59 -6
- package/package.json +11 -6
- package/src/tools/Mermaid/Mermaid.story.tsx +36 -1
- package/src/tools/Mermaid/builders/SequenceDiagram/SequenceDiagram.ts +36 -1
- package/src/tools/Mermaid/builders/SequenceDiagram/index.ts +1 -0
- package/src/tools/Mermaid/builders/SequenceDiagram/types.ts +45 -0
package/README.md
CHANGED
|
@@ -49,8 +49,21 @@ import { Gallery, GalleryLightbox } from '@djangocfg/ui-tools/gallery';
|
|
|
49
49
|
|
|
50
50
|
// Only loads Map (~800KB)
|
|
51
51
|
import { MapContainer, MapMarker } from '@djangocfg/ui-tools/map';
|
|
52
|
+
|
|
53
|
+
// Mermaid builders (no heavy Mermaid dependency until render)
|
|
54
|
+
import { FlowDiagram, SequenceDiagram, JourneyDiagram } from '@djangocfg/ui-tools/mermaid';
|
|
52
55
|
```
|
|
53
56
|
|
|
57
|
+
## Exports
|
|
58
|
+
|
|
59
|
+
| Path | Content |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| `@djangocfg/ui-tools` | All tools with lazy loading |
|
|
62
|
+
| `@djangocfg/ui-tools/gallery` | Gallery components & hooks |
|
|
63
|
+
| `@djangocfg/ui-tools/map` | Map components & utilities |
|
|
64
|
+
| `@djangocfg/ui-tools/mermaid` | Mermaid component & declarative builders |
|
|
65
|
+
| `@djangocfg/ui-tools/styles` | CSS styles |
|
|
66
|
+
|
|
54
67
|
## Gallery
|
|
55
68
|
|
|
56
69
|
Full-featured image/video gallery with carousel, grid view, and fullscreen lightbox.
|
|
@@ -241,9 +254,9 @@ Render Mermaid diagrams with fullscreen zoom support and type-safe declarative b
|
|
|
241
254
|
### Basic Usage
|
|
242
255
|
|
|
243
256
|
```tsx
|
|
244
|
-
import
|
|
257
|
+
import { LazyMermaid } from '@djangocfg/ui-tools';
|
|
245
258
|
|
|
246
|
-
<
|
|
259
|
+
<LazyMermaid chart={`
|
|
247
260
|
graph TD
|
|
248
261
|
A[Start] --> B{Decision}
|
|
249
262
|
B -->|Yes| C[Action]
|
|
@@ -265,13 +278,14 @@ import Mermaid from '@djangocfg/ui-tools/tools/Mermaid';
|
|
|
265
278
|
Type-safe builders for creating Mermaid diagrams programmatically:
|
|
266
279
|
|
|
267
280
|
```tsx
|
|
268
|
-
import
|
|
281
|
+
import { LazyMermaid } from '@djangocfg/ui-tools';
|
|
282
|
+
import {
|
|
269
283
|
FlowDiagram,
|
|
270
284
|
SequenceDiagram,
|
|
271
285
|
JourneyDiagram,
|
|
272
286
|
useStylePresets,
|
|
273
287
|
useBoxColors,
|
|
274
|
-
} from '@djangocfg/ui-tools/
|
|
288
|
+
} from '@djangocfg/ui-tools/mermaid';
|
|
275
289
|
|
|
276
290
|
function MyDiagram() {
|
|
277
291
|
const presets = useStylePresets();
|
|
@@ -295,7 +309,7 @@ function MyDiagram() {
|
|
|
295
309
|
flow.style.define('success', presets.success);
|
|
296
310
|
flow.style.apply('success', 'success', 'finish');
|
|
297
311
|
|
|
298
|
-
return <
|
|
312
|
+
return <LazyMermaid chart={flow.toString()} />;
|
|
299
313
|
}
|
|
300
314
|
```
|
|
301
315
|
|
|
@@ -347,6 +361,8 @@ flow.style.apply('myStyle', 'A', 'B');
|
|
|
347
361
|
|
|
348
362
|
### SequenceDiagram API
|
|
349
363
|
|
|
364
|
+
**Static API** (type-safe, for known participants):
|
|
365
|
+
|
|
350
366
|
```tsx
|
|
351
367
|
const { d, rect, alt, loop, toString } = SequenceDiagram({
|
|
352
368
|
User: 'actor',
|
|
@@ -354,7 +370,7 @@ const { d, rect, alt, loop, toString } = SequenceDiagram({
|
|
|
354
370
|
API: 'participant',
|
|
355
371
|
}, { autoNumber: true });
|
|
356
372
|
|
|
357
|
-
// Messages
|
|
373
|
+
// Messages (type-safe chain)
|
|
358
374
|
d.User.sync.App.msg('Click button');
|
|
359
375
|
d.App.async.API.msg('Fetch data');
|
|
360
376
|
d.API.asyncReply.App.msg('Response');
|
|
@@ -377,6 +393,43 @@ loop('Every 5s', () => {
|
|
|
377
393
|
return toString();
|
|
378
394
|
```
|
|
379
395
|
|
|
396
|
+
**Dynamic API** (for runtime participant names):
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
// Participants from API/database
|
|
400
|
+
const characters = ['Alice', 'Bob', 'Charlie'];
|
|
401
|
+
const participants: Record<string, 'participant'> = {};
|
|
402
|
+
characters.forEach(c => { participants[c] = 'participant'; });
|
|
403
|
+
|
|
404
|
+
const seq = SequenceDiagram(participants, { autoNumber: true });
|
|
405
|
+
|
|
406
|
+
// Dynamic methods - no type assertions needed
|
|
407
|
+
seq.message('Alice', 'Bob', 'Hello!');
|
|
408
|
+
seq.message('Bob', 'Alice', 'Hi!', 'syncReply');
|
|
409
|
+
seq.message('Alice', 'Charlie', 'Ping', 'async');
|
|
410
|
+
|
|
411
|
+
// Notes
|
|
412
|
+
seq.noteOver('Alice', 'Thinking...');
|
|
413
|
+
seq.noteOverSpan('Alice', 'Bob', 'Discussion');
|
|
414
|
+
seq.noteLeft('Charlie', 'Waiting');
|
|
415
|
+
seq.noteRight('Charlie', 'Done');
|
|
416
|
+
|
|
417
|
+
// Blocks work the same
|
|
418
|
+
seq.rect('rgba(100,200,255,0.2)', () => {
|
|
419
|
+
seq.message('Alice', 'Bob', 'Secret message');
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
return seq.toString();
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
| Dynamic Method | Description |
|
|
426
|
+
|----------------|-------------|
|
|
427
|
+
| `message(from, to, text, arrow?)` | Send message (arrow: sync, syncReply, async, asyncReply, solid, dotted, cross) |
|
|
428
|
+
| `noteOver(participant, text)` | Note over one participant |
|
|
429
|
+
| `noteOverSpan(p1, p2, text)` | Note spanning two participants |
|
|
430
|
+
| `noteLeft(participant, text)` | Note left of participant |
|
|
431
|
+
| `noteRight(participant, text)` | Note right of participant |
|
|
432
|
+
|
|
380
433
|
### JourneyDiagram API
|
|
381
434
|
|
|
382
435
|
```tsx
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-tools",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.160",
|
|
4
4
|
"description": "Heavy React tools with lazy loading - for Electron, Vite, CRA, Next.js apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-tools",
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
"import": "./src/tools/Map/index.ts",
|
|
48
48
|
"require": "./src/tools/Map/index.ts"
|
|
49
49
|
},
|
|
50
|
+
"./mermaid": {
|
|
51
|
+
"types": "./src/tools/Mermaid/index.tsx",
|
|
52
|
+
"import": "./src/tools/Mermaid/index.tsx",
|
|
53
|
+
"require": "./src/tools/Mermaid/index.tsx"
|
|
54
|
+
},
|
|
50
55
|
"./styles": "./src/styles/index.css"
|
|
51
56
|
},
|
|
52
57
|
"files": [
|
|
@@ -63,8 +68,8 @@
|
|
|
63
68
|
"check": "tsc --noEmit"
|
|
64
69
|
},
|
|
65
70
|
"peerDependencies": {
|
|
66
|
-
"@djangocfg/i18n": "^2.1.
|
|
67
|
-
"@djangocfg/ui-core": "^2.1.
|
|
71
|
+
"@djangocfg/i18n": "^2.1.160",
|
|
72
|
+
"@djangocfg/ui-core": "^2.1.160",
|
|
68
73
|
"lucide-react": "^0.545.0",
|
|
69
74
|
"react": "^19.0.0",
|
|
70
75
|
"react-dom": "^19.0.0",
|
|
@@ -96,10 +101,10 @@
|
|
|
96
101
|
"@maplibre/maplibre-gl-geocoder": "^1.7.0"
|
|
97
102
|
},
|
|
98
103
|
"devDependencies": {
|
|
99
|
-
"@djangocfg/i18n": "^2.1.
|
|
104
|
+
"@djangocfg/i18n": "^2.1.160",
|
|
100
105
|
"@djangocfg/playground": "workspace:*",
|
|
101
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
102
|
-
"@djangocfg/ui-core": "^2.1.
|
|
106
|
+
"@djangocfg/typescript-config": "^2.1.160",
|
|
107
|
+
"@djangocfg/ui-core": "^2.1.160",
|
|
103
108
|
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
|
|
104
109
|
"@types/node": "^24.7.2",
|
|
105
110
|
"@types/react": "^19.1.0",
|
|
@@ -96,6 +96,39 @@ function useJourneyDiagram() {
|
|
|
96
96
|
return journey.toString();
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Example using dynamic methods for runtime participant names
|
|
101
|
+
* Useful when participants are not known at compile time
|
|
102
|
+
*/
|
|
103
|
+
function useDynamicSequenceDiagram() {
|
|
104
|
+
const boxes = useBoxColors();
|
|
105
|
+
|
|
106
|
+
// Participants from dynamic data (e.g., from API)
|
|
107
|
+
const participants = ['Alice', 'Bob', 'Charlie'];
|
|
108
|
+
const participantsObj: Record<string, 'participant'> = {};
|
|
109
|
+
participants.forEach(p => { participantsObj[p] = 'participant'; });
|
|
110
|
+
|
|
111
|
+
const seq = SequenceDiagram(participantsObj, { autoNumber: true });
|
|
112
|
+
|
|
113
|
+
// Using dynamic methods - no type assertions needed
|
|
114
|
+
seq.rect(boxes.primary, () => {
|
|
115
|
+
seq.noteOver('Alice', 'Starting conversation');
|
|
116
|
+
seq.message('Alice', 'Bob', 'Hello Bob!');
|
|
117
|
+
seq.message('Bob', 'Alice', 'Hi Alice!', 'syncReply');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
seq.blank();
|
|
121
|
+
|
|
122
|
+
seq.rect(boxes.success, () => {
|
|
123
|
+
seq.noteOverSpan('Alice', 'Charlie', 'Group discussion');
|
|
124
|
+
seq.message('Alice', 'Charlie', 'Hey Charlie!');
|
|
125
|
+
seq.message('Charlie', 'Bob', 'Bob, join us!');
|
|
126
|
+
seq.message('Bob', 'Charlie', 'On my way!', 'async');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return seq.toString();
|
|
130
|
+
}
|
|
131
|
+
|
|
99
132
|
function useArchitectureDiagram() {
|
|
100
133
|
type Nodes = 'client' | 'nginx' | 'api1' | 'api2' | 'db' | 'cache' | 'queue';
|
|
101
134
|
const flow = FlowDiagram<Nodes>({ direction: 'TB' });
|
|
@@ -148,7 +181,7 @@ function useArchitectureDiagram() {
|
|
|
148
181
|
|
|
149
182
|
export const Interactive = () => {
|
|
150
183
|
const [diagramType] = useSelect('diagramType', {
|
|
151
|
-
options: ['flow', 'sequence', 'journey', 'architecture'] as const,
|
|
184
|
+
options: ['flow', 'sequence', 'sequenceDynamic', 'journey', 'architecture'] as const,
|
|
152
185
|
defaultValue: 'flow',
|
|
153
186
|
label: 'Diagram Type',
|
|
154
187
|
});
|
|
@@ -160,12 +193,14 @@ export const Interactive = () => {
|
|
|
160
193
|
|
|
161
194
|
const flowChart = useFlowDiagram();
|
|
162
195
|
const sequenceChart = useSequenceDiagram();
|
|
196
|
+
const sequenceDynamicChart = useDynamicSequenceDiagram();
|
|
163
197
|
const journeyChart = useJourneyDiagram();
|
|
164
198
|
const architectureChart = useArchitectureDiagram();
|
|
165
199
|
|
|
166
200
|
const charts = {
|
|
167
201
|
flow: flowChart,
|
|
168
202
|
sequence: sequenceChart,
|
|
203
|
+
sequenceDynamic: sequenceDynamicChart,
|
|
169
204
|
journey: journeyChart,
|
|
170
205
|
architecture: architectureChart,
|
|
171
206
|
};
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
createCriticalBuilder,
|
|
19
19
|
createBreakBuilder,
|
|
20
20
|
} from './functions/getBlocks';
|
|
21
|
-
import type { ParticipantsObject, SequenceDiagramOptions, SequenceDiagramBuilder } from './types';
|
|
21
|
+
import type { ParticipantsObject, SequenceDiagramOptions, SequenceDiagramBuilder, DynamicArrowType } from './types';
|
|
22
22
|
|
|
23
23
|
const DEFAULT_OPTIONS: Required<SequenceDiagramOptions> = {
|
|
24
24
|
autoNumber: false,
|
|
@@ -97,10 +97,45 @@ export function SequenceDiagram<P extends string>(
|
|
|
97
97
|
const noteBuilder = createNoteBuilder<P>(store, participantKeys);
|
|
98
98
|
const activationBuilder = createActivationBuilder<P>(store, participantKeys);
|
|
99
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
|
+
|
|
100
112
|
return {
|
|
101
113
|
d: messageBuilder,
|
|
102
114
|
note: noteBuilder,
|
|
103
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
|
+
|
|
104
139
|
loop: createLoopBuilder(store),
|
|
105
140
|
alt: createAltBuilder(store),
|
|
106
141
|
par: createParBuilder(store),
|
|
@@ -116,6 +116,11 @@ export type ActivationBuilder<P extends string> = {
|
|
|
116
116
|
};
|
|
117
117
|
};
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Arrow type for dynamic message method
|
|
121
|
+
*/
|
|
122
|
+
export type DynamicArrowType = 'sync' | 'syncReply' | 'async' | 'asyncReply' | 'solid' | 'dotted' | 'cross' | 'crossDotted';
|
|
123
|
+
|
|
119
124
|
/**
|
|
120
125
|
* Main SequenceDiagram builder result
|
|
121
126
|
*/
|
|
@@ -126,6 +131,46 @@ export interface SequenceDiagramBuilder<P extends string> {
|
|
|
126
131
|
note: NoteBuilder<P>;
|
|
127
132
|
/** Activation builder (activate.Alice.activate()) */
|
|
128
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
|
+
|
|
129
174
|
/** Loop block */
|
|
130
175
|
loop(label: string, fn: () => void): void;
|
|
131
176
|
/** Optional/alternative block */
|