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/dist/plugin.cjs CHANGED
@@ -3,35 +3,17 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = void 0;
6
+ exports.default = void 0;
7
7
  var _chart2music = _interopRequireDefault(require("chart2music"));
8
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
9
- function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
10
- function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
11
- function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
12
- function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
13
- function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
14
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
17
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
18
- function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
19
- function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
20
- function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
21
- function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
22
- function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
23
- function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
24
- function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
25
- var calcMedian = function calcMedian(nums) {
26
- return nums.length % 2 ? nums[Math.floor(nums.length / 2)] : (nums[nums.length / 2] + nums[nums.length / 2 - 1]) / 2;
27
- };
28
- var fiveNumberSummary = function fiveNumberSummary(nums) {
29
- var sortedNumbers = nums.sort();
30
- var min, max, q1, q3;
31
- var datamin = Math.min.apply(Math, _toConsumableArray(sortedNumbers));
32
- var datamax = Math.max.apply(Math, _toConsumableArray(sortedNumbers));
33
- var median = calcMedian(sortedNumbers);
34
- var length = sortedNumbers.length;
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ const calcMedian = nums => nums.length % 2 ? nums[Math.floor(nums.length / 2)] : (nums[nums.length / 2] + nums[nums.length / 2 - 1]) / 2;
10
+ const fiveNumberSummary = nums => {
11
+ const sortedNumbers = nums.sort();
12
+ let min, max, q1, q3;
13
+ const datamin = Math.min(...sortedNumbers);
14
+ const datamax = Math.max(...sortedNumbers);
15
+ const median = calcMedian(sortedNumbers);
16
+ const length = sortedNumbers.length;
35
17
  if (length % 2) {
36
18
  q1 = calcMedian(sortedNumbers.slice(0, Math.floor(length / 2)));
37
19
  q3 = calcMedian(sortedNumbers.slice(Math.ceil(length % 2)));
@@ -39,75 +21,70 @@ var fiveNumberSummary = function fiveNumberSummary(nums) {
39
21
  q1 = calcMedian(sortedNumbers.slice(0, length / 2 - 1));
40
22
  q3 = calcMedian(sortedNumbers.slice(length / 2));
41
23
  }
42
- var iqr = q3 - q1;
24
+ const iqr = q3 - q1;
43
25
  if (datamax < q3 + iqr) {
44
26
  max = datamax;
45
27
  } else {
46
- var _sortedNumbers$revers;
47
- max = (_sortedNumbers$revers = sortedNumbers.reverse().find(function (num) {
48
- return num < datamax;
49
- })) !== null && _sortedNumbers$revers !== void 0 ? _sortedNumbers$revers : datamax;
28
+ max = sortedNumbers.reverse().find(num => num < datamax) ?? datamax;
50
29
  }
51
30
  if (datamin < q1 - iqr) {
52
31
  min = datamin;
53
32
  } else {
54
- var _sortedNumbers$revers2;
55
- min = (_sortedNumbers$revers2 = sortedNumbers.reverse().find(function (num) {
56
- return num < datamin;
57
- })) !== null && _sortedNumbers$revers2 !== void 0 ? _sortedNumbers$revers2 : datamin;
33
+ min = sortedNumbers.reverse().find(num => num < datamin) ?? datamin;
58
34
  }
59
- var outlier = sortedNumbers.filter(function (num) {
60
- return num < min || num > max;
61
- });
62
- return _objectSpread({
35
+ const outlier = sortedNumbers.filter(num => num < min || num > max);
36
+ return {
63
37
  low: min,
64
38
  high: max,
65
- median: median,
66
- q1: q1,
67
- q3: q3
68
- }, outlier.length > 0 ? {
69
- outlier: outlier
70
- } : {});
39
+ median,
40
+ q1,
41
+ q3,
42
+ ...(outlier.length > 0 ? {
43
+ outlier
44
+ } : {})
45
+ };
71
46
  };
72
- var whichBoxData = function whichBoxData(data) {
73
- return data.map(function (row, index) {
74
- if (_typeof(row) === "object" && "min" in row) {
75
- return _objectSpread(_objectSpread({}, row), {}, {
47
+ const whichBoxData = data => {
48
+ return data.map((row, index) => {
49
+ if (typeof row === "object" && "min" in row) {
50
+ return {
51
+ ...row,
76
52
  low: row.min,
77
53
  high: row.max,
78
- x: index
79
- }, "outliers" in row ? {
80
- outlier: row.outliers
81
- } : {});
54
+ x: index,
55
+ ...("outliers" in row ? {
56
+ outlier: row.outliers
57
+ } : {})
58
+ };
82
59
  }
83
60
  if (Array.isArray(row)) {
84
- return _objectSpread(_objectSpread({}, fiveNumberSummary(row)), {}, {
61
+ return {
62
+ ...fiveNumberSummary(row),
85
63
  x: index
86
- });
64
+ };
87
65
  }
88
66
  });
89
67
  };
90
- var processBoxData = function processBoxData(data) {
68
+ const processBoxData = data => {
91
69
  if (data.datasets.length === 1) {
92
70
  return {
93
71
  data: whichBoxData(data.datasets[0].data)
94
72
  };
95
73
  }
96
- var groups = [];
97
- var result = {};
98
- data.datasets.forEach(function (obj, index) {
99
- var _obj$label;
100
- var groupName = (_obj$label = obj.label) !== null && _obj$label !== void 0 ? _obj$label : "Group ".concat(index + 1);
74
+ const groups = [];
75
+ const result = {};
76
+ data.datasets.forEach((obj, index) => {
77
+ const groupName = obj.label ?? `Group ${index + 1}`;
101
78
  groups.push(groupName);
102
79
  result[groupName] = whichBoxData(obj.data);
103
80
  });
104
81
  return {
105
- groups: groups,
82
+ groups,
106
83
  data: result
107
84
  };
108
85
  };
109
- var chartStates = new Map();
110
- var chartjs_c2m_converter = {
86
+ const chartStates = new Map();
87
+ const chartjs_c2m_converter = {
111
88
  bar: "bar",
112
89
  line: "line",
113
90
  pie: "pie",
@@ -116,24 +93,22 @@ var chartjs_c2m_converter = {
116
93
  boxplot: "box",
117
94
  radar: "bar",
118
95
  wordCloud: "bar",
119
- scatter: "scatter"
96
+ scatter: "scatter",
97
+ matrix: "matrix"
120
98
  };
121
- var processChartType = function processChartType(chart) {
122
- var topLevelType = chart.config.type;
123
- var panelTypes = chart.data.datasets.map(function (_ref) {
124
- var type = _ref.type;
125
- return type !== null && type !== void 0 ? type : topLevelType;
126
- });
127
- var invalid = panelTypes.find(function (t) {
128
- return !(t in chartjs_c2m_converter);
129
- });
99
+ const processChartType = chart => {
100
+ const topLevelType = chart.config.type;
101
+ const panelTypes = chart.data.datasets.map(({
102
+ type
103
+ }) => type ?? topLevelType);
104
+ const invalid = panelTypes.find(t => !(t in chartjs_c2m_converter));
130
105
  if (invalid) {
131
106
  return {
132
107
  valid: false,
133
108
  invalidType: invalid
134
109
  };
135
110
  }
136
- if (_toConsumableArray(new Set(panelTypes)).length === 1) {
111
+ if ([...new Set(panelTypes)].length === 1) {
137
112
  return {
138
113
  valid: true,
139
114
  c2m_types: chartjs_c2m_converter[panelTypes[0]]
@@ -141,112 +116,160 @@ var processChartType = function processChartType(chart) {
141
116
  }
142
117
  return {
143
118
  valid: true,
144
- c2m_types: panelTypes.map(function (t) {
145
- return chartjs_c2m_converter[t];
146
- })
119
+ c2m_types: panelTypes.map(t => chartjs_c2m_converter[t])
147
120
  };
148
121
  };
149
- var generateAxisInfo = function generateAxisInfo(chartAxisInfo, chart) {
150
- var _chartAxisInfo$title;
151
- var axis = {};
152
- if ((chartAxisInfo === null || chartAxisInfo === void 0 ? void 0 : chartAxisInfo.min) !== undefined) {
122
+ const generateAxisInfo = (chartAxisInfo, chart) => {
123
+ const axis = {};
124
+ if (chartAxisInfo?.min !== undefined) {
153
125
  if (typeof chartAxisInfo.min === "string") {
154
126
  axis.minimum = chart.data.labels.indexOf(chartAxisInfo.min);
155
127
  } else {
156
128
  axis.minimum = chartAxisInfo.min;
157
129
  }
158
130
  }
159
- if ((chartAxisInfo === null || chartAxisInfo === void 0 ? void 0 : chartAxisInfo.max) !== undefined) {
131
+ if (chartAxisInfo?.max !== undefined) {
160
132
  if (typeof chartAxisInfo.max === "string") {
161
133
  axis.maximum = chart.data.labels.indexOf(chartAxisInfo.max);
162
134
  } else {
163
135
  axis.maximum = chartAxisInfo.max;
164
136
  }
165
137
  }
166
- var label = chartAxisInfo === null || chartAxisInfo === void 0 || (_chartAxisInfo$title = chartAxisInfo.title) === null || _chartAxisInfo$title === void 0 ? void 0 : _chartAxisInfo$title.text;
138
+ const label = chartAxisInfo?.title?.text;
167
139
  if (label) {
168
140
  axis.label = label;
169
141
  }
170
- if ((chartAxisInfo === null || chartAxisInfo === void 0 ? void 0 : chartAxisInfo.type) === "logarithmic") {
142
+ if (chartAxisInfo?.type === "logarithmic") {
171
143
  axis.type = "log10";
172
144
  }
173
145
  return axis;
174
146
  };
175
- var generateAxes = function generateAxes(chart) {
176
- var _chart$options, _chart$options2;
177
- var axes = {
178
- x: _objectSpread({}, generateAxisInfo((_chart$options = chart.options) === null || _chart$options === void 0 || (_chart$options = _chart$options.scales) === null || _chart$options === void 0 ? void 0 : _chart$options.x, chart)),
179
- y: _objectSpread({
180
- format: function format(value) {
181
- return value.toLocaleString();
182
- }
183
- }, generateAxisInfo((_chart$options2 = chart.options) === null || _chart$options2 === void 0 || (_chart$options2 = _chart$options2.scales) === null || _chart$options2 === void 0 ? void 0 : _chart$options2.y, chart))
147
+ const generateAxes = (chart, options) => {
148
+ const axes = {
149
+ x: {
150
+ ...generateAxisInfo(chart.options?.scales?.x, chart)
151
+ },
152
+ y: {
153
+ format: options?.axes?.y?.format || (value => value.toLocaleString()),
154
+ ...generateAxisInfo(chart.options?.scales?.y, chart)
155
+ }
184
156
  };
185
- var xAxisValueLabels = chart.data.labels.slice(0);
157
+ const xAxisValueLabels = chart.data.labels.slice(0);
186
158
  if (xAxisValueLabels.length > 0) {
187
159
  axes.x.valueLabels = xAxisValueLabels;
188
160
  }
189
161
  return axes;
190
162
  };
191
- var whichDataStructure = function whichDataStructure(data) {
163
+ const whichDataStructure = data => {
192
164
  if (Array.isArray(data[0])) {
193
- return data.map(function (arr, x) {
194
- var _arr$sort = arr.sort(),
195
- _arr$sort2 = _slicedToArray(_arr$sort, 2),
196
- low = _arr$sort2[0],
197
- high = _arr$sort2[1];
165
+ return data.map((arr, x) => {
166
+ let [low, high] = arr.sort();
198
167
  return {
199
- x: x,
200
- low: low,
201
- high: high
168
+ x,
169
+ low,
170
+ high
202
171
  };
203
172
  });
204
173
  }
205
174
  return data;
206
175
  };
207
- var scrubX = function scrubX(data) {
208
- var blackboard = JSON.parse(JSON.stringify(data));
209
- var labels = [];
176
+ const labelIndex = (labels, value) => {
177
+ let index = labels.indexOf(value);
178
+ if (index === -1) {
179
+ labels.push(value);
180
+ index = labels.length - 1;
181
+ }
182
+ return index;
183
+ };
184
+ const processMatrixDataPoints = (data, datasetIndex, xLabels, yLabels) => {
185
+ return data.map((point, index) => {
186
+ const x = typeof point.x === "string" ? labelIndex(xLabels, point.x) : point.x;
187
+ const y = typeof point.y === "string" ? labelIndex(yLabels, point.y) : point.y;
188
+ return {
189
+ ...point,
190
+ x,
191
+ y,
192
+ custom: {
193
+ ...point.custom,
194
+ group: datasetIndex,
195
+ datasetIndex,
196
+ index
197
+ }
198
+ };
199
+ });
200
+ };
201
+ const processMatrixData = data => {
202
+ const xLabels = [];
203
+ const yLabels = [];
204
+ // Chart2Music represents matrix rows as groups and columns as points within
205
+ // each group. This preserves two-dimensional keyboard navigation.
206
+ const result = {};
207
+ data.datasets.forEach((obj, index) => {
208
+ const points = processMatrixDataPoints(obj.data, index, xLabels, yLabels);
209
+ points.forEach(point => {
210
+ const rowLabel = typeof point.y === "number" && yLabels[point.y] !== undefined ? yLabels[point.y] : String(point.y);
211
+ const groupName = data.datasets.length === 1 ? rowLabel : `${obj.label ?? `Group ${index + 1}`}: ${rowLabel}`;
212
+ if (!result[groupName]) {
213
+ result[groupName] = [];
214
+ }
215
+ result[groupName].push(point);
216
+ });
217
+ });
218
+ Object.values(result).forEach(row => row.sort((a, b) => a.x - b.x));
219
+ return {
220
+ groups: Object.keys(result),
221
+ data: result,
222
+ xLabels,
223
+ yLabels
224
+ };
225
+ };
226
+ const scrubX = data => {
227
+ const blackboard = JSON.parse(JSON.stringify(data));
228
+ let labels = [];
210
229
  if (Array.isArray(data)) {
211
230
  // console.log("not grouped");
212
231
  // Not grouped
213
- blackboard.forEach(function (item, x) {
214
- if (_typeof(item) === "object" && item !== null && "x" in item) {
232
+ blackboard.forEach((item, x) => {
233
+ if (typeof item === "object" && item !== null && "x" in item) {
215
234
  labels.push(item.x);
216
235
  item.x = x;
217
236
  }
218
237
  });
219
238
  return {
220
- labels: labels,
239
+ labels,
221
240
  data: blackboard
222
241
  };
242
+ } else {
243
+ // Grouped
244
+ return undefined;
223
245
  }
224
246
  };
225
- var processData = function processData(data, c2m_types) {
247
+ const processData = (data, c2m_types) => {
226
248
  if (c2m_types === "box") {
227
249
  return processBoxData(data);
228
250
  }
229
- var groups = [];
251
+ if (c2m_types === "matrix") {
252
+ return processMatrixData(data);
253
+ }
254
+ let groups = [];
230
255
  if (data.datasets.length === 1) {
231
256
  return {
232
257
  data: whichDataStructure(data.datasets[0].data)
233
258
  };
234
259
  }
235
- var result = {};
236
- data.datasets.forEach(function (obj, index) {
237
- var _obj$label2;
238
- var groupName = (_obj$label2 = obj.label) !== null && _obj$label2 !== void 0 ? _obj$label2 : "Group ".concat(index + 1);
260
+ const result = {};
261
+ data.datasets.forEach((obj, index) => {
262
+ const groupName = obj.label ?? `Group ${index + 1}`;
239
263
  groups.push(groupName);
240
264
  result[groupName] = whichDataStructure(obj.data);
241
265
  });
242
266
  return {
243
- groups: groups,
267
+ groups,
244
268
  data: result
245
269
  };
246
270
  };
247
- var determineChartTitle = function determineChartTitle(options) {
248
- var _options$plugins;
249
- if ((_options$plugins = options.plugins) !== null && _options$plugins !== void 0 && (_options$plugins = _options$plugins.title) !== null && _options$plugins !== void 0 && _options$plugins.text) {
271
+ const determineChartTitle = options => {
272
+ if (options.plugins?.title?.text) {
250
273
  if (Array.isArray(options.plugins.title.text)) {
251
274
  return options.plugins.title.text.join(", ");
252
275
  }
@@ -254,62 +277,70 @@ var determineChartTitle = function determineChartTitle(options) {
254
277
  }
255
278
  return "";
256
279
  };
257
- var determineCCElement = function determineCCElement(canvas, provided) {
280
+ const determineCCElement = (canvas, provided) => {
258
281
  if (provided) {
259
282
  return provided;
260
283
  }
261
- var cc = document.createElement("div");
284
+ const cc = document.createElement("div");
262
285
  canvas.insertAdjacentElement("afterend", cc);
263
286
  return cc;
264
287
  };
265
- var displayPoint = function displayPoint(chart) {
288
+ const createDataSnapshot = chart => {
289
+ return JSON.stringify({
290
+ datasets: chart.data.datasets.map(ds => ds.data),
291
+ labels: chart.data.labels
292
+ });
293
+ };
294
+ const displayPoint = chart => {
266
295
  if (!chartStates.has(chart)) {
267
296
  return;
268
297
  }
269
- var _chartStates$get = chartStates.get(chart),
270
- ref = _chartStates$get.c2m,
271
- visible_groups = _chartStates$get.visible_groups;
272
- var _ref$getCurrent = ref.getCurrent(),
273
- point = _ref$getCurrent.point,
274
- index = _ref$getCurrent.index;
298
+ const {
299
+ c2m: ref
300
+ } = chartStates.get(chart);
301
+ const {
302
+ point,
303
+ index
304
+ } = ref.getCurrent();
305
+ // Use Chart2Music's internal visible group tracking
306
+ // @ts-ignore - accessing internal Chart2Music property
307
+ const visibleGroupIndices = ref._visible_group_indices?.slice(1) || [];
275
308
  try {
276
- var _chart$tooltip;
277
- var highlightElements = [];
309
+ const highlightElements = [];
278
310
  if ("custom" in point) {
311
+ const customPoint = point;
279
312
  highlightElements.push({
280
- // @ts-ignore
281
- datasetIndex: point.custom.group,
282
- // @ts-ignore
283
- index: point.custom.index
313
+ datasetIndex: customPoint.custom.datasetIndex ?? customPoint.custom.group,
314
+ index: customPoint.custom.index
284
315
  });
285
316
  } else {
286
- visible_groups.forEach(function (datasetIndex) {
317
+ // For stacked charts, Chart2Music includes an "All" group at index 0,
318
+ // so we subtract 1 to get the actual dataset index
319
+ visibleGroupIndices.forEach(groupIndex => {
287
320
  highlightElements.push({
288
- datasetIndex: datasetIndex,
289
- index: index
321
+ datasetIndex: groupIndex - 1,
322
+ index
290
323
  });
291
324
  });
292
325
  }
293
- chart === null || chart === void 0 || chart.setActiveElements(highlightElements);
294
- chart === null || chart === void 0 || (_chart$tooltip = chart.tooltip) === null || _chart$tooltip === void 0 || _chart$tooltip.setActiveElements(highlightElements, {});
295
- chart === null || chart === void 0 || chart.update();
326
+ chart?.setActiveElements(highlightElements);
327
+ chart?.tooltip?.setActiveElements(highlightElements, {});
328
+ chart?.update();
296
329
  } catch (e) {
297
330
  // console.warn(e);
298
331
  }
299
332
  };
300
- var generateChart = function generateChart(chart, options) {
301
- var _scrub$labels, _options$axes, _options$axes2, _scrub$data, _chart$config$options, _groups$map;
302
- var _processChartType = processChartType(chart),
303
- valid = _processChartType.valid,
304
- c2m_types = _processChartType.c2m_types,
305
- invalidType = _processChartType.invalidType;
333
+ const generateChart = (chart, options) => {
334
+ const {
335
+ valid,
336
+ c2m_types,
337
+ invalidType
338
+ } = processChartType(chart);
306
339
  if (!valid) {
307
- var _options$errorCallbac;
308
- // @ts-ignore
309
- (_options$errorCallbac = options.errorCallback) === null || _options$errorCallbac === void 0 || _options$errorCallbac.call(options, "Unable to connect chart2music to chart. The chart is of type \"".concat(invalidType, "\", which is not one of the supported chart types for this plugin. This plugin supports: ").concat(Object.keys(chartjs_c2m_converter).join(", ")));
340
+ 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(", ")}`);
310
341
  return;
311
342
  }
312
- var axes = generateAxes(chart);
343
+ let axes = generateAxes(chart, options);
313
344
  if (chart.config.type === "wordCloud") {
314
345
  delete axes.x.minimum;
315
346
  delete axes.x.maximum;
@@ -323,96 +354,126 @@ var generateChart = function generateChart(chart, options) {
323
354
  }
324
355
  }
325
356
  // Generate CC element
326
- var cc = determineCCElement(chart.canvas, options.cc);
327
- var _processData = processData(chart.data, c2m_types),
328
- data = _processData.data,
329
- groups = _processData.groups;
357
+ const cc = determineCCElement(chart.canvas, options.cc);
358
+ const processedData = processData(chart.data, c2m_types);
359
+ const {
360
+ data
361
+ } = processedData;
330
362
  // lastDataObj = JSON.stringify(data);
331
- var scrub = scrubX(data);
332
- if (scrub !== null && scrub !== void 0 && scrub.labels && (scrub === null || scrub === void 0 || (_scrub$labels = scrub.labels) === null || _scrub$labels === void 0 ? void 0 : _scrub$labels.length) > 0) {
363
+ if (c2m_types === "matrix") {
364
+ if (processedData.xLabels?.length > 0) {
365
+ axes.x.valueLabels = processedData.xLabels.slice(0);
366
+ }
367
+ if (processedData.yLabels?.length > 0) {
368
+ // Category labels provide the Matrix Plot's Y-axis formatter.
369
+ // The general numeric formatter would otherwise announce row indexes.
370
+ delete axes.y.format;
371
+ axes.y.valueLabels = processedData.yLabels.slice(0);
372
+ }
373
+ }
374
+ let scrub = scrubX(data);
375
+ if (scrub?.labels && scrub?.labels?.length > 0) {
333
376
  // Something was scrubbed
334
377
  if (!chart.data.labels || chart.data.labels.length === 0) {
335
378
  axes.x.valueLabels = scrub.labels.slice(0);
336
379
  }
337
380
  }
338
381
  if (c2m_types === "scatter") {
339
- scrub === null || scrub === void 0 || delete scrub.data;
382
+ delete scrub?.data;
340
383
  delete axes.x.valueLabels;
341
384
  }
342
- axes = _objectSpread(_objectSpread({}, axes), {}, {
343
- x: _objectSpread(_objectSpread({}, axes.x), (_options$axes = options.axes) === null || _options$axes === void 0 ? void 0 : _options$axes.x),
344
- y: _objectSpread(_objectSpread({}, axes.y), (_options$axes2 = options.axes) === null || _options$axes2 === void 0 ? void 0 : _options$axes2.y)
345
- });
346
- var c2mOptions = {
347
- cc: cc,
385
+ axes = {
386
+ ...axes,
387
+ x: {
388
+ ...axes.x,
389
+ ...options.axes?.x
390
+ },
391
+ y: {
392
+ ...axes.y,
393
+ ...options.axes?.y
394
+ }
395
+ };
396
+ // Start with plugin's internal onFocusCallback
397
+ const pluginOnFocusCallback = () => {
398
+ displayPoint(chart);
399
+ };
400
+ // Merge user's options, wrapping onFocusCallback if provided
401
+ const userOptions = options.options || {};
402
+ const userOnFocusCallback = userOptions.onFocusCallback;
403
+ const c2mOptions = {
404
+ cc,
348
405
  element: chart.canvas,
349
406
  type: c2m_types,
350
- data: (_scrub$data = scrub === null || scrub === void 0 ? void 0 : scrub.data) !== null && _scrub$data !== void 0 ? _scrub$data : data,
407
+ data: scrub?.data ?? data,
351
408
  title: determineChartTitle(chart.options),
352
- axes: axes,
409
+ axes,
353
410
  options: {
354
- // @ts-ignore
355
- onFocusCallback: function onFocusCallback() {
356
- displayPoint(chart);
357
- }
411
+ ...userOptions,
412
+ onFocusCallback: userOnFocusCallback ? point => {
413
+ pluginOnFocusCallback();
414
+ userOnFocusCallback(point);
415
+ } : pluginOnFocusCallback
358
416
  }
359
417
  };
360
418
  if (Array.isArray(c2mOptions.data)) {
361
419
  if (isNaN(c2mOptions.data[0])) {
362
- c2mOptions.data = c2mOptions.data.map(function (point, index) {
363
- return _objectSpread(_objectSpread({}, point), {}, {
420
+ c2mOptions.data = c2mOptions.data.map((point, index) => {
421
+ return {
422
+ ...point,
364
423
  custom: {
365
- group: 0,
366
- index: index
424
+ ...point.custom,
425
+ group: point.custom?.group ?? 0,
426
+ index: point.custom?.index ?? index
367
427
  }
368
- });
428
+ };
369
429
  });
370
430
  } else {
371
- c2mOptions.data = c2mOptions.data.map(function (num, index) {
431
+ c2mOptions.data = c2mOptions.data.map((num, index) => {
372
432
  return {
373
433
  x: index,
374
434
  y: num,
375
435
  custom: {
376
436
  group: 0,
377
- index: index
437
+ index
378
438
  }
379
439
  };
380
440
  });
381
441
  }
382
442
  } else {
383
- var _groups = Object.keys(c2mOptions.data);
384
- _groups.forEach(function (groupName, groupNumber) {
385
- if (!isNaN(c2mOptions.data[groupName][0])) {
386
- c2mOptions.data[groupName] = c2mOptions.data[groupName].map(function (num, index) {
443
+ const dataObj = c2mOptions.data;
444
+ const groups = Object.keys(dataObj);
445
+ groups.forEach((groupName, groupNumber) => {
446
+ if (!isNaN(dataObj[groupName][0])) {
447
+ dataObj[groupName] = dataObj[groupName].map((num, index) => {
387
448
  return {
388
449
  x: index,
389
450
  y: num,
390
451
  custom: {
391
452
  group: groupNumber,
392
- index: index
453
+ index
393
454
  }
394
455
  };
395
456
  });
396
457
  } else {
397
- c2mOptions.data[groupName] = c2mOptions.data[groupName].map(function (point, index) {
398
- return _objectSpread(_objectSpread({}, point), {}, {
458
+ dataObj[groupName] = dataObj[groupName].map((point, index) => {
459
+ return {
460
+ ...point,
399
461
  custom: {
400
- group: groupNumber,
401
- index: index
462
+ ...point.custom,
463
+ group: point.custom?.group ?? groupNumber,
464
+ index: point.custom?.index ?? index
402
465
  }
403
- });
466
+ };
404
467
  });
405
468
  }
406
469
  });
407
470
  }
408
- // @ts-ignore
409
- if ((_chart$config$options = chart.config.options) !== null && _chart$config$options !== void 0 && (_chart$config$options = _chart$config$options.scales) !== null && _chart$config$options !== void 0 && (_chart$config$options = _chart$config$options.x) !== null && _chart$config$options !== void 0 && _chart$config$options.stacked) {
410
- // @ts-ignore
411
- c2mOptions.options.stack = true;
471
+ if (chart.config.options?.scales?.x?.stacked) {
472
+ if (c2mOptions.options) {
473
+ c2mOptions.options.stack = true;
474
+ }
412
475
  }
413
- // @ts-ignore
414
476
  if (options.audioEngine) {
415
- // @ts-ignore
416
477
  c2mOptions.audioEngine = options.audioEngine;
417
478
  }
418
479
  if (c2mOptions.data.length === 0) {
@@ -421,36 +482,51 @@ var generateChart = function generateChart(chart, options) {
421
482
  if (options.lang) {
422
483
  c2mOptions.lang = options.lang;
423
484
  }
424
- var _c2mChart = (0, _chart2music["default"])(c2mOptions),
425
- err = _c2mChart.err,
426
- c2m = _c2mChart.data;
485
+ const {
486
+ err,
487
+ data: c2m
488
+ } = (0, _chart2music.default)(c2mOptions);
427
489
  /* istanbul-ignore-next */
428
490
  if (err) {
429
- var _options$errorCallbac2;
430
- // @ts-ignore
431
- (_options$errorCallbac2 = options.errorCallback) === null || _options$errorCallbac2 === void 0 || _options$errorCallbac2.call(options, err);
491
+ options.errorCallback?.(err);
432
492
  return;
433
493
  }
434
494
  if (!c2m) {
435
495
  return;
436
496
  }
437
497
  chartStates.set(chart, {
438
- c2m: c2m,
439
- visible_groups: (_groups$map = groups === null || groups === void 0 ? void 0 : groups.map(function (g, i) {
440
- return i;
441
- })) !== null && _groups$map !== void 0 ? _groups$map : []
498
+ c2m,
499
+ lastDataSnapshot: createDataSnapshot(chart)
442
500
  });
501
+ if (c2m_types === "matrix") {
502
+ const matrixKeydown = event => {
503
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") {
504
+ return;
505
+ }
506
+ event.preventDefault();
507
+ event.stopImmediatePropagation();
508
+ const actions = c2m._availableActions;
509
+ if (event.key === "ArrowUp") {
510
+ actions.next_category();
511
+ } else {
512
+ actions.previous_category();
513
+ }
514
+ };
515
+ chart.canvas.addEventListener("keydown", matrixKeydown, true);
516
+ const state = chartStates.get(chart);
517
+ state.matrixKeydown = matrixKeydown;
518
+ state.matrixKeydownTarget = chart.canvas;
519
+ }
443
520
  };
444
- var plugin = exports["default"] = {
521
+ const plugin = exports.default = {
445
522
  id: "chartjs2music",
446
- afterInit: function afterInit(chart, args, options) {
523
+ afterInit: (chart, _args, options) => {
447
524
  if (!chartStates.has(chart)) {
448
525
  generateChart(chart, options);
449
526
  // Remove tooltip when the chart blurs
450
- chart.canvas.addEventListener("blur", function () {
451
- var _chart$tooltip2;
527
+ chart.canvas.addEventListener("blur", () => {
452
528
  chart.setActiveElements([]);
453
- (_chart$tooltip2 = chart.tooltip) === null || _chart$tooltip2 === void 0 || _chart$tooltip2.setActiveElements([], {});
529
+ chart.tooltip?.setActiveElements([], {});
454
530
  try {
455
531
  chart.update();
456
532
  } catch (e) {
@@ -458,58 +534,85 @@ var plugin = exports["default"] = {
458
534
  }
459
535
  });
460
536
  // Show tooltip when the chart receives focus
461
- chart.canvas.addEventListener("focus", function () {
537
+ chart.canvas.addEventListener("focus", () => {
462
538
  displayPoint(chart);
463
539
  });
464
540
  }
465
541
  },
466
- afterDatasetUpdate: function afterDatasetUpdate(chart, args, options) {
542
+ afterDatasetUpdate: (chart, args, options) => {
467
543
  if (!args.mode) {
468
544
  return;
469
545
  }
470
546
  if (!chartStates.has(chart)) {
471
547
  generateChart(chart, options);
472
548
  }
473
- var _chartStates$get2 = chartStates.get(chart),
474
- ref = _chartStates$get2.c2m,
475
- visible_groups = _chartStates$get2.visible_groups;
549
+ const {
550
+ c2m: ref
551
+ } = chartStates.get(chart);
476
552
  if (!ref) {
477
553
  return;
478
554
  }
479
- // @ts-ignore
480
- var groups = ref._groups.slice(0);
481
- // @ts-ignore
482
- if (ref._options.stack) {
555
+ const refInternal = ref;
556
+ const groups = refInternal._groups.slice(0);
557
+ if (refInternal._options.stack) {
483
558
  groups.shift();
484
559
  }
485
560
  if (args.mode === "hide") {
486
- var err = ref.setCategoryVisibility(groups[args.index], false);
487
- visible_groups.splice(args.index, 1);
561
+ const err = ref.setCategoryVisibility(groups[args.index], false);
488
562
  if (err) {
489
563
  console.error(err);
490
564
  }
491
565
  return;
492
566
  }
493
567
  if (args.mode === "show") {
494
- var _err = ref.setCategoryVisibility(groups[args.index], true);
495
- visible_groups.push(args.index);
496
- if (_err) {
497
- console.error(_err);
568
+ const err = ref.setCategoryVisibility(groups[args.index], true);
569
+ if (err) {
570
+ console.error(err);
498
571
  }
499
572
  return;
500
573
  }
501
574
  },
502
- afterDestroy: function afterDestroy(chart) {
503
- var _chartStates$get3 = chartStates.get(chart),
504
- ref = _chartStates$get3.c2m;
505
- if (!ref) {
575
+ afterDatasetsUpdate: (chart, _args, options) => {
576
+ const state = chartStates.get(chart);
577
+ if (!state?.c2m) return;
578
+ // Check if data has changed
579
+ const currentSnapshot = createDataSnapshot(chart);
580
+ if (currentSnapshot === state.lastDataSnapshot) {
581
+ return; // No data change, skip update
582
+ }
583
+ // Get chart type
584
+ const {
585
+ valid,
586
+ c2m_types
587
+ } = processChartType(chart);
588
+ if (!valid) return;
589
+ // Process data and generate axes
590
+ const {
591
+ data
592
+ } = processData(chart.data, c2m_types);
593
+ const axes = generateAxes(chart, options);
594
+ // Update Chart2Music with new data
595
+ state.c2m.setData(data, axes);
596
+ // Update snapshot
597
+ state.lastDataSnapshot = currentSnapshot;
598
+ },
599
+ afterDestroy: chart => {
600
+ const state = chartStates.get(chart);
601
+ if (!state?.c2m) {
506
602
  return;
507
603
  }
604
+ const {
605
+ c2m: ref
606
+ } = state;
607
+ if (state?.matrixKeydown && state.matrixKeydownTarget) {
608
+ state.matrixKeydownTarget.removeEventListener("keydown", state.matrixKeydown, true);
609
+ }
508
610
  ref.cleanUp();
509
611
  },
510
612
  defaults: {
511
613
  cc: null,
512
614
  audioEngine: null,
513
- errorCallback: null
615
+ errorCallback: null,
616
+ options: {}
514
617
  }
515
618
  };