@hestia-earth/ui-components-charts 0.43.23

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/index.js ADDED
@@ -0,0 +1,834 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@hestia-earth/utils');
4
+ var Gradient = require('javascript-color-gradient');
5
+ var shadeGenerator = require('shade-generator/dist/shadeGenerator');
6
+ var emissionGroupings = require('@hestia-earth/glossary/resources/emission-groupings.json');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var Gradient__default = /*#__PURE__*/_interopDefault(Gradient);
11
+ var emissionGroupings__default = /*#__PURE__*/_interopDefault(emissionGroupings);
12
+
13
+ // src/charts/bar.ts
14
+
15
+ // src/charts/constants.ts
16
+ var defaultTicksFont = {
17
+ family: "Lato",
18
+ size: 13
19
+ };
20
+ var chartLabelMaxLength = 50;
21
+
22
+ // src/charts/bar.ts
23
+ var grey = "#4a4a4a";
24
+ var defaultBarSettings = Object.freeze({
25
+ type: "bar",
26
+ options: {
27
+ animation: {
28
+ duration: 0
29
+ },
30
+ plugins: {
31
+ legend: {
32
+ align: "start"
33
+ },
34
+ tooltip: {
35
+ enabled: false
36
+ }
37
+ },
38
+ scales: {
39
+ x: {
40
+ display: true,
41
+ min: 0,
42
+ title: {
43
+ display: false,
44
+ color: grey,
45
+ font: defaultTicksFont
46
+ },
47
+ ticks: {
48
+ font: defaultTicksFont
49
+ }
50
+ },
51
+ y: {
52
+ display: true,
53
+ position: "left",
54
+ border: {
55
+ display: true
56
+ },
57
+ grid: {
58
+ display: true,
59
+ drawOnChartArea: true,
60
+ drawTicks: true,
61
+ tickLength: 8,
62
+ offset: false
63
+ },
64
+ ticks: {
65
+ color: grey,
66
+ padding: 4,
67
+ crossAlign: "center",
68
+ font: {
69
+ ...defaultTicksFont,
70
+ weight: 400
71
+ },
72
+ callback: function(value) {
73
+ const label = this.getLabelForValue(value);
74
+ return utils.ellipsis(label, chartLabelMaxLength);
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ });
81
+ var defaultBarDatasets = {
82
+ barThickness: 2,
83
+ barPercentage: 1,
84
+ categoryPercentage: 1
85
+ };
86
+ var withOthers = (data2, maximum) => {
87
+ const included = data2.slice(0, maximum);
88
+ const excluded = data2.slice(maximum);
89
+ return excluded.length ? [
90
+ ...included,
91
+ {
92
+ label: `${excluded.length} others`,
93
+ count: utils.sum(excluded.map(({ count }) => count)),
94
+ backgroundColor: excluded[0].backgroundColor,
95
+ borderColor: excluded[0].borderColor,
96
+ color: excluded[0].color,
97
+ includedItems: excluded
98
+ }
99
+ ] : included;
100
+ };
101
+ var hasNegativeContributions = (data2 = []) => data2.some(({ count }) => count < 0);
102
+ var barDataset = (data2, label = "", showNegativeValues = true) => ({
103
+ ...defaultBarDatasets,
104
+ backgroundColor: data2.map(({ backgroundColor, color }) => backgroundColor || color),
105
+ borderColor: data2.map(({ borderColor, color }) => borderColor || color),
106
+ data: data2.map(({ count }) => utils.toPrecision(showNegativeValues ? count : Math.max(0, count))),
107
+ label
108
+ });
109
+ var ColorPalette = /* @__PURE__ */ ((ColorPalette2) => {
110
+ ColorPalette2[ColorPalette2["categorial"] = 0] = "categorial";
111
+ ColorPalette2[ColorPalette2["continuous"] = 1] = "continuous";
112
+ ColorPalette2[ColorPalette2["divergent"] = 2] = "divergent";
113
+ return ColorPalette2;
114
+ })(ColorPalette || {});
115
+ var palette = Object.freeze({
116
+ [0 /* categorial */]: [
117
+ "#193957",
118
+ "#FFC000",
119
+ "#249DA5",
120
+ "#DC7033",
121
+ "#349B3E",
122
+ "#CCA76D",
123
+ "#6394CE",
124
+ "#9AB04C",
125
+ "#8B50A7",
126
+ "#8A5402"
127
+ ],
128
+ [1 /* continuous */]: [
129
+ "#193957",
130
+ "#1D586F",
131
+ "#207788",
132
+ "#498F78",
133
+ "#7FA55B",
134
+ "#B0B43B",
135
+ "#DDBB19",
136
+ "#FFC514",
137
+ "#FDD866",
138
+ "#FCEBB8"
139
+ ],
140
+ [2 /* divergent */]: [
141
+ "#193957",
142
+ "#1D6176",
143
+ "#228A96",
144
+ "#58B3B5",
145
+ "#ACD6CF",
146
+ "#FCEBB8",
147
+ "#FCE18F",
148
+ "#FDD763",
149
+ "#FECC34",
150
+ "#FFC000"
151
+ ]
152
+ });
153
+ var getGradients = (...colours) => new Gradient__default.default().setColorGradient(...colours).setMidpoint(10).getColors();
154
+ var getColorGradient = (index, colors) => {
155
+ const start = colors[index % colors.length];
156
+ const end = colors[index % colors.length + 1];
157
+ const gradientIndex = Math.ceil(index / colors.length);
158
+ return getGradients(start, end)[gradientIndex];
159
+ };
160
+ var getColorShade = (index, colors) => {
161
+ const color = colors[index % colors.length];
162
+ const shade = Math.min(100, Math.max(10, Math.abs(100 - Math.ceil(index / colors.length) * 10)));
163
+ return shadeGenerator.ShadeGenerator.hue(color).shade(`${shade}`).hex();
164
+ };
165
+ var getColor = (index, scheme = 0 /* categorial */) => {
166
+ const colors = palette[scheme];
167
+ return index < colors.length ? colors[index] : scheme === 0 /* categorial */ ? getColorShade(index, colors) : getColorGradient(index, colors);
168
+ };
169
+ var listColor = (_v, index) => getColor(index);
170
+ var listColorContinuous = (_v, index) => getColor(index, 1 /* continuous */);
171
+ var parseColor = (color) => {
172
+ let r = 75, g = 192, b = 192;
173
+ if (color) {
174
+ if (color.startsWith("#")) {
175
+ const hex = color.replace("#", "");
176
+ r = parseInt(hex.substring(0, 2), 16);
177
+ g = parseInt(hex.substring(2, 4), 16);
178
+ b = parseInt(hex.substring(4, 6), 16);
179
+ } else if (color.startsWith("rgb")) {
180
+ const matches = color.match(/\d+/g);
181
+ if (matches) {
182
+ r = parseInt(matches[0]);
183
+ g = parseInt(matches[1]);
184
+ b = parseInt(matches[2]);
185
+ }
186
+ }
187
+ }
188
+ return { r, g, b };
189
+ };
190
+ var colorToRgba = (color, aplha) => `rgba(${color.r}, ${color.g}, ${color.b}, ${aplha})`;
191
+ var hexToRgba = (hex, aplha = 1) => {
192
+ const color = parseColor(hex);
193
+ return color ? `rgba(${color.r}, ${color.g}, ${color.b}, ${aplha})` : hex;
194
+ };
195
+ var listColorWithAlpha = (alpha = 0.8) => (_v, index) => hexToRgba(getColor(index), alpha);
196
+ var sumValues = (values) => {
197
+ const definedValues = values.filter((value) => !utils.isUndefined(value));
198
+ return definedValues.length ? utils.sum(definedValues) : void 0;
199
+ };
200
+ var groupByCategory = (data2, category, emissionCategoryValue2) => data2.reduce(
201
+ (prev, curr) => {
202
+ const groupKey = emissionCategoryValue2(curr.id, category);
203
+ prev[groupKey] = prev[groupKey] || [];
204
+ prev[groupKey].push(curr);
205
+ return prev;
206
+ },
207
+ {}
208
+ );
209
+ var groupDataByCategory = (data2, labels, category, { emissionCategoryValue: emissionCategoryValue2, categoryLabel: categoryLabel2 }) => {
210
+ if (!category) {
211
+ return data2;
212
+ }
213
+ const groupedData = groupByCategory(data2, category, emissionCategoryValue2);
214
+ return Object.keys(groupedData).length === 1 ? data2 : Object.entries(groupedData).map(([groupKey, items], index) => ({
215
+ label: categoryLabel2(groupKey),
216
+ values: labels.map((_, i) => sumValues(items.map((item) => item.values[i]))),
217
+ color: listColor(groupKey, index),
218
+ includedItems: items.map((item, i) => ({
219
+ ...item,
220
+ color: listColor(item.id, i)
221
+ }))
222
+ }));
223
+ };
224
+
225
+ // src/charts/csv.ts
226
+ var escape = (value) => `${value}`.includes(",") ? `"${value}"` : value;
227
+ var toLine = (values) => values.map(escape).join(",");
228
+ var data = emissionGroupings__default.default;
229
+ var categoryLabel = (key) => key ? utils.keyToLabel(key.replace(/category/gi, "")) : "Unset";
230
+ var emissionCategories = Object.keys(data).filter((key) => key.toLowerCase().endsWith("category")).map((key) => ({ key, label: categoryLabel(key) }));
231
+ var categoryValues = (category) => data[category];
232
+ var emissionCategoriesOf = (emissionId) => Object.keys(data[emissionId]).filter((key) => !!data[emissionId][key]);
233
+ var emissionCategoryValue = (emissionId, category) => {
234
+ var _a;
235
+ return (_a = data[emissionId]) == null ? void 0 : _a[category];
236
+ };
237
+ var defaultHorizontalBarConfig = Object.freeze({
238
+ options: {
239
+ indexAxis: "y",
240
+ scales: {
241
+ x: {
242
+ grid: {
243
+ drawOnChartArea: false
244
+ },
245
+ ticks: {
246
+ callback: (value) => utils.toPrecision(Number(value), 3)
247
+ }
248
+ },
249
+ y: {
250
+ grid: {
251
+ drawOnChartArea: false
252
+ },
253
+ ticks: {
254
+ crossAlign: "near"
255
+ }
256
+ }
257
+ }
258
+ }
259
+ });
260
+ var defaultHorizontalTooltip = ({ label, count, includedItems }) => (includedItems == null ? void 0 : includedItems.length) ? includedItems.map(defaultHorizontalTooltip).join("</br>") : `${label}: ${count}`;
261
+ var rowHeight = 40;
262
+ var padding = 60;
263
+ var horizontalBarHeight = (minHeight, maxHeight, nbRows) => Math.min(Math.max(nbRows * rowHeight + padding, minHeight), maxHeight);
264
+ var placementGap = (placement) => placement === "right" ? 8 : -8;
265
+ var placementX = (x, chart, data2, placement) => placement === "right" ? !Array.isArray(data2) && data2 < 0 ? positionAtZero(chart) : x : x;
266
+ var positionAtZero = (chart) => chart.scales.x.getPixelForValue(0);
267
+ var defaultBarDrawSettings = {
268
+ placement: "right",
269
+ xPosFn: (x, index, width, chart, data2, placement) => (utils.isEmpty(data2) ? positionAtZero(chart) : placementX(x, chart, data2, placement)) + placementGap(placement),
270
+ yPosFn: (y) => y + 3,
271
+ colorFn: (m, index, chart, data2) => utils.isUndefined(data2) ? "#b5b5b5" : "#4a4a4a",
272
+ textFn: ({ data: data2 }) => `${data2}`,
273
+ font: `${defaultTicksFont.size}px ${defaultTicksFont.family}`,
274
+ maxWidth: 90,
275
+ emptyValueLabel: "No data"
276
+ };
277
+ var drawText = (ctx, {
278
+ text,
279
+ font,
280
+ fillStyle,
281
+ placement,
282
+ xPos,
283
+ yPos,
284
+ maxWidth
285
+ }) => {
286
+ ctx.font = font;
287
+ ctx.fillStyle = fillStyle;
288
+ ctx.textAlign = placement === "left" ? "right" : "left";
289
+ if (Array.isArray(text)) {
290
+ ctx.fillText(text[0], xPos, yPos - 5, maxWidth);
291
+ ctx.fillText(text[1], xPos, yPos + 5, maxWidth);
292
+ } else {
293
+ ctx.fillText(text, xPos, yPos, maxWidth);
294
+ }
295
+ };
296
+ var calculateNextData = (currentData, newData, hasExistingData) => utils.isUndefined(newData) ? currentData : hasExistingData ? typeof newData === "number" && typeof currentData === "number" ? currentData + newData : newData : newData;
297
+ var afterBarDrawPlugin = (settings) => ({
298
+ id: ["afterBarDrawPlugin", settings == null ? void 0 : settings.placement].filter(Boolean).join("-"),
299
+ afterDatasetsDraw: (chart) => {
300
+ var _a, _b;
301
+ if (!((_a = chart.data.datasets) == null ? void 0 : _a.length) || !((_b = chart.data.labels) == null ? void 0 : _b.length)) {
302
+ return;
303
+ }
304
+ const { placement, xPosFn, yPosFn, colorFn, textFn, maxWidth, font, emptyValueLabel } = {
305
+ ...defaultBarDrawSettings,
306
+ ...settings != null ? settings : {}
307
+ };
308
+ const { ctx, width, height } = chart;
309
+ ctx.save();
310
+ const getStackItems = (index) => chart.data.datasets.map((dataset, dsIndex) => ({ dataset, meta: chart.getDatasetMeta(dsIndex) })).filter(({ meta }) => !meta.hidden).map(({ dataset, meta }) => ({
311
+ element: meta.data[index],
312
+ dataVal: dataset.data[index]
313
+ })).filter(({ element }) => element && !element.skip);
314
+ const aggregateStackItems = (stackItems) => stackItems.reduce(
315
+ (acc, { element, dataVal }) => ({
316
+ finalData: calculateNextData(acc.finalData, dataVal, acc.hasData),
317
+ hasData: acc.hasData || !utils.isUndefined(dataVal),
318
+ minX: Math.min(acc.minX, element.base, element.x),
319
+ maxX: Math.max(acc.maxX, element.base, element.x),
320
+ lastVisibleElement: element
321
+ }),
322
+ {
323
+ finalData: void 0,
324
+ hasData: false,
325
+ minX: Infinity,
326
+ maxX: -Infinity,
327
+ lastVisibleElement: stackItems[0].element
328
+ }
329
+ );
330
+ const getLabel = (index) => {
331
+ var _a2, _b2;
332
+ return (_b2 = (_a2 = chart.data.labels) == null ? void 0 : _a2[index]) != null ? _b2 : "";
333
+ };
334
+ const getXPos = (anchorX, index, dataToPass) => xPosFn(anchorX, index, width, chart, dataToPass, placement);
335
+ const getYPos = (y, index, dataToPass) => yPosFn(y, index, height, chart, dataToPass, placement);
336
+ const getAnchorX = (minX, maxX) => placement === "left" ? minX : maxX;
337
+ const processIndex = (index) => {
338
+ const stackItems = getStackItems(index);
339
+ if (!stackItems.length) {
340
+ return;
341
+ }
342
+ const { finalData, hasData, minX, maxX, lastVisibleElement } = aggregateStackItems(stackItems);
343
+ const dataToPass = hasData ? finalData : void 0;
344
+ const text = !hasData ? emptyValueLabel : textFn({ label: getLabel(index), data: dataToPass }, index, chart);
345
+ if (text) {
346
+ drawText(ctx, {
347
+ text,
348
+ font,
349
+ fillStyle: colorFn(lastVisibleElement.options, index, chart, dataToPass),
350
+ placement,
351
+ xPos: getXPos(getAnchorX(minX, maxX), index, dataToPass),
352
+ yPos: getYPos(lastVisibleElement.y, index, dataToPass),
353
+ maxWidth
354
+ });
355
+ }
356
+ };
357
+ const indices = Array.from({ length: chart.data.labels.length }, (_, i) => i);
358
+ indices.forEach(processIndex);
359
+ ctx.restore();
360
+ }
361
+ });
362
+
363
+ // src/charts/plugins/axisHover.plugin.ts
364
+ var axisLabelCursor = ({ label, event }) => {
365
+ var _a;
366
+ const canvas = (_a = event == null ? void 0 : event.native) == null ? void 0 : _a.target;
367
+ if (label && canvas) {
368
+ canvas.style.cursor = "pointer";
369
+ }
370
+ };
371
+ var axisSettings = (chart) => {
372
+ var _a;
373
+ return ((_a = chart.config.options.plugins) == null ? void 0 : _a.axisHover) || {};
374
+ };
375
+ var disabled = (chart, { onHoverLabel, onClickLabel }) => !chart.scales.y || !onHoverLabel && !onClickLabel;
376
+ var isOverAxis = (scale, event) => event.x != null && event.x >= scale.left && event.x <= scale.right;
377
+ var tickAt = (scale, y, maxDistance) => {
378
+ const ticks = scale.getTicks();
379
+ const position = ticks.findIndex((_, index) => Math.abs(y - scale.getPixelForTick(index)) < maxDistance);
380
+ return position < 0 ? null : { index: ticks[position].value, y: scale.getPixelForTick(position) };
381
+ };
382
+ var labelAt = (chart, index) => index >= 0 ? chart.data.labels[index] : "";
383
+ var labelEvent = (chart, event, maxDistance) => {
384
+ const scale = chart.scales.y;
385
+ const tick = isOverAxis(scale, event) ? tickAt(scale, event.y, maxDistance) : null;
386
+ const index = tick ? tick.index : -1;
387
+ return {
388
+ label: labelAt(chart, index),
389
+ index,
390
+ anchorX: (scale.left + scale.right) / 2,
391
+ anchorY: (tick ? tick.y : event.y) + maxDistance,
392
+ event
393
+ };
394
+ };
395
+ var notifyClick = ({ onClickLabel }, data2) => data2.index >= 0 && data2.event.type === "click" ? onClickLabel == null ? void 0 : onClickLabel(data2) : void 0;
396
+ var axisHoverPlugin = {
397
+ id: "axisHover",
398
+ afterEvent(chart, { event }) {
399
+ var _a, _b;
400
+ const settings = axisSettings(chart);
401
+ if (disabled(chart, settings)) return;
402
+ const data2 = labelEvent(chart, event, (_a = settings.maxDistance) != null ? _a : 15);
403
+ (_b = settings.onHoverLabel) == null ? void 0 : _b.call(settings, data2);
404
+ notifyClick(settings, data2);
405
+ }
406
+ };
407
+
408
+ // src/charts/plugins/background-hover.plugin.ts
409
+ var createHoverGradient = (ctx, chartArea, color) => {
410
+ const gradient = ctx.createLinearGradient(chartArea.left, 0, chartArea.right, 0);
411
+ gradient.addColorStop(0, colorToRgba(color, 0.05));
412
+ gradient.addColorStop(0.5, colorToRgba(color, 0.12));
413
+ gradient.addColorStop(1, colorToRgba(color, 0.05));
414
+ return gradient;
415
+ };
416
+ var calculateBarBounds = (element, indexAxis = "y") => {
417
+ const { x, y, base, width, height } = element;
418
+ if (indexAxis === "y") {
419
+ const barHeight = height;
420
+ const barTop = y - barHeight / 2;
421
+ const barBottom = y + barHeight / 2;
422
+ const barLeft = Math.min(base, x);
423
+ const barRight = Math.max(base, x);
424
+ return { barLeft, barRight, barTop, barBottom, thickness: barHeight };
425
+ } else {
426
+ const barWidth = width;
427
+ const barLeft = x - barWidth / 2;
428
+ const barRight = x + barWidth / 2;
429
+ const barTop = Math.min(base, y);
430
+ const barBottom = Math.max(base, y);
431
+ return { barLeft, barRight, barTop, barBottom, thickness: barWidth };
432
+ }
433
+ };
434
+ var calculateShadowBounds = (element, bounds, threshold) => {
435
+ return {
436
+ top: bounds.barTop - threshold,
437
+ bottom: bounds.barBottom + threshold,
438
+ left: bounds.barLeft,
439
+ // Usually we expand Y (thickness), but keep X (length) capped at value
440
+ right: bounds.barRight,
441
+ width: bounds.barRight - bounds.barLeft,
442
+ height: bounds.barBottom + threshold - (bounds.barTop - threshold)
443
+ };
444
+ };
445
+ var drawHoverEffect = (ctx, chartArea, element, threshold, indexAxis) => {
446
+ const opts = element.options || null;
447
+ const barColor = (opts == null ? void 0 : opts.backgroundColor) || (opts == null ? void 0 : opts.borderColor) || "#000000";
448
+ if (barColor.length !== 7) {
449
+ return;
450
+ }
451
+ const color = parseColor(barColor);
452
+ const bounds = calculateBarBounds(element, indexAxis);
453
+ const shadow = calculateShadowBounds(element, bounds, threshold);
454
+ ctx.save();
455
+ const gradient = createHoverGradient(ctx, { left: shadow.left, right: shadow.right }, color);
456
+ ctx.fillStyle = gradient;
457
+ ctx.fillRect(shadow.left, shadow.top, shadow.width, shadow.height);
458
+ drawHorizontalLines(ctx, { left: shadow.left, right: shadow.right }, shadow.top, shadow.bottom, color);
459
+ ctx.restore();
460
+ };
461
+ var drawHorizontalLines = (ctx, barArea, topY, bottomY, color) => {
462
+ ctx.strokeStyle = colorToRgba(color, 0.25);
463
+ ctx.lineWidth = 1;
464
+ ctx.setLineDash([5, 5]);
465
+ ctx.beginPath();
466
+ ctx.moveTo(barArea.left, topY);
467
+ ctx.lineTo(barArea.right, topY);
468
+ ctx.moveTo(barArea.left, bottomY);
469
+ ctx.lineTo(barArea.right, bottomY);
470
+ ctx.stroke();
471
+ };
472
+ var calculateDistanceToBar = (x, y, bounds) => {
473
+ let distance = 0;
474
+ if (x < bounds.barLeft) distance += Math.pow(bounds.barLeft - x, 2);
475
+ else if (x > bounds.barRight) distance += Math.pow(x - bounds.barRight, 2);
476
+ if (y < bounds.barTop) distance += Math.pow(bounds.barTop - y, 2);
477
+ else if (y > bounds.barBottom) distance += Math.pow(y - bounds.barBottom, 2);
478
+ return Math.sqrt(distance);
479
+ };
480
+ var isWithinHitArea = (x, y, bounds, threshold) => {
481
+ const hitLeft = bounds.barLeft - threshold;
482
+ const hitRight = bounds.barRight + threshold;
483
+ const hitTop = bounds.barTop - threshold;
484
+ const hitBottom = bounds.barBottom + threshold;
485
+ return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
486
+ };
487
+ var checkBarProximity = (element, index, datasetIndex, x, y, threshold, currentClosest, indexAxis) => {
488
+ const bar2 = element;
489
+ if (bar2.x == null || bar2.y == null) return;
490
+ const bounds = calculateBarBounds(bar2, indexAxis);
491
+ if (!isWithinHitArea(x, y, bounds, threshold)) return;
492
+ const distance = calculateDistanceToBar(x, y, bounds);
493
+ if (distance < currentClosest.distance) {
494
+ currentClosest.distance = distance;
495
+ currentClosest.bar = {
496
+ datasetIndex,
497
+ index,
498
+ element: bar2
499
+ // Save the whole element
500
+ };
501
+ }
502
+ };
503
+ var findNearBar = (chart, x, y, threshold) => {
504
+ var _a;
505
+ const closest = { bar: null, distance: Infinity };
506
+ const indexAxis = chart.options.indexAxis || "x";
507
+ (_a = chart.data.datasets) == null ? void 0 : _a.forEach((dataset, datasetIndex) => {
508
+ if (!chart.isDatasetVisible(datasetIndex)) return;
509
+ const meta = chart.getDatasetMeta(datasetIndex);
510
+ meta.data.forEach((element, index) => {
511
+ if (element.hidden || element.skip) return;
512
+ checkBarProximity(element, index, datasetIndex, x, y, threshold, closest, indexAxis);
513
+ });
514
+ });
515
+ return closest.distance <= threshold ? closest.bar : void 0;
516
+ };
517
+ var triggerBarClick = (chart, barData) => {
518
+ var _a;
519
+ const activeElements = [
520
+ {
521
+ element: barData.element,
522
+ datasetIndex: barData.datasetIndex,
523
+ index: barData.index
524
+ }
525
+ ];
526
+ chart.setActiveElements(activeElements);
527
+ (_a = chart.tooltip) == null ? void 0 : _a.setActiveElements(activeElements, { x: 0, y: 0 });
528
+ const onClickHandler = chart.options.onClick;
529
+ if (onClickHandler) {
530
+ const mockEvent = {
531
+ type: "click",
532
+ x: barData.element.x,
533
+ y: barData.element.y,
534
+ chart,
535
+ native: {
536
+ target: chart.canvas,
537
+ preventDefault: () => {
538
+ },
539
+ stopPropagation: () => {
540
+ }
541
+ }
542
+ };
543
+ onClickHandler.call(chart, mockEvent, activeElements, chart);
544
+ }
545
+ chart.update();
546
+ };
547
+ var handleMouseMove = (chart, nearBar, threshold, e) => {
548
+ const newNearBar = findNearBar(chart, e.x, e.y, threshold);
549
+ const prevCursor = chart.canvas.style.cursor;
550
+ const newCursor = newNearBar ? "pointer" : "default";
551
+ if (prevCursor !== newCursor) {
552
+ chart.canvas.style.cursor = newCursor;
553
+ }
554
+ const hasChanged = (newNearBar == null ? void 0 : newNearBar.datasetIndex) !== (nearBar == null ? void 0 : nearBar.datasetIndex) || (newNearBar == null ? void 0 : newNearBar.index) !== (nearBar == null ? void 0 : nearBar.index);
555
+ return {
556
+ nearBar: hasChanged ? newNearBar || null : nearBar,
557
+ changed: hasChanged
558
+ };
559
+ };
560
+ var handleMouseOut = (chart, nearBar) => {
561
+ chart.canvas.style.cursor = "default";
562
+ return !!nearBar;
563
+ };
564
+ var handleClick = (chart, nearBar) => {
565
+ if (nearBar) triggerBarClick(chart, nearBar);
566
+ };
567
+ var getActiveBar = (chart) => {
568
+ var _a, _b;
569
+ const activeElements = (_a = chart.tooltip) == null ? void 0 : _a.getActiveElements();
570
+ return (_b = activeElements == null ? void 0 : activeElements[0]) == null ? void 0 : _b.element;
571
+ };
572
+ var defaultSettings = { threshold: 10 };
573
+ var backgroundHoverPlugin = (settings = {}) => {
574
+ let nearBar = null;
575
+ const { threshold } = { ...defaultSettings, ...settings };
576
+ return {
577
+ id: "backgroundHover",
578
+ afterEvent: (chart, args) => {
579
+ const e = args.event;
580
+ let changed = false;
581
+ if (e.type === "mousemove") {
582
+ const result = handleMouseMove(chart, nearBar, threshold || 10, e);
583
+ nearBar = result.nearBar;
584
+ changed = result.changed;
585
+ } else if (e.type === "mouseout") {
586
+ changed = handleMouseOut(chart, nearBar);
587
+ nearBar = null;
588
+ } else if (e.type === "click") {
589
+ handleClick(chart, nearBar);
590
+ }
591
+ args.changed = changed;
592
+ },
593
+ beforeDraw: (chart) => {
594
+ const indexAxis = chart.options.indexAxis || "x";
595
+ const activeBar = nearBar ? nearBar.element : getActiveBar(chart);
596
+ activeBar && drawHoverEffect(chart.ctx, chart.chartArea, activeBar, threshold || 10, indexAxis);
597
+ }
598
+ };
599
+ };
600
+ var isMouseInsideCircle = (event, circleCenter, circleRadius) => {
601
+ const { clientX, clientY } = event;
602
+ const circleX = circleCenter.x;
603
+ const circleY = circleCenter.y;
604
+ const distance = Math.sqrt((clientX - circleX) ** 2 + (clientY - circleY) ** 2);
605
+ return distance <= circleRadius;
606
+ };
607
+ var circle = (ctx, x, y, color, radius) => {
608
+ ctx.beginPath();
609
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
610
+ ctx.fillStyle = color;
611
+ ctx.fill();
612
+ };
613
+ var line = (ctx, startX, endX, y, color, lineWidth) => {
614
+ ctx.beginPath();
615
+ ctx.lineWidth = lineWidth;
616
+ ctx.strokeStyle = color;
617
+ ctx.moveTo(startX, y);
618
+ ctx.lineTo(endX, y);
619
+ ctx.stroke();
620
+ };
621
+ var defaultLollipopSettings = {
622
+ circleRadius: 4,
623
+ colorFn: (m) => m.backgroundColor,
624
+ isMouseInsideLollipopFn: () => null
625
+ };
626
+ var lollipopChartPlugin = (settings) => ({
627
+ id: "lollipopChartPlugin",
628
+ afterDatasetsDraw: (chart) => {
629
+ var _a;
630
+ if (!((_a = chart.data.datasets) == null ? void 0 : _a.length)) {
631
+ return;
632
+ }
633
+ const { circleRadius, lineWidth, colorFn, isMouseInsideLollipopFn, valueFn } = {
634
+ ...defaultLollipopSettings,
635
+ ...settings != null ? settings : {}
636
+ };
637
+ const { ctx } = chart;
638
+ ctx.save();
639
+ const clickableDictionary = [];
640
+ chart.data.datasets.forEach((dataset, datasetIndex) => {
641
+ const meta = chart.getDatasetMeta(datasetIndex);
642
+ if (meta.hidden) return;
643
+ ctx.save();
644
+ const elements = meta.data;
645
+ elements.filter((element) => !element.skip).forEach((element, index) => {
646
+ const { base, x, y } = element;
647
+ const color = colorFn(element.options, index);
648
+ const data2 = dataset.data[index];
649
+ const overrideValue = valueFn == null ? void 0 : valueFn(dataset, index, chart);
650
+ const xValue = utils.isUndefined(overrideValue) ? x : chart.scales.x.getPixelForValue(overrideValue);
651
+ clickableDictionary.push(
652
+ (clientX, clientY) => isMouseInsideCircle({ clientX, clientY }, { x: xValue, y }, circleRadius)
653
+ );
654
+ circle(ctx, xValue, y, color, circleRadius);
655
+ [Array.isArray(data2), !!lineWidth].every(Boolean) && line(ctx, base, x, y, color, lineWidth);
656
+ if (utils.isUndefined(overrideValue)) {
657
+ clickableDictionary.push(
658
+ (clientX, clientY) => isMouseInsideCircle({ clientX, clientY }, { x: base, y }, circleRadius)
659
+ );
660
+ Array.isArray(data2) && circle(ctx, base, y, color, circleRadius);
661
+ }
662
+ });
663
+ ctx.restore();
664
+ });
665
+ isMouseInsideLollipopFn((x, y) => clickableDictionary.findIndex((fn) => fn(x, y)));
666
+ }
667
+ });
668
+
669
+ // src/charts/plugins/index.ts
670
+ var increaseScaleLimits = (scale, handleMinScaleZero = true) => {
671
+ const range = scale.max - scale.min;
672
+ const safeRange = range === 0 ? Math.abs(scale.max) || 1 : range;
673
+ const margin = 0.1;
674
+ const padding2 = safeRange * margin;
675
+ scale.min -= handleMinScaleZero && scale.min === 0 ? 0 : padding2;
676
+ scale.max += padding2;
677
+ };
678
+
679
+ // src/charts/indicator-breakdown.ts
680
+ var logsTotalValue = (logs, includeNegativeValues = true) => utils.sum((includeNegativeValues ? logs : logs.filter((curr) => curr.value >= 0)).map((v) => v.value));
681
+ var valueRatio = (value, total) => total === 0 ? 0 : utils.toPrecision(value * 100 / Math.abs(total), 2);
682
+ var chartLabel = (value, total) => {
683
+ const ratio = total === 0 ? 0 : value * 100 / Math.abs(total);
684
+ return value === 0 ? "0" : `${value}, ${utils.toPrecision(ratio, 2)}%`;
685
+ };
686
+ var chartBreakdownLabel = (logs, total, maxValues) => logs.slice(maxValues).map(({ blankNodeTermId, value }) => `${blankNodeTermId}: ${chartLabel(value, total)}`).join("</br>");
687
+ var logToCsv = (logs, impact) => [
688
+ toLine(["Impact", "Impact Unit", "Method", "Emission", "Value", "Inputs", "Functional Unit"]),
689
+ ...[...logs].sort((a, b) => a.impactTermId.localeCompare(b.impactTermId)).map(
690
+ ({ impactTermId, impactTermUnits, modelId, blankNodeTermId, value, inputs }) => {
691
+ var _a, _b;
692
+ return toLine([
693
+ impactTermId,
694
+ impactTermUnits,
695
+ modelId,
696
+ blankNodeTermId,
697
+ value,
698
+ inputs.join(";"),
699
+ (_b = (_a = impact.product) == null ? void 0 : _a.term) == null ? void 0 : _b.units
700
+ ]);
701
+ }
702
+ )
703
+ ].join("\n");
704
+ var processContributions = (record, nodes, impacts) => Object.entries(record || {}).flatMap(([index, impactContr]) => {
705
+ const indicator = nodes[+index];
706
+ return indicator ? Object.entries(impactContr).flatMap(([impactTermId, modelContr]) => {
707
+ const impact = impacts == null ? void 0 : impacts.find((v) => v.term["@id"] === impactTermId);
708
+ return impact && Object.entries(modelContr).map(
709
+ ([modelId, value]) => {
710
+ var _a;
711
+ return {
712
+ modelId,
713
+ impactTermId,
714
+ impactTermUnits: (_a = impact.term) == null ? void 0 : _a.units,
715
+ blankNodeTermId: indicator.term["@id"],
716
+ value: utils.toPrecision(value, 3),
717
+ inputs: (indicator.inputs || []).map((v) => v["@id"])
718
+ };
719
+ }
720
+ );
721
+ }) : null;
722
+ });
723
+ var parseContributions = (impactAssessment, contributions) => [
724
+ ...processContributions(
725
+ contributions == null ? void 0 : contributions.emissionsResourceUse,
726
+ impactAssessment.emissionsResourceUse || [],
727
+ (impactAssessment == null ? void 0 : impactAssessment.impacts) || []
728
+ ),
729
+ ...processContributions(contributions == null ? void 0 : contributions.impacts, impactAssessment.impacts || [], (impactAssessment == null ? void 0 : impactAssessment.endpoints) || [])
730
+ ].filter(Boolean);
731
+ var groupLogsById = (logs) => Object.values(
732
+ logs.reduce(
733
+ (prev, log) => {
734
+ const key = log.blankNodeTermId;
735
+ prev[key] = prev[key] || { ...log, value: 0 };
736
+ prev[key].value += log.value;
737
+ prev[key].inputs = utils.unique([...prev[key].inputs, ...log.inputs || []]);
738
+ return prev;
739
+ },
740
+ {}
741
+ )
742
+ );
743
+ var indicatorLogs = (logs, termId, methodId) => logs.filter(({ impactTermId, modelId }) => impactTermId === termId && (!methodId || modelId === methodId));
744
+ var nonZeroContributors = (logs) => groupLogsById(logs).filter((log) => log.value !== 0);
745
+ var namedContributors = (logs, nameOf) => logs.map((value, index) => ({
746
+ ...value,
747
+ name: nameOf(value.blankNodeTermId) || value.blankNodeTermId,
748
+ color: listColor(value, index)
749
+ }));
750
+ var bar = ({ name, value, color }) => ({
751
+ label: name,
752
+ count: value,
753
+ backgroundColor: color
754
+ });
755
+ var breakdownBars = (values, category) => {
756
+ const groups = values.reduce(
757
+ (prev, curr) => {
758
+ const key = category ? emissionCategoryValue(curr.blankNodeTermId, category) : void 0;
759
+ return { ...prev, [key]: [...prev[key] || [], curr] };
760
+ },
761
+ {}
762
+ );
763
+ const bars = Object.keys(groups).length === 1 ? values.map(bar) : Object.entries(groups).map(([key, items]) => ({
764
+ label: categoryLabel(key),
765
+ count: logsTotalValue(items, true),
766
+ backgroundColor: items[0].color,
767
+ includedItems: items.map(bar)
768
+ }));
769
+ return bars.sort((a, b) => Math.abs(b.count) - Math.abs(a.count));
770
+ };
771
+ var breakdownBarText = (displayValue, total, maximumValues) => displayValue ? { textFn: ({ label, data: data2 }, index) => index === maximumValues ? `${label} at ${data2}` : `${data2}` } : { textFn: ({ data: data2 }) => `${valueRatio(data2, total)}%` };
772
+ var breakdownTooltip = (item, includedItems, logs, total, maximumValues) => !utils.isUndefined(item == null ? void 0 : item.count) ? (includedItems == null ? void 0 : includedItems.length) ? chartBreakdownLabel(logs, total, maximumValues) : `${item.label}: ${chartLabel(item.count, total)}` : "";
773
+ var breakdownScale = (values) => {
774
+ const numbers = values.map((v) => v.value);
775
+ return {
776
+ options: {
777
+ scales: {
778
+ x: {
779
+ afterDataLimits: (scale) => increaseScaleLimits(scale),
780
+ min: numbers.some((v) => v < 0) ? Math.min(...numbers) : 0,
781
+ max: Math.max(...numbers)
782
+ }
783
+ }
784
+ }
785
+ };
786
+ };
787
+
788
+ exports.ColorPalette = ColorPalette;
789
+ exports.afterBarDrawPlugin = afterBarDrawPlugin;
790
+ exports.axisHoverPlugin = axisHoverPlugin;
791
+ exports.axisLabelCursor = axisLabelCursor;
792
+ exports.backgroundHoverPlugin = backgroundHoverPlugin;
793
+ exports.barDataset = barDataset;
794
+ exports.breakdownBarText = breakdownBarText;
795
+ exports.breakdownBars = breakdownBars;
796
+ exports.breakdownScale = breakdownScale;
797
+ exports.breakdownTooltip = breakdownTooltip;
798
+ exports.categoryLabel = categoryLabel;
799
+ exports.categoryValues = categoryValues;
800
+ exports.chartBreakdownLabel = chartBreakdownLabel;
801
+ exports.chartLabel = chartLabel;
802
+ exports.chartLabelMaxLength = chartLabelMaxLength;
803
+ exports.colorToRgba = colorToRgba;
804
+ exports.defaultBarDatasets = defaultBarDatasets;
805
+ exports.defaultBarSettings = defaultBarSettings;
806
+ exports.defaultHorizontalBarConfig = defaultHorizontalBarConfig;
807
+ exports.defaultHorizontalTooltip = defaultHorizontalTooltip;
808
+ exports.defaultTicksFont = defaultTicksFont;
809
+ exports.emissionCategories = emissionCategories;
810
+ exports.emissionCategoriesOf = emissionCategoriesOf;
811
+ exports.emissionCategoryValue = emissionCategoryValue;
812
+ exports.escape = escape;
813
+ exports.getColor = getColor;
814
+ exports.groupDataByCategory = groupDataByCategory;
815
+ exports.groupLogsById = groupLogsById;
816
+ exports.hasNegativeContributions = hasNegativeContributions;
817
+ exports.hexToRgba = hexToRgba;
818
+ exports.horizontalBarHeight = horizontalBarHeight;
819
+ exports.increaseScaleLimits = increaseScaleLimits;
820
+ exports.indicatorLogs = indicatorLogs;
821
+ exports.listColor = listColor;
822
+ exports.listColorContinuous = listColorContinuous;
823
+ exports.listColorWithAlpha = listColorWithAlpha;
824
+ exports.logToCsv = logToCsv;
825
+ exports.logsTotalValue = logsTotalValue;
826
+ exports.lollipopChartPlugin = lollipopChartPlugin;
827
+ exports.namedContributors = namedContributors;
828
+ exports.nonZeroContributors = nonZeroContributors;
829
+ exports.parseColor = parseColor;
830
+ exports.parseContributions = parseContributions;
831
+ exports.sumValues = sumValues;
832
+ exports.toLine = toLine;
833
+ exports.valueRatio = valueRatio;
834
+ exports.withOthers = withOthers;