@amcharts/amcharts5-mcp 1.0.0

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.
@@ -0,0 +1,523 @@
1
+ # Flow Charts — Sankey, Chord, Arc Diagrams
2
+
3
+
4
+ ## Required imports
5
+
6
+ ### ES modules
7
+ ```ts
8
+ import * as am5 from "@amcharts/amcharts5";
9
+ import * as am5flow from "@amcharts/amcharts5/flow";
10
+ import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
11
+ ```
12
+
13
+ ### CDN
14
+ ```html
15
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
16
+ <script src="https://cdn.amcharts.com/lib/5/flow.js"></script>
17
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
18
+ ```
19
+
20
+ ## Data structure (shared by all flow types)
21
+
22
+ ```js
23
+ var data = [
24
+ { from: "A", to: "D", value: 10 },
25
+ { from: "B", to: "D", value: 8 },
26
+ { from: "B", to: "E", value: 4 },
27
+ { from: "C", to: "E", value: 3 },
28
+ { from: "D", to: "F", value: 15 },
29
+ { from: "E", to: "F", value: 6 }
30
+ ];
31
+ ```
32
+
33
+ Node IDs ("A", "B", etc.) are automatically extracted — you don't define nodes separately.
34
+
35
+ ## Series types
36
+
37
+ | Class | Use for | Visual |
38
+ |-------|---------|--------|
39
+ | `am5flow.Sankey` | Linear flow diagrams | Rectangular nodes + curved links |
40
+ | `am5flow.Chord` | Circular relationship diagrams | Arc nodes + ribbon links |
41
+ | `am5flow.ChordDirected` | Directed chord (arrows) | Arc nodes + directed ribbons |
42
+ | `am5flow.ChordNonRibbon` | Chord with line links | Arc nodes + line links |
43
+ | `am5flow.ArcDiagram` | Arc connections between linear nodes | Linear nodes + arc links |
44
+
45
+ ## Sankey diagram
46
+
47
+ ```js
48
+ const root = am5.Root.new("chartdiv");
49
+ root.setThemes([am5themes_Animated.new(root)]);
50
+
51
+ const series = root.container.children.push(
52
+ am5flow.Sankey.new(root, {
53
+ sourceIdField: "from",
54
+ targetIdField: "to",
55
+ valueField: "value",
56
+ orientation: "horizontal", // "horizontal" (default) or "vertical"
57
+ nodeAlign: "justify", // "left" | "right" | "center" | "justify"
58
+ nodeWidth: 10, // pixel width of nodes
59
+ nodePadding: 10, // pixel gap between nodes
60
+ minSize: 0.01 // min node size as fraction of total
61
+ })
62
+ );
63
+
64
+ // Node appearance
65
+ series.nodes.rectangles.template.setAll({
66
+ fillOpacity: 1,
67
+ stroke: am5.color(0x000000),
68
+ strokeWidth: 1,
69
+ cornerRadiusTL: 4,
70
+ cornerRadiusTR: 4,
71
+ cornerRadiusBL: 4,
72
+ cornerRadiusBR: 4
73
+ });
74
+
75
+ // Disable node dragging (optional)
76
+ series.nodes.nodes.template.setAll({
77
+ draggable: false,
78
+ toggleKey: "none"
79
+ });
80
+
81
+ // Link appearance
82
+ series.links.template.setAll({
83
+ fillOpacity: 0.3,
84
+ strokeOpacity: 0,
85
+ tooltipText: "{sourceId} → {targetId}: {value}"
86
+ });
87
+
88
+ // Node labels
89
+ series.nodes.labels.template.setAll({
90
+ text: "{name}",
91
+ fontSize: 12,
92
+ fill: am5.color(0x000000)
93
+ });
94
+
95
+ // Node tooltip
96
+ series.nodes.nodes.template.set("tooltipText", "{name}: {sumIncoming} in / {sumOutgoing} out");
97
+
98
+ // Data — LAST
99
+ series.data.setAll(data);
100
+ series.appear(1000);
101
+ ```
102
+
103
+ ### Sankey key settings
104
+
105
+ | Setting | Type | Description |
106
+ |---------|------|-------------|
107
+ | `sourceIdField` | string | Data field for source node ID (required) |
108
+ | `targetIdField` | string | Data field for target node ID (required) |
109
+ | `valueField` | string | Data field for link value (required) |
110
+ | `orientation` | `"horizontal" \| "vertical"` | Flow direction |
111
+ | `nodeAlign` | `"left" \| "right" \| "center" \| "justify"` | Node alignment |
112
+ | `nodeWidth` | number | Width of nodes in pixels (default: 10) |
113
+ | `nodePadding` | number | Gap between nodes (default: 10) |
114
+ | `linkSort` | function \| null | Custom link sort, `null` for no sorting |
115
+
116
+ ## Chord diagram
117
+
118
+ ```js
119
+ const series = root.container.children.push(
120
+ am5flow.Chord.new(root, {
121
+ sourceIdField: "from",
122
+ targetIdField: "to",
123
+ valueField: "value",
124
+ nodeWidth: 20,
125
+ padAngle: 2, // gap between nodes in degrees
126
+ radius: am5.percent(90),
127
+ startAngle: 80,
128
+ sort: "ascending" // "ascending" | "descending" | "none"
129
+ })
130
+ );
131
+
132
+ // Node appearance (slices on the ring)
133
+ series.nodes.rectangles.template.setAll({
134
+ fillOpacity: 0.8,
135
+ stroke: am5.color(0xffffff),
136
+ strokeWidth: 2
137
+ });
138
+
139
+ // Link appearance
140
+ series.links.template.setAll({
141
+ fillOpacity: 0.3,
142
+ tooltipText: "{sourceId} → {targetId}: {value}"
143
+ });
144
+
145
+ series.data.setAll(data);
146
+ ```
147
+
148
+ ### ChordDirected (with arrows)
149
+
150
+ ```js
151
+ const series = root.container.children.push(
152
+ am5flow.ChordDirected.new(root, {
153
+ sourceIdField: "from",
154
+ targetIdField: "to",
155
+ valueField: "value"
156
+ })
157
+ );
158
+ ```
159
+
160
+ ### ChordNonRibbon (thin lines)
161
+
162
+ ```js
163
+ const series = root.container.children.push(
164
+ am5flow.ChordNonRibbon.new(root, {
165
+ sourceIdField: "from",
166
+ targetIdField: "to",
167
+ valueField: "value"
168
+ })
169
+ );
170
+
171
+ // Line appearance
172
+ series.links.template.setAll({
173
+ strokeOpacity: 0.5,
174
+ fillOpacity: 0,
175
+ strokeWidth: 2
176
+ });
177
+ ```
178
+
179
+ ## Arc diagram
180
+
181
+ ```js
182
+ const series = root.container.children.push(
183
+ am5flow.ArcDiagram.new(root, {
184
+ sourceIdField: "from",
185
+ targetIdField: "to",
186
+ valueField: "value",
187
+ orientation: "horizontal"
188
+ })
189
+ );
190
+
191
+ series.links.template.setAll({
192
+ strokeOpacity: 0.3,
193
+ fillOpacity: 0,
194
+ strokeWidth: 1
195
+ });
196
+
197
+ series.data.setAll(data);
198
+ ```
199
+
200
+ ## Custom node data and colors
201
+
202
+ ```js
203
+ // Define explicit node data with colors and custom names
204
+ series.nodes.data.setAll([
205
+ { id: "A", name: "Source A", fill: am5.color(0x095256) },
206
+ { id: "B", name: "Target B", fill: am5.color(0x087f8c) },
207
+ { id: "C", name: "Output C", fill: am5.color(0x5aaa95) }
208
+ ]);
209
+
210
+ // Apply node colors from data
211
+ series.nodes.rectangles.template.adapters.add("fill", function(fill, target) {
212
+ var dataItem = target.dataItem;
213
+ if (dataItem && dataItem.dataContext && dataItem.dataContext.fill) {
214
+ return dataItem.dataContext.fill;
215
+ }
216
+ return fill;
217
+ });
218
+
219
+ // Node labels use the "name" field by default
220
+ series.nodes.labels.template.setAll({
221
+ text: "{name}"
222
+ });
223
+ ```
224
+
225
+ ## Multi-level Sankey (3+ levels)
226
+
227
+ ```js
228
+ // Data with 3 levels: Source → Category → Destination
229
+ var data = [
230
+ { from: "Solar", to: "Renewable", value: 30 },
231
+ { from: "Wind", to: "Renewable", value: 25 },
232
+ { from: "Coal", to: "Fossil", value: 40 },
233
+ { from: "Gas", to: "Fossil", value: 35 },
234
+ { from: "Renewable", to: "Residential", value: 20 },
235
+ { from: "Renewable", to: "Commercial", value: 35 },
236
+ { from: "Fossil", to: "Residential", value: 30 },
237
+ { from: "Fossil", to: "Industrial", value: 45 }
238
+ ];
239
+ ```
240
+
241
+ ## Events on flow elements
242
+
243
+ ```js
244
+ // Node click
245
+ series.nodes.nodes.template.events.on("click", function(ev) {
246
+ console.log("Node:", ev.target.dataItem.get("id"));
247
+ });
248
+
249
+ // Link click
250
+ series.links.template.events.on("click", function(ev) {
251
+ console.log("Link:", ev.target.dataItem.dataContext);
252
+ });
253
+ ```
254
+
255
+ ## Animated bullets along links
256
+
257
+ Animated labels/circles that flow along Sankey or Chord links use `series.bullets.push()` — **NOT** `series.links.template.bullets.push()`. The bullet position (0 = source, 1 = target) is animated in a loop, and an adapter fades opacity based on position.
258
+
259
+ **Important:** Sankey uses `locationX`, Chord uses `locationY`.
260
+
261
+ ```js
262
+ series.bullets.push(function(root, series, dataItem) {
263
+ var label = am5.Label.new(root, {
264
+ text: "${value}B",
265
+ populateText: true,
266
+ centerX: am5.p50,
267
+ fill: am5.color(0xffffff),
268
+ fontSize: 11
269
+ });
270
+
271
+ var bullet = am5.Bullet.new(root, {
272
+ locationX: 0,
273
+ sprite: label,
274
+ autoRotate: true // orient along link direction
275
+ });
276
+
277
+ // Animate position along the link: 0 → 1, looping forever
278
+ bullet.animate({
279
+ key: "locationX",
280
+ from: 0,
281
+ to: 1,
282
+ duration: Math.random() * 10000 + 2000,
283
+ loops: Infinity
284
+ });
285
+
286
+ // Fade in at center, fade out at edges via adapter
287
+ label.adapters.add("opacity", function(opacity) {
288
+ return 0.5 - Math.abs(0.5 - bullet.get("locationX"));
289
+ });
290
+
291
+ // Drive the adapter on every position change
292
+ bullet.on("locationX", function() {
293
+ label.set("opacity", label.get("opacity"));
294
+ });
295
+
296
+ return bullet;
297
+ });
298
+ ```
299
+
300
+ **For Chord diagrams**, replace every `locationX` with `locationY`:
301
+
302
+ ```js
303
+ var bullet = am5.Bullet.new(root, {
304
+ locationY: 0,
305
+ sprite: label,
306
+ autoRotate: true
307
+ });
308
+
309
+ bullet.animate({
310
+ key: "locationY",
311
+ from: 0, to: 1,
312
+ duration: Math.random() * 10000 + 2000,
313
+ loops: Infinity
314
+ });
315
+
316
+ label.adapters.add("opacity", function(opacity) {
317
+ return 0.5 - Math.abs(0.5 - bullet.get("locationY"));
318
+ });
319
+
320
+ bullet.on("locationY", function() {
321
+ label.set("opacity", label.get("opacity"));
322
+ });
323
+ ```
324
+
325
+ **Key points:**
326
+ - Use `series.bullets.push()` — bullets belong to the series, not to `series.links.template`
327
+ - **Sankey → `locationX`**, **Chord → `locationY`** (using the wrong one means bullets won't animate)
328
+ - `autoRotate: true` makes the label follow the link curve
329
+ - Random duration per bullet creates natural asynchronous flow
330
+ - The opacity adapter formula `0.5 - Math.abs(0.5 - location)` gives a bell curve: invisible at edges, max at center
331
+ - `bullet.on("locationX"/"locationY", ...)` is needed to trigger the adapter as position changes
332
+
333
+ ## Disposal
334
+
335
+ ```js
336
+ root.dispose();
337
+ ```
338
+
339
+
340
+ ---
341
+
342
+ # amCharts 5 Flow Charts — Working Examples
343
+
344
+ ## Example 1: Sankey diagram
345
+
346
+ ```html
347
+ <!DOCTYPE html>
348
+ <html>
349
+ <head>
350
+ <meta charset="UTF-8">
351
+ <title>Sankey Diagram</title>
352
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
353
+ <script src="https://cdn.amcharts.com/lib/5/flow.js"></script>
354
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
355
+ <style>#chartdiv { width: 100%; height: 500px; }</style>
356
+ </head>
357
+ <body>
358
+ <div id="chartdiv"></div>
359
+ <script>
360
+ var root = am5.Root.new("chartdiv");
361
+ root.setThemes([am5themes_Animated.new(root)]);
362
+
363
+ var series = root.container.children.push(am5flow.Sankey.new(root, {
364
+ sourceIdField: "from",
365
+ targetIdField: "to",
366
+ valueField: "value",
367
+ nodeAlign: "justify",
368
+ nodeWidth: 12,
369
+ nodePadding: 15
370
+ }));
371
+
372
+ series.nodes.rectangles.template.setAll({
373
+ fillOpacity: 1,
374
+ stroke: am5.color(0xffffff),
375
+ strokeWidth: 1,
376
+ cornerRadiusTL: 2, cornerRadiusTR: 2,
377
+ cornerRadiusBL: 2, cornerRadiusBR: 2
378
+ });
379
+
380
+ series.links.template.setAll({
381
+ fillOpacity: 0.3,
382
+ tooltipText: "{sourceId} → {targetId}\n[bold]{value}[/]"
383
+ });
384
+
385
+ series.nodes.labels.template.setAll({
386
+ fontSize: 13,
387
+ fill: am5.color(0x333333)
388
+ });
389
+
390
+ series.data.setAll([
391
+ { from: "Website", to: "Sign Up", value: 5000 },
392
+ { from: "Website", to: "Bounce", value: 3000 },
393
+ { from: "Social Media", to: "Sign Up", value: 2000 },
394
+ { from: "Social Media", to: "Bounce", value: 1500 },
395
+ { from: "Email", to: "Sign Up", value: 1000 },
396
+ { from: "Sign Up", to: "Free Trial", value: 4800 },
397
+ { from: "Sign Up", to: "Abandoned", value: 3200 },
398
+ { from: "Free Trial", to: "Paid", value: 2400 },
399
+ { from: "Free Trial", to: "Cancelled", value: 2400 }
400
+ ]);
401
+
402
+ series.appear(1000, 100);
403
+ </script>
404
+ </body>
405
+ </html>
406
+ ```
407
+
408
+ ## Example 2: Chord diagram
409
+
410
+ ```html
411
+ <!DOCTYPE html>
412
+ <html>
413
+ <head>
414
+ <meta charset="UTF-8">
415
+ <title>Chord Diagram</title>
416
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
417
+ <script src="https://cdn.amcharts.com/lib/5/flow.js"></script>
418
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
419
+ <style>#chartdiv { width: 100%; height: 600px; }</style>
420
+ </head>
421
+ <body>
422
+ <div id="chartdiv"></div>
423
+ <script>
424
+ var root = am5.Root.new("chartdiv");
425
+ root.setThemes([am5themes_Animated.new(root)]);
426
+
427
+ var series = root.container.children.push(am5flow.Chord.new(root, {
428
+ sourceIdField: "from",
429
+ targetIdField: "to",
430
+ valueField: "value",
431
+ nodeWidth: 15,
432
+ padAngle: 3
433
+ }));
434
+
435
+ series.nodes.rectangles.template.setAll({
436
+ fillOpacity: 0.9,
437
+ stroke: am5.color(0xffffff),
438
+ strokeWidth: 2
439
+ });
440
+
441
+ series.links.template.setAll({
442
+ fillOpacity: 0.2,
443
+ tooltipText: "{sourceId} ↔ {targetId}: {value}"
444
+ });
445
+
446
+ series.nodes.labels.template.setAll({
447
+ textType: "radial",
448
+ fontSize: 13
449
+ });
450
+
451
+ series.data.setAll([
452
+ { from: "US", to: "China", value: 120 },
453
+ { from: "US", to: "EU", value: 200 },
454
+ { from: "US", to: "Japan", value: 80 },
455
+ { from: "China", to: "EU", value: 150 },
456
+ { from: "China", to: "Japan", value: 90 },
457
+ { from: "EU", to: "Japan", value: 60 },
458
+ { from: "US", to: "India", value: 70 },
459
+ { from: "China", to: "India", value: 110 },
460
+ { from: "EU", to: "India", value: 40 }
461
+ ]);
462
+
463
+ series.appear(1000, 100);
464
+ </script>
465
+ </body>
466
+ </html>
467
+ ```
468
+
469
+ ## Example 3: Vertical Sankey
470
+
471
+ ```html
472
+ <!DOCTYPE html>
473
+ <html>
474
+ <head>
475
+ <meta charset="UTF-8">
476
+ <title>Vertical Sankey</title>
477
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
478
+ <script src="https://cdn.amcharts.com/lib/5/flow.js"></script>
479
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
480
+ <style>#chartdiv { width: 100%; height: 600px; }</style>
481
+ </head>
482
+ <body>
483
+ <div id="chartdiv"></div>
484
+ <script>
485
+ var root = am5.Root.new("chartdiv");
486
+ root.setThemes([am5themes_Animated.new(root)]);
487
+
488
+ var series = root.container.children.push(am5flow.Sankey.new(root, {
489
+ sourceIdField: "from",
490
+ targetIdField: "to",
491
+ valueField: "value",
492
+ orientation: "vertical",
493
+ nodeAlign: "center",
494
+ nodeWidth: 8,
495
+ nodePadding: 20
496
+ }));
497
+
498
+ series.nodes.rectangles.template.setAll({
499
+ fillOpacity: 1, strokeOpacity: 0
500
+ });
501
+
502
+ series.links.template.setAll({
503
+ fillOpacity: 0.25,
504
+ tooltipText: "{sourceId} → {targetId}: {value}"
505
+ });
506
+
507
+ series.data.setAll([
508
+ { from: "Total Budget", to: "Marketing", value: 300 },
509
+ { from: "Total Budget", to: "Engineering", value: 500 },
510
+ { from: "Total Budget", to: "Operations", value: 200 },
511
+ { from: "Marketing", to: "Digital Ads", value: 150 },
512
+ { from: "Marketing", to: "Content", value: 100 },
513
+ { from: "Marketing", to: "Events", value: 50 },
514
+ { from: "Engineering", to: "Frontend", value: 200 },
515
+ { from: "Engineering", to: "Backend", value: 200 },
516
+ { from: "Engineering", to: "Infra", value: 100 }
517
+ ]);
518
+
519
+ series.appear(1000, 100);
520
+ </script>
521
+ </body>
522
+ </html>
523
+ ```