chartjs-plugin-chart2music 0.0.1 → 0.0.3
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 +45 -10
- package/dist/plugin.js +3 -5
- package/dist/plugin.mjs +196 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
#
|
|
2
|
-
chartjs plugin for chart2music
|
|
1
|
+
# chartjs-plugin-chart2music
|
|
2
|
+
chartjs plugin for chart2music.
|
|
3
|
+
|
|
4
|
+
**This is a beta release of this plugin**
|
|
5
|
+
|
|
6
|
+
Turns your chart.js charts into music so the blind can hear data. This plugin will automatically add Chart2Music, an interactive sonification library, to your chart.js charts. The contents of the chart element will be modified to best support screen reader users, and the interactions will be visually synchronized to provide support for keyboard-only users.
|
|
3
7
|
|
|
4
8
|
## Getting started
|
|
5
9
|
|
|
@@ -7,17 +11,48 @@ Add the chartjs2music plugin to your existing chart.js code like this:
|
|
|
7
11
|
|
|
8
12
|
```js
|
|
9
13
|
import {Chart} from "chart.js/auto";
|
|
10
|
-
import
|
|
14
|
+
import plugin from "chartjs-plugin-chart2music";
|
|
11
15
|
|
|
12
|
-
Chart.register(
|
|
16
|
+
Chart.register(plugin);
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
That will register the plugin globally. Alternatively, if you only want to enable for a given chart, you can do this:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import {Chart} from "chart.js/auto";
|
|
23
|
+
import chartjs2music from "chartjs-plugin-chart2music";
|
|
24
|
+
|
|
25
|
+
new Chart(canvasElement, {
|
|
26
|
+
type: "bar",
|
|
27
|
+
data: {
|
|
28
|
+
datasets: [{
|
|
29
|
+
data: [1,4,2,8]
|
|
30
|
+
}]
|
|
31
|
+
},
|
|
32
|
+
plugins: [chartjs2music]
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Supported features
|
|
38
|
+
|
|
39
|
+
This plugin is currently in beta, so not all of the chart.js features are currently supported.
|
|
16
40
|
|
|
17
|
-
|
|
41
|
+
A quick list of chart.js features we currently support includes:
|
|
42
|
+
* Chart types: bar, line, pie, doughnut, polar, and combinations therein.
|
|
43
|
+
* Axes options: `title`, `min`, `max`, `type="linear"`, `type="logarithmic"`.
|
|
44
|
+
* Chart title
|
|
45
|
+
* Most data structures (not including `parsing` or non-standard axes identifiers)
|
|
18
46
|
|
|
19
|
-
|
|
20
|
-
2. run `yarn start` to start a server in watch mode
|
|
21
|
-
3. point a browser to `localhost:5173`, or whichever port numberVite indicates after you ran `yarn start`.
|
|
47
|
+
Note that visual-specific chart features are ignored. This includes things like color, padding, line thickness, etc.
|
|
22
48
|
|
|
23
|
-
|
|
49
|
+
Things we plan to support in the future:
|
|
50
|
+
* Interactions and updates
|
|
51
|
+
* Other chart.js plugins
|
|
52
|
+
* Complex `parsing` options for data
|
|
53
|
+
* Date/Time support for axes
|
|
54
|
+
* Subtitle
|
|
55
|
+
* Locale
|
|
56
|
+
* Dataset visibility (when you show/hide a category from the legend)
|
|
57
|
+
* Radar charts
|
|
58
|
+
* Custom formatting for axis tick values
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var chartjs2music = (function (
|
|
1
|
+
var chartjs2music = (function (c2mChart) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
// let lastDataObj = "";
|
|
@@ -194,8 +194,6 @@ var chartjs2music = (function (exports, c2mChart) {
|
|
|
194
194
|
}
|
|
195
195
|
};
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
return plugin;
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
})({}, c2mChart);
|
|
199
|
+
})(c2mChart);
|
package/dist/plugin.mjs
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import c2mChart from 'chart2music';
|
|
2
|
+
|
|
3
|
+
// let lastDataObj = "";
|
|
4
|
+
const chartjs_c2m_converter = {
|
|
5
|
+
bar: "bar",
|
|
6
|
+
line: "line",
|
|
7
|
+
pie: "pie",
|
|
8
|
+
polarArea: "bar",
|
|
9
|
+
doughnut: "pie"
|
|
10
|
+
};
|
|
11
|
+
const processChartType = (chart) => {
|
|
12
|
+
const topLevelType = chart.config.type;
|
|
13
|
+
const panelTypes = chart.data.datasets.map(({ type }) => type ?? topLevelType);
|
|
14
|
+
const invalid = panelTypes.find((t) => !(t in chartjs_c2m_converter));
|
|
15
|
+
if (invalid) {
|
|
16
|
+
return {
|
|
17
|
+
valid: false,
|
|
18
|
+
invalidType: invalid
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if ([...new Set(panelTypes)].length === 1) {
|
|
22
|
+
return {
|
|
23
|
+
valid: true,
|
|
24
|
+
c2m_types: chartjs_c2m_converter[panelTypes[0]]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
valid: true,
|
|
29
|
+
c2m_types: panelTypes.map((t) => chartjs_c2m_converter[t])
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const generateAxisInfo = (chartAxisInfo, chart) => {
|
|
33
|
+
const axis = {};
|
|
34
|
+
if (chartAxisInfo?.min) {
|
|
35
|
+
if (typeof chartAxisInfo.min === "string") {
|
|
36
|
+
axis.minimum = chart.data.labels.indexOf(chartAxisInfo.min);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
axis.minimum = chartAxisInfo.min;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (chartAxisInfo?.max) {
|
|
43
|
+
if (typeof chartAxisInfo.max === "string") {
|
|
44
|
+
axis.maximum = chart.data.labels.indexOf(chartAxisInfo.max);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
axis.maximum = chartAxisInfo.max;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const label = chartAxisInfo?.title?.text;
|
|
51
|
+
if (label) {
|
|
52
|
+
axis.label = label;
|
|
53
|
+
}
|
|
54
|
+
if (chartAxisInfo?.type === "logarithmic") {
|
|
55
|
+
axis.type = "log10";
|
|
56
|
+
}
|
|
57
|
+
return axis;
|
|
58
|
+
};
|
|
59
|
+
const generateAxes = (chart) => {
|
|
60
|
+
const axes = {
|
|
61
|
+
x: {
|
|
62
|
+
...generateAxisInfo(chart.options?.scales?.x, chart),
|
|
63
|
+
valueLabels: chart.data.labels.slice(0)
|
|
64
|
+
},
|
|
65
|
+
y: {
|
|
66
|
+
...generateAxisInfo(chart.options?.scales?.y, chart),
|
|
67
|
+
format: (value) => value.toLocaleString()
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
return axes;
|
|
71
|
+
};
|
|
72
|
+
const whichDataStructure = (data) => {
|
|
73
|
+
if (Array.isArray(data[0])) {
|
|
74
|
+
return data.map((arr, x) => {
|
|
75
|
+
let [low, high] = arr.sort();
|
|
76
|
+
return { x, low, high };
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return data;
|
|
80
|
+
};
|
|
81
|
+
const scrubX = (data) => {
|
|
82
|
+
const blackboard = JSON.parse(JSON.stringify(data));
|
|
83
|
+
let labels = [];
|
|
84
|
+
if (Array.isArray(data)) {
|
|
85
|
+
// console.log("not grouped");
|
|
86
|
+
// Not grouped
|
|
87
|
+
blackboard.forEach((item, x) => {
|
|
88
|
+
if (typeof item === "object" && "x" in item) {
|
|
89
|
+
labels.push(item.x);
|
|
90
|
+
item.x = x;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return { labels, data: blackboard };
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const processData = (data) => {
|
|
97
|
+
let groups = [];
|
|
98
|
+
if (data.datasets.length === 1) {
|
|
99
|
+
return {
|
|
100
|
+
data: whichDataStructure(data.datasets[0].data)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const result = {};
|
|
104
|
+
data.datasets.forEach((obj, index) => {
|
|
105
|
+
const groupName = obj.label ?? `Group ${index + 1}`;
|
|
106
|
+
groups.push(groupName);
|
|
107
|
+
result[groupName] = whichDataStructure(obj.data);
|
|
108
|
+
});
|
|
109
|
+
return { groups, data: result };
|
|
110
|
+
};
|
|
111
|
+
const determineChartTitle = (options) => {
|
|
112
|
+
if (options.plugins?.title?.text) {
|
|
113
|
+
if (Array.isArray(options.plugins.title.text)) {
|
|
114
|
+
return options.plugins.title.text.join(", ");
|
|
115
|
+
}
|
|
116
|
+
return options.plugins.title.text;
|
|
117
|
+
}
|
|
118
|
+
return "";
|
|
119
|
+
};
|
|
120
|
+
const determineCCElement = (canvas, provided) => {
|
|
121
|
+
if (provided) {
|
|
122
|
+
return provided;
|
|
123
|
+
}
|
|
124
|
+
const cc = document.createElement("div");
|
|
125
|
+
canvas.insertAdjacentElement("afterend", cc);
|
|
126
|
+
return cc;
|
|
127
|
+
};
|
|
128
|
+
const plugin = {
|
|
129
|
+
id: "chartjs2music",
|
|
130
|
+
afterInit: (chart, args, options) => {
|
|
131
|
+
const { valid, c2m_types, invalidType } = processChartType(chart);
|
|
132
|
+
if (!valid) {
|
|
133
|
+
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(", ")}`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const axes = generateAxes(chart);
|
|
137
|
+
// Generate CC element
|
|
138
|
+
const cc = determineCCElement(chart.canvas, options.cc);
|
|
139
|
+
const { data, groups } = processData(chart.data);
|
|
140
|
+
// lastDataObj = JSON.stringify(data);
|
|
141
|
+
let scrub = scrubX(data);
|
|
142
|
+
if (scrub?.labels && scrub?.labels?.length > 0) { // Something was scrubbed
|
|
143
|
+
if (!chart.data.labels || chart.data.labels.length === 0) {
|
|
144
|
+
axes.x.valueLabels = scrub.labels.slice(0);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const c2mOptions = {
|
|
148
|
+
cc,
|
|
149
|
+
element: chart.canvas,
|
|
150
|
+
type: c2m_types,
|
|
151
|
+
data: scrub?.data ?? data,
|
|
152
|
+
title: determineChartTitle(chart.options),
|
|
153
|
+
axes,
|
|
154
|
+
options: {
|
|
155
|
+
// @ts-ignore
|
|
156
|
+
onFocusCallback: ({ slice, index }) => {
|
|
157
|
+
try {
|
|
158
|
+
const highlightElements = [{
|
|
159
|
+
datasetIndex: groups?.indexOf(slice) ?? 0,
|
|
160
|
+
index
|
|
161
|
+
}];
|
|
162
|
+
chart?.setActiveElements(highlightElements);
|
|
163
|
+
chart?.tooltip?.setActiveElements(highlightElements, {});
|
|
164
|
+
chart?.update();
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
// console.warn(e);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
if (options.audioEngine) {
|
|
173
|
+
// @ts-ignore
|
|
174
|
+
c2mOptions.audioEngine = options.audioEngine;
|
|
175
|
+
}
|
|
176
|
+
const { err, data: c2m } = c2mChart(c2mOptions);
|
|
177
|
+
// /* istanbul-ignore-next */
|
|
178
|
+
if (err) {
|
|
179
|
+
options.errorCallback?.(err);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
// afterUpdate: (chart: Chart) => {
|
|
183
|
+
// const {data} = processData(chart.data);
|
|
184
|
+
// if(JSON.stringify(data) !== lastDataObj){
|
|
185
|
+
// c2mChartRef?.setData(data);
|
|
186
|
+
// lastDataObj = JSON.stringify(data);
|
|
187
|
+
// }
|
|
188
|
+
// },
|
|
189
|
+
defaults: {
|
|
190
|
+
cc: null,
|
|
191
|
+
audioEngine: null,
|
|
192
|
+
errorCallback: null
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export { plugin as default };
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chartjs-plugin-chart2music",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Chart.js plugin for Chart2Music. Turns chart.js charts into music so the blind can hear data.",
|
|
6
6
|
"main": "dist/plugin.js",
|
|
7
|
+
"module": "dist/plugin.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
"import": "./dist/plugin.mjs"
|
|
10
|
+
},
|
|
7
11
|
"files": ["dist/*"],
|
|
8
12
|
"keywords": [
|
|
9
13
|
"a11y",
|