@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,1150 @@
1
+ # XY Charts — Line, Area, Bar, Column, Candlestick, Scatter
2
+
3
+
4
+ ## Required imports
5
+
6
+ ### ES modules / TypeScript
7
+ ```ts
8
+ import * as am5 from "@amcharts/amcharts5";
9
+ import * as am5xy from "@amcharts/amcharts5/xy";
10
+ import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
11
+ ```
12
+
13
+ ### CDN / script tags
14
+ ```html
15
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
16
+ <script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
17
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
18
+ ```
19
+
20
+ ## Core setup pattern
21
+
22
+ Every XY chart follows this structure:
23
+
24
+ ```js
25
+ // 1. Root
26
+ const root = am5.Root.new("chartdiv");
27
+
28
+ // 2. Theme
29
+ root.setThemes([am5themes_Animated.new(root)]);
30
+
31
+ // 3. Chart
32
+ const chart = root.container.children.push(
33
+ am5xy.XYChart.new(root, { layout: root.verticalLayout })
34
+ );
35
+
36
+ // 4. Axes (X + Y)
37
+ const xAxis = chart.xAxes.push(/* axis */);
38
+ const yAxis = chart.yAxes.push(/* axis */);
39
+
40
+ // 5. Series
41
+ const series = chart.series.push(/* series */);
42
+
43
+ // 6. Optional: cursor, scrollbar, legend
44
+
45
+ // 7. Data — ALWAYS LAST
46
+ series.data.setAll(data);
47
+
48
+ // 8. Animate in
49
+ series.appear(1000);
50
+ chart.appear(1000, 100);
51
+ ```
52
+
53
+ ## Axis types
54
+
55
+ | Axis class | Use when | Data field prefix | Needs own data? |
56
+ |-----------|----------|-------------------|-----------------|
57
+ | `am5xy.CategoryAxis` | Discrete categories (product names, months) | `category` | **Yes** — `xAxis.data.setAll()` |
58
+ | `am5xy.DateAxis` | Time-based data | `value` (timestamps) | No |
59
+ | `am5xy.ValueAxis` | Numeric values | `value` | No |
60
+ | `am5xy.DurationAxis` | Duration values | `value` | No |
61
+ | `am5xy.GaplessDateAxis` | Time data without gaps | `value` (timestamps) | No |
62
+
63
+ ## Series types
64
+
65
+ | Series class | Use for |
66
+ |-------------|---------|
67
+ | `am5xy.LineSeries` | Line and area charts |
68
+ | `am5xy.ColumnSeries` | Vertical columns or horizontal bars |
69
+ | `am5xy.StepLineSeries` | Step/staircase lines |
70
+ | `am5xy.CandlestickSeries` | Financial candlesticks |
71
+ | `am5xy.OHLCSeries` | Financial OHLC |
72
+ | `am5xy.SmoothedXLineSeries` | Smoothed/curved lines (X smooth) |
73
+ | `am5xy.SmoothedXYLineSeries` | Smoothed lines (XY smooth) |
74
+
75
+ ## Data field naming convention
76
+
77
+ The field name combines the **value type** + **axis direction**:
78
+
79
+ - Category axis on X → `categoryXField: "category"`
80
+ - Value axis on Y → `valueYField: "value"`
81
+ - Date axis on X → `valueXField: "date"` (dates are numeric timestamps)
82
+ - Open/high/low/close → `openValueYField`, `highValueYField`, `lowValueYField`, `valueYField`
83
+
84
+ ## Common additions
85
+
86
+ ### Cursor
87
+ ```js
88
+ chart.set("cursor", am5xy.XYCursor.new(root, {
89
+ behavior: "none" // "zoomX", "zoomY", "zoomXY", "selectX", "selectXY"
90
+ }));
91
+ ```
92
+
93
+ ### Scrollbar
94
+ ```js
95
+ chart.set("scrollbarX", am5.Scrollbar.new(root, { orientation: "horizontal" }));
96
+ ```
97
+
98
+ ### Legend
99
+ ```js
100
+ const legend = chart.children.push(am5.Legend.new(root, {
101
+ centerX: am5.percent(50),
102
+ x: am5.percent(50)
103
+ }));
104
+ legend.data.setAll(chart.series.values);
105
+ ```
106
+
107
+ ### Tooltip on series
108
+ ```js
109
+ series.set("tooltip", am5.Tooltip.new(root, {
110
+ labelText: "{name}: {valueY}"
111
+ }));
112
+ ```
113
+
114
+ ### Stacking
115
+ ```js
116
+ // On each series:
117
+ am5xy.ColumnSeries.new(root, {
118
+ stacked: true,
119
+ // ... other settings
120
+ })
121
+ ```
122
+
123
+ ## Disposal (SPA frameworks)
124
+
125
+ Always dispose on component unmount:
126
+ ```js
127
+ root.dispose();
128
+ ```
129
+
130
+
131
+ ---
132
+
133
+ # amCharts 5 XY Charts — API Reference
134
+
135
+ Detailed reference for axes, series, and configuration options.
136
+ Full docs: https://www.amcharts.com/docs/v5/charts/xy-chart/
137
+
138
+ ## CategoryAxis
139
+
140
+ Used for discrete, non-numeric categories (product names, countries, months as labels).
141
+
142
+ ```js
143
+ const xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
144
+ categoryField: "category",
145
+ renderer: am5xy.AxisRendererX.new(root, {
146
+ minGridDistance: 30,
147
+ cellStartLocation: 0.1, // padding between columns
148
+ cellEndLocation: 0.9
149
+ }),
150
+ tooltip: am5.Tooltip.new(root, {})
151
+ }));
152
+
153
+ // CRITICAL: CategoryAxis MUST receive data separately
154
+ xAxis.data.setAll(data);
155
+ ```
156
+
157
+ **Key settings:**
158
+ - `categoryField` (required) — field name in data for categories
159
+ - `startLocation` / `endLocation` — 0 to 1, controls where axis starts/ends within first/last category
160
+ - `tooltip` — shows category under cursor
161
+
162
+ **Gotchas:**
163
+ - Category order in axis data and series data must match
164
+ - Forgetting `xAxis.data.setAll()` is the #1 mistake — the chart will render empty
165
+
166
+ ## DateAxis
167
+
168
+ Used for time-series data. Values must be **millisecond timestamps** (numbers), not Date objects.
169
+
170
+ ```js
171
+ const xAxis = chart.xAxes.push(am5xy.DateAxis.new(root, {
172
+ baseInterval: { timeUnit: "day", count: 1 },
173
+ renderer: am5xy.AxisRendererX.new(root, {
174
+ minGridDistance: 50
175
+ }),
176
+ tooltip: am5.Tooltip.new(root, {})
177
+ }));
178
+ ```
179
+
180
+ **Key settings:**
181
+ - `baseInterval` (required) — smallest data granularity: `{ timeUnit: "minute"|"hour"|"day"|"week"|"month"|"year", count: N }`
182
+ - `groupData: true` — automatically groups data when zoomed out (e.g., daily → monthly)
183
+ - `groupCount: 500` — target number of data items after grouping
184
+ - `groupIntervals` — array of allowed group intervals
185
+ - `markUnitChange: true` — highlights when time unit changes on axis labels (e.g., new month)
186
+ - `tooltipDateFormat` — format string for cursor tooltip
187
+
188
+ **Data format:**
189
+ ```js
190
+ series.data.setAll([
191
+ { date: new Date(2024, 0, 1).getTime(), value: 100 }, // ✅ timestamp
192
+ { date: 1704067200000, value: 120 }, // ✅ timestamp
193
+ { date: "2024-01-01", value: 100 }, // ❌ WRONG — string
194
+ { date: new Date(2024, 0, 1), value: 100 }, // ❌ WRONG — Date object
195
+ ]);
196
+ ```
197
+
198
+ ## ValueAxis
199
+
200
+ Used for numeric values. Auto-calculates min/max from data.
201
+
202
+ ```js
203
+ const yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
204
+ renderer: am5xy.AxisRendererY.new(root, {}),
205
+ // Optional:
206
+ min: 0, // force minimum
207
+ max: 100, // force maximum
208
+ strictMinMax: true, // don't let axis adjust beyond min/max
209
+ logarithmic: false, // log scale
210
+ treatZeroAs: 0.001, // when logarithmic, replace zeros
211
+ numberFormat: "#,###", // custom format
212
+ extraMax: 0.1, // add 10% padding above highest value
213
+ extraMin: 0.1, // add 10% padding below lowest value
214
+ calculateTotals: true, // needed for 100% stacked charts
215
+ maxPrecision: 0 // max decimal places in axis labels
216
+ }));
217
+ ```
218
+
219
+ ## AxisRendererX / AxisRendererY
220
+
221
+ Controls visual rendering of an axis. Every axis MUST have a renderer.
222
+
223
+ ```js
224
+ am5xy.AxisRendererX.new(root, {
225
+ minGridDistance: 30, // min pixels between grid lines
226
+ opposite: false, // true → show on top/right
227
+ inversed: false, // true → flip direction
228
+ inside: false, // true → labels inside plot area
229
+ cellStartLocation: 0, // 0–1, column start offset
230
+ cellEndLocation: 1, // 0–1, column end offset
231
+ pan: "none", // "zoom" → enable drag-zoom on axis
232
+ })
233
+ ```
234
+
235
+ **Configuring grid lines:**
236
+ ```js
237
+ const renderer = yAxis.get("renderer");
238
+ renderer.grid.template.setAll({
239
+ stroke: am5.color(0xcccccc),
240
+ strokeWidth: 1,
241
+ strokeDasharray: [3, 3]
242
+ });
243
+ ```
244
+
245
+ **Configuring axis labels:**
246
+ ```js
247
+ renderer.labels.template.setAll({
248
+ fontSize: 12,
249
+ fill: am5.color(0x666666),
250
+ rotation: -45, // rotate labels
251
+ centerY: am5.percent(50),
252
+ centerX: am5.percent(100)
253
+ });
254
+ ```
255
+
256
+ ## LineSeries
257
+
258
+ For line and area charts.
259
+
260
+ ```js
261
+ const series = chart.series.push(am5xy.LineSeries.new(root, {
262
+ name: "Revenue",
263
+ xAxis: xAxis,
264
+ yAxis: yAxis,
265
+ valueYField: "revenue",
266
+ valueXField: "date", // for DateAxis/ValueAxis on X
267
+ // OR categoryXField: "cat", // for CategoryAxis on X
268
+ tooltip: am5.Tooltip.new(root, { labelText: "{valueY}" }),
269
+ // Visual:
270
+ stroke: am5.color(0x095256),
271
+ fill: am5.color(0x095256),
272
+ // Stacking:
273
+ stacked: false, // true for stacked area
274
+ }));
275
+
276
+ // Make it an area chart:
277
+ series.fills.template.setAll({
278
+ visible: true,
279
+ fillOpacity: 0.3
280
+ });
281
+
282
+ // Stroke width:
283
+ series.strokes.template.setAll({
284
+ strokeWidth: 2
285
+ });
286
+ ```
287
+
288
+ **Line appearance:**
289
+ - `connect: true` — connect lines over missing data (default: true). Set `false` for line breaks at gaps
290
+ - `minDistance: 10` — min pixel distance between points before simplifying
291
+
292
+ ## ColumnSeries
293
+
294
+ For vertical columns or horizontal bars.
295
+
296
+ ```js
297
+ const series = chart.series.push(am5xy.ColumnSeries.new(root, {
298
+ name: "Sales",
299
+ xAxis: xAxis,
300
+ yAxis: yAxis,
301
+ valueYField: "sales",
302
+ categoryXField: "category", // or valueXField for scatter
303
+ tooltip: am5.Tooltip.new(root, { labelText: "{categoryX}: {valueY}" }),
304
+ stacked: false, // true for stacked columns
305
+ clustered: true, // false to overlap columns
306
+ }));
307
+
308
+ // Column appearance:
309
+ series.columns.template.setAll({
310
+ cornerRadiusTL: 5,
311
+ cornerRadiusTR: 5,
312
+ strokeOpacity: 0,
313
+ width: am5.percent(80), // column width
314
+ });
315
+
316
+ // Individual column colors from data:
317
+ series.columns.template.adapters.add("fill", (fill, target) => {
318
+ return chart.get("colors").getIndex(series.columns.indexOf(target));
319
+ });
320
+ ```
321
+
322
+ **Horizontal bar chart:** swap axes — put CategoryAxis on Y, ValueAxis on X:
323
+ ```js
324
+ const yAxis = chart.yAxes.push(am5xy.CategoryAxis.new(root, {
325
+ categoryField: "category",
326
+ renderer: am5xy.AxisRendererY.new(root, { inversed: true })
327
+ }));
328
+ const xAxis = chart.xAxes.push(am5xy.ValueAxis.new(root, {
329
+ renderer: am5xy.AxisRendererX.new(root, {})
330
+ }));
331
+ // Series uses categoryYField + valueXField
332
+ ```
333
+
334
+ ## CandlestickSeries
335
+
336
+ For financial OHLC candlestick charts. Requires 4 value fields.
337
+
338
+ ```js
339
+ const series = chart.series.push(am5xy.CandlestickSeries.new(root, {
340
+ name: "Price",
341
+ xAxis: xAxis,
342
+ yAxis: yAxis,
343
+ openValueYField: "open",
344
+ highValueYField: "high",
345
+ lowValueYField: "low",
346
+ valueYField: "close",
347
+ valueXField: "date"
348
+ }));
349
+ ```
350
+
351
+ ## XYCursor
352
+
353
+ Adds crosshair and enables tooltips on hover.
354
+
355
+ ```js
356
+ const cursor = chart.set("cursor", am5xy.XYCursor.new(root, {
357
+ behavior: "none", // "zoomX", "zoomY", "zoomXY", "selectX", "selectXY"
358
+ xAxis: xAxis, // optional: snap to axis
359
+ }));
360
+
361
+ // Customize appearance:
362
+ cursor.lineX.setAll({ stroke: am5.color(0x000000), strokeDasharray: [2, 2] });
363
+ cursor.lineY.setAll({ visible: false });
364
+
365
+ // Snap tooltips to series data points:
366
+ series.set("tooltip", am5.Tooltip.new(root, { labelText: "{valueY}" }));
367
+ chart.set("cursor", am5xy.XYCursor.new(root, { snapToSeries: [series] }));
368
+ ```
369
+
370
+ ## Scrollbar
371
+
372
+ Enables zooming/panning via scrollbar. Can optionally include a preview chart.
373
+
374
+ ```js
375
+ // Simple scrollbar:
376
+ chart.set("scrollbarX", am5.Scrollbar.new(root, {
377
+ orientation: "horizontal"
378
+ }));
379
+
380
+ // Scrollbar with preview chart:
381
+ const scrollbar = am5xy.XYChartScrollbar.new(root, {
382
+ orientation: "horizontal",
383
+ height: 50
384
+ });
385
+ chart.set("scrollbarX", scrollbar);
386
+
387
+ // Push axes into the scrollbar's internal chart
388
+ const sbxAxis = scrollbar.chart.xAxes.push(am5xy.DateAxis.new(root, {
389
+ baseInterval: { timeUnit: "day", count: 1 },
390
+ renderer: am5xy.AxisRendererX.new(root, {})
391
+ }));
392
+ const sbyAxis = scrollbar.chart.yAxes.push(am5xy.ValueAxis.new(root, {
393
+ renderer: am5xy.AxisRendererY.new(root, {})
394
+ }));
395
+
396
+ // Add a series to the scrollbar's chart for the preview
397
+ const sbSeries = scrollbar.chart.series.push(am5xy.LineSeries.new(root, {
398
+ xAxis: sbxAxis,
399
+ yAxis: sbyAxis,
400
+ valueYField: "value",
401
+ valueXField: "date"
402
+ }));
403
+ ```
404
+
405
+ ## Legend
406
+
407
+ ```js
408
+ const legend = chart.children.push(am5.Legend.new(root, {
409
+ centerX: am5.percent(50),
410
+ x: am5.percent(50),
411
+ layout: root.horizontalLayout // or root.verticalLayout, root.gridLayout
412
+ }));
413
+ legend.data.setAll(chart.series.values);
414
+ ```
415
+
416
+ **Dynamic legend labels (show values under cursor):**
417
+ ```js
418
+ const series = chart.series.push(am5xy.LineSeries.new(root, {
419
+ // ...
420
+ legendLabelText: "{name}",
421
+ legendValueText: "{valueY}",
422
+ legendRangeLabelText: "{name}",
423
+ legendRangeValueText: ""
424
+ }));
425
+ ```
426
+
427
+ ## Stacking
428
+
429
+ ```js
430
+ // Each series in the stack needs stacked: true
431
+ const series1 = chart.series.push(am5xy.ColumnSeries.new(root, {
432
+ stacked: true, name: "A", valueYField: "a", /* ... */
433
+ }));
434
+ const series2 = chart.series.push(am5xy.ColumnSeries.new(root, {
435
+ stacked: true, name: "B", valueYField: "b", /* ... */
436
+ }));
437
+
438
+ // For 100% stacked:
439
+ // 1. Set calculateTotals on value axis:
440
+ const yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
441
+ calculateTotals: true,
442
+ min: 0, max: 100,
443
+ renderer: am5xy.AxisRendererY.new(root, {})
444
+ }));
445
+ // 2. Use display fields on series:
446
+ am5xy.ColumnSeries.new(root, {
447
+ stacked: true,
448
+ valueYField: "a",
449
+ valueYShow: "valueYTotalPercent", // display as % of total
450
+ /* ... */
451
+ });
452
+ ```
453
+
454
+ ## Axis ranges (highlights / reference lines)
455
+
456
+ ```js
457
+ // Highlight a value range:
458
+ const rangeDataItem = yAxis.makeDataItem({ value: 50, endValue: 100 });
459
+ const range = yAxis.createAxisRange(rangeDataItem);
460
+ rangeDataItem.get("grid").setAll({ stroke: am5.color(0xff0000), strokeOpacity: 1 });
461
+ rangeDataItem.get("axisFill").setAll({
462
+ fill: am5.color(0xff0000),
463
+ fillOpacity: 0.1,
464
+ visible: true
465
+ });
466
+ rangeDataItem.get("label").setAll({ text: "Target", fill: am5.color(0xff0000) });
467
+ ```
468
+
469
+ ## Scatter / Bubble chart
470
+
471
+ Use `ValueAxis` on both X and Y with `LineSeries` (no connecting line) or `ColumnSeries`.
472
+
473
+ ```js
474
+ // Scatter: ValueAxis on both axes
475
+ const xAxis = chart.xAxes.push(am5xy.ValueAxis.new(root, {
476
+ renderer: am5xy.AxisRendererX.new(root, {}),
477
+ tooltip: am5.Tooltip.new(root, {})
478
+ }));
479
+ const yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
480
+ renderer: am5xy.AxisRendererY.new(root, {})
481
+ }));
482
+
483
+ const series = chart.series.push(am5xy.LineSeries.new(root, {
484
+ xAxis: xAxis,
485
+ yAxis: yAxis,
486
+ valueXField: "x",
487
+ valueYField: "y",
488
+ tooltip: am5.Tooltip.new(root, { labelText: "({valueX}, {valueY})" })
489
+ }));
490
+
491
+ // Hide connecting line (scatter, not line chart)
492
+ series.strokes.template.set("visible", false);
493
+
494
+ // Add circle bullets
495
+ series.bullets.push(function(root, series, dataItem) {
496
+ return am5.Bullet.new(root, {
497
+ sprite: am5.Circle.new(root, {
498
+ radius: 5,
499
+ fill: series.get("fill"),
500
+ fillOpacity: 0.7
501
+ })
502
+ });
503
+ });
504
+
505
+ // Bubble chart: variable-size bullets from data
506
+ series.bullets.push(function(root, series, dataItem) {
507
+ var size = dataItem.dataContext.size || 1;
508
+ return am5.Bullet.new(root, {
509
+ sprite: am5.Circle.new(root, {
510
+ radius: Math.sqrt(size) * 3,
511
+ fill: series.get("fill"),
512
+ fillOpacity: 0.5,
513
+ tooltipText: "{name}: ({valueX}, {valueY}) size: {size}"
514
+ })
515
+ });
516
+ });
517
+
518
+ series.data.setAll([
519
+ { x: 10, y: 20, size: 50, name: "A" },
520
+ { x: 30, y: 45, size: 120, name: "B" },
521
+ { x: 55, y: 15, size: 80, name: "C" }
522
+ ]);
523
+ ```
524
+
525
+ ## Bullets (data point markers)
526
+
527
+ ```js
528
+ // Add circle bullets to a line series
529
+ series.bullets.push(function(root, series, dataItem) {
530
+ return am5.Bullet.new(root, {
531
+ sprite: am5.Circle.new(root, {
532
+ radius: 4,
533
+ fill: series.get("fill"),
534
+ stroke: am5.color(0xffffff),
535
+ strokeWidth: 1
536
+ })
537
+ });
538
+ });
539
+
540
+ // Custom bullet (e.g., star, image, label)
541
+ series.bullets.push(function(root, series, dataItem) {
542
+ return am5.Bullet.new(root, {
543
+ sprite: am5.Label.new(root, {
544
+ text: "{valueY}",
545
+ centerX: am5.percent(50),
546
+ centerY: am5.percent(100),
547
+ populateText: true
548
+ })
549
+ });
550
+ });
551
+
552
+ // Auto-hide bullets when too close together
553
+ series.set("minBulletDistance", 20);
554
+ ```
555
+
556
+ ## Dual Y-axis (multiple value axes)
557
+
558
+ ```js
559
+ // First Y axis (left)
560
+ var yAxis1 = chart.yAxes.push(am5xy.ValueAxis.new(root, {
561
+ renderer: am5xy.AxisRendererY.new(root, {})
562
+ }));
563
+
564
+ // Second Y axis (right)
565
+ var yAxis2 = chart.yAxes.push(am5xy.ValueAxis.new(root, {
566
+ renderer: am5xy.AxisRendererY.new(root, { opposite: true })
567
+ }));
568
+
569
+ // Sync grid lines (optional)
570
+ yAxis2.set("syncWithAxis", yAxis1);
571
+
572
+ // Series on first axis
573
+ var series1 = chart.series.push(am5xy.LineSeries.new(root, {
574
+ name: "Revenue ($)",
575
+ xAxis: xAxis, yAxis: yAxis1,
576
+ valueYField: "revenue", valueXField: "date"
577
+ }));
578
+
579
+ // Series on second axis
580
+ var series2 = chart.series.push(am5xy.LineSeries.new(root, {
581
+ name: "Units Sold",
582
+ xAxis: xAxis, yAxis: yAxis2,
583
+ valueYField: "units", valueXField: "date"
584
+ }));
585
+ ```
586
+
587
+ ## Axis titles
588
+
589
+ ```js
590
+ yAxis.children.unshift(am5.Label.new(root, {
591
+ text: "Revenue ($)",
592
+ rotation: -90,
593
+ y: am5.percent(50),
594
+ centerX: am5.percent(50)
595
+ }));
596
+
597
+ xAxis.children.push(am5.Label.new(root, {
598
+ text: "Month",
599
+ x: am5.percent(50),
600
+ centerX: am5.percent(50)
601
+ }));
602
+ ```
603
+
604
+ ## Range columns / Waterfall (openValueYField)
605
+
606
+ ```js
607
+ // Range columns: each bar spans from openValueY to valueY
608
+ var series = chart.series.push(am5xy.ColumnSeries.new(root, {
609
+ xAxis: xAxis, yAxis: yAxis,
610
+ valueYField: "high",
611
+ openValueYField: "low", // bar starts from this value
612
+ categoryXField: "category"
613
+ }));
614
+
615
+ // Waterfall: cumulative bars using calculated open/close values
616
+ // Data should pre-calculate start/end values for each bar
617
+ ```
618
+
619
+ ## Events on XY elements
620
+
621
+ ```js
622
+ // Click on a column
623
+ series.columns.template.events.on("click", function(ev) {
624
+ var data = ev.target.dataItem.dataContext;
625
+ console.log("Clicked:", data);
626
+ });
627
+
628
+ // Hover on a data point (line series bullet)
629
+ series.bullets.push(function(root, series, dataItem) {
630
+ var circle = am5.Circle.new(root, { radius: 5, fill: series.get("fill") });
631
+ circle.events.on("pointerover", function() {
632
+ circle.set("scale", 1.5);
633
+ });
634
+ circle.events.on("pointerout", function() {
635
+ circle.set("scale", 1);
636
+ });
637
+ return am5.Bullet.new(root, { sprite: circle });
638
+ });
639
+
640
+ // Axis zoom/scroll event
641
+ xAxis.events.on("rangechanged", function() {
642
+ console.log("Axis range changed (zoomed/scrolled)");
643
+ });
644
+ ```
645
+
646
+ ## Chart settings reference
647
+
648
+ ```js
649
+ am5xy.XYChart.new(root, {
650
+ layout: root.verticalLayout, // how children (legend, chart) are laid out
651
+ panX: true, // allow horizontal panning
652
+ panY: false, // allow vertical panning
653
+ wheelX: "panX", // mouse wheel: "panX", "panY", "zoomX", "zoomY", "none"
654
+ wheelY: "zoomX", // mouse wheel vertical
655
+ pinchZoomX: true, // mobile pinch zoom on X
656
+ arrangeFields: false, // auto-arrange overlapping fields
657
+ maxTooltipDistance: 0, // show tooltips for nearby series (-1 = all)
658
+ })
659
+ ```
660
+
661
+ ---
662
+
663
+ # amCharts 5 XY Charts — Working Examples
664
+
665
+ Complete, tested examples ready to use. Each example is self-contained.
666
+
667
+ ## Example 1: Column chart with categories
668
+
669
+ A basic vertical bar chart with category axis.
670
+
671
+ ```html
672
+ <!DOCTYPE html>
673
+ <html>
674
+ <head>
675
+ <meta charset="UTF-8">
676
+ <title>Column Chart</title>
677
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
678
+ <script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
679
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
680
+ <style>
681
+ #chartdiv { width: 100%; height: 500px; }
682
+ </style>
683
+ </head>
684
+ <body>
685
+ <div id="chartdiv"></div>
686
+ <script>
687
+ // Create root
688
+ var root = am5.Root.new("chartdiv");
689
+
690
+ // Set theme
691
+ root.setThemes([am5themes_Animated.new(root)]);
692
+
693
+ // Create chart
694
+ var chart = root.container.children.push(am5xy.XYChart.new(root, {
695
+ panX: true,
696
+ panY: false,
697
+ wheelX: "panX",
698
+ wheelY: "zoomX",
699
+ layout: root.verticalLayout
700
+ }));
701
+
702
+ // Data
703
+ var data = [
704
+ { category: "Research", value: 1000 },
705
+ { category: "Marketing", value: 1200 },
706
+ { category: "Sales", value: 850 },
707
+ { category: "Support", value: 600 },
708
+ { category: "Development", value: 1500 }
709
+ ];
710
+
711
+ // Create axes
712
+ var xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
713
+ categoryField: "category",
714
+ renderer: am5xy.AxisRendererX.new(root, {
715
+ minGridDistance: 30,
716
+ cellStartLocation: 0.1,
717
+ cellEndLocation: 0.9
718
+ }),
719
+ tooltip: am5.Tooltip.new(root, {})
720
+ }));
721
+ xAxis.data.setAll(data); // CategoryAxis MUST receive data
722
+
723
+ var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
724
+ renderer: am5xy.AxisRendererY.new(root, {})
725
+ }));
726
+
727
+ // Create series
728
+ var series = chart.series.push(am5xy.ColumnSeries.new(root, {
729
+ name: "Revenue",
730
+ xAxis: xAxis,
731
+ yAxis: yAxis,
732
+ valueYField: "value",
733
+ categoryXField: "category",
734
+ tooltip: am5.Tooltip.new(root, {
735
+ labelText: "{categoryX}: ${valueY}"
736
+ })
737
+ }));
738
+
739
+ series.columns.template.setAll({
740
+ cornerRadiusTL: 5,
741
+ cornerRadiusTR: 5,
742
+ strokeOpacity: 0
743
+ });
744
+
745
+ // Assign individual colors from chart's color set
746
+ series.columns.template.adapters.add("fill", function (fill, target) {
747
+ return chart.get("colors").getIndex(series.columns.indexOf(target));
748
+ });
749
+ series.columns.template.adapters.add("stroke", function (stroke, target) {
750
+ return chart.get("colors").getIndex(series.columns.indexOf(target));
751
+ });
752
+
753
+ // Set data LAST
754
+ series.data.setAll(data);
755
+
756
+ // Animate
757
+ series.appear(1000);
758
+ chart.appear(1000, 100);
759
+ </script>
760
+ </body>
761
+ </html>
762
+ ```
763
+
764
+ ## Example 2: Multi-series line chart with date axis
765
+
766
+ A time-series line chart with two series, cursor, scrollbar, and legend.
767
+
768
+ ```html
769
+ <!DOCTYPE html>
770
+ <html>
771
+ <head>
772
+ <meta charset="UTF-8">
773
+ <title>Multi-Series Line Chart</title>
774
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
775
+ <script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
776
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
777
+ <style>
778
+ #chartdiv { width: 100%; height: 500px; }
779
+ </style>
780
+ </head>
781
+ <body>
782
+ <div id="chartdiv"></div>
783
+ <script>
784
+ var root = am5.Root.new("chartdiv");
785
+ root.setThemes([am5themes_Animated.new(root)]);
786
+
787
+ var chart = root.container.children.push(am5xy.XYChart.new(root, {
788
+ panX: true,
789
+ panY: false,
790
+ wheelX: "panX",
791
+ wheelY: "zoomX",
792
+ layout: root.verticalLayout
793
+ }));
794
+
795
+ // Generate sample date-based data
796
+ var data = [];
797
+ var date = new Date(2024, 0, 1);
798
+ var revenue = 100;
799
+ var expenses = 80;
800
+
801
+ for (var i = 0; i < 90; i++) {
802
+ revenue += Math.round((Math.random() - 0.48) * 10);
803
+ expenses += Math.round((Math.random() - 0.5) * 8);
804
+ data.push({
805
+ date: date.getTime(), // Must be timestamp!
806
+ revenue: revenue,
807
+ expenses: expenses
808
+ });
809
+ date.setDate(date.getDate() + 1);
810
+ }
811
+
812
+ // Axes
813
+ var xAxis = chart.xAxes.push(am5xy.DateAxis.new(root, {
814
+ baseInterval: { timeUnit: "day", count: 1 },
815
+ renderer: am5xy.AxisRendererX.new(root, { minGridDistance: 50 }),
816
+ tooltip: am5.Tooltip.new(root, {})
817
+ }));
818
+
819
+ var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
820
+ renderer: am5xy.AxisRendererY.new(root, {})
821
+ }));
822
+
823
+ // Helper function to create series
824
+ function createSeries(name, field) {
825
+ var series = chart.series.push(am5xy.LineSeries.new(root, {
826
+ name: name,
827
+ xAxis: xAxis,
828
+ yAxis: yAxis,
829
+ valueYField: field,
830
+ valueXField: "date",
831
+ tooltip: am5.Tooltip.new(root, {
832
+ labelText: "{name}: {valueY}"
833
+ })
834
+ }));
835
+
836
+ series.strokes.template.setAll({ strokeWidth: 2 });
837
+ series.data.setAll(data);
838
+ series.appear(1000);
839
+ return series;
840
+ }
841
+
842
+ createSeries("Revenue", "revenue");
843
+ createSeries("Expenses", "expenses");
844
+
845
+ // Cursor
846
+ chart.set("cursor", am5xy.XYCursor.new(root, {
847
+ behavior: "none"
848
+ }));
849
+
850
+ // Scrollbar
851
+ chart.set("scrollbarX", am5.Scrollbar.new(root, {
852
+ orientation: "horizontal"
853
+ }));
854
+
855
+ // Legend
856
+ var legend = chart.children.push(am5.Legend.new(root, {
857
+ centerX: am5.percent(50),
858
+ x: am5.percent(50)
859
+ }));
860
+ legend.data.setAll(chart.series.values);
861
+
862
+ chart.appear(1000, 100);
863
+ </script>
864
+ </body>
865
+ </html>
866
+ ```
867
+
868
+ ## Example 3: Stacked area chart
869
+
870
+ Area chart with three stacked series.
871
+
872
+ ```html
873
+ <!DOCTYPE html>
874
+ <html>
875
+ <head>
876
+ <meta charset="UTF-8">
877
+ <title>Stacked Area Chart</title>
878
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
879
+ <script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
880
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
881
+ <style>
882
+ #chartdiv { width: 100%; height: 500px; }
883
+ </style>
884
+ </head>
885
+ <body>
886
+ <div id="chartdiv"></div>
887
+ <script>
888
+ var root = am5.Root.new("chartdiv");
889
+ root.setThemes([am5themes_Animated.new(root)]);
890
+
891
+ var chart = root.container.children.push(am5xy.XYChart.new(root, {
892
+ panX: true,
893
+ panY: false,
894
+ wheelX: "panX",
895
+ wheelY: "zoomX",
896
+ layout: root.verticalLayout
897
+ }));
898
+
899
+ var data = [
900
+ { year: "2019", mobile: 250, desktop: 580, tablet: 120 },
901
+ { year: "2020", mobile: 300, desktop: 550, tablet: 150 },
902
+ { year: "2021", mobile: 380, desktop: 500, tablet: 170 },
903
+ { year: "2022", mobile: 450, desktop: 460, tablet: 190 },
904
+ { year: "2023", mobile: 520, desktop: 420, tablet: 200 },
905
+ { year: "2024", mobile: 600, desktop: 380, tablet: 210 }
906
+ ];
907
+
908
+ var xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
909
+ categoryField: "year",
910
+ renderer: am5xy.AxisRendererX.new(root, {}),
911
+ tooltip: am5.Tooltip.new(root, {})
912
+ }));
913
+ xAxis.data.setAll(data);
914
+
915
+ var yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
916
+ renderer: am5xy.AxisRendererY.new(root, {})
917
+ }));
918
+
919
+ function createSeries(name, field) {
920
+ var series = chart.series.push(am5xy.LineSeries.new(root, {
921
+ name: name,
922
+ xAxis: xAxis,
923
+ yAxis: yAxis,
924
+ stacked: true,
925
+ valueYField: field,
926
+ categoryXField: "year",
927
+ tooltip: am5.Tooltip.new(root, {
928
+ labelText: "{name}: {valueY}"
929
+ })
930
+ }));
931
+
932
+ // Enable area fill
933
+ series.fills.template.setAll({
934
+ visible: true,
935
+ fillOpacity: 0.5
936
+ });
937
+
938
+ series.strokes.template.setAll({ strokeWidth: 2 });
939
+ series.data.setAll(data);
940
+ series.appear(1000);
941
+ return series;
942
+ }
943
+
944
+ createSeries("Mobile", "mobile");
945
+ createSeries("Desktop", "desktop");
946
+ createSeries("Tablet", "tablet");
947
+
948
+ // Cursor
949
+ chart.set("cursor", am5xy.XYCursor.new(root, {}));
950
+
951
+ // Legend
952
+ var legend = chart.children.push(am5.Legend.new(root, {
953
+ centerX: am5.percent(50),
954
+ x: am5.percent(50)
955
+ }));
956
+ legend.data.setAll(chart.series.values);
957
+
958
+ chart.appear(1000, 100);
959
+ </script>
960
+ </body>
961
+ </html>
962
+ ```
963
+
964
+ ## Example 4: Horizontal bar chart
965
+
966
+ Bars extending horizontally — swap category to Y axis.
967
+
968
+ ```html
969
+ <!DOCTYPE html>
970
+ <html>
971
+ <head>
972
+ <meta charset="UTF-8">
973
+ <title>Horizontal Bar Chart</title>
974
+ <script src="https://cdn.amcharts.com/lib/5/index.js"></script>
975
+ <script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
976
+ <script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
977
+ <style>
978
+ #chartdiv { width: 100%; height: 500px; }
979
+ </style>
980
+ </head>
981
+ <body>
982
+ <div id="chartdiv"></div>
983
+ <script>
984
+ var root = am5.Root.new("chartdiv");
985
+ root.setThemes([am5themes_Animated.new(root)]);
986
+
987
+ var chart = root.container.children.push(am5xy.XYChart.new(root, {
988
+ panX: false,
989
+ panY: true,
990
+ wheelX: "none",
991
+ wheelY: "panY",
992
+ layout: root.verticalLayout
993
+ }));
994
+
995
+ var data = [
996
+ { country: "United States", visits: 4025 },
997
+ { country: "China", visits: 1882 },
998
+ { country: "Japan", visits: 1809 },
999
+ { country: "Germany", visits: 1322 },
1000
+ { country: "United Kingdom", visits: 1122 },
1001
+ { country: "France", visits: 1114 },
1002
+ { country: "India", visits: 984 },
1003
+ { country: "Spain", visits: 711 }
1004
+ ];
1005
+
1006
+ // Note: CategoryAxis on Y, ValueAxis on X
1007
+ var yAxis = chart.yAxes.push(am5xy.CategoryAxis.new(root, {
1008
+ categoryField: "country",
1009
+ renderer: am5xy.AxisRendererY.new(root, {
1010
+ inversed: true, // top-to-bottom order
1011
+ cellStartLocation: 0.1,
1012
+ cellEndLocation: 0.9
1013
+ })
1014
+ }));
1015
+ yAxis.data.setAll(data);
1016
+
1017
+ var xAxis = chart.xAxes.push(am5xy.ValueAxis.new(root, {
1018
+ renderer: am5xy.AxisRendererX.new(root, {}),
1019
+ min: 0
1020
+ }));
1021
+
1022
+ // Note: categoryYField + valueXField (swapped from vertical)
1023
+ var series = chart.series.push(am5xy.ColumnSeries.new(root, {
1024
+ name: "Visits",
1025
+ xAxis: xAxis,
1026
+ yAxis: yAxis,
1027
+ valueXField: "visits",
1028
+ categoryYField: "country",
1029
+ tooltip: am5.Tooltip.new(root, {
1030
+ labelText: "{categoryY}: {valueX}"
1031
+ })
1032
+ }));
1033
+
1034
+ series.columns.template.setAll({
1035
+ cornerRadiusBR: 5,
1036
+ cornerRadiusTR: 5,
1037
+ strokeOpacity: 0
1038
+ });
1039
+
1040
+ series.columns.template.adapters.add("fill", function (fill, target) {
1041
+ return chart.get("colors").getIndex(series.columns.indexOf(target));
1042
+ });
1043
+
1044
+ series.data.setAll(data);
1045
+ series.appear(1000);
1046
+ chart.appear(1000, 100);
1047
+ </script>
1048
+ </body>
1049
+ </html>
1050
+ ```
1051
+
1052
+ ## Example 5: TypeScript + ES modules (framework-ready)
1053
+
1054
+ Suitable for React, Angular, Vue, or any bundled project.
1055
+
1056
+ ```ts
1057
+ import * as am5 from "@amcharts/amcharts5";
1058
+ import * as am5xy from "@amcharts/amcharts5/xy";
1059
+ import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
1060
+
1061
+ export function createChart(elementId: string): am5.Root {
1062
+ const root = am5.Root.new(elementId);
1063
+ root.setThemes([am5themes_Animated.new(root)]);
1064
+
1065
+ const chart = root.container.children.push(
1066
+ am5xy.XYChart.new(root, {
1067
+ panX: true,
1068
+ panY: false,
1069
+ wheelX: "panX",
1070
+ wheelY: "zoomX",
1071
+ layout: root.verticalLayout,
1072
+ })
1073
+ );
1074
+
1075
+ const xAxis = chart.xAxes.push(
1076
+ am5xy.DateAxis.new(root, {
1077
+ baseInterval: { timeUnit: "month", count: 1 },
1078
+ renderer: am5xy.AxisRendererX.new(root, { minGridDistance: 50 }),
1079
+ tooltip: am5.Tooltip.new(root, {}),
1080
+ })
1081
+ );
1082
+
1083
+ const yAxis = chart.yAxes.push(
1084
+ am5xy.ValueAxis.new(root, {
1085
+ renderer: am5xy.AxisRendererY.new(root, {}),
1086
+ })
1087
+ );
1088
+
1089
+ const series = chart.series.push(
1090
+ am5xy.LineSeries.new(root, {
1091
+ name: "Series 1",
1092
+ xAxis: xAxis,
1093
+ yAxis: yAxis,
1094
+ valueYField: "value",
1095
+ valueXField: "date",
1096
+ tooltip: am5.Tooltip.new(root, { labelText: "{valueY}" }),
1097
+ })
1098
+ );
1099
+
1100
+ series.fills.template.setAll({ visible: true, fillOpacity: 0.2 });
1101
+
1102
+ chart.set("cursor", am5xy.XYCursor.new(root, { behavior: "none" }));
1103
+ chart.set("scrollbarX", am5.Scrollbar.new(root, { orientation: "horizontal" }));
1104
+
1105
+ const legend = chart.children.push(
1106
+ am5.Legend.new(root, {
1107
+ centerX: am5.percent(50),
1108
+ x: am5.percent(50),
1109
+ })
1110
+ );
1111
+ legend.data.setAll(chart.series.values);
1112
+
1113
+ // Set data
1114
+ const data: Array<{ date: number; value: number }> = [];
1115
+ let value = 100;
1116
+ for (let i = 0; i < 24; i++) {
1117
+ value += Math.round((Math.random() - 0.45) * 20);
1118
+ data.push({
1119
+ date: new Date(2023, i, 1).getTime(),
1120
+ value: value,
1121
+ });
1122
+ }
1123
+
1124
+ series.data.setAll(data);
1125
+ series.appear(1000);
1126
+ chart.appear(1000, 100);
1127
+
1128
+ // Return root so caller can dispose: root.dispose()
1129
+ return root;
1130
+ }
1131
+ ```
1132
+
1133
+ **React usage:**
1134
+ ```tsx
1135
+ import { useLayoutEffect, useRef } from "react";
1136
+ import { createChart } from "./chart";
1137
+
1138
+ export function ChartComponent() {
1139
+ const chartRef = useRef<am5.Root | null>(null);
1140
+
1141
+ useLayoutEffect(() => {
1142
+ chartRef.current = createChart("chartdiv");
1143
+ return () => {
1144
+ chartRef.current?.dispose();
1145
+ };
1146
+ }, []);
1147
+
1148
+ return <div id="chartdiv" style={{ width: "100%", height: "500px" }} />;
1149
+ }
1150
+ ```