chartjs-plugin-chart2music 0.6.0 → 0.7.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/README.md +96 -71
- package/dist/plugin.amd.js +180 -46
- package/dist/plugin.cjs +336 -231
- package/dist/plugin.d.ts +8 -1
- package/dist/plugin.js +180 -46
- package/dist/plugin.mjs +180 -46
- package/package.json +58 -23
package/dist/plugin.d.ts
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Plugin } from "chart.js";
|
|
2
|
+
import type { C2MChartConfig } from "chart2music";
|
|
3
|
+
|
|
4
|
+
export type C2MPluginOptions = Pick<C2MChartConfig, "audioEngine" | "axes" | "cc" | "lang" | "options"> & {
|
|
5
|
+
errorCallback?: (err: string) => void;
|
|
6
|
+
}
|
|
7
|
+
declare const plugin: Plugin;
|
|
8
|
+
export default plugin;
|
package/dist/plugin.js
CHANGED
|
@@ -85,7 +85,8 @@ var chartjs2music = (function (c2mChart) {
|
|
|
85
85
|
boxplot: "box",
|
|
86
86
|
radar: "bar",
|
|
87
87
|
wordCloud: "bar",
|
|
88
|
-
scatter: "scatter"
|
|
88
|
+
scatter: "scatter",
|
|
89
|
+
matrix: "matrix"
|
|
89
90
|
};
|
|
90
91
|
const processChartType = (chart) => {
|
|
91
92
|
const topLevelType = chart.config.type;
|
|
@@ -110,7 +111,7 @@ var chartjs2music = (function (c2mChart) {
|
|
|
110
111
|
};
|
|
111
112
|
const generateAxisInfo = (chartAxisInfo, chart) => {
|
|
112
113
|
const axis = {};
|
|
113
|
-
if (chartAxisInfo?.min) {
|
|
114
|
+
if (chartAxisInfo?.min !== undefined) {
|
|
114
115
|
if (typeof chartAxisInfo.min === "string") {
|
|
115
116
|
axis.minimum = chart.data.labels.indexOf(chartAxisInfo.min);
|
|
116
117
|
}
|
|
@@ -118,7 +119,7 @@ var chartjs2music = (function (c2mChart) {
|
|
|
118
119
|
axis.minimum = chartAxisInfo.min;
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
|
-
if (chartAxisInfo?.max) {
|
|
122
|
+
if (chartAxisInfo?.max !== undefined) {
|
|
122
123
|
if (typeof chartAxisInfo.max === "string") {
|
|
123
124
|
axis.maximum = chart.data.labels.indexOf(chartAxisInfo.max);
|
|
124
125
|
}
|
|
@@ -135,17 +136,20 @@ var chartjs2music = (function (c2mChart) {
|
|
|
135
136
|
}
|
|
136
137
|
return axis;
|
|
137
138
|
};
|
|
138
|
-
const generateAxes = (chart) => {
|
|
139
|
+
const generateAxes = (chart, options) => {
|
|
139
140
|
const axes = {
|
|
140
141
|
x: {
|
|
141
142
|
...generateAxisInfo(chart.options?.scales?.x, chart),
|
|
142
|
-
valueLabels: chart.data.labels.slice(0)
|
|
143
143
|
},
|
|
144
144
|
y: {
|
|
145
|
+
format: options?.axes?.y?.format || ((value) => value.toLocaleString()),
|
|
145
146
|
...generateAxisInfo(chart.options?.scales?.y, chart),
|
|
146
|
-
format: (value) => value.toLocaleString()
|
|
147
147
|
}
|
|
148
148
|
};
|
|
149
|
+
const xAxisValueLabels = chart.data.labels.slice(0);
|
|
150
|
+
if (xAxisValueLabels.length > 0) {
|
|
151
|
+
axes.x.valueLabels = xAxisValueLabels;
|
|
152
|
+
}
|
|
149
153
|
return axes;
|
|
150
154
|
};
|
|
151
155
|
const whichDataStructure = (data) => {
|
|
@@ -157,6 +161,55 @@ var chartjs2music = (function (c2mChart) {
|
|
|
157
161
|
}
|
|
158
162
|
return data;
|
|
159
163
|
};
|
|
164
|
+
const labelIndex = (labels, value) => {
|
|
165
|
+
let index = labels.indexOf(value);
|
|
166
|
+
if (index === -1) {
|
|
167
|
+
labels.push(value);
|
|
168
|
+
index = labels.length - 1;
|
|
169
|
+
}
|
|
170
|
+
return index;
|
|
171
|
+
};
|
|
172
|
+
const processMatrixDataPoints = (data, datasetIndex, xLabels, yLabels) => {
|
|
173
|
+
return data.map((point, index) => {
|
|
174
|
+
const x = typeof point.x === "string" ? labelIndex(xLabels, point.x) : point.x;
|
|
175
|
+
const y = typeof point.y === "string" ? labelIndex(yLabels, point.y) : point.y;
|
|
176
|
+
return {
|
|
177
|
+
...point,
|
|
178
|
+
x,
|
|
179
|
+
y,
|
|
180
|
+
custom: {
|
|
181
|
+
...point.custom,
|
|
182
|
+
group: datasetIndex,
|
|
183
|
+
datasetIndex,
|
|
184
|
+
index
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
const processMatrixData = (data) => {
|
|
190
|
+
const xLabels = [];
|
|
191
|
+
const yLabels = [];
|
|
192
|
+
// Chart2Music represents matrix rows as groups and columns as points within
|
|
193
|
+
// each group. This preserves two-dimensional keyboard navigation.
|
|
194
|
+
const result = {};
|
|
195
|
+
data.datasets.forEach((obj, index) => {
|
|
196
|
+
const points = processMatrixDataPoints(obj.data, index, xLabels, yLabels);
|
|
197
|
+
points.forEach((point) => {
|
|
198
|
+
const rowLabel = typeof point.y === "number" && yLabels[point.y] !== undefined
|
|
199
|
+
? yLabels[point.y]
|
|
200
|
+
: String(point.y);
|
|
201
|
+
const groupName = data.datasets.length === 1
|
|
202
|
+
? rowLabel
|
|
203
|
+
: `${obj.label ?? `Group ${index + 1}`}: ${rowLabel}`;
|
|
204
|
+
if (!result[groupName]) {
|
|
205
|
+
result[groupName] = [];
|
|
206
|
+
}
|
|
207
|
+
result[groupName].push(point);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
Object.values(result).forEach((row) => row.sort((a, b) => a.x - b.x));
|
|
211
|
+
return { groups: Object.keys(result), data: result, xLabels, yLabels };
|
|
212
|
+
};
|
|
160
213
|
const scrubX = (data) => {
|
|
161
214
|
const blackboard = JSON.parse(JSON.stringify(data));
|
|
162
215
|
let labels = [];
|
|
@@ -171,11 +224,18 @@ var chartjs2music = (function (c2mChart) {
|
|
|
171
224
|
});
|
|
172
225
|
return { labels, data: blackboard };
|
|
173
226
|
}
|
|
227
|
+
else {
|
|
228
|
+
// Grouped
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
174
231
|
};
|
|
175
232
|
const processData = (data, c2m_types) => {
|
|
176
233
|
if (c2m_types === "box") {
|
|
177
234
|
return processBoxData(data);
|
|
178
235
|
}
|
|
236
|
+
if (c2m_types === "matrix") {
|
|
237
|
+
return processMatrixData(data);
|
|
238
|
+
}
|
|
179
239
|
let groups = [];
|
|
180
240
|
if (data.datasets.length === 1) {
|
|
181
241
|
return {
|
|
@@ -207,26 +267,36 @@ var chartjs2music = (function (c2mChart) {
|
|
|
207
267
|
canvas.insertAdjacentElement("afterend", cc);
|
|
208
268
|
return cc;
|
|
209
269
|
};
|
|
270
|
+
const createDataSnapshot = (chart) => {
|
|
271
|
+
return JSON.stringify({
|
|
272
|
+
datasets: chart.data.datasets.map(ds => ds.data),
|
|
273
|
+
labels: chart.data.labels
|
|
274
|
+
});
|
|
275
|
+
};
|
|
210
276
|
const displayPoint = (chart) => {
|
|
211
277
|
if (!chartStates.has(chart)) {
|
|
212
278
|
return;
|
|
213
279
|
}
|
|
214
|
-
const { c2m: ref
|
|
280
|
+
const { c2m: ref } = chartStates.get(chart);
|
|
215
281
|
const { point, index } = ref.getCurrent();
|
|
282
|
+
// Use Chart2Music's internal visible group tracking
|
|
283
|
+
// @ts-ignore - accessing internal Chart2Music property
|
|
284
|
+
const visibleGroupIndices = ref._visible_group_indices?.slice(1) || [];
|
|
216
285
|
try {
|
|
217
286
|
const highlightElements = [];
|
|
218
287
|
if ("custom" in point) {
|
|
288
|
+
const customPoint = point;
|
|
219
289
|
highlightElements.push({
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// @ts-ignore
|
|
223
|
-
index: point.custom.index
|
|
290
|
+
datasetIndex: customPoint.custom.datasetIndex ?? customPoint.custom.group,
|
|
291
|
+
index: customPoint.custom.index
|
|
224
292
|
});
|
|
225
293
|
}
|
|
226
294
|
else {
|
|
227
|
-
|
|
295
|
+
// For stacked charts, Chart2Music includes an "All" group at index 0,
|
|
296
|
+
// so we subtract 1 to get the actual dataset index
|
|
297
|
+
visibleGroupIndices.forEach((groupIndex) => {
|
|
228
298
|
highlightElements.push({
|
|
229
|
-
datasetIndex,
|
|
299
|
+
datasetIndex: groupIndex - 1,
|
|
230
300
|
index
|
|
231
301
|
});
|
|
232
302
|
});
|
|
@@ -242,11 +312,10 @@ var chartjs2music = (function (c2mChart) {
|
|
|
242
312
|
const generateChart = (chart, options) => {
|
|
243
313
|
const { valid, c2m_types, invalidType } = processChartType(chart);
|
|
244
314
|
if (!valid) {
|
|
245
|
-
// @ts-ignore
|
|
246
315
|
options.errorCallback?.(`Unable to connect chart2music to chart. The chart is of type "${invalidType}", which is not one of the supported chart types for this plugin. This plugin supports: ${Object.keys(chartjs_c2m_converter).join(", ")}`);
|
|
247
316
|
return;
|
|
248
317
|
}
|
|
249
|
-
let axes = generateAxes(chart);
|
|
318
|
+
let axes = generateAxes(chart, options);
|
|
250
319
|
if (chart.config.type === "wordCloud") {
|
|
251
320
|
delete axes.x.minimum;
|
|
252
321
|
delete axes.x.maximum;
|
|
@@ -261,8 +330,20 @@ var chartjs2music = (function (c2mChart) {
|
|
|
261
330
|
}
|
|
262
331
|
// Generate CC element
|
|
263
332
|
const cc = determineCCElement(chart.canvas, options.cc);
|
|
264
|
-
const
|
|
333
|
+
const processedData = processData(chart.data, c2m_types);
|
|
334
|
+
const { data } = processedData;
|
|
265
335
|
// lastDataObj = JSON.stringify(data);
|
|
336
|
+
if (c2m_types === "matrix") {
|
|
337
|
+
if (processedData.xLabels?.length > 0) {
|
|
338
|
+
axes.x.valueLabels = processedData.xLabels.slice(0);
|
|
339
|
+
}
|
|
340
|
+
if (processedData.yLabels?.length > 0) {
|
|
341
|
+
// Category labels provide the Matrix Plot's Y-axis formatter.
|
|
342
|
+
// The general numeric formatter would otherwise announce row indexes.
|
|
343
|
+
delete axes.y.format;
|
|
344
|
+
axes.y.valueLabels = processedData.yLabels.slice(0);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
266
347
|
let scrub = scrubX(data);
|
|
267
348
|
if (scrub?.labels && scrub?.labels?.length > 0) { // Something was scrubbed
|
|
268
349
|
if (!chart.data.labels || chart.data.labels.length === 0) {
|
|
@@ -284,6 +365,13 @@ var chartjs2music = (function (c2mChart) {
|
|
|
284
365
|
...(options.axes?.y)
|
|
285
366
|
},
|
|
286
367
|
};
|
|
368
|
+
// Start with plugin's internal onFocusCallback
|
|
369
|
+
const pluginOnFocusCallback = () => {
|
|
370
|
+
displayPoint(chart);
|
|
371
|
+
};
|
|
372
|
+
// Merge user's options, wrapping onFocusCallback if provided
|
|
373
|
+
const userOptions = options.options || {};
|
|
374
|
+
const userOnFocusCallback = userOptions.onFocusCallback;
|
|
287
375
|
const c2mOptions = {
|
|
288
376
|
cc,
|
|
289
377
|
element: chart.canvas,
|
|
@@ -292,10 +380,13 @@ var chartjs2music = (function (c2mChart) {
|
|
|
292
380
|
title: determineChartTitle(chart.options),
|
|
293
381
|
axes,
|
|
294
382
|
options: {
|
|
295
|
-
|
|
296
|
-
onFocusCallback:
|
|
297
|
-
|
|
298
|
-
|
|
383
|
+
...userOptions,
|
|
384
|
+
onFocusCallback: userOnFocusCallback
|
|
385
|
+
? (point) => {
|
|
386
|
+
pluginOnFocusCallback();
|
|
387
|
+
userOnFocusCallback(point);
|
|
388
|
+
}
|
|
389
|
+
: pluginOnFocusCallback
|
|
299
390
|
}
|
|
300
391
|
};
|
|
301
392
|
if (Array.isArray(c2mOptions.data)) {
|
|
@@ -304,8 +395,9 @@ var chartjs2music = (function (c2mChart) {
|
|
|
304
395
|
return {
|
|
305
396
|
...point,
|
|
306
397
|
custom: {
|
|
307
|
-
|
|
308
|
-
|
|
398
|
+
...point.custom,
|
|
399
|
+
group: point.custom?.group ?? 0,
|
|
400
|
+
index: point.custom?.index ?? index
|
|
309
401
|
}
|
|
310
402
|
};
|
|
311
403
|
});
|
|
@@ -324,10 +416,11 @@ var chartjs2music = (function (c2mChart) {
|
|
|
324
416
|
}
|
|
325
417
|
}
|
|
326
418
|
else {
|
|
327
|
-
const
|
|
419
|
+
const dataObj = c2mOptions.data;
|
|
420
|
+
const groups = Object.keys(dataObj);
|
|
328
421
|
groups.forEach((groupName, groupNumber) => {
|
|
329
|
-
if (!isNaN(
|
|
330
|
-
|
|
422
|
+
if (!isNaN(dataObj[groupName][0])) {
|
|
423
|
+
dataObj[groupName] = dataObj[groupName].map((num, index) => {
|
|
331
424
|
return {
|
|
332
425
|
x: index,
|
|
333
426
|
y: num,
|
|
@@ -339,26 +432,25 @@ var chartjs2music = (function (c2mChart) {
|
|
|
339
432
|
});
|
|
340
433
|
}
|
|
341
434
|
else {
|
|
342
|
-
|
|
435
|
+
dataObj[groupName] = dataObj[groupName].map((point, index) => {
|
|
343
436
|
return {
|
|
344
437
|
...point,
|
|
345
438
|
custom: {
|
|
346
|
-
|
|
347
|
-
|
|
439
|
+
...point.custom,
|
|
440
|
+
group: point.custom?.group ?? groupNumber,
|
|
441
|
+
index: point.custom?.index ?? index
|
|
348
442
|
}
|
|
349
443
|
};
|
|
350
444
|
});
|
|
351
445
|
}
|
|
352
446
|
});
|
|
353
447
|
}
|
|
354
|
-
// @ts-ignore
|
|
355
448
|
if (chart.config.options?.scales?.x?.stacked) {
|
|
356
|
-
|
|
357
|
-
|
|
449
|
+
if (c2mOptions.options) {
|
|
450
|
+
c2mOptions.options.stack = true;
|
|
451
|
+
}
|
|
358
452
|
}
|
|
359
|
-
// @ts-ignore
|
|
360
453
|
if (options.audioEngine) {
|
|
361
|
-
// @ts-ignore
|
|
362
454
|
c2mOptions.audioEngine = options.audioEngine;
|
|
363
455
|
}
|
|
364
456
|
if (c2mOptions.data.length === 0) {
|
|
@@ -370,7 +462,6 @@ var chartjs2music = (function (c2mChart) {
|
|
|
370
462
|
const { err, data: c2m } = c2mChart(c2mOptions);
|
|
371
463
|
/* istanbul-ignore-next */
|
|
372
464
|
if (err) {
|
|
373
|
-
// @ts-ignore
|
|
374
465
|
options.errorCallback?.(err);
|
|
375
466
|
return;
|
|
376
467
|
}
|
|
@@ -379,12 +470,32 @@ var chartjs2music = (function (c2mChart) {
|
|
|
379
470
|
}
|
|
380
471
|
chartStates.set(chart, {
|
|
381
472
|
c2m,
|
|
382
|
-
|
|
473
|
+
lastDataSnapshot: createDataSnapshot(chart)
|
|
383
474
|
});
|
|
475
|
+
if (c2m_types === "matrix") {
|
|
476
|
+
const matrixKeydown = (event) => {
|
|
477
|
+
if (event.key !== "ArrowUp" && event.key !== "ArrowDown") {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
event.preventDefault();
|
|
481
|
+
event.stopImmediatePropagation();
|
|
482
|
+
const actions = c2m._availableActions;
|
|
483
|
+
if (event.key === "ArrowUp") {
|
|
484
|
+
actions.next_category();
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
actions.previous_category();
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
chart.canvas.addEventListener("keydown", matrixKeydown, true);
|
|
491
|
+
const state = chartStates.get(chart);
|
|
492
|
+
state.matrixKeydown = matrixKeydown;
|
|
493
|
+
state.matrixKeydownTarget = chart.canvas;
|
|
494
|
+
}
|
|
384
495
|
};
|
|
385
496
|
const plugin = {
|
|
386
497
|
id: "chartjs2music",
|
|
387
|
-
afterInit: (chart,
|
|
498
|
+
afterInit: (chart, _args, options) => {
|
|
388
499
|
if (!chartStates.has(chart)) {
|
|
389
500
|
generateChart(chart, options);
|
|
390
501
|
// Remove tooltip when the chart blurs
|
|
@@ -411,19 +522,17 @@ var chartjs2music = (function (c2mChart) {
|
|
|
411
522
|
if (!chartStates.has(chart)) {
|
|
412
523
|
generateChart(chart, options);
|
|
413
524
|
}
|
|
414
|
-
const { c2m: ref
|
|
525
|
+
const { c2m: ref } = chartStates.get(chart);
|
|
415
526
|
if (!ref) {
|
|
416
527
|
return;
|
|
417
528
|
}
|
|
418
|
-
|
|
419
|
-
const groups =
|
|
420
|
-
|
|
421
|
-
if (ref._options.stack) {
|
|
529
|
+
const refInternal = ref;
|
|
530
|
+
const groups = refInternal._groups.slice(0);
|
|
531
|
+
if (refInternal._options.stack) {
|
|
422
532
|
groups.shift();
|
|
423
533
|
}
|
|
424
534
|
if (args.mode === "hide") {
|
|
425
535
|
const err = ref.setCategoryVisibility(groups[args.index], false);
|
|
426
|
-
visible_groups.splice(args.index, 1);
|
|
427
536
|
if (err) {
|
|
428
537
|
console.error(err);
|
|
429
538
|
}
|
|
@@ -431,24 +540,49 @@ var chartjs2music = (function (c2mChart) {
|
|
|
431
540
|
}
|
|
432
541
|
if (args.mode === "show") {
|
|
433
542
|
const err = ref.setCategoryVisibility(groups[args.index], true);
|
|
434
|
-
visible_groups.push(args.index);
|
|
435
543
|
if (err) {
|
|
436
544
|
console.error(err);
|
|
437
545
|
}
|
|
438
546
|
return;
|
|
439
547
|
}
|
|
440
548
|
},
|
|
549
|
+
afterDatasetsUpdate: (chart, _args, options) => {
|
|
550
|
+
const state = chartStates.get(chart);
|
|
551
|
+
if (!state?.c2m)
|
|
552
|
+
return;
|
|
553
|
+
// Check if data has changed
|
|
554
|
+
const currentSnapshot = createDataSnapshot(chart);
|
|
555
|
+
if (currentSnapshot === state.lastDataSnapshot) {
|
|
556
|
+
return; // No data change, skip update
|
|
557
|
+
}
|
|
558
|
+
// Get chart type
|
|
559
|
+
const { valid, c2m_types } = processChartType(chart);
|
|
560
|
+
if (!valid)
|
|
561
|
+
return;
|
|
562
|
+
// Process data and generate axes
|
|
563
|
+
const { data } = processData(chart.data, c2m_types);
|
|
564
|
+
const axes = generateAxes(chart, options);
|
|
565
|
+
// Update Chart2Music with new data
|
|
566
|
+
state.c2m.setData(data, axes);
|
|
567
|
+
// Update snapshot
|
|
568
|
+
state.lastDataSnapshot = currentSnapshot;
|
|
569
|
+
},
|
|
441
570
|
afterDestroy: (chart) => {
|
|
442
|
-
const
|
|
443
|
-
if (!
|
|
571
|
+
const state = chartStates.get(chart);
|
|
572
|
+
if (!state?.c2m) {
|
|
444
573
|
return;
|
|
445
574
|
}
|
|
575
|
+
const { c2m: ref } = state;
|
|
576
|
+
if (state?.matrixKeydown && state.matrixKeydownTarget) {
|
|
577
|
+
state.matrixKeydownTarget.removeEventListener("keydown", state.matrixKeydown, true);
|
|
578
|
+
}
|
|
446
579
|
ref.cleanUp();
|
|
447
580
|
},
|
|
448
581
|
defaults: {
|
|
449
582
|
cc: null,
|
|
450
583
|
audioEngine: null,
|
|
451
|
-
errorCallback: null
|
|
584
|
+
errorCallback: null,
|
|
585
|
+
options: {}
|
|
452
586
|
}
|
|
453
587
|
};
|
|
454
588
|
|