@anvme/nanocharts 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.
package/dist/index.cjs ADDED
@@ -0,0 +1,635 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ NanoBarChart: () => NanoBarChart,
24
+ NanoLineChart: () => NanoLineChart,
25
+ NanoPieChart: () => NanoPieChart
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/shared.ts
30
+ var SVG_NS = "http://www.w3.org/2000/svg";
31
+ function attachHoverListeners(container, svgEl, onUpdate, onLeave) {
32
+ var rafPending = false;
33
+ var lastCX = 0, lastCY = 0;
34
+ function schedule() {
35
+ if (!rafPending) {
36
+ rafPending = true;
37
+ requestAnimationFrame(function() {
38
+ rafPending = false;
39
+ var r = svgEl.getBoundingClientRect();
40
+ onUpdate(lastCX - r.left, lastCY - r.top);
41
+ });
42
+ }
43
+ }
44
+ container.addEventListener("mousemove", function(e) {
45
+ lastCX = e.clientX;
46
+ lastCY = e.clientY;
47
+ schedule();
48
+ }, { passive: true });
49
+ container.addEventListener("mouseleave", onLeave, { passive: true });
50
+ container.addEventListener("touchmove", function(e) {
51
+ var t = e.touches[0];
52
+ lastCX = t.clientX;
53
+ lastCY = t.clientY;
54
+ schedule();
55
+ }, { passive: true });
56
+ container.addEventListener("touchend", onLeave, { passive: true });
57
+ }
58
+
59
+ // src/line-chart.ts
60
+ function NanoLineChart(containerId, config) {
61
+ var data = config.data;
62
+ var labels = config.labels;
63
+ var xLabel = config.xLabel || "";
64
+ var yLabel = config.yLabel || "";
65
+ var colors = config.colors || ["#215CAF", "#007894", "#00C9A7", "#DE4437", "#F69C3D"];
66
+ var padding = { top: 20, right: 30, bottom: 50, left: 60 };
67
+ var smooth = config.smooth === true;
68
+ var areaFill = config.areaFill === true;
69
+ var areaOpacity = config.areaOpacity !== void 0 ? config.areaOpacity : 0.1;
70
+ var showTicks = config.showTicks !== false;
71
+ var showGrid = config.showGrid !== false;
72
+ var gridDashed = config.gridDashed === true;
73
+ var strokeWidth = config.strokeWidth !== void 0 ? config.strokeWidth : 2.5;
74
+ var hoverEnabled = config.hover !== false;
75
+ var container = document.getElementById(containerId);
76
+ if (!container || !data.length || !data[0].length) return;
77
+ var chartWidth = config.width || container.clientWidth || 600;
78
+ var chartHeight = config.height || 350;
79
+ var plotWidth = chartWidth - padding.left - padding.right;
80
+ var plotHeight = chartHeight - padding.top - padding.bottom;
81
+ var plotBottom = padding.top + plotHeight;
82
+ var seriesIndex, pointIndex, minValue, maxValue;
83
+ minValue = maxValue = data[0][0];
84
+ for (seriesIndex = 0; seriesIndex < data.length; seriesIndex++)
85
+ for (pointIndex = 0; pointIndex < data[seriesIndex].length; pointIndex++) {
86
+ if (data[seriesIndex][pointIndex] < minValue) minValue = data[seriesIndex][pointIndex];
87
+ if (data[seriesIndex][pointIndex] > maxValue) maxValue = data[seriesIndex][pointIndex];
88
+ }
89
+ var range = maxValue - minValue || 1;
90
+ minValue -= range * 0.05;
91
+ maxValue += range * 0.05;
92
+ range = maxValue - minValue;
93
+ var pointCount = labels.length;
94
+ var stepX = pointCount > 1 ? plotWidth / (pointCount - 1) : 0;
95
+ var tickCount = 6;
96
+ var tickStep = range / (tickCount - 1);
97
+ var totalCoords = data.length * pointCount;
98
+ var coordsX = new Int16Array(totalCoords);
99
+ var coordsY = new Int16Array(totalCoords);
100
+ for (seriesIndex = 0; seriesIndex < data.length; seriesIndex++)
101
+ for (pointIndex = 0; pointIndex < pointCount; pointIndex++) {
102
+ var coordIndex = seriesIndex * pointCount + pointIndex;
103
+ coordsX[coordIndex] = padding.left + pointIndex * stepX | 0;
104
+ coordsY[coordIndex] = padding.top + plotHeight - (data[seriesIndex][pointIndex] - minValue) / range * plotHeight | 0;
105
+ }
106
+ var gridYPositions = new Int16Array(tickCount);
107
+ var gridLabels = new Array(tickCount);
108
+ var tickIndex;
109
+ for (tickIndex = 0; tickIndex < tickCount; tickIndex++) {
110
+ var tickValue = minValue + tickIndex * tickStep;
111
+ gridYPositions[tickIndex] = padding.top + plotHeight - (tickValue - minValue) / range * plotHeight | 0;
112
+ gridLabels[tickIndex] = tickValue.toFixed(1);
113
+ }
114
+ var svgMarkup = '<svg xmlns="http://www.w3.org/2000/svg" width="' + chartWidth + '" height="' + chartHeight + '" viewBox="0 0 ' + chartWidth + " " + chartHeight + '" class="chart">';
115
+ if (showGrid) {
116
+ var gridPath = "";
117
+ for (tickIndex = 0; tickIndex < tickCount; tickIndex++)
118
+ gridPath += "M" + padding.left + "," + gridYPositions[tickIndex] + "H" + (padding.left + plotWidth);
119
+ svgMarkup += '<path d="' + gridPath + '" class="chart-grid"' + (gridDashed ? ' stroke-dasharray="3 4"' : "") + "/>";
120
+ }
121
+ for (seriesIndex = 0; seriesIndex < data.length; seriesIndex++) {
122
+ var ci = seriesIndex * pointCount;
123
+ var pathData = "M" + coordsX[ci] + "," + coordsY[ci];
124
+ if (smooth) {
125
+ for (pointIndex = 0; pointIndex < pointCount - 1; pointIndex++) {
126
+ var prevI = pointIndex ? pointIndex - 1 : 0;
127
+ var next2I = pointIndex + 2 < pointCount ? pointIndex + 2 : pointCount - 1;
128
+ var curX = coordsX[ci + pointIndex], curY = coordsY[ci + pointIndex];
129
+ var nextX = coordsX[ci + pointIndex + 1], nextY = coordsY[ci + pointIndex + 1];
130
+ var prevX = coordsX[ci + prevI], prevY = coordsY[ci + prevI];
131
+ var next2X = coordsX[ci + next2I], next2Y = coordsY[ci + next2I];
132
+ pathData += " C" + (curX + (nextX - prevX) / 6 | 0) + "," + (curY + (nextY - prevY) / 6 | 0) + " " + (nextX - (next2X - curX) / 6 | 0) + "," + (nextY - (next2Y - curY) / 6 | 0) + " " + nextX + "," + nextY;
133
+ }
134
+ } else {
135
+ for (pointIndex = 1; pointIndex < pointCount; pointIndex++)
136
+ pathData += "L" + coordsX[ci + pointIndex] + "," + coordsY[ci + pointIndex];
137
+ }
138
+ if (areaFill) {
139
+ var firstX = coordsX[ci], lastX = coordsX[ci + pointCount - 1];
140
+ svgMarkup += '<path d="' + pathData + " L" + lastX + "," + plotBottom + " L" + firstX + "," + plotBottom + ' Z" fill="' + colors[seriesIndex % colors.length] + '" fill-opacity="' + areaOpacity + '" stroke="none" pointer-events="none"/>';
141
+ }
142
+ svgMarkup += '<path d="' + pathData + '" class="chart-series" stroke="' + colors[seriesIndex % colors.length] + '"' + (strokeWidth !== 2.5 ? ' stroke-width="' + strokeWidth + '"' : "") + "/>";
143
+ }
144
+ svgMarkup += "</svg>";
145
+ var labelMarkup = '<div class="chart-labels">';
146
+ if (showTicks) {
147
+ for (tickIndex = 0; tickIndex < tickCount; tickIndex++)
148
+ labelMarkup += '<span class="chart-tick-y" style="left:' + (padding.left - 4) + "px;top:" + (gridYPositions[tickIndex] - 1) + 'px">' + gridLabels[tickIndex] + "</span>";
149
+ for (pointIndex = 0; pointIndex < pointCount; pointIndex++)
150
+ labelMarkup += '<span class="chart-tick-x" style="left:' + coordsX[pointIndex] + "px;top:" + (padding.top + plotHeight + 6) + 'px">' + labels[pointIndex] + "</span>";
151
+ }
152
+ if (xLabel)
153
+ labelMarkup += '<span class="chart-title" style="left:' + (padding.left + (plotWidth >> 1)) + "px;top:" + (chartHeight - 10) + 'px">' + xLabel + "</span>";
154
+ if (yLabel)
155
+ labelMarkup += '<span class="chart-title" style="left:4px;top:' + (padding.top - 14) + 'px;transform:none;text-align:left">' + yLabel + "</span>";
156
+ labelMarkup += "</div>";
157
+ container.style.cssText = "position:relative;width:" + chartWidth + "px;height:" + chartHeight + "px";
158
+ container.innerHTML = svgMarkup + labelMarkup;
159
+ if (!hoverEnabled) return;
160
+ var svg = container.firstChild;
161
+ var indicator = null;
162
+ var tooltip = null;
163
+ function ensureHoverElements() {
164
+ if (!indicator) {
165
+ indicator = document.createElementNS(SVG_NS, "circle");
166
+ indicator.setAttribute("r", "5");
167
+ indicator.setAttribute("class", "chart-indicator");
168
+ indicator.style.display = "none";
169
+ svg.appendChild(indicator);
170
+ }
171
+ if (!tooltip) {
172
+ tooltip = document.createElement("div");
173
+ tooltip.className = "chart-tooltip";
174
+ container.appendChild(tooltip);
175
+ }
176
+ }
177
+ attachHoverListeners(
178
+ container,
179
+ svg,
180
+ function(mouseX, mouseY) {
181
+ if (mouseX < padding.left || mouseX > padding.left + plotWidth || mouseY < padding.top || mouseY > padding.top + plotHeight) {
182
+ if (indicator) indicator.style.display = "none";
183
+ if (tooltip) tooltip.style.opacity = "0";
184
+ return;
185
+ }
186
+ ensureHoverElements();
187
+ var relativeX = mouseX - padding.left;
188
+ if (relativeX < 0) relativeX = 0;
189
+ if (relativeX > plotWidth) relativeX = plotWidth;
190
+ var nearestIndex = relativeX / stepX + 0.5 | 0;
191
+ if (nearestIndex >= pointCount) nearestIndex = pointCount - 1;
192
+ var bestSeriesIndex = 0;
193
+ var bestDistance = Math.abs(coordsY[nearestIndex] - mouseY);
194
+ for (var searchIndex = 1; searchIndex < data.length; searchIndex++) {
195
+ var distance = Math.abs(coordsY[searchIndex * pointCount + nearestIndex] - mouseY);
196
+ if (distance < bestDistance) {
197
+ bestDistance = distance;
198
+ bestSeriesIndex = searchIndex;
199
+ }
200
+ }
201
+ var pointX = coordsX[bestSeriesIndex * pointCount + nearestIndex];
202
+ var pointY = coordsY[bestSeriesIndex * pointCount + nearestIndex];
203
+ indicator.setAttribute("transform", "translate(" + pointX + "," + pointY + ")");
204
+ indicator.setAttribute("fill", colors[bestSeriesIndex % colors.length]);
205
+ indicator.style.display = "";
206
+ tooltip.innerHTML = "<b>Series " + (bestSeriesIndex + 1) + "</b><br>" + xLabel + ": " + labels[nearestIndex] + "<br>" + yLabel + ": " + data[bestSeriesIndex][nearestIndex];
207
+ tooltip.style.transform = "translate(" + pointX + "px," + (pointY - 40) + "px) translate(-50%,-100%)";
208
+ tooltip.style.opacity = "1";
209
+ },
210
+ function() {
211
+ if (indicator) indicator.style.display = "none";
212
+ if (tooltip) tooltip.style.opacity = "0";
213
+ }
214
+ );
215
+ }
216
+
217
+ // src/bar-chart.ts
218
+ function NanoBarChart(containerId, config) {
219
+ var data = config.data;
220
+ var labels = config.labels;
221
+ var xLabel = config.xLabel || "";
222
+ var yLabel = config.yLabel || "";
223
+ var colors = config.colors || ["#215CAF", "#007894", "#00C9A7", "#DE4437", "#F69C3D"];
224
+ var stacked = config.stacked === true;
225
+ var horizontal = config.horizontal === true;
226
+ var showTicks = config.showTicks !== false;
227
+ var showGrid = config.showGrid !== false;
228
+ var gridDashed = config.gridDashed === true;
229
+ var hoverEnabled = config.hover !== false;
230
+ var barGap = config.barGap !== void 0 ? config.barGap : 0.2;
231
+ var padding = { top: 20, right: 30, bottom: 50, left: horizontal ? 80 : 60 };
232
+ var container = document.getElementById(containerId);
233
+ if (!container || !data.length || !data[0].length) return;
234
+ var chartWidth = config.width || container.clientWidth || 600;
235
+ var chartHeight = config.height || 350;
236
+ var plotWidth = chartWidth - padding.left - padding.right;
237
+ var plotHeight = chartHeight - padding.top - padding.bottom;
238
+ var plotBottom = padding.top + plotHeight;
239
+ var seriesCount = data.length;
240
+ var catCount = labels.length;
241
+ var si, ci, val, ti;
242
+ var minVal, maxVal;
243
+ if (stacked) {
244
+ minVal = 0;
245
+ maxVal = 0;
246
+ for (ci = 0; ci < catCount; ci++) {
247
+ var colSum = 0;
248
+ for (si = 0; si < seriesCount; si++) {
249
+ colSum += data[si][ci];
250
+ if (data[si][ci] < minVal) minVal = data[si][ci];
251
+ }
252
+ if (colSum > maxVal) maxVal = colSum;
253
+ }
254
+ } else {
255
+ minVal = maxVal = data[0][0];
256
+ for (si = 0; si < seriesCount; si++)
257
+ for (ci = 0; ci < catCount; ci++) {
258
+ val = data[si][ci];
259
+ if (val < minVal) minVal = val;
260
+ if (val > maxVal) maxVal = val;
261
+ }
262
+ }
263
+ if (minVal > 0) minVal = 0;
264
+ if (maxVal < 0) maxVal = 0;
265
+ var range = maxVal - minVal || 1;
266
+ maxVal += range * 0.05;
267
+ range = maxVal - minVal;
268
+ var tickCount = 6;
269
+ var tickStep = range / (tickCount - 1);
270
+ var gridPositions = new Int16Array(tickCount);
271
+ var gridLabels = new Array(tickCount);
272
+ var totalBars = seriesCount * catCount;
273
+ var barX = new Int16Array(totalBars);
274
+ var barY = new Int16Array(totalBars);
275
+ var barW = new Int16Array(totalBars);
276
+ var barH = new Int16Array(totalBars);
277
+ var groupSize;
278
+ var zeroY = 0;
279
+ if (horizontal) {
280
+ groupSize = plotHeight / catCount;
281
+ var zeroX = padding.left + (0 - minVal) / range * plotWidth | 0;
282
+ for (ti = 0; ti < tickCount; ti++) {
283
+ var tickVal = minVal + ti * tickStep;
284
+ gridPositions[ti] = padding.left + (tickVal - minVal) / range * plotWidth | 0;
285
+ gridLabels[ti] = tickVal % 1 === 0 ? tickVal.toString() : tickVal.toFixed(1);
286
+ }
287
+ var hBarHeight = groupSize * (1 - barGap) / seriesCount | 0;
288
+ var hGroupStart = (groupSize - hBarHeight * seriesCount) / 2 | 0;
289
+ for (si = 0; si < seriesCount; si++)
290
+ for (ci = 0; ci < catCount; ci++) {
291
+ var idx = si * catCount + ci;
292
+ val = data[si][ci];
293
+ var valX = padding.left + (val - minVal) / range * plotWidth | 0;
294
+ barY[idx] = padding.top + ci * groupSize + hGroupStart + si * hBarHeight | 0;
295
+ barH[idx] = hBarHeight;
296
+ if (val >= 0) {
297
+ barX[idx] = zeroX;
298
+ barW[idx] = valX - zeroX || 1;
299
+ } else {
300
+ barX[idx] = valX;
301
+ barW[idx] = zeroX - valX || 1;
302
+ }
303
+ }
304
+ } else {
305
+ groupSize = plotWidth / catCount;
306
+ zeroY = padding.top + plotHeight - (0 - minVal) / range * plotHeight | 0;
307
+ for (ti = 0; ti < tickCount; ti++) {
308
+ var tickVal = minVal + ti * tickStep;
309
+ gridPositions[ti] = padding.top + plotHeight - (tickVal - minVal) / range * plotHeight | 0;
310
+ gridLabels[ti] = tickVal % 1 === 0 ? tickVal.toString() : tickVal.toFixed(1);
311
+ }
312
+ if (stacked) {
313
+ var stackBarWidth = groupSize * (1 - barGap) | 0;
314
+ var stackBarOffset = (groupSize - stackBarWidth) / 2 | 0;
315
+ for (ci = 0; ci < catCount; ci++) {
316
+ var cumPos = 0;
317
+ for (si = 0; si < seriesCount; si++) {
318
+ var idx = si * catCount + ci;
319
+ val = data[si][ci];
320
+ var baseY = padding.top + plotHeight - (cumPos - minVal) / range * plotHeight | 0;
321
+ var topY = padding.top + plotHeight - (cumPos + val - minVal) / range * plotHeight | 0;
322
+ cumPos += val;
323
+ barX[idx] = padding.left + ci * groupSize + stackBarOffset | 0;
324
+ barY[idx] = Math.min(baseY, topY);
325
+ barW[idx] = stackBarWidth;
326
+ barH[idx] = Math.abs(topY - baseY) || 1;
327
+ }
328
+ }
329
+ } else {
330
+ var singleBarWidth = groupSize * (1 - barGap) / seriesCount | 0;
331
+ var groupBarStart = (groupSize - singleBarWidth * seriesCount) / 2 | 0;
332
+ for (si = 0; si < seriesCount; si++)
333
+ for (ci = 0; ci < catCount; ci++) {
334
+ var idx = si * catCount + ci;
335
+ val = data[si][ci];
336
+ var topY = padding.top + plotHeight - (val - minVal) / range * plotHeight | 0;
337
+ barX[idx] = padding.left + ci * groupSize + groupBarStart + si * singleBarWidth | 0;
338
+ barW[idx] = singleBarWidth;
339
+ if (val >= 0) {
340
+ barY[idx] = topY;
341
+ barH[idx] = zeroY - topY || 1;
342
+ } else {
343
+ barY[idx] = zeroY;
344
+ barH[idx] = topY - zeroY || 1;
345
+ }
346
+ }
347
+ }
348
+ }
349
+ var svgMarkup = '<svg xmlns="http://www.w3.org/2000/svg" width="' + chartWidth + '" height="' + chartHeight + '" viewBox="0 0 ' + chartWidth + " " + chartHeight + '" class="chart">';
350
+ if (showGrid) {
351
+ var gridPath = "";
352
+ if (horizontal) {
353
+ for (ti = 0; ti < tickCount; ti++)
354
+ gridPath += "M" + gridPositions[ti] + "," + padding.top + "V" + plotBottom;
355
+ } else {
356
+ for (ti = 0; ti < tickCount; ti++)
357
+ gridPath += "M" + padding.left + "," + gridPositions[ti] + "H" + (padding.left + plotWidth);
358
+ }
359
+ svgMarkup += '<path d="' + gridPath + '" class="chart-grid"' + (gridDashed ? ' stroke-dasharray="3 4"' : "") + "/>";
360
+ }
361
+ if (minVal < 0) {
362
+ if (horizontal) {
363
+ var zx = padding.left + (0 - minVal) / range * plotWidth | 0;
364
+ svgMarkup += '<line x1="' + zx + '" y1="' + padding.top + '" x2="' + zx + '" y2="' + plotBottom + '" stroke="#999" stroke-width="1" shape-rendering="crispEdges"/>';
365
+ } else {
366
+ svgMarkup += '<line x1="' + padding.left + '" y1="' + zeroY + '" x2="' + (padding.left + plotWidth) + '" y2="' + zeroY + '" stroke="#999" stroke-width="1" shape-rendering="crispEdges"/>';
367
+ }
368
+ }
369
+ for (si = 0; si < seriesCount; si++) {
370
+ var color = colors[si % colors.length];
371
+ for (ci = 0; ci < catCount; ci++) {
372
+ var idx = si * catCount + ci;
373
+ svgMarkup += '<rect x="' + barX[idx] + '" y="' + barY[idx] + '" width="' + barW[idx] + '" height="' + barH[idx] + '" fill="' + color + '" class="chart-bar"/>';
374
+ }
375
+ }
376
+ svgMarkup += "</svg>";
377
+ var labelMarkup = '<div class="chart-labels">';
378
+ if (showTicks) {
379
+ if (horizontal) {
380
+ for (ti = 0; ti < tickCount; ti++)
381
+ labelMarkup += '<span class="chart-tick-x" style="left:' + gridPositions[ti] + "px;top:" + (plotBottom + 6) + 'px">' + gridLabels[ti] + "</span>";
382
+ for (ci = 0; ci < catCount; ci++) {
383
+ var centerY = padding.top + ci * groupSize + groupSize / 2 | 0;
384
+ labelMarkup += '<span class="chart-tick-y" style="left:' + (padding.left - 4) + "px;top:" + (centerY - 1) + 'px">' + labels[ci] + "</span>";
385
+ }
386
+ } else {
387
+ for (ti = 0; ti < tickCount; ti++)
388
+ labelMarkup += '<span class="chart-tick-y" style="left:' + (padding.left - 4) + "px;top:" + (gridPositions[ti] - 1) + 'px">' + gridLabels[ti] + "</span>";
389
+ for (ci = 0; ci < catCount; ci++) {
390
+ var centerX = padding.left + ci * groupSize + groupSize / 2 | 0;
391
+ labelMarkup += '<span class="chart-tick-x" style="left:' + centerX + "px;top:" + (plotBottom + 6) + 'px">' + labels[ci] + "</span>";
392
+ }
393
+ }
394
+ }
395
+ if (xLabel)
396
+ labelMarkup += '<span class="chart-title" style="left:' + (padding.left + (plotWidth >> 1)) + "px;top:" + (chartHeight - 10) + 'px">' + xLabel + "</span>";
397
+ if (yLabel)
398
+ labelMarkup += '<span class="chart-title" style="left:4px;top:' + (padding.top - 14) + 'px;transform:none;text-align:left">' + yLabel + "</span>";
399
+ labelMarkup += "</div>";
400
+ container.style.cssText = "position:relative;width:" + chartWidth + "px;height:" + chartHeight + "px";
401
+ container.innerHTML = svgMarkup + labelMarkup;
402
+ if (!hoverEnabled) return;
403
+ var svgEl = container.firstChild;
404
+ var highlight = null;
405
+ var tooltip = null;
406
+ function ensureHover() {
407
+ if (!highlight) {
408
+ highlight = document.createElementNS(SVG_NS, "rect");
409
+ highlight.setAttribute("class", "chart-indicator");
410
+ highlight.setAttribute("fill", "rgba(255,255,255,0.3)");
411
+ highlight.setAttribute("stroke", "#333");
412
+ highlight.setAttribute("stroke-width", "1");
413
+ highlight.style.display = "none";
414
+ svgEl.appendChild(highlight);
415
+ }
416
+ if (!tooltip) {
417
+ tooltip = document.createElement("div");
418
+ tooltip.className = "chart-tooltip";
419
+ container.appendChild(tooltip);
420
+ }
421
+ }
422
+ attachHoverListeners(
423
+ container,
424
+ svgEl,
425
+ function(mx, my) {
426
+ if (mx < padding.left || mx > padding.left + plotWidth || my < padding.top || my > plotBottom) {
427
+ if (highlight) highlight.style.display = "none";
428
+ if (tooltip) tooltip.style.opacity = "0";
429
+ return;
430
+ }
431
+ var nearCat;
432
+ if (horizontal) {
433
+ nearCat = (my - padding.top) / groupSize | 0;
434
+ } else {
435
+ nearCat = (mx - padding.left) / groupSize | 0;
436
+ }
437
+ if (nearCat >= catCount) nearCat = catCount - 1;
438
+ if (nearCat < 0) nearCat = 0;
439
+ var bestIdx = -1;
440
+ var bestDist = Infinity;
441
+ for (si = 0; si < seriesCount; si++) {
442
+ var idx2 = si * catCount + nearCat;
443
+ var cx = barX[idx2] + (barW[idx2] >> 1);
444
+ var cy = barY[idx2] + (barH[idx2] >> 1);
445
+ var dx = mx - cx, dy = my - cy;
446
+ var dist = dx * dx + dy * dy;
447
+ if (dist < bestDist) {
448
+ bestDist = dist;
449
+ bestIdx = idx2;
450
+ }
451
+ }
452
+ if (bestIdx === -1) return;
453
+ ensureHover();
454
+ var bsi = bestIdx / catCount | 0;
455
+ var bci = bestIdx % catCount;
456
+ highlight.setAttribute("x", "" + barX[bestIdx]);
457
+ highlight.setAttribute("y", "" + barY[bestIdx]);
458
+ highlight.setAttribute("width", "" + barW[bestIdx]);
459
+ highlight.setAttribute("height", "" + barH[bestIdx]);
460
+ highlight.setAttribute("stroke", colors[bsi % colors.length]);
461
+ highlight.style.display = "";
462
+ var tipX, tipY;
463
+ if (horizontal) {
464
+ tipX = barX[bestIdx] + barW[bestIdx];
465
+ tipY = barY[bestIdx] + (barH[bestIdx] >> 1);
466
+ tooltip.style.transform = "translate(" + (tipX + 8) + "px," + tipY + "px) translate(0,-50%)";
467
+ } else {
468
+ tipX = barX[bestIdx] + (barW[bestIdx] >> 1);
469
+ tipY = barY[bestIdx];
470
+ tooltip.style.transform = "translate(" + tipX + "px," + (tipY - 10) + "px) translate(-50%,-100%)";
471
+ }
472
+ tooltip.innerHTML = (seriesCount > 1 ? "<b>Series " + (bsi + 1) + "</b><br>" : "") + labels[bci] + ": " + data[bsi][bci];
473
+ tooltip.style.opacity = "1";
474
+ },
475
+ function() {
476
+ if (highlight) highlight.style.display = "none";
477
+ if (tooltip) tooltip.style.opacity = "0";
478
+ }
479
+ );
480
+ }
481
+
482
+ // src/pie-chart.ts
483
+ var TWO_PI = Math.PI * 2;
484
+ function NanoPieChart(containerId, config) {
485
+ var data = config.data;
486
+ var labels = config.labels;
487
+ var colors = config.colors || ["#215CAF", "#007894", "#00C9A7", "#DE4437", "#F69C3D", "#0d3a6e", "#003d4a", "#006651"];
488
+ var isDonut = config.donut === true;
489
+ var donutWidth = config.donutWidth !== void 0 ? config.donutWidth : 60;
490
+ var showLabels = config.showLabels !== false;
491
+ var hoverEnabled = config.hover !== false;
492
+ var container = document.getElementById(containerId);
493
+ if (!container) return;
494
+ var chartWidth = config.width || container.clientWidth || 350;
495
+ var chartHeight = config.height || chartWidth;
496
+ var sliceCount = data.length;
497
+ var centerX = chartWidth / 2;
498
+ var centerY = chartHeight / 2;
499
+ var outerR = Math.min(centerX, centerY) - (showLabels ? 40 : 10);
500
+ var innerR = isDonut ? Math.max(outerR - donutWidth, 0) : 0;
501
+ var i;
502
+ var total = 0;
503
+ for (i = 0; i < sliceCount; i++) total += data[i];
504
+ var startAngles = new Float64Array(sliceCount);
505
+ var endAngles = new Float64Array(sliceCount);
506
+ var percentages = new Float64Array(sliceCount);
507
+ var angle = -Math.PI / 2;
508
+ for (i = 0; i < sliceCount; i++) {
509
+ startAngles[i] = angle;
510
+ var sliceAngle = total > 0 ? data[i] / total * TWO_PI : 0;
511
+ percentages[i] = total > 0 ? data[i] / total * 100 : 0;
512
+ angle += sliceAngle;
513
+ endAngles[i] = angle;
514
+ }
515
+ var svgMarkup = '<svg xmlns="http://www.w3.org/2000/svg" width="' + chartWidth + '" height="' + chartHeight + '" viewBox="0 0 ' + chartWidth + " " + chartHeight + '" class="chart">';
516
+ for (i = 0; i < sliceCount; i++) {
517
+ if (endAngles[i] - startAngles[i] < 9e-3) continue;
518
+ var color = colors[i % colors.length];
519
+ var cos1 = Math.cos(startAngles[i]), sin1 = Math.sin(startAngles[i]);
520
+ var cos2 = Math.cos(endAngles[i]), sin2 = Math.sin(endAngles[i]);
521
+ var largeArc = endAngles[i] - startAngles[i] > Math.PI ? 1 : 0;
522
+ var ox1 = centerX + outerR * cos1, oy1 = centerY + outerR * sin1;
523
+ var ox2 = centerX + outerR * cos2, oy2 = centerY + outerR * sin2;
524
+ var pathData;
525
+ if (isDonut) {
526
+ var ix1 = centerX + innerR * cos1, iy1 = centerY + innerR * sin1;
527
+ var ix2 = centerX + innerR * cos2, iy2 = centerY + innerR * sin2;
528
+ pathData = "M" + ox1.toFixed(2) + "," + oy1.toFixed(2) + "A" + outerR + "," + outerR + " 0 " + largeArc + " 1 " + ox2.toFixed(2) + "," + oy2.toFixed(2) + "L" + ix2.toFixed(2) + "," + iy2.toFixed(2) + "A" + innerR + "," + innerR + " 0 " + largeArc + " 0 " + ix1.toFixed(2) + "," + iy1.toFixed(2) + "Z";
529
+ } else {
530
+ pathData = "M" + centerX.toFixed(2) + "," + centerY.toFixed(2) + "L" + ox1.toFixed(2) + "," + oy1.toFixed(2) + "A" + outerR + "," + outerR + " 0 " + largeArc + " 1 " + ox2.toFixed(2) + "," + oy2.toFixed(2) + "Z";
531
+ }
532
+ svgMarkup += '<path d="' + pathData + '" fill="' + color + '" class="chart-slice"/>';
533
+ }
534
+ svgMarkup += "</svg>";
535
+ var labelMarkup = '<div class="chart-labels">';
536
+ if (showLabels) {
537
+ var labelR = outerR + 20;
538
+ for (i = 0; i < sliceCount; i++) {
539
+ if (percentages[i] < 5) continue;
540
+ var midAngle = (startAngles[i] + endAngles[i]) / 2;
541
+ var lx = centerX + labelR * Math.cos(midAngle);
542
+ var ly = centerY + labelR * Math.sin(midAngle);
543
+ labelMarkup += '<span style="left:' + lx.toFixed(1) + "px;top:" + ly.toFixed(1) + 'px;transform:translate(-50%,-50%);text-align:center">' + labels[i] + " " + percentages[i].toFixed(0) + "%</span>";
544
+ }
545
+ }
546
+ labelMarkup += "</div>";
547
+ container.style.cssText = "position:relative;width:" + chartWidth + "px;height:" + chartHeight + "px";
548
+ container.innerHTML = svgMarkup + labelMarkup;
549
+ if (!hoverEnabled) return;
550
+ var svgEl = container.firstChild;
551
+ var highlight = null;
552
+ var tooltip = null;
553
+ var lastSlice = -1;
554
+ function ensureHover() {
555
+ if (!highlight) {
556
+ highlight = document.createElementNS(SVG_NS, "path");
557
+ highlight.setAttribute("class", "chart-indicator");
558
+ highlight.setAttribute("fill", "none");
559
+ highlight.setAttribute("stroke", "#fff");
560
+ highlight.setAttribute("stroke-width", "3");
561
+ highlight.style.display = "none";
562
+ svgEl.appendChild(highlight);
563
+ }
564
+ if (!tooltip) {
565
+ tooltip = document.createElement("div");
566
+ tooltip.className = "chart-tooltip";
567
+ container.appendChild(tooltip);
568
+ }
569
+ }
570
+ attachHoverListeners(
571
+ container,
572
+ svgEl,
573
+ function(mx, my) {
574
+ var dx = mx - centerX, dy = my - centerY;
575
+ var dist = Math.sqrt(dx * dx + dy * dy);
576
+ if (dist > outerR || dist < innerR) {
577
+ if (highlight) highlight.style.display = "none";
578
+ if (tooltip) tooltip.style.opacity = "0";
579
+ lastSlice = -1;
580
+ return;
581
+ }
582
+ var hoverAngle = Math.atan2(dy, dx);
583
+ if (hoverAngle < -Math.PI / 2) hoverAngle += TWO_PI;
584
+ var hitSlice = -1;
585
+ for (var s = 0; s < sliceCount; s++) {
586
+ if (hoverAngle >= startAngles[s] && hoverAngle < endAngles[s]) {
587
+ hitSlice = s;
588
+ break;
589
+ }
590
+ }
591
+ if (hitSlice === -1) return;
592
+ ensureHover();
593
+ if (hitSlice !== lastSlice) {
594
+ lastSlice = hitSlice;
595
+ var cos12 = Math.cos(startAngles[hitSlice]), sin12 = Math.sin(startAngles[hitSlice]);
596
+ var cos22 = Math.cos(endAngles[hitSlice]), sin22 = Math.sin(endAngles[hitSlice]);
597
+ var largeArc2 = endAngles[hitSlice] - startAngles[hitSlice] > Math.PI ? 1 : 0;
598
+ var ox12 = centerX + outerR * cos12, oy12 = centerY + outerR * sin12;
599
+ var ox22 = centerX + outerR * cos22, oy22 = centerY + outerR * sin22;
600
+ var hPath;
601
+ if (isDonut) {
602
+ var ix12 = centerX + innerR * cos12, iy12 = centerY + innerR * sin12;
603
+ var ix22 = centerX + innerR * cos22, iy22 = centerY + innerR * sin22;
604
+ hPath = "M" + ox12.toFixed(2) + "," + oy12.toFixed(2) + "A" + outerR + "," + outerR + " 0 " + largeArc2 + " 1 " + ox22.toFixed(2) + "," + oy22.toFixed(2) + "L" + ix22.toFixed(2) + "," + iy22.toFixed(2) + "A" + innerR + "," + innerR + " 0 " + largeArc2 + " 0 " + ix12.toFixed(2) + "," + iy12.toFixed(2) + "Z";
605
+ } else {
606
+ hPath = "M" + centerX.toFixed(2) + "," + centerY.toFixed(2) + "L" + ox12.toFixed(2) + "," + oy12.toFixed(2) + "A" + outerR + "," + outerR + " 0 " + largeArc2 + " 1 " + ox22.toFixed(2) + "," + oy22.toFixed(2) + "Z";
607
+ }
608
+ highlight.setAttribute("d", hPath);
609
+ }
610
+ highlight.style.display = "";
611
+ tooltip.innerHTML = "<b>" + labels[hitSlice] + "</b><br>" + data[hitSlice] + " (" + percentages[hitSlice].toFixed(1) + "%)";
612
+ tooltip.style.transform = "translate(" + mx + "px," + (my - 40) + "px) translate(-50%,-100%)";
613
+ tooltip.style.opacity = "1";
614
+ },
615
+ function() {
616
+ if (highlight) highlight.style.display = "none";
617
+ if (tooltip) tooltip.style.opacity = "0";
618
+ lastSlice = -1;
619
+ }
620
+ );
621
+ }
622
+
623
+ // src/index.ts
624
+ if (typeof window !== "undefined") {
625
+ window.NanoLineChart = NanoLineChart;
626
+ window.NanoBarChart = NanoBarChart;
627
+ window.NanoPieChart = NanoPieChart;
628
+ }
629
+ // Annotate the CommonJS export names for ESM import in node:
630
+ 0 && (module.exports = {
631
+ NanoBarChart,
632
+ NanoLineChart,
633
+ NanoPieChart
634
+ });
635
+ //# sourceMappingURL=index.cjs.map