@djangocfg/ui-tools 2.1.159 → 2.1.161

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 CHANGED
@@ -361,6 +361,8 @@ flow.style.apply('myStyle', 'A', 'B');
361
361
 
362
362
  ### SequenceDiagram API
363
363
 
364
+ **Static API** (type-safe, for known participants):
365
+
364
366
  ```tsx
365
367
  const { d, rect, alt, loop, toString } = SequenceDiagram({
366
368
  User: 'actor',
@@ -368,7 +370,7 @@ const { d, rect, alt, loop, toString } = SequenceDiagram({
368
370
  API: 'participant',
369
371
  }, { autoNumber: true });
370
372
 
371
- // Messages
373
+ // Messages (type-safe chain)
372
374
  d.User.sync.App.msg('Click button');
373
375
  d.App.async.API.msg('Fetch data');
374
376
  d.API.asyncReply.App.msg('Response');
@@ -391,6 +393,43 @@ loop('Every 5s', () => {
391
393
  return toString();
392
394
  ```
393
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
+
394
433
  ### JourneyDiagram API
395
434
 
396
435
  ```tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-tools",
3
- "version": "2.1.159",
3
+ "version": "2.1.161",
4
4
  "description": "Heavy React tools with lazy loading - for Electron, Vite, CRA, Next.js apps",
5
5
  "keywords": [
6
6
  "ui-tools",
@@ -68,8 +68,8 @@
68
68
  "check": "tsc --noEmit"
69
69
  },
70
70
  "peerDependencies": {
71
- "@djangocfg/i18n": "^2.1.159",
72
- "@djangocfg/ui-core": "^2.1.159",
71
+ "@djangocfg/i18n": "^2.1.161",
72
+ "@djangocfg/ui-core": "^2.1.161",
73
73
  "lucide-react": "^0.545.0",
74
74
  "react": "^19.0.0",
75
75
  "react-dom": "^19.0.0",
@@ -101,10 +101,10 @@
101
101
  "@maplibre/maplibre-gl-geocoder": "^1.7.0"
102
102
  },
103
103
  "devDependencies": {
104
- "@djangocfg/i18n": "^2.1.159",
104
+ "@djangocfg/i18n": "^2.1.161",
105
105
  "@djangocfg/playground": "workspace:*",
106
- "@djangocfg/typescript-config": "^2.1.159",
107
- "@djangocfg/ui-core": "^2.1.159",
106
+ "@djangocfg/typescript-config": "^2.1.161",
107
+ "@djangocfg/ui-core": "^2.1.161",
108
108
  "@types/mapbox__mapbox-gl-draw": "^1.4.8",
109
109
  "@types/node": "^24.7.2",
110
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),
@@ -14,4 +14,5 @@ export type {
14
14
  MessageAction,
15
15
  NoteBuilder,
16
16
  ActivationBuilder,
17
+ DynamicArrowType,
17
18
  } from './types';
@@ -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 */