@malinconico/nmcharts 2.3.0 → 2.5.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/nmcharts-categories.min.js +1 -1
- package/nmcharts-extras.min.js +1 -1
- package/nmcharts.d.ts +75 -2
- package/nmcharts.esm.js +1152 -2
- package/nmcharts.js +1170 -1
- package/package.json +17 -7
- package/nmcharts-categories.js +0 -5597
- package/nmcharts-extras.js +0 -320
package/nmcharts-extras.js
DELETED
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NMCharts Extras — Themes, SVG Export, ResizeObserver, Accessibility
|
|
3
|
-
* Load after nmcharts.js and nmcharts-categories.js
|
|
4
|
-
*/
|
|
5
|
-
(function (root) {
|
|
6
|
-
'use strict';
|
|
7
|
-
|
|
8
|
-
var NM = root.NMCharts;
|
|
9
|
-
if (!NM) throw new Error('NMCharts-Extras: NMCharts must be loaded first.');
|
|
10
|
-
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// 1. PREDEFINED THEMES
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
NM.themes = {
|
|
16
|
-
corporate: ['#1e3a5f', '#2d6a9f', '#4a90d9', '#7cb5ec', '#a8d1f0', '#2f4858', '#547a96', '#8fb3c9'],
|
|
17
|
-
pastel: ['#a8d8ea', '#aa96da', '#fcbad3', '#ffffd2', '#b5ead7', '#c7ceea', '#ffdac1', '#e2f0cb'],
|
|
18
|
-
vibrant: ['#ff6b6b', '#feca57', '#48dbfb', '#ff9ff3', '#54a0ff', '#5f27cd', '#01a3a4', '#f368e0'],
|
|
19
|
-
monochrome: ['#1a1a2e', '#3d3d5c', '#5f5f8a', '#8282b8', '#a5a5d0', '#c8c8e6', '#e0e0f0', '#f0f0f8'],
|
|
20
|
-
colorblind: ['#0072B2', '#E69F00', '#009E73', '#D55E00', '#CC79A7', '#56B4E9', '#F0E442', '#999999']
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// 2. HELPERS
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
/** Debounce: returns a function that delays invoking fn until after wait ms. */
|
|
28
|
-
function debounce(fn, wait) {
|
|
29
|
-
var timer;
|
|
30
|
-
return function () {
|
|
31
|
-
var ctx = this, args = arguments;
|
|
32
|
-
clearTimeout(timer);
|
|
33
|
-
timer = setTimeout(function () { fn.apply(ctx, args); }, wait);
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Inject theme colors into opts.series entries that lack explicit colors,
|
|
39
|
-
* and into opts.colors if not already set.
|
|
40
|
-
* Mutates a shallow clone of opts to avoid modifying the caller's object.
|
|
41
|
-
*/
|
|
42
|
-
function applyTheme(opts) {
|
|
43
|
-
if (!opts || !opts.theme) return opts;
|
|
44
|
-
var palette = NM.themes[opts.theme];
|
|
45
|
-
if (!palette) return opts;
|
|
46
|
-
|
|
47
|
-
// Shallow clone
|
|
48
|
-
var o = Object.assign({}, opts);
|
|
49
|
-
|
|
50
|
-
// Apply colors array if not explicitly provided
|
|
51
|
-
if (!o.colors) {
|
|
52
|
-
o.colors = palette;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Apply per-series colors where missing
|
|
56
|
-
if (o.series) {
|
|
57
|
-
o.series = o.series.map(function (s, i) {
|
|
58
|
-
if (s.color) return s;
|
|
59
|
-
return Object.assign({}, s, { color: palette[i % palette.length] });
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return o;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
// 3. ACCESSIBILITY HELPERS
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
|
|
70
|
-
/** Inject ARIA attributes and a visually-hidden data table for screen readers. */
|
|
71
|
-
function injectA11y(instance, chartType) {
|
|
72
|
-
var root = instance._root;
|
|
73
|
-
if (!root) return;
|
|
74
|
-
|
|
75
|
-
var opts = instance.opts || {};
|
|
76
|
-
var title = opts.title || '';
|
|
77
|
-
var label = title ? (title + ' — ' + chartType + ' chart') : (chartType + ' chart');
|
|
78
|
-
|
|
79
|
-
root.setAttribute('role', 'img');
|
|
80
|
-
root.setAttribute('aria-label', label);
|
|
81
|
-
root.setAttribute('tabindex', '0');
|
|
82
|
-
|
|
83
|
-
// Build a visually-hidden table from series data when available
|
|
84
|
-
var series = instance.series;
|
|
85
|
-
var categories = instance.categories;
|
|
86
|
-
if (!series || !series.length) return;
|
|
87
|
-
|
|
88
|
-
// Only build if categories exist (category charts), or data arrays exist
|
|
89
|
-
var table = document.createElement('table');
|
|
90
|
-
table.style.cssText = [
|
|
91
|
-
'position:absolute',
|
|
92
|
-
'width:1px',
|
|
93
|
-
'height:1px',
|
|
94
|
-
'padding:0',
|
|
95
|
-
'margin:-1px',
|
|
96
|
-
'overflow:hidden',
|
|
97
|
-
'clip:rect(0,0,0,0)',
|
|
98
|
-
'white-space:nowrap',
|
|
99
|
-
'border:0'
|
|
100
|
-
].join(';');
|
|
101
|
-
|
|
102
|
-
// Caption
|
|
103
|
-
var caption = document.createElement('caption');
|
|
104
|
-
caption.textContent = label;
|
|
105
|
-
table.appendChild(caption);
|
|
106
|
-
|
|
107
|
-
if (categories && categories.length) {
|
|
108
|
-
// Header row: category labels
|
|
109
|
-
var thead = document.createElement('thead');
|
|
110
|
-
var headerRow = document.createElement('tr');
|
|
111
|
-
var thEmpty = document.createElement('th');
|
|
112
|
-
thEmpty.textContent = 'Series';
|
|
113
|
-
headerRow.appendChild(thEmpty);
|
|
114
|
-
categories.forEach(function (cat) {
|
|
115
|
-
var th = document.createElement('th');
|
|
116
|
-
th.setAttribute('scope', 'col');
|
|
117
|
-
th.textContent = String(cat);
|
|
118
|
-
headerRow.appendChild(th);
|
|
119
|
-
});
|
|
120
|
-
thead.appendChild(headerRow);
|
|
121
|
-
table.appendChild(thead);
|
|
122
|
-
|
|
123
|
-
// Body rows: one row per series
|
|
124
|
-
var tbody = document.createElement('tbody');
|
|
125
|
-
series.forEach(function (s) {
|
|
126
|
-
var tr = document.createElement('tr');
|
|
127
|
-
var th = document.createElement('th');
|
|
128
|
-
th.setAttribute('scope', 'row');
|
|
129
|
-
th.textContent = s.name || '';
|
|
130
|
-
tr.appendChild(th);
|
|
131
|
-
(s.data || []).forEach(function (v) {
|
|
132
|
-
var td = document.createElement('td');
|
|
133
|
-
td.textContent = v != null ? String(v) : '';
|
|
134
|
-
tr.appendChild(td);
|
|
135
|
-
});
|
|
136
|
-
tbody.appendChild(tr);
|
|
137
|
-
});
|
|
138
|
-
table.appendChild(tbody);
|
|
139
|
-
} else {
|
|
140
|
-
// Single-value series (gauge, donut-like): just list name + value
|
|
141
|
-
var tbody2 = document.createElement('tbody');
|
|
142
|
-
series.forEach(function (s) {
|
|
143
|
-
var tr = document.createElement('tr');
|
|
144
|
-
var tdName = document.createElement('td');
|
|
145
|
-
tdName.textContent = s.name || '';
|
|
146
|
-
var tdVal = document.createElement('td');
|
|
147
|
-
tdVal.textContent = s.value != null ? String(s.value) : (Array.isArray(s.data) ? s.data.join(', ') : '');
|
|
148
|
-
tr.appendChild(tdName);
|
|
149
|
-
tr.appendChild(tdVal);
|
|
150
|
-
tbody2.appendChild(tr);
|
|
151
|
-
});
|
|
152
|
-
table.appendChild(tbody2);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Append to root (positioned so it doesn't affect layout)
|
|
156
|
-
root.style.position = root.style.position || 'relative';
|
|
157
|
-
root.appendChild(table);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ---------------------------------------------------------------------------
|
|
161
|
-
// 4. SVG EXPORT
|
|
162
|
-
// ---------------------------------------------------------------------------
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Export chart as SVG by wrapping the canvas data URL in an SVG <image> element.
|
|
166
|
-
* Works on any chart instance that has a .canvas property.
|
|
167
|
-
* @param {string} [filename] - Download filename (default: 'chart.svg')
|
|
168
|
-
*/
|
|
169
|
-
function exportSVG(filename) {
|
|
170
|
-
var canvas = this.canvas;
|
|
171
|
-
if (!canvas) {
|
|
172
|
-
console.warn('NMCharts exportSVG: no canvas found on instance');
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
var w = canvas.width / (window.devicePixelRatio || 1);
|
|
177
|
-
var h = canvas.height / (window.devicePixelRatio || 1);
|
|
178
|
-
var dataURL = canvas.toDataURL('image/png');
|
|
179
|
-
|
|
180
|
-
var svg = [
|
|
181
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
182
|
-
'<svg xmlns="http://www.w3.org/2000/svg"',
|
|
183
|
-
' xmlns:xlink="http://www.w3.org/1999/xlink"',
|
|
184
|
-
' width="' + w + '" height="' + h + '"',
|
|
185
|
-
' viewBox="0 0 ' + w + ' ' + h + '">',
|
|
186
|
-
' <title>' + ((this.opts && this.opts.title) ? escapeXml(this.opts.title) : 'Chart') + '</title>',
|
|
187
|
-
' <image x="0" y="0" width="' + w + '" height="' + h + '"',
|
|
188
|
-
' xlink:href="' + dataURL + '"',
|
|
189
|
-
' href="' + dataURL + '"/>',
|
|
190
|
-
'</svg>'
|
|
191
|
-
].join('\n');
|
|
192
|
-
|
|
193
|
-
var blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
|
|
194
|
-
var url = URL.createObjectURL(blob);
|
|
195
|
-
var a = document.createElement('a');
|
|
196
|
-
a.download = filename || 'chart.svg';
|
|
197
|
-
a.href = url;
|
|
198
|
-
a.click();
|
|
199
|
-
setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function escapeXml(str) {
|
|
203
|
-
return String(str)
|
|
204
|
-
.replace(/&/g, '&')
|
|
205
|
-
.replace(/</g, '<')
|
|
206
|
-
.replace(/>/g, '>')
|
|
207
|
-
.replace(/"/g, '"')
|
|
208
|
-
.replace(/'/g, ''');
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Attach exportSVG to the time-series prototype
|
|
212
|
-
NM.prototype.exportSVG = exportSVG;
|
|
213
|
-
|
|
214
|
-
// ---------------------------------------------------------------------------
|
|
215
|
-
// 5. FACTORY METHOD WRAPPING
|
|
216
|
-
// Wraps each factory method to:
|
|
217
|
-
// a) apply theme colors from opts.theme
|
|
218
|
-
// b) attach ResizeObserver for re-render on container resize
|
|
219
|
-
// c) inject accessibility attributes
|
|
220
|
-
// d) expose exportSVG on the returned instance
|
|
221
|
-
// ---------------------------------------------------------------------------
|
|
222
|
-
|
|
223
|
-
var FACTORY_METHODS = [
|
|
224
|
-
'create',
|
|
225
|
-
'bar',
|
|
226
|
-
'column',
|
|
227
|
-
'barStacked',
|
|
228
|
-
'lollipop',
|
|
229
|
-
'combo',
|
|
230
|
-
'donut',
|
|
231
|
-
'gauge',
|
|
232
|
-
'heatmap',
|
|
233
|
-
'radar',
|
|
234
|
-
'lineBar',
|
|
235
|
-
'waterfall',
|
|
236
|
-
'bullet',
|
|
237
|
-
'timeline',
|
|
238
|
-
'treemap',
|
|
239
|
-
'funnel',
|
|
240
|
-
'polar',
|
|
241
|
-
'scatter',
|
|
242
|
-
'sankey',
|
|
243
|
-
'boxplot',
|
|
244
|
-
'sparkline'
|
|
245
|
-
];
|
|
246
|
-
|
|
247
|
-
var RESIZE_DEBOUNCE_MS = 150;
|
|
248
|
-
|
|
249
|
-
FACTORY_METHODS.forEach(function (method) {
|
|
250
|
-
var original = NM[method];
|
|
251
|
-
if (typeof original !== 'function') return;
|
|
252
|
-
|
|
253
|
-
NM[method] = function (selector, opts) {
|
|
254
|
-
// a) Apply theme if specified
|
|
255
|
-
var themedOpts = applyTheme(opts);
|
|
256
|
-
|
|
257
|
-
// b) Create chart via original factory
|
|
258
|
-
var instance = original.call(NM, selector, themedOpts);
|
|
259
|
-
if (!instance) return instance;
|
|
260
|
-
|
|
261
|
-
// c) Expose exportSVG if not already present
|
|
262
|
-
if (typeof instance.exportSVG !== 'function') {
|
|
263
|
-
instance.exportSVG = exportSVG.bind(instance);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// d) Accessibility
|
|
267
|
-
var chartType = method === 'create' ? 'line' : method;
|
|
268
|
-
injectA11y(instance, chartType);
|
|
269
|
-
|
|
270
|
-
// e) ResizeObserver
|
|
271
|
-
if (typeof ResizeObserver !== 'undefined' && instance._root) {
|
|
272
|
-
var debouncedRender = debounce(function () {
|
|
273
|
-
if (typeof instance._initSize === 'function') {
|
|
274
|
-
instance._initSize();
|
|
275
|
-
}
|
|
276
|
-
if (typeof instance.render === 'function') {
|
|
277
|
-
instance.render();
|
|
278
|
-
}
|
|
279
|
-
// Time-series also needs navigator re-render
|
|
280
|
-
if (typeof instance._renderNav === 'function') {
|
|
281
|
-
instance._renderNav();
|
|
282
|
-
}
|
|
283
|
-
}, RESIZE_DEBOUNCE_MS);
|
|
284
|
-
|
|
285
|
-
var observer = new ResizeObserver(function (entries) {
|
|
286
|
-
if (!entries || !entries.length) return;
|
|
287
|
-
debouncedRender();
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// Observe the root element (the mc-root div)
|
|
291
|
-
observer.observe(instance._root);
|
|
292
|
-
|
|
293
|
-
// Store on instance for cleanup
|
|
294
|
-
instance._resizeObserver = observer;
|
|
295
|
-
|
|
296
|
-
// Patch destroy to disconnect observer
|
|
297
|
-
var originalDestroy = instance.destroy;
|
|
298
|
-
instance.destroy = function () {
|
|
299
|
-
if (instance._resizeObserver) {
|
|
300
|
-
instance._resizeObserver.disconnect();
|
|
301
|
-
instance._resizeObserver = null;
|
|
302
|
-
}
|
|
303
|
-
if (typeof originalDestroy === 'function') {
|
|
304
|
-
originalDestroy.call(instance);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return instance;
|
|
310
|
-
};
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// ---------------------------------------------------------------------------
|
|
314
|
-
// UMD export (mirror pattern from nmcharts.js / nmcharts-categories.js)
|
|
315
|
-
// ---------------------------------------------------------------------------
|
|
316
|
-
if (typeof module === 'object' && module.exports) {
|
|
317
|
-
module.exports = NM;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
})(typeof window !== 'undefined' ? window : typeof globalThis !== 'undefined' ? globalThis : this);
|