@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,820 @@
1
+ # amCharts 5 – AI coding rules
2
+ # Drop this file in your project root as .cursorrules (Cursor),
3
+ # .windsurfrules (Windsurf), or .github/copilot-instructions.md (VS Code + Copilot)
4
+ # Docs: https://www.amcharts.com/docs/v5/
5
+
6
+ ## Version
7
+
8
+ Always use **amCharts 5** (not v3 or v4). The APIs are completely different.
9
+ amCharts 5 uses Canvas rendering (not SVG like v4).
10
+
11
+ ## Object creation
12
+
13
+ amCharts 5 does NOT use `new ClassName()`. Always use the static `.new()` factory:
14
+
15
+ ```
16
+ // ✅ CORRECT
17
+ const root = am5.Root.new("chartdiv");
18
+ const chart = root.container.children.push(am5xy.XYChart.new(root, { ... }));
19
+
20
+ // ❌ WRONG – never use `new` keyword
21
+ const root = new am5.Root("chartdiv");
22
+ const chart = new am5xy.XYChart(root, { ... });
23
+ ```
24
+
25
+ Every `.new()` call takes `root` as the first argument (except Root itself which takes the div ID).
26
+
27
+ ## Core setup pattern
28
+
29
+ Every chart follows this structure:
30
+
31
+ ```js
32
+ // 1. Create root
33
+ const root = am5.Root.new("chartdiv");
34
+
35
+ // 2. Apply theme(s)
36
+ root.setThemes([am5themes_Animated.new(root)]);
37
+
38
+ // 3. Create chart
39
+ const chart = root.container.children.push(
40
+ am5xy.XYChart.new(root, { layout: root.verticalLayout })
41
+ );
42
+
43
+ // 4. Create axes, series, etc.
44
+ // 5. Set data LAST (important!)
45
+ series.data.setAll([...]);
46
+
47
+ // 6. Play animations
48
+ series.appear(1000);
49
+ chart.appear(1000, 100);
50
+ ```
51
+
52
+ **CRITICAL:** Always set data as late as possible. Once data is set, related objects are created — configuration applied afterward may not take effect.
53
+
54
+ ## Packages and imports
55
+
56
+ ### ES modules / TypeScript
57
+ ```ts
58
+ import * as am5 from "@amcharts/amcharts5";
59
+ import * as am5xy from "@amcharts/amcharts5/xy";
60
+ import * as am5percent from "@amcharts/amcharts5/percent";
61
+ import * as am5map from "@amcharts/amcharts5/map";
62
+ import * as am5hierarchy from "@amcharts/amcharts5/hierarchy";
63
+ import * as am5flow from "@amcharts/amcharts5/flow";
64
+ import * as am5radar from "@amcharts/amcharts5/radar";
65
+ import * as am5stock from "@amcharts/amcharts5/stock";
66
+ import * as am5wc from "@amcharts/amcharts5/wc";
67
+ import * as am5venn from "@amcharts/amcharts5/venn";
68
+ import * as am5gantt from "@amcharts/amcharts5/gantt";
69
+ import * as am5timeline from "@amcharts/amcharts5/timeline";
70
+ import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
71
+ import am5themes_Dark from "@amcharts/amcharts5/themes/Dark";
72
+
73
+ // Map geodata (example)
74
+ import am5geodata_worldLow from "@amcharts/amcharts5-geodata/worldLow";
75
+ ```
76
+
77
+ ### CDN / script tags
78
+ ```html
79
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
80
+ <script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
81
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
82
+ <!-- Geodata for maps -->
83
+ <script src="https://cdn.amcharts.com/lib/5/geodata/worldLow.js"></script>
84
+ ```
85
+
86
+ CDN script objects use `am5` (core), `am5xy`, `am5percent`, `am5map`, `am5hierarchy`, `am5flow`, `am5radar`, `am5stock`, `am5timeline`, `am5wc`, `am5venn`, `am5gantt` namespaces.
87
+ Themes: `am5themes_Animated`, `am5themes_Dark`, etc.
88
+ Geodata: `am5geodata_worldLow`, `am5geodata_usaLow`, etc.
89
+
90
+ ### Package → chart type mapping
91
+
92
+ | You need | Import package |
93
+ |----------|---------------|
94
+ | XY chart (line, bar, column, area, candlestick) | `@amcharts/amcharts5/xy` |
95
+ | Pie, donut, funnel, pyramid | `@amcharts/amcharts5/percent` |
96
+ | Map / choropleth | `@amcharts/amcharts5/map` + geodata |
97
+ | Radar / gauge | `@amcharts/amcharts5/radar` (also needs xy) |
98
+ | Treemap, sunburst, force-directed, pack | `@amcharts/amcharts5/hierarchy` |
99
+ | Sankey, chord, arc diagram | `@amcharts/amcharts5/flow` |
100
+ | Stock chart | `@amcharts/amcharts5/stock` |
101
+ | Word cloud | `@amcharts/amcharts5/wc` |
102
+ | Venn diagram | `@amcharts/amcharts5/venn` |
103
+ | Timeline (serpentine, spiral, curve) | `@amcharts/amcharts5/timeline` (also needs xy) |
104
+ | Gantt chart | `@amcharts/amcharts5/gantt` (also needs xy) |
105
+
106
+ ## Settings pattern
107
+
108
+ amCharts 5 uses `.set()`, `.setAll()`, and `.get()` — not direct property assignment:
109
+
110
+ ```js
111
+ // ✅ CORRECT
112
+ series.set("valueField", "value");
113
+ series.setAll({ valueField: "value", categoryField: "category" });
114
+ const val = series.get("valueField");
115
+
116
+ // ❌ WRONG
117
+ series.valueField = "value";
118
+ ```
119
+
120
+ Settings are passed in the second argument of `.new()`:
121
+ ```js
122
+ am5xy.LineSeries.new(root, {
123
+ xAxis: xAxis,
124
+ yAxis: yAxis,
125
+ valueYField: "value",
126
+ valueXField: "date"
127
+ })
128
+ ```
129
+
130
+ ## Dark theme
131
+
132
+ If the page has a dark background, always include the Dark theme. Without it, labels and grid will be invisible:
133
+
134
+ ```js
135
+ root.setThemes([
136
+ am5themes_Animated.new(root),
137
+ am5themes_Dark.new(root) // must come after Animated
138
+ ]);
139
+ ```
140
+
141
+ **Rule:** Only add Dark theme when background is clearly dark. Default to white/light with no Dark theme unless the user asks.
142
+
143
+ ## Colors — use defaults, don't invent palettes
144
+
145
+ amCharts assigns colors automatically. Do NOT create custom color palettes unless the user explicitly requests specific colors.
146
+
147
+ ```js
148
+ // Get default colors programmatically
149
+ var colors = chart.get("colors"); // XY, radar, etc.
150
+ var colors = series.get("colors"); // pie/percent charts
151
+
152
+ colors.getIndex(0); // color by index (no counter advance)
153
+ colors.next(); // next color in sequence (advances counter)
154
+ colors.reset(); // restart counter
155
+
156
+ // Only if user requests custom colors:
157
+ colors.set("colors", [am5.color(0x095256), am5.color(0x087f8c)]);
158
+ ```
159
+
160
+ ## Common chart recipes
161
+
162
+ ### XY chart with category axis (bar/column)
163
+ ```js
164
+ const xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
165
+ categoryField: "category",
166
+ renderer: am5xy.AxisRendererX.new(root, { minGridDistance: 30 })
167
+ }));
168
+
169
+ const yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
170
+ renderer: am5xy.AxisRendererY.new(root, {})
171
+ }));
172
+
173
+ const series = chart.series.push(am5xy.ColumnSeries.new(root, {
174
+ xAxis: xAxis,
175
+ yAxis: yAxis,
176
+ valueYField: "value",
177
+ categoryXField: "category"
178
+ }));
179
+
180
+ xAxis.data.setAll(data); // CategoryAxis needs data too!
181
+ series.data.setAll(data);
182
+ ```
183
+
184
+ ### XY chart with date axis (time series)
185
+ ```js
186
+ const xAxis = chart.xAxes.push(am5xy.DateAxis.new(root, {
187
+ baseInterval: { timeUnit: "day", count: 1 },
188
+ renderer: am5xy.AxisRendererX.new(root, { minGridDistance: 50 })
189
+ }));
190
+
191
+ const yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
192
+ renderer: am5xy.AxisRendererY.new(root, {})
193
+ }));
194
+
195
+ const series = chart.series.push(am5xy.LineSeries.new(root, {
196
+ xAxis: xAxis,
197
+ yAxis: yAxis,
198
+ valueYField: "value",
199
+ valueXField: "date"
200
+ }));
201
+
202
+ // Date values must be timestamps (milliseconds)
203
+ series.data.setAll([
204
+ { date: new Date(2024, 0, 1).getTime(), value: 100 },
205
+ { date: new Date(2024, 1, 1).getTime(), value: 120 }
206
+ ]);
207
+ ```
208
+
209
+ ### Pie / Donut chart
210
+ ```js
211
+ const chart = root.container.children.push(
212
+ am5percent.PieChart.new(root, {
213
+ innerRadius: am5.percent(50), // 0 for pie, >0 for donut
214
+ layout: root.verticalLayout
215
+ })
216
+ );
217
+
218
+ const series = chart.series.push(am5percent.PieSeries.new(root, {
219
+ valueField: "value",
220
+ categoryField: "category"
221
+ }));
222
+
223
+ series.data.setAll([
224
+ { category: "A", value: 10 },
225
+ { category: "B", value: 7 },
226
+ { category: "C", value: 3 }
227
+ ]);
228
+ ```
229
+
230
+ ### Map chart (choropleth)
231
+ ```js
232
+ const chart = root.container.children.push(
233
+ am5map.MapChart.new(root, {
234
+ panX: "rotateX",
235
+ projection: am5map.geoMercator()
236
+ })
237
+ );
238
+
239
+ const polygonSeries = chart.series.push(am5map.MapPolygonSeries.new(root, {
240
+ geoJSON: am5geodata_worldLow,
241
+ valueField: "value",
242
+ calculateAggregates: true
243
+ }));
244
+ ```
245
+
246
+ ### Funnel chart
247
+ ```js
248
+ const chart = root.container.children.push(
249
+ am5percent.SlicedChart.new(root, { layout: root.verticalLayout })
250
+ );
251
+
252
+ const series = chart.series.push(am5percent.FunnelSeries.new(root, {
253
+ name: "Funnel",
254
+ valueField: "value",
255
+ categoryField: "stage",
256
+ orientation: "vertical",
257
+ bottomRatio: 0 // 0 = trapezoids (classic funnel), 1 = rectangles
258
+ }));
259
+
260
+ series.data.setAll([
261
+ { stage: "Visits", value: 10000 },
262
+ { stage: "Signups", value: 5200 },
263
+ { stage: "Paid", value: 1400 }
264
+ ]);
265
+ ```
266
+
267
+ ### Stock chart (candlestick with toolbar)
268
+ ```js
269
+ // Requires: index.js, xy.js, stock.js
270
+ // HTML: <div id="chartcontrols"></div> <div id="chartdiv"></div>
271
+ const stockChart = root.container.children.push(
272
+ am5stock.StockChart.new(root, {})
273
+ );
274
+
275
+ const mainPanel = stockChart.panels.push(
276
+ am5stock.StockPanel.new(root, { wheelY: "zoomX", panX: true, panY: true })
277
+ );
278
+
279
+ const dateAxis = mainPanel.xAxes.push(am5xy.DateAxis.new(root, {
280
+ baseInterval: { timeUnit: "day", count: 1 },
281
+ renderer: am5xy.AxisRendererX.new(root, {})
282
+ }));
283
+
284
+ const valueAxis = mainPanel.yAxes.push(am5xy.ValueAxis.new(root, {
285
+ renderer: am5xy.AxisRendererY.new(root, {})
286
+ }));
287
+
288
+ const valueSeries = mainPanel.series.push(am5xy.CandlestickSeries.new(root, {
289
+ name: "STCK",
290
+ xAxis: dateAxis, yAxis: valueAxis,
291
+ valueXField: "Date", valueYField: "Close",
292
+ highValueYField: "High", lowValueYField: "Low",
293
+ openValueYField: "Open", calculateAggregates: true
294
+ }));
295
+
296
+ stockChart.set("stockSeries", valueSeries); // CRITICAL: register main series
297
+ valueSeries.data.setAll(data);
298
+ ```
299
+
300
+ ### Serpentine timeline
301
+ ```js
302
+ const chart = root.container.children.push(am5timeline.SerpentineChart.new(root, {
303
+ orientation: "vertical", levelCount: 3,
304
+ yAxisRadius: am5.percent(25), yAxisInnerRadius: -25
305
+ }));
306
+ const yRenderer = am5timeline.AxisRendererCurveY.new(root, {}); // create Y renderer FIRST
307
+ const xAxis = chart.xAxes.push(am5xy.DateAxis.new(root, {
308
+ baseInterval: { timeUnit: "day", count: 1 },
309
+ renderer: am5timeline.AxisRendererCurveX.new(root, { yRenderer: yRenderer }) // NOT AxisRendererX
310
+ }));
311
+ const yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
312
+ renderer: yRenderer // reuse the same renderer
313
+ }));
314
+ const series = chart.series.push(am5timeline.CurveLineSeries.new(root, { // NOT LineSeries
315
+ xAxis, yAxis, valueYField: "value", valueXField: "date"
316
+ }));
317
+ series.data.setAll(data); // timestamps for DateAxis
318
+ ```
319
+
320
+ ### Word cloud
321
+ ```js
322
+ // Requires: index.js, wc.js
323
+ const series = root.container.children.push(am5wc.WordCloud.new(root, {
324
+ categoryField: "tag",
325
+ valueField: "weight",
326
+ maxFontSize: am5.percent(20),
327
+ minFontSize: am5.percent(3)
328
+ }));
329
+
330
+ series.labels.template.setAll({ tooltipText: "{category}: {value}" });
331
+
332
+ series.data.setAll([
333
+ { tag: "JavaScript", weight: 64 },
334
+ { tag: "Python", weight: 48 },
335
+ { tag: "React", weight: 28 }
336
+ ]);
337
+ ```
338
+
339
+ ### Venn diagram
340
+ ```js
341
+ // Requires: index.js, venn.js
342
+ // NOTE: There is NO VennDiagram class — push Venn directly into a Container
343
+ const container = root.container.children.push(am5.Container.new(root, {
344
+ width: am5.percent(100),
345
+ height: am5.percent(100),
346
+ layout: root.verticalLayout
347
+ }));
348
+
349
+ const series = container.children.push(am5venn.Venn.new(root, {
350
+ categoryField: "name",
351
+ valueField: "value",
352
+ intersectionsField: "sets"
353
+ }));
354
+
355
+ series.data.setAll([
356
+ { name: "A", value: 100 },
357
+ { name: "B", value: 80 },
358
+ { name: "A∩B", value: 30, sets: ["A", "B"] }
359
+ ]);
360
+ ```
361
+
362
+ ### Gantt chart
363
+ ```js
364
+ // Requires: index.js, xy.js, plugins/colorPicker.js, gantt.js
365
+ const chart = root.container.children.push(am5gantt.Gantt.new(root, {
366
+ editable: true,
367
+ durationUnit: "day"
368
+ }));
369
+
370
+ // Gantt uses TWO separate data calls — NOT nested children
371
+ chart.yAxis.data.setAll([
372
+ { id: "phase1", name: "Phase 1", color: am5.color(0x297373) },
373
+ { id: "taskA", name: "Task A", parentId: "phase1" },
374
+ { id: "taskB", name: "Task B", parentId: "phase1" }
375
+ ]);
376
+ chart.series.data.setAll([
377
+ { id: "phase1", start: new Date(2024, 0, 1).getTime(), duration: 10, progress: 100 },
378
+ { id: "taskA", start: new Date(2024, 0, 1).getTime(), duration: 5, progress: 100 },
379
+ { id: "taskB", start: new Date(2024, 0, 6).getTime(), duration: 5, progress: 80 }
380
+ ]);
381
+ ```
382
+
383
+ ## Events
384
+
385
+ ```js
386
+ // Click on elements
387
+ series.columns.template.events.on("click", function(ev) {
388
+ console.log("Clicked:", ev.target.dataItem.dataContext);
389
+ });
390
+
391
+ // Lifecycle
392
+ series.events.on("datavalidated", function() { /* data ready */ });
393
+
394
+ // One-time listener
395
+ series.events.once("datavalidated", function() { /* runs once */ });
396
+
397
+ // Setting change watch
398
+ sprite.on("width", function(width) { console.log("Width:", width); });
399
+
400
+ // Remove listener
401
+ var disposer = sprite.events.on("click", handler);
402
+ disposer.dispose();
403
+
404
+ // Common events: click, pointerover, pointerout, pointerdown, globalpointermove
405
+ ```
406
+
407
+ ## Adapters
408
+
409
+ Dynamically modify a setting value before it's applied:
410
+
411
+ ```js
412
+ series.columns.template.adapters.add("fill", function(fill, target) {
413
+ return target.dataItem.get("valueY") > 100
414
+ ? am5.color(0x00cc00)
415
+ : am5.color(0xcc0000);
416
+ });
417
+ ```
418
+
419
+ ## States
420
+
421
+ ```js
422
+ series.columns.template.states.create("hover", {
423
+ fillOpacity: 0.8,
424
+ scale: 1.05
425
+ });
426
+ // Built-in states: "hover", "active", "disabled", "hidden"
427
+ ```
428
+
429
+ ## Heat rules
430
+
431
+ Color or size elements by data value. Requires `calculateAggregates: true` on the series (unless `minValue`/`maxValue` are set).
432
+
433
+ ```js
434
+ // Color columns by value
435
+ series.set("heatRules", [{
436
+ target: series.columns.template,
437
+ dataField: "valueY",
438
+ min: am5.color(0xe5dc36),
439
+ max: am5.color(0x5faa46),
440
+ key: "fill"
441
+ }]);
442
+
443
+ // Scale bullet radius by value — bullets need am5.Template
444
+ var circleTemplate = am5.Template.new({});
445
+ series.bullets.push(function() {
446
+ return am5.Bullet.new(root, {
447
+ sprite: am5.Circle.new(root, {
448
+ fill: series.get("fill")
449
+ }, circleTemplate) // template as 3rd arg
450
+ });
451
+ });
452
+ series.set("heatRules", [{
453
+ target: circleTemplate,
454
+ dataField: "valueY",
455
+ min: 3, max: 25,
456
+ key: "radius"
457
+ }]);
458
+ ```
459
+
460
+ **Heat rule keys:** `"fill"`, `"radius"`, `"opacity"`, `"strokeWidth"`, `"fontSize"`, etc.
461
+ **customFunction:** `(sprite, min, max, value)` for full control.
462
+
463
+ **HeatLegend:**
464
+ ```js
465
+ var heatLegend = chart.children.push(am5.HeatLegend.new(root, {
466
+ orientation: "horizontal",
467
+ startColor: am5.color(0xe5dc36),
468
+ endColor: am5.color(0x5faa46),
469
+ startText: "Low",
470
+ endText: "High",
471
+ stepCount: 5
472
+ }));
473
+ series.events.on("datavalidated", function() {
474
+ heatLegend.set("startValue", series.getPrivate("valueLow"));
475
+ heatLegend.set("endValue", series.getPrivate("valueHigh"));
476
+ });
477
+ ```
478
+
479
+ ## Accessibility
480
+
481
+ ```js
482
+ series.columns.template.setAll({
483
+ focusable: true,
484
+ ariaLabel: "{categoryX}: {valueY}",
485
+ role: "figure"
486
+ });
487
+ // Keyboard: TAB to navigate focusable elements, ENTER to click
488
+ // focusableGroup: "series1" — TAB to first, arrows within group
489
+ ```
490
+
491
+ ## Container & Layout
492
+
493
+ ```js
494
+ var container = root.container.children.push(am5.Container.new(root, {
495
+ width: am5.percent(100),
496
+ height: am5.percent(100),
497
+ layout: root.verticalLayout // also: horizontalLayout, gridLayout
498
+ }));
499
+ container.setAll({ paddingTop: 10, paddingBottom: 10, marginLeft: 20 });
500
+ ```
501
+
502
+ **Layout options:**
503
+
504
+ | Layout | Access | Behavior |
505
+ |--------|--------|----------|
506
+ | Vertical | `root.verticalLayout` | Children stacked top-to-bottom |
507
+ | Horizontal | `root.horizontalLayout` | Children in a row left-to-right |
508
+ | Grid | `root.gridLayout` | Multi-column grid |
509
+ | Custom grid | `am5.GridLayout.new(root, { maxColumns: 3 })` | Grid with custom column count |
510
+ | None | omit `layout` | Children placed at x/y coordinates |
511
+
512
+ ## UI Elements
513
+
514
+ amCharts 5 provides built-in interactive UI widgets. **Prefer these over HTML elements** when building controls.
515
+
516
+ | Element | Class | Description |
517
+ |---------|-------|-------------|
518
+ | Button | `am5.Button` | Clickable with label, icon, hover/down/active states |
519
+ | Slider | `am5.Slider` | Single-value slider (0-1 range), fires `rangechanged` |
520
+ | Scrollbar | `am5.Scrollbar` | Two-grip range selector |
521
+ | NumericStepper | `am5.NumericStepper` | Number input with up/down arrows |
522
+ | ProgressPie | `am5.ProgressPie` | Circular progress indicator |
523
+ | SpriteResizer | `am5.SpriteResizer` | Drag-to-resize handles on any sprite |
524
+ | EditableLabel | `am5.EditableLabel` | Label editable on click |
525
+ | Modal | `am5.Modal` | HTML overlay dialog with `open()`/`close()` |
526
+
527
+ ```js
528
+ // Slider controlling a chart value
529
+ var slider = container.children.push(am5.Slider.new(root, {
530
+ orientation: "horizontal",
531
+ start: 0.5,
532
+ width: am5.percent(80),
533
+ centerX: am5.percent(50),
534
+ x: am5.percent(50)
535
+ }));
536
+ slider.events.on("rangechanged", function(ev) {
537
+ var value = ev.start; // 0-1 range
538
+ });
539
+
540
+ // Button
541
+ var button = container.children.push(am5.Button.new(root, {
542
+ label: am5.Label.new(root, { text: "Reset" }),
543
+ centerX: am5.percent(50),
544
+ x: am5.percent(50)
545
+ }));
546
+ button.events.on("click", function() {
547
+ slider.set("start", 0.5);
548
+ });
549
+ ```
550
+
551
+ ## Dynamic data
552
+
553
+ ```js
554
+ series.data.setAll(newData); // replace all (NO animation)
555
+ series.data.push({ cat: "X", val: 1 }); // add
556
+ series.data.setIndex(0, newItem); // update at index (ANIMATES)
557
+ series.data.removeIndex(2); // remove at index
558
+ series.data.insertIndex(1, newItem); // insert at index
559
+
560
+ // To update data WITH animation, use setIndex() per item:
561
+ // newData.forEach(function(item, i) { series.data.setIndex(i, item); });
562
+
563
+ // IMPORTANT: series.dataItems is a plain array, NOT a List
564
+ // ❌ series.dataItems.each(fn) — will throw
565
+ // ✅ series.dataItems.forEach(fn) — standard JS
566
+ // ✅ am5.array.each(series.dataItems, function(dataItem) {})
567
+ ```
568
+
569
+ ## Data processor (for external/API data)
570
+
571
+ ```js
572
+ series.data.processor = am5.DataProcessor.new(root, {
573
+ dateFields: ["date"],
574
+ dateFormat: "yyyy-MM-dd",
575
+ numericFields: ["value"],
576
+ emptyAs: 0
577
+ });
578
+ // Configure BEFORE setting data
579
+ ```
580
+
581
+ ## Common components
582
+
583
+ ### Legend
584
+ ```js
585
+ const legend = chart.children.push(am5.Legend.new(root, {
586
+ centerX: am5.percent(50),
587
+ x: am5.percent(50)
588
+ }));
589
+ legend.data.setAll(chart.series.values);
590
+ ```
591
+
592
+ ### Cursor (XY charts)
593
+ ```js
594
+ chart.set("cursor", am5xy.XYCursor.new(root, {
595
+ behavior: "none" // "zoomX", "zoomY", "zoomXY", "selectX", "selectXY", "none"
596
+ }));
597
+ ```
598
+
599
+ ### Scrollbar (XY charts)
600
+ ```js
601
+ chart.set("scrollbarX", am5.Scrollbar.new(root, {
602
+ orientation: "horizontal"
603
+ }));
604
+ ```
605
+
606
+ ### Tooltip
607
+ ```js
608
+ series.set("tooltip", am5.Tooltip.new(root, {
609
+ labelText: "{categoryX}: {valueY}"
610
+ }));
611
+ ```
612
+
613
+ ### Chart title
614
+ Do NOT add titles as HTML — they won't be part of the canvas or exports. Use `am5.Label` in the container BEFORE the chart, with `verticalLayout`:
615
+ ```js
616
+ root.container.children.push(am5.Label.new(root, {
617
+ text: "Chart Title",
618
+ fontSize: "1.3em",
619
+ x: am5.p50,
620
+ centerX: am5.p50
621
+ }));
622
+ root.container.set("layout", root.verticalLayout);
623
+
624
+ var chart = root.container.children.push(am5xy.XYChart.new(root, { ... }));
625
+ ```
626
+
627
+ ## Key differences from amCharts 4 (common AI mistakes)
628
+
629
+ | amCharts 4 (WRONG) | amCharts 5 (CORRECT) |
630
+ |---------------------|----------------------|
631
+ | `am4core.create("div", am4charts.XYChart)` | `am5.Root.new("div")` then `am5xy.XYChart.new(root, {})` |
632
+ | `new am4charts.CategoryAxis()` | `am5xy.CategoryAxis.new(root, { ... })` |
633
+ | `chart.data = [...]` | `series.data.setAll([...])` |
634
+ | `series.dataFields.valueY = "v"` | `valueYField: "v"` in `.new()` settings |
635
+ | `am4core.color("#f00")` | `am5.color(0xff0000)` or `am5.color("#f00")` |
636
+ | `new am4charts.Legend()` | `am5.Legend.new(root, {})` |
637
+ | `chart.cursor = new am4charts.XYCursor()` | `chart.set("cursor", am5xy.XYCursor.new(root, {}))` |
638
+ | SVG-based rendering | Canvas-based rendering |
639
+ | `am4core.useTheme(am4themes_animated)` | `root.setThemes([am5themes_Animated.new(root)])` |
640
+
641
+ ## Common pitfalls
642
+
643
+ 1. **CategoryAxis needs data too** — Unlike ValueAxis, CategoryAxis must receive data: `xAxis.data.setAll(data)` in addition to `series.data.setAll(data)`.
644
+
645
+ 2. **Date values must be timestamps** — DateAxis expects numbers (ms since epoch), not Date objects or strings. Use `new Date(...).getTime()`.
646
+
647
+ 3. **Renderer is required for every axis** — Always pass a `renderer` when creating an axis: `am5xy.AxisRendererX.new(root, {})`.
648
+
649
+ 4. **Don't forget `root` in `.new()` calls** — Every `.new()` call (except Root itself) takes `root` as the first arg.
650
+
651
+ 5. **Dispose via root** — To clean up, call `root.dispose()`, not `chart.dispose()`.
652
+
653
+ 6. **Colors** — Use `am5.color(0xFF0000)` or `am5.color("#ff0000")`. For color sets, access `chart.get("colors")`.
654
+
655
+ 7. **Percent values** — Use `am5.percent(50)` not plain `50` or `"50%"` when a setting expects a Percent value.
656
+
657
+ 8. **children.push()** — Adding elements to containers uses `.children.push()` which returns the element. Always capture the return value.
658
+
659
+ 9. **No CSS styling** — amCharts 5 renders to Canvas, not DOM/SVG. You cannot style chart elements with CSS. Use themes or `.set()`/`.setAll()`.
660
+
661
+ 10. **Hierarchy charts are chart-less** — Treemap, force-directed, etc. add series directly to `root.container` (or a wrapper Container), not to a chart object.
662
+
663
+ 11. **`dataItems` is a plain array** — `series.dataItems` does NOT have `.each()`. Use `series.dataItems.forEach(fn)` or `am5.array.each(series.dataItems, fn)`. Only `series.data` (the ListData object) has `.each()`.
664
+
665
+ 12. **CDN script load order matters** — `index.js` must load first, then `xy.js`, then any package that depends on it (`radar.js`, `timeline.js`, `gantt.js`). Wrong order causes runtime errors. Correct order: `index.js` → `xy.js` → `radar.js` / `timeline.js` / `gantt.js` → `themes/*.js`.
666
+
667
+ 13. **No `minorGrid` / `minorTicks` / `minorLabels` objects** — These properties don't exist. Enable minor grid via renderer flags:
668
+ ```js
669
+ am5xy.AxisRendererX.new(root, {
670
+ minorGridEnabled: true, // shows minor grid lines
671
+ minorLabelsEnabled: true // optional: shows minor labels
672
+ })
673
+ ```
674
+ Style minor elements through theme rules targeting the `"minor"` tag — they auto-inherit main grid appearance but slightly lighter.
675
+
676
+ 14. **Flow chart animated bullets go on `series.bullets`, NOT `series.links.template.bullets`** — To animate labels/circles flowing along Sankey/Chord links, use `series.bullets.push()`. Animate `bullet.locationX` (Sankey) or `bullet.locationY` (Chord) from 0→1 with `loops: Infinity`. Use an adapter on opacity based on position for fade effect:
677
+ ```js
678
+ // Sankey: use locationX. For Chord: replace ALL locationX with locationY.
679
+ series.bullets.push(function(root, series, dataItem) {
680
+ var label = am5.Label.new(root, {
681
+ text: "${value}B", populateText: true,
682
+ centerX: am5.p50, fill: am5.color(0xffffff)
683
+ });
684
+ var bullet = am5.Bullet.new(root, {
685
+ locationX: 0, sprite: label, autoRotate: true
686
+ });
687
+ bullet.animate({
688
+ key: "locationX", from: 0, to: 1,
689
+ duration: Math.random() * 10000 + 2000, loops: Infinity
690
+ });
691
+ label.adapters.add("opacity", function(opacity) {
692
+ return 0.5 - Math.abs(0.5 - bullet.get("locationX"));
693
+ });
694
+ bullet.on("locationX", function() {
695
+ label.set("opacity", label.get("opacity"));
696
+ });
697
+ return bullet;
698
+ });
699
+ ```
700
+
701
+ 15. **`MapPointSeries` needs `latitudeField`/`longitudeField` when using `data.setAll()`** — Without these settings, points silently won't appear:
702
+ ```js
703
+ // WRONG — points won't show
704
+ am5map.MapPointSeries.new(root, {})
705
+
706
+ // CORRECT — tell the series which fields hold coordinates
707
+ am5map.MapPointSeries.new(root, {
708
+ latitudeField: "latitude",
709
+ longitudeField: "longitude"
710
+ })
711
+ ```
712
+ This is NOT needed when using `pushDataItem({ latitude: ..., longitude: ... })` which passes coordinates directly to the data item.
713
+
714
+ 16. **`data.setAll()` does NOT animate — use `data.setIndex()` for animated updates** — `series.data.setAll(newData)` replaces everything instantly with no transition. To update data with smooth animation, use `series.data.setIndex(i, newItem)` per item:
715
+ ```js
716
+ // WRONG — no animation
717
+ series.data.setAll(newData);
718
+
719
+ // CORRECT — animates each value change
720
+ newData.forEach(function(item, i) {
721
+ series.data.setIndex(i, item);
722
+ });
723
+ ```
724
+
725
+ 17. **`color.lighten()` / `color.darken()` are NOT instance methods** — `am5.color(0xff0000).lighten(0.3)` does NOT work. Use static methods: `am5.Color.lighten(color, 0.3)` to lighten, `am5.Color.lighten(color, -0.3)` to darken. There is NO `darken()` method — use negative lighten. Also: `am5.Color.brighten()`, `am5.Color.saturate()`.
726
+
727
+ 18. **Venn diagram has no `VennDiagram` class** — `am5venn.Venn` is pushed directly into a `Container`, NOT into a chart's `series`. See references/venn.md.
728
+
729
+ 19. **Timeline `AxisRendererCurveX` requires `yRenderer`** — Always create the Y renderer first, then pass it:
730
+ ```js
731
+ var yRenderer = am5timeline.AxisRendererCurveY.new(root, {});
732
+ var xRenderer = am5timeline.AxisRendererCurveX.new(root, {
733
+ yRenderer: yRenderer
734
+ });
735
+ ```
736
+ Without this, crashes with `Cannot read properties of undefined (reading 'axis')`.
737
+
738
+ 20. **Gantt data uses TWO separate calls** — Do NOT use `chart.data.setAll()`. Set categories on `chart.yAxis.data.setAll([{id, name, parentId, color}])` and tasks on `chart.series.data.setAll([{id, start, duration, progress, linkTo}])`. Use flat `parentId` for hierarchy, NOT nested `children` arrays. CDN order: `index.js` → `xy.js` → `plugins/colorPicker.js` → `gantt.js` → `themes/Animated.js`:
739
+ ```js
740
+ // WRONG — single data call with nested children
741
+ chart.data.setAll([{ name: "Task", children: [...] }]);
742
+
743
+ // CORRECT — two separate calls with flat parentId hierarchy
744
+ chart.yAxis.data.setAll([
745
+ { id: "phase1", name: "Phase 1", color: am5.color(0x297373) },
746
+ { id: "task1", name: "Task 1", parentId: "phase1" }
747
+ ]);
748
+ chart.series.data.setAll([
749
+ { id: "phase1", start: timestamp, duration: 10, progress: 50 },
750
+ { id: "task1", start: timestamp, duration: 5, progress: 100, linkTo: ["phase1"] }
751
+ ]);
752
+ ```
753
+
754
+ 21. **No continent-level geodata at top-level CDN** — `geodata/europeLow.js` does NOT exist. Use `geodata/region/world/europeLow.js` (global: `am5geodata_region_world_europeLow`) or filter `worldLow` with `include: ["FR", "DE", "ES", ...]`.
755
+
756
+ 22. **Do not add chart titles as HTML** — HTML titles are outside the canvas and won't appear in exports. Use `am5.Label` pushed into the container BEFORE the chart, with `verticalLayout` on the container. See "Chart title" in Common components above.
757
+
758
+ 23. **`XYChartScrollbar` axes — never use `scrollbar.get("xAxis")`** — Axes passed to the scrollbar constructor are NOT stored as gettable settings. Create axes as separate variables, pass them to the scrollbar, and reuse those same variables for the scrollbar's inner series:
759
+ ```js
760
+ // WRONG — scrollbar.get("xAxis") returns undefined
761
+ var sbSeries = scrollbar.chart.series.push(am5xy.LineSeries.new(root, {
762
+ xAxis: scrollbar.get("xAxis"), // undefined!
763
+ yAxis: scrollbar.get("yAxis") // undefined!
764
+ }));
765
+
766
+ // CORRECT — reuse the axis variables
767
+ var sbxAxis = am5xy.DateAxis.new(root, { renderer: am5xy.AxisRendererX.new(root, {}) });
768
+ var sbyAxis = am5xy.ValueAxis.new(root, { renderer: am5xy.AxisRendererY.new(root, {}) });
769
+ var scrollbar = am5xy.XYChartScrollbar.new(root, { xAxis: sbxAxis, yAxis: sbyAxis });
770
+ var sbSeries = scrollbar.chart.series.push(am5xy.LineSeries.new(root, {
771
+ xAxis: sbxAxis, yAxis: sbyAxis, valueYField: "value", valueXField: "date"
772
+ }));
773
+ ```
774
+
775
+ 24. **`am5.color()` only accepts hex integers or CSS strings** — `am5.color(0xff0000)`, `am5.color("#ff0000")`, `am5.color("rgb(255,0,0)")` are valid. `am5.color({ r: 255, g: 0, b: 0 })` is NOT — it throws.
776
+
777
+ 25. **`MapChart` has NO `"colors"` setting** — `chart.get("colors")` returns `undefined` on `MapChart`. Only XY, Radar, and Percent charts auto-create a ColorSet. For maps, create your own: `am5.ColorSet.new(root, {})`.
778
+
779
+ 26. **`VoronoiTreemap` has NO `.rectangles` property** — Unlike `Treemap` (which has `series.rectangles.template`), `VoronoiTreemap` renders organic polygon cells. Style via `series.nodes.template` and its children, not `.rectangles`.
780
+ 27. **Labels with data placeholders need `populateText: true`** — When a Label uses data field placeholders like `text: "{name}"`, you MUST also set `populateText: true`. Without it, the placeholder is not resolved and the label appears blank. This applies everywhere Labels display dynamic data — bullet labels, map point labels, etc. Example: `am5.Label.new(root, { text: "{name}", populateText: true, ... })`.
781
+
782
+ ## Disposal (SPA / React / Angular / Vue)
783
+
784
+ **Always call `root.dispose()`** — this cleans up all children, series, and event listeners.
785
+
786
+ ```js
787
+ // React
788
+ useLayoutEffect(() => {
789
+ const root = am5.Root.new("chartdiv");
790
+ // ... build chart
791
+ return () => { root.dispose(); };
792
+ }, []);
793
+
794
+ // Angular
795
+ ngOnDestroy() { this.root?.dispose(); }
796
+
797
+ // Vue
798
+ onUnmounted(() => { root.dispose(); });
799
+ ```
800
+
801
+ ## Verify unfamiliar API before using it
802
+
803
+ If unsure whether a method, property, or setting exists, **verify it** before using it:
804
+
805
+ 1. **Primary:** `https://www.amcharts.com/docs/v5/reference/{classname}/` (lowercase, e.g. `LineSeries` → `lineseries`)
806
+ 2. **Fallback:** TypeScript source on GitHub — `https://raw.githubusercontent.com/amcharts/amcharts5/master/src/.internal/charts/` — settings interfaces are named `I{ClassName}Settings`
807
+
808
+ Common source paths:
809
+ - XY series: `charts/xy/series/{ClassName}.ts`
810
+ - XY axes: `charts/xy/axes/{ClassName}.ts`
811
+ - Pie: `charts/percent/pie/{ClassName}.ts`
812
+ - Radar: `charts/radar/{ClassName}.ts`
813
+ - Map: `charts/map/{ClassName}.ts`
814
+ - Core sprites: `core/render/{ClassName}.ts`
815
+
816
+ ## Documentation reference
817
+
818
+ Full docs: https://www.amcharts.com/docs/v5/
819
+ Demos: https://www.amcharts.com/demos/
820
+ Class reference: https://www.amcharts.com/docs/v5/reference/