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/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,110 +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 && chartAxisInfo.min) {
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 && chartAxisInfo.max) {
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(_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
- valueLabels: chart.data.labels.slice(0)
180
- }),
181
- y: _objectSpread(_objectSpread({}, 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)), {}, {
182
- format: function format(value) {
183
- return value.toLocaleString();
184
- }
185
- })
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
+ }
186
156
  };
157
+ const xAxisValueLabels = chart.data.labels.slice(0);
158
+ if (xAxisValueLabels.length > 0) {
159
+ axes.x.valueLabels = xAxisValueLabels;
160
+ }
187
161
  return axes;
188
162
  };
189
- var whichDataStructure = function whichDataStructure(data) {
163
+ const whichDataStructure = data => {
190
164
  if (Array.isArray(data[0])) {
191
- return data.map(function (arr, x) {
192
- var _arr$sort = arr.sort(),
193
- _arr$sort2 = _slicedToArray(_arr$sort, 2),
194
- low = _arr$sort2[0],
195
- high = _arr$sort2[1];
165
+ return data.map((arr, x) => {
166
+ let [low, high] = arr.sort();
196
167
  return {
197
- x: x,
198
- low: low,
199
- high: high
168
+ x,
169
+ low,
170
+ high
200
171
  };
201
172
  });
202
173
  }
203
174
  return data;
204
175
  };
205
- var scrubX = function scrubX(data) {
206
- var blackboard = JSON.parse(JSON.stringify(data));
207
- 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 = [];
208
229
  if (Array.isArray(data)) {
209
230
  // console.log("not grouped");
210
231
  // Not grouped
211
- blackboard.forEach(function (item, x) {
212
- if (_typeof(item) === "object" && item !== null && "x" in item) {
232
+ blackboard.forEach((item, x) => {
233
+ if (typeof item === "object" && item !== null && "x" in item) {
213
234
  labels.push(item.x);
214
235
  item.x = x;
215
236
  }
216
237
  });
217
238
  return {
218
- labels: labels,
239
+ labels,
219
240
  data: blackboard
220
241
  };
242
+ } else {
243
+ // Grouped
244
+ return undefined;
221
245
  }
222
246
  };
223
- var processData = function processData(data, c2m_types) {
247
+ const processData = (data, c2m_types) => {
224
248
  if (c2m_types === "box") {
225
249
  return processBoxData(data);
226
250
  }
227
- var groups = [];
251
+ if (c2m_types === "matrix") {
252
+ return processMatrixData(data);
253
+ }
254
+ let groups = [];
228
255
  if (data.datasets.length === 1) {
229
256
  return {
230
257
  data: whichDataStructure(data.datasets[0].data)
231
258
  };
232
259
  }
233
- var result = {};
234
- data.datasets.forEach(function (obj, index) {
235
- var _obj$label2;
236
- 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}`;
237
263
  groups.push(groupName);
238
264
  result[groupName] = whichDataStructure(obj.data);
239
265
  });
240
266
  return {
241
- groups: groups,
267
+ groups,
242
268
  data: result
243
269
  };
244
270
  };
245
- var determineChartTitle = function determineChartTitle(options) {
246
- var _options$plugins;
247
- 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) {
248
273
  if (Array.isArray(options.plugins.title.text)) {
249
274
  return options.plugins.title.text.join(", ");
250
275
  }
@@ -252,62 +277,70 @@ var determineChartTitle = function determineChartTitle(options) {
252
277
  }
253
278
  return "";
254
279
  };
255
- var determineCCElement = function determineCCElement(canvas, provided) {
280
+ const determineCCElement = (canvas, provided) => {
256
281
  if (provided) {
257
282
  return provided;
258
283
  }
259
- var cc = document.createElement("div");
284
+ const cc = document.createElement("div");
260
285
  canvas.insertAdjacentElement("afterend", cc);
261
286
  return cc;
262
287
  };
263
- 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 => {
264
295
  if (!chartStates.has(chart)) {
265
296
  return;
266
297
  }
267
- var _chartStates$get = chartStates.get(chart),
268
- ref = _chartStates$get.c2m,
269
- visible_groups = _chartStates$get.visible_groups;
270
- var _ref$getCurrent = ref.getCurrent(),
271
- point = _ref$getCurrent.point,
272
- 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) || [];
273
308
  try {
274
- var _chart$tooltip;
275
- var highlightElements = [];
309
+ const highlightElements = [];
276
310
  if ("custom" in point) {
311
+ const customPoint = point;
277
312
  highlightElements.push({
278
- // @ts-ignore
279
- datasetIndex: point.custom.group,
280
- // @ts-ignore
281
- index: point.custom.index
313
+ datasetIndex: customPoint.custom.datasetIndex ?? customPoint.custom.group,
314
+ index: customPoint.custom.index
282
315
  });
283
316
  } else {
284
- 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 => {
285
320
  highlightElements.push({
286
- datasetIndex: datasetIndex,
287
- index: index
321
+ datasetIndex: groupIndex - 1,
322
+ index
288
323
  });
289
324
  });
290
325
  }
291
- chart === null || chart === void 0 || chart.setActiveElements(highlightElements);
292
- chart === null || chart === void 0 || (_chart$tooltip = chart.tooltip) === null || _chart$tooltip === void 0 || _chart$tooltip.setActiveElements(highlightElements, {});
293
- chart === null || chart === void 0 || chart.update();
326
+ chart?.setActiveElements(highlightElements);
327
+ chart?.tooltip?.setActiveElements(highlightElements, {});
328
+ chart?.update();
294
329
  } catch (e) {
295
330
  // console.warn(e);
296
331
  }
297
332
  };
298
- var generateChart = function generateChart(chart, options) {
299
- var _scrub$labels, _options$axes, _options$axes2, _scrub$data, _chart$config$options, _groups$map;
300
- var _processChartType = processChartType(chart),
301
- valid = _processChartType.valid,
302
- c2m_types = _processChartType.c2m_types,
303
- invalidType = _processChartType.invalidType;
333
+ const generateChart = (chart, options) => {
334
+ const {
335
+ valid,
336
+ c2m_types,
337
+ invalidType
338
+ } = processChartType(chart);
304
339
  if (!valid) {
305
- var _options$errorCallbac;
306
- // @ts-ignore
307
- (_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(", ")}`);
308
341
  return;
309
342
  }
310
- var axes = generateAxes(chart);
343
+ let axes = generateAxes(chart, options);
311
344
  if (chart.config.type === "wordCloud") {
312
345
  delete axes.x.minimum;
313
346
  delete axes.x.maximum;
@@ -321,96 +354,126 @@ var generateChart = function generateChart(chart, options) {
321
354
  }
322
355
  }
323
356
  // Generate CC element
324
- var cc = determineCCElement(chart.canvas, options.cc);
325
- var _processData = processData(chart.data, c2m_types),
326
- data = _processData.data,
327
- 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;
328
362
  // lastDataObj = JSON.stringify(data);
329
- var scrub = scrubX(data);
330
- 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) {
331
376
  // Something was scrubbed
332
377
  if (!chart.data.labels || chart.data.labels.length === 0) {
333
378
  axes.x.valueLabels = scrub.labels.slice(0);
334
379
  }
335
380
  }
336
381
  if (c2m_types === "scatter") {
337
- scrub === null || scrub === void 0 || delete scrub.data;
382
+ delete scrub?.data;
338
383
  delete axes.x.valueLabels;
339
384
  }
340
- axes = _objectSpread(_objectSpread({}, axes), {}, {
341
- x: _objectSpread(_objectSpread({}, axes.x), (_options$axes = options.axes) === null || _options$axes === void 0 ? void 0 : _options$axes.x),
342
- y: _objectSpread(_objectSpread({}, axes.y), (_options$axes2 = options.axes) === null || _options$axes2 === void 0 ? void 0 : _options$axes2.y)
343
- });
344
- var c2mOptions = {
345
- 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,
346
405
  element: chart.canvas,
347
406
  type: c2m_types,
348
- 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,
349
408
  title: determineChartTitle(chart.options),
350
- axes: axes,
409
+ axes,
351
410
  options: {
352
- // @ts-ignore
353
- onFocusCallback: function onFocusCallback() {
354
- displayPoint(chart);
355
- }
411
+ ...userOptions,
412
+ onFocusCallback: userOnFocusCallback ? point => {
413
+ pluginOnFocusCallback();
414
+ userOnFocusCallback(point);
415
+ } : pluginOnFocusCallback
356
416
  }
357
417
  };
358
418
  if (Array.isArray(c2mOptions.data)) {
359
419
  if (isNaN(c2mOptions.data[0])) {
360
- c2mOptions.data = c2mOptions.data.map(function (point, index) {
361
- return _objectSpread(_objectSpread({}, point), {}, {
420
+ c2mOptions.data = c2mOptions.data.map((point, index) => {
421
+ return {
422
+ ...point,
362
423
  custom: {
363
- group: 0,
364
- index: index
424
+ ...point.custom,
425
+ group: point.custom?.group ?? 0,
426
+ index: point.custom?.index ?? index
365
427
  }
366
- });
428
+ };
367
429
  });
368
430
  } else {
369
- c2mOptions.data = c2mOptions.data.map(function (num, index) {
431
+ c2mOptions.data = c2mOptions.data.map((num, index) => {
370
432
  return {
371
433
  x: index,
372
434
  y: num,
373
435
  custom: {
374
436
  group: 0,
375
- index: index
437
+ index
376
438
  }
377
439
  };
378
440
  });
379
441
  }
380
442
  } else {
381
- var _groups = Object.keys(c2mOptions.data);
382
- _groups.forEach(function (groupName, groupNumber) {
383
- if (!isNaN(c2mOptions.data[groupName][0])) {
384
- 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) => {
385
448
  return {
386
449
  x: index,
387
450
  y: num,
388
451
  custom: {
389
452
  group: groupNumber,
390
- index: index
453
+ index
391
454
  }
392
455
  };
393
456
  });
394
457
  } else {
395
- c2mOptions.data[groupName] = c2mOptions.data[groupName].map(function (point, index) {
396
- return _objectSpread(_objectSpread({}, point), {}, {
458
+ dataObj[groupName] = dataObj[groupName].map((point, index) => {
459
+ return {
460
+ ...point,
397
461
  custom: {
398
- group: groupNumber,
399
- index: index
462
+ ...point.custom,
463
+ group: point.custom?.group ?? groupNumber,
464
+ index: point.custom?.index ?? index
400
465
  }
401
- });
466
+ };
402
467
  });
403
468
  }
404
469
  });
405
470
  }
406
- // @ts-ignore
407
- 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) {
408
- // @ts-ignore
409
- c2mOptions.options.stack = true;
471
+ if (chart.config.options?.scales?.x?.stacked) {
472
+ if (c2mOptions.options) {
473
+ c2mOptions.options.stack = true;
474
+ }
410
475
  }
411
- // @ts-ignore
412
476
  if (options.audioEngine) {
413
- // @ts-ignore
414
477
  c2mOptions.audioEngine = options.audioEngine;
415
478
  }
416
479
  if (c2mOptions.data.length === 0) {
@@ -419,36 +482,51 @@ var generateChart = function generateChart(chart, options) {
419
482
  if (options.lang) {
420
483
  c2mOptions.lang = options.lang;
421
484
  }
422
- var _c2mChart = (0, _chart2music["default"])(c2mOptions),
423
- err = _c2mChart.err,
424
- c2m = _c2mChart.data;
485
+ const {
486
+ err,
487
+ data: c2m
488
+ } = (0, _chart2music.default)(c2mOptions);
425
489
  /* istanbul-ignore-next */
426
490
  if (err) {
427
- var _options$errorCallbac2;
428
- // @ts-ignore
429
- (_options$errorCallbac2 = options.errorCallback) === null || _options$errorCallbac2 === void 0 || _options$errorCallbac2.call(options, err);
491
+ options.errorCallback?.(err);
430
492
  return;
431
493
  }
432
494
  if (!c2m) {
433
495
  return;
434
496
  }
435
497
  chartStates.set(chart, {
436
- c2m: c2m,
437
- visible_groups: (_groups$map = groups === null || groups === void 0 ? void 0 : groups.map(function (g, i) {
438
- return i;
439
- })) !== null && _groups$map !== void 0 ? _groups$map : []
498
+ c2m,
499
+ lastDataSnapshot: createDataSnapshot(chart)
440
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
+ }
441
520
  };
442
- var plugin = exports["default"] = {
521
+ const plugin = exports.default = {
443
522
  id: "chartjs2music",
444
- afterInit: function afterInit(chart, args, options) {
523
+ afterInit: (chart, _args, options) => {
445
524
  if (!chartStates.has(chart)) {
446
525
  generateChart(chart, options);
447
526
  // Remove tooltip when the chart blurs
448
- chart.canvas.addEventListener("blur", function () {
449
- var _chart$tooltip2;
527
+ chart.canvas.addEventListener("blur", () => {
450
528
  chart.setActiveElements([]);
451
- (_chart$tooltip2 = chart.tooltip) === null || _chart$tooltip2 === void 0 || _chart$tooltip2.setActiveElements([], {});
529
+ chart.tooltip?.setActiveElements([], {});
452
530
  try {
453
531
  chart.update();
454
532
  } catch (e) {
@@ -456,58 +534,85 @@ var plugin = exports["default"] = {
456
534
  }
457
535
  });
458
536
  // Show tooltip when the chart receives focus
459
- chart.canvas.addEventListener("focus", function () {
537
+ chart.canvas.addEventListener("focus", () => {
460
538
  displayPoint(chart);
461
539
  });
462
540
  }
463
541
  },
464
- afterDatasetUpdate: function afterDatasetUpdate(chart, args, options) {
542
+ afterDatasetUpdate: (chart, args, options) => {
465
543
  if (!args.mode) {
466
544
  return;
467
545
  }
468
546
  if (!chartStates.has(chart)) {
469
547
  generateChart(chart, options);
470
548
  }
471
- var _chartStates$get2 = chartStates.get(chart),
472
- ref = _chartStates$get2.c2m,
473
- visible_groups = _chartStates$get2.visible_groups;
549
+ const {
550
+ c2m: ref
551
+ } = chartStates.get(chart);
474
552
  if (!ref) {
475
553
  return;
476
554
  }
477
- // @ts-ignore
478
- var groups = ref._groups.slice(0);
479
- // @ts-ignore
480
- if (ref._options.stack) {
555
+ const refInternal = ref;
556
+ const groups = refInternal._groups.slice(0);
557
+ if (refInternal._options.stack) {
481
558
  groups.shift();
482
559
  }
483
560
  if (args.mode === "hide") {
484
- var err = ref.setCategoryVisibility(groups[args.index], false);
485
- visible_groups.splice(args.index, 1);
561
+ const err = ref.setCategoryVisibility(groups[args.index], false);
486
562
  if (err) {
487
563
  console.error(err);
488
564
  }
489
565
  return;
490
566
  }
491
567
  if (args.mode === "show") {
492
- var _err = ref.setCategoryVisibility(groups[args.index], true);
493
- visible_groups.push(args.index);
494
- if (_err) {
495
- console.error(_err);
568
+ const err = ref.setCategoryVisibility(groups[args.index], true);
569
+ if (err) {
570
+ console.error(err);
496
571
  }
497
572
  return;
498
573
  }
499
574
  },
500
- afterDestroy: function afterDestroy(chart) {
501
- var _chartStates$get3 = chartStates.get(chart),
502
- ref = _chartStates$get3.c2m;
503
- 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) {
504
602
  return;
505
603
  }
604
+ const {
605
+ c2m: ref
606
+ } = state;
607
+ if (state?.matrixKeydown && state.matrixKeydownTarget) {
608
+ state.matrixKeydownTarget.removeEventListener("keydown", state.matrixKeydown, true);
609
+ }
506
610
  ref.cleanUp();
507
611
  },
508
612
  defaults: {
509
613
  cc: null,
510
614
  audioEngine: null,
511
- errorCallback: null
615
+ errorCallback: null,
616
+ options: {}
512
617
  }
513
618
  };