@hestia-earth/ui-components 0.43.23 → 0.43.25
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.
|
@@ -10,10 +10,9 @@ import { ReplaySubject, of, mergeMap, shareReplay, delay, map, catchError, disti
|
|
|
10
10
|
import { HttpClient } from '@angular/common/http';
|
|
11
11
|
import get from 'lodash.get';
|
|
12
12
|
import { SCHEMA_VERSION, SchemaType, NodeType, TermTermType, productTermTermType, nestedSearchableKeys, SiteSiteType, EmissionMethodTier, isExpandable, isTypeNode, sortKeysByType, uniquenessFields, BlankNodesKey, impactAssessmentTermTermType, measurementTermTermType, emissionTermTermType, inputTermTermType, CycleFunctionalUnit, NonBlankNodesKey, jsonldPath, isTypeValid, typeToSchemaType, infrastructureTermTermType, managementTermTermType } from '@hestia-earth/schema';
|
|
13
|
-
import { axisLabelCursor,
|
|
14
|
-
export { ColorPalette, afterBarDrawPlugin, axisHoverPlugin, axisLabelCursor, backgroundHoverPlugin, chartLabelMaxLength, colorToRgba, defaultTicksFont, getColor, groupDataByCategory, hexToRgba, increaseScaleLimits, listColor, listColorContinuous, listColorWithAlpha, lollipopChartPlugin, parseColor, sumValues } from '@hestia-earth/ui-components-charts';
|
|
15
|
-
import {
|
|
16
|
-
import { isEmpty, unique, monthsBefore, toPrecision, isNumber, isUndefined, toComma, duration, keyToLabel, sum, min, max, mean, median, ellipsis as ellipsis$1, getPercentileValue, isEqual as isEqual$2, diffInDays } from '@hestia-earth/utils';
|
|
13
|
+
import { formatPrecision, axisLabelCursor, defaultChartSettings, exportAsSVG, downloadFile, hasNegativeContributions, withOthers, barDataset, defaultBarDatasets, defaultBarSettings, categoryLabel, emissionCategories, categoryValues, emissionCategoriesOf, emissionCategoryValue, contributionItemColor, contributionChartHeight, groupDataByCategory, contributionChartConfig, hasNegativeValues, contributionData, defaultContributionSettings, contributionCsv, dataAtIndex, datasetCenter, distributionChart, defaultLineSettings, defaultHorizontalTooltip, horizontalBarHeight, lollipopChartPlugin, backgroundHoverPlugin, afterBarDrawPlugin, axisHoverPlugin, defaultHorizontalBarConfig, chartLabelMaxLength, listColor, getColor, nodeBorderColours, nodeColours as nodeColours$1, hierarchyLegend, downloadSvg, downloadPng, hierarchyCsv, drawHierarchyChart, hierarchyTermTypes, hierarchyContributions, missingTermIds, hierarchyWarnings, hierarchyModels, hierarchyData, defaultIndicatorsModelId, parseContributions, indicatorLogs, nonZeroContributors, logsTotalValue, namedContributors, breakdownBars, breakdownTooltip, breakdownBarText, breakdownScale, logToCsv as logToCsv$1 } from '@hestia-earth/ui-components-charts';
|
|
14
|
+
export { ColorPalette, afterBarDrawPlugin, axisHoverPlugin, axisLabelCursor, backgroundHoverPlugin, chartLabelMaxLength, colorToRgba, convertToSvg, d3ellipse, d3wrap, defaultTicksFont, downloadFile, downloadPng, downloadSvg, exportAsSVG, getColor, groupDataByCategory, hexToRgba, increaseScaleLimits, listColor, listColorContinuous, listColorWithAlpha, lollipopChartPlugin, parseColor, registerChart, sumValues } from '@hestia-earth/ui-components-charts';
|
|
15
|
+
import { isEmpty, unique, monthsBefore, toPrecision, isNumber, isUndefined, duration, keyToLabel, sum, min, max, mean, median, isEqual as isEqual$2, diffInDays } from '@hestia-earth/utils';
|
|
17
16
|
import { json2csv } from 'json-2-csv';
|
|
18
17
|
import { propertyValue as propertyValue$1, emptyValue } from '@hestia-earth/utils/term';
|
|
19
18
|
import defaultModelId from '@hestia-earth/glossary/resources/defaultModelId.json';
|
|
@@ -39,14 +38,11 @@ import { GoogleMap, MapMarker, MapPolygon } from '@angular/google-maps';
|
|
|
39
38
|
import { Meta, DomSanitizer } from '@angular/platform-browser';
|
|
40
39
|
import removeMd from 'remove-markdown';
|
|
41
40
|
import orderBy from 'lodash.orderby';
|
|
42
|
-
import { Chart, BarController, LineController, CategoryScale, LinearScale, PointElement, BarElement, LineElement, Title, Tooltip, Legend, TimeScale } from 'chart.js';
|
|
43
|
-
import C2S from 'canvas-to-svg';
|
|
44
|
-
import 'chartjs-adapter-date-fns';
|
|
45
|
-
import annotationPlugin from 'chartjs-plugin-annotation';
|
|
46
41
|
import merge$1 from 'lodash.merge';
|
|
42
|
+
import { Chart } from 'chart.js';
|
|
47
43
|
import set from 'lodash.set';
|
|
48
44
|
import uniqBy from 'lodash.uniqby';
|
|
49
|
-
import { headersFromCsv, toCsv
|
|
45
|
+
import { headersFromCsv, toCsv, toJson, toCsvPivot, ErrorKeys } from '@hestia-earth/schema-convert';
|
|
50
46
|
import { isCSVIncluded, isDefaultCSVSelected } from '@hestia-earth/json-schema/schema-utils';
|
|
51
47
|
import { recommendedProperties, loadSchemas } from '@hestia-earth/json-schema';
|
|
52
48
|
import { ScrollStrategyOptions, CdkOverlayOrigin, CdkConnectedOverlay } from '@angular/cdk/overlay';
|
|
@@ -63,10 +59,6 @@ import { createErrorFormatter, htmlRenderer, isMigrationError, isMissingProperty
|
|
|
63
59
|
export { errorHasError, errorHasWarning, filterError, guidePageId, isMigrationError, isMissingOneOfError, isMissingPropertyError, missingNodeErrors } from '@hestia-earth/ui-components/file-errors';
|
|
64
60
|
import { format, subDays, parseISO } from 'date-fns';
|
|
65
61
|
import { enGB } from 'date-fns/locale';
|
|
66
|
-
import { linkHorizontal } from 'd3-shape';
|
|
67
|
-
import 'd3-transition';
|
|
68
|
-
import { hierarchy, tree } from 'd3-hierarchy';
|
|
69
|
-
import { easeElasticIn, easeElasticOut } from 'd3-ease';
|
|
70
62
|
|
|
71
63
|
class HeAuthService {
|
|
72
64
|
set token(token) {
|
|
@@ -263,20 +255,6 @@ const toSnakeCase = (value) => (value || '')
|
|
|
263
255
|
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
264
256
|
.replace(/([A-Z])([A-Z][a-z])/g, '$1_$2')
|
|
265
257
|
.toLowerCase();
|
|
266
|
-
const downloadFile = (url, fileName) => {
|
|
267
|
-
if (!url) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
const a = document.createElement('a');
|
|
271
|
-
document.body.appendChild(a);
|
|
272
|
-
a.setAttribute('style', 'display: none');
|
|
273
|
-
a.href = url;
|
|
274
|
-
a.download = fileName;
|
|
275
|
-
a.click();
|
|
276
|
-
a.remove();
|
|
277
|
-
// Clean up the object URL after a short delay
|
|
278
|
-
setTimeout(() => URL.revokeObjectURL(url), 100);
|
|
279
|
-
};
|
|
280
258
|
const dataVersionHeaderKey = 'X-DATA-VERSION';
|
|
281
259
|
const dataVersionHeader = (dataVersion) => (dataVersion ? { [dataVersionHeaderKey]: dataVersion } : {});
|
|
282
260
|
|
|
@@ -560,51 +538,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
560
538
|
}]
|
|
561
539
|
}] });
|
|
562
540
|
|
|
563
|
-
const d3ellipse = (selection, maxWidth, ellipsisStr = '...') => {
|
|
564
|
-
selection.each(function () {
|
|
565
|
-
const t = select(this);
|
|
566
|
-
if (t.node().getComputedTextLength() > maxWidth) {
|
|
567
|
-
const chars = t.text().split('');
|
|
568
|
-
const line = [];
|
|
569
|
-
do {
|
|
570
|
-
line.push(chars.shift());
|
|
571
|
-
t.text(line.join('') + ellipsisStr);
|
|
572
|
-
} while (t.node().getComputedTextLength() <= maxWidth);
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
};
|
|
576
|
-
const shouldBreakLine = (overflows, isMultiWordLine, isOwnLineWord, isUnderLineLimit) => ((overflows && isMultiWordLine) || isOwnLineWord) && isUnderLineLimit;
|
|
577
|
-
const d3wrap = (t, { maxWidth, maxLines, words, nHeight, lHeight, vcentre = false, ownLineWord = '' }) => {
|
|
578
|
-
words = words.reverse();
|
|
579
|
-
const y = t.attr('y');
|
|
580
|
-
const x = t.attr('x');
|
|
581
|
-
let lineNumber = 1;
|
|
582
|
-
let word;
|
|
583
|
-
let line = [];
|
|
584
|
-
let tspan = t.text(null).append('tspan').attr('x', x).attr('y', y);
|
|
585
|
-
while ((word = words.pop()) && lineNumber <= maxLines) {
|
|
586
|
-
line.push(word);
|
|
587
|
-
tspan.text(line.join(' '));
|
|
588
|
-
// TODO: hyphenate words that are too long
|
|
589
|
-
if (shouldBreakLine(tspan.node().getComputedTextLength() > maxWidth, line.length > 1, word === ownLineWord, lineNumber < maxLines)) {
|
|
590
|
-
line.pop();
|
|
591
|
-
tspan.text(line.join(' '));
|
|
592
|
-
line = [word];
|
|
593
|
-
tspan = t
|
|
594
|
-
.append('tspan')
|
|
595
|
-
.attr('x', x)
|
|
596
|
-
.attr('y', y)
|
|
597
|
-
.attr('dy', lineNumber++ * lHeight + 'px')
|
|
598
|
-
.text(word);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
d3ellipse(tspan, maxWidth, '... ' + ownLineWord);
|
|
602
|
-
if (vcentre) {
|
|
603
|
-
const yOffset = nHeight / 2 - (t.selectAll('tspan').size() - 1) * (lHeight / 2);
|
|
604
|
-
t.selectAll('tspan').attr('y', yOffset);
|
|
605
|
-
}
|
|
606
|
-
};
|
|
607
|
-
|
|
608
541
|
const SUCCESS_CRITERION_MAX_DELTA_PERCENT = 5;
|
|
609
542
|
const WARNING_CRITERION_MAX_DELTA_PERCENT = 20;
|
|
610
543
|
var DeltaColour;
|
|
@@ -666,7 +599,7 @@ const parseMessage = (message = '') => message.split(',').reduce((prev, parts) =
|
|
|
666
599
|
};
|
|
667
600
|
}, {});
|
|
668
601
|
const formatLine = ({ data: { message } }) => parseMessage(message);
|
|
669
|
-
const logToCsv
|
|
602
|
+
const logToCsv = (lines) => json2csv((lines || []).map(formatLine).filter(data => !isEmpty(data)), {
|
|
670
603
|
emptyFieldValue: ''
|
|
671
604
|
});
|
|
672
605
|
|
|
@@ -1814,10 +1747,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
1814
1747
|
args: [{ selector: 'he-blank-node-state-notice', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (show()) {\n <div class=\"is-flex is-gap-8 is-align-items-center is-size-7 is-italic\">\n <span class=\"notice-data-unchaged\">\u2020 Data uploaded by the user</span>\n <span class=\"divider\"></span>\n <span class=\"notice-data-added\">* Data added by HESTIA</span>\n <span class=\"divider\"></span>\n <span class=\"notice-data-updated\">** Data updated by HESTIA</span>\n @if (showDeleted()) {\n <span class=\"divider\"></span>\n <span class=\"notice-data-deleted\">**** Data deleted by HESTIA</span>\n }\n </div>\n}\n", styles: [":host{display:block}@media screen and (max-width:767px){:host div{flex-direction:column;align-items:flex-start!important}:host div .divider{display:none}}.divider{width:1px;height:20px;background:#4a4a4a}\n"] }]
|
|
1815
1748
|
}], propDecorators: { dataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataState", required: false }] }], showDeleted: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDeleted", required: false }] }] } });
|
|
1816
1749
|
|
|
1817
|
-
const
|
|
1818
|
-
const transform = (value, precision = 3, enableComma = true) => typeof value !== 'boolean' && !isUndefined(value) && isNumber(value)
|
|
1819
|
-
? `${(enableComma ? toComma : v => v)(parsePrecision(value, precision))}`.replace(/\.00$/, '')
|
|
1820
|
-
: value;
|
|
1750
|
+
const transform = formatPrecision;
|
|
1821
1751
|
class PrecisionPipe {
|
|
1822
1752
|
transform(value, precision = 3, enableComma = true) {
|
|
1823
1753
|
return transform(value, precision, enableComma);
|
|
@@ -5624,163 +5554,6 @@ const axisLabelSettings = (tooltip, tooltipFn, clickFn) => {
|
|
|
5624
5554
|
};
|
|
5625
5555
|
};
|
|
5626
5556
|
|
|
5627
|
-
const defaultChartWidth = 800;
|
|
5628
|
-
const defaultChartHeight = 600;
|
|
5629
|
-
const extractEssentialChartConfig = (config) => ({
|
|
5630
|
-
type: config.type || 'bar',
|
|
5631
|
-
data: config.data,
|
|
5632
|
-
options: {
|
|
5633
|
-
indexAxis: config.options?.indexAxis,
|
|
5634
|
-
responsive: false,
|
|
5635
|
-
maintainAspectRatio: false,
|
|
5636
|
-
devicePixelRatio: 1,
|
|
5637
|
-
animation: false,
|
|
5638
|
-
animations: {
|
|
5639
|
-
colors: false,
|
|
5640
|
-
x: false,
|
|
5641
|
-
y: false
|
|
5642
|
-
},
|
|
5643
|
-
transitions: {
|
|
5644
|
-
active: {
|
|
5645
|
-
animation: {
|
|
5646
|
-
duration: 0
|
|
5647
|
-
}
|
|
5648
|
-
}
|
|
5649
|
-
},
|
|
5650
|
-
plugins: {
|
|
5651
|
-
legend: { display: false },
|
|
5652
|
-
title: config.options.plugins?.title || { display: false }
|
|
5653
|
-
},
|
|
5654
|
-
scales: config.options.scales
|
|
5655
|
-
}
|
|
5656
|
-
});
|
|
5657
|
-
const stretchSvg = (svgString, width, height) => {
|
|
5658
|
-
const parser = new DOMParser();
|
|
5659
|
-
const doc = parser.parseFromString(svgString, 'image/svg+xml');
|
|
5660
|
-
const svgElement = doc.documentElement;
|
|
5661
|
-
// Add 100px padding to ViewBox for overflow labels
|
|
5662
|
-
svgElement.setAttribute('viewBox', `0 0 ${width + 100} ${height}`);
|
|
5663
|
-
svgElement.setAttribute('width', '100%');
|
|
5664
|
-
svgElement.setAttribute('height', '100%');
|
|
5665
|
-
svgElement.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
5666
|
-
return svgElement.outerHTML;
|
|
5667
|
-
};
|
|
5668
|
-
const parseAfterDrawBarPlugins = (afterBarDrawConfig, units) => Array.isArray(afterBarDrawConfig)
|
|
5669
|
-
? afterBarDrawConfig.map(afterBarDrawPlugin)
|
|
5670
|
-
: [
|
|
5671
|
-
afterBarDrawPlugin({
|
|
5672
|
-
textFn: ({ data }) => [toPrecision(Array.isArray(data) ? data[0] : data), units].filter(Boolean).join(' '),
|
|
5673
|
-
emptyValueLabel: 'No data',
|
|
5674
|
-
...(afterBarDrawConfig || {})
|
|
5675
|
-
})
|
|
5676
|
-
];
|
|
5677
|
-
const convertToSvg = (config, metadata = {}) => new Promise((resolve, reject) => {
|
|
5678
|
-
const width = metadata.width || defaultChartWidth;
|
|
5679
|
-
const height = metadata.height || defaultChartHeight;
|
|
5680
|
-
// 1. Create the SVG Context
|
|
5681
|
-
const svgContext = new C2S(width, height);
|
|
5682
|
-
svgContext.setTransform = () => { };
|
|
5683
|
-
svgContext.resetTransform = () => { };
|
|
5684
|
-
svgContext.roundRect = function (x, y, w, h) {
|
|
5685
|
-
this.rect(x, y, w, h);
|
|
5686
|
-
};
|
|
5687
|
-
// 2. Mock the Canvas Element
|
|
5688
|
-
const mockCanvas = document.createElement('canvas');
|
|
5689
|
-
mockCanvas.width = width;
|
|
5690
|
-
mockCanvas.height = height;
|
|
5691
|
-
mockCanvas.style.width = `${width}px`;
|
|
5692
|
-
mockCanvas.style.height = `${height}px`;
|
|
5693
|
-
mockCanvas.getBoundingClientRect = () => ({
|
|
5694
|
-
x: 0,
|
|
5695
|
-
y: 0,
|
|
5696
|
-
bottom: height,
|
|
5697
|
-
height: height,
|
|
5698
|
-
left: 0,
|
|
5699
|
-
right: width,
|
|
5700
|
-
top: 0,
|
|
5701
|
-
width: width,
|
|
5702
|
-
toJSON: () => { }
|
|
5703
|
-
});
|
|
5704
|
-
// Intercept getContext to return our SVG generator
|
|
5705
|
-
mockCanvas.getContext = (type) => {
|
|
5706
|
-
if (type === '2d') {
|
|
5707
|
-
svgContext.canvas = mockCanvas;
|
|
5708
|
-
return svgContext;
|
|
5709
|
-
}
|
|
5710
|
-
return null;
|
|
5711
|
-
};
|
|
5712
|
-
// 3. Prepare Config
|
|
5713
|
-
const chartConfig = extractEssentialChartConfig(config);
|
|
5714
|
-
// eslint-disable-next-line prefer-const
|
|
5715
|
-
let chart;
|
|
5716
|
-
chartConfig.options.animation = {
|
|
5717
|
-
onComplete: () => {
|
|
5718
|
-
try {
|
|
5719
|
-
const svgString = svgContext.getSerializedSvg(true);
|
|
5720
|
-
const finalSvg = stretchSvg(svgString, width, height);
|
|
5721
|
-
chart.destroy();
|
|
5722
|
-
resolve(finalSvg);
|
|
5723
|
-
}
|
|
5724
|
-
catch (e) {
|
|
5725
|
-
reject(e);
|
|
5726
|
-
}
|
|
5727
|
-
}
|
|
5728
|
-
};
|
|
5729
|
-
// 4. Register Export-Specific Plugins
|
|
5730
|
-
chartConfig.plugins = [
|
|
5731
|
-
metadata.afterBarDrawConfig !== null && parseAfterDrawBarPlugins(metadata.afterBarDrawConfig, metadata.units),
|
|
5732
|
-
metadata.lollipopConfig !== null && lollipopChartPlugin(metadata.lollipopConfig)
|
|
5733
|
-
]
|
|
5734
|
-
.filter(Boolean)
|
|
5735
|
-
.flat();
|
|
5736
|
-
chart = new Chart(mockCanvas, chartConfig);
|
|
5737
|
-
});
|
|
5738
|
-
const exportAsSVG = async (config, metadata) => {
|
|
5739
|
-
try {
|
|
5740
|
-
const content = await convertToSvg(config, metadata);
|
|
5741
|
-
const blob = new Blob([content], { type: 'image/svg+xml;charset=utf-8' });
|
|
5742
|
-
const url = URL.createObjectURL(blob);
|
|
5743
|
-
downloadFile(url, metadata.fileName || 'chart-export.svg');
|
|
5744
|
-
}
|
|
5745
|
-
catch (error) {
|
|
5746
|
-
console.error('Failed to export SVG', error);
|
|
5747
|
-
}
|
|
5748
|
-
};
|
|
5749
|
-
const svgToUrl = (svgElement) => {
|
|
5750
|
-
const serializer = new XMLSerializer();
|
|
5751
|
-
let svgString = serializer.serializeToString(svgElement);
|
|
5752
|
-
if (!svgString.includes('xmlns')) {
|
|
5753
|
-
svgString = svgString.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
|
|
5754
|
-
}
|
|
5755
|
-
const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });
|
|
5756
|
-
return URL.createObjectURL(blob);
|
|
5757
|
-
};
|
|
5758
|
-
const downloadSvg = (svgElement) => downloadFile(svgToUrl(svgElement), 'chart.svg');
|
|
5759
|
-
const downloadPng = async (svgElement, { width, height } = {}) => {
|
|
5760
|
-
const canvas = document.createElement('canvas');
|
|
5761
|
-
const ctx = canvas.getContext('2d');
|
|
5762
|
-
const img = new Image();
|
|
5763
|
-
// Set canvas dimensions based on SVG size
|
|
5764
|
-
// (You might want to fetch getBBox() or use explicitly set width/height)
|
|
5765
|
-
const svgSize = svgElement.getBoundingClientRect();
|
|
5766
|
-
canvas.width = width || svgSize.width;
|
|
5767
|
-
canvas.height = height || svgSize.height;
|
|
5768
|
-
img.onload = () => {
|
|
5769
|
-
if (ctx) {
|
|
5770
|
-
// ctx.fillStyle = 'white';
|
|
5771
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
5772
|
-
ctx.drawImage(img, 0, 0);
|
|
5773
|
-
const pngUrl = canvas.toDataURL('image/png');
|
|
5774
|
-
downloadFile(pngUrl, 'chart.png');
|
|
5775
|
-
}
|
|
5776
|
-
};
|
|
5777
|
-
img.src = svgToUrl(svgElement);
|
|
5778
|
-
};
|
|
5779
|
-
|
|
5780
|
-
const registerChart = (items = []) => () => {
|
|
5781
|
-
Chart.register(BarController, LineController, CategoryScale, LinearScale, PointElement, BarElement, LineElement, Title, Tooltip, Legend, TimeScale, annotationPlugin, ...items);
|
|
5782
|
-
};
|
|
5783
|
-
|
|
5784
5557
|
class ChartConfigurationDirective {
|
|
5785
5558
|
constructor() {
|
|
5786
5559
|
this._elementRef = inject(ElementRef);
|
|
@@ -5882,17 +5655,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
5882
5655
|
args: [{ selector: 'he-chart-export-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [HESvgIconComponent, NgbTooltip], template: "<button\n class=\"{{ buttonClass() }}\"\n type=\"button\"\n [ngbTooltip]=\"chart()?.exporting() ? '' : 'Download'\"\n placement=\"bottom\"\n (click)=\"open()\">\n <he-svg-icon name=\"download\" />\n</button>\n\n<ng-template #modal>\n <!-- the chart may sit in a popover closing on outside clicks: keep it open, or the chart is gone -->\n <div class=\"modal is-active\" (mousedown)=\"$event.stopPropagation()\">\n <div class=\"modal-background\"></div>\n <div class=\"modal-card\">\n <header class=\"modal-card-head\">\n <p class=\"modal-card-title\">Download Chart</p>\n <button class=\"delete is-small\" aria-label=\"close\" type=\"button\" (click)=\"close()\"></button>\n </header>\n <section class=\"modal-card-body\">\n <p class=\"has-text-secondary is-mb-2\">Download chart as:</p>\n <div class=\"is-flex is-flex-direction-column is-gap-4\">\n @for (format of exportFormats(); track format.extension) {\n <div class=\"is-flex is-gap-4\">\n <div class=\"field is-mb-0\">\n <input\n type=\"checkbox\"\n [id]=\"format.extension\"\n [checked]=\"exportFormat() === format.extension\"\n (change)=\"exportFormat.set(format.extension)\" />\n </div>\n <he-svg-icon name=\"image\" />\n <label class=\"has-text-grey-dark is-clickable\" [for]=\"format.extension\">\n {{ format.label }} (.{{ format.extension }})\n </label>\n </div>\n }\n </div>\n </section>\n <footer class=\"modal-card-foot\">\n <button class=\"button is-primary\" [disabled]=\"!exportFormat()\" (click)=\"download()\">\n @if (chart()?.exporting()) {\n <he-svg-icon name=\"loading\" animation=\"spin\" />\n } @else {\n <he-svg-icon name=\"download\" />\n }\n <span class=\"is-pl-2\">Download</span>\n </button>\n </footer>\n </div>\n </div>\n</ng-template>\n" }]
|
|
5883
5656
|
}], propDecorators: { modal: [{ type: i0.ViewChild, args: ['modal', { isSignal: true }] }], buttonClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonClass", required: false }] }], chart: [{ type: i0.Input, args: [{ isSignal: true, alias: "chart", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], exportFormats: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportFormats", required: false }] }], chartExportFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartExportFn", required: false }] }] } });
|
|
5884
5657
|
|
|
5885
|
-
const defaultSettings$3 = Object.freeze({
|
|
5886
|
-
options: {
|
|
5887
|
-
responsive: true,
|
|
5888
|
-
maintainAspectRatio: false,
|
|
5889
|
-
plugins: {
|
|
5890
|
-
legend: {
|
|
5891
|
-
display: false
|
|
5892
|
-
}
|
|
5893
|
-
}
|
|
5894
|
-
}
|
|
5895
|
-
});
|
|
5896
5658
|
class ChartComponent {
|
|
5897
5659
|
constructor() {
|
|
5898
5660
|
this.data = input(undefined, ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
@@ -5901,7 +5663,7 @@ class ChartComponent {
|
|
|
5901
5663
|
this.exporting = signal(false, ...(ngDevMode ? [{ debugName: "exporting" }] : []));
|
|
5902
5664
|
this.chartRef = viewChild('chartRef', ...(ngDevMode ? [{ debugName: "chartRef" }] : []));
|
|
5903
5665
|
this.configuration = computed(() => ({
|
|
5904
|
-
...merge$1({},
|
|
5666
|
+
...merge$1({}, defaultChartSettings, this.config()),
|
|
5905
5667
|
data: this.data()
|
|
5906
5668
|
}), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
5907
5669
|
}
|
|
@@ -6076,81 +5838,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
6076
5838
|
}]
|
|
6077
5839
|
}] });
|
|
6078
5840
|
|
|
6079
|
-
const grey = '#4a4a4a';
|
|
6080
|
-
const barheight = 8; // 8px
|
|
6081
|
-
const hoverHeight = 10; // 10px
|
|
6082
|
-
const defaultSettings$2 = Object.freeze({
|
|
6083
|
-
type: 'bar',
|
|
6084
|
-
options: {
|
|
6085
|
-
indexAxis: 'y',
|
|
6086
|
-
responsive: true,
|
|
6087
|
-
plugins: {
|
|
6088
|
-
tooltip: {
|
|
6089
|
-
enabled: false
|
|
6090
|
-
},
|
|
6091
|
-
legend: {
|
|
6092
|
-
display: false
|
|
6093
|
-
}
|
|
6094
|
-
},
|
|
6095
|
-
scales: {
|
|
6096
|
-
x: {
|
|
6097
|
-
stacked: true,
|
|
6098
|
-
display: true,
|
|
6099
|
-
grid: {
|
|
6100
|
-
display: true,
|
|
6101
|
-
drawOnChartArea: true
|
|
6102
|
-
},
|
|
6103
|
-
ticks: {
|
|
6104
|
-
font: defaultTicksFont,
|
|
6105
|
-
callback: label => `${transform(+label)}`
|
|
6106
|
-
}
|
|
6107
|
-
},
|
|
6108
|
-
y: {
|
|
6109
|
-
stacked: true,
|
|
6110
|
-
display: true,
|
|
6111
|
-
position: 'left',
|
|
6112
|
-
grid: {
|
|
6113
|
-
display: true,
|
|
6114
|
-
drawOnChartArea: false,
|
|
6115
|
-
drawTicks: true,
|
|
6116
|
-
tickLength: 8,
|
|
6117
|
-
offset: false
|
|
6118
|
-
},
|
|
6119
|
-
ticks: {
|
|
6120
|
-
color: grey,
|
|
6121
|
-
padding: 4,
|
|
6122
|
-
crossAlign: 'center',
|
|
6123
|
-
font: {
|
|
6124
|
-
...defaultTicksFont,
|
|
6125
|
-
weight: 400
|
|
6126
|
-
},
|
|
6127
|
-
callback: function (value) {
|
|
6128
|
-
const label = this.getLabelForValue(value);
|
|
6129
|
-
return ellipsis$1(label, chartLabelMaxLength);
|
|
6130
|
-
}
|
|
6131
|
-
}
|
|
6132
|
-
}
|
|
6133
|
-
}
|
|
6134
|
-
}
|
|
6135
|
-
});
|
|
6136
|
-
const chartHeight = (minHeight, maxHeight, nbRows) => {
|
|
6137
|
-
const rowHeight = barheight + hoverHeight * 2; // Pixels per bar (includes bar + gap)
|
|
6138
|
-
const padding = 60; // Extra pixels for x-axis labels, legend, etc.
|
|
6139
|
-
const height = nbRows * rowHeight + padding;
|
|
6140
|
-
return Math.min(Math.max(height, minHeight), maxHeight);
|
|
6141
|
-
};
|
|
6142
|
-
const datasetCenter = (chart, index) => {
|
|
6143
|
-
const elements = chart.data.datasets
|
|
6144
|
-
.map((_, i) => {
|
|
6145
|
-
const meta = chart.getDatasetMeta(i);
|
|
6146
|
-
return meta.hidden ? null : meta.data[index];
|
|
6147
|
-
})
|
|
6148
|
-
.filter(e => !!e);
|
|
6149
|
-
const right = Math.max(...elements.map(e => e.x), ...elements.map(e => e.base));
|
|
6150
|
-
const left = Math.min(...elements.map(e => e.x), ...elements.map(e => e.base));
|
|
6151
|
-
return (right + left) / 2;
|
|
6152
|
-
};
|
|
6153
|
-
const itemColor = (item) => item.backgroundColor || item.borderColor || item.color;
|
|
6154
5841
|
const tooltipFn = ({ label, selectedLabel, units, values }) => `
|
|
6155
5842
|
<div class="is-flex is-flex-direction-column is-overflow-y-auto" style="max-height:400px">
|
|
6156
5843
|
<div class="is-hidden-tablet is-p-1 has-text-centered has-text-secondary has-text-weight-bold is-size-7 with-border-secondary-accent" style="border-bottom-width:1px">${label}</div>
|
|
@@ -6162,7 +5849,7 @@ const tooltipFn = ({ label, selectedLabel, units, values }) => `
|
|
|
6162
5849
|
.map(value => `
|
|
6163
5850
|
<div class="is-flex is-flex-direction-column is-align-self-stretch w-100">
|
|
6164
5851
|
<div class="is-flex is-align-items-center is-gap-4 is-p-1 has-text-grey-dark is-size-7" style="height:21px">
|
|
6165
|
-
<div class="is-flex-shrink-0 is-radius-3" style="width:12px;height:12px;background:${
|
|
5852
|
+
<div class="is-flex-shrink-0 is-radius-3" style="width:12px;height:12px;background:${contributionItemColor(value)}"></div>
|
|
6166
5853
|
<div class="is-flex-grow-1">
|
|
6167
5854
|
<span class="has-text-ellipsis is-ellipsis-1 ${value.label === selectedLabel ? 'has-text-weight-bold' : ''}">${value.label}</span>
|
|
6168
5855
|
</div>
|
|
@@ -6170,7 +5857,7 @@ const tooltipFn = ({ label, selectedLabel, units, values }) => `
|
|
|
6170
5857
|
${((value.label === selectedLabel ? value.includedItems : []) || [])
|
|
6171
5858
|
.map(included => `
|
|
6172
5859
|
<div class="is-flex is-align-items-center is-gap-4 is-p-1 is-pl-4 has-text-grey-dark is-size-7" style="height:21px">
|
|
6173
|
-
<div class="is-flex-shrink-0 is-radius-3" style="width:8px;height:8px;background:${
|
|
5860
|
+
<div class="is-flex-shrink-0 is-radius-3" style="width:8px;height:8px;background:${contributionItemColor(included)}"></div>
|
|
6174
5861
|
<div class="is-flex-grow-1">
|
|
6175
5862
|
<span class="has-text-ellipsis is-ellipsis-1">${included.label}</span>
|
|
6176
5863
|
</div>
|
|
@@ -6208,23 +5895,6 @@ const tooltipFn = ({ label, selectedLabel, units, values }) => `
|
|
|
6208
5895
|
</div>
|
|
6209
5896
|
</div>
|
|
6210
5897
|
`.trim();
|
|
6211
|
-
const dataAtIndex = (data, index, includeItems = true) => data
|
|
6212
|
-
.map(item => ({
|
|
6213
|
-
...item,
|
|
6214
|
-
value: item.values[index],
|
|
6215
|
-
includedItems: includeItems
|
|
6216
|
-
? item.includedItems?.map(included => ({
|
|
6217
|
-
...included,
|
|
6218
|
-
value: included.values[index]
|
|
6219
|
-
}))
|
|
6220
|
-
: []
|
|
6221
|
-
}))
|
|
6222
|
-
.filter(v => !isUndefined(v.value))
|
|
6223
|
-
.sort((a, b) => Math.abs(b.value) - Math.abs(a.value));
|
|
6224
|
-
const logToCsv = (labels, data) => [
|
|
6225
|
-
toLine(['@id', ...data.map(v => v.label)]),
|
|
6226
|
-
labels.map((label, index) => toLine([label, ...dataAtIndex(data, index, false).map(v => v.value)])).join('\n')
|
|
6227
|
-
].join('\n');
|
|
6228
5898
|
class ContributionChartComponent {
|
|
6229
5899
|
constructor() {
|
|
6230
5900
|
this.emissionCategoryService = inject(CyclesEmissionsCategoryService);
|
|
@@ -6276,53 +5946,18 @@ class ContributionChartComponent {
|
|
|
6276
5946
|
this.axisEnabled = computed(() => !!this.axisTooltipFn() || !!this.axisLabelClickFn(), ...(ngDevMode ? [{ debugName: "axisEnabled" }] : []));
|
|
6277
5947
|
this.axisTooltipContent = computed(() => (data) => this.axisTooltipFn()?.(data.label, data.index) ?? '', ...(ngDevMode ? [{ debugName: "axisTooltipContent" }] : []));
|
|
6278
5948
|
this.exporting = computed(() => this.chart()?.exporting(), ...(ngDevMode ? [{ debugName: "exporting" }] : []));
|
|
6279
|
-
this.chartHeight = computed(() => this.height() ||
|
|
6280
|
-
this.hasNegativeContributions = computed(() => this.displayData()?.some(value => value.values.some(v => v < 0)), ...(ngDevMode ? [{ debugName: "hasNegativeContributions" }] : []));
|
|
5949
|
+
this.chartHeight = computed(() => this.height() || contributionChartHeight(this.minHeight(), this.maxHeight(), this.labels()?.length ?? 0), ...(ngDevMode ? [{ debugName: "chartHeight" }] : []));
|
|
6281
5950
|
this.displayData = computed(() => groupDataByCategory(this.data(), this.labels(), this.category(), this.emissionCategoryService), ...(ngDevMode ? [{ debugName: "displayData" }] : []));
|
|
6282
|
-
this.defaultConfig = computed(() => ({
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
display: !this.responsiveService.isMobile(),
|
|
6289
|
-
afterDataLimits: scale => increaseScaleLimits(scale),
|
|
6290
|
-
title: {
|
|
6291
|
-
display: !!this.label(),
|
|
6292
|
-
text: this.label()
|
|
6293
|
-
},
|
|
6294
|
-
grid: {
|
|
6295
|
-
lineWidth: context => (context.tick.value === 0 && this.hasNegativeContributions() ? 1 : 0)
|
|
6296
|
-
}
|
|
6297
|
-
},
|
|
6298
|
-
y: {
|
|
6299
|
-
display: !this.responsiveService.isMobile()
|
|
6300
|
-
}
|
|
6301
|
-
}
|
|
6302
|
-
},
|
|
6303
|
-
plugins: [
|
|
6304
|
-
backgroundHoverPlugin({ threshold: hoverHeight }),
|
|
6305
|
-
afterBarDrawPlugin({
|
|
6306
|
-
placement: 'right',
|
|
6307
|
-
textFn: ({ data }) => (isUndefined(data) ? 'No data' : '')
|
|
6308
|
-
}),
|
|
6309
|
-
...(this.axisEnabled() ? [axisHoverPlugin] : [])
|
|
6310
|
-
]
|
|
5951
|
+
this.defaultConfig = computed(() => contributionChartConfig({
|
|
5952
|
+
onClick: (event, activeElements, chart) => this.onItemClick(event, activeElements, chart),
|
|
5953
|
+
axis: this.axisEnabled() ? this.axisSettings : undefined,
|
|
5954
|
+
label: this.label(),
|
|
5955
|
+
hasNegativeValues: hasNegativeValues(this.displayData()),
|
|
5956
|
+
mobile: this.responsiveService.isMobile()
|
|
6311
5957
|
}), ...(ngDevMode ? [{ debugName: "defaultConfig" }] : []));
|
|
6312
|
-
this.dataConfig = computed(() => ({
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
data: item.values,
|
|
6316
|
-
backgroundColor: item.backgroundColor || item.color,
|
|
6317
|
-
borderColor: item.borderColor || item.color,
|
|
6318
|
-
barThickness: barheight,
|
|
6319
|
-
maxBarThickness: barheight,
|
|
6320
|
-
includedItems: item.includedItems
|
|
6321
|
-
})),
|
|
6322
|
-
labels: this.labels()
|
|
6323
|
-
}), ...(ngDevMode ? [{ debugName: "dataConfig" }] : []));
|
|
6324
|
-
this.configuration = computed(() => merge$1({}, defaultSettings$2, this.defaultConfig(), this.config()), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
6325
|
-
this.csvContent = computed(() => `data:text/html;charset=utf-8,${encodeURIComponent(logToCsv(this.labels(), this.displayData()))}`, ...(ngDevMode ? [{ debugName: "csvContent" }] : []));
|
|
5958
|
+
this.dataConfig = computed(() => contributionData(this.displayData(), this.labels()), ...(ngDevMode ? [{ debugName: "dataConfig" }] : []));
|
|
5959
|
+
this.configuration = computed(() => merge$1({}, defaultContributionSettings, this.defaultConfig(), this.config()), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
5960
|
+
this.csvContent = computed(() => `data:text/html;charset=utf-8,${encodeURIComponent(contributionCsv(this.labels(), this.displayData()))}`, ...(ngDevMode ? [{ debugName: "csvContent" }] : []));
|
|
6326
5961
|
}
|
|
6327
5962
|
onItemClick(event, activeElements, chart) {
|
|
6328
5963
|
const index = activeElements?.[0]?.index;
|
|
@@ -6359,182 +5994,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
6359
5994
|
args: [{ selector: 'he-contribution-chart', exportAs: 'contributionChart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ChartComponent, ChartTooltipComponent, BarChartLegendComponent], template: "<div class=\"is-relative\" [class.chart-area-border]=\"showLegend()\">\n <he-chart\n [data]=\"dataConfig()\"\n [config]=\"configuration()\"\n [showExportButton]=\"showExportButton()\"\n [style.height.px]=\"chartHeight()\">\n <he-chart-tooltip [tooltipFn]=\"tooltipFn\" />\n @if (axisEnabled()) {\n <he-chart-tooltip #axisTooltip=\"chartTooltip\" [tooltipFn]=\"axisTooltipContent()\" />\n }\n </he-chart>\n\n @if (!data()?.length) {\n <div class=\"is-absolute w-100 h-100 | no-data\">\n <div class=\"is-flex is-justify-content-center is-align-items-center w-100 h-100\">\n <span class=\"is-size-7\">No contribution available.</span>\n </div>\n </div>\n }\n</div>\n\n@if (showLegend()) {\n <he-bar-chart-legend [data]=\"displayData()\" />\n}\n", styles: [":host{display:block}he-chart{height:400px}he-bar-chart-legend ::ng-deep .breakdown-legend--color{border-radius:3px}.no-data{top:0;background-color:#0000004d}\n"] }]
|
|
6360
5995
|
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], showExportButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportButton", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], axisTooltipFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisTooltipFn", required: false }] }], axisLabelClickFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisLabelClickFn", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], category: [{ type: i0.Input, args: [{ isSignal: true, alias: "category", required: false }] }], chart: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ChartComponent), { isSignal: true }] }], tooltip: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ChartTooltipComponent), { isSignal: true }] }], axisTooltip: [{ type: i0.ViewChild, args: ['axisTooltip', { isSignal: true }] }] } });
|
|
6361
5996
|
|
|
6362
|
-
|
|
6363
|
-
darkBlue: 'rgba(25, 57, 87, 1)',
|
|
6364
|
-
lightBlue: 'rgba(132, 160, 220, 1)',
|
|
6365
|
-
// `$warning` — marks the reported value, and is the one colour that reads against both a white
|
|
6366
|
-
// page and the dark blue popover the parametric chart is shown in
|
|
6367
|
-
orange: 'rgba(255, 136, 27, 1)'
|
|
6368
|
-
};
|
|
6369
|
-
const opaqueColor = (color, opacity = 0.5) => color.replace(', 1)', `, ${opacity})`);
|
|
6370
|
-
const defaultSettings$1 = Object.freeze({
|
|
6371
|
-
type: 'line',
|
|
6372
|
-
options: {
|
|
6373
|
-
events: [],
|
|
6374
|
-
responsive: true,
|
|
6375
|
-
plugins: {
|
|
6376
|
-
tooltip: {
|
|
6377
|
-
enabled: false
|
|
6378
|
-
},
|
|
6379
|
-
legend: {
|
|
6380
|
-
display: false,
|
|
6381
|
-
align: 'center'
|
|
6382
|
-
}
|
|
6383
|
-
},
|
|
6384
|
-
scales: {
|
|
6385
|
-
y: {
|
|
6386
|
-
display: true,
|
|
6387
|
-
position: 'left',
|
|
6388
|
-
grid: {
|
|
6389
|
-
display: true
|
|
6390
|
-
},
|
|
6391
|
-
beginAtZero: true,
|
|
6392
|
-
ticks: {
|
|
6393
|
-
crossAlign: 'center'
|
|
6394
|
-
}
|
|
6395
|
-
}
|
|
6396
|
-
}
|
|
6397
|
-
}
|
|
6398
|
-
});
|
|
6399
|
-
const joinXLabel = ' to ';
|
|
6400
|
-
const createDynamicHistogramData = (values, numberOfBins) => {
|
|
6401
|
-
if (values.length === 0) {
|
|
6402
|
-
return { labels: [], frequencies: [] };
|
|
6403
|
-
}
|
|
6404
|
-
// Find the minimum and maximum values in the dataset
|
|
6405
|
-
const min = Math.min(...values);
|
|
6406
|
-
const max = Math.max(...values);
|
|
6407
|
-
// Calculate the size of each bin
|
|
6408
|
-
const binSize = (max - min) / numberOfBins;
|
|
6409
|
-
// Create an array to hold the bin frequencies, initialized to zero
|
|
6410
|
-
const frequencies = new Array(numberOfBins).fill(0);
|
|
6411
|
-
// Create labels for the chart based on the bin ranges
|
|
6412
|
-
const labels = [];
|
|
6413
|
-
for (let i = 0; i < numberOfBins; i++) {
|
|
6414
|
-
const binStart = min + i * binSize;
|
|
6415
|
-
const binEnd = binStart + binSize;
|
|
6416
|
-
labels.push([binStart, binEnd].join(joinXLabel));
|
|
6417
|
-
// Count data points for the current bin
|
|
6418
|
-
values.forEach(point => {
|
|
6419
|
-
if (point >= binStart && point < binEnd) {
|
|
6420
|
-
frequencies[i]++;
|
|
6421
|
-
}
|
|
6422
|
-
});
|
|
6423
|
-
}
|
|
6424
|
-
// Ensure the maximum value is included in the last bin
|
|
6425
|
-
frequencies[numberOfBins - 1] += values.filter(p => p === max).length;
|
|
6426
|
-
return { labels, frequencies };
|
|
6427
|
-
};
|
|
6428
|
-
/**
|
|
6429
|
-
* Parametric mode: how far either side of the mean the curve is drawn, in standard deviations.
|
|
6430
|
-
* Four covers ~99.99% of the density, so the tails are visually flat by the time it stops.
|
|
6431
|
-
*/
|
|
6432
|
-
const curveSpread = 4;
|
|
6433
|
-
const curvePoints = 120;
|
|
6434
|
-
/** Fraction of the drawn range kept clear at each end, so a marker on the edge is not clipped. */
|
|
6435
|
-
const curvePadding = 0.05;
|
|
6436
|
-
const normalPdf = (x, mu, sd) => Math.exp(-0.5 * ((x - mu) / sd) ** 2) / (sd * Math.sqrt(2 * Math.PI));
|
|
6437
|
-
/**
|
|
6438
|
-
* The x range to draw. The reported value is included when it is known, because the whole point of
|
|
6439
|
-
* the chart is to show how far outside the interval it sits — clipping it to the curve would hide
|
|
6440
|
-
* exactly what the reader came to see.
|
|
6441
|
-
*/
|
|
6442
|
-
const curveRange = (mu, sd, value, minX) => {
|
|
6443
|
-
const values = [mu - curveSpread * sd, mu + curveSpread * sd, ...(Number.isFinite(value) ? [value] : [])];
|
|
6444
|
-
const from = Math.min(...values);
|
|
6445
|
-
const to = Math.max(...values);
|
|
6446
|
-
const padding = (to - from) * curvePadding;
|
|
6447
|
-
// a fitted normal has mass either side of the mean, but the quantity it describes may not:
|
|
6448
|
-
// drawing a physical amount below its floor invites the reader to think that is possible
|
|
6449
|
-
return { from: Number.isFinite(minX) ? Math.max(from - padding, minX) : from - padding, to: to + padding };
|
|
6450
|
-
};
|
|
6451
|
-
/** Samples the normal PDF, as `{x, y}` points for a linear x axis. */
|
|
6452
|
-
const createNormalCurve = (mu, sd, value, minX) => {
|
|
6453
|
-
const { from, to } = curveRange(mu, sd, value, minX);
|
|
6454
|
-
return Array.from({ length: curvePoints + 1 }, (_, index) => from + ((to - from) * index) / curvePoints).map(x => ({
|
|
6455
|
-
x,
|
|
6456
|
-
y: normalPdf(x, mu, sd)
|
|
6457
|
-
}));
|
|
6458
|
-
};
|
|
6459
|
-
/**
|
|
6460
|
-
* The shaded confidence interval, the mean, and the vertical marker at the reported value.
|
|
6461
|
-
*
|
|
6462
|
-
* The mean earns its line: the y axis is a probability density, whose absolute value means nothing
|
|
6463
|
-
* to a reader, so without it there is no way to tell what the peak of the curve represents.
|
|
6464
|
-
*/
|
|
6465
|
-
const distributionAnnotations = (min, max, value, mean) => ({
|
|
6466
|
-
...(Number.isFinite(min) && Number.isFinite(max)
|
|
6467
|
-
? {
|
|
6468
|
-
interval: {
|
|
6469
|
-
type: 'box',
|
|
6470
|
-
xMin: min,
|
|
6471
|
-
xMax: max,
|
|
6472
|
-
backgroundColor: opaqueColor(colors.lightBlue, 0.3),
|
|
6473
|
-
borderWidth: 0,
|
|
6474
|
-
drawTime: 'beforeDatasetsDraw'
|
|
6475
|
-
}
|
|
6476
|
-
}
|
|
6477
|
-
: {}),
|
|
6478
|
-
...(Number.isFinite(mean)
|
|
6479
|
-
? {
|
|
6480
|
-
mean: {
|
|
6481
|
-
type: 'line',
|
|
6482
|
-
xMin: mean,
|
|
6483
|
-
xMax: mean,
|
|
6484
|
-
borderColor: colors.lightBlue,
|
|
6485
|
-
borderWidth: 1,
|
|
6486
|
-
borderDash: [4, 4],
|
|
6487
|
-
label: {
|
|
6488
|
-
display: true,
|
|
6489
|
-
content: 'average',
|
|
6490
|
-
position: 'start',
|
|
6491
|
-
backgroundColor: colors.lightBlue,
|
|
6492
|
-
color: colors.darkBlue,
|
|
6493
|
-
font: { size: 9 }
|
|
6494
|
-
}
|
|
6495
|
-
}
|
|
6496
|
-
}
|
|
6497
|
-
: {}),
|
|
6498
|
-
...(Number.isFinite(value)
|
|
6499
|
-
? {
|
|
6500
|
-
value: {
|
|
6501
|
-
type: 'line',
|
|
6502
|
-
xMin: value,
|
|
6503
|
-
xMax: value,
|
|
6504
|
-
borderColor: colors.orange,
|
|
6505
|
-
borderWidth: 2,
|
|
6506
|
-
label: {
|
|
6507
|
-
display: true,
|
|
6508
|
-
content: `${transform(value, 3, true)}`,
|
|
6509
|
-
position: 'start',
|
|
6510
|
-
backgroundColor: colors.orange,
|
|
6511
|
-
color: '#fff',
|
|
6512
|
-
font: { size: 10 }
|
|
6513
|
-
}
|
|
6514
|
-
}
|
|
6515
|
-
}
|
|
6516
|
-
: {})
|
|
6517
|
-
});
|
|
6518
|
-
const createSinglePointDataset = (labels, pointValue) => {
|
|
6519
|
-
// Create an array with the same length as the labels, filled with nulls.
|
|
6520
|
-
const pointData = new Array(labels.length).fill(null);
|
|
6521
|
-
// Find the index of the bin that contains the pointValue.
|
|
6522
|
-
// We parse the bin range from the label string (e.g., '900-1000').
|
|
6523
|
-
const binIndex = labels.findIndex(label => {
|
|
6524
|
-
// Split the label to get the bin's min and max values
|
|
6525
|
-
const [minStr, maxStr] = label.split(joinXLabel);
|
|
6526
|
-
const binMin = parseFloat(minStr);
|
|
6527
|
-
const binMax = parseFloat(maxStr);
|
|
6528
|
-
return pointValue >= binMin && pointValue < binMax;
|
|
6529
|
-
});
|
|
6530
|
-
// If a valid bin is found, place the point value in the array.
|
|
6531
|
-
if (binIndex !== -1) {
|
|
6532
|
-
// We'll set the single point's Y-value to its own value here,
|
|
6533
|
-
// but you could also set it to a frequency or a custom value.
|
|
6534
|
-
pointData[binIndex] = 1;
|
|
6535
|
-
}
|
|
6536
|
-
return pointData;
|
|
6537
|
-
};
|
|
5997
|
+
/** A distribution as a histogram, or as a normal curve; drawn from `@hestia-earth/ui-components-charts`. */
|
|
6538
5998
|
class DistributionChartComponent {
|
|
6539
5999
|
constructor() {
|
|
6540
6000
|
/**
|
|
@@ -6581,159 +6041,20 @@ class DistributionChartComponent {
|
|
|
6581
6041
|
* for a physical amount. Left unset the curve is drawn symmetrically about `mu`.
|
|
6582
6042
|
*/
|
|
6583
6043
|
this.minX = input(...(ngDevMode ? [undefined, { debugName: "minX" }] : []));
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
:
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
display: true,
|
|
6599
|
-
grid: {
|
|
6600
|
-
offset: true
|
|
6601
|
-
},
|
|
6602
|
-
min: 0,
|
|
6603
|
-
ticks: {
|
|
6604
|
-
callback: function (value) {
|
|
6605
|
-
const label = this.getLabelForValue(value);
|
|
6606
|
-
return (label
|
|
6607
|
-
?.split(joinXLabel)
|
|
6608
|
-
?.map(parseFloat)
|
|
6609
|
-
?.map(v => transform(v, 3, true))
|
|
6610
|
-
// .map(v => v.toExponential())
|
|
6611
|
-
?.join('-'));
|
|
6612
|
-
}
|
|
6613
|
-
},
|
|
6614
|
-
title: {
|
|
6615
|
-
display: true,
|
|
6616
|
-
text: this.label()
|
|
6617
|
-
}
|
|
6618
|
-
},
|
|
6619
|
-
y: {
|
|
6620
|
-
ticks: {
|
|
6621
|
-
font: {
|
|
6622
|
-
family: defaultTicksFont.family,
|
|
6623
|
-
size: 10,
|
|
6624
|
-
weight: 400
|
|
6625
|
-
},
|
|
6626
|
-
color: '#4A4A4A'
|
|
6627
|
-
},
|
|
6628
|
-
title: {
|
|
6629
|
-
display: true,
|
|
6630
|
-
text: 'Frequency of values'
|
|
6631
|
-
}
|
|
6632
|
-
}
|
|
6633
|
-
}
|
|
6634
|
-
}
|
|
6635
|
-
}), ...(ngDevMode ? [{ debugName: "defaultConfig" }] : []));
|
|
6636
|
-
/**
|
|
6637
|
-
* The x axis is `linear` here, not the category axis the histogram uses: the curve is `{x, y}`
|
|
6638
|
-
* points, and the annotations position the interval and the marker at real x values.
|
|
6639
|
-
*/
|
|
6640
|
-
this.parametricConfig = computed(() => ({
|
|
6641
|
-
options: {
|
|
6642
|
-
plugins: {
|
|
6643
|
-
annotation: {
|
|
6644
|
-
annotations: distributionAnnotations(this.intervalMin(), this.intervalMax(), this.value(), this.mu())
|
|
6645
|
-
}
|
|
6646
|
-
},
|
|
6647
|
-
scales: {
|
|
6648
|
-
x: {
|
|
6649
|
-
type: 'linear',
|
|
6650
|
-
display: true,
|
|
6651
|
-
ticks: {
|
|
6652
|
-
font: {
|
|
6653
|
-
family: defaultTicksFont.family,
|
|
6654
|
-
size: 10,
|
|
6655
|
-
weight: 400
|
|
6656
|
-
},
|
|
6657
|
-
color: '#4A4A4A',
|
|
6658
|
-
callback: value => `${transform(value, 3, true)}`
|
|
6659
|
-
},
|
|
6660
|
-
title: {
|
|
6661
|
-
display: true,
|
|
6662
|
-
text: this.label()
|
|
6663
|
-
}
|
|
6664
|
-
},
|
|
6665
|
-
y: {
|
|
6666
|
-
// the density itself carries no meaning for the reader, only the shape does
|
|
6667
|
-
ticks: {
|
|
6668
|
-
display: false
|
|
6669
|
-
},
|
|
6670
|
-
grid: {
|
|
6671
|
-
display: false
|
|
6672
|
-
},
|
|
6673
|
-
title: {
|
|
6674
|
-
display: false
|
|
6675
|
-
}
|
|
6676
|
-
}
|
|
6677
|
-
}
|
|
6678
|
-
}
|
|
6679
|
-
}), ...(ngDevMode ? [{ debugName: "parametricConfig" }] : []));
|
|
6680
|
-
this.histogramData = computed(() => ({
|
|
6681
|
-
datasets: [
|
|
6682
|
-
this.distribution()
|
|
6683
|
-
? [
|
|
6684
|
-
{
|
|
6685
|
-
label: 'Distribution',
|
|
6686
|
-
data: this.groupedData().frequencies,
|
|
6687
|
-
backgroundColor: opaqueColor(colors.lightBlue),
|
|
6688
|
-
borderColor: colors.lightBlue,
|
|
6689
|
-
borderWidth: 1,
|
|
6690
|
-
type: 'bar'
|
|
6691
|
-
},
|
|
6692
|
-
{
|
|
6693
|
-
label: 'Distribution',
|
|
6694
|
-
data: this.groupedData().frequencies,
|
|
6695
|
-
backgroundColor: opaqueColor(colors.darkBlue),
|
|
6696
|
-
borderColor: colors.darkBlue,
|
|
6697
|
-
borderWidth: 1,
|
|
6698
|
-
fill: false,
|
|
6699
|
-
pointRadius: 0,
|
|
6700
|
-
type: 'line'
|
|
6701
|
-
}
|
|
6702
|
-
]
|
|
6703
|
-
: [],
|
|
6704
|
-
this.singlePoint()?.length
|
|
6705
|
-
? {
|
|
6706
|
-
label: 'Value',
|
|
6707
|
-
data: this.singlePoint(),
|
|
6708
|
-
fill: false,
|
|
6709
|
-
borderColor: colors.darkBlue,
|
|
6710
|
-
backgroundColor: colors.darkBlue,
|
|
6711
|
-
pointRadius: 4,
|
|
6712
|
-
showLine: false,
|
|
6713
|
-
type: 'line',
|
|
6714
|
-
tension: 0
|
|
6715
|
-
}
|
|
6716
|
-
: []
|
|
6717
|
-
].flat(),
|
|
6718
|
-
labels: this.groupedData().labels
|
|
6719
|
-
}), ...(ngDevMode ? [{ debugName: "histogramData" }] : []));
|
|
6720
|
-
this.parametricData = computed(() => ({
|
|
6721
|
-
datasets: [
|
|
6722
|
-
{
|
|
6723
|
-
label: 'Distribution',
|
|
6724
|
-
data: this.curve(),
|
|
6725
|
-
backgroundColor: opaqueColor(colors.lightBlue, 0.2),
|
|
6726
|
-
// light enough to read on a dark panel, dark enough to read on a white one
|
|
6727
|
-
borderColor: colors.lightBlue,
|
|
6728
|
-
borderWidth: 1,
|
|
6729
|
-
fill: true,
|
|
6730
|
-
pointRadius: 0,
|
|
6731
|
-
type: 'line'
|
|
6732
|
-
}
|
|
6733
|
-
]
|
|
6734
|
-
}), ...(ngDevMode ? [{ debugName: "parametricData" }] : []));
|
|
6735
|
-
this.dataConfig = computed(() => this.parametric() ? this.parametricData() : this.histogramData(), ...(ngDevMode ? [{ debugName: "dataConfig" }] : []));
|
|
6736
|
-
this.configuration = computed(() => merge$1({}, defaultSettings$1, this.parametric() ? this.parametricConfig() : this.defaultConfig(), this.config()), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
6044
|
+
this.chart = computed(() => distributionChart({
|
|
6045
|
+
distribution: this.distribution(),
|
|
6046
|
+
value: this.value(),
|
|
6047
|
+
label: this.label(),
|
|
6048
|
+
nbBins: this.nbBins(),
|
|
6049
|
+
maxPercentile: this.maxPercentile(),
|
|
6050
|
+
mu: this.mu(),
|
|
6051
|
+
sd: this.sd(),
|
|
6052
|
+
intervalMin: this.intervalMin(),
|
|
6053
|
+
intervalMax: this.intervalMax(),
|
|
6054
|
+
minX: this.minX()
|
|
6055
|
+
}), ...(ngDevMode ? [{ debugName: "chart" }] : []));
|
|
6056
|
+
this.dataConfig = computed(() => this.chart().data, ...(ngDevMode ? [{ debugName: "dataConfig" }] : []));
|
|
6057
|
+
this.configuration = computed(() => merge$1({}, this.chart().config, this.config()), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
6737
6058
|
}
|
|
6738
6059
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DistributionChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6739
6060
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: DistributionChartComponent, isStandalone: true, selector: "he-distribution-chart", inputs: { distribution: { classPropertyName: "distribution", publicName: "distribution", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, nbBins: { classPropertyName: "nbBins", publicName: "nbBins", isSignal: true, isRequired: false, transformFunction: null }, maxPercentile: { classPropertyName: "maxPercentile", publicName: "maxPercentile", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, mu: { classPropertyName: "mu", publicName: "mu", isSignal: true, isRequired: false, transformFunction: null }, sd: { classPropertyName: "sd", publicName: "sd", isSignal: true, isRequired: false, transformFunction: null }, intervalMin: { classPropertyName: "intervalMin", publicName: "intervalMin", isSignal: true, isRequired: false, transformFunction: null }, intervalMax: { classPropertyName: "intervalMax", publicName: "intervalMax", isSignal: true, isRequired: false, transformFunction: null }, minX: { classPropertyName: "minX", publicName: "minX", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["distributionChart"], ngImport: i0, template: "<he-chart [data]=\"dataConfig()\" [config]=\"configuration()\" />\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: ChartComponent, selector: "he-chart", inputs: ["data", "config", "showExportButton"], exportAs: ["chart"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
@@ -6743,22 +6064,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
6743
6064
|
args: [{ selector: 'he-distribution-chart', exportAs: 'distributionChart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ChartComponent], template: "<he-chart [data]=\"dataConfig()\" [config]=\"configuration()\" />\n", styles: [":host{display:block}\n"] }]
|
|
6744
6065
|
}], propDecorators: { distribution: [{ type: i0.Input, args: [{ isSignal: true, alias: "distribution", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], nbBins: [{ type: i0.Input, args: [{ isSignal: true, alias: "nbBins", required: false }] }], maxPercentile: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxPercentile", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], mu: [{ type: i0.Input, args: [{ isSignal: true, alias: "mu", required: false }] }], sd: [{ type: i0.Input, args: [{ isSignal: true, alias: "sd", required: false }] }], intervalMin: [{ type: i0.Input, args: [{ isSignal: true, alias: "intervalMin", required: false }] }], intervalMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "intervalMax", required: false }] }], minX: [{ type: i0.Input, args: [{ isSignal: true, alias: "minX", required: false }] }] } });
|
|
6745
6066
|
|
|
6746
|
-
const defaultSettings = Object.freeze({
|
|
6747
|
-
type: 'line',
|
|
6748
|
-
options: {
|
|
6749
|
-
scales: {
|
|
6750
|
-
x: {
|
|
6751
|
-
type: 'time',
|
|
6752
|
-
time: {
|
|
6753
|
-
unit: 'day'
|
|
6754
|
-
},
|
|
6755
|
-
ticks: {
|
|
6756
|
-
font: defaultTicksFont
|
|
6757
|
-
}
|
|
6758
|
-
}
|
|
6759
|
-
}
|
|
6760
|
-
}
|
|
6761
|
-
});
|
|
6762
6067
|
class LineChartComponent {
|
|
6763
6068
|
constructor() {
|
|
6764
6069
|
this.datasets = input([], ...(ngDevMode ? [{ debugName: "datasets" }] : []));
|
|
@@ -6769,7 +6074,7 @@ class LineChartComponent {
|
|
|
6769
6074
|
this.dataConfig = computed(() => ({
|
|
6770
6075
|
datasets: this.datasets()
|
|
6771
6076
|
}), ...(ngDevMode ? [{ debugName: "dataConfig" }] : []));
|
|
6772
|
-
this.configuration = computed(() => merge$1({},
|
|
6077
|
+
this.configuration = computed(() => merge$1({}, defaultLineSettings, this.config()), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
6773
6078
|
}
|
|
6774
6079
|
exportAsSvg(config = {}) {
|
|
6775
6080
|
return this.chart().exportAsSvg(config);
|
|
@@ -8425,9 +7730,9 @@ class NodeCsvExportConfirmComponent {
|
|
|
8425
7730
|
this.included = computed(() => this.includedNodes()
|
|
8426
7731
|
.filter(({ included }) => included)
|
|
8427
7732
|
.map(({ node }) => node), ...(ngDevMode ? [{ debugName: "included" }] : []));
|
|
8428
|
-
this.csvData = computed(() => toCsv
|
|
7733
|
+
this.csvData = computed(() => toCsv(this.nodes() || [], { includeExising: !this.isUpload() }), ...(ngDevMode ? [{ debugName: "csvData" }] : []));
|
|
8429
7734
|
this.csvContent = computed(() => this.headers().length
|
|
8430
|
-
? this.domSanitizer.bypassSecurityTrustResourceUrl(`data:text/html;charset=utf-8,${encodeURIComponent(toCsv
|
|
7735
|
+
? this.domSanitizer.bypassSecurityTrustResourceUrl(`data:text/html;charset=utf-8,${encodeURIComponent(toCsv(this.included(), { includeExising: !this.isUpload(), selectedHeaders: this.headers() }))}`)
|
|
8431
7736
|
: undefined, ...(ngDevMode ? [{ debugName: "csvContent" }] : []));
|
|
8432
7737
|
this.downloadFilename = computed(() => fileToExt(this.filename(), this.extension()), ...(ngDevMode ? [{ debugName: "downloadFilename" }] : []));
|
|
8433
7738
|
}
|
|
@@ -10892,7 +10197,8 @@ const isWeightedMean = (formula) => formula.bindings.some(({ column }) => column
|
|
|
10892
10197
|
const REPORTING_TABLE = 'reporting';
|
|
10893
10198
|
const isCompletenessMean = (formula) => formula.bindings.some(({ key }) => key === REPORTING_TABLE);
|
|
10894
10199
|
const isMeanThatRan = (formula, weighted) => weighted ? !isSimpleMean(formula) : !isWeightedMean(formula);
|
|
10895
|
-
// dividing a phase weight between the Cycles that share it
|
|
10200
|
+
// dividing a phase weight between the Cycles that share it: a step of how the mean is computed
|
|
10201
|
+
// rather than of the calculation, and already written in the guide, so it is never shown
|
|
10896
10202
|
const isWeightNormalisation = (formula) => formula.bindings.some(({ symbol }) => WEIGHT_SYMBOLS.includes(symbol ?? ''));
|
|
10897
10203
|
/**
|
|
10898
10204
|
* A symbol that should carry a value: the result and the inputs, but never a fixed constant or a
|
|
@@ -11151,8 +10457,7 @@ const ranForAggregation = (rules) => (formula) => {
|
|
|
11151
10457
|
applies: isSubSystemWeight,
|
|
11152
10458
|
ran: () => rules.termType === TermTermType.crop && !rules.subAggregation && !rules.worldAggregation
|
|
11153
10459
|
},
|
|
11154
|
-
{ applies: isPlantationWeight, ran: () => ranPlantationWeight(rules) }
|
|
11155
|
-
{ applies: isWeightNormalisation, ran: () => ranPlantationWeight(rules) }
|
|
10460
|
+
{ applies: isPlantationWeight, ran: () => ranPlantationWeight(rules) }
|
|
11156
10461
|
];
|
|
11157
10462
|
return stages.find(({ applies }) => applies(formula))?.ran() ?? true;
|
|
11158
10463
|
};
|
|
@@ -11162,6 +10467,7 @@ const ranForAggregation = (rules) => (formula) => {
|
|
|
11162
10467
|
*/
|
|
11163
10468
|
const appliesToNodeKey = (nodeKey) => (formula) => !nodeKey || !isEconomicValueShare(formula) || nodeKey === PRODUCTS_KEY;
|
|
11164
10469
|
const appliesToAggregation = (rules) => (formula) => !isCompletenessMean(formula) &&
|
|
10470
|
+
!isWeightNormalisation(formula) &&
|
|
11165
10471
|
ranForAggregation(rules)(formula) &&
|
|
11166
10472
|
appliesToNodeKey(rules.nodeKey)(formula) &&
|
|
11167
10473
|
isMeanThatRan(formula, isWeightedAggregation(rules));
|
|
@@ -11652,6 +10958,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
11652
10958
|
|
|
11653
10959
|
// TODO: compute from the list of unique properties
|
|
11654
10960
|
const additionalKeys = [
|
|
10961
|
+
'referencePeriod',
|
|
11655
10962
|
'key',
|
|
11656
10963
|
'methodModelDescription',
|
|
11657
10964
|
'methodDescription',
|
|
@@ -11683,6 +10990,7 @@ const defaultTableKeys = [
|
|
|
11683
10990
|
'operation',
|
|
11684
10991
|
'impactAssessment',
|
|
11685
10992
|
'value',
|
|
10993
|
+
'referencePeriod',
|
|
11686
10994
|
'dates',
|
|
11687
10995
|
'startDate',
|
|
11688
10996
|
'endDate',
|
|
@@ -12635,7 +11943,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
12635
11943
|
}]
|
|
12636
11944
|
}] });
|
|
12637
11945
|
|
|
12638
|
-
const nodeColours
|
|
11946
|
+
const nodeColours = {
|
|
12639
11947
|
[NodeType.Cycle]: '#ecf1ff',
|
|
12640
11948
|
[NodeType.Site]: '#d5f3d8',
|
|
12641
11949
|
[NodeType.ImpactAssessment]: '#e7e0cb',
|
|
@@ -12912,7 +12220,7 @@ class NodeCsvPreviewComponent {
|
|
|
12912
12220
|
? this.schemas()
|
|
12913
12221
|
? toCsvPivot(this.schemas(), this.nodes())
|
|
12914
12222
|
: ''
|
|
12915
|
-
: toCsv
|
|
12223
|
+
: toCsv(this.nodes(), this.csvOptions()), ...(ngDevMode ? [{ debugName: "_csv" }] : []));
|
|
12916
12224
|
this.csv = computed(() => this.domSanitizer.bypassSecurityTrustResourceUrl(`data:text/html;charset=utf-8,${encodeURIComponent(this._csv())}`), ...(ngDevMode ? [{ debugName: "csv" }] : []));
|
|
12917
12225
|
this.parsedCsv = computed(() => parse(this._csv()).data, ...(ngDevMode ? [{ debugName: "parsedCsv" }] : []));
|
|
12918
12226
|
this.headers = computed(() => this.parsedCsv()[0], ...(ngDevMode ? [{ debugName: "headers" }] : []));
|
|
@@ -13032,7 +12340,7 @@ class NodeLogsFileComponent {
|
|
|
13032
12340
|
?.filter(({ level }) => levels.indexOf(level) >= levels.indexOf(this.selectedLevel()))
|
|
13033
12341
|
?.filter(({ code }) => (!this.modelFilter() || code.toLocaleLowerCase().includes(`model=${this.modelFilter().toLowerCase()}`)) &&
|
|
13034
12342
|
(!this.termFilter() || code.toLocaleLowerCase().includes(`term=${this.termFilter().toLowerCase()}`))), ...(ngDevMode ? [{ debugName: "nodeLogLines" }] : []));
|
|
13035
|
-
this.csvContent = computed(() => this.domSanitizer.bypassSecurityTrustResourceUrl(`data:text/html;charset=utf-8,${encodeURIComponent(logToCsv
|
|
12343
|
+
this.csvContent = computed(() => this.domSanitizer.bypassSecurityTrustResourceUrl(`data:text/html;charset=utf-8,${encodeURIComponent(logToCsv(this.allLogs()))}`), ...(ngDevMode ? [{ debugName: "csvContent" }] : []));
|
|
13036
12344
|
this.csvFilename = computed(() => fileToExt(this.node()?.['@id'] ?? '', 'csv'), ...(ngDevMode ? [{ debugName: "csvFilename" }] : []));
|
|
13037
12345
|
}
|
|
13038
12346
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeLogsFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
@@ -13842,7 +13150,8 @@ const propertyTypeValue = {
|
|
|
13842
13150
|
? val.map(value => propertyValue(items, value)).join(ARRAY_DELIMITER)
|
|
13843
13151
|
: val;
|
|
13844
13152
|
},
|
|
13845
|
-
|
|
13153
|
+
// a GeoJSON object is shown as its JSON: the input formatter has no name to show for it
|
|
13154
|
+
json: val => (val ? safeJSONStringify(val) : ''),
|
|
13846
13155
|
string: val => (`${val}` || '').trim(),
|
|
13847
13156
|
number: val => +(`${val}` || '0'),
|
|
13848
13157
|
integer: val => +(`${val}` || '0'),
|
|
@@ -13878,7 +13187,8 @@ const propertyTypeParseValue = {
|
|
|
13878
13187
|
? val
|
|
13879
13188
|
: val.split(ARRAY_DELIMITER).map(value => parseNewValue(items, value));
|
|
13880
13189
|
},
|
|
13881
|
-
|
|
13190
|
+
// stored as the object the schema asks for; text that is not JSON is kept for the validation to report
|
|
13191
|
+
json: val => safeJSONParse(val, val) || '',
|
|
13882
13192
|
string: val => (`${val}` || '').trim(),
|
|
13883
13193
|
number: val => +(`${val}` || '0'),
|
|
13884
13194
|
integer: val => +(`${val}` || '0'),
|
|
@@ -15181,7 +14491,7 @@ const valueToNodes = (value, type) => Array.isArray(value)
|
|
|
15181
14491
|
const nodesFromError = (error, type) => error ? valueToNodes(error.node, type) || valueToNodes(error.value, error.schema) || [] : [];
|
|
15182
14492
|
const errorCsv = nodes => {
|
|
15183
14493
|
try {
|
|
15184
|
-
return toCsv
|
|
14494
|
+
return toCsv(nodes, { includeExising: true });
|
|
15185
14495
|
}
|
|
15186
14496
|
catch {
|
|
15187
14497
|
return '';
|
|
@@ -15412,528 +14722,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
15412
14722
|
*/
|
|
15413
14723
|
const impactAssessmentProduct = (impactAssessment) => impactAssessment?.product?.term || impactAssessment?.term;
|
|
15414
14724
|
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15418
|
-
|
|
15419
|
-
ChartNodeType["category"] = "category";
|
|
15420
|
-
ChartNodeType["emission"] = "emission";
|
|
15421
|
-
ChartNodeType["endpoint"] = "endpoint";
|
|
15422
|
-
ChartNodeType["input"] = "input";
|
|
15423
|
-
ChartNodeType["operation"] = "operation";
|
|
15424
|
-
ChartNodeType["root"] = "root";
|
|
15425
|
-
})(ChartNodeType || (ChartNodeType = {}));
|
|
15426
|
-
const nodeBorderColours = {
|
|
15427
|
-
[ChartNodeType.endpoint]: '#193957',
|
|
15428
|
-
[ChartNodeType.midpoint]: '#249DA5',
|
|
15429
|
-
[ChartNodeType.category]: '#DC7033',
|
|
15430
|
-
[ChartNodeType.emission]: '#498F78',
|
|
15431
|
-
[ChartNodeType.input]: '#FFC000',
|
|
15432
|
-
[ChartNodeType.operation]: '#D0CABA'
|
|
15433
|
-
};
|
|
15434
|
-
const nodeColours = {
|
|
15435
|
-
[ChartNodeType.endpoint]: '#DBE3EA',
|
|
15436
|
-
[ChartNodeType.midpoint]: '#D3EBED',
|
|
15437
|
-
[ChartNodeType.category]: '#FFDEC0',
|
|
15438
|
-
[ChartNodeType.emission]: '#D5F3D8',
|
|
15439
|
-
[ChartNodeType.input]: '#FFE8A3',
|
|
15440
|
-
[ChartNodeType.operation]: '#FBF4DF'
|
|
15441
|
-
};
|
|
15442
|
-
const legend = [
|
|
15443
|
-
{ type: ChartNodeType.endpoint, text: 'Endpoint / Single Score' },
|
|
15444
|
-
{ type: ChartNodeType.midpoint, text: 'Characterised Indicator' },
|
|
15445
|
-
{ type: ChartNodeType.category, text: 'Category' },
|
|
15446
|
-
{ type: ChartNodeType.emission, text: 'Emission / Resource Use' },
|
|
15447
|
-
{ type: ChartNodeType.input, text: 'Input' },
|
|
15448
|
-
{ type: ChartNodeType.operation, text: 'Operation' }
|
|
15449
|
-
];
|
|
15450
|
-
/* eslint-disable-next-line complexity */
|
|
15451
|
-
const getUnitsAndLabel = (terms, fallbackLabel = 'unknown term') => {
|
|
15452
|
-
const allTerms = (Array.isArray(terms) ? terms : [terms]).filter(Boolean);
|
|
15453
|
-
const term = allTerms?.[0];
|
|
15454
|
-
const label = term?.name ?? term?.['@id'] ?? fallbackLabel;
|
|
15455
|
-
const units = term?.units ? `(${term?.units})` : '';
|
|
15456
|
-
return {
|
|
15457
|
-
label: `${label.replace(units, '').trim()}${allTerms.length > 1 ? ` (and ${allTerms.length - 1} more)` : ''}`,
|
|
15458
|
-
terms: allTerms,
|
|
15459
|
-
units
|
|
15460
|
-
};
|
|
15461
|
-
};
|
|
15462
|
-
const typeMapping = {
|
|
15463
|
-
[TermTermType.endpointIndicator]: ChartNodeType.endpoint,
|
|
15464
|
-
[TermTermType.characterisedIndicator]: ChartNodeType.midpoint,
|
|
15465
|
-
[TermTermType.emission]: ChartNodeType.emission,
|
|
15466
|
-
[TermTermType.resourceUse]: ChartNodeType.emission,
|
|
15467
|
-
[TermTermType.operation]: ChartNodeType.operation
|
|
15468
|
-
};
|
|
15469
|
-
const safeDivide = (numerator, denominator) => (denominator !== 0 ? numerator / denominator : 0);
|
|
15470
|
-
const sumIndicators = (data) => {
|
|
15471
|
-
const totalValues = data.reduce((acc, log) => {
|
|
15472
|
-
acc[log.indicator] = acc[log.indicator] || [];
|
|
15473
|
-
!isUndefined(log.weightedValue) && acc[log.indicator].push(log.weightedValue);
|
|
15474
|
-
if (log.category) {
|
|
15475
|
-
const categoryKey = `${log.indicator}-${log.category}`;
|
|
15476
|
-
acc[categoryKey] = acc[categoryKey] || [];
|
|
15477
|
-
!isUndefined(log.weightedValue) && acc[categoryKey].push(log.weightedValue);
|
|
15478
|
-
}
|
|
15479
|
-
return acc;
|
|
15480
|
-
}, {});
|
|
15481
|
-
return Object.fromEntries(Object.entries(totalValues).map(([key, values]) => [
|
|
15482
|
-
key,
|
|
15483
|
-
// if the total is positive, we ignore negative values
|
|
15484
|
-
sum(values) < 0 ? sum(values) : sum(values.filter(v => v >= 0))
|
|
15485
|
-
]));
|
|
15486
|
-
};
|
|
15487
|
-
const groupIndicators = (data, terms = {}, totals = {}) => data.reduce((acc, log) => {
|
|
15488
|
-
const contributorType = typeMapping[terms[log.contributor]?.termType] || ChartNodeType.input;
|
|
15489
|
-
const indicatorType = typeMapping[terms[log.indicator]?.termType];
|
|
15490
|
-
const categoryKey = log.category ? `${log.indicator}-${log.category}` : undefined;
|
|
15491
|
-
const parentTotal = categoryKey ? totals[categoryKey] : totals[log.indicator];
|
|
15492
|
-
const contributorNode = {
|
|
15493
|
-
id: log.contributor,
|
|
15494
|
-
...getUnitsAndLabel(terms[log.contributor], log.contributor),
|
|
15495
|
-
type: contributorType,
|
|
15496
|
-
value: log.value,
|
|
15497
|
-
weightedValue: log.weightedValue,
|
|
15498
|
-
modelId: log.modelId,
|
|
15499
|
-
fraction: safeDivide(log.weightedValue, parentTotal),
|
|
15500
|
-
children: contributorType === ChartNodeType.emission
|
|
15501
|
-
? log.inputs.map(i => ({
|
|
15502
|
-
...i,
|
|
15503
|
-
...getUnitsAndLabel(terms[i.id], i.label),
|
|
15504
|
-
type: ChartNodeType.input,
|
|
15505
|
-
fraction: safeDivide(i.weightedValue, log.value),
|
|
15506
|
-
children: i.operations.map(o => ({
|
|
15507
|
-
...o,
|
|
15508
|
-
...getUnitsAndLabel(terms[o.id], o.label),
|
|
15509
|
-
type: ChartNodeType.operation,
|
|
15510
|
-
fraction: safeDivide(o.weightedValue, i.weightedValue)
|
|
15511
|
-
}))
|
|
15512
|
-
}))
|
|
15513
|
-
: []
|
|
15514
|
-
};
|
|
15515
|
-
acc[log.indicator] = acc[log.indicator] || {
|
|
15516
|
-
id: log.indicator,
|
|
15517
|
-
...getUnitsAndLabel(terms[log.indicator], log.indicator),
|
|
15518
|
-
modelId: log.modelId,
|
|
15519
|
-
type: indicatorType,
|
|
15520
|
-
value: totals[log.indicator],
|
|
15521
|
-
children: []
|
|
15522
|
-
};
|
|
15523
|
-
if (log.category) {
|
|
15524
|
-
const categoryId = `cat-${log.indicator}-${log.category}`;
|
|
15525
|
-
let categoryNode = acc[log.indicator].children.find(c => c.id === categoryId);
|
|
15526
|
-
if (!categoryNode) {
|
|
15527
|
-
categoryNode = {
|
|
15528
|
-
id: categoryId,
|
|
15529
|
-
label: log.category,
|
|
15530
|
-
type: ChartNodeType.category,
|
|
15531
|
-
units: '',
|
|
15532
|
-
value: totals[categoryKey],
|
|
15533
|
-
weightedValue: totals[categoryKey],
|
|
15534
|
-
modelId: log.modelId,
|
|
15535
|
-
fraction: safeDivide(totals[categoryKey], totals[log.indicator]),
|
|
15536
|
-
children: []
|
|
15537
|
-
};
|
|
15538
|
-
acc[log.indicator].children.push(categoryNode);
|
|
15539
|
-
}
|
|
15540
|
-
categoryNode.children.push(contributorNode);
|
|
15541
|
-
}
|
|
15542
|
-
else {
|
|
15543
|
-
acc[log.indicator].children.push(contributorNode);
|
|
15544
|
-
}
|
|
15545
|
-
return acc;
|
|
15546
|
-
}, {});
|
|
15547
|
-
|
|
15548
|
-
const width = 800;
|
|
15549
|
-
const margin = { top: 10, right: 20, bottom: 10, left: 20 };
|
|
15550
|
-
const fontSize = Math.floor(width / 100);
|
|
15551
|
-
const nodePadding = fontSize / 2;
|
|
15552
|
-
const lineHeight = fontSize * 1.2;
|
|
15553
|
-
const barHeight = lineHeight;
|
|
15554
|
-
const barRadius = 4;
|
|
15555
|
-
const nodeHeight = nodePadding * 2 + 2 * lineHeight + barHeight;
|
|
15556
|
-
const nodeWidth = nodeHeight * 2 * 1.618;
|
|
15557
|
-
const nodeContentWidth = nodeWidth - nodePadding * 2;
|
|
15558
|
-
const layerSpacing = (width - margin.left - margin.right - nodeWidth * 5) / 4;
|
|
15559
|
-
const nodeMargin = nodePadding * 0.75;
|
|
15560
|
-
const nodeSpacingFactor = (nodeHeight + nodeMargin * 2) / nodeHeight;
|
|
15561
|
-
const nodeDuration = 600;
|
|
15562
|
-
const linkDuration = 200;
|
|
15563
|
-
const groupDelay = nodeDuration - 100;
|
|
15564
|
-
const nonLCAIndicatorsId = 'nonLCAIndicators';
|
|
15565
|
-
const greyedOpacity = 0.2;
|
|
15566
|
-
const groupOverlap = lineHeight - nodePadding - nodeMargin;
|
|
15567
|
-
const groupNodeSpace = nodeHeight - 2 * groupOverlap;
|
|
15568
|
-
const collapseChildren = (node) => {
|
|
15569
|
-
node._children = node.children || node._children;
|
|
15570
|
-
node.children = null;
|
|
15571
|
-
return node;
|
|
15572
|
-
};
|
|
15573
|
-
const restoreChildren = (node) => {
|
|
15574
|
-
node.children = node._children;
|
|
15575
|
-
node._children = null;
|
|
15576
|
-
return node;
|
|
15577
|
-
};
|
|
15578
|
-
// children currently laid out, falling back to the collapsed ones while a node is closing
|
|
15579
|
-
const childrenOf = (node) => (node?.children || node?._children || []);
|
|
15580
|
-
// Stagger the children so they animate one after the other: in order when they appear, in reverse
|
|
15581
|
-
// when they disappear. A child that is not in the list anymore has no known position: it is not
|
|
15582
|
-
// delayed, which also keeps the delay finite (a `NaN` delay would never run, nor ever be removed).
|
|
15583
|
-
const enterStagger = (parent, child) => Math.max(childrenOf(parent).indexOf(child), 0) * 10;
|
|
15584
|
-
const exitStagger = (parent, child) => {
|
|
15585
|
-
const children = childrenOf(parent);
|
|
15586
|
-
const index = children.indexOf(child);
|
|
15587
|
-
return index < 0 ? 0 : (children.length - index - 1) * 10;
|
|
15588
|
-
};
|
|
15589
|
-
const wrap = (selection, maxWidth = nodeContentWidth) => {
|
|
15590
|
-
selection.each(function (d) {
|
|
15591
|
-
const t = select(this);
|
|
15592
|
-
const units = ellipsis$1(d.data.units, 20);
|
|
15593
|
-
d3wrap(t, {
|
|
15594
|
-
maxWidth,
|
|
15595
|
-
maxLines: d.data.showBar ? 2 : 3,
|
|
15596
|
-
vcentre: !d.data.showBar,
|
|
15597
|
-
words: [...d.data.label.split(/\s+/), units],
|
|
15598
|
-
ownLineWord: units,
|
|
15599
|
-
nHeight: nodeHeight,
|
|
15600
|
-
lHeight: lineHeight
|
|
15601
|
-
});
|
|
15602
|
-
});
|
|
15603
|
-
};
|
|
15604
|
-
const setNodeSize = selection => selection.attr('width', nodeWidth).attr('height', nodeHeight);
|
|
15605
|
-
const formatBar = (selection) => selection
|
|
15606
|
-
.attr('height', barHeight)
|
|
15607
|
-
.attr('rx', `${barRadius}`)
|
|
15608
|
-
.attr('ry', `${barRadius}`)
|
|
15609
|
-
.attr('y', nodeHeight - nodePadding - barHeight)
|
|
15610
|
-
.attr('x', nodePadding);
|
|
15611
|
-
const formatBarPath = (data) => {
|
|
15612
|
-
const x = nodePadding;
|
|
15613
|
-
const y = nodeHeight - nodePadding - barHeight;
|
|
15614
|
-
const h = barHeight;
|
|
15615
|
-
const w = fractionWidth(data);
|
|
15616
|
-
const isFull = data.fraction >= 0.99;
|
|
15617
|
-
// Prevent visual glitches if the bar is narrower than the radius
|
|
15618
|
-
const safeW = data.fraction < 0.01 ? 0 : Math.max(w, barRadius);
|
|
15619
|
-
const safeH = Math.max(h, 2 * barRadius);
|
|
15620
|
-
return isFull
|
|
15621
|
-
? `
|
|
15622
|
-
M ${x + safeW - barRadius}, ${y}
|
|
15623
|
-
a ${barRadius},${barRadius} 0 0 1 ${barRadius},${barRadius} v ${safeH - 2 * barRadius}
|
|
15624
|
-
a ${barRadius},${barRadius} 0 0 1 -${barRadius},${barRadius} h -${safeW - 2 * barRadius}
|
|
15625
|
-
a ${barRadius},${barRadius} 0 0 1 -${barRadius},-${barRadius} v -${safeH - 2 * barRadius}
|
|
15626
|
-
a ${barRadius},${barRadius} 0 0 1 ${barRadius},-${barRadius}
|
|
15627
|
-
z
|
|
15628
|
-
`
|
|
15629
|
-
: `
|
|
15630
|
-
M ${x + safeW}, ${y}
|
|
15631
|
-
v ${safeH}
|
|
15632
|
-
h -${safeW - barRadius}
|
|
15633
|
-
a ${barRadius},${barRadius} 0 0 1 -${barRadius},-${barRadius}
|
|
15634
|
-
v -${safeH - 2 * barRadius}
|
|
15635
|
-
a ${barRadius},${barRadius} 0 0 1 ${barRadius},-${barRadius}
|
|
15636
|
-
z
|
|
15637
|
-
`;
|
|
15638
|
-
};
|
|
15639
|
-
const groupButtonWidth = (nodeWidth * 2) / 7;
|
|
15640
|
-
const groupButtonHeight = nodePadding * 2 + lineHeight + 3;
|
|
15641
|
-
const setGroupButtonXY = (selection) => selection
|
|
15642
|
-
.attr('width', groupButtonWidth)
|
|
15643
|
-
.attr('height', d => (d._groupOpen ? lineHeight + nodePadding * 2 : groupButtonHeight))
|
|
15644
|
-
.attr('x', d => (d._groupOpen ? (nodeWidth * 2.5) / 7 : nodeWidth / 2 - groupButtonWidth / 2))
|
|
15645
|
-
.attr('y', d => d._groupOpen ? groupOverlap + (groupNodeSpace - (lineHeight + nodePadding * 2)) / 2 : nodePadding + lineHeight + 6);
|
|
15646
|
-
const formatNumber = (value) => (isNumber(value) ? toPrecision(value, 3) : 'N/A');
|
|
15647
|
-
const isTypeWithoutValue = (nodeDatum) => [ChartNodeType.input, ChartNodeType.operation].includes(nodeDatum.data.type);
|
|
15648
|
-
const getWeightedValueText = (nodeDatum) => `${formatNumber(nodeDatum.data.weightedValue)}${nodeDatum.parent?.data.value === null
|
|
15649
|
-
? ''
|
|
15650
|
-
: ` of ${formatNumber(nodeDatum.parent?.data.value || nodeDatum.parent?.data.weightedValue || 0)}`} ${isNumber(nodeDatum.parent?.data.value) ? nodeDatum.parent?.data?.units : nodeDatum.parent?.parent?.data?.units}`;
|
|
15651
|
-
const generateTipData = (nodeDatum) => ({
|
|
15652
|
-
hasWeightedValue: nodeDatum.data.weightedValue,
|
|
15653
|
-
weightedValueText: getWeightedValueText(nodeDatum),
|
|
15654
|
-
valueKeyHref: `${schemaBaseUrl()}/${SchemaType.Indicator}#value`,
|
|
15655
|
-
modelDocsHref: nodeDatum.data.modelDocs,
|
|
15656
|
-
valueValueText: isNumber(nodeDatum.data.value)
|
|
15657
|
-
? `${formatNumber(nodeDatum.data.value)} ${nodeDatum.data.units}`
|
|
15658
|
-
: isTypeWithoutValue(nodeDatum)
|
|
15659
|
-
? ''
|
|
15660
|
-
: 'no contribution',
|
|
15661
|
-
hasValue: nodeDatum.data.value,
|
|
15662
|
-
modelKeyHref: `${schemaBaseUrl()}/${SchemaType.Indicator}#methodModel`,
|
|
15663
|
-
modelValue: nodeDatum.data.modelName,
|
|
15664
|
-
modelValueHref: `${baseUrl()}/term/${nodeDatum.data.modelId}`,
|
|
15665
|
-
termKeyHref: `${schemaBaseUrl()}/${SchemaType.Indicator}#term`,
|
|
15666
|
-
terms: nodeDatum.data.terms?.map(term => ({
|
|
15667
|
-
text: term.name || term['@id'],
|
|
15668
|
-
href: `${baseUrl()}/term/${term['@id']}`
|
|
15669
|
-
}))
|
|
15670
|
-
});
|
|
15671
|
-
const addTooltip = (selection, { tooltipOperator }) => {
|
|
15672
|
-
let hoveringTip = false;
|
|
15673
|
-
let hoveringNode = false;
|
|
15674
|
-
let lastNodeId;
|
|
15675
|
-
const closeTip = () => {
|
|
15676
|
-
tooltipOperator.close(false);
|
|
15677
|
-
selectAll('.tip-target').classed('tip-target', false);
|
|
15678
|
-
lastNodeId = null;
|
|
15679
|
-
};
|
|
15680
|
-
const startCloseTip = () => {
|
|
15681
|
-
setTimeout(() => {
|
|
15682
|
-
if (!hoveringTip && !hoveringNode) {
|
|
15683
|
-
closeTip();
|
|
15684
|
-
}
|
|
15685
|
-
}, 100);
|
|
15686
|
-
};
|
|
15687
|
-
selection
|
|
15688
|
-
.on('click', closeTip)
|
|
15689
|
-
.on('pointerenter', (event, nodeDatum) => {
|
|
15690
|
-
hoveringNode = true;
|
|
15691
|
-
if (lastNodeId === nodeDatum.data.id) {
|
|
15692
|
-
return;
|
|
15693
|
-
}
|
|
15694
|
-
closeTip();
|
|
15695
|
-
lastNodeId = nodeDatum.data.id;
|
|
15696
|
-
select(event.target).classed('tip-target', true);
|
|
15697
|
-
tooltipOperator.open({ tipData: generateTipData(nodeDatum) });
|
|
15698
|
-
select('.driver-chart-tooltip')
|
|
15699
|
-
.on('pointerenter', () => {
|
|
15700
|
-
hoveringTip = true;
|
|
15701
|
-
})
|
|
15702
|
-
.on('pointerleave', () => {
|
|
15703
|
-
hoveringTip = false;
|
|
15704
|
-
startCloseTip();
|
|
15705
|
-
});
|
|
15706
|
-
})
|
|
15707
|
-
.on('pointerleave', () => {
|
|
15708
|
-
hoveringNode = false;
|
|
15709
|
-
startCloseTip();
|
|
15710
|
-
});
|
|
15711
|
-
return selection;
|
|
15712
|
-
};
|
|
15713
|
-
const mergedNodes = (selection, { tooltipOperator }) => {
|
|
15714
|
-
selection
|
|
15715
|
-
.selectAll('.node-box')
|
|
15716
|
-
.classed('node-openable', d => !!d.height)
|
|
15717
|
-
.classed('node-open', d => d._open)
|
|
15718
|
-
.call(setNodeSize)
|
|
15719
|
-
.attr('stroke', ({ data }) => nodeBorderColours[data.type])
|
|
15720
|
-
.attr('fill', d => nodeColours[d.data.type])
|
|
15721
|
-
.attr('stroke-width', '2')
|
|
15722
|
-
.attr('rx', '3px')
|
|
15723
|
-
.attr('ry', '3px')
|
|
15724
|
-
.style('user-select', typeof window !== 'undefined' && window.innerWidth < 768 ? 'none' : null);
|
|
15725
|
-
selection.filter(d => !d.data.group && d.data.id !== nonLCAIndicatorsId).call(addTooltip, { tooltipOperator });
|
|
15726
|
-
selection
|
|
15727
|
-
.selectAll('.node-label')
|
|
15728
|
-
.attr('y', d => (d.data.showBar ? lineHeight / 2 + nodePadding / 2 : 0))
|
|
15729
|
-
.attr('x', nodeWidth / 2)
|
|
15730
|
-
.attr('font-weight', 'bold')
|
|
15731
|
-
.attr('fill', '#4a4a4a')
|
|
15732
|
-
.attr('text-anchor', 'middle')
|
|
15733
|
-
.attr('alignment-baseline', 'central');
|
|
15734
|
-
const groups = selection.filter(d => d.data.group);
|
|
15735
|
-
groups
|
|
15736
|
-
.selectAll('.group-border')
|
|
15737
|
-
.attr('width', nodeWidth + nodePadding * 2)
|
|
15738
|
-
.attr('x', -nodePadding)
|
|
15739
|
-
.attr('y', nodeHeight - (lineHeight + nodePadding * 2))
|
|
15740
|
-
.attr('opacity', d => (d._greyed && d._groupOpen ? greyedOpacity : d._groupOpen ? 1 : 0))
|
|
15741
|
-
.attr('height', d => d._groupOpen
|
|
15742
|
-
? Math.abs(d.parent._groupChildren.length - d.parent.children.length) * (nodeHeight * nodeSpacingFactor) +
|
|
15743
|
-
nodePadding +
|
|
15744
|
-
(lineHeight + nodePadding * 2) / 2
|
|
15745
|
-
: 0)
|
|
15746
|
-
.attr('fill', 'none')
|
|
15747
|
-
.attr('stroke-dasharray', '2, 5')
|
|
15748
|
-
.attr('stroke', '#b5b5b5')
|
|
15749
|
-
.attr('stroke-width', '1');
|
|
15750
|
-
groups
|
|
15751
|
-
.selectAll('.group-label')
|
|
15752
|
-
.attr('width', nodeWidth)
|
|
15753
|
-
.attr('y', nodeHeight / 3)
|
|
15754
|
-
.attr('x', nodeWidth / 2)
|
|
15755
|
-
.text(d => d.data.label)
|
|
15756
|
-
.attr('opacity', d => (d._groupOpen ? 0 : d._greyed ? greyedOpacity : 1))
|
|
15757
|
-
.attr('fill', '#4a4a4a')
|
|
15758
|
-
.attr('text-anchor', 'middle')
|
|
15759
|
-
.attr('alignment-baseline', 'central')
|
|
15760
|
-
.attr('font-weight', 'bold');
|
|
15761
|
-
groups
|
|
15762
|
-
.selectAll('.group-button-border')
|
|
15763
|
-
.attr('width', nodeWidth)
|
|
15764
|
-
.attr('x', 0)
|
|
15765
|
-
.attr('y', 0)
|
|
15766
|
-
.attr('height', d => (d._groupOpen ? '0' : '45'))
|
|
15767
|
-
.attr('fill', 'none')
|
|
15768
|
-
.attr('stroke-dasharray', '2, 5')
|
|
15769
|
-
.attr('stroke', '#b5b5b5')
|
|
15770
|
-
.attr('stroke-width', '1');
|
|
15771
|
-
groups
|
|
15772
|
-
.selectAll('.group-button')
|
|
15773
|
-
.call(setGroupButtonXY)
|
|
15774
|
-
.style('pointer-events', d => (d._greyed ? 'none' : 'auto'))
|
|
15775
|
-
.style('stroke-opacity', d => (d._greyed ? greyedOpacity : 1))
|
|
15776
|
-
.style('display', 'none') // do not show on export
|
|
15777
|
-
.attr('fill', d => nodeColours[d.data.type])
|
|
15778
|
-
.attr('rx', '3px')
|
|
15779
|
-
.attr('ry', '3px');
|
|
15780
|
-
groups
|
|
15781
|
-
.selectAll('.group-button-text')
|
|
15782
|
-
.text(d => (d._groupOpen ? 'Hide' : 'Show'))
|
|
15783
|
-
.style('display', 'none') // do not show on export
|
|
15784
|
-
.attr('x', d => (d._groupOpen ? nodeWidth / 2 : nodeWidth / 2))
|
|
15785
|
-
.attr('y', d => (d._groupOpen ? nodeMargin + lineHeight + lineHeight / 2 : nodeHeight / 3 + groupButtonHeight - 4))
|
|
15786
|
-
.attr('font-size', () => `${fontSize * 1.3}px`)
|
|
15787
|
-
.attr('fill', '#4a4a4a')
|
|
15788
|
-
.attr('text-anchor', 'middle')
|
|
15789
|
-
.attr('alignment-baseline', 'central');
|
|
15790
|
-
selection.selectAll('.sibling-mask').remove();
|
|
15791
|
-
selection
|
|
15792
|
-
.filter(d => d._greyed && !d.data.group)
|
|
15793
|
-
.append('rect')
|
|
15794
|
-
.classed('node-openable', d => !!d.height)
|
|
15795
|
-
.classed('sibling-mask mask-openable', true)
|
|
15796
|
-
.attr('fill', 'rgba(250, 250, 250)')
|
|
15797
|
-
.attr('stroke', 'rgba(250, 250, 250)')
|
|
15798
|
-
.attr('fill-opacity', '0.8')
|
|
15799
|
-
.attr('stroke-opacity', '0.9')
|
|
15800
|
-
.attr('stroke-width', '2')
|
|
15801
|
-
.attr('rx', '3px')
|
|
15802
|
-
.attr('ry', '3px')
|
|
15803
|
-
.call(setNodeSize);
|
|
15804
|
-
selection
|
|
15805
|
-
.filter(d => d._greyed && d.data.group)
|
|
15806
|
-
.append('rect')
|
|
15807
|
-
.classed('sibling-mask', true)
|
|
15808
|
-
.attr('fill', 'rgba(250, 250, 250)')
|
|
15809
|
-
.attr('stroke', 'rgba(250, 250, 250)')
|
|
15810
|
-
.attr('fill-opacity', '0.8')
|
|
15811
|
-
.attr('stroke-opacity', '0.9')
|
|
15812
|
-
.attr('stroke-width', '2')
|
|
15813
|
-
.attr('rx', '3px')
|
|
15814
|
-
.attr('ry', '3px')
|
|
15815
|
-
.call(setGroupButtonXY);
|
|
15816
|
-
return selection;
|
|
15817
|
-
};
|
|
15818
|
-
// more than 70%
|
|
15819
|
-
const fractionCutoff = 0.7;
|
|
15820
|
-
const fractionWidth = (data) => Math.min(Math.abs(data.fraction), 1) * nodeContentWidth;
|
|
15821
|
-
const enterNodes = (selection) => {
|
|
15822
|
-
const nodes = selection.filter(d => d.data.type !== ChartNodeType.root && !d.data.group);
|
|
15823
|
-
nodes.append('rect').classed('node-box', true);
|
|
15824
|
-
nodes
|
|
15825
|
-
.append('text')
|
|
15826
|
-
.classed('node-label', true)
|
|
15827
|
-
.text(({ data }) => data.label + ' ' + data.units)
|
|
15828
|
-
.attr('opacity', 0);
|
|
15829
|
-
// Text elements must be rendered before we can calculate wrapping
|
|
15830
|
-
setTimeout(() => {
|
|
15831
|
-
const labels = nodes.selectAll('.node-label').call(wrap);
|
|
15832
|
-
labels.attr('opacity', 1);
|
|
15833
|
-
});
|
|
15834
|
-
const groups = selection.filter(d => d.data.group);
|
|
15835
|
-
groups.append('rect').attr('class', 'group-border');
|
|
15836
|
-
groups.append('text').attr('class', 'group-label');
|
|
15837
|
-
groups.append('rect').attr('class', 'group-button-border');
|
|
15838
|
-
groups.append('rect').attr('class', 'group-button');
|
|
15839
|
-
groups.append('text').attr('class', 'group-button-text');
|
|
15840
|
-
const withBar = nodes.filter(d => d.data.showBar);
|
|
15841
|
-
withBar
|
|
15842
|
-
.filter(d => d.data.fraction > 0)
|
|
15843
|
-
.append('rect')
|
|
15844
|
-
.classed('bar-background', true)
|
|
15845
|
-
.attr('width', nodeContentWidth)
|
|
15846
|
-
.attr('fill', 'white')
|
|
15847
|
-
.call(formatBar);
|
|
15848
|
-
withBar
|
|
15849
|
-
.append('path')
|
|
15850
|
-
.classed('bar-foreground', true)
|
|
15851
|
-
.attr('d', d => formatBarPath(d.data))
|
|
15852
|
-
.attr('fill', d => nodeBorderColours[d.data.type]);
|
|
15853
|
-
withBar
|
|
15854
|
-
.append('text')
|
|
15855
|
-
.classed('bar-label', true)
|
|
15856
|
-
.text(({ data }) => data.fraction === 0
|
|
15857
|
-
? 'no contribution'
|
|
15858
|
-
: `${Math.round(100 * data.fraction)}% ${Math.round(100 * data.fraction) === 0 ? ' (rounded)' : ''}`)
|
|
15859
|
-
.attr('text-anchor', d => (d.data.fraction === 0 ? 'middle' : d.data.fraction > fractionCutoff ? 'end' : 'start'))
|
|
15860
|
-
.attr('x', d => d.data.fraction === 0
|
|
15861
|
-
? nodeWidth / 2
|
|
15862
|
-
: fractionWidth(d.data) + nodePadding + (d.data.fraction > fractionCutoff ? -4 : 4))
|
|
15863
|
-
.attr('y', nodeHeight - nodePadding - barHeight / 2)
|
|
15864
|
-
.attr('fill', '#4a4a4a')
|
|
15865
|
-
.attr('alignment-baseline', 'central');
|
|
15866
|
-
return selection;
|
|
15867
|
-
};
|
|
15868
|
-
const mergedLinks = (selection, { togglingGroup, switchingSelection }) => selection
|
|
15869
|
-
.style('stroke', d => (d.target._open && nodeBorderColours[d.source.data.type]) || null)
|
|
15870
|
-
.attr('stroke-dasharray', d => (d.target.data.group ? '2,5' : null))
|
|
15871
|
-
.attr('stroke-width', d => (d.target._open ? 2 : d.target._groupOpen ? 0 : 1))
|
|
15872
|
-
.attr('stroke-opacity', d => (d.target._greyed ? 0.1 : 0.6))
|
|
15873
|
-
.transition()
|
|
15874
|
-
.duration(linkDuration)
|
|
15875
|
-
.delay(d => {
|
|
15876
|
-
const groupClosing = togglingGroup && !childrenOf(d.target.parent).find(n => n.data.group)?._groupOpen;
|
|
15877
|
-
return groupClosing ? groupDelay : (switchingSelection ? nodeDuration : 0) + enterStagger(d.source, d.target);
|
|
15878
|
-
})
|
|
15879
|
-
.attr('opacity', 1)
|
|
15880
|
-
.attr('d', d => {
|
|
15881
|
-
const targetDy = d.target._groupOpen ? nodePadding + lineHeight / 2 + nodeMargin * 2 : nodeHeight / 2;
|
|
15882
|
-
return linkHorizontal()({
|
|
15883
|
-
source: [d.source.y + nodeWidth, d.source.x + nodeHeight / 2],
|
|
15884
|
-
target: [d.target.y, d.target.x + targetDy]
|
|
15885
|
-
});
|
|
15886
|
-
});
|
|
15887
|
-
const groupByPercentage = node => [ChartNodeType.endpoint, ChartNodeType.midpoint, ChartNodeType.emission].includes(node.data.type);
|
|
15888
|
-
const groupPercentage = (node, percentage = 0.02) => {
|
|
15889
|
-
const grouped = node._children?.filter(child => child.data.fraction < percentage);
|
|
15890
|
-
if (node._children?.length > 6 && grouped?.length > 1) {
|
|
15891
|
-
const type = node._children[0].data.type;
|
|
15892
|
-
const groupNode = {
|
|
15893
|
-
...hierarchy({
|
|
15894
|
-
id: `${type}-group`,
|
|
15895
|
-
label: `${grouped.length} more under ${Math.round(percentage * 100)}%.`,
|
|
15896
|
-
weightedValue: null,
|
|
15897
|
-
value: null,
|
|
15898
|
-
type,
|
|
15899
|
-
group: true
|
|
15900
|
-
}),
|
|
15901
|
-
depth: node.depth + 1,
|
|
15902
|
-
height: 1,
|
|
15903
|
-
parent: node
|
|
15904
|
-
};
|
|
15905
|
-
const notGrouped = node._children?.filter(child => child.data.fraction >= percentage);
|
|
15906
|
-
node._children.splice(notGrouped.length, 0, groupNode);
|
|
15907
|
-
notGrouped.push(groupNode);
|
|
15908
|
-
node._groupChildren = node._children;
|
|
15909
|
-
node._children = notGrouped;
|
|
15910
|
-
}
|
|
15911
|
-
};
|
|
15912
|
-
const showBar = (node) => [
|
|
15913
|
-
node.parent?.data.type !== ChartNodeType.root,
|
|
15914
|
-
node.parent?.data.id !== nonLCAIndicatorsId,
|
|
15915
|
-
!node.data.group,
|
|
15916
|
-
!isNaN(node.data.fraction)
|
|
15917
|
-
].every(Boolean);
|
|
15918
|
-
const extraInputKeys = ['input', 'input value'];
|
|
15919
|
-
const extraOperationKeys = ['operation', 'operation value'];
|
|
15920
|
-
const dataToCsv = (keys, terms) => (data) => [
|
|
15921
|
-
// line for the data
|
|
15922
|
-
toLine([
|
|
15923
|
-
...keys.map(k => terms[data[k]]?.name || data[k] || ''),
|
|
15924
|
-
...extraInputKeys.map(() => ''),
|
|
15925
|
-
...extraOperationKeys.map(() => '')
|
|
15926
|
-
]),
|
|
15927
|
-
// lines for each inputs and operations
|
|
15928
|
-
...(data.inputs ?? []).flatMap(input => [
|
|
15929
|
-
toLine([...keys.map(() => ''), input.label, input.weightedValue, ...extraOperationKeys.map(() => '')]),
|
|
15930
|
-
...(input.operations ?? []).map(operation => toLine([...keys.map(() => ''), ...extraInputKeys.map(() => ''), operation.label, operation.weightedValue]))
|
|
15931
|
-
])
|
|
15932
|
-
];
|
|
15933
|
-
const toCsv = (data, terms) => {
|
|
15934
|
-
const keys = Object.keys(data[0]).filter(k => k !== 'inputs');
|
|
15935
|
-
return [toLine([...keys, ...extraInputKeys, ...extraOperationKeys]), ...data.flatMap(dataToCsv(keys, terms))].join('\n');
|
|
15936
|
-
};
|
|
14725
|
+
/**
|
|
14726
|
+
* The driver chart, drawn with d3 by `@hestia-earth/ui-components-charts`; this component gives it the
|
|
14727
|
+
* platform's popover, legend and export.
|
|
14728
|
+
*/
|
|
15937
14729
|
class HierarchyChartComponent {
|
|
15938
14730
|
constructor() {
|
|
15939
14731
|
this.chartRef = viewChild.required('chart');
|
|
@@ -15942,58 +14734,10 @@ class HierarchyChartComponent {
|
|
|
15942
14734
|
this.terms = input({}, ...(ngDevMode ? [{ debugName: "terms" }] : []));
|
|
15943
14735
|
this.chartError = output();
|
|
15944
14736
|
this.nodeBorderColours = nodeBorderColours;
|
|
15945
|
-
this.nodeColours = nodeColours;
|
|
14737
|
+
this.nodeColours = nodeColours$1;
|
|
15946
14738
|
this.expandDownload = signal(false, ...(ngDevMode ? [{ debugName: "expandDownload" }] : []));
|
|
15947
14739
|
this.includedTypes = signal([], ...(ngDevMode ? [{ debugName: "includedTypes" }] : []));
|
|
15948
|
-
this.legend = computed(() => {
|
|
15949
|
-
const includedTypes = this.includedTypes();
|
|
15950
|
-
const defaultLegend = legend.filter(({ type }) => includedTypes.includes(type));
|
|
15951
|
-
const categoryLabel = this.data().find(d => d.categoryLabel)?.categoryLabel;
|
|
15952
|
-
return categoryLabel
|
|
15953
|
-
? defaultLegend.map(l => (l.type === ChartNodeType.category ? { ...l, text: categoryLabel } : l))
|
|
15954
|
-
: defaultLegend;
|
|
15955
|
-
}, ...(ngDevMode ? [{ debugName: "legend" }] : []));
|
|
15956
|
-
this.totalsPerIndicator = computed(() => sumIndicators(this.data()), ...(ngDevMode ? [{ debugName: "totalsPerIndicator" }] : []));
|
|
15957
|
-
this.indicators = computed(() => groupIndicators(this.data(), this.terms(), this.totalsPerIndicator()), ...(ngDevMode ? [{ debugName: "indicators" }] : []));
|
|
15958
|
-
this.indicatorArray = computed(() => Object.values(this.indicators()), ...(ngDevMode ? [{ debugName: "indicatorArray" }] : []));
|
|
15959
|
-
this.linkedIndicators = computed(() => this.indicatorArray()
|
|
15960
|
-
// link endpoints to midpoints and midpoints to any child midpoints
|
|
15961
|
-
.map(indicator => ({
|
|
15962
|
-
...indicator,
|
|
15963
|
-
children: indicator.children.map(child => {
|
|
15964
|
-
const prefixRemoved = indicator.type === ChartNodeType.endpoint && child.label.split(', ')[1];
|
|
15965
|
-
const childIndicator = this.indicators()[child.id];
|
|
15966
|
-
return child.type === ChartNodeType.midpoint
|
|
15967
|
-
? {
|
|
15968
|
-
...child,
|
|
15969
|
-
label: prefixRemoved
|
|
15970
|
-
? '[...]' + prefixRemoved.charAt(0).toUpperCase() + prefixRemoved.slice(1)
|
|
15971
|
-
: child.label,
|
|
15972
|
-
modelId: childIndicator?.modelId || child.modelId,
|
|
15973
|
-
children: childIndicator?.children
|
|
15974
|
-
}
|
|
15975
|
-
: child;
|
|
15976
|
-
})
|
|
15977
|
-
}))
|
|
15978
|
-
// remove duplicated indicators
|
|
15979
|
-
.filter(indicator => !this.indicatorArray().some(parent => parent.children.some(child => child.id === indicator.id))), ...(ngDevMode ? [{ debugName: "linkedIndicators" }] : []));
|
|
15980
|
-
this.nonLCAIndicators = computed(() => this.linkedIndicators().filter(indicator => indicator.type === ChartNodeType.midpoint), ...(ngDevMode ? [{ debugName: "nonLCAIndicators" }] : []));
|
|
15981
|
-
this.otherEndpoint = computed(() => this.nonLCAIndicators().length
|
|
15982
|
-
? {
|
|
15983
|
-
id: nonLCAIndicatorsId,
|
|
15984
|
-
label: 'Other indicators',
|
|
15985
|
-
term: 'n/a',
|
|
15986
|
-
modelId: 'n/a',
|
|
15987
|
-
units: '',
|
|
15988
|
-
value: null,
|
|
15989
|
-
weightedValue: null,
|
|
15990
|
-
type: ChartNodeType.endpoint,
|
|
15991
|
-
fraction: null,
|
|
15992
|
-
children: this.nonLCAIndicators()
|
|
15993
|
-
}
|
|
15994
|
-
: null, ...(ngDevMode ? [{ debugName: "otherEndpoint" }] : []));
|
|
15995
|
-
this.endpoints = computed(() => this.linkedIndicators().filter(indicator => indicator.type === ChartNodeType.endpoint), ...(ngDevMode ? [{ debugName: "endpoints" }] : []));
|
|
15996
|
-
this.chartChildren = computed(() => this.endpoints().length ? [...this.endpoints(), this.otherEndpoint()].filter(Boolean) : this.linkedIndicators(), ...(ngDevMode ? [{ debugName: "chartChildren" }] : []));
|
|
14740
|
+
this.legend = computed(() => hierarchyLegend(this.data(), this.includedTypes()), ...(ngDevMode ? [{ debugName: "legend" }] : []));
|
|
15997
14741
|
this.exportFormats = [
|
|
15998
14742
|
...exportFormats,
|
|
15999
14743
|
{
|
|
@@ -16003,201 +14747,35 @@ class HierarchyChartComponent {
|
|
|
16003
14747
|
];
|
|
16004
14748
|
this.chartExportFn = async (format) => {
|
|
16005
14749
|
return {
|
|
16006
|
-
png: () => this.
|
|
16007
|
-
svg: () => this.
|
|
16008
|
-
csv: () => downloadFile(`data:text/html;charset=utf-8,${encodeURIComponent(
|
|
14750
|
+
png: () => downloadPng(this.chartRef().nativeElement),
|
|
14751
|
+
svg: () => downloadSvg(this.chartRef().nativeElement),
|
|
14752
|
+
csv: () => downloadFile(`data:text/html;charset=utf-8,${encodeURIComponent(hierarchyCsv(this.data(), this.terms()))}`, 'chart.csv')
|
|
16009
14753
|
}[format]();
|
|
16010
14754
|
};
|
|
16011
14755
|
effect(() => {
|
|
16012
14756
|
if ([!isEmpty(this.data()), !isEmpty(this.terms()), this.chartRef()?.nativeElement].every(Boolean)) {
|
|
16013
|
-
untracked(() => this.
|
|
14757
|
+
untracked(() => this.draw());
|
|
16014
14758
|
}
|
|
16015
14759
|
});
|
|
16016
14760
|
}
|
|
16017
|
-
|
|
14761
|
+
draw() {
|
|
16018
14762
|
try {
|
|
16019
|
-
this.
|
|
16020
|
-
|
|
16021
|
-
|
|
16022
|
-
|
|
14763
|
+
const { includedTypes } = drawHierarchyChart(this.chartRef().nativeElement, {
|
|
14764
|
+
data: this.data(),
|
|
14765
|
+
terms: this.terms(),
|
|
14766
|
+
tooltip: {
|
|
14767
|
+
open: tipData => this.tooltipOperator().open({ tipData }),
|
|
14768
|
+
close: () => this.tooltipOperator().close(false),
|
|
14769
|
+
element: () => document.querySelector('.driver-chart-tooltip')
|
|
14770
|
+
},
|
|
14771
|
+
urls: { baseUrl, schemaBaseUrl }
|
|
14772
|
+
});
|
|
14773
|
+
this.includedTypes.set(includedTypes);
|
|
16023
14774
|
}
|
|
16024
14775
|
catch (error) {
|
|
16025
14776
|
this.chartError.emit(error);
|
|
16026
14777
|
}
|
|
16027
14778
|
}
|
|
16028
|
-
initChart() {
|
|
16029
|
-
const root = hierarchy({
|
|
16030
|
-
id: 'dummy-root',
|
|
16031
|
-
label: '',
|
|
16032
|
-
terms: [],
|
|
16033
|
-
modelId: '',
|
|
16034
|
-
value: null,
|
|
16035
|
-
weightedValue: null,
|
|
16036
|
-
units: '',
|
|
16037
|
-
type: ChartNodeType.root,
|
|
16038
|
-
children: this.chartChildren()
|
|
16039
|
-
});
|
|
16040
|
-
this.includedTypes.set(unique(root.descendants().map(n => n.data.type)));
|
|
16041
|
-
this.root = root
|
|
16042
|
-
// treat `0` as -infinity so they appear all the way to the bottom
|
|
16043
|
-
.sort((a, b) => (b.data.fraction || -Infinity) - (a.data.fraction || -Infinity))
|
|
16044
|
-
.eachAfter(node => {
|
|
16045
|
-
if (node.data.fraction === 0) {
|
|
16046
|
-
node.children = null;
|
|
16047
|
-
node.height = 0;
|
|
16048
|
-
}
|
|
16049
|
-
node.data.type !== ChartNodeType.root && collapseChildren(node);
|
|
16050
|
-
groupByPercentage(node) && groupPercentage(node);
|
|
16051
|
-
node.data.showBar = showBar(node);
|
|
16052
|
-
node.data.modelName = this.terms()?.[node.data.modelId]?.name;
|
|
16053
|
-
node.data.modelDocs = this.terms()?.[node.data.modelId]?.docs;
|
|
16054
|
-
});
|
|
16055
|
-
this.renderChart();
|
|
16056
|
-
}
|
|
16057
|
-
renderChart() {
|
|
16058
|
-
this.svg = select(this.chartRef()?.nativeElement);
|
|
16059
|
-
this.svg.selectAll('*').remove();
|
|
16060
|
-
// root node is always positioned at ⟨0, 0⟩
|
|
16061
|
-
const innerHeight = (this.root.children.length - 1) * nodeHeight * nodeSpacingFactor + nodeHeight;
|
|
16062
|
-
this.yMin = (nodeHeight - innerHeight) / 2 - margin.top;
|
|
16063
|
-
this.yTotal = innerHeight + margin.top + margin.bottom;
|
|
16064
|
-
const numLayers = this.legend().length;
|
|
16065
|
-
const chartWidth = numLayers * nodeWidth + (numLayers - 1) * layerSpacing;
|
|
16066
|
-
const totalWidth = Math.max(width, chartWidth + margin.left + margin.right);
|
|
16067
|
-
this.svg
|
|
16068
|
-
.attr('viewBox', [-margin.left, this.yMin, totalWidth, this.yTotal].join(' '))
|
|
16069
|
-
.attr('font-size', `${fontSize}px`);
|
|
16070
|
-
this.node = this.svg.append('g').selectAll('g');
|
|
16071
|
-
this.link = this.svg.append('g').selectAll('path');
|
|
16072
|
-
this.handleNodeClick(null, this.root.find(node => node.depth === 1));
|
|
16073
|
-
}
|
|
16074
|
-
// flip x/y to go left-right instead of top-bottom
|
|
16075
|
-
updateChart({ togglingGroup = false, switchingSelection = false } = {}) {
|
|
16076
|
-
const root = this.root;
|
|
16077
|
-
const treeLayout = tree()
|
|
16078
|
-
.nodeSize([nodeHeight, nodeWidth])
|
|
16079
|
-
.separation((...neighbours) => neighbours.some(n => n.data.group && n._groupOpen) ? 1 - groupOverlap / nodeHeight : nodeSpacingFactor);
|
|
16080
|
-
const tree$1 = treeLayout(root);
|
|
16081
|
-
const links = tree$1.links().filter(d => d.source.data.type !== ChartNodeType.root) || [];
|
|
16082
|
-
const nodes = tree$1.descendants().reverse();
|
|
16083
|
-
const numLayers = this.legend().length;
|
|
16084
|
-
const chartWidth = numLayers * nodeWidth + (numLayers - 1) * layerSpacing;
|
|
16085
|
-
const totalWidth = Math.max(width, chartWidth + margin.left + margin.right);
|
|
16086
|
-
const offset = Math.max(0, (totalWidth - margin.left - margin.right - chartWidth) / 2);
|
|
16087
|
-
nodes.forEach(d => {
|
|
16088
|
-
d.y = offset + (d.depth - 1) * (nodeWidth + layerSpacing);
|
|
16089
|
-
});
|
|
16090
|
-
// By default the tree expands symmetrically upwards and downwards from the selected node.
|
|
16091
|
-
// We move children down to prevent expansion upwards or up to minimise expansion downwards
|
|
16092
|
-
const minimiseHeight = ({ previousTanslateY, previousYTotal }, depth) => {
|
|
16093
|
-
const maxYTotal = Math.max(previousYTotal, this.yTotal);
|
|
16094
|
-
const parent = tree$1.find(node => node.depth === depth - 1 && node._open);
|
|
16095
|
-
parent?.children?.forEach(node => (node.x += previousTanslateY));
|
|
16096
|
-
const { min: minX, max: maxX } = (parent?.children || []).reduce(({ min, max }, n) => ({
|
|
16097
|
-
min: n.x < min ? n.x : min,
|
|
16098
|
-
max: n.x > max ? n.x : max
|
|
16099
|
-
}), { min: parent?.x, max: parent?.x });
|
|
16100
|
-
const { yMin, yTotal } = parent?.children?.length
|
|
16101
|
-
? { yMin: minX - margin.top, yTotal: maxX - minX + margin.bottom + margin.top + nodeHeight }
|
|
16102
|
-
: { yMin: null, yTotal: null };
|
|
16103
|
-
const overflowAbove = this.yMin - yMin; // positive when overflows above
|
|
16104
|
-
const overflowBelow = yTotal + yMin - (maxYTotal + this.yMin); // positive when overflows below
|
|
16105
|
-
// - translateY when overflows above: positive
|
|
16106
|
-
// - translateY when overflows below: negative by smallest of underflow above or overflow below
|
|
16107
|
-
// - translateY when no overflow: 0
|
|
16108
|
-
const translateY = overflowAbove > 0 ? overflowAbove : overflowBelow > 0 ? Math.max(-overflowBelow, overflowAbove) : 0;
|
|
16109
|
-
parent?.children?.forEach(node => (node.x += translateY));
|
|
16110
|
-
return {
|
|
16111
|
-
previousYTotal: Math.max(yTotal, previousYTotal),
|
|
16112
|
-
previousTanslateY: translateY + previousTanslateY
|
|
16113
|
-
};
|
|
16114
|
-
};
|
|
16115
|
-
const depths = Array.from({ length: numLayers }, (_, i) => i + 2);
|
|
16116
|
-
const { previousYTotal: newHeight } = depths.reduce(minimiseHeight, {
|
|
16117
|
-
previousTanslateY: 0,
|
|
16118
|
-
previousYTotal: this.yTotal
|
|
16119
|
-
});
|
|
16120
|
-
this.svg.transition().duration(300).attr('viewBox', [-margin.left, this.yMin, totalWidth, newHeight].join(' '));
|
|
16121
|
-
this.node = this.node
|
|
16122
|
-
// the root is a dummy node: it is never rendered, and has no parent to animate against
|
|
16123
|
-
.data(nodes.filter(d => d.data.type !== ChartNodeType.root), d => `${d.parent?.parent?.parent?.data.id}-${d.parent?.parent?.data.id}-${d.parent?.data.id}-${d.data.id}`)
|
|
16124
|
-
.join(enter => enter
|
|
16125
|
-
.append('g')
|
|
16126
|
-
.call(enterNodes)
|
|
16127
|
-
.attr('opacity', 0)
|
|
16128
|
-
.attr('transform', d => 'translate(' + (togglingGroup ? d.y : d.y - nodeWidth / 2) + ',' + d.x + ')'), update => update, exit => {
|
|
16129
|
-
this.tooltipOperator().close(false);
|
|
16130
|
-
return (exit
|
|
16131
|
-
.style('pointer-events', 'none')
|
|
16132
|
-
.transition()
|
|
16133
|
-
.duration(nodeDuration / 2)
|
|
16134
|
-
.delay(d => exitStagger(d.parent, d))
|
|
16135
|
-
.ease(easeElasticIn.amplitude(0.5).period(1))
|
|
16136
|
-
.attr('transform', d => 'translate(' + (togglingGroup ? d.y : d.y - nodeWidth / 2) + ',' + d.x + ')')
|
|
16137
|
-
.attr('opacity', 0)
|
|
16138
|
-
.remove()
|
|
16139
|
-
// in case a tooltip was opened on a transitioning element
|
|
16140
|
-
.on('end', () => this.tooltipOperator().close(false)));
|
|
16141
|
-
});
|
|
16142
|
-
this.node
|
|
16143
|
-
.call(mergedNodes, { tooltipOperator: this.tooltipOperator() })
|
|
16144
|
-
.transition()
|
|
16145
|
-
.delay(d => {
|
|
16146
|
-
const groupClosing = togglingGroup && !childrenOf(d.parent).find(n => n.data.group)?._groupOpen;
|
|
16147
|
-
return groupClosing ? groupDelay : (switchingSelection ? nodeDuration : 0) + enterStagger(d.parent, d);
|
|
16148
|
-
})
|
|
16149
|
-
.duration(nodeDuration)
|
|
16150
|
-
.ease(easeElasticOut.amplitude(0.5).period(1))
|
|
16151
|
-
.attr('transform', d => `translate(${d.y},${d.x})`)
|
|
16152
|
-
.attr('opacity', 1);
|
|
16153
|
-
this.node.selectAll('.node-openable').on('click', this.handleNodeClick.bind(this));
|
|
16154
|
-
this.node.selectAll('.group-button').on('click', this.handleGroupButtonClick.bind(this));
|
|
16155
|
-
this.link = this.link
|
|
16156
|
-
.data(links, d => `${d.source.parent?.parent?.data.id}-${d.source.parent?.data.id}-${d.source.data.id}-${d.target.data.id}`)
|
|
16157
|
-
.join(enter => enter
|
|
16158
|
-
.append('path')
|
|
16159
|
-
.classed('link-path', true)
|
|
16160
|
-
.attr('opacity', 0)
|
|
16161
|
-
.attr('fill', 'none')
|
|
16162
|
-
.attr('stroke', '#b5b5b5'), update => update, exit => exit
|
|
16163
|
-
.transition()
|
|
16164
|
-
.duration(linkDuration / 2)
|
|
16165
|
-
.delay(d => exitStagger(d.source, d.target))
|
|
16166
|
-
.attr('opacity', 0)
|
|
16167
|
-
.remove())
|
|
16168
|
-
.call(mergedLinks, { togglingGroup, switchingSelection });
|
|
16169
|
-
}
|
|
16170
|
-
handleGroupButtonClick(_event, d) {
|
|
16171
|
-
if (d._greyed) {
|
|
16172
|
-
return;
|
|
16173
|
-
}
|
|
16174
|
-
d._groupOpen = !d._groupOpen;
|
|
16175
|
-
const children = d.parent._groupChildren;
|
|
16176
|
-
d.parent._groupChildren = d.parent.children;
|
|
16177
|
-
d.parent.children = children;
|
|
16178
|
-
this.updateChart({ togglingGroup: true });
|
|
16179
|
-
}
|
|
16180
|
-
handleNodeClick(_event, d) {
|
|
16181
|
-
d._open = !d._open;
|
|
16182
|
-
const switchingSelection = '_greyed' in d && d._greyed;
|
|
16183
|
-
// the node can still be clicked while it animates out, by which time its parent is collapsed
|
|
16184
|
-
childrenOf(d.parent).forEach(child => {
|
|
16185
|
-
child._greyed = false;
|
|
16186
|
-
if (child !== d) {
|
|
16187
|
-
child._open = false;
|
|
16188
|
-
collapseChildren(child);
|
|
16189
|
-
child._greyed = d._open;
|
|
16190
|
-
}
|
|
16191
|
-
});
|
|
16192
|
-
d.children ? collapseChildren(d) : restoreChildren(d);
|
|
16193
|
-
this.updateChart({ switchingSelection });
|
|
16194
|
-
}
|
|
16195
|
-
downloadSvg() {
|
|
16196
|
-
return downloadSvg(this.chartRef().nativeElement);
|
|
16197
|
-
}
|
|
16198
|
-
downloadPng() {
|
|
16199
|
-
return downloadPng(this.chartRef().nativeElement);
|
|
16200
|
-
}
|
|
16201
14779
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: HierarchyChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
16202
14780
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: HierarchyChartComponent, isStandalone: true, selector: "he-hierarchy-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, terms: { classPropertyName: "terms", publicName: "terms", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { chartError: "chartError" }, viewQueries: [{ propertyName: "chartRef", first: true, predicate: ["chart"], descendants: true, isSignal: true }, { propertyName: "tooltipOperator", first: true, predicate: ["t"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"chart-area-border\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-flex-wrap-wrap is-gap-16 chart-area-border is-legend\">\n @for (legendItem of legend(); track legendItem.type) {\n <div class=\"is-flex is-align-items-center is-gap-8 | is-legend-item\">\n <span\n class=\"is-inline-block-tablet is-align-middle | key-colour\"\n [style.backgroundColor]=\"nodeColours[legendItem.type]\"\n [style.borderColor]=\"nodeBorderColours[legendItem.type]\"></span>\n <span class=\"is-size-7\">{{ legendItem.text }}</span>\n </div>\n }\n </div>\n\n <div class=\"chart-container is-relative w-100 is-mt-2\">\n <svg class=\"w-100 h-100\" #chart></svg>\n\n <span\n class=\"is-hidden\"\n [ngbPopover]=\"tipContent\"\n triggers=\"manual\"\n placement=\"right left auto\"\n container=\"body\"\n #t=\"ngbPopover\"\n positionTarget=\".tip-target\"\n popoverClass=\"is-narrow driver-chart-tooltip\"\n [animation]=\"true\"></span>\n\n <div class=\"is-absolute | chart-controls\">\n <div class=\"is-flex is-flex-direction-column is-align-items-flex-end is-gap-4\">\n <he-chart-export-button\n buttonClass=\"button is-small\"\n [chartExportFn]=\"chartExportFn\"\n [exportFormats]=\"exportFormats\" />\n </div>\n </div>\n </div>\n</div>\n\n<ng-template #tipContent let-tipData=\"tipData\">\n @if (tipData.hasWeightedValue) {\n <p>contribution: {{ tipData.weightedValueText }}</p>\n }\n @if (tipData.valueValueText) {\n <p>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.valueKeyHref\">value</a>\n <span class=\"is-pr-1\">:</span>\n <span>{{ tipData.valueValueText }}</span>\n @if (tipData.hasValue) {\n <a class=\"is-dark is-ml-2\" [href]=\"tipData.modelDocsHref\" target=\"_blank\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </p>\n }\n @if (tipData.modelValue) {\n <p>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.modelKeyHref\">methodModel</a>\n <span class=\"is-pr-1\">:</span>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.modelValueHref\">{{ tipData.modelValue }}</a>\n </p>\n }\n @if (tipData.terms?.length) {\n <p>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.termKeyHref\">terms</a>\n <span class=\"is-pr-1\">:</span>\n @for (term of tipData.terms; track term.text; let lastTerm = $last) {\n <a class=\"is-dark\" target=\"_blank\" [href]=\"term.href\">{{ term.text }}</a>\n @if (!lastTerm) {\n <span class=\"is-px-1\">;</span>\n }\n }\n </p>\n }\n</ng-template>\n", styles: [".chart-controls{top:0;right:0}.chart-controls .button,.chart-controls he-chart-export-button ::ng-deep>.button{width:36px;height:36px}.key-colour{border-radius:3px;border:1px solid transparent;height:20px;width:20px}svg{pointer-events:none}:host ::ng-deep .node-box{fill-opacity:.3;stroke-opacity:.3;pointer-events:auto}:host ::ng-deep .node-openable:not(.mask-openable){cursor:pointer;stroke-opacity:1;fill-opacity:1}:host ::ng-deep .group-button,:host ::ng-deep .group-button-text{display:inline!important;cursor:pointer}:host ::ng-deep .node-label tspan{alignment-baseline:inherit}:host ::ng-deep .sibling-mask{pointer-events:none}:host ::ng-deep .mask-openable:hover{fill:none;stroke:none}:host ::ng-deep .mask-openable{cursor:pointer;pointer-events:all}\n"], dependencies: [{ kind: "directive", type: NgbPopover, selector: "[ngbPopover]", inputs: ["animation", "autoClose", "ngbPopover", "popoverTitle", "placement", "popperOptions", "triggers", "positionTarget", "container", "disablePopover", "popoverClass", "popoverContext", "openDelay", "closeDelay"], outputs: ["shown", "hidden"], exportAs: ["ngbPopover"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: ChartExportButtonComponent, selector: "he-chart-export-button", inputs: ["buttonClass", "chart", "config", "exportFormats", "chartExportFn"] }] }); }
|
|
16203
14781
|
}
|
|
@@ -16206,13 +14784,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
16206
14784
|
args: [{ selector: 'he-hierarchy-chart', imports: [NgbPopover, HESvgIconComponent, ChartExportButtonComponent], template: "<div class=\"chart-area-border\">\n <div\n class=\"is-flex is-justify-content-center is-align-items-center is-flex-wrap-wrap is-gap-16 chart-area-border is-legend\">\n @for (legendItem of legend(); track legendItem.type) {\n <div class=\"is-flex is-align-items-center is-gap-8 | is-legend-item\">\n <span\n class=\"is-inline-block-tablet is-align-middle | key-colour\"\n [style.backgroundColor]=\"nodeColours[legendItem.type]\"\n [style.borderColor]=\"nodeBorderColours[legendItem.type]\"></span>\n <span class=\"is-size-7\">{{ legendItem.text }}</span>\n </div>\n }\n </div>\n\n <div class=\"chart-container is-relative w-100 is-mt-2\">\n <svg class=\"w-100 h-100\" #chart></svg>\n\n <span\n class=\"is-hidden\"\n [ngbPopover]=\"tipContent\"\n triggers=\"manual\"\n placement=\"right left auto\"\n container=\"body\"\n #t=\"ngbPopover\"\n positionTarget=\".tip-target\"\n popoverClass=\"is-narrow driver-chart-tooltip\"\n [animation]=\"true\"></span>\n\n <div class=\"is-absolute | chart-controls\">\n <div class=\"is-flex is-flex-direction-column is-align-items-flex-end is-gap-4\">\n <he-chart-export-button\n buttonClass=\"button is-small\"\n [chartExportFn]=\"chartExportFn\"\n [exportFormats]=\"exportFormats\" />\n </div>\n </div>\n </div>\n</div>\n\n<ng-template #tipContent let-tipData=\"tipData\">\n @if (tipData.hasWeightedValue) {\n <p>contribution: {{ tipData.weightedValueText }}</p>\n }\n @if (tipData.valueValueText) {\n <p>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.valueKeyHref\">value</a>\n <span class=\"is-pr-1\">:</span>\n <span>{{ tipData.valueValueText }}</span>\n @if (tipData.hasValue) {\n <a class=\"is-dark is-ml-2\" [href]=\"tipData.modelDocsHref\" target=\"_blank\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </p>\n }\n @if (tipData.modelValue) {\n <p>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.modelKeyHref\">methodModel</a>\n <span class=\"is-pr-1\">:</span>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.modelValueHref\">{{ tipData.modelValue }}</a>\n </p>\n }\n @if (tipData.terms?.length) {\n <p>\n <a class=\"is-dark\" target=\"_blank\" [href]=\"tipData.termKeyHref\">terms</a>\n <span class=\"is-pr-1\">:</span>\n @for (term of tipData.terms; track term.text; let lastTerm = $last) {\n <a class=\"is-dark\" target=\"_blank\" [href]=\"term.href\">{{ term.text }}</a>\n @if (!lastTerm) {\n <span class=\"is-px-1\">;</span>\n }\n }\n </p>\n }\n</ng-template>\n", styles: [".chart-controls{top:0;right:0}.chart-controls .button,.chart-controls he-chart-export-button ::ng-deep>.button{width:36px;height:36px}.key-colour{border-radius:3px;border:1px solid transparent;height:20px;width:20px}svg{pointer-events:none}:host ::ng-deep .node-box{fill-opacity:.3;stroke-opacity:.3;pointer-events:auto}:host ::ng-deep .node-openable:not(.mask-openable){cursor:pointer;stroke-opacity:1;fill-opacity:1}:host ::ng-deep .group-button,:host ::ng-deep .group-button-text{display:inline!important;cursor:pointer}:host ::ng-deep .node-label tspan{alignment-baseline:inherit}:host ::ng-deep .sibling-mask{pointer-events:none}:host ::ng-deep .mask-openable:hover{fill:none;stroke:none}:host ::ng-deep .mask-openable{cursor:pointer;pointer-events:all}\n"] }]
|
|
16207
14785
|
}], ctorParameters: () => [], propDecorators: { chartRef: [{ type: i0.ViewChild, args: ['chart', { isSignal: true }] }], tooltipOperator: [{ type: i0.ViewChild, args: ['t', { isSignal: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], terms: [{ type: i0.Input, args: [{ isSignal: true, alias: "terms", required: false }] }], chartError: [{ type: i0.Output, args: ["chartError"] }] } });
|
|
16208
14786
|
|
|
16209
|
-
const filterTermTypes$1 = [
|
|
16210
|
-
TermTermType.resourceUse,
|
|
16211
|
-
TermTermType.emission,
|
|
16212
|
-
TermTermType.characterisedIndicator,
|
|
16213
|
-
TermTermType.endpointIndicator,
|
|
16214
|
-
TermTermType.model
|
|
16215
|
-
];
|
|
16216
14787
|
const searchParams = (should) => ({
|
|
16217
14788
|
fields: ['@type', '@id', 'name', 'units', 'termType'],
|
|
16218
14789
|
limit: 9999, // do not use scroll
|
|
@@ -16224,89 +14795,13 @@ const searchParams = (should) => ({
|
|
|
16224
14795
|
}
|
|
16225
14796
|
}
|
|
16226
14797
|
});
|
|
16227
|
-
const defaultIndicatorsModelId = 'defaultIndicatorsModelId';
|
|
16228
|
-
const defaultIndicatorsModelName = 'Default Indicators (multi-model)';
|
|
16229
|
-
const warnings = (logs, terms) => [!logs.every(log => terms[log.indicator] && terms[log.contributor]) && 'missing-terms'].filter(Boolean);
|
|
16230
|
-
const parseLogValues = (impactAssessment, values) => {
|
|
16231
|
-
const log = values[0];
|
|
16232
|
-
const { weightedTotal, total } = values.reduce((prev, curr) => ({
|
|
16233
|
-
weightedTotal: prev.weightedTotal + curr.weightedValue,
|
|
16234
|
-
total: prev.total + curr.value
|
|
16235
|
-
}), { weightedTotal: 0, total: 0 });
|
|
16236
|
-
const inputsObj = (impactAssessment.emissionsResourceUse || [])
|
|
16237
|
-
.filter(eru => eru.term['@id'] === log.contributor && eru.inputs?.length)
|
|
16238
|
-
.reduce((acc, eru) => {
|
|
16239
|
-
const inputId = eru.inputs.map(i => i['@id']).join(';');
|
|
16240
|
-
const currentOperations = eru.operation
|
|
16241
|
-
? [
|
|
16242
|
-
{
|
|
16243
|
-
id: eru.operation['@id'],
|
|
16244
|
-
...getUnitsAndLabel(eru.operation),
|
|
16245
|
-
modelId: log.modelId,
|
|
16246
|
-
weightedValue: eru.value
|
|
16247
|
-
}
|
|
16248
|
-
]
|
|
16249
|
-
: [];
|
|
16250
|
-
acc[inputId] = acc[inputId]
|
|
16251
|
-
? {
|
|
16252
|
-
...acc[inputId],
|
|
16253
|
-
weightedValue: acc[inputId].weightedValue + eru.value,
|
|
16254
|
-
modelId: log.modelId,
|
|
16255
|
-
operations: [...acc[inputId].operations, ...currentOperations]
|
|
16256
|
-
}
|
|
16257
|
-
: {
|
|
16258
|
-
id: inputId,
|
|
16259
|
-
...(!eru.inputs?.length ? { label: 'Input group (under development)' } : getUnitsAndLabel(eru.inputs)),
|
|
16260
|
-
modelId: log.modelId,
|
|
16261
|
-
weightedValue: eru.value,
|
|
16262
|
-
operations: [...currentOperations]
|
|
16263
|
-
};
|
|
16264
|
-
return acc;
|
|
16265
|
-
}, {});
|
|
16266
|
-
const inputs = Object.values(inputsObj);
|
|
16267
|
-
return { ...log, weightedValue: weightedTotal, value: total, inputs };
|
|
16268
|
-
};
|
|
16269
|
-
const processContributions = (record, nodes) => Object.entries(record || {}).flatMap(([index, contributions]) => {
|
|
16270
|
-
const indicator = nodes[+index];
|
|
16271
|
-
return indicator
|
|
16272
|
-
? Object.entries(contributions).flatMap(([impactTermId, modelContr]) => Object.entries(modelContr).map(([modelId, weightedValue]) => {
|
|
16273
|
-
const contributor = indicator.term['@id'];
|
|
16274
|
-
const value = indicator.value || 0;
|
|
16275
|
-
const coefficient = value !== 0 ? weightedValue / value : 0;
|
|
16276
|
-
// only get contributions with positive coefficient
|
|
16277
|
-
return coefficient > 0
|
|
16278
|
-
? {
|
|
16279
|
-
indicator: impactTermId,
|
|
16280
|
-
contributor,
|
|
16281
|
-
coefficient,
|
|
16282
|
-
modelId,
|
|
16283
|
-
weightedValue,
|
|
16284
|
-
value
|
|
16285
|
-
}
|
|
16286
|
-
: null;
|
|
16287
|
-
}))
|
|
16288
|
-
: null;
|
|
16289
|
-
});
|
|
16290
|
-
const parseContributions = (impactAssessment, contributions) => {
|
|
16291
|
-
const allLogs = [
|
|
16292
|
-
...processContributions(contributions.emissionsResourceUse, impactAssessment.emissionsResourceUse || []),
|
|
16293
|
-
...processContributions(contributions.impacts, impactAssessment.impacts || [])
|
|
16294
|
-
].filter(Boolean);
|
|
16295
|
-
const grouped = allLogs.reduce((acc, log) => {
|
|
16296
|
-
const key = [log.indicator, log.contributor, log.modelId].join('/');
|
|
16297
|
-
acc[key] = acc[key] || [];
|
|
16298
|
-
acc[key].push(log);
|
|
16299
|
-
return acc;
|
|
16300
|
-
}, {});
|
|
16301
|
-
return Object.values(grouped).map(logs => parseLogValues(impactAssessment, logs));
|
|
16302
|
-
};
|
|
16303
14798
|
class ImpactAssessmentsGraphComponent {
|
|
16304
14799
|
constructor() {
|
|
16305
14800
|
this.searchService = inject(HeSearchService);
|
|
16306
14801
|
this.nodeService = inject(HeNodeService);
|
|
16307
14802
|
this.emissionCategoryService = inject(CyclesEmissionsCategoryService);
|
|
16308
14803
|
this.initialTerms = toSignal(this.searchService
|
|
16309
|
-
.search$(searchParams(
|
|
14804
|
+
.search$(searchParams(hierarchyTermTypes.map(termType => matchTermType(termType))))
|
|
16310
14805
|
.pipe(mergeMap(({ results }) => from(results).pipe(map(term => {
|
|
16311
14806
|
const modelDocs = term.termType === TermTermType.model ? findMatchingModel({ model: term['@id'] }) : null;
|
|
16312
14807
|
return {
|
|
@@ -16329,16 +14824,14 @@ class ImpactAssessmentsGraphComponent {
|
|
|
16329
14824
|
}),
|
|
16330
14825
|
stream: ({ params: { selectedImpactAssessment, isRecalculated } }) => !selectedImpactAssessment || !isRecalculated
|
|
16331
14826
|
? of([])
|
|
16332
|
-
: this.nodeService.getContributions$(selectedImpactAssessment).pipe(map(contributions =>
|
|
14827
|
+
: this.nodeService.getContributions$(selectedImpactAssessment).pipe(map(contributions => hierarchyContributions(selectedImpactAssessment, contributions)), catchError(err => {
|
|
16333
14828
|
console.error(err);
|
|
16334
14829
|
this.error.set(err.toString());
|
|
16335
14830
|
return of([]);
|
|
16336
14831
|
}))
|
|
16337
14832
|
});
|
|
16338
14833
|
this.nodeLogs = computed(() => this.nodeLogsResource.value() ?? [], ...(ngDevMode ? [{ debugName: "nodeLogs" }] : []));
|
|
16339
|
-
this.logTermIds = computed(() =>
|
|
16340
|
-
.flatMap(l => [l.contributor, l.indicator, ...l.inputs.map(i => i.id)])
|
|
16341
|
-
.filter(termId => !this.initialTerms()?.[termId])), ...(ngDevMode ? [{ debugName: "logTermIds" }] : []));
|
|
14834
|
+
this.logTermIds = computed(() => missingTermIds(this.nodeLogs(), this.initialTerms()), ...(ngDevMode ? [{ debugName: "logTermIds" }] : []));
|
|
16342
14835
|
this.allTermsResource = rxResource({
|
|
16343
14836
|
params: () => ({
|
|
16344
14837
|
logTermIds: this.logTermIds(),
|
|
@@ -16355,40 +14848,14 @@ class ImpactAssessmentsGraphComponent {
|
|
|
16355
14848
|
});
|
|
16356
14849
|
this.allTerms = computed(() => this.allTermsResource.value() ?? {}, ...(ngDevMode ? [{ debugName: "allTerms" }] : []));
|
|
16357
14850
|
this.noData = computed(() => !this.nodeLogs()?.length, ...(ngDevMode ? [{ debugName: "noData" }] : []));
|
|
16358
|
-
this.warnings = computed(() => [this.nodeLogs(), this.allTerms()].some(v => isEmpty(v)) ? [] :
|
|
14851
|
+
this.warnings = computed(() => [this.nodeLogs(), this.allTerms()].some(v => isEmpty(v)) ? [] : hierarchyWarnings(this.nodeLogs(), this.allTerms()), ...(ngDevMode ? [{ debugName: "warnings" }] : []));
|
|
16359
14852
|
this.showWarnings = signal(false, ...(ngDevMode ? [{ debugName: "showWarnings" }] : []));
|
|
16360
14853
|
this.error = signal(undefined, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
16361
14854
|
this.selectedModelId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedModelId" }] : []));
|
|
16362
|
-
this.
|
|
16363
|
-
this.models = computed(() => [
|
|
16364
|
-
...(this.hasDefaultModelLogs() ? [{ '@id': defaultIndicatorsModelId, name: defaultIndicatorsModelName }] : []),
|
|
16365
|
-
...unique(this.nodeLogs().map(({ modelId }) => this.allTerms()[modelId] || { '@id': modelId }))
|
|
16366
|
-
], ...(ngDevMode ? [{ debugName: "models" }] : []));
|
|
14855
|
+
this.models = computed(() => hierarchyModels(this.nodeLogs(), this.allTerms()), ...(ngDevMode ? [{ debugName: "models" }] : []));
|
|
16367
14856
|
this.categories = this.emissionCategoryService.categories;
|
|
16368
14857
|
this.selectedCategory = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedCategory" }] : []));
|
|
16369
|
-
this.chartData = computed(() => {
|
|
16370
|
-
const category = this.selectedCategory();
|
|
16371
|
-
const categoryLabel = category ? this.emissionCategoryService.categoryLabel(category) : undefined;
|
|
16372
|
-
return this.nodeLogs()
|
|
16373
|
-
.filter(log => log.modelId ===
|
|
16374
|
-
(this.selectedModelId() === defaultIndicatorsModelId
|
|
16375
|
-
? getDefaultModelId(log.indicator)
|
|
16376
|
-
: this.selectedModelId()))
|
|
16377
|
-
.map(log => {
|
|
16378
|
-
const includeCategory = category &&
|
|
16379
|
-
[TermTermType.emission, TermTermType.resourceUse].includes(this.allTerms()[log.contributor]?.termType);
|
|
16380
|
-
const categoryValue = includeCategory
|
|
16381
|
-
? this.emissionCategoryService.emissionCategoryValue(log.contributor, category)
|
|
16382
|
-
: null;
|
|
16383
|
-
return includeCategory
|
|
16384
|
-
? {
|
|
16385
|
-
...log,
|
|
16386
|
-
category: categoryValue || `Not available for ${categoryLabel}`,
|
|
16387
|
-
categoryLabel
|
|
16388
|
-
}
|
|
16389
|
-
: log;
|
|
16390
|
-
});
|
|
16391
|
-
}, ...(ngDevMode ? [{ debugName: "chartData" }] : []));
|
|
14858
|
+
this.chartData = computed(() => hierarchyData(this.nodeLogs(), this.allTerms(), this.selectedModelId(), this.selectedCategory()), ...(ngDevMode ? [{ debugName: "chartData" }] : []));
|
|
16392
14859
|
this.loading = computed(() => [this.nodeLogsResource.isLoading(), this.allTermsResource.isLoading()].some(Boolean), ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
16393
14860
|
this.showChart = computed(() => [!isEmpty(this.chartData()), !isEmpty(this.allTerms()), !this.loading(), !this.error(), !this.showWarnings()].every(Boolean), ...(ngDevMode ? [{ debugName: "showChart" }] : []));
|
|
16394
14861
|
effect(() => {
|
|
@@ -16430,7 +14897,7 @@ class ImpactAssessmentsIndicatorBreakdownChartComponent {
|
|
|
16430
14897
|
}),
|
|
16431
14898
|
stream: ({ params: { impactAssessment } }) => this.nodeService
|
|
16432
14899
|
.getContributions$(impactAssessment)
|
|
16433
|
-
.pipe(map(contributions => parseContributions
|
|
14900
|
+
.pipe(map(contributions => parseContributions(impactAssessment, contributions)))
|
|
16434
14901
|
});
|
|
16435
14902
|
this.logs = computed(() => indicatorLogs(this.logsResource.value() ?? [], this.selectedTermId(), this.selectedMethodId()), ...(ngDevMode ? [{ debugName: "logs" }] : []));
|
|
16436
14903
|
this.nonZeroLogs = computed(() => nonZeroContributors(this.logs()), ...(ngDevMode ? [{ debugName: "nonZeroLogs" }] : []));
|
|
@@ -16469,7 +14936,7 @@ class ImpactAssessmentsIndicatorBreakdownChartComponent {
|
|
|
16469
14936
|
this.chartData = computed(() => breakdownBars(this.values(), this.selectedCategory()), ...(ngDevMode ? [{ debugName: "chartData" }] : []));
|
|
16470
14937
|
this.chartTooltipFn = ({ includedItems, index }) => breakdownTooltip(this.chartData()[index], includedItems, this.nonZeroLogs(), this.total(), this.maximumValues());
|
|
16471
14938
|
this.afterBarDrawSettings = computed(() => breakdownBarText(this.displayValue(), this.total(), this.maximumValues()), ...(ngDevMode ? [{ debugName: "afterBarDrawSettings" }] : []));
|
|
16472
|
-
this.configuration = computed(() => breakdownScale(this.values()), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
14939
|
+
this.configuration = computed(() => breakdownScale(this.values(), this.selectedCategory()), ...(ngDevMode ? [{ debugName: "configuration" }] : []));
|
|
16473
14940
|
this.exportFormats = [
|
|
16474
14941
|
...exportFormats,
|
|
16475
14942
|
{
|
|
@@ -16477,7 +14944,7 @@ class ImpactAssessmentsIndicatorBreakdownChartComponent {
|
|
|
16477
14944
|
label: 'Data'
|
|
16478
14945
|
}
|
|
16479
14946
|
];
|
|
16480
|
-
this.csvContent = computed(() => `data:text/html;charset=utf-8,${encodeURIComponent(logToCsv$
|
|
14947
|
+
this.csvContent = computed(() => `data:text/html;charset=utf-8,${encodeURIComponent(logToCsv$1(this.logs(), this.impactAssessment()))}`, ...(ngDevMode ? [{ debugName: "csvContent" }] : []));
|
|
16481
14948
|
this.chartExportFn = async (format, chart) => {
|
|
16482
14949
|
return {
|
|
16483
14950
|
png: () => chart.exportAsPng(),
|
|
@@ -17514,5 +15981,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
17514
15981
|
* Generated bundle index. Do not edit.
|
|
17515
15982
|
*/
|
|
17516
15983
|
|
|
17517
|
-
export { ARRAY_DELIMITER, ApplyPurePipe, BarChartComponent, BibliographiesSearchConfirmComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, CapitalizePipe, ChartComponent, ChartConfigurationDirective, ChartExportButtonComponent, ChartTooltipComponent, ClickOutsideDirective, ClipboardComponent, CollapsibleBlockComponent, CollapsibleBoxComponent, CollapsibleBoxStyle, CompoundDirective, CompoundPipe, ContributionChartComponent, ControlValueAccessor, CycleNodesKeyGroup, CyclesCompletenessComponent, CyclesEmissionsCategoryService, CyclesEmissionsChartComponent, CyclesFunctionalUnitMeasureComponent, CyclesMetadataComponent, CyclesNodesComponent, CyclesNodesTimelineComponent, CyclesResultComponent, DataTableComponent, DefaultPipe, DeltaColour, DistributionChartComponent, DrawerContainerComponent, DurationPipe, EllipsisPipe, EngineModelsLinkComponent, EngineModelsLookupInfoComponent, EngineModelsStageComponent, EngineModelsStageDeepComponent, EngineModelsStageDeepService, EngineModelsVersionInfoComponent, EngineModelsVersionLinkComponent, EngineOrchestratorEditComponent, EngineRequirementsFormComponent, FileSizePipe, FileUploadErrorKeys, FilesDragDropDirective, FilesDropZoneComponent, FilesErrorSummaryComponent, FilesFormComponent, FilesFormEditableComponent, FilesUploadErrorsComponent, FilterAccordionComponent, FormulaBlockComponent, GUIDE_ENABLED, GetPipe, GlossaryMigrationFormat, GuideOverlayComponent, HE_API_BASE_URL, HE_CALCULATIONS_BASE_URL, HE_MAP_LOADED, HE_REGION_GEOJSON_BASE_URL, HeAuthService, HeCommonService, HeEngineService, HeGlossaryService, HeMendeleyService, HeNodeCsvService, HeNodeService, HeNodeStoreService, HeSchemaService, HeSearchService, HeToastService, HorizontalBarChartComponent, HorizontalButtonsGroupComponent, ImpactAssessmentsGraphComponent, ImpactAssessmentsIndicatorBreakdownChartComponent, ImpactAssessmentsIndicatorsChartComponent, ImpactAssessmentsProductsComponent, IsArrayPipe, IsObjectPipe, IssueConfirmComponent, KeyToLabelPipe, Level, LineChartComponent, LinkKeyValueComponent, LogStatus, LongPressDirective, MAX_RESULTS, MapsDrawingComponent, MapsDrawingConfirmComponent, MaxPipe, MeanPipe, MedianPipe, MendeleySearchResult, MinPipe, MobileShellComponent, NavigationMenuComponent, NoExtPipe, NodeAggregatedComponent, NodeAggregatedFormulasComponent, NodeAggregatedInfoComponent, NodeAggregatedQualityScoreComponent, NodeAggregationLogsComponent, NodeCsvExportConfirmComponent, NodeCsvPreviewComponent, NodeCsvSelectHeadersComponent, NodeIconComponent, NodeJLogModelsComponent, NodeJsonldComponent, NodeJsonldSchemaComponent, NodeKeyState, NodeLinkComponent, NodeLogsFileComponent, NodeLogsModelsComponent, NodeLogsTimeComponent, NodeMissingLookupFactorsComponent, NodeQualityScore, NodeRecommendationsComponent, NodeSelectComponent, NodeValueDetailsComponent, PipelineStagesProgressComponent, PluralizePipe, PopoverComponent, PopoverConfirmComponent, PrecisionPipe, RelatedNodeResult, RemoveMarkdownPipe, RepeatPipe, Repository, ResizedDirective, ResizedEvent, ResponsiveService, SchemaInfoComponent, SchemaVersionLinkComponent, SearchExtendComponent, ShelfDialogComponent, ShellComponent, SiteNodesKeyGroup, SitesManagementChartComponent, SitesMapsComponent, SitesNodesComponent, SkeletonTextComponent, SocialTagsComponent, SortByPipe, SortSelectComponent, SumPipe, TagsInputDirective, Template, TermsPropertyContentComponent, TermsSubClassOfContentComponent, TermsUnitsDescriptionComponent, ThousandSuffixesPipe, ThousandsPipe, TimesPipe, ToastComponent, UncapitalizePipe, addPolygonToFeature, aggregatedSitePrimaryProduct, allCountriesQuery, allGroups, allOptions, availableProperties, axisLabelSettings, backgroundModelParams, baseApiUrl, baseUrl, bottom, buildSummary, bytesSize, calculateCycleDuration, calculateCycleDurationEnabled, calculateCycleStartDate, calculateCycleStartDateEnabled, capitalize, changelogUrl, clustererImage, code, compoundToHtml, computeKeys, computeTerms, contactUsEmail, contactUsLink,
|
|
15984
|
+
export { ARRAY_DELIMITER, ApplyPurePipe, BarChartComponent, BibliographiesSearchConfirmComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, CapitalizePipe, ChartComponent, ChartConfigurationDirective, ChartExportButtonComponent, ChartTooltipComponent, ClickOutsideDirective, ClipboardComponent, CollapsibleBlockComponent, CollapsibleBoxComponent, CollapsibleBoxStyle, CompoundDirective, CompoundPipe, ContributionChartComponent, ControlValueAccessor, CycleNodesKeyGroup, CyclesCompletenessComponent, CyclesEmissionsCategoryService, CyclesEmissionsChartComponent, CyclesFunctionalUnitMeasureComponent, CyclesMetadataComponent, CyclesNodesComponent, CyclesNodesTimelineComponent, CyclesResultComponent, DataTableComponent, DefaultPipe, DeltaColour, DistributionChartComponent, DrawerContainerComponent, DurationPipe, EllipsisPipe, EngineModelsLinkComponent, EngineModelsLookupInfoComponent, EngineModelsStageComponent, EngineModelsStageDeepComponent, EngineModelsStageDeepService, EngineModelsVersionInfoComponent, EngineModelsVersionLinkComponent, EngineOrchestratorEditComponent, EngineRequirementsFormComponent, FileSizePipe, FileUploadErrorKeys, FilesDragDropDirective, FilesDropZoneComponent, FilesErrorSummaryComponent, FilesFormComponent, FilesFormEditableComponent, FilesUploadErrorsComponent, FilterAccordionComponent, FormulaBlockComponent, GUIDE_ENABLED, GetPipe, GlossaryMigrationFormat, GuideOverlayComponent, HE_API_BASE_URL, HE_CALCULATIONS_BASE_URL, HE_MAP_LOADED, HE_REGION_GEOJSON_BASE_URL, HeAuthService, HeCommonService, HeEngineService, HeGlossaryService, HeMendeleyService, HeNodeCsvService, HeNodeService, HeNodeStoreService, HeSchemaService, HeSearchService, HeToastService, HorizontalBarChartComponent, HorizontalButtonsGroupComponent, ImpactAssessmentsGraphComponent, ImpactAssessmentsIndicatorBreakdownChartComponent, ImpactAssessmentsIndicatorsChartComponent, ImpactAssessmentsProductsComponent, IsArrayPipe, IsObjectPipe, IssueConfirmComponent, KeyToLabelPipe, Level, LineChartComponent, LinkKeyValueComponent, LogStatus, LongPressDirective, MAX_RESULTS, MapsDrawingComponent, MapsDrawingConfirmComponent, MaxPipe, MeanPipe, MedianPipe, MendeleySearchResult, MinPipe, MobileShellComponent, NavigationMenuComponent, NoExtPipe, NodeAggregatedComponent, NodeAggregatedFormulasComponent, NodeAggregatedInfoComponent, NodeAggregatedQualityScoreComponent, NodeAggregationLogsComponent, NodeCsvExportConfirmComponent, NodeCsvPreviewComponent, NodeCsvSelectHeadersComponent, NodeIconComponent, NodeJLogModelsComponent, NodeJsonldComponent, NodeJsonldSchemaComponent, NodeKeyState, NodeLinkComponent, NodeLogsFileComponent, NodeLogsModelsComponent, NodeLogsTimeComponent, NodeMissingLookupFactorsComponent, NodeQualityScore, NodeRecommendationsComponent, NodeSelectComponent, NodeValueDetailsComponent, PipelineStagesProgressComponent, PluralizePipe, PopoverComponent, PopoverConfirmComponent, PrecisionPipe, RelatedNodeResult, RemoveMarkdownPipe, RepeatPipe, Repository, ResizedDirective, ResizedEvent, ResponsiveService, SchemaInfoComponent, SchemaVersionLinkComponent, SearchExtendComponent, ShelfDialogComponent, ShellComponent, SiteNodesKeyGroup, SitesManagementChartComponent, SitesMapsComponent, SitesNodesComponent, SkeletonTextComponent, SocialTagsComponent, SortByPipe, SortSelectComponent, SumPipe, TagsInputDirective, Template, TermsPropertyContentComponent, TermsSubClassOfContentComponent, TermsUnitsDescriptionComponent, ThousandSuffixesPipe, ThousandsPipe, TimesPipe, ToastComponent, UncapitalizePipe, addPolygonToFeature, aggregatedSitePrimaryProduct, allCountriesQuery, allGroups, allOptions, availableProperties, axisLabelSettings, backgroundModelParams, baseApiUrl, baseUrl, bottom, buildSummary, bytesSize, calculateCycleDuration, calculateCycleDurationEnabled, calculateCycleStartDate, calculateCycleStartDateEnabled, capitalize, changelogUrl, clustererImage, code, compoundToHtml, computeKeys, computeTerms, contactUsEmail, contactUsLink, coordinatesToPoint, copyObject, countGroupVisibleNodes, countriesQuery, createMarker, cropsQuery, dataPathLabel, dataPathToKey, dataVersionHeader, dataVersionHeaderKey, defaultFeature, defaultLabel, defaultSuggestionType, definitionToSchemaType, distinctUntilChangedDeep, ellipsis, engineGitBaseUrl, engineGitUrl, errorText, errorsLabel, evaluateSuccess, exportFormats, externalLink, externalNodeLink, fillColor, fillStyle, filterBlankNode$1 as filterBlankNode, filterParams, findBackgroundModel, findConfigModels, findMatchingModel, findModels, findNodeModel, findOrchestratorModel, findProperty, findPropertyById, findTermModels, flatFilterData, flatFilterNode, formatCustomErrorMessage, formatDate, formatError, formatPropertyError, formatter$1 as formatter, getDatesBetween, gitBranch, gitHome, glossaryBaseUrl, glossaryLink, groupBlankNodesByTermIdentity, groupChanged, groupJLogByField, groupJLogByTerm, groupLogsByTerm, groupNodesByTerm, groupdLogsByKey, grouppedKeys, grouppedValueKeys, groupsLogsByFields, guideModelUrl, guideNamespace, handleAPIError, handleGuideEvent, hasError, hasValidationError, hasWarning, highestMethodTier, ignoreKeys$2 as ignoreKeys, impactAssessmentProduct, initialFilterState, injectResizeEvent$, inputGroupsTermTypes, isAddPropertyEnabled, isChrome, isDateBetween, isEqual, isExternal, isIdenticalNodeValue, isKeyClosedVisible, isKeyHidden, isMaxStage, isMethodModelAllowed, isNonNodeModelKey, isSchemaIri, isScrolledBelow, isState, isTermTypeAllowed, isValidKey, jLogModelCount, keyToDataPath, levels, loadMapApi, locationQuery, logToCsv, logValueArray, logsKey, lookupUrl, mapFilterData, mapsUrl, markerIcon, markerPie, matchAggregatedQuery, matchAggregatedValidatedQuery, matchBoolPrefixQuery, matchCountry, matchExactQuery, matchGlobalRegion, matchId, matchNameNormalized, matchNestedKey, matchPhrasePrefixQuery, matchPhraseQuery, matchPrimaryProductQuery, matchQuery, matchRegex, matchRegion, matchTermType, matchType, maxAreaSize, measurementValue, mergeDataWithHeaders, methodTierOrder, migrationErrorMessage, migrationsUrl, modelCount, modelKeyParams, modelParams, models, multiMatchQuery, nestedProperty, nestingEnabled, nestingTypeEnabled, noValue, nodeAvailableProperties, nodeById, nodeChartLabels, nodeColours, nodeDataState, nodeDataStates, nodeDataVersion, nodeId, nodeIdWithoutDataVersion, nodeIds, nodeLink, nodeLinkEnabled, nodeLinkTypeEnabled, nodeLogsUrl, nodeQualityScoreColor, nodeQualityScoreLevel, nodeQualityScoreMaxDefault, nodeQualityScoreOrder, nodeRequestId, nodeSecondaryColours, nodeToAggregationFilename, nodeType, nodeTypeDataState, nodeTypeIcon, nodeTypeIconSchema, nodeUrl, nodeUrlParams, nodeVersion, nodeVersionKey, nodesByState, nodesByType, numberGte, openNodeUrl, openUrl, optionsFromGroup, parentKey, parentProperty, parseData, parseDataPath, parseLines, parseMessage, parseNewValue, pluralize, pointToCoordinates, polygonBounds, polygonToCoordinates, polygonToMap, polygonsFromFeature, populateWithTrackIdsFilterData, postGuideEvent, primaryProduct, productsQuery, propertyError, propertyId, recursiveProperties, refToSchemaType, refreshPropertyKeys, regionsQuery, repeat, reportIssueLink, reportIssueUrl, safeJSONParse, safeJSONStringify, schemaBaseUrl, schemaDataBaseUrl, schemaLink, schemaRequiredProperties, schemaTypeToDefaultValue, scrollToEl, scrollTop, searchFilterData, searchableTypes, siblingProperty, simplifyContributions, singleProperty, siteTooBig, siteTypeToColor, siteTypeToIcon, sortProperties, sortedDates, strokeColor, strokeStyle, subValueKeys, suggestMatchQuery, suggestQuery, suggestTerms, takeAfterViewInit, termLocation, termLocationName, termProperties, termSearch, termTypeLabel, toSnakeCase, toTextParts, toThousands, typeToNewProperty, typeaheadFocus, uncapitalize, uniqueDatesBetween, updateProperties, valueLink, valueToString, valueTypeToDefault, valueValue, waitFor, wildcardQuery };
|
|
17518
15985
|
//# sourceMappingURL=hestia-earth-ui-components.mjs.map
|