chartjs-plugin-chart2music 0.6.1 → 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 +93 -77
- package/dist/plugin.amd.js +174 -43
- package/dist/plugin.cjs +333 -230
- package/dist/plugin.d.ts +8 -1
- package/dist/plugin.js +174 -43
- package/dist/plugin.mjs +174 -43
- package/package.json +52 -23
package/README.md
CHANGED
|
@@ -1,117 +1,133 @@
|
|
|
1
1
|
# chartjs-plugin-chart2music
|
|
2
|
-
[](https://badge.fury.io/js/chartjs-plugin-chart2music)
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/chartjs-plugin-chart2music)
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
`chartjs-plugin-chart2music` connects [Chart.js](https://www.chartjs.org/) charts to [Chart2Music](https://chart2music.com/), providing keyboard navigation, screen-reader output, and interactive data sonification. Focus a chart to explore its data with the keyboard; the active data point is also highlighted on the visual chart.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
This plugin is in beta. The supported chart types and integration points are listed below.
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## Install and use
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
```bash
|
|
12
|
+
npm install chart.js chartjs-plugin-chart2music
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Register the plugin globally:
|
|
13
16
|
|
|
14
17
|
```js
|
|
15
|
-
import
|
|
16
|
-
import
|
|
18
|
+
import Chart from "chart.js/auto";
|
|
19
|
+
import chart2music from "chartjs-plugin-chart2music";
|
|
17
20
|
|
|
18
|
-
Chart.register(
|
|
21
|
+
Chart.register(chart2music);
|
|
22
|
+
|
|
23
|
+
new Chart(canvasElement, {
|
|
24
|
+
type: "bar",
|
|
25
|
+
data: {
|
|
26
|
+
labels: ["Q1", "Q2", "Q3", "Q4"],
|
|
27
|
+
datasets: [{label: "Revenue", data: [1, 4, 2, 8]}]
|
|
28
|
+
}
|
|
29
|
+
});
|
|
19
30
|
```
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
Or add it to one chart only:
|
|
22
33
|
|
|
23
34
|
```js
|
|
24
|
-
import {Chart} from "chart.js/auto";
|
|
25
|
-
import chartjs2music from "chartjs-plugin-chart2music";
|
|
26
|
-
|
|
27
35
|
new Chart(canvasElement, {
|
|
28
36
|
type: "bar",
|
|
29
37
|
data: {
|
|
30
|
-
datasets: [{
|
|
31
|
-
data: [1,4,2,8]
|
|
32
|
-
}]
|
|
38
|
+
datasets: [{data: [1, 4, 2, 8]}]
|
|
33
39
|
},
|
|
34
|
-
plugins: [
|
|
35
|
-
})
|
|
36
|
-
|
|
40
|
+
plugins: [chart2music]
|
|
41
|
+
});
|
|
37
42
|
```
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
When no `cc` element is supplied, the plugin creates a caption element immediately after the canvas. Supply your own element when you need to control where the Chart2Music output appears.
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
* `errorCallback` - A callback that will return errors if any arise while the plugin works.
|
|
43
|
-
* `cc` - the equivalent of the [chart2music option `cc`](https://chart2music.com/docs/API/Config#axes)
|
|
44
|
-
* `audioEngine` - the equivalent of the [chart2music option `audioEngine`](https://chart2music.com/docs/API/Config#cc)
|
|
45
|
-
* `axes` - the equivalent of the [chart2music option `axes`](https://chart2music.com/docs/API/Config#axes)
|
|
46
|
-
* `lang` - the language your user speaks. The available languages that Chart2Music supports are: "en", "de", "es", "fr", "it". The default is "en". If you would like to add translations for another language, [Chart2Music is open to PRs](https://github.com/julianna-langston/chart2music).
|
|
46
|
+
## Plugin options
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
```js
|
|
50
|
-
import {Chart} from "chart.js/auto";
|
|
51
|
-
import chartjs2music from "chartjs-plugin-chart2music";
|
|
48
|
+
Configure the plugin through `options.plugins.chartjs2music`:
|
|
52
49
|
|
|
50
|
+
```js
|
|
53
51
|
new Chart(canvasElement, {
|
|
54
52
|
type: "bar",
|
|
55
53
|
data: {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}]
|
|
54
|
+
labels: ["Q1", "Q2", "Q3", "Q4"],
|
|
55
|
+
datasets: [{label: "Revenue", data: [1, 4, 2, 8]}]
|
|
59
56
|
},
|
|
60
57
|
options: {
|
|
61
58
|
plugins: {
|
|
62
59
|
chartjs2music: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Here's a div I made to be the CC
|
|
66
|
-
cc: myDiv,
|
|
67
|
-
// The Y values should all be money
|
|
60
|
+
cc: outputElement,
|
|
61
|
+
lang: "en",
|
|
68
62
|
axes: {
|
|
63
|
+
x: {label: "Quarter"},
|
|
69
64
|
y: {
|
|
70
|
-
|
|
65
|
+
label: "Revenue",
|
|
66
|
+
format: (value) => `$${value.toLocaleString()}`
|
|
71
67
|
}
|
|
72
|
-
}
|
|
68
|
+
},
|
|
69
|
+
options: {
|
|
70
|
+
enableSound: true,
|
|
71
|
+
onFocusCallback: (point) => console.log(point),
|
|
72
|
+
onSelectCallback: ({point}) => console.log(point)
|
|
73
|
+
},
|
|
74
|
+
errorCallback: console.error
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
|
-
}
|
|
76
|
-
plugins: [chartjs2music]
|
|
77
|
+
}
|
|
77
78
|
});
|
|
78
79
|
```
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
```ts
|
|
82
|
-
import {Chart, type ChartTypeRegistry} from "chart.js/auto";
|
|
83
|
-
import chartjs2music from "chartjs-plugin-chart2music";
|
|
81
|
+
Available plugin options:
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
- `cc`: Element that receives Chart2Music's accessible chart caption. Optional.
|
|
84
|
+
- `lang`: Chart2Music language. Supported values include `en`, `de`, `es`, `fr`, and `it`; default is `en`.
|
|
85
|
+
- `axes`: Chart2Music axis configuration, including labels and value formatters.
|
|
86
|
+
- `audioEngine`: A custom Chart2Music audio engine.
|
|
87
|
+
- `options`: Chart2Music interaction options such as `enableSound`, `onFocusCallback`, and `onSelectCallback`.
|
|
88
|
+
- `errorCallback`: Receives errors while the plugin initializes or updates a chart.
|
|
89
|
+
|
|
90
|
+
## Supported charts
|
|
91
|
+
|
|
92
|
+
The plugin supports Chart.js bar, line, pie, doughnut, polar area, and scatter charts, including mixed bar/line configurations. It also supports:
|
|
93
|
+
|
|
94
|
+
- Box plots from [`@sgratzl/chartjs-chart-boxplot`](https://www.npmjs.com/package/@sgratzl/chartjs-chart-boxplot).
|
|
95
|
+
- Matrix plots from [`chartjs-chart-matrix`](https://www.npmjs.com/package/chartjs-chart-matrix).
|
|
96
|
+
- Word clouds from [`chartjs-chart-wordcloud`](https://www.npmjs.com/package/chartjs-chart-wordcloud).
|
|
97
|
+
- Chart titles, axis titles, minimum and maximum values, linear and logarithmic axes, custom axis formatting, dataset visibility, and chart data updates.
|
|
98
|
+
|
|
99
|
+
Visual-only Chart.js settings such as color, padding, and line thickness do not affect the sonification. Advanced Chart.js parsing configurations and nonstandard axis identifiers are not currently supported.
|
|
100
|
+
|
|
101
|
+
## Future support
|
|
102
|
+
|
|
103
|
+
Planned support includes:
|
|
104
|
+
|
|
105
|
+
- Chart subtitles.
|
|
106
|
+
- Radar plots.
|
|
107
|
+
- Error bars via [`chartjs-chart-error-bars`](https://www.npmjs.com/package/chartjs-chart-error-bars).
|
|
108
|
+
- Parallel coordinate plots via [`chartjs-chart-pcp`](https://www.npmjs.com/package/chartjs-chart-pcp).
|
|
109
|
+
- Zoom interactions via [`chartjs-plugin-zoom`](https://www.npmjs.com/package/chartjs-plugin-zoom).
|
|
110
|
+
|
|
111
|
+
## Examples and Storybook
|
|
112
|
+
|
|
113
|
+
The repository includes runnable chart configurations under [`stories/charts`](./stories/charts), covering the supported chart types and plugin integrations. A Storybook catalogue exposes these examples as Bar, Line, Multi-chart, Distribution, Circular, Scatter, Matrix, Word cloud, and Options stories.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm run storybook
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Build the static Storybook site with:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm run build-storybook
|
|
87
123
|
```
|
|
88
124
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* Chart title
|
|
99
|
-
* Most data structures (not including `parsing` or non-standard axes identifiers)
|
|
100
|
-
* Dataset visibility (when you show/hide a category from the legend)
|
|
101
|
-
|
|
102
|
-
Note that visual-specific chart features are ignored. This includes things like color, padding, line thickness, etc.
|
|
103
|
-
|
|
104
|
-
Things we plan to support in the future:
|
|
105
|
-
* Interactions and updates
|
|
106
|
-
* Other chart.js plugins
|
|
107
|
-
* Complex `parsing` options for data
|
|
108
|
-
* Date/Time support for axes
|
|
109
|
-
* Subtitle
|
|
110
|
-
* Radar charts
|
|
111
|
-
* Custom formatting for axis tick values
|
|
112
|
-
|
|
113
|
-
Plugins we plan to support in the future:
|
|
114
|
-
* [chartjs-char-error-bars](https://www.npmjs.com/package/chartjs-chart-error-bars)
|
|
115
|
-
* [chartjs-chart-matrix](https://www.npmjs.com/package/chartjs-chart-matrix)
|
|
116
|
-
* [chartjs-chart-pcp](https://www.npmjs.com/package/chartjs-chart-pcp)
|
|
117
|
-
* [chartjs-plugin-zoom](https://www.npmjs.com/package/chartjs-plugin-zoom)
|
|
125
|
+
More examples are available in the [CodePen collection](https://codepen.io/collection/VYEvEQ).
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm test
|
|
131
|
+
npm run build
|
|
132
|
+
npm run depcheck
|
|
133
|
+
```
|
package/dist/plugin.amd.js
CHANGED
|
@@ -84,7 +84,8 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
84
84
|
boxplot: "box",
|
|
85
85
|
radar: "bar",
|
|
86
86
|
wordCloud: "bar",
|
|
87
|
-
scatter: "scatter"
|
|
87
|
+
scatter: "scatter",
|
|
88
|
+
matrix: "matrix"
|
|
88
89
|
};
|
|
89
90
|
const processChartType = (chart) => {
|
|
90
91
|
const topLevelType = chart.config.type;
|
|
@@ -134,13 +135,13 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
134
135
|
}
|
|
135
136
|
return axis;
|
|
136
137
|
};
|
|
137
|
-
const generateAxes = (chart) => {
|
|
138
|
+
const generateAxes = (chart, options) => {
|
|
138
139
|
const axes = {
|
|
139
140
|
x: {
|
|
140
141
|
...generateAxisInfo(chart.options?.scales?.x, chart),
|
|
141
142
|
},
|
|
142
143
|
y: {
|
|
143
|
-
format: (value) => value.toLocaleString(),
|
|
144
|
+
format: options?.axes?.y?.format || ((value) => value.toLocaleString()),
|
|
144
145
|
...generateAxisInfo(chart.options?.scales?.y, chart),
|
|
145
146
|
}
|
|
146
147
|
};
|
|
@@ -159,6 +160,55 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
159
160
|
}
|
|
160
161
|
return data;
|
|
161
162
|
};
|
|
163
|
+
const labelIndex = (labels, value) => {
|
|
164
|
+
let index = labels.indexOf(value);
|
|
165
|
+
if (index === -1) {
|
|
166
|
+
labels.push(value);
|
|
167
|
+
index = labels.length - 1;
|
|
168
|
+
}
|
|
169
|
+
return index;
|
|
170
|
+
};
|
|
171
|
+
const processMatrixDataPoints = (data, datasetIndex, xLabels, yLabels) => {
|
|
172
|
+
return data.map((point, index) => {
|
|
173
|
+
const x = typeof point.x === "string" ? labelIndex(xLabels, point.x) : point.x;
|
|
174
|
+
const y = typeof point.y === "string" ? labelIndex(yLabels, point.y) : point.y;
|
|
175
|
+
return {
|
|
176
|
+
...point,
|
|
177
|
+
x,
|
|
178
|
+
y,
|
|
179
|
+
custom: {
|
|
180
|
+
...point.custom,
|
|
181
|
+
group: datasetIndex,
|
|
182
|
+
datasetIndex,
|
|
183
|
+
index
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
const processMatrixData = (data) => {
|
|
189
|
+
const xLabels = [];
|
|
190
|
+
const yLabels = [];
|
|
191
|
+
// Chart2Music represents matrix rows as groups and columns as points within
|
|
192
|
+
// each group. This preserves two-dimensional keyboard navigation.
|
|
193
|
+
const result = {};
|
|
194
|
+
data.datasets.forEach((obj, index) => {
|
|
195
|
+
const points = processMatrixDataPoints(obj.data, index, xLabels, yLabels);
|
|
196
|
+
points.forEach((point) => {
|
|
197
|
+
const rowLabel = typeof point.y === "number" && yLabels[point.y] !== undefined
|
|
198
|
+
? yLabels[point.y]
|
|
199
|
+
: String(point.y);
|
|
200
|
+
const groupName = data.datasets.length === 1
|
|
201
|
+
? rowLabel
|
|
202
|
+
: `${obj.label ?? `Group ${index + 1}`}: ${rowLabel}`;
|
|
203
|
+
if (!result[groupName]) {
|
|
204
|
+
result[groupName] = [];
|
|
205
|
+
}
|
|
206
|
+
result[groupName].push(point);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
Object.values(result).forEach((row) => row.sort((a, b) => a.x - b.x));
|
|
210
|
+
return { groups: Object.keys(result), data: result, xLabels, yLabels };
|
|
211
|
+
};
|
|
162
212
|
const scrubX = (data) => {
|
|
163
213
|
const blackboard = JSON.parse(JSON.stringify(data));
|
|
164
214
|
let labels = [];
|
|
@@ -173,11 +223,18 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
173
223
|
});
|
|
174
224
|
return { labels, data: blackboard };
|
|
175
225
|
}
|
|
226
|
+
else {
|
|
227
|
+
// Grouped
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
176
230
|
};
|
|
177
231
|
const processData = (data, c2m_types) => {
|
|
178
232
|
if (c2m_types === "box") {
|
|
179
233
|
return processBoxData(data);
|
|
180
234
|
}
|
|
235
|
+
if (c2m_types === "matrix") {
|
|
236
|
+
return processMatrixData(data);
|
|
237
|
+
}
|
|
181
238
|
let groups = [];
|
|
182
239
|
if (data.datasets.length === 1) {
|
|
183
240
|
return {
|
|
@@ -209,26 +266,36 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
209
266
|
canvas.insertAdjacentElement("afterend", cc);
|
|
210
267
|
return cc;
|
|
211
268
|
};
|
|
269
|
+
const createDataSnapshot = (chart) => {
|
|
270
|
+
return JSON.stringify({
|
|
271
|
+
datasets: chart.data.datasets.map(ds => ds.data),
|
|
272
|
+
labels: chart.data.labels
|
|
273
|
+
});
|
|
274
|
+
};
|
|
212
275
|
const displayPoint = (chart) => {
|
|
213
276
|
if (!chartStates.has(chart)) {
|
|
214
277
|
return;
|
|
215
278
|
}
|
|
216
|
-
const { c2m: ref
|
|
279
|
+
const { c2m: ref } = chartStates.get(chart);
|
|
217
280
|
const { point, index } = ref.getCurrent();
|
|
281
|
+
// Use Chart2Music's internal visible group tracking
|
|
282
|
+
// @ts-ignore - accessing internal Chart2Music property
|
|
283
|
+
const visibleGroupIndices = ref._visible_group_indices?.slice(1) || [];
|
|
218
284
|
try {
|
|
219
285
|
const highlightElements = [];
|
|
220
286
|
if ("custom" in point) {
|
|
287
|
+
const customPoint = point;
|
|
221
288
|
highlightElements.push({
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// @ts-ignore
|
|
225
|
-
index: point.custom.index
|
|
289
|
+
datasetIndex: customPoint.custom.datasetIndex ?? customPoint.custom.group,
|
|
290
|
+
index: customPoint.custom.index
|
|
226
291
|
});
|
|
227
292
|
}
|
|
228
293
|
else {
|
|
229
|
-
|
|
294
|
+
// For stacked charts, Chart2Music includes an "All" group at index 0,
|
|
295
|
+
// so we subtract 1 to get the actual dataset index
|
|
296
|
+
visibleGroupIndices.forEach((groupIndex) => {
|
|
230
297
|
highlightElements.push({
|
|
231
|
-
datasetIndex,
|
|
298
|
+
datasetIndex: groupIndex - 1,
|
|
232
299
|
index
|
|
233
300
|
});
|
|
234
301
|
});
|
|
@@ -244,11 +311,10 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
244
311
|
const generateChart = (chart, options) => {
|
|
245
312
|
const { valid, c2m_types, invalidType } = processChartType(chart);
|
|
246
313
|
if (!valid) {
|
|
247
|
-
// @ts-ignore
|
|
248
314
|
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(", ")}`);
|
|
249
315
|
return;
|
|
250
316
|
}
|
|
251
|
-
let axes = generateAxes(chart);
|
|
317
|
+
let axes = generateAxes(chart, options);
|
|
252
318
|
if (chart.config.type === "wordCloud") {
|
|
253
319
|
delete axes.x.minimum;
|
|
254
320
|
delete axes.x.maximum;
|
|
@@ -263,8 +329,20 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
263
329
|
}
|
|
264
330
|
// Generate CC element
|
|
265
331
|
const cc = determineCCElement(chart.canvas, options.cc);
|
|
266
|
-
const
|
|
332
|
+
const processedData = processData(chart.data, c2m_types);
|
|
333
|
+
const { data } = processedData;
|
|
267
334
|
// lastDataObj = JSON.stringify(data);
|
|
335
|
+
if (c2m_types === "matrix") {
|
|
336
|
+
if (processedData.xLabels?.length > 0) {
|
|
337
|
+
axes.x.valueLabels = processedData.xLabels.slice(0);
|
|
338
|
+
}
|
|
339
|
+
if (processedData.yLabels?.length > 0) {
|
|
340
|
+
// Category labels provide the Matrix Plot's Y-axis formatter.
|
|
341
|
+
// The general numeric formatter would otherwise announce row indexes.
|
|
342
|
+
delete axes.y.format;
|
|
343
|
+
axes.y.valueLabels = processedData.yLabels.slice(0);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
268
346
|
let scrub = scrubX(data);
|
|
269
347
|
if (scrub?.labels && scrub?.labels?.length > 0) { // Something was scrubbed
|
|
270
348
|
if (!chart.data.labels || chart.data.labels.length === 0) {
|
|
@@ -286,6 +364,13 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
286
364
|
...(options.axes?.y)
|
|
287
365
|
},
|
|
288
366
|
};
|
|
367
|
+
// Start with plugin's internal onFocusCallback
|
|
368
|
+
const pluginOnFocusCallback = () => {
|
|
369
|
+
displayPoint(chart);
|
|
370
|
+
};
|
|
371
|
+
// Merge user's options, wrapping onFocusCallback if provided
|
|
372
|
+
const userOptions = options.options || {};
|
|
373
|
+
const userOnFocusCallback = userOptions.onFocusCallback;
|
|
289
374
|
const c2mOptions = {
|
|
290
375
|
cc,
|
|
291
376
|
element: chart.canvas,
|
|
@@ -294,10 +379,13 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
294
379
|
title: determineChartTitle(chart.options),
|
|
295
380
|
axes,
|
|
296
381
|
options: {
|
|
297
|
-
|
|
298
|
-
onFocusCallback:
|
|
299
|
-
|
|
300
|
-
|
|
382
|
+
...userOptions,
|
|
383
|
+
onFocusCallback: userOnFocusCallback
|
|
384
|
+
? (point) => {
|
|
385
|
+
pluginOnFocusCallback();
|
|
386
|
+
userOnFocusCallback(point);
|
|
387
|
+
}
|
|
388
|
+
: pluginOnFocusCallback
|
|
301
389
|
}
|
|
302
390
|
};
|
|
303
391
|
if (Array.isArray(c2mOptions.data)) {
|
|
@@ -306,8 +394,9 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
306
394
|
return {
|
|
307
395
|
...point,
|
|
308
396
|
custom: {
|
|
309
|
-
|
|
310
|
-
|
|
397
|
+
...point.custom,
|
|
398
|
+
group: point.custom?.group ?? 0,
|
|
399
|
+
index: point.custom?.index ?? index
|
|
311
400
|
}
|
|
312
401
|
};
|
|
313
402
|
});
|
|
@@ -326,10 +415,11 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
326
415
|
}
|
|
327
416
|
}
|
|
328
417
|
else {
|
|
329
|
-
const
|
|
418
|
+
const dataObj = c2mOptions.data;
|
|
419
|
+
const groups = Object.keys(dataObj);
|
|
330
420
|
groups.forEach((groupName, groupNumber) => {
|
|
331
|
-
if (!isNaN(
|
|
332
|
-
|
|
421
|
+
if (!isNaN(dataObj[groupName][0])) {
|
|
422
|
+
dataObj[groupName] = dataObj[groupName].map((num, index) => {
|
|
333
423
|
return {
|
|
334
424
|
x: index,
|
|
335
425
|
y: num,
|
|
@@ -341,26 +431,25 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
341
431
|
});
|
|
342
432
|
}
|
|
343
433
|
else {
|
|
344
|
-
|
|
434
|
+
dataObj[groupName] = dataObj[groupName].map((point, index) => {
|
|
345
435
|
return {
|
|
346
436
|
...point,
|
|
347
437
|
custom: {
|
|
348
|
-
|
|
349
|
-
|
|
438
|
+
...point.custom,
|
|
439
|
+
group: point.custom?.group ?? groupNumber,
|
|
440
|
+
index: point.custom?.index ?? index
|
|
350
441
|
}
|
|
351
442
|
};
|
|
352
443
|
});
|
|
353
444
|
}
|
|
354
445
|
});
|
|
355
446
|
}
|
|
356
|
-
// @ts-ignore
|
|
357
447
|
if (chart.config.options?.scales?.x?.stacked) {
|
|
358
|
-
|
|
359
|
-
|
|
448
|
+
if (c2mOptions.options) {
|
|
449
|
+
c2mOptions.options.stack = true;
|
|
450
|
+
}
|
|
360
451
|
}
|
|
361
|
-
// @ts-ignore
|
|
362
452
|
if (options.audioEngine) {
|
|
363
|
-
// @ts-ignore
|
|
364
453
|
c2mOptions.audioEngine = options.audioEngine;
|
|
365
454
|
}
|
|
366
455
|
if (c2mOptions.data.length === 0) {
|
|
@@ -372,7 +461,6 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
372
461
|
const { err, data: c2m } = c2mChart(c2mOptions);
|
|
373
462
|
/* istanbul-ignore-next */
|
|
374
463
|
if (err) {
|
|
375
|
-
// @ts-ignore
|
|
376
464
|
options.errorCallback?.(err);
|
|
377
465
|
return;
|
|
378
466
|
}
|
|
@@ -381,12 +469,32 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
381
469
|
}
|
|
382
470
|
chartStates.set(chart, {
|
|
383
471
|
c2m,
|
|
384
|
-
|
|
472
|
+
lastDataSnapshot: createDataSnapshot(chart)
|
|
385
473
|
});
|
|
474
|
+
if (c2m_types === "matrix") {
|
|
475
|
+
const matrixKeydown = (event) => {
|
|
476
|
+
if (event.key !== "ArrowUp" && event.key !== "ArrowDown") {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
event.preventDefault();
|
|
480
|
+
event.stopImmediatePropagation();
|
|
481
|
+
const actions = c2m._availableActions;
|
|
482
|
+
if (event.key === "ArrowUp") {
|
|
483
|
+
actions.next_category();
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
actions.previous_category();
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
chart.canvas.addEventListener("keydown", matrixKeydown, true);
|
|
490
|
+
const state = chartStates.get(chart);
|
|
491
|
+
state.matrixKeydown = matrixKeydown;
|
|
492
|
+
state.matrixKeydownTarget = chart.canvas;
|
|
493
|
+
}
|
|
386
494
|
};
|
|
387
495
|
const plugin = {
|
|
388
496
|
id: "chartjs2music",
|
|
389
|
-
afterInit: (chart,
|
|
497
|
+
afterInit: (chart, _args, options) => {
|
|
390
498
|
if (!chartStates.has(chart)) {
|
|
391
499
|
generateChart(chart, options);
|
|
392
500
|
// Remove tooltip when the chart blurs
|
|
@@ -413,19 +521,17 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
413
521
|
if (!chartStates.has(chart)) {
|
|
414
522
|
generateChart(chart, options);
|
|
415
523
|
}
|
|
416
|
-
const { c2m: ref
|
|
524
|
+
const { c2m: ref } = chartStates.get(chart);
|
|
417
525
|
if (!ref) {
|
|
418
526
|
return;
|
|
419
527
|
}
|
|
420
|
-
|
|
421
|
-
const groups =
|
|
422
|
-
|
|
423
|
-
if (ref._options.stack) {
|
|
528
|
+
const refInternal = ref;
|
|
529
|
+
const groups = refInternal._groups.slice(0);
|
|
530
|
+
if (refInternal._options.stack) {
|
|
424
531
|
groups.shift();
|
|
425
532
|
}
|
|
426
533
|
if (args.mode === "hide") {
|
|
427
534
|
const err = ref.setCategoryVisibility(groups[args.index], false);
|
|
428
|
-
visible_groups.splice(args.index, 1);
|
|
429
535
|
if (err) {
|
|
430
536
|
console.error(err);
|
|
431
537
|
}
|
|
@@ -433,24 +539,49 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
433
539
|
}
|
|
434
540
|
if (args.mode === "show") {
|
|
435
541
|
const err = ref.setCategoryVisibility(groups[args.index], true);
|
|
436
|
-
visible_groups.push(args.index);
|
|
437
542
|
if (err) {
|
|
438
543
|
console.error(err);
|
|
439
544
|
}
|
|
440
545
|
return;
|
|
441
546
|
}
|
|
442
547
|
},
|
|
548
|
+
afterDatasetsUpdate: (chart, _args, options) => {
|
|
549
|
+
const state = chartStates.get(chart);
|
|
550
|
+
if (!state?.c2m)
|
|
551
|
+
return;
|
|
552
|
+
// Check if data has changed
|
|
553
|
+
const currentSnapshot = createDataSnapshot(chart);
|
|
554
|
+
if (currentSnapshot === state.lastDataSnapshot) {
|
|
555
|
+
return; // No data change, skip update
|
|
556
|
+
}
|
|
557
|
+
// Get chart type
|
|
558
|
+
const { valid, c2m_types } = processChartType(chart);
|
|
559
|
+
if (!valid)
|
|
560
|
+
return;
|
|
561
|
+
// Process data and generate axes
|
|
562
|
+
const { data } = processData(chart.data, c2m_types);
|
|
563
|
+
const axes = generateAxes(chart, options);
|
|
564
|
+
// Update Chart2Music with new data
|
|
565
|
+
state.c2m.setData(data, axes);
|
|
566
|
+
// Update snapshot
|
|
567
|
+
state.lastDataSnapshot = currentSnapshot;
|
|
568
|
+
},
|
|
443
569
|
afterDestroy: (chart) => {
|
|
444
|
-
const
|
|
445
|
-
if (!
|
|
570
|
+
const state = chartStates.get(chart);
|
|
571
|
+
if (!state?.c2m) {
|
|
446
572
|
return;
|
|
447
573
|
}
|
|
574
|
+
const { c2m: ref } = state;
|
|
575
|
+
if (state?.matrixKeydown && state.matrixKeydownTarget) {
|
|
576
|
+
state.matrixKeydownTarget.removeEventListener("keydown", state.matrixKeydown, true);
|
|
577
|
+
}
|
|
448
578
|
ref.cleanUp();
|
|
449
579
|
},
|
|
450
580
|
defaults: {
|
|
451
581
|
cc: null,
|
|
452
582
|
audioEngine: null,
|
|
453
|
-
errorCallback: null
|
|
583
|
+
errorCallback: null,
|
|
584
|
+
options: {}
|
|
454
585
|
}
|
|
455
586
|
};
|
|
456
587
|
|