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/README.md
CHANGED
|
@@ -1,108 +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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
81
|
+
Available plugin options:
|
|
82
|
+
|
|
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
|
|
123
|
+
```
|
|
124
|
+
|
|
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;
|
|
@@ -109,7 +110,7 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
109
110
|
};
|
|
110
111
|
const generateAxisInfo = (chartAxisInfo, chart) => {
|
|
111
112
|
const axis = {};
|
|
112
|
-
if (chartAxisInfo?.min) {
|
|
113
|
+
if (chartAxisInfo?.min !== undefined) {
|
|
113
114
|
if (typeof chartAxisInfo.min === "string") {
|
|
114
115
|
axis.minimum = chart.data.labels.indexOf(chartAxisInfo.min);
|
|
115
116
|
}
|
|
@@ -117,7 +118,7 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
117
118
|
axis.minimum = chartAxisInfo.min;
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
|
-
if (chartAxisInfo?.max) {
|
|
121
|
+
if (chartAxisInfo?.max !== undefined) {
|
|
121
122
|
if (typeof chartAxisInfo.max === "string") {
|
|
122
123
|
axis.maximum = chart.data.labels.indexOf(chartAxisInfo.max);
|
|
123
124
|
}
|
|
@@ -134,17 +135,20 @@ 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
|
-
valueLabels: chart.data.labels.slice(0)
|
|
142
142
|
},
|
|
143
143
|
y: {
|
|
144
|
+
format: options?.axes?.y?.format || ((value) => value.toLocaleString()),
|
|
144
145
|
...generateAxisInfo(chart.options?.scales?.y, chart),
|
|
145
|
-
format: (value) => value.toLocaleString()
|
|
146
146
|
}
|
|
147
147
|
};
|
|
148
|
+
const xAxisValueLabels = chart.data.labels.slice(0);
|
|
149
|
+
if (xAxisValueLabels.length > 0) {
|
|
150
|
+
axes.x.valueLabels = xAxisValueLabels;
|
|
151
|
+
}
|
|
148
152
|
return axes;
|
|
149
153
|
};
|
|
150
154
|
const whichDataStructure = (data) => {
|
|
@@ -156,6 +160,55 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
156
160
|
}
|
|
157
161
|
return data;
|
|
158
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
|
+
};
|
|
159
212
|
const scrubX = (data) => {
|
|
160
213
|
const blackboard = JSON.parse(JSON.stringify(data));
|
|
161
214
|
let labels = [];
|
|
@@ -170,11 +223,18 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
170
223
|
});
|
|
171
224
|
return { labels, data: blackboard };
|
|
172
225
|
}
|
|
226
|
+
else {
|
|
227
|
+
// Grouped
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
173
230
|
};
|
|
174
231
|
const processData = (data, c2m_types) => {
|
|
175
232
|
if (c2m_types === "box") {
|
|
176
233
|
return processBoxData(data);
|
|
177
234
|
}
|
|
235
|
+
if (c2m_types === "matrix") {
|
|
236
|
+
return processMatrixData(data);
|
|
237
|
+
}
|
|
178
238
|
let groups = [];
|
|
179
239
|
if (data.datasets.length === 1) {
|
|
180
240
|
return {
|
|
@@ -206,26 +266,36 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
206
266
|
canvas.insertAdjacentElement("afterend", cc);
|
|
207
267
|
return cc;
|
|
208
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
|
+
};
|
|
209
275
|
const displayPoint = (chart) => {
|
|
210
276
|
if (!chartStates.has(chart)) {
|
|
211
277
|
return;
|
|
212
278
|
}
|
|
213
|
-
const { c2m: ref
|
|
279
|
+
const { c2m: ref } = chartStates.get(chart);
|
|
214
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) || [];
|
|
215
284
|
try {
|
|
216
285
|
const highlightElements = [];
|
|
217
286
|
if ("custom" in point) {
|
|
287
|
+
const customPoint = point;
|
|
218
288
|
highlightElements.push({
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// @ts-ignore
|
|
222
|
-
index: point.custom.index
|
|
289
|
+
datasetIndex: customPoint.custom.datasetIndex ?? customPoint.custom.group,
|
|
290
|
+
index: customPoint.custom.index
|
|
223
291
|
});
|
|
224
292
|
}
|
|
225
293
|
else {
|
|
226
|
-
|
|
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) => {
|
|
227
297
|
highlightElements.push({
|
|
228
|
-
datasetIndex,
|
|
298
|
+
datasetIndex: groupIndex - 1,
|
|
229
299
|
index
|
|
230
300
|
});
|
|
231
301
|
});
|
|
@@ -241,11 +311,10 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
241
311
|
const generateChart = (chart, options) => {
|
|
242
312
|
const { valid, c2m_types, invalidType } = processChartType(chart);
|
|
243
313
|
if (!valid) {
|
|
244
|
-
// @ts-ignore
|
|
245
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(", ")}`);
|
|
246
315
|
return;
|
|
247
316
|
}
|
|
248
|
-
let axes = generateAxes(chart);
|
|
317
|
+
let axes = generateAxes(chart, options);
|
|
249
318
|
if (chart.config.type === "wordCloud") {
|
|
250
319
|
delete axes.x.minimum;
|
|
251
320
|
delete axes.x.maximum;
|
|
@@ -260,8 +329,20 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
260
329
|
}
|
|
261
330
|
// Generate CC element
|
|
262
331
|
const cc = determineCCElement(chart.canvas, options.cc);
|
|
263
|
-
const
|
|
332
|
+
const processedData = processData(chart.data, c2m_types);
|
|
333
|
+
const { data } = processedData;
|
|
264
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
|
+
}
|
|
265
346
|
let scrub = scrubX(data);
|
|
266
347
|
if (scrub?.labels && scrub?.labels?.length > 0) { // Something was scrubbed
|
|
267
348
|
if (!chart.data.labels || chart.data.labels.length === 0) {
|
|
@@ -283,6 +364,13 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
283
364
|
...(options.axes?.y)
|
|
284
365
|
},
|
|
285
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;
|
|
286
374
|
const c2mOptions = {
|
|
287
375
|
cc,
|
|
288
376
|
element: chart.canvas,
|
|
@@ -291,10 +379,13 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
291
379
|
title: determineChartTitle(chart.options),
|
|
292
380
|
axes,
|
|
293
381
|
options: {
|
|
294
|
-
|
|
295
|
-
onFocusCallback:
|
|
296
|
-
|
|
297
|
-
|
|
382
|
+
...userOptions,
|
|
383
|
+
onFocusCallback: userOnFocusCallback
|
|
384
|
+
? (point) => {
|
|
385
|
+
pluginOnFocusCallback();
|
|
386
|
+
userOnFocusCallback(point);
|
|
387
|
+
}
|
|
388
|
+
: pluginOnFocusCallback
|
|
298
389
|
}
|
|
299
390
|
};
|
|
300
391
|
if (Array.isArray(c2mOptions.data)) {
|
|
@@ -303,8 +394,9 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
303
394
|
return {
|
|
304
395
|
...point,
|
|
305
396
|
custom: {
|
|
306
|
-
|
|
307
|
-
|
|
397
|
+
...point.custom,
|
|
398
|
+
group: point.custom?.group ?? 0,
|
|
399
|
+
index: point.custom?.index ?? index
|
|
308
400
|
}
|
|
309
401
|
};
|
|
310
402
|
});
|
|
@@ -323,10 +415,11 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
323
415
|
}
|
|
324
416
|
}
|
|
325
417
|
else {
|
|
326
|
-
const
|
|
418
|
+
const dataObj = c2mOptions.data;
|
|
419
|
+
const groups = Object.keys(dataObj);
|
|
327
420
|
groups.forEach((groupName, groupNumber) => {
|
|
328
|
-
if (!isNaN(
|
|
329
|
-
|
|
421
|
+
if (!isNaN(dataObj[groupName][0])) {
|
|
422
|
+
dataObj[groupName] = dataObj[groupName].map((num, index) => {
|
|
330
423
|
return {
|
|
331
424
|
x: index,
|
|
332
425
|
y: num,
|
|
@@ -338,26 +431,25 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
338
431
|
});
|
|
339
432
|
}
|
|
340
433
|
else {
|
|
341
|
-
|
|
434
|
+
dataObj[groupName] = dataObj[groupName].map((point, index) => {
|
|
342
435
|
return {
|
|
343
436
|
...point,
|
|
344
437
|
custom: {
|
|
345
|
-
|
|
346
|
-
|
|
438
|
+
...point.custom,
|
|
439
|
+
group: point.custom?.group ?? groupNumber,
|
|
440
|
+
index: point.custom?.index ?? index
|
|
347
441
|
}
|
|
348
442
|
};
|
|
349
443
|
});
|
|
350
444
|
}
|
|
351
445
|
});
|
|
352
446
|
}
|
|
353
|
-
// @ts-ignore
|
|
354
447
|
if (chart.config.options?.scales?.x?.stacked) {
|
|
355
|
-
|
|
356
|
-
|
|
448
|
+
if (c2mOptions.options) {
|
|
449
|
+
c2mOptions.options.stack = true;
|
|
450
|
+
}
|
|
357
451
|
}
|
|
358
|
-
// @ts-ignore
|
|
359
452
|
if (options.audioEngine) {
|
|
360
|
-
// @ts-ignore
|
|
361
453
|
c2mOptions.audioEngine = options.audioEngine;
|
|
362
454
|
}
|
|
363
455
|
if (c2mOptions.data.length === 0) {
|
|
@@ -369,7 +461,6 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
369
461
|
const { err, data: c2m } = c2mChart(c2mOptions);
|
|
370
462
|
/* istanbul-ignore-next */
|
|
371
463
|
if (err) {
|
|
372
|
-
// @ts-ignore
|
|
373
464
|
options.errorCallback?.(err);
|
|
374
465
|
return;
|
|
375
466
|
}
|
|
@@ -378,12 +469,32 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
378
469
|
}
|
|
379
470
|
chartStates.set(chart, {
|
|
380
471
|
c2m,
|
|
381
|
-
|
|
472
|
+
lastDataSnapshot: createDataSnapshot(chart)
|
|
382
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
|
+
}
|
|
383
494
|
};
|
|
384
495
|
const plugin = {
|
|
385
496
|
id: "chartjs2music",
|
|
386
|
-
afterInit: (chart,
|
|
497
|
+
afterInit: (chart, _args, options) => {
|
|
387
498
|
if (!chartStates.has(chart)) {
|
|
388
499
|
generateChart(chart, options);
|
|
389
500
|
// Remove tooltip when the chart blurs
|
|
@@ -410,19 +521,17 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
410
521
|
if (!chartStates.has(chart)) {
|
|
411
522
|
generateChart(chart, options);
|
|
412
523
|
}
|
|
413
|
-
const { c2m: ref
|
|
524
|
+
const { c2m: ref } = chartStates.get(chart);
|
|
414
525
|
if (!ref) {
|
|
415
526
|
return;
|
|
416
527
|
}
|
|
417
|
-
|
|
418
|
-
const groups =
|
|
419
|
-
|
|
420
|
-
if (ref._options.stack) {
|
|
528
|
+
const refInternal = ref;
|
|
529
|
+
const groups = refInternal._groups.slice(0);
|
|
530
|
+
if (refInternal._options.stack) {
|
|
421
531
|
groups.shift();
|
|
422
532
|
}
|
|
423
533
|
if (args.mode === "hide") {
|
|
424
534
|
const err = ref.setCategoryVisibility(groups[args.index], false);
|
|
425
|
-
visible_groups.splice(args.index, 1);
|
|
426
535
|
if (err) {
|
|
427
536
|
console.error(err);
|
|
428
537
|
}
|
|
@@ -430,24 +539,49 @@ define(['chart2music'], (function (c2mChart) { 'use strict';
|
|
|
430
539
|
}
|
|
431
540
|
if (args.mode === "show") {
|
|
432
541
|
const err = ref.setCategoryVisibility(groups[args.index], true);
|
|
433
|
-
visible_groups.push(args.index);
|
|
434
542
|
if (err) {
|
|
435
543
|
console.error(err);
|
|
436
544
|
}
|
|
437
545
|
return;
|
|
438
546
|
}
|
|
439
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
|
+
},
|
|
440
569
|
afterDestroy: (chart) => {
|
|
441
|
-
const
|
|
442
|
-
if (!
|
|
570
|
+
const state = chartStates.get(chart);
|
|
571
|
+
if (!state?.c2m) {
|
|
443
572
|
return;
|
|
444
573
|
}
|
|
574
|
+
const { c2m: ref } = state;
|
|
575
|
+
if (state?.matrixKeydown && state.matrixKeydownTarget) {
|
|
576
|
+
state.matrixKeydownTarget.removeEventListener("keydown", state.matrixKeydown, true);
|
|
577
|
+
}
|
|
445
578
|
ref.cleanUp();
|
|
446
579
|
},
|
|
447
580
|
defaults: {
|
|
448
581
|
cc: null,
|
|
449
582
|
audioEngine: null,
|
|
450
|
-
errorCallback: null
|
|
583
|
+
errorCallback: null,
|
|
584
|
+
options: {}
|
|
451
585
|
}
|
|
452
586
|
};
|
|
453
587
|
|