chartforge 0.0.1 → 0.0.2

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.
Files changed (94) hide show
  1. package/dist/chartforge.js +1148 -0
  2. package/dist/chartforge.umd.cjs +1 -0
  3. package/dist/index-BCodEFNd.js +1060 -0
  4. package/dist/plugins.js +14 -0
  5. package/dist/themes.js +44 -0
  6. package/dist/types/ChartForge.d.ts +50 -0
  7. package/dist/types/ChartForge.d.ts.map +1 -0
  8. package/dist/types/adapters/PollingAdapter.d.ts +18 -0
  9. package/dist/types/adapters/PollingAdapter.d.ts.map +1 -0
  10. package/dist/types/adapters/RealTimeModule.d.ts +14 -0
  11. package/dist/types/adapters/RealTimeModule.d.ts.map +1 -0
  12. package/dist/types/adapters/WebSocketAdapter.d.ts +17 -0
  13. package/dist/types/adapters/WebSocketAdapter.d.ts.map +1 -0
  14. package/dist/types/adapters/index.d.ts +4 -0
  15. package/dist/types/adapters/index.d.ts.map +1 -0
  16. package/dist/types/core/AnimationEngine.d.ts +8 -0
  17. package/dist/types/core/AnimationEngine.d.ts.map +1 -0
  18. package/dist/types/core/DataPipeline.d.ts +10 -0
  19. package/dist/types/core/DataPipeline.d.ts.map +1 -0
  20. package/dist/types/core/EventBus.d.ts +10 -0
  21. package/dist/types/core/EventBus.d.ts.map +1 -0
  22. package/dist/types/core/MiddlewarePipeline.d.ts +7 -0
  23. package/dist/types/core/MiddlewarePipeline.d.ts.map +1 -0
  24. package/dist/types/core/PluginManager.d.ts +15 -0
  25. package/dist/types/core/PluginManager.d.ts.map +1 -0
  26. package/dist/types/core/ThemeManager.d.ts +11 -0
  27. package/dist/types/core/ThemeManager.d.ts.map +1 -0
  28. package/dist/types/core/VirtualRenderer.d.ts +18 -0
  29. package/dist/types/core/VirtualRenderer.d.ts.map +1 -0
  30. package/dist/types/core/index.d.ts +8 -0
  31. package/dist/types/core/index.d.ts.map +1 -0
  32. package/dist/types/index.d.ts +9 -0
  33. package/dist/types/index.d.ts.map +1 -0
  34. package/dist/types/plugins/AnnotationPlugin.d.ts +42 -0
  35. package/dist/types/plugins/AnnotationPlugin.d.ts.map +1 -0
  36. package/dist/types/plugins/AxisPlugin.d.ts +27 -0
  37. package/dist/types/plugins/AxisPlugin.d.ts.map +1 -0
  38. package/dist/types/plugins/BasePlugin.d.ts +10 -0
  39. package/dist/types/plugins/BasePlugin.d.ts.map +1 -0
  40. package/dist/types/plugins/CrosshairPlugin.d.ts +29 -0
  41. package/dist/types/plugins/CrosshairPlugin.d.ts.map +1 -0
  42. package/dist/types/plugins/DataLabelsPlugin.d.ts +19 -0
  43. package/dist/types/plugins/DataLabelsPlugin.d.ts.map +1 -0
  44. package/dist/types/plugins/ExportPlugin.d.ts +20 -0
  45. package/dist/types/plugins/ExportPlugin.d.ts.map +1 -0
  46. package/dist/types/plugins/GridPlugin.d.ts +26 -0
  47. package/dist/types/plugins/GridPlugin.d.ts.map +1 -0
  48. package/dist/types/plugins/LegendPlugin.d.ts +26 -0
  49. package/dist/types/plugins/LegendPlugin.d.ts.map +1 -0
  50. package/dist/types/plugins/TooltipPlugin.d.ts +34 -0
  51. package/dist/types/plugins/TooltipPlugin.d.ts.map +1 -0
  52. package/dist/types/plugins/ZoomPlugin.d.ts +24 -0
  53. package/dist/types/plugins/ZoomPlugin.d.ts.map +1 -0
  54. package/dist/types/plugins/index.d.ts +37 -0
  55. package/dist/types/plugins/index.d.ts.map +1 -0
  56. package/dist/types/renderers/BarRenderer.d.ts +5 -0
  57. package/dist/types/renderers/BarRenderer.d.ts.map +1 -0
  58. package/dist/types/renderers/BaseRenderer.d.ts +31 -0
  59. package/dist/types/renderers/BaseRenderer.d.ts.map +1 -0
  60. package/dist/types/renderers/CandlestickRenderer.d.ts +5 -0
  61. package/dist/types/renderers/CandlestickRenderer.d.ts.map +1 -0
  62. package/dist/types/renderers/ColumnRenderer.d.ts +5 -0
  63. package/dist/types/renderers/ColumnRenderer.d.ts.map +1 -0
  64. package/dist/types/renderers/DonutRenderer.d.ts +5 -0
  65. package/dist/types/renderers/DonutRenderer.d.ts.map +1 -0
  66. package/dist/types/renderers/FunnelRenderer.d.ts +5 -0
  67. package/dist/types/renderers/FunnelRenderer.d.ts.map +1 -0
  68. package/dist/types/renderers/HeatmapRenderer.d.ts +5 -0
  69. package/dist/types/renderers/HeatmapRenderer.d.ts.map +1 -0
  70. package/dist/types/renderers/LineRenderer.d.ts +5 -0
  71. package/dist/types/renderers/LineRenderer.d.ts.map +1 -0
  72. package/dist/types/renderers/PieRenderer.d.ts +8 -0
  73. package/dist/types/renderers/PieRenderer.d.ts.map +1 -0
  74. package/dist/types/renderers/ScatterRenderer.d.ts +5 -0
  75. package/dist/types/renderers/ScatterRenderer.d.ts.map +1 -0
  76. package/dist/types/renderers/StackedBarRenderer.d.ts +5 -0
  77. package/dist/types/renderers/StackedBarRenderer.d.ts.map +1 -0
  78. package/dist/types/renderers/StackedColumnRenderer.d.ts +5 -0
  79. package/dist/types/renderers/StackedColumnRenderer.d.ts.map +1 -0
  80. package/dist/types/renderers/index.d.ts +17 -0
  81. package/dist/types/renderers/index.d.ts.map +1 -0
  82. package/dist/types/themes/builtins.d.ts +6 -0
  83. package/dist/types/themes/builtins.d.ts.map +1 -0
  84. package/dist/types/themes/index.d.ts +2 -0
  85. package/dist/types/themes/index.d.ts.map +1 -0
  86. package/dist/types/types.d.ts +97 -0
  87. package/dist/types/types.d.ts.map +1 -0
  88. package/dist/types/utils/dom.d.ts +9 -0
  89. package/dist/types/utils/dom.d.ts.map +1 -0
  90. package/dist/types/utils/index.d.ts +3 -0
  91. package/dist/types/utils/index.d.ts.map +1 -0
  92. package/dist/types/utils/misc.d.ts +12 -0
  93. package/dist/types/utils/misc.d.ts.map +1 -0
  94. package/package.json +10 -8
@@ -0,0 +1,1060 @@
1
+ function createSVGElement(tag, attrs = {}) {
2
+ const el = document.createElementNS("http://www.w3.org/2000/svg", tag);
3
+ for (const [key, value] of Object.entries(attrs)) {
4
+ if (key === "className") {
5
+ el.setAttribute("class", String(value));
6
+ } else if (key.startsWith("on") && typeof value === "function") {
7
+ el.addEventListener(key.slice(2).toLowerCase(), value);
8
+ } else {
9
+ el.setAttribute(key, String(value));
10
+ }
11
+ }
12
+ return el;
13
+ }
14
+ function removeChildren(el) {
15
+ while (el.firstChild) el.removeChild(el.firstChild);
16
+ }
17
+ function polarToCartesian(cx, cy, r, angleDeg) {
18
+ const rad = (angleDeg - 90) * Math.PI / 180;
19
+ return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
20
+ }
21
+ function describeArc(x, y, r, startAngle, endAngle) {
22
+ const start = polarToCartesian(x, y, r, endAngle);
23
+ const end = polarToCartesian(x, y, r, startAngle);
24
+ const large = endAngle - startAngle <= 180 ? "0" : "1";
25
+ return `M ${start.x} ${start.y} A ${r} ${r} 0 ${large} 0 ${end.x} ${end.y}`;
26
+ }
27
+ function uid() {
28
+ return `cf-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
29
+ }
30
+ function clamp(v, min, max) {
31
+ return Math.min(Math.max(v, min), max);
32
+ }
33
+ function debounce(fn, delay) {
34
+ let id;
35
+ return (...args) => {
36
+ clearTimeout(id);
37
+ id = setTimeout(() => fn(...args), delay);
38
+ };
39
+ }
40
+ function throttle(fn, limit) {
41
+ let busy = false;
42
+ return (...args) => {
43
+ if (!busy) {
44
+ fn(...args);
45
+ busy = true;
46
+ setTimeout(() => busy = false, limit);
47
+ }
48
+ };
49
+ }
50
+ function merge(target, ...sources) {
51
+ for (const src of sources) {
52
+ if (!src) continue;
53
+ for (const key of Object.keys(src)) {
54
+ const sv = src[key];
55
+ const tv = target[key];
56
+ if (sv && typeof sv === "object" && !Array.isArray(sv) && tv && typeof tv === "object" && !Array.isArray(tv)) {
57
+ merge(tv, sv);
58
+ } else if (sv !== void 0) {
59
+ target[key] = sv;
60
+ }
61
+ }
62
+ }
63
+ return target;
64
+ }
65
+ function flatMax(series) {
66
+ return Math.max(...series.flatMap((s) => s.data));
67
+ }
68
+ function flatMin(series) {
69
+ return Math.min(...series.flatMap((s) => s.data));
70
+ }
71
+ class BasePlugin {
72
+ constructor(_chart, _cfg) {
73
+ this._chart = _chart;
74
+ this._cfg = _cfg;
75
+ this._els = [];
76
+ }
77
+ destroy() {
78
+ this._els.forEach((el) => el.parentNode?.removeChild(el));
79
+ this._els.length = 0;
80
+ }
81
+ }
82
+ class TooltipPlugin extends BasePlugin {
83
+ constructor(chart, cfg = {}) {
84
+ super(chart, cfg);
85
+ this._mx = 0;
86
+ this._my = 0;
87
+ this._visible = false;
88
+ const c = chart;
89
+ const { formatter, ...rest } = cfg;
90
+ this._opts = {
91
+ ...merge({
92
+ enabled: true,
93
+ backgroundColor: c.theme.tooltip.background,
94
+ textColor: c.theme.tooltip.text,
95
+ borderColor: c.theme.tooltip.border,
96
+ borderRadius: 6,
97
+ padding: 10,
98
+ fontSize: 13,
99
+ shadow: true,
100
+ followCursor: true,
101
+ offset: { x: 14, y: 14 }
102
+ }, rest),
103
+ formatter
104
+ };
105
+ }
106
+ init() {
107
+ if (!this._opts.enabled) return;
108
+ this._createTip();
109
+ this._attachEvents();
110
+ }
111
+ _createTip() {
112
+ this._tip = document.createElement("div");
113
+ this._tip.className = "cf-tooltip";
114
+ const o = this._opts;
115
+ const shadow = o.shadow ? "box-shadow:0 6px 20px rgba(0,0,0,.22)" : "";
116
+ this._tip.style.cssText = [
117
+ "position:fixed",
118
+ `padding:${o.padding}px ${o.padding + 4}px`,
119
+ `background:${o.backgroundColor}`,
120
+ `color:${o.textColor}`,
121
+ `border:1px solid ${o.borderColor}`,
122
+ `border-radius:${o.borderRadius}px`,
123
+ `font-size:${o.fontSize}px`,
124
+ "line-height:1.5",
125
+ "font-family:inherit",
126
+ "pointer-events:none",
127
+ "opacity:0",
128
+ "transform:scale(0.95)",
129
+ "transition:opacity .12s ease,transform .12s ease",
130
+ "z-index:99999",
131
+ "white-space:nowrap",
132
+ "max-width:260px",
133
+ shadow,
134
+ "top:0",
135
+ "left:0"
136
+ ].filter(Boolean).join(";");
137
+ document.body.appendChild(this._tip);
138
+ this._els.push(this._tip);
139
+ }
140
+ _attachEvents() {
141
+ const c = this._chart;
142
+ c.svg.addEventListener("mousemove", (e) => {
143
+ const me = e;
144
+ this._mx = me.clientX;
145
+ this._my = me.clientY;
146
+ if (this._visible) this._position();
147
+ });
148
+ c.on("hover", (raw) => {
149
+ const data = raw;
150
+ this._buildContent(data);
151
+ this._position();
152
+ this._show();
153
+ });
154
+ c.svg.addEventListener("mouseleave", () => this._hide());
155
+ }
156
+ _buildContent(d) {
157
+ const c = this._chart;
158
+ if (this._opts.formatter) {
159
+ this._tip.innerHTML = this._opts.formatter(d);
160
+ return;
161
+ }
162
+ const lbl = (i) => c.config.data.labels?.[i] ?? `Item ${i + 1}`;
163
+ const sName = (i) => c.config.data.series[i]?.name ?? `Series ${i + 1}`;
164
+ const fmt = (v) => typeof v === "number" ? v.toLocaleString() : String(v);
165
+ const row = (label, value) => `<div style="display:flex;justify-content:space-between;gap:12px"><span style="opacity:.7">${label}</span><span style="font-weight:600">${fmt(value)}</span></div>`;
166
+ let html = "";
167
+ const type = d["type"];
168
+ switch (type) {
169
+ case "pie":
170
+ case "donut": {
171
+ const idx = d["index"];
172
+ html = `<div style="font-weight:700;margin-bottom:4px">${lbl(idx)}</div>${row("Value", d["value"])}`;
173
+ break;
174
+ }
175
+ case "column":
176
+ case "bar": {
177
+ const idx = d["index"];
178
+ html = `<div style="font-weight:700;margin-bottom:4px">${lbl(idx)}</div>${row("Value", d["value"])}`;
179
+ break;
180
+ }
181
+ case "line": {
182
+ const si = d["seriesIndex"];
183
+ const idx = d["index"];
184
+ html = `<div style="font-weight:700;margin-bottom:4px">${sName(si)}</div>${row(lbl(idx), d["value"])}`;
185
+ break;
186
+ }
187
+ case "scatter": {
188
+ const pt = d["point"];
189
+ const si = d["seriesIndex"];
190
+ html = `<div style="font-weight:700;margin-bottom:4px">${sName(si)}</div>${row("X", pt.x)}${row("Y", pt.y)}${pt.r !== void 0 ? row("R", pt.r) : ""}`;
191
+ break;
192
+ }
193
+ case "heatmap": {
194
+ html = `<div style="font-weight:700;margin-bottom:4px">Cell [${d["row"]}, ${d["col"]}]</div>${row("Value", d["value"])}`;
195
+ break;
196
+ }
197
+ case "candlestick": {
198
+ const cc = d["candle"];
199
+ const idx = d["index"];
200
+ const chg = cc.close - cc.open;
201
+ const pct = (chg / cc.open * 100).toFixed(2);
202
+ const clr = chg >= 0 ? "#10b981" : "#ef4444";
203
+ html = `<div style="font-weight:700;margin-bottom:4px">${lbl(idx)}</div>` + row("Open", cc.open) + row("High", cc.high) + row("Low", cc.low) + row("Close", cc.close) + `<div style="color:${clr};font-weight:700;margin-top:4px">${chg >= 0 ? "▲" : "▼"} ${Math.abs(chg).toLocaleString()} (${pct}%)</div>`;
204
+ break;
205
+ }
206
+ case "stackedColumn":
207
+ case "stackedBar": {
208
+ const si = d["seriesIndex"];
209
+ const ci = d["catIndex"];
210
+ html = `<div style="font-weight:700;margin-bottom:4px">${sName(si)}</div>${row(lbl(ci), d["value"])}`;
211
+ break;
212
+ }
213
+ case "funnel": {
214
+ const idx = d["index"];
215
+ html = `<div style="font-weight:700;margin-bottom:4px">${lbl(idx)}</div>${row("Value", d["value"])}`;
216
+ break;
217
+ }
218
+ default:
219
+ html = `<div>${fmt(d["value"] ?? JSON.stringify(d))}</div>`;
220
+ }
221
+ this._tip.innerHTML = html;
222
+ }
223
+ _position() {
224
+ const rect = this._tip.getBoundingClientRect();
225
+ const ox = this._opts.offset.x;
226
+ const oy = this._opts.offset.y;
227
+ let left = this._mx + ox;
228
+ let top = this._my + oy;
229
+ if (left + rect.width > window.innerWidth - 8) left = this._mx - rect.width - ox;
230
+ if (top + rect.height > window.innerHeight - 8) top = this._my - rect.height - oy;
231
+ if (left < 8) left = 8;
232
+ if (top < 8) top = 8;
233
+ this._tip.style.left = `${left}px`;
234
+ this._tip.style.top = `${top}px`;
235
+ }
236
+ _show() {
237
+ this._visible = true;
238
+ this._tip.style.opacity = "1";
239
+ this._tip.style.transform = "scale(1)";
240
+ }
241
+ _hide() {
242
+ this._visible = false;
243
+ this._tip.style.opacity = "0";
244
+ this._tip.style.transform = "scale(0.95)";
245
+ }
246
+ destroy() {
247
+ this._tip?.parentNode?.removeChild(this._tip);
248
+ this._els.length = 0;
249
+ }
250
+ }
251
+ class LegendPlugin extends BasePlugin {
252
+ constructor(chart, cfg = {}) {
253
+ super(chart, cfg);
254
+ this._hidden = /* @__PURE__ */ new Set();
255
+ this._origSeries = [];
256
+ const defaults = {
257
+ enabled: true,
258
+ position: "bottom",
259
+ align: "center",
260
+ layout: "horizontal",
261
+ fontSize: 12,
262
+ itemSpacing: 12,
263
+ markerSize: 12,
264
+ markerType: "square",
265
+ clickable: true
266
+ };
267
+ this._opts = merge(defaults, cfg);
268
+ }
269
+ init() {
270
+ if (!this._opts.enabled) return;
271
+ const c = this._chart;
272
+ this._origSeries = [...c.config.data.series];
273
+ this._group = createSVGElement("g", { className: "cf-legend" });
274
+ c.svg.appendChild(this._group);
275
+ this._els.push(this._group);
276
+ this._draw();
277
+ c.on("afterRender", () => {
278
+ this._origSeries = [...c.config.data.series];
279
+ this._draw();
280
+ });
281
+ }
282
+ _draw() {
283
+ const c = this._chart;
284
+ removeChildren(this._group);
285
+ const series = this._origSeries;
286
+ if (!series.length) return;
287
+ const vb = c.svg.getAttribute("viewBox").split(" ").map(Number);
288
+ const W = vb[2], H = vb[3];
289
+ const o = this._opts;
290
+ const step = 110;
291
+ let x = 0, y = 0;
292
+ if (o.position === "bottom") {
293
+ y = H - 28;
294
+ x = this._alignX(W, series.length, step);
295
+ } else if (o.position === "top") {
296
+ y = 8;
297
+ x = this._alignX(W, series.length, step);
298
+ } else if (o.position === "left") {
299
+ x = 8;
300
+ y = (H - series.length * (o.markerSize + o.itemSpacing)) / 2;
301
+ } else {
302
+ x = W - 110;
303
+ y = (H - series.length * (o.markerSize + o.itemSpacing)) / 2;
304
+ }
305
+ series.forEach((s, i) => {
306
+ const item = this._item(s, i, x, y);
307
+ this._group.appendChild(item);
308
+ o.layout === "horizontal" ? x += step : y += o.markerSize + o.itemSpacing;
309
+ });
310
+ }
311
+ _alignX(W, count, step) {
312
+ const total = count * step;
313
+ const o = this._opts;
314
+ if (o.align === "center") return (W - total) / 2;
315
+ if (o.align === "end") return W - total - 20;
316
+ return 20;
317
+ }
318
+ _item(s, i, x, y) {
319
+ const c = this._chart;
320
+ const o = this._opts;
321
+ const hidden = this._hidden.has(i);
322
+ const color = c.theme.colors[i % c.theme.colors.length];
323
+ const opacity = hidden ? 0.3 : 1;
324
+ const group = createSVGElement("g");
325
+ let marker;
326
+ if (o.markerType === "circle") {
327
+ marker = createSVGElement("circle", {
328
+ cx: x + o.markerSize / 2,
329
+ cy: y + o.markerSize / 2,
330
+ r: o.markerSize / 2,
331
+ fill: color,
332
+ opacity
333
+ });
334
+ } else if (o.markerType === "line") {
335
+ marker = createSVGElement("line", {
336
+ x1: x,
337
+ y1: y + o.markerSize / 2,
338
+ x2: x + o.markerSize,
339
+ y2: y + o.markerSize / 2,
340
+ stroke: color,
341
+ "stroke-width": 3,
342
+ opacity
343
+ });
344
+ } else {
345
+ marker = createSVGElement("rect", {
346
+ x,
347
+ y,
348
+ width: o.markerSize,
349
+ height: o.markerSize,
350
+ fill: color,
351
+ opacity
352
+ });
353
+ }
354
+ group.appendChild(marker);
355
+ const text = createSVGElement("text", {
356
+ x: x + o.markerSize + 6,
357
+ y: y + o.markerSize / 2,
358
+ fill: c.theme.legend.text,
359
+ "font-size": o.fontSize,
360
+ "dominant-baseline": "middle",
361
+ opacity
362
+ });
363
+ text.textContent = s.name ?? `Series ${i + 1}`;
364
+ group.appendChild(text);
365
+ if (o.clickable) {
366
+ group.style.cursor = "pointer";
367
+ group.addEventListener("click", () => this._toggle(i));
368
+ group.addEventListener("mouseenter", () => text.setAttribute("fill", c.theme.legend.hover));
369
+ group.addEventListener("mouseleave", () => text.setAttribute("fill", c.theme.legend.text));
370
+ }
371
+ return group;
372
+ }
373
+ _toggle(i) {
374
+ const c = this._chart;
375
+ this._hidden.has(i) ? this._hidden.delete(i) : this._hidden.add(i);
376
+ c.config.data.series = this._origSeries.filter((_, idx) => !this._hidden.has(idx));
377
+ c.render();
378
+ c.config.data.series = this._origSeries;
379
+ this._draw();
380
+ }
381
+ }
382
+ class AxisPlugin extends BasePlugin {
383
+ constructor(chart, cfg = {}) {
384
+ super(chart, cfg);
385
+ this._opts = merge({
386
+ x: { enabled: true, label: "", fontSize: 11, tickLength: 5 },
387
+ y: { enabled: true, label: "", fontSize: 11, tickLength: 5, ticks: 5 }
388
+ }, cfg);
389
+ }
390
+ init() {
391
+ const c = this._chart;
392
+ this._group = createSVGElement("g", { className: "cf-axis" });
393
+ c.svg.appendChild(this._group);
394
+ this._els.push(this._group);
395
+ this._draw();
396
+ c.on("beforeRender", () => this._draw());
397
+ }
398
+ _draw() {
399
+ const c = this._chart;
400
+ removeChildren(this._group);
401
+ const vb = c.svg.getAttribute("viewBox").split(" ").map(Number);
402
+ const W = vb[2], H = vb[3];
403
+ const pad = c.config.padding ?? {};
404
+ const p = { top: pad.top ?? 40, right: pad.right ?? 40, bottom: pad.bottom ?? 60, left: pad.left ?? 60 };
405
+ if (this._opts.x?.enabled) this._drawX(W, H, p);
406
+ if (this._opts.y?.enabled) this._drawY(W, H, p);
407
+ }
408
+ _drawX(W, H, p) {
409
+ const c = this._chart;
410
+ const xo = this._opts.x;
411
+ const y = H - p.bottom;
412
+ const sx = p.left, ex = W - p.right;
413
+ this._group.appendChild(createSVGElement("line", {
414
+ x1: sx,
415
+ y1: y,
416
+ x2: ex,
417
+ y2: y,
418
+ stroke: c.theme.axis.line,
419
+ "stroke-width": 1
420
+ }));
421
+ const labels = c.config.data.labels ?? [];
422
+ const step = (ex - sx) / (labels.length || 1);
423
+ labels.forEach((lbl, i) => {
424
+ const x = sx + step * i + step / 2;
425
+ this._group.appendChild(createSVGElement("line", {
426
+ x1: x,
427
+ y1: y,
428
+ x2: x,
429
+ y2: y + (xo.tickLength ?? 5),
430
+ stroke: c.theme.axis.line,
431
+ "stroke-width": 1
432
+ }));
433
+ const t = createSVGElement("text", {
434
+ x,
435
+ y: y + (xo.tickLength ?? 5) + 13,
436
+ fill: c.theme.axis.text,
437
+ "font-size": xo.fontSize ?? 11,
438
+ "text-anchor": "middle"
439
+ });
440
+ t.textContent = lbl;
441
+ this._group.appendChild(t);
442
+ });
443
+ if (xo.label) {
444
+ const t = createSVGElement("text", {
445
+ x: (sx + ex) / 2,
446
+ y: H - 8,
447
+ fill: c.theme.text,
448
+ "font-size": (xo.fontSize ?? 11) + 2,
449
+ "text-anchor": "middle",
450
+ "font-weight": "bold"
451
+ });
452
+ t.textContent = xo.label;
453
+ this._group.appendChild(t);
454
+ }
455
+ }
456
+ // @ts-ignore
457
+ _drawY(W, H, p) {
458
+ const c = this._chart;
459
+ const yo = this._opts.y;
460
+ const x = p.left;
461
+ const sy = p.top, ey = H - p.bottom;
462
+ this._group.appendChild(createSVGElement("line", {
463
+ x1: x,
464
+ y1: sy,
465
+ x2: x,
466
+ y2: ey,
467
+ stroke: c.theme.axis.line,
468
+ "stroke-width": 1
469
+ }));
470
+ const nticks = yo.ticks ?? 5;
471
+ const step = (ey - sy) / nticks;
472
+ const allVals = (c.config.data.series ?? []).flatMap((s) => s.data);
473
+ const maxVal = allVals.length ? Math.max(...allVals) : 100;
474
+ for (let i = 0; i <= nticks; i++) {
475
+ const ty = ey - i * step;
476
+ const val = (maxVal / nticks * i).toFixed(0);
477
+ this._group.appendChild(createSVGElement("line", {
478
+ x1: x - (yo.tickLength ?? 5),
479
+ y1: ty,
480
+ x2: x,
481
+ y2: ty,
482
+ stroke: c.theme.axis.line,
483
+ "stroke-width": 1
484
+ }));
485
+ const t = createSVGElement("text", {
486
+ x: x - (yo.tickLength ?? 5) - 5,
487
+ y: ty,
488
+ fill: c.theme.axis.text,
489
+ "font-size": yo.fontSize ?? 11,
490
+ "text-anchor": "end",
491
+ "dominant-baseline": "middle"
492
+ });
493
+ t.textContent = val;
494
+ this._group.appendChild(t);
495
+ }
496
+ if (yo.label) {
497
+ const mid = (sy + ey) / 2;
498
+ const t = createSVGElement("text", {
499
+ x: 15,
500
+ y: mid,
501
+ fill: c.theme.text,
502
+ "font-size": (yo.fontSize ?? 11) + 2,
503
+ "text-anchor": "middle",
504
+ "font-weight": "bold",
505
+ transform: `rotate(-90,15,${mid})`
506
+ });
507
+ t.textContent = yo.label;
508
+ this._group.appendChild(t);
509
+ }
510
+ }
511
+ }
512
+ class GridPlugin extends BasePlugin {
513
+ constructor(chart, cfg = {}) {
514
+ super(chart, cfg);
515
+ const c = chart;
516
+ this._opts = merge({
517
+ enabled: true,
518
+ x: { enabled: true, color: c.theme.axis.grid, strokeWidth: 1, dashArray: "2,2" },
519
+ y: { enabled: true, color: c.theme.axis.grid, strokeWidth: 1, dashArray: "2,2", ticks: 5 }
520
+ }, cfg);
521
+ }
522
+ init() {
523
+ if (!this._opts.enabled) return;
524
+ const c = this._chart;
525
+ this._group = createSVGElement("g", { className: "cf-grid" });
526
+ c.mainGroup.insertBefore(this._group, c.mainGroup.firstChild);
527
+ this._els.push(this._group);
528
+ this._draw();
529
+ c.on("beforeRender", () => this._draw());
530
+ }
531
+ _draw() {
532
+ const c = this._chart;
533
+ removeChildren(this._group);
534
+ const vb = c.svg.getAttribute("viewBox").split(" ").map(Number);
535
+ const W = vb[2], H = vb[3];
536
+ const pad = c.config.padding ?? {};
537
+ const p = { top: pad.top ?? 40, right: pad.right ?? 40, bottom: pad.bottom ?? 60, left: pad.left ?? 60 };
538
+ const sx = p.left, ex = W - p.right;
539
+ const sy = p.top, ey = H - p.bottom;
540
+ if (this._opts.y?.enabled) {
541
+ const nticks = this._opts.y.ticks ?? 5;
542
+ const step = (ey - sy) / nticks;
543
+ for (let i = 0; i <= nticks; i++) {
544
+ const y = ey - i * step;
545
+ this._group.appendChild(createSVGElement("line", {
546
+ x1: sx,
547
+ y1: y,
548
+ x2: ex,
549
+ y2: y,
550
+ stroke: this._opts.y.color ?? "#ccc",
551
+ "stroke-width": this._opts.y.strokeWidth ?? 1,
552
+ "stroke-dasharray": this._opts.y.dashArray ?? "2,2",
553
+ opacity: "0.5"
554
+ }));
555
+ }
556
+ }
557
+ if (this._opts.x?.enabled) {
558
+ const labels = c.config.data.labels ?? [];
559
+ const step = (ex - sx) / (labels.length || 1);
560
+ for (let i = 0; i <= labels.length; i++) {
561
+ const x = sx + step * i;
562
+ this._group.appendChild(createSVGElement("line", {
563
+ x1: x,
564
+ y1: sy,
565
+ x2: x,
566
+ y2: ey,
567
+ stroke: this._opts.x.color ?? "#ccc",
568
+ "stroke-width": this._opts.x.strokeWidth ?? 1,
569
+ "stroke-dasharray": this._opts.x.dashArray ?? "2,2",
570
+ opacity: "0.5"
571
+ }));
572
+ }
573
+ }
574
+ }
575
+ }
576
+ class CrosshairPlugin extends BasePlugin {
577
+ constructor(chart, cfg = {}) {
578
+ super(chart, cfg);
579
+ const c = chart;
580
+ this._opts = {
581
+ enabled: true,
582
+ x: { enabled: true, color: c.theme.textSecondary, dashArray: "4,4", width: 1, ...cfg.x },
583
+ y: { enabled: true, color: c.theme.textSecondary, dashArray: "4,4", width: 1, ...cfg.y },
584
+ snap: cfg.snap ?? false
585
+ };
586
+ }
587
+ init() {
588
+ if (!this._opts.enabled) return;
589
+ const c = this._chart;
590
+ this._group = createSVGElement("g", { className: "cf-crosshair", style: "pointer-events:none" });
591
+ this._xLine = createSVGElement("line", {
592
+ stroke: this._opts.x.color ?? "#888",
593
+ "stroke-width": this._opts.x.width ?? 1,
594
+ "stroke-dasharray": this._opts.x.dashArray ?? "4,4",
595
+ opacity: 0
596
+ });
597
+ this._yLine = createSVGElement("line", {
598
+ stroke: this._opts.y.color ?? "#888",
599
+ "stroke-width": this._opts.y.width ?? 1,
600
+ "stroke-dasharray": this._opts.y.dashArray ?? "4,4",
601
+ opacity: 0
602
+ });
603
+ if (this._opts.x.enabled) this._group.appendChild(this._xLine);
604
+ if (this._opts.y.enabled) this._group.appendChild(this._yLine);
605
+ c.mainGroup.appendChild(this._group);
606
+ this._els.push(this._group);
607
+ c.svg.addEventListener("mousemove", (e) => this._onMove(e));
608
+ c.svg.addEventListener("mouseleave", () => this._hide());
609
+ }
610
+ _onMove(e) {
611
+ const c = this._chart;
612
+ const vb = c.svg.getAttribute("viewBox").split(" ").map(Number);
613
+ const W = vb[2], H = vb[3];
614
+ const pad = c.config.padding ?? {};
615
+ const pl = pad.left ?? 60;
616
+ const pb = pad.bottom ?? 60;
617
+ const pt = pad.top ?? 40;
618
+ const pr = pad.right ?? 40;
619
+ const rect = c.svg.getBoundingClientRect();
620
+ const scaleX = W / rect.width;
621
+ const scaleY = H / rect.height;
622
+ const mx = (e.clientX - rect.left) * scaleX;
623
+ const my = (e.clientY - rect.top) * scaleY;
624
+ if (mx < pl || mx > W - pr || my < pt || my > H - pb) {
625
+ this._hide();
626
+ return;
627
+ }
628
+ this._xLine.setAttribute("x1", String(mx));
629
+ this._xLine.setAttribute("y1", String(pt));
630
+ this._xLine.setAttribute("x2", String(mx));
631
+ this._xLine.setAttribute("y2", String(H - pb));
632
+ this._xLine.setAttribute("opacity", "1");
633
+ this._yLine.setAttribute("x1", String(pl));
634
+ this._yLine.setAttribute("y1", String(my));
635
+ this._yLine.setAttribute("x2", String(W - pr));
636
+ this._yLine.setAttribute("y2", String(my));
637
+ this._yLine.setAttribute("opacity", "1");
638
+ }
639
+ _hide() {
640
+ this._xLine.setAttribute("opacity", "0");
641
+ this._yLine.setAttribute("opacity", "0");
642
+ }
643
+ destroy() {
644
+ this._group?.parentNode?.removeChild(this._group);
645
+ }
646
+ }
647
+ class DataLabelsPlugin extends BasePlugin {
648
+ constructor(chart, cfg = {}) {
649
+ super(chart, cfg);
650
+ const c = chart;
651
+ this._opts = {
652
+ enabled: true,
653
+ fontSize: 11,
654
+ color: c.theme.text,
655
+ anchor: "top",
656
+ offset: 5,
657
+ formatter: (v) => v.toLocaleString(),
658
+ rotation: 0,
659
+ ...cfg
660
+ };
661
+ }
662
+ init() {
663
+ if (!this._opts.enabled) return;
664
+ const c = this._chart;
665
+ this._group = createSVGElement("g", {
666
+ className: "cf-data-labels",
667
+ "pointer-events": "none"
668
+ });
669
+ c.mainGroup.appendChild(this._group);
670
+ this._els.push(this._group);
671
+ c.on("afterRender", () => this._draw());
672
+ }
673
+ _draw() {
674
+ const c = this._chart;
675
+ while (this._group.firstChild) this._group.removeChild(this._group.firstChild);
676
+ const type = c.config.type;
677
+ const data = c.config.data;
678
+ const vb = c.svg.getAttribute("viewBox").split(" ").map(Number);
679
+ const W = vb[2], H = vb[3];
680
+ const pad = c.config.padding ?? {};
681
+ const pl = pad.left ?? 60, pr = pad.right ?? 40;
682
+ const pt = pad.top ?? 40, pb = pad.bottom ?? 60;
683
+ const dw = W - pl - pr;
684
+ const dh = H - pt - pb;
685
+ if (type === "column") {
686
+ const values = data.series[0].data;
687
+ const maxVal = Math.max(...values);
688
+ const cw = dw / values.length * 0.8;
689
+ const gp = dw / values.length * 0.2;
690
+ const baseY = pt + dh;
691
+ values.forEach((v, i) => {
692
+ const barH = v / maxVal * dh;
693
+ const x = pl + i * (cw + gp) + cw / 2;
694
+ const y = baseY - barH - this._opts.offset;
695
+ this._addLabel(x, y, v, "middle");
696
+ });
697
+ } else if (type === "bar") {
698
+ const values = data.series[0].data;
699
+ const maxVal = Math.max(...values);
700
+ const bh = dh / values.length * 0.8;
701
+ const gp = dh / values.length * 0.2;
702
+ values.forEach((v, i) => {
703
+ const barW = v / maxVal * dw;
704
+ const x = pl + barW + this._opts.offset;
705
+ const y = pt + i * (bh + gp) + bh / 2;
706
+ this._addLabel(x, y, v, "start", "middle");
707
+ });
708
+ }
709
+ }
710
+ _addLabel(x, y, value, textAnchor = "middle", baseline = "auto") {
711
+ const label = createSVGElement("text", {
712
+ x,
713
+ y,
714
+ fill: this._opts.color,
715
+ "font-size": this._opts.fontSize,
716
+ "text-anchor": textAnchor,
717
+ "dominant-baseline": baseline
718
+ });
719
+ if (this._opts.rotation !== 0) {
720
+ label.setAttribute("transform", `rotate(${this._opts.rotation},${x},${y})`);
721
+ }
722
+ label.textContent = this._opts.formatter(value);
723
+ this._group.appendChild(label);
724
+ }
725
+ }
726
+ class ExportPlugin extends BasePlugin {
727
+ constructor(chart, cfg = {}) {
728
+ super(chart, cfg);
729
+ this._opts = {
730
+ filename: cfg.filename ?? "chart",
731
+ svgButton: cfg.svgButton ?? true,
732
+ pngButton: cfg.pngButton ?? true,
733
+ csvButton: cfg.csvButton ?? true,
734
+ buttonStyle: cfg.buttonStyle ?? ""
735
+ };
736
+ }
737
+ init() {
738
+ const c = this._chart;
739
+ this._toolbar = document.createElement("div");
740
+ this._toolbar.className = "cf-export-toolbar";
741
+ this._toolbar.style.cssText = "display:flex;gap:6px;padding:4px 0;";
742
+ if (this._opts.svgButton) this._toolbar.appendChild(this._btn("SVG", () => this.exportSVG()));
743
+ if (this._opts.pngButton) this._toolbar.appendChild(this._btn("PNG", () => void this.exportPNG()));
744
+ if (this._opts.csvButton) this._toolbar.appendChild(this._btn("CSV", () => this.exportCSV()));
745
+ c.container.insertBefore(this._toolbar, c.container.firstChild);
746
+ this._els.push(this._toolbar);
747
+ }
748
+ _btn(label, onClick) {
749
+ const btn = document.createElement("button");
750
+ btn.textContent = `↓ ${label}`;
751
+ btn.style.cssText = [
752
+ "padding:3px 10px",
753
+ "font-size:11px",
754
+ "border-radius:4px",
755
+ "border:1px solid #888",
756
+ "background:transparent",
757
+ "cursor:pointer",
758
+ "opacity:.7",
759
+ "color: white",
760
+ this._opts.buttonStyle
761
+ ].join(";");
762
+ btn.addEventListener("mouseenter", () => btn.style.opacity = "1");
763
+ btn.addEventListener("mouseleave", () => btn.style.opacity = ".7");
764
+ btn.addEventListener("click", onClick);
765
+ return btn;
766
+ }
767
+ exportSVG() {
768
+ const c = this._chart;
769
+ const xml = new XMLSerializer().serializeToString(c.svg);
770
+ const blob = new Blob([xml], { type: "image/svg+xml" });
771
+ this._download(URL.createObjectURL(blob), `${this._opts.filename}.svg`);
772
+ }
773
+ async exportPNG(scale = 2) {
774
+ const c = this._chart;
775
+ const xml = new XMLSerializer().serializeToString(c.svg);
776
+ const svgBlob = new Blob([xml], { type: "image/svg+xml;charset=utf-8" });
777
+ const url = URL.createObjectURL(svgBlob);
778
+ const img = new Image();
779
+ await new Promise((resolve, reject) => {
780
+ img.onload = () => resolve();
781
+ img.onerror = reject;
782
+ img.src = url;
783
+ });
784
+ const vb = c.svg.getAttribute("viewBox")?.split(" ").map(Number) ?? [0, 0, 800, 400];
785
+ const canvas = document.createElement("canvas");
786
+ canvas.width = vb[2] * scale;
787
+ canvas.height = vb[3] * scale;
788
+ const ctx = canvas.getContext("2d");
789
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
790
+ URL.revokeObjectURL(url);
791
+ canvas.toBlob((blob) => {
792
+ if (blob) this._download(URL.createObjectURL(blob), `${this._opts.filename}.png`);
793
+ }, "image/png");
794
+ }
795
+ exportCSV() {
796
+ const data = this._chart.config.data;
797
+ const rows = [];
798
+ const header = ["Label", ...data.series.map((s, i) => s.name ?? `Series ${i + 1}`)];
799
+ rows.push(header.join(","));
800
+ const len = data.labels?.length ?? (data.series[0]?.data).length ?? 0;
801
+ for (let i = 0; i < len; i++) {
802
+ const lbl = data.labels?.[i] ?? i;
803
+ const vals = data.series.map((s) => s.data[i] ?? "");
804
+ rows.push([lbl, ...vals].join(","));
805
+ }
806
+ const blob = new Blob([rows.join("\n")], { type: "text/csv" });
807
+ this._download(URL.createObjectURL(blob), `${this._opts.filename}.csv`);
808
+ }
809
+ _download(url, filename) {
810
+ const a = document.createElement("a");
811
+ a.href = url;
812
+ a.download = filename;
813
+ a.click();
814
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
815
+ }
816
+ }
817
+ class ZoomPlugin extends BasePlugin {
818
+ constructor(chart, cfg = {}) {
819
+ super(chart, cfg);
820
+ this._scale = 1;
821
+ this._tx = 0;
822
+ this._ty = 0;
823
+ this._dragging = false;
824
+ this._dragStart = { x: 0, y: 0 };
825
+ this._opts = {
826
+ enabled: true,
827
+ type: "xy",
828
+ minZoom: 0.5,
829
+ maxZoom: 10,
830
+ resetOnDblClick: true,
831
+ ...cfg
832
+ };
833
+ }
834
+ init() {
835
+ if (!this._opts.enabled) return;
836
+ const c = this._chart;
837
+ const svg = c.svg;
838
+ svg.style.userSelect = "none";
839
+ svg.style.touchAction = "none";
840
+ svg.addEventListener("wheel", (e) => this._onWheel(e), { passive: false });
841
+ svg.addEventListener("mousedown", (e) => this._onMouseDown(e));
842
+ svg.addEventListener("mousemove", (e) => this._onMouseMove(e));
843
+ svg.addEventListener("mouseup", () => {
844
+ this._dragging = false;
845
+ });
846
+ svg.addEventListener("mouseleave", () => {
847
+ this._dragging = false;
848
+ });
849
+ if (this._opts.resetOnDblClick) {
850
+ svg.addEventListener("dblclick", () => this.reset());
851
+ }
852
+ }
853
+ _onWheel(e) {
854
+ e.preventDefault();
855
+ const c = this._chart;
856
+ const rect = c.svg.getBoundingClientRect();
857
+ const mx = e.clientX - rect.left;
858
+ const my = e.clientY - rect.top;
859
+ const delta = e.deltaY > 0 ? 0.85 : 1.15;
860
+ const ns = Math.max(this._opts.minZoom, Math.min(this._opts.maxZoom, this._scale * delta));
861
+ const scaleRatio = ns / this._scale;
862
+ const t = this._opts.type;
863
+ if (t === "x" || t === "xy") this._tx = mx - scaleRatio * (mx - this._tx);
864
+ if (t === "y" || t === "xy") this._ty = my - scaleRatio * (my - this._ty);
865
+ this._scale = ns;
866
+ this._apply();
867
+ }
868
+ _onMouseDown(e) {
869
+ if (e.button !== 0) return;
870
+ this._dragging = true;
871
+ this._dragStart = { x: e.clientX - this._tx, y: e.clientY - this._ty };
872
+ this._chart.svg.style.cursor = "grabbing";
873
+ }
874
+ _onMouseMove(e) {
875
+ if (!this._dragging) return;
876
+ const t = this._opts.type;
877
+ if (t === "x" || t === "xy") this._tx = e.clientX - this._dragStart.x;
878
+ if (t === "y" || t === "xy") this._ty = e.clientY - this._dragStart.y;
879
+ this._apply();
880
+ }
881
+ _apply() {
882
+ const mg = this._chart.mainGroup;
883
+ mg.style.transform = `translate(${this._tx}px, ${this._ty}px) scale(${this._scale})`;
884
+ mg.style.transformOrigin = "0 0";
885
+ this._chart.svg.style.cursor = this._dragging ? "grabbing" : "grab";
886
+ }
887
+ reset() {
888
+ this._scale = 1;
889
+ this._tx = 0;
890
+ this._ty = 0;
891
+ this._apply();
892
+ this._chart.svg.style.cursor = "";
893
+ }
894
+ }
895
+ class AnnotationPlugin extends BasePlugin {
896
+ constructor(chart, cfg = {}) {
897
+ super(chart, cfg);
898
+ this._annCfg = cfg;
899
+ }
900
+ init() {
901
+ const c = this._chart;
902
+ this._group = createSVGElement("g", {
903
+ className: "cf-annotations",
904
+ style: "pointer-events:none"
905
+ });
906
+ c.mainGroup.appendChild(this._group);
907
+ this._els.push(this._group);
908
+ this._draw();
909
+ c.on("afterRender", () => this._draw());
910
+ }
911
+ addMarkLine(ml) {
912
+ this._annCfg.markLines = [...this._annCfg.markLines ?? [], ml];
913
+ this._draw();
914
+ }
915
+ addMarkArea(ma) {
916
+ this._annCfg.markAreas = [...this._annCfg.markAreas ?? [], ma];
917
+ this._draw();
918
+ }
919
+ addText(t) {
920
+ this._annCfg.texts = [...this._annCfg.texts ?? [], t];
921
+ this._draw();
922
+ }
923
+ _draw() {
924
+ while (this._group.firstChild) this._group.removeChild(this._group.firstChild);
925
+ const c = this._chart;
926
+ const vb = c.svg.getAttribute("viewBox").split(" ").map(Number);
927
+ const W = vb[2], H = vb[3];
928
+ const pad = c.config.padding ?? {};
929
+ const pl = pad.left ?? 60, pr = pad.right ?? 40;
930
+ const pt = pad.top ?? 40, pb = pad.bottom ?? 60;
931
+ const dw = W - pl - pr;
932
+ const dh = H - pt - pb;
933
+ const allVals = (c.config.data.series ?? []).flatMap((s) => s.data);
934
+ const maxVal = allVals.length ? Math.max(...allVals) : 100;
935
+ const minVal = allVals.length ? Math.min(...allVals) : 0;
936
+ const range = maxVal - minVal || 1;
937
+ const toX = (idx, total) => pl + idx / Math.max(total - 1, 1) * dw;
938
+ const toY = (v) => pt + dh - (v - minVal) / range * dh;
939
+ const nLabels = c.config.data.labels?.length ?? 1;
940
+ const defColor = c.theme.textSecondary;
941
+ for (const ml of this._annCfg.markLines ?? []) {
942
+ const color = ml.color ?? defColor;
943
+ let x1, y1, x2, y2;
944
+ if (ml.type === "horizontal") {
945
+ y1 = y2 = toY(ml.value);
946
+ x1 = pl;
947
+ x2 = W - pr;
948
+ } else {
949
+ x1 = x2 = toX(ml.value, nLabels);
950
+ y1 = pt;
951
+ y2 = H - pb;
952
+ }
953
+ this._group.appendChild(createSVGElement("line", {
954
+ x1,
955
+ y1,
956
+ x2,
957
+ y2,
958
+ stroke: color,
959
+ "stroke-width": ml.width ?? 1.5,
960
+ "stroke-dasharray": ml.dashArray ?? "5,3"
961
+ }));
962
+ if (ml.label) {
963
+ const tx = ml.type === "horizontal" ? x2 + 4 : x1 + 4;
964
+ const ty = ml.type === "horizontal" ? y1 - 4 : pt - 4;
965
+ const t = createSVGElement("text", {
966
+ x: tx,
967
+ y: ty,
968
+ fill: color,
969
+ "font-size": 11
970
+ });
971
+ t.textContent = ml.label;
972
+ this._group.appendChild(t);
973
+ }
974
+ }
975
+ for (const ma of this._annCfg.markAreas ?? []) {
976
+ const x1 = ma.xStart !== void 0 ? toX(ma.xStart, nLabels) : pl;
977
+ const x2 = ma.xEnd !== void 0 ? toX(ma.xEnd, nLabels) : W - pr;
978
+ const y1 = ma.yEnd !== void 0 ? toY(ma.yEnd) : pt;
979
+ const y2 = ma.yStart !== void 0 ? toY(ma.yStart) : H - pb;
980
+ this._group.appendChild(createSVGElement("rect", {
981
+ x: x1,
982
+ y: y1,
983
+ width: x2 - x1,
984
+ height: y2 - y1,
985
+ fill: ma.color ?? defColor,
986
+ opacity: ma.opacity ?? 0.12
987
+ }));
988
+ if (ma.label) {
989
+ const t = createSVGElement("text", {
990
+ x: x1 + 4,
991
+ y: y1 + 14,
992
+ fill: ma.color ?? defColor,
993
+ "font-size": 11
994
+ });
995
+ t.textContent = ma.label;
996
+ this._group.appendChild(t);
997
+ }
998
+ }
999
+ for (const ta of this._annCfg.texts ?? []) {
1000
+ const x = toX(ta.x, nLabels);
1001
+ const y = toY(ta.y);
1002
+ const color = ta.color ?? c.theme.text;
1003
+ if (ta.background) {
1004
+ const bg = createSVGElement("rect", {
1005
+ x: x - 3,
1006
+ y: y - (ta.fontSize ?? 12),
1007
+ width: ta.text.length * ((ta.fontSize ?? 12) * 0.6) + 6,
1008
+ height: (ta.fontSize ?? 12) + 4,
1009
+ fill: ta.background,
1010
+ rx: 3
1011
+ });
1012
+ this._group.appendChild(bg);
1013
+ }
1014
+ const t = createSVGElement("text", {
1015
+ x,
1016
+ y,
1017
+ fill: color,
1018
+ "font-size": ta.fontSize ?? 12,
1019
+ "font-weight": "bold"
1020
+ });
1021
+ t.textContent = ta.text;
1022
+ this._group.appendChild(t);
1023
+ }
1024
+ }
1025
+ }
1026
+ const plugins = {
1027
+ Tooltip: TooltipPlugin,
1028
+ Legend: LegendPlugin,
1029
+ Axis: AxisPlugin,
1030
+ Grid: GridPlugin,
1031
+ Crosshair: CrosshairPlugin,
1032
+ DataLabels: DataLabelsPlugin,
1033
+ Export: ExportPlugin,
1034
+ Zoom: ZoomPlugin,
1035
+ Annotation: AnnotationPlugin
1036
+ };
1037
+ export {
1038
+ AnnotationPlugin as A,
1039
+ BasePlugin as B,
1040
+ CrosshairPlugin as C,
1041
+ DataLabelsPlugin as D,
1042
+ ExportPlugin as E,
1043
+ GridPlugin as G,
1044
+ LegendPlugin as L,
1045
+ TooltipPlugin as T,
1046
+ ZoomPlugin as Z,
1047
+ clamp as a,
1048
+ describeArc as b,
1049
+ createSVGElement as c,
1050
+ debounce as d,
1051
+ flatMin as e,
1052
+ flatMax as f,
1053
+ plugins as g,
1054
+ AxisPlugin as h,
1055
+ merge as m,
1056
+ polarToCartesian as p,
1057
+ removeChildren as r,
1058
+ throttle as t,
1059
+ uid as u
1060
+ };