@hestia-earth/ui-components 0.43.20 → 0.43.22

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.
@@ -95,6 +95,7 @@ const gitBranch = () => (['dev', 'staging'].some(env => baseUrl().includes(env))
95
95
  */
96
96
  const windowRef = () => (typeof window === 'undefined' ? undefined : window);
97
97
  const origin = () => windowRef()?.location?.origin ?? '';
98
+ const openUrl = (url, target = '_self') => windowRef()?.open(url, target);
98
99
  const isChrome = () => windowRef()?.navigator?.userAgent?.includes('Chrome') ?? false;
99
100
  const baseUrl = (allowLocalhost = true) => origin().includes('localhost')
100
101
  ? allowLocalhost
@@ -173,6 +174,26 @@ const nodeDefaultLabel = {
173
174
  const defaultLabel = (node) => node
174
175
  ? (node['@type'] in nodeDefaultLabel ? nodeDefaultLabel[node['@type']](node) : node.name) || node['@id'] || node.id
175
176
  : '';
177
+ const labelWithId = (label, id, maxLength) => {
178
+ const suffix = ` (${id})`;
179
+ // `ellipsis` appends 3 characters to the length it is given
180
+ return `${ellipsis(label, Math.max(maxLength - suffix.length - 3, 0))}${suffix}`;
181
+ };
182
+ /**
183
+ * Numbered labels for a list of nodes compared on a chart.
184
+ * Nodes sharing a name are indistinguishable once drawn, so the `@id` is appended to every label
185
+ * that is not unique. The name is shortened to make room for it, as a label longer than `maxLength`
186
+ * is truncated by the chart and would lose the `@id` it was given.
187
+ */
188
+ const nodeChartLabels = (nodes, maxLength) => {
189
+ const names = (nodes || []).map(node => defaultLabel(node));
190
+ const duplicates = names.filter((name, index) => names.indexOf(name) !== index);
191
+ return names.map((name, index) => {
192
+ const label = `${index + 1}. ${name}`;
193
+ const id = nodes[index]?.['@id'];
194
+ return duplicates.includes(name) && id ? labelWithId(label, id, maxLength) : label;
195
+ });
196
+ };
176
197
  /**
177
198
  * Stable `@for` track key for a node that uniquely identifies it across data versions.
178
199
  * Two nodes with the same `@id` but a different `dataVersion` (e.g. current vs a compared version)
@@ -1057,6 +1078,9 @@ const methodTierOrder = (methodTier) => [
1057
1078
  EmissionMethodTier.background,
1058
1079
  EmissionMethodTier['not relevant']
1059
1080
  ].indexOf(methodTier);
1081
+ // an emission also produced by a Transformation is written on the Cycle twice, the second one
1082
+ // "not relevant" with a value of 0 - the tier holding the real value is the one to report under
1083
+ const highestMethodTier = (methodTiers) => methodTiers.filter(Boolean).sort((a, b) => methodTierOrder(a) - methodTierOrder(b))[0];
1060
1084
  const grouppedValueKeys = [
1061
1085
  'dates',
1062
1086
  'sd',
@@ -1086,12 +1110,14 @@ const groupNodesByTerm = (nodes = [], key, includeNode = (_node) => true, hideZe
1086
1110
  // (same `@id`, different `dataVersion`) are grouped separately instead of merged
1087
1111
  const nodeId = nodeVersionKey(node);
1088
1112
  const groupedKey = groupNodeKey(blankNode);
1089
- group[groupedKey] = group[groupedKey] || {
1113
+ const methodTier = highestMethodTier([group[groupedKey]?.methodTier, blankNode.methodTier]);
1114
+ group[groupedKey] = {
1090
1115
  visible: true,
1091
1116
  term: blankNode.term,
1092
- methodTier: blankNode.methodTier,
1093
- methodTierOrder: methodTierOrder(blankNode.methodTier),
1094
- values: {}
1117
+ values: {},
1118
+ ...group[groupedKey],
1119
+ methodTier,
1120
+ methodTierOrder: methodTierOrder(methodTier)
1095
1121
  };
1096
1122
  group[groupedKey].values[nodeId] = group[groupedKey].values[nodeId] || {
1097
1123
  index,
@@ -3657,6 +3683,10 @@ const termKeyToLabel = {
3657
3683
  agrovoc: 'AGROVOC'
3658
3684
  };
3659
3685
  const termTypeLabel = (value = 'N/A') => termTypeToLabel[value] || termKeyToLabel[value] || keyToLabel(value);
3686
+ // glossary names are punctuated ("Land transformation, 20 year average") and a reader typing one
3687
+ // leaves the punctuation out, so neither side keeps it
3688
+ const termSearch = (term = '') => term.replace(/[,\s]/g, '').toLowerCase();
3689
+ const suggestTerms = (terms, value) => terms.filter(({ name }) => termSearch(name).includes(termSearch(value)));
3660
3690
 
3661
3691
  class KeyToLabelPipe {
3662
3692
  transform(value) {
@@ -5899,18 +5929,18 @@ class ChartExportButtonComponent {
5899
5929
  this.close();
5900
5930
  }
5901
5931
  open() {
5902
- this.modalService.open(this.modal());
5932
+ this.modalService.open(this.modal(), { windowClass: 'is-above-popover' });
5903
5933
  }
5904
5934
  close() {
5905
5935
  this.exportFormat.set(undefined);
5906
5936
  this.modalService.dismissAll();
5907
5937
  }
5908
5938
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ChartExportButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5909
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ChartExportButtonComponent, isStandalone: true, selector: "he-chart-export-button", inputs: { buttonClass: { classPropertyName: "buttonClass", publicName: "buttonClass", isSignal: true, isRequired: false, transformFunction: null }, chart: { classPropertyName: "chart", publicName: "chart", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, exportFormats: { classPropertyName: "exportFormats", publicName: "exportFormats", isSignal: true, isRequired: false, transformFunction: null }, chartExportFn: { classPropertyName: "chartExportFn", publicName: "chartExportFn", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true, isSignal: true }], ngImport: i0, 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 <div class=\"modal is-active\">\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", styles: [""], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "directive", type: NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5939
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ChartExportButtonComponent, isStandalone: true, selector: "he-chart-export-button", inputs: { buttonClass: { classPropertyName: "buttonClass", publicName: "buttonClass", isSignal: true, isRequired: false, transformFunction: null }, chart: { classPropertyName: "chart", publicName: "chart", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, exportFormats: { classPropertyName: "exportFormats", publicName: "exportFormats", isSignal: true, isRequired: false, transformFunction: null }, chartExportFn: { classPropertyName: "chartExportFn", publicName: "chartExportFn", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true, isSignal: true }], ngImport: i0, 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", styles: [""], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "directive", type: NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "popperOptions", "triggers", "positionTarget", "container", "disableTooltip", "tooltipClass", "tooltipContext", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5910
5940
  }
5911
5941
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ChartExportButtonComponent, decorators: [{
5912
5942
  type: Component$1,
5913
- 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 <div class=\"modal is-active\">\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" }]
5943
+ 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" }]
5914
5944
  }], 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 }] }] } });
5915
5945
 
5916
5946
  const defaultSettings$5 = Object.freeze({
@@ -5928,6 +5958,10 @@ const defaultTicksFont = {
5928
5958
  family: 'Lato',
5929
5959
  size: 13
5930
5960
  };
5961
+ /**
5962
+ * Maximum length of an axis label, past which the charts truncate it.
5963
+ */
5964
+ const chartLabelMaxLength = 50;
5931
5965
  class ChartComponent {
5932
5966
  constructor() {
5933
5967
  this.data = input(undefined, ...(ngDevMode ? [{ debugName: "data" }] : []));
@@ -6064,19 +6098,46 @@ const afterBarDrawPlugin = settings => ({
6064
6098
  }
6065
6099
  });
6066
6100
 
6101
+ /**
6102
+ * Hover handler turning the pointer into a cursor over a clickable axis label.
6103
+ */
6104
+ const axisLabelCursor = ({ label, event }) => {
6105
+ const canvas = event?.native?.target;
6106
+ if (label && canvas) {
6107
+ canvas.style.cursor = 'pointer';
6108
+ }
6109
+ };
6110
+ const axisSettings = (chart) => (chart.config.options.plugins?.axisHover || {});
6111
+ const disabled = (chart, { onHoverLabel, onClickLabel }) => !chart.scales.y || (!onHoverLabel && !onClickLabel);
6112
+ const isOverAxis = (scale, event) => event.x != null && event.x >= scale.left && event.x <= scale.right;
6113
+ const tickAt = (scale, y, maxDistance) => {
6114
+ const ticks = scale.getTicks();
6115
+ const position = ticks.findIndex((_, index) => Math.abs(y - scale.getPixelForTick(index)) < maxDistance);
6116
+ return position < 0 ? null : { index: ticks[position].value, y: scale.getPixelForTick(position) };
6117
+ };
6118
+ const labelAt = (chart, index) => (index >= 0 ? chart.data.labels[index] : '');
6119
+ const labelEvent = (chart, event, maxDistance) => {
6120
+ const scale = chart.scales.y;
6121
+ const tick = isOverAxis(scale, event) ? tickAt(scale, event.y, maxDistance) : null;
6122
+ const index = tick ? tick.index : -1;
6123
+ return {
6124
+ label: labelAt(chart, index),
6125
+ index,
6126
+ anchorX: (scale.left + scale.right) / 2,
6127
+ anchorY: (tick ? tick.y : event.y) + maxDistance,
6128
+ event
6129
+ };
6130
+ };
6131
+ const notifyClick = ({ onClickLabel }, data) => data.index >= 0 && data.event.type === 'click' ? onClickLabel?.(data) : undefined;
6067
6132
  const axisHoverPlugin = {
6068
6133
  id: 'axisHover',
6069
- afterEvent(chart, args) {
6070
- const { event } = args;
6071
- const { maxDistance = 15, onHoverLabel } = (chart.config.options.plugins.axisHover ||
6072
- {});
6073
- const yScale = chart.scales.y;
6074
- const isOverYAxis = event.x >= yScale.left && event.x <= yScale.right;
6075
- const closestTick = isOverYAxis
6076
- ? yScale.getTicks().find((tick, index) => Math.abs(event.y - yScale.getPixelForTick(index)) < maxDistance)
6077
- : null;
6078
- const label = closestTick ? chart.data.labels[closestTick.value] : '';
6079
- onHoverLabel({ label, event });
6134
+ afterEvent(chart, { event }) {
6135
+ const settings = axisSettings(chart);
6136
+ if (disabled(chart, settings))
6137
+ return;
6138
+ const data = labelEvent(chart, event, settings.maxDistance ?? 15);
6139
+ settings.onHoverLabel?.(data);
6140
+ notifyClick(settings, data);
6080
6141
  }
6081
6142
  };
6082
6143
 
@@ -6381,6 +6442,30 @@ const increaseScaleLimits = (scale, handleMinScaleZero = true) => {
6381
6442
  scale.max += padding;
6382
6443
  };
6383
6444
 
6445
+ // below the row being read, never on it, and never over the plot beside it
6446
+ const placements = ['bottom', 'top'];
6447
+ /**
6448
+ * Hover and click behaviour for axis labels, which are drawn on the canvas and so can carry
6449
+ * neither a `title` nor a link. The tooltip opens once per label rather than on every mouse move,
6450
+ * and the pointer only becomes a cursor where a click does something.
6451
+ */
6452
+ const axisLabelSettings = (tooltip, tooltipFn, clickFn) => {
6453
+ let openIndex = -1;
6454
+ const moveTooltip = (data) => {
6455
+ openIndex >= 0 && tooltip()?.hide();
6456
+ openIndex = data.index;
6457
+ data.index >= 0 &&
6458
+ tooltip()?.show(data, data.anchorX, data.anchorY, { container: 'body', placement: placements });
6459
+ };
6460
+ return {
6461
+ onHoverLabel: data => {
6462
+ clickFn() && data.label && axisLabelCursor(data);
6463
+ data.index !== openIndex && moveTooltip(data);
6464
+ },
6465
+ onClickLabel: ({ index }) => clickFn()?.(index)
6466
+ };
6467
+ };
6468
+
6384
6469
  const registerChart = (items = []) => () => {
6385
6470
  Chart.register(BarController, LineController, CategoryScale, LinearScale, PointElement, BarElement, LineElement, Title, Tooltip, Legend, TimeScale, annotationPlugin, ...items);
6386
6471
  };
@@ -6451,7 +6536,7 @@ const defaultSettings$3 = Object.freeze({
6451
6536
  },
6452
6537
  callback: function (value) {
6453
6538
  const label = this.getLabelForValue(value);
6454
- return ellipsis$1(label, 50);
6539
+ return ellipsis$1(label, chartLabelMaxLength);
6455
6540
  }
6456
6541
  }
6457
6542
  }
@@ -6746,7 +6831,7 @@ const defaultSettings$2 = Object.freeze({
6746
6831
  },
6747
6832
  callback: function (value) {
6748
6833
  const label = this.getLabelForValue(value);
6749
- return ellipsis$1(label, 50);
6834
+ return ellipsis$1(label, chartLabelMaxLength);
6750
6835
  }
6751
6836
  }
6752
6837
  }
@@ -6874,6 +6959,14 @@ class ContributionChartComponent {
6874
6959
  * Override the default configuration.
6875
6960
  */
6876
6961
  this.config = input({}, ...(ngDevMode ? [{ debugName: "config" }] : []));
6962
+ /**
6963
+ * Tooltip shown when hovering an axis label. Without it the labels are not interactive.
6964
+ */
6965
+ this.axisTooltipFn = input(...(ngDevMode ? [undefined, { debugName: "axisTooltipFn" }] : []));
6966
+ /**
6967
+ * Called with the index of the axis label that was clicked.
6968
+ */
6969
+ this.axisLabelClickFn = input(...(ngDevMode ? [undefined, { debugName: "axisLabelClickFn" }] : []));
6877
6970
  this.minHeight = input(100, ...(ngDevMode ? [{ debugName: "minHeight" }] : []));
6878
6971
  this.maxHeight = input(400, ...(ngDevMode ? [{ debugName: "maxHeight" }] : []));
6879
6972
  this.height = input(0, ...(ngDevMode ? [{ debugName: "height" }] : []));
@@ -6883,6 +6976,10 @@ class ContributionChartComponent {
6883
6976
  this.category = input(undefined, ...(ngDevMode ? [{ debugName: "category" }] : []));
6884
6977
  this.chart = viewChild.required(ChartComponent);
6885
6978
  this.tooltip = viewChild.required(ChartTooltipComponent);
6979
+ this.axisTooltip = viewChild('axisTooltip', ...(ngDevMode ? [{ debugName: "axisTooltip" }] : []));
6980
+ this.axisSettings = axisLabelSettings(() => this.axisTooltip(), () => this.axisTooltipFn(), () => this.axisLabelClickFn());
6981
+ this.axisEnabled = computed(() => !!this.axisTooltipFn() || !!this.axisLabelClickFn(), ...(ngDevMode ? [{ debugName: "axisEnabled" }] : []));
6982
+ this.axisTooltipContent = computed(() => (data) => this.axisTooltipFn()?.(data.label, data.index) ?? '', ...(ngDevMode ? [{ debugName: "axisTooltipContent" }] : []));
6886
6983
  this.exporting = computed(() => this.chart()?.exporting(), ...(ngDevMode ? [{ debugName: "exporting" }] : []));
6887
6984
  this.chartHeight = computed(() => this.height() || chartHeight$1(this.minHeight(), this.maxHeight(), this.labels()?.length ?? 0), ...(ngDevMode ? [{ debugName: "chartHeight" }] : []));
6888
6985
  this.hasNegativeContributions = computed(() => this.displayData()?.some(value => value.values.some(v => v < 0)), ...(ngDevMode ? [{ debugName: "hasNegativeContributions" }] : []));
@@ -6890,6 +6987,7 @@ class ContributionChartComponent {
6890
6987
  this.defaultConfig = computed(() => ({
6891
6988
  options: {
6892
6989
  onClick: (event, activeElements, chart) => this.onItemClick(event, activeElements, chart),
6990
+ plugins: { axisHover: this.axisSettings },
6893
6991
  scales: {
6894
6992
  x: {
6895
6993
  display: !this.responsiveService.isMobile(),
@@ -6912,7 +7010,8 @@ class ContributionChartComponent {
6912
7010
  afterBarDrawPlugin({
6913
7011
  placement: 'right',
6914
7012
  textFn: ({ data }) => (isUndefined(data) ? 'No data' : '')
6915
- })
7013
+ }),
7014
+ ...(this.axisEnabled() ? [axisHoverPlugin] : [])
6916
7015
  ]
6917
7016
  }), ...(ngDevMode ? [{ debugName: "defaultConfig" }] : []));
6918
7017
  this.dataConfig = computed(() => ({
@@ -6958,12 +7057,12 @@ class ContributionChartComponent {
6958
7057
  return downloadFile(this.csvContent(), `${title}-contribution.csv`);
6959
7058
  }
6960
7059
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ContributionChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6961
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ContributionChartComponent, isStandalone: true, selector: "he-contribution-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "chart", first: true, predicate: ChartComponent, descendants: true, isSignal: true }, { propertyName: "tooltip", first: true, predicate: ChartTooltipComponent, descendants: true, isSignal: true }], exportAs: ["contributionChart"], ngImport: i0, 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 </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"], dependencies: [{ kind: "component", type: ChartComponent, selector: "he-chart", inputs: ["data", "config", "showExportButton"], exportAs: ["chart"] }, { kind: "component", type: ChartTooltipComponent, selector: "he-chart-tooltip", inputs: ["tooltipFn"], exportAs: ["chartTooltip"] }, { kind: "component", type: BarChartLegendComponent, selector: "he-bar-chart-legend", inputs: ["data"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7060
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ContributionChartComponent, isStandalone: true, selector: "he-contribution-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, axisTooltipFn: { classPropertyName: "axisTooltipFn", publicName: "axisTooltipFn", isSignal: true, isRequired: false, transformFunction: null }, axisLabelClickFn: { classPropertyName: "axisLabelClickFn", publicName: "axisLabelClickFn", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "chart", first: true, predicate: ChartComponent, descendants: true, isSignal: true }, { propertyName: "tooltip", first: true, predicate: ChartTooltipComponent, descendants: true, isSignal: true }, { propertyName: "axisTooltip", first: true, predicate: ["axisTooltip"], descendants: true, isSignal: true }], exportAs: ["contributionChart"], ngImport: i0, 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"], dependencies: [{ kind: "component", type: ChartComponent, selector: "he-chart", inputs: ["data", "config", "showExportButton"], exportAs: ["chart"] }, { kind: "component", type: ChartTooltipComponent, selector: "he-chart-tooltip", inputs: ["tooltipFn"], exportAs: ["chartTooltip"] }, { kind: "component", type: BarChartLegendComponent, selector: "he-bar-chart-legend", inputs: ["data"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6962
7061
  }
6963
7062
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ContributionChartComponent, decorators: [{
6964
7063
  type: Component$1,
6965
- 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 </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"] }]
6966
- }], 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 }] }], 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 }] }] } });
7064
+ 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"] }]
7065
+ }], 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 }] }] } });
6967
7066
 
6968
7067
  const colors = {
6969
7068
  darkBlue: 'rgba(25, 57, 87, 1)',
@@ -7423,9 +7522,21 @@ class HorizontalBarChartComponent extends BarChartComponent {
7423
7522
  super(...arguments);
7424
7523
  this.tooltipFn = input(defaultTooltipFn, ...(ngDevMode ? [{ debugName: "tooltipFn" }] : []));
7425
7524
  this.afterBarDrawSettings = input(...(ngDevMode ? [undefined, { debugName: "afterBarDrawSettings" }] : []));
7525
+ /**
7526
+ * Tooltip shown when hovering an axis label. Without it the labels are not interactive.
7527
+ */
7528
+ this.axisTooltipFn = input(...(ngDevMode ? [undefined, { debugName: "axisTooltipFn" }] : []));
7529
+ /**
7530
+ * Called with the index of the axis label that was clicked.
7531
+ */
7532
+ this.axisLabelClickFn = input(...(ngDevMode ? [undefined, { debugName: "axisLabelClickFn" }] : []));
7426
7533
  this.minHeight = input(100, ...(ngDevMode ? [{ debugName: "minHeight" }] : []));
7427
7534
  this.maxHeight = input(400, ...(ngDevMode ? [{ debugName: "maxHeight" }] : []));
7428
7535
  this.tooltip = viewChild.required(ChartTooltipComponent);
7536
+ this.axisTooltip = viewChild('axisTooltip', ...(ngDevMode ? [{ debugName: "axisTooltip" }] : []));
7537
+ this.axisSettings = axisLabelSettings(() => this.axisTooltip(), () => this.axisTooltipFn(), () => this.axisLabelClickFn());
7538
+ this.axisEnabled = computed(() => !!this.axisTooltipFn() || !!this.axisLabelClickFn(), ...(ngDevMode ? [{ debugName: "axisEnabled" }] : []));
7539
+ this.axisTooltipContent = computed(() => (data) => this.axisTooltipFn()?.(data.label, data.index) ?? '', ...(ngDevMode ? [{ debugName: "axisTooltipContent" }] : []));
7429
7540
  this.height = computed(() => chartHeight(this.minHeight(), this.maxHeight(), this.maximumData()?.length ?? 0), ...(ngDevMode ? [{ debugName: "height" }] : []));
7430
7541
  this.horizontalConfiguration = computed(() => ({
7431
7542
  ...merge$1(this.configuration(), defaultConfig, {
@@ -7435,6 +7546,7 @@ class HorizontalBarChartComponent extends BarChartComponent {
7435
7546
  const data = this.maximumData()[index];
7436
7547
  !isUndefined(index) && this.tooltip().show({ ...data, index }, event.x, event.y, { container: 'body' });
7437
7548
  },
7549
+ plugins: { axisHover: this.axisSettings },
7438
7550
  scales: {
7439
7551
  x: {
7440
7552
  grid: {
@@ -7451,17 +7563,18 @@ class HorizontalBarChartComponent extends BarChartComponent {
7451
7563
  lollipopChartPlugin(),
7452
7564
  backgroundHoverPlugin({ threshold: 5 }),
7453
7565
  afterBarDrawPlugin(this.afterBarDrawSettings()),
7566
+ ...(this.axisEnabled() ? [axisHoverPlugin] : []),
7454
7567
  ...(this.configuration().plugins ?? [])
7455
7568
  ]
7456
7569
  }), ...(ngDevMode ? [{ debugName: "horizontalConfiguration" }] : []));
7457
7570
  }
7458
7571
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: HorizontalBarChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
7459
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.6", type: HorizontalBarChartComponent, isStandalone: true, selector: "he-horizontal-bar-chart", inputs: { tooltipFn: { classPropertyName: "tooltipFn", publicName: "tooltipFn", isSignal: true, isRequired: false, transformFunction: null }, afterBarDrawSettings: { classPropertyName: "afterBarDrawSettings", publicName: "afterBarDrawSettings", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tooltip", first: true, predicate: ChartTooltipComponent, descendants: true, isSignal: true }], exportAs: ["horizontalBarChart"], usesInheritance: true, ngImport: i0, template: "<div class=\"chart-area-border\">\n <he-chart\n class=\"is-relative h-100\"\n [data]=\"dataConfig()\"\n [config]=\"horizontalConfiguration()\"\n [showExportButton]=\"showExportButton()\"\n [style.height.px]=\"height()\">\n <he-chart-tooltip [tooltipFn]=\"tooltipFn()\" />\n\n <ng-content />\n </he-chart>\n</div>\n\n<he-bar-chart-legend class=\"is-hidden-tablet\" [data]=\"maximumData()\" />\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: ChartComponent, selector: "he-chart", inputs: ["data", "config", "showExportButton"], exportAs: ["chart"] }, { kind: "component", type: ChartTooltipComponent, selector: "he-chart-tooltip", inputs: ["tooltipFn"], exportAs: ["chartTooltip"] }, { kind: "component", type: BarChartLegendComponent, selector: "he-bar-chart-legend", inputs: ["data"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7572
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: HorizontalBarChartComponent, isStandalone: true, selector: "he-horizontal-bar-chart", inputs: { tooltipFn: { classPropertyName: "tooltipFn", publicName: "tooltipFn", isSignal: true, isRequired: false, transformFunction: null }, afterBarDrawSettings: { classPropertyName: "afterBarDrawSettings", publicName: "afterBarDrawSettings", isSignal: true, isRequired: false, transformFunction: null }, axisTooltipFn: { classPropertyName: "axisTooltipFn", publicName: "axisTooltipFn", isSignal: true, isRequired: false, transformFunction: null }, axisLabelClickFn: { classPropertyName: "axisLabelClickFn", publicName: "axisLabelClickFn", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tooltip", first: true, predicate: ChartTooltipComponent, descendants: true, isSignal: true }, { propertyName: "axisTooltip", first: true, predicate: ["axisTooltip"], descendants: true, isSignal: true }], exportAs: ["horizontalBarChart"], usesInheritance: true, ngImport: i0, template: "<div class=\"chart-area-border\">\n <he-chart\n class=\"is-relative h-100\"\n [data]=\"dataConfig()\"\n [config]=\"horizontalConfiguration()\"\n [showExportButton]=\"showExportButton()\"\n [style.height.px]=\"height()\">\n <he-chart-tooltip [tooltipFn]=\"tooltipFn()\" />\n @if (axisEnabled()) {\n <he-chart-tooltip #axisTooltip=\"chartTooltip\" [tooltipFn]=\"axisTooltipContent()\" />\n }\n\n <ng-content />\n </he-chart>\n</div>\n\n<he-bar-chart-legend class=\"is-hidden-tablet\" [data]=\"maximumData()\" />\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: ChartComponent, selector: "he-chart", inputs: ["data", "config", "showExportButton"], exportAs: ["chart"] }, { kind: "component", type: ChartTooltipComponent, selector: "he-chart-tooltip", inputs: ["tooltipFn"], exportAs: ["chartTooltip"] }, { kind: "component", type: BarChartLegendComponent, selector: "he-bar-chart-legend", inputs: ["data"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7460
7573
  }
7461
7574
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: HorizontalBarChartComponent, decorators: [{
7462
7575
  type: Component$1,
7463
- args: [{ selector: 'he-horizontal-bar-chart', exportAs: 'horizontalBarChart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ChartComponent, ChartTooltipComponent, BarChartLegendComponent], template: "<div class=\"chart-area-border\">\n <he-chart\n class=\"is-relative h-100\"\n [data]=\"dataConfig()\"\n [config]=\"horizontalConfiguration()\"\n [showExportButton]=\"showExportButton()\"\n [style.height.px]=\"height()\">\n <he-chart-tooltip [tooltipFn]=\"tooltipFn()\" />\n\n <ng-content />\n </he-chart>\n</div>\n\n<he-bar-chart-legend class=\"is-hidden-tablet\" [data]=\"maximumData()\" />\n", styles: [":host{display:block}\n"] }]
7464
- }], propDecorators: { tooltipFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipFn", required: false }] }], afterBarDrawSettings: [{ type: i0.Input, args: [{ isSignal: true, alias: "afterBarDrawSettings", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], tooltip: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ChartTooltipComponent), { isSignal: true }] }] } });
7576
+ args: [{ selector: 'he-horizontal-bar-chart', exportAs: 'horizontalBarChart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ChartComponent, ChartTooltipComponent, BarChartLegendComponent], template: "<div class=\"chart-area-border\">\n <he-chart\n class=\"is-relative h-100\"\n [data]=\"dataConfig()\"\n [config]=\"horizontalConfiguration()\"\n [showExportButton]=\"showExportButton()\"\n [style.height.px]=\"height()\">\n <he-chart-tooltip [tooltipFn]=\"tooltipFn()\" />\n @if (axisEnabled()) {\n <he-chart-tooltip #axisTooltip=\"chartTooltip\" [tooltipFn]=\"axisTooltipContent()\" />\n }\n\n <ng-content />\n </he-chart>\n</div>\n\n<he-bar-chart-legend class=\"is-hidden-tablet\" [data]=\"maximumData()\" />\n", styles: [":host{display:block}\n"] }]
7577
+ }], propDecorators: { tooltipFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipFn", required: false }] }], afterBarDrawSettings: [{ type: i0.Input, args: [{ isSignal: true, alias: "afterBarDrawSettings", 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 }] }], tooltip: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ChartTooltipComponent), { isSignal: true }] }], axisTooltip: [{ type: i0.ViewChild, args: ['axisTooltip', { isSignal: true }] }] } });
7465
7578
 
7466
7579
  const primaryProduct = ({ products }) => (products || []).find(({ primary }) => primary);
7467
7580
  /**
@@ -8095,6 +8208,12 @@ const nodeLinkEnabled = (node) => [
8095
8208
  isExternal() && !!node.id
8096
8209
  ].some(Boolean)
8097
8210
  ].every(Boolean);
8211
+ /**
8212
+ * Open a node's page, as `he-node-link` would.
8213
+ */
8214
+ const openNodeUrl = (node, dataVersion) => nodeLinkEnabled(node)
8215
+ ? openUrl([baseUrl(), nodeLink(node, nodeDataState(node), nodeDataVersion(node) || dataVersion)].join('/'), isExternal() ? '_blank' : '_self')
8216
+ : undefined;
8098
8217
  var NodeQualityScore;
8099
8218
  (function (NodeQualityScore) {
8100
8219
  NodeQualityScore["low"] = "Low";
@@ -9483,11 +9602,11 @@ class FormulaBlockComponent {
9483
9602
  this.substituted.set(!this.substituted());
9484
9603
  }
9485
9604
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormulaBlockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9486
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: FormulaBlockComponent, isStandalone: true, selector: "he-formula-block", inputs: { formulas: { classPropertyName: "formulas", publicName: "formulas", isSignal: true, isRequired: false, transformFunction: null }, hasSubstitutions: { classPropertyName: "hasSubstitutions", publicName: "hasSubstitutions", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: false, transformFunction: null }, showToggle: { classPropertyName: "showToggle", publicName: "showToggle", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, substituted: { classPropertyName: "substituted", publicName: "substituted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", substituted: "substitutedChange" }, ngImport: i0, template: "@if (items().length) {\n <he-collapsible-block [heading]=\"blockHeading()\" [collapsible]=\"collapsible()\" [(open)]=\"open\">\n @if (showToggle()) {\n <div blockActions class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n\n <div class=\"formula-block\">\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.note) {\n <span class=\"formula-variable-note\">({{ variable.note }})</span>\n } @else if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n </he-collapsible-block>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown class=\"is-inline-block\" [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [":host{display:block}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-variable-note{opacity:.7;white-space:nowrap}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: KatexDirective, selector: "[heKatex]", inputs: ["heKatex", "heKatexInline"] }, { kind: "component", type: MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: CollapsibleBlockComponent, selector: "he-collapsible-block", inputs: ["heading", "collapsible", "open"], outputs: ["openChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9605
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: FormulaBlockComponent, isStandalone: true, selector: "he-formula-block", inputs: { formulas: { classPropertyName: "formulas", publicName: "formulas", isSignal: true, isRequired: false, transformFunction: null }, hasSubstitutions: { classPropertyName: "hasSubstitutions", publicName: "hasSubstitutions", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: false, transformFunction: null }, showToggle: { classPropertyName: "showToggle", publicName: "showToggle", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, substituted: { classPropertyName: "substituted", publicName: "substituted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", substituted: "substitutedChange" }, ngImport: i0, template: "@if (items().length) {\n <he-collapsible-block [heading]=\"blockHeading()\" [collapsible]=\"collapsible()\" [(open)]=\"open\">\n @if (showToggle()) {\n <div blockActions class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n\n <div class=\"formula-block\">\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.note) {\n <span class=\"formula-variable-note\">({{ variable.note }})</span>\n } @else if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n </he-collapsible-block>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [":host{display:block}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-variable-note{opacity:.7;white-space:nowrap}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: KatexDirective, selector: "[heKatex]", inputs: ["heKatex", "heKatexInline"] }, { kind: "component", type: MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: CollapsibleBlockComponent, selector: "he-collapsible-block", inputs: ["heading", "collapsible", "open"], outputs: ["openChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9487
9606
  }
9488
9607
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FormulaBlockComponent, decorators: [{
9489
9608
  type: Component$1,
9490
- args: [{ selector: 'he-formula-block', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, KatexDirective, MarkdownComponent, CollapsibleBlockComponent], template: "@if (items().length) {\n <he-collapsible-block [heading]=\"blockHeading()\" [collapsible]=\"collapsible()\" [(open)]=\"open\">\n @if (showToggle()) {\n <div blockActions class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n\n <div class=\"formula-block\">\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.note) {\n <span class=\"formula-variable-note\">({{ variable.note }})</span>\n } @else if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n </he-collapsible-block>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown class=\"is-inline-block\" [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [":host{display:block}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-variable-note{opacity:.7;white-space:nowrap}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"] }]
9609
+ args: [{ selector: 'he-formula-block', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, KatexDirective, MarkdownComponent, CollapsibleBlockComponent], template: "@if (items().length) {\n <he-collapsible-block [heading]=\"blockHeading()\" [collapsible]=\"collapsible()\" [(open)]=\"open\">\n @if (showToggle()) {\n <div blockActions class=\"field\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"toggleId\"\n [checked]=\"substituted()\"\n [disabled]=\"!hasSubstitutions()\"\n (change)=\"toggle()\" />\n <label [for]=\"toggleId\" [title]=\"hasSubstitutions() ? '' : emptyTitle()\">\n <span>{{ substituted() ? 'Substituted' : 'Raw' }}</span>\n </label>\n </div>\n }\n\n <div class=\"formula-block\">\n @if (note()) {\n <p class=\"is-size-8 is-mb-1 | formula-note\">{{ note() }}</p>\n }\n\n @for (item of items(); track $index) {\n @if (item.section) {\n <p class=\"is-size-8 is-uppercase has-text-weight-semibold is-mt-2 | formula-section\">{{ item.section }}</p>\n }\n\n @if (item.contextParts.length) {\n <p class=\"is-size-7 is-mt-1 | formula-context\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: item.contextParts }\" />\n </p>\n }\n\n <div class=\"formula\" [heKatex]=\"item.rendered\"></div>\n\n @if (item.variables.length) {\n <ul class=\"is-size-7 is-mt-1 is-mb-2 is-list-style-disc | formula-variables\">\n @for (variable of item.variables; track $index) {\n <li>\n <div class=\"is-flex is-align-items-baseline is-gap-4\">\n <span class=\"formula-variable-symbol is-nowrap\">\n <span [heKatex]=\"variable.symbol\" [heKatexInline]=\"true\"></span>\n @if (variable.descriptionParts.length) {\n <span>:</span>\n }\n </span>\n @if (variable.descriptionParts.length) {\n <span class=\"is-italic | formula-variable-desc\">\n <ng-container *ngTemplateOutlet=\"textParts; context: { parts: variable.descriptionParts }\" />\n </span>\n }\n @if (variable.note) {\n <span class=\"formula-variable-note\">({{ variable.note }})</span>\n } @else if (variable.missing) {\n <span class=\"has-text-warning\">(missing)</span>\n }\n </div>\n </li>\n }\n </ul>\n }\n }\n </div>\n </he-collapsible-block>\n}\n\n<!-- documentation text: the symbols it names render as math, the rest as markdown -->\n<ng-template #textParts let-parts=\"parts\">\n @for (part of parts; track $index) {\n @if (part.math) {\n <span class=\"formula-inline-math\" [heKatex]=\"part.math\" [heKatexInline]=\"true\"></span>\n } @else {\n <markdown [data]=\"part.text\" />\n }\n }\n</ng-template>\n", styles: [":host{display:block}.formula-section{opacity:.7;letter-spacing:.03em}.formula-note{opacity:.7}.formula-context{opacity:.9}.formula-variables{padding-inline-start:1rem}.formula-variable-symbol{flex-shrink:0}.formula-variable-symbol ::ng-deep .katex{font-size:1em}.formula-variable-desc{opacity:.85}.formula-variable-note{opacity:.7;white-space:nowrap}.formula-context ::ng-deep markdown,.formula-context ::ng-deep markdown *,.formula-variable-desc ::ng-deep markdown,.formula-variable-desc ::ng-deep markdown *{display:inline;margin:0;white-space:pre-wrap}.formula-context .formula-inline-math ::ng-deep .katex,.formula-variable-desc .formula-inline-math ::ng-deep .katex{font-size:1em}.formula{overflow-x:auto;overflow-y:hidden}.formula ::ng-deep .katex-display{margin:.35rem 0}\n"] }]
9491
9610
  }], propDecorators: { formulas: [{ type: i0.Input, args: [{ isSignal: true, alias: "formulas", required: false }] }], hasSubstitutions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasSubstitutions", required: false }] }], emptyTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTitle", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], note: [{ type: i0.Input, args: [{ isSignal: true, alias: "note", required: false }] }], showToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToggle", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], substituted: [{ type: i0.Input, args: [{ isSignal: true, alias: "substituted", required: false }] }, { type: i0.Output, args: ["substitutedChange"] }] } });
9492
9611
 
9493
9612
  const isMissing$1 = (value) => value === undefined || value === null || value === '';
@@ -10949,7 +11068,6 @@ const methodIdLabel$1 = (methodId) => (methodId
10949
11068
  const modelKeyLabel = {
10950
11069
  'input.hestiaAggregatedData': 'HESTIA aggregated data'
10951
11070
  };
10952
- const termSearch$1 = (term = '') => term.replace(/[,\s]/g, '').toLowerCase();
10953
11071
  class NodeJLogModelsComponent {
10954
11072
  constructor() {
10955
11073
  this.searchService = inject(HeSearchService);
@@ -11052,7 +11170,7 @@ class NodeJLogModelsComponent {
11052
11170
  .filter(Boolean), ...(ngDevMode ? [{ debugName: "allTerms" }] : []));
11053
11171
  this.enableFilterByTerm = computed(() => this.allTerms().length > 0, ...(ngDevMode ? [{ debugName: "enableFilterByTerm" }] : []));
11054
11172
  this.termFormatter = ({ name }) => name;
11055
- this.suggestTerm = (text$) => text$.pipe(distinctUntilChanged(), map(value => this.allTerms().filter(v => termSearch$1(v.name).includes(termSearch$1(value)))));
11173
+ this.suggestTerm = (text$) => text$.pipe(distinctUntilChanged(), map(value => suggestTerms(this.allTerms(), value)));
11056
11174
  this.methodsById = toSignal(this.searchTerms({
11057
11175
  bool: { must: [matchType(NodeType.Term), { match: { termType: TermTermType.model } }] }
11058
11176
  }).pipe(map(groupTerms$1)));
@@ -11199,7 +11317,6 @@ const orderBlankNodesByNodeKey = {
11199
11317
  })), ['methodTierOrder'], ['asc']).map(({ value }) => value)
11200
11318
  };
11201
11319
  const orderBlankNodes = (blankNodes, nodeKey) => (orderBlankNodesByNodeKey[nodeKey] || (v => v))(blankNodes);
11202
- const termSearch = (term = '') => term.replace(/[,\s]/g, '').toLowerCase();
11203
11320
  const functionalUnit = {
11204
11321
  [NodeType.Cycle]: (data) => data.functionalUnit === CycleFunctionalUnit['1 ha'] ? 'hectare' : 'functional unit',
11205
11322
  [NodeType.ImpactAssessment]: data => (data.product?.term?.units || data.term?.units) === 'kg'
@@ -11390,7 +11507,7 @@ class NodeLogsModelsComponent {
11390
11507
  this.term = signal(undefined, ...(ngDevMode ? [{ debugName: "term" }] : []));
11391
11508
  this.enableFilterByTerm = computed(() => this.allTerms()?.length > 0, ...(ngDevMode ? [{ debugName: "enableFilterByTerm" }] : []));
11392
11509
  this.termFormatter = ({ name }) => name;
11393
- this.suggestTerm = (text$) => text$.pipe(distinctUntilChanged(), map(v => this.suggestByTerm(v)));
11510
+ this.suggestTerm = (text$) => text$.pipe(distinctUntilChanged(), map(value => suggestTerms(this.allTerms(), value)));
11394
11511
  this.typeaheadFocus = typeaheadFocus;
11395
11512
  this.loading = computed(() => [
11396
11513
  !this.logsResource.hasValue(),
@@ -11406,9 +11523,6 @@ class NodeLogsModelsComponent {
11406
11523
  ].some(Boolean), ...(ngDevMode ? [{ debugName: "loading" }] : []));
11407
11524
  this.isNumber = (value) => typeof value === 'number';
11408
11525
  }
11409
- suggestByTerm(term) {
11410
- return this.allTerms().filter(v => termSearch(v.name).includes(termSearch(term)));
11411
- }
11412
11526
  searchTerms(query, ...extraFields) {
11413
11527
  return this.searchService
11414
11528
  .search$({
@@ -11579,20 +11693,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
11579
11693
  }], ctorParameters: () => [], propDecorators: { dataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataState", required: false }] }], hideIdenticalValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideIdenticalValues", required: false }] }] } });
11580
11694
 
11581
11695
  const cycleValue = (cycle, values) => (values[nodeVersionKey(cycle)]?.nodes[0] || { value: [0] }).value;
11582
- const cycleName = (cycle, index) => `${index + 1}. ${defaultLabel(cycle)}`;
11583
11696
  class CyclesEmissionsChartComponent {
11584
11697
  constructor() {
11698
+ this.route = inject(ActivatedRoute);
11585
11699
  this.cycles = input.required(...(ngDevMode ? [{ debugName: "cycles" }] : []));
11700
+ this.dataVersion = toSignal(this.route.queryParams.pipe(map(({ dataVersion }) => dataVersion)));
11586
11701
  this.emissionPerCycle = computed(() => groupNodesByTerm(this.cycles(), 'emissions'), ...(ngDevMode ? [{ debugName: "emissionPerCycle" }] : []));
11587
11702
  this.terms = computed(() => Object.values(this.emissionPerCycle() ?? {})
11588
11703
  .filter(({ values }) => Object.values(values).some(({ nodes: [{ value }] }) => propertyValue$1(value) > 0))
11589
11704
  .map(({ term }) => term)
11590
11705
  .sort((a, b) => a.name.localeCompare(b.name)), ...(ngDevMode ? [{ debugName: "terms" }] : []));
11591
11706
  this.selectedTerm = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedTerm" }] : []));
11592
- this.labels = computed(() => this.cycles().map(cycleName), ...(ngDevMode ? [{ debugName: "labels" }] : []));
11707
+ this.labels = computed(() => nodeChartLabels(this.cycles(), chartLabelMaxLength), ...(ngDevMode ? [{ debugName: "labels" }] : []));
11708
+ this.axisTooltipFn = (label, index) => this.cycleLabelTooltip(index) || label;
11709
+ this.axisLabelClickFn = index => this.openCycle(index);
11593
11710
  this.chartValues = computed(() => this.emissionPerCycle()?.[this.selectedTerm()?.name]?.values || {}, ...(ngDevMode ? [{ debugName: "chartValues" }] : []));
11594
11711
  this.chartData = computed(() => this.cycles().map((cycle, index) => ({
11595
- label: cycleName(cycle, index),
11712
+ label: this.labels()[index],
11596
11713
  count: propertyValue$1(cycleValue(cycle, this.chartValues()), this.selectedTerm?.['@id']),
11597
11714
  color: listColor(cycle, index)
11598
11715
  })), ...(ngDevMode ? [{ debugName: "chartData" }] : []));
@@ -11609,12 +11726,20 @@ class CyclesEmissionsChartComponent {
11609
11726
  const term = this.terms().find(term => term['@id'] === value);
11610
11727
  this.selectedTerm.set(term);
11611
11728
  }
11729
+ cycleLabelTooltip(index) {
11730
+ const cycle = this.cycles()[index];
11731
+ return cycle ? [`${index + 1}. ${defaultLabel(cycle)}`, cycle['@id']].filter(Boolean).join('<br />') : '';
11732
+ }
11733
+ openCycle(index) {
11734
+ const cycle = this.cycles()[index];
11735
+ return cycle ? openNodeUrl(cycle, this.dataVersion()) : undefined;
11736
+ }
11612
11737
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesEmissionsChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11613
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesEmissionsChartComponent, isStandalone: true, selector: "he-cycles-emissions-chart", inputs: { cycles: { classPropertyName: "cycles", publicName: "cycles", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart\" [config]=\"{ lollipopConfig: null }\" />\n\n <ng-content />\n</div>\n\n@if (terms().length) {\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectTerm($event)\" id=\"selectTerm\">\n @for (term of terms(); track term) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n}\n\n<he-horizontal-bar-chart\n #chart=\"horizontalBarChart\"\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"labels()\"\n [showExportButton]=\"false\" />\n", styles: [":host{display:block;overflow:visible}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "component", type: HorizontalBarChartComponent, selector: "he-horizontal-bar-chart", inputs: ["tooltipFn", "afterBarDrawSettings", "minHeight", "maxHeight"], exportAs: ["horizontalBarChart"] }, { kind: "component", type: ChartExportButtonComponent, selector: "he-chart-export-button", inputs: ["buttonClass", "chart", "config", "exportFormats", "chartExportFn"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11738
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: CyclesEmissionsChartComponent, isStandalone: true, selector: "he-cycles-emissions-chart", inputs: { cycles: { classPropertyName: "cycles", publicName: "cycles", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart\" [config]=\"{ lollipopConfig: null }\" />\n\n <ng-content />\n</div>\n\n@if (terms().length) {\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectTerm($event)\" id=\"selectTerm\">\n @for (term of terms(); track term) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n}\n\n<he-horizontal-bar-chart\n #chart=\"horizontalBarChart\"\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"labels()\"\n [axisTooltipFn]=\"axisTooltipFn\"\n [axisLabelClickFn]=\"axisLabelClickFn\"\n [showExportButton]=\"false\" />\n", styles: [":host{display:block;overflow:visible}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "component", type: HorizontalBarChartComponent, selector: "he-horizontal-bar-chart", inputs: ["tooltipFn", "afterBarDrawSettings", "axisTooltipFn", "axisLabelClickFn", "minHeight", "maxHeight"], exportAs: ["horizontalBarChart"] }, { kind: "component", type: ChartExportButtonComponent, selector: "he-chart-export-button", inputs: ["buttonClass", "chart", "config", "exportFormats", "chartExportFn"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11614
11739
  }
11615
11740
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesEmissionsChartComponent, decorators: [{
11616
11741
  type: Component$1,
11617
- args: [{ selector: 'he-cycles-emissions-chart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, HorizontalBarChartComponent, ChartExportButtonComponent], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart\" [config]=\"{ lollipopConfig: null }\" />\n\n <ng-content />\n</div>\n\n@if (terms().length) {\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectTerm($event)\" id=\"selectTerm\">\n @for (term of terms(); track term) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n}\n\n<he-horizontal-bar-chart\n #chart=\"horizontalBarChart\"\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"labels()\"\n [showExportButton]=\"false\" />\n", styles: [":host{display:block;overflow:visible}\n"] }]
11742
+ args: [{ selector: 'he-cycles-emissions-chart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, HorizontalBarChartComponent, ChartExportButtonComponent], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart\" [config]=\"{ lollipopConfig: null }\" />\n\n <ng-content />\n</div>\n\n@if (terms().length) {\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select (change)=\"selectTerm($event)\" id=\"selectTerm\">\n @for (term of terms(); track term) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n}\n\n<he-horizontal-bar-chart\n #chart=\"horizontalBarChart\"\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"labels()\"\n [axisTooltipFn]=\"axisTooltipFn\"\n [axisLabelClickFn]=\"axisLabelClickFn\"\n [showExportButton]=\"false\" />\n", styles: [":host{display:block;overflow:visible}\n"] }]
11618
11743
  }], ctorParameters: () => [], propDecorators: { cycles: [{ type: i0.Input, args: [{ isSignal: true, alias: "cycles", required: true }] }] } });
11619
11744
 
11620
11745
  class CyclesFunctionalUnitMeasureComponent {
@@ -12021,9 +12146,7 @@ const distinguishingLabels = (ids) => {
12021
12146
  */
12022
12147
  const contributorsTable = (values, formulas) => {
12023
12148
  const bindings = formulas.flatMap(formula => formula.bindings.filter(isTableBinding));
12024
- const key = bindings
12025
- .map(binding => binding.key)
12026
- .find(name => !isMissing(values[name]) || tableCount(values, name));
12149
+ const key = bindings.map(binding => binding.key).find(name => !isMissing(values[name]) || tableCount(values, name));
12027
12150
  if (!key)
12028
12151
  return undefined;
12029
12152
  const parsed = parseRows(values[key]);
@@ -12083,7 +12206,6 @@ const inclusionRule = (term, values) => {
12083
12206
  const fallback = hasLogs(values) ? SKIPPED_RULE : undefined;
12084
12207
  return areas.length ? completenessRule(areas) : (termRule(term) ?? fallback);
12085
12208
  };
12086
-
12087
12209
  // The page every aggregation follows, whatever the product.
12088
12210
  const GENERAL_PAGE = 'general-process';
12089
12211
  // The product-specific page, by the primary product's `termType`. A term type with no page of its
@@ -12092,17 +12214,6 @@ const PRODUCT_PAGES = {
12092
12214
  crop: 'crop',
12093
12215
  processedFood: 'processed-food'
12094
12216
  };
12095
- /**
12096
- * The rules are shown in the order they answer a reader's questions: how this value was calculated,
12097
- * which Cycles went into it, then how each of those was weighted. Grouping them by what they
12098
- * compute rather than by the page they are documented on is what puts the two calculations either
12099
- * side of the contributors they combine.
12100
- */
12101
- const GROUPS = [
12102
- { id: 'value', heading: 'How the value is calculated', pageOrder: pages => pages },
12103
- // a weight is derived by the product's own rules first, then normalised by the general one
12104
- { id: 'weights', heading: 'How the weights are calculated', pageOrder: pages => [...pages].reverse() }
12105
- ];
12106
12217
  // A symbol worth listing under its formula: it carries documentation the reader needs. Constants
12107
12218
  // (e.g. `365`) are self-evident in the formula itself.
12108
12219
  const isDocumented = (binding) => !!binding.symbol && !!binding.description;
@@ -12127,6 +12238,79 @@ const isPlantationWeight = (formula) => bindsTo(formula, 'plantation_lifespan');
12127
12238
  // The blank node keys whose terms carry a completeness area.
12128
12239
  const COMPLETENESS_KEYS = ['products', 'emissions', 'inputs', 'practices'];
12129
12240
  const PRODUCTS_KEY = 'products';
12241
+ /**
12242
+ * The rules are shown in the order they answer a reader's questions: how this value was calculated,
12243
+ * which Cycles went into it, then how each of those was weighted. Grouping them by what they
12244
+ * compute rather than by the page they are documented on is what puts the two calculations either
12245
+ * side of the contributors they combine.
12246
+ */
12247
+ const GROUPS = [
12248
+ { id: 'value', heading: 'How the value is calculated', pageOrder: pages => pages },
12249
+ // a weight is derived by the product's own rules first, then normalised by the general one
12250
+ { id: 'weights', heading: 'How the weights are calculated', pageOrder: pages => [...pages].reverse() }
12251
+ ];
12252
+ /**
12253
+ * Whether the values were combined by a weighting structure rather than counted equally, which
12254
+ * decides which of the two means is shown. Only `crop` aggregations are split into sub-systems, so
12255
+ * every other product type combines its Cycles unweighted however it was built.
12256
+ */
12257
+ const isWeightedAggregation = ({ termType, termId, worldAggregation, subAggregation }) => !!worldAggregation || isPlantation(termId ?? '') || (termType === TermTermType.crop && !subAggregation);
12258
+ /**
12259
+ * The pages whose formulas are shown: the general rules, then the product-specific ones.
12260
+ */
12261
+ const aggregationPages = (termType) => {
12262
+ const productPage = PRODUCT_PAGES[termType ?? ''];
12263
+ return [GENERAL_PAGE, ...(productPage ? [productPage] : [])];
12264
+ };
12265
+ /**
12266
+ * Whether the rule ran for this aggregation at all. Some stages depend on what was aggregated
12267
+ * rather than on the data item shown: the production-share weighting only combines countries into
12268
+ * a World aggregation, and the phase weighting only splits the lifespan of a plantation crop.
12269
+ */
12270
+ const ranForAggregation = (rules) => (formula) => {
12271
+ // the stages that only run for some aggregations, and whether each ran for this one. A formula
12272
+ // matching none of them is part of every aggregation.
12273
+ const stages = [
12274
+ { applies: isWorldWeight, ran: () => !!rules.worldAggregation },
12275
+ { applies: isSubSystemWeight, ran: () => !!rules.subAggregation },
12276
+ { applies: isPlantationWeight, ran: () => isPlantation(rules.termId ?? '') },
12277
+ { applies: isWeightNormalisation, ran: () => isWeightedAggregation(rules) }
12278
+ ];
12279
+ return stages.find(({ applies }) => applies(formula))?.ran() ?? true;
12280
+ };
12281
+ /**
12282
+ * Whether the rule applies to the kind of data item shown. With no `nodeKey` every rule is kept,
12283
+ * which is the whole-aggregation view.
12284
+ */
12285
+ const appliesToNodeKey = (nodeKey) => (formula) => {
12286
+ if (!nodeKey)
12287
+ return true;
12288
+ if (isEconomicValueShare(formula))
12289
+ return nodeKey === PRODUCTS_KEY;
12290
+ if (isCompletenessZeroFill(formula))
12291
+ return COMPLETENESS_KEYS.includes(nodeKey);
12292
+ return true;
12293
+ };
12294
+ const appliesToAggregation = (rules) => (formula) => ranForAggregation(rules)(formula) &&
12295
+ appliesToNodeKey(rules.nodeKey)(formula) &&
12296
+ isMeanThatRan(formula, isWeightedAggregation(rules));
12297
+ /**
12298
+ * Empty when the documentation carries no rule that applies: `@hestia-earth/aggregation-engine`
12299
+ * before 0.23.5 documents only the weighted mean, so an unweighted aggregation matches nothing.
12300
+ * `pageFormulas` is how it is read - a build resolves the package's JSON module, a test does not.
12301
+ */
12302
+ const aggregationSections = (rules, pageFormulas = getFormulas$1) => {
12303
+ const applies = appliesToAggregation(rules);
12304
+ return GROUPS.map(({ id, heading, pageOrder }) => ({
12305
+ id,
12306
+ heading,
12307
+ formulas: pageOrder(aggregationPages(rules.termType))
12308
+ .flatMap(page => pageFormulas(page))
12309
+ .filter(formula => formulaGroup(formula) === id && applies(formula))
12310
+ })).filter(section => section.formulas.length > 0);
12311
+ };
12312
+ const documentedBindings = (formula) => formula.bindings.filter(isDocumented);
12313
+
12130
12314
  class NodeAggregatedFormulasComponent {
12131
12315
  constructor() {
12132
12316
  /**
@@ -12170,20 +12354,15 @@ class NodeAggregatedFormulasComponent {
12170
12354
  // show the substituted formula rather than the symbolic one, when there is anything to substitute
12171
12355
  this.substituted = model(false, ...(ngDevMode ? [{ debugName: "substituted" }] : []));
12172
12356
  /**
12173
- * The pages whose formulas are shown: the general rules, then the product-specific ones.
12174
- */
12175
- this.pages = computed(() => {
12176
- const productPage = PRODUCT_PAGES[this.termType() ?? ''];
12177
- return [GENERAL_PAGE, ...(productPage ? [productPage] : [])];
12178
- }, ...(ngDevMode ? [{ debugName: "pages" }] : []));
12179
- /**
12180
- * Whether the values were combined by a weighting structure rather than counted equally, which
12181
- * decides which of the two means is shown. Only `crop` aggregations are split into sub-systems, so
12182
- * every other product type combines its Cycles unweighted however it was built.
12357
+ * The rules that produced this value, grouped by what each one computes.
12183
12358
  */
12184
- this.weighted = computed(() => this.worldAggregation() ||
12185
- isPlantation(this.termId() ?? '') ||
12186
- (this.termType() === TermTermType.crop && !this.subAggregation()), ...(ngDevMode ? [{ debugName: "weighted" }] : []));
12359
+ this.sections = computed(() => aggregationSections({
12360
+ termType: this.termType(),
12361
+ termId: this.termId(),
12362
+ nodeKey: this.nodeKey(),
12363
+ worldAggregation: this.worldAggregation(),
12364
+ subAggregation: this.subAggregation()
12365
+ }), ...(ngDevMode ? [{ debugName: "sections" }] : []));
12187
12366
  this.inclusion = computed(() => inclusionRule(this.term(), this.values()), ...(ngDevMode ? [{ debugName: "inclusion" }] : []));
12188
12367
  // whether any symbol actually resolves; when none does, the substituted view would be identical
12189
12368
  // to the symbolic one, so the toggle is disabled rather than silently doing nothing
@@ -12191,16 +12370,6 @@ class NodeAggregatedFormulasComponent {
12191
12370
  const values = this.values();
12192
12371
  return this.sections().some(section => section.formulas.some(formula => hasAnySubstitution(values, formula.bindings)));
12193
12372
  }, ...(ngDevMode ? [{ debugName: "hasSubstitutions" }] : []));
12194
- /**
12195
- * The rules that produced this value, grouped by what each one computes.
12196
- */
12197
- this.sections = computed(() => GROUPS.map(({ id, heading, pageOrder }) => ({
12198
- id,
12199
- heading,
12200
- formulas: pageOrder(this.pages())
12201
- .flatMap(page => getFormulas$1(page))
12202
- .filter(formula => formulaGroup(formula) === id && this.applies(formula))
12203
- })).filter(section => section.formulas.length > 0), ...(ngDevMode ? [{ debugName: "sections" }] : []));
12204
12373
  /**
12205
12374
  * What this value was combined from, as the aggregation recorded it: one row per contributor,
12206
12375
  * under the symbols the formulas above read them with. Absent where the contributors were too
@@ -12232,7 +12401,7 @@ class NodeAggregatedFormulasComponent {
12232
12401
  // this formula was extracted from - so the panel explains it in the documentation's words
12233
12402
  section: formula.section,
12234
12403
  context: formula.context,
12235
- variables: formula.bindings.filter(isDocumented).map(binding => ({
12404
+ variables: documentedBindings(formula).map(binding => ({
12236
12405
  symbol: binding.symbol,
12237
12406
  description: binding.description,
12238
12407
  // only an input that should have resolved is flagged - a constant, or a quantity the
@@ -12247,41 +12416,6 @@ class NodeAggregatedFormulasComponent {
12247
12416
  }));
12248
12417
  }, ...(ngDevMode ? [{ debugName: "renderedSections" }] : []));
12249
12418
  }
12250
- /**
12251
- * Whether the rule ran for this aggregation at all. Some stages depend on what was aggregated
12252
- * rather than on the data item shown: the production-share weighting only combines countries into
12253
- * a World aggregation, and the phase weighting only splits the lifespan of a plantation crop.
12254
- */
12255
- ranForAggregation(formula) {
12256
- // the stages that only run for some aggregations, and whether each ran for this one. A formula
12257
- // matching none of them is part of every aggregation.
12258
- const stages = [
12259
- { applies: isWorldWeight, ran: () => this.worldAggregation() },
12260
- { applies: isSubSystemWeight, ran: () => this.subAggregation() },
12261
- { applies: isPlantationWeight, ran: () => isPlantation(this.termId() ?? '') },
12262
- { applies: isWeightNormalisation, ran: () => this.weighted() }
12263
- ];
12264
- return stages.find(({ applies }) => applies(formula))?.ran() ?? true;
12265
- }
12266
- /**
12267
- * Whether the rule applies to the kind of data item shown. With no `nodeKey` every rule is kept,
12268
- * which is the whole-aggregation view.
12269
- */
12270
- appliesToNodeKey(formula) {
12271
- const nodeKey = this.nodeKey();
12272
- if (!nodeKey)
12273
- return true;
12274
- if (isEconomicValueShare(formula))
12275
- return nodeKey === PRODUCTS_KEY;
12276
- if (isCompletenessZeroFill(formula))
12277
- return COMPLETENESS_KEYS.includes(nodeKey);
12278
- return true;
12279
- }
12280
- applies(formula) {
12281
- return (this.ranForAggregation(formula) &&
12282
- this.appliesToNodeKey(formula) &&
12283
- isMeanThatRan(formula, this.weighted()));
12284
- }
12285
12419
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregatedFormulasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12286
12420
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregatedFormulasComponent, isStandalone: true, selector: "he-node-aggregated-formulas", inputs: { termType: { classPropertyName: "termType", publicName: "termType", isSignal: true, isRequired: false, transformFunction: null }, termId: { classPropertyName: "termId", publicName: "termId", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, term: { classPropertyName: "term", publicName: "term", isSignal: true, isRequired: false, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, worldAggregation: { classPropertyName: "worldAggregation", publicName: "worldAggregation", isSignal: true, isRequired: false, transformFunction: null }, subAggregation: { classPropertyName: "subAggregation", publicName: "subAggregation", isSignal: true, isRequired: false, transformFunction: null }, substituted: { classPropertyName: "substituted", publicName: "substituted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { substituted: "substitutedChange" }, ngImport: i0, template: "@if (renderedSections().length) {\n <div class=\"aggregated-formulas\">\n <p class=\"is-size-7 is-mb-2 | formula-intro\">\n Aggregated values are calculated from the underlying {{ contributor().group }}, not measured. These are the rules\n that produced this one, in the order they are applied.\n </p>\n\n <div class=\"is-pt-2 is-pr-1 is-overflow-y-auto | aggregated-formulas-rules\">\n <!-- what went in, before the rules that combine it: the formulas cannot show this -->\n @if (inclusion(); as rule) {\n <he-collapsible-block heading=\"Which Cycles are included\">\n <p class=\"is-size-7\">{{ rule.included }}</p>\n <p class=\"is-size-7\">{{ rule.blank }}</p>\n </he-collapsible-block>\n }\n\n @for (section of renderedSections(); track section.id; let first = $first) {\n <he-formula-block\n [formulas]=\"section.formulas\"\n [heading]=\"section.heading\"\n [collapsible]=\"true\"\n [showToggle]=\"first\"\n [hasSubstitutions]=\"hasSubstitutions()\"\n [(substituted)]=\"substituted\"\n emptyTitle=\"No recorded values to substitute\" />\n\n <!-- the contributors sit between the two calculations: what went in, above how a weight is derived -->\n @if (section.id === 'value' && contributors(); as table) {\n <he-collapsible-block [heading]=\"contributor().heading\">\n @if (table.rows.length) {\n <table class=\"table is-narrow is-size-7 w-100 | contributors-table\">\n <thead>\n <tr>\n <th>{{ contributor().row }}</th>\n @for (column of table.columns; track column.symbol) {\n <th class=\"has-text-right\">\n <span [heKatex]=\"column.symbol\" [heKatexInline]=\"true\"></span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of table.rows; track row.id) {\n <tr>\n <td [attr.title]=\"row.id\">\n <he-node-link [node]=\"row.node\" linkClass=\"has-text-white\">\n <span class=\"break-word\">{{ row.label }}</span>\n </he-node-link>\n </td>\n @for (value of row.values; track $index) {\n <td class=\"has-text-right is-nowrap\">{{ value | precision: 4 | default: '-' }}</td>\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <p class=\"is-size-7\">\n Combined from {{ table.count }} {{ contributor().group }}, too many for the aggregation to record one by\n one.\n </p>\n }\n </he-collapsible-block>\n }\n }\n </div>\n </div>\n}\n", styles: [".formula-intro{opacity:.85}.contributors-table{background-color:transparent;color:inherit}.contributors-table th,.contributors-table td{border-color:#fff3;color:inherit;padding:.15rem .5rem .15rem 0;vertical-align:top}.contributors-table th{opacity:.7}.contributors-table ::ng-deep .katex{font-size:1em}.aggregated-formulas-rules{max-height:50vh}\n"], dependencies: [{ kind: "directive", type: KatexDirective, selector: "[heKatex]", inputs: ["heKatex", "heKatexInline"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: FormulaBlockComponent, selector: "he-formula-block", inputs: ["formulas", "hasSubstitutions", "emptyTitle", "heading", "note", "showToggle", "collapsible", "open", "substituted"], outputs: ["openChange", "substitutedChange"] }, { kind: "component", type: CollapsibleBlockComponent, selector: "he-collapsible-block", inputs: ["heading", "collapsible", "open"], outputs: ["openChange"] }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12287
12421
  }
@@ -12530,7 +12664,7 @@ class NodeAggregationLogsComponent {
12530
12664
  * entries for one term (e.g. a measurement at two depths) collapse into one expandable group whose
12531
12665
  * rows are labelled by what tells them apart.
12532
12666
  */
12533
- this.groups = computed(() => {
12667
+ this.allGroups = computed(() => {
12534
12668
  const blankNodes = (this.node()?.[this.nodeKey()] ?? []);
12535
12669
  const logs = this.scopedJlog()?.[this.nodeKey()] ?? {};
12536
12670
  const open = this.openGroups();
@@ -12547,6 +12681,33 @@ class NodeAggregationLogsComponent {
12547
12681
  }));
12548
12682
  return { ...group, rows, isOpen: open.has(group.termId), observations: groupObservations(rows) };
12549
12683
  });
12684
+ }, ...(ngDevMode ? [{ debugName: "allGroups" }] : []));
12685
+ /**
12686
+ * The rules that produced these values, which the row's `Logs` panel shows. They are read from the
12687
+ * documentation shipped by `@hestia-earth/aggregation-engine`, so an older copy of it can document
12688
+ * none that apply to this aggregation - and the panel would then open on nothing.
12689
+ */
12690
+ this.hasRules = computed(() => aggregationSections({
12691
+ termType: this.termType(),
12692
+ termId: this.termId(),
12693
+ nodeKey: this.nodeKey(),
12694
+ worldAggregation: this.worldAggregation(),
12695
+ subAggregation: this.subAggregation()
12696
+ }).length > 0, ...(ngDevMode ? [{ debugName: "hasRules" }] : []));
12697
+ // filter the list of results by a single Term, as the recalculation logs do
12698
+ this.term = signal(undefined, ...(ngDevMode ? [{ debugName: "term" }] : []));
12699
+ this.typeaheadFocus = typeaheadFocus;
12700
+ this.allTerms = computed(() => this.allGroups()
12701
+ .map(({ term }) => term)
12702
+ .filter(Boolean), ...(ngDevMode ? [{ debugName: "allTerms" }] : []));
12703
+ this.enableFilterByTerm = computed(() => this.allTerms().length > 0, ...(ngDevMode ? [{ debugName: "enableFilterByTerm" }] : []));
12704
+ this.termFormatter = ({ name }) => name;
12705
+ this.suggestTerm = (text$) => text$.pipe(distinctUntilChanged(), map(value => suggestTerms(this.allTerms(), value)));
12706
+ this.groups = computed(() => {
12707
+ const selected = this.term();
12708
+ return selected
12709
+ ? this.allGroups().filter(({ term }) => term?.name?.toLowerCase().includes(selected.name.toLowerCase()))
12710
+ : this.allGroups();
12550
12711
  }, ...(ngDevMode ? [{ debugName: "groups" }] : []));
12551
12712
  }
12552
12713
  toggleGroup(group) {
@@ -12563,13 +12724,15 @@ class NodeAggregationLogsComponent {
12563
12724
  return `${row.index}-${row.label}`;
12564
12725
  }
12565
12726
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregationLogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12566
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregationLogsComponent, isStandalone: true, selector: "he-node-aggregation-logs", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, termType: { classPropertyName: "termType", publicName: "termType", isSignal: true, isRequired: false, transformFunction: null }, termId: { classPropertyName: "termId", publicName: "termId", isSignal: true, isRequired: false, transformFunction: null }, jlogParentKey: { classPropertyName: "jlogParentKey", publicName: "jlogParentKey", isSignal: true, isRequired: false, transformFunction: null }, jlogParentIndex: { classPropertyName: "jlogParentIndex", publicName: "jlogParentIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No aggregated data to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values, term: row.term })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\" let-term=\"term\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [term]=\"term\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeIdentityComponent, selector: "he-blank-node-identity", inputs: ["segments", "scalars", "type", "fallback"] }, { kind: "component", type: GuideOverlayComponent, selector: "he-guide-overlay", inputs: ["pageId", "width", "height", "positions"], outputs: ["widthChange", "heightChange"] }, { kind: "component", type: NodeAggregatedFormulasComponent, selector: "he-node-aggregated-formulas", inputs: ["termType", "termId", "nodeKey", "term", "values", "worldAggregation", "subAggregation", "substituted"], outputs: ["substitutedChange"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12727
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: NodeAggregationLogsComponent, isStandalone: true, selector: "he-node-aggregation-logs", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, termType: { classPropertyName: "termType", publicName: "termType", isSignal: true, isRequired: false, transformFunction: null }, termId: { classPropertyName: "termId", publicName: "termId", isSignal: true, isRequired: false, transformFunction: null }, jlogParentKey: { classPropertyName: "jlogParentKey", publicName: "jlogParentKey", isSignal: true, isRequired: false, transformFunction: null }, jlogParentIndex: { classPropertyName: "jlogParentIndex", publicName: "jlogParentIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">\n No aggregated data to show\n @if (term()) {\n <span>matching your search criteria</span>\n }\n <span>.</span>\n </p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n <!-- nothing opens the panel when no rule applies: it would open on an empty popover -->\n @if (hasRules()) {\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values, term: row.term })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n }\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\" let-term=\"term\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [term]=\"term\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "directive", type: NgbTypeahead, selector: "input[ngbTypeahead]", inputs: ["autocomplete", "container", "editable", "focusFirst", "inputFormatter", "ngbTypeahead", "resultFormatter", "resultTemplate", "selectOnExact", "showHint", "placement", "popperOptions", "popupClass"], outputs: ["selectItem"], exportAs: ["ngbTypeahead"] }, { kind: "component", type: DataTableComponent, selector: "he-data-table", inputs: ["minHeight", "maxHeight", "small"] }, { kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "component", type: NodeLinkComponent, selector: "he-node-link", inputs: ["node", "dataState", "showExternalLink", "linkClass"] }, { kind: "component", type: BlankNodeIdentityComponent, selector: "he-blank-node-identity", inputs: ["segments", "scalars", "type", "fallback"] }, { kind: "component", type: GuideOverlayComponent, selector: "he-guide-overlay", inputs: ["pageId", "width", "height", "positions"], outputs: ["widthChange", "heightChange"] }, { kind: "component", type: NodeAggregatedFormulasComponent, selector: "he-node-aggregated-formulas", inputs: ["termType", "termId", "nodeKey", "term", "values", "worldAggregation", "subAggregation", "substituted"], outputs: ["substitutedChange"] }, { kind: "pipe", type: CompoundPipe, name: "compound" }, { kind: "pipe", type: DefaultPipe, name: "default" }, { kind: "pipe", type: PrecisionPipe, name: "precision" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12567
12728
  }
12568
12729
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NodeAggregationLogsComponent, decorators: [{
12569
12730
  type: Component$1,
12570
12731
  args: [{ selector: 'he-node-aggregation-logs', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
12571
12732
  NgTemplateOutlet,
12733
+ FormsModule,
12572
12734
  NgbPopover,
12735
+ NgbTypeahead,
12573
12736
  CompoundPipe,
12574
12737
  DefaultPipe,
12575
12738
  PrecisionPipe,
@@ -12579,7 +12742,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
12579
12742
  BlankNodeIdentityComponent,
12580
12743
  GuideOverlayComponent,
12581
12744
  NodeAggregatedFormulasComponent
12582
- ], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">No aggregated data to show.</p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values, term: row.term })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\" let-term=\"term\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [term]=\"term\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"] }]
12745
+ ], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-my-2\">\n <div>\n <ng-content />\n </div>\n</div>\n\n<he-data-table class=\"is-mt-2 is-mb-1 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n <tr>\n <th class=\"width-auto has-border-right\">\n @if (enableFilterByTerm()) {\n <div class=\"field is-pb-1\">\n <div class=\"control is-expanded has-icons-right\">\n <input\n class=\"input search-input is-small\"\n [ngModel]=\"term()\"\n name=\"term\"\n placeholder=\"Select entry by name\"\n [ngbTypeahead]=\"suggestTerm\"\n [resultFormatter]=\"termFormatter\"\n [inputFormatter]=\"termFormatter\"\n [focusFirst]=\"true\"\n (focus)=\"typeaheadFocus($event)\"\n (selectItem)=\"term.set($event.item)\"\n container=\"body\"\n popupClass=\"is-small\" />\n <a class=\"icon is-small is-right\" [class.is-hidden]=\"!term()\" (click)=\"term.set(undefined)\">\n <he-svg-icon name=\"xmark\" />\n </a>\n </div>\n </div>\n }\n </th>\n <th class=\"has-border-right\"><span>Units</span></th>\n <th class=\"has-border-right\"><span>Aggregated</span></th>\n <th class=\"has-border-right\">\n <span title=\"The number of Cycles the value was aggregated from\">Observations</span>\n </th>\n <th class=\"has-border-right\"><span>Model</span></th>\n </tr>\n </thead>\n <tbody>\n @if (groups().length === 0) {\n <tr>\n <td class=\"has-border-right has-text-centered\" colspan=\"100\">\n <p class=\"is-p-1\">\n No aggregated data to show\n @if (term()) {\n <span>matching your search criteria</span>\n }\n <span>.</span>\n </p>\n </td>\n </tr>\n }\n @for (group of groups(); track trackByGroup($index, group)) {\n @let single = group.rows.length === 1;\n <tr [class.has-sub-rows]=\"group.canOpen\" [class.is-open]=\"group.isOpen\">\n <td class=\"width-auto has-border-right is-nowrap\" [attr.title]=\"group.term?.name\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4\">\n @if (group.canOpen) {\n <a class=\"open-node\" (click)=\"toggleGroup(group)\">\n <he-svg-icon [name]=\"group.isOpen ? 'chevron-down' : 'chevron-right'\" />\n </a>\n }\n <he-node-link class=\"is-inline-block is-pre-wrap is-pr-2\" [node]=\"group.term\">\n <span class=\"break-word\" [innerHtml]=\"group.term?.name | compound: group.term?.termType\"></span>\n </he-node-link>\n </div>\n </td>\n\n @if (single) {\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row: group.rows[0], term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row: group.rows[0] }\" />\n } @else {\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"group.term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n @if (group.valueFormula) {\n <span\n class=\"has-formula\"\n [ngbPopover]=\"group.valueFormula\"\n popoverClass=\"is-narrow\"\n triggers=\"click\"\n container=\"body\">\n {{ group.value | precision: 3 | default: '-' }}\n </span>\n } @else {\n {{ group.value | precision: 3 | default: '-' }}\n }\n </td>\n <td class=\"has-border-right is-nowrap\">{{ group.observations | default: '-' }}</td>\n <ng-container *ngTemplateOutlet=\"modelCell\" />\n }\n </tr>\n\n @if (!single && group.isOpen) {\n @for (row of group.rows; track trackByRow($index, row)) {\n <tr class=\"is-sub-row\">\n <td class=\"width-auto has-border-right\">\n <div class=\"is-flex is-align-items-flex-start is-gap-4 is-pl-3\">\n <he-svg-icon class=\"sub-sub-row-icon\" name=\"chevron-double-right\" />\n <he-blank-node-identity\n [segments]=\"row.segments\"\n [scalars]=\"row.scalars\"\n [type]=\"group.type\"\n [fallback]=\"'entry ' + (row.index + 1)\" />\n </div>\n </td>\n <ng-container *ngTemplateOutlet=\"valueCells; context: { row, term: group.term }\" />\n <ng-container *ngTemplateOutlet=\"modelCell; context: { row }\" />\n </tr>\n }\n }\n }\n </tbody>\n </table>\n</he-data-table>\n\n<ng-template #valueCells let-row=\"row\" let-term=\"term\">\n <td class=\"has-border-right\">\n <span class=\"is-nowrap\" [innerHtml]=\"term?.units | compound\"></span>\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.displayValue | precision: 3 | default: '-' }}\n </td>\n <td class=\"has-border-right is-nowrap\">\n {{ row.observations | default: '-' }}\n </td>\n</ng-template>\n\n<!-- `row` is left out on a term group: the rules are the same for every entry it holds, so the guide\n is shown there too, but the values to substitute belong to one entry - those stay on the sub-rows -->\n<ng-template #modelCell let-row=\"row\">\n <td class=\"has-border-right\">\n <div class=\"is-flex is-align-self-stretch is-align-items-center is-gap-8\">\n <span class=\"is-flex-grow-1 is-nowrap\">{{ modelName }}</span>\n\n <div class=\"is-flex is-gap-4 is-flex-shrink-0 is-align-items-center\">\n <!-- nothing opens the panel when no rule applies: it would open on an empty popover -->\n @if (hasRules()) {\n @if (row) {\n <span\n class=\"is-nowrap is-clickable\"\n [ngbPopover]=\"aggregationRules\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow\"\n triggers=\"manual\"\n #p=\"ngbPopover\"\n placement=\"bottom left right auto\"\n container=\"body\"\n (click)=\"p.isOpen() ? p.close() : p.open({ values: row.values, term: row.term })\">\n <span class=\"has-text-link\">Logs</span>\n </span>\n } @else {\n <span class=\"has-text-grey is-nowrap\">Expand for logs</span>\n }\n\n <div class=\"vertical-divider\"></div>\n }\n\n @if (guideEnabled) {\n <he-guide-overlay [pageId]=\"guidePage()\" [width]=\"500\" />\n } @else {\n <a [href]=\"guideHref()\" target=\"_blank\" rel=\"noopener\" (click)=\"$event.stopPropagation()\">\n <span>Docs</span>\n <he-svg-icon name=\"external-link\" class=\"ml-2\" />\n </a>\n }\n </div>\n </div>\n </td>\n</ng-template>\n\n<ng-template #aggregationRules let-values=\"values\" let-term=\"term\">\n <he-node-aggregated-formulas\n [termType]=\"termType()\"\n [termId]=\"termId()\"\n [nodeKey]=\"nodeKey()\"\n [term]=\"term\"\n [values]=\"values\"\n [worldAggregation]=\"worldAggregation()\"\n [subAggregation]=\"subAggregation()\" />\n</ng-template>\n", styles: [":host{display:block}:host .vertical-divider{width:1px;height:20px;background:#dbe3ea}:host .has-formula{cursor:help;border-bottom:1px dotted currentColor}::ng-deep .table{background-color:transparent}::ng-deep .table td.has-border-right{box-shadow:1px 0 #4c7194}::ng-deep .table td>div{min-height:24px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before,::ng-deep .table .is-sub-row>td:first-child:before{display:block;position:absolute;content:\" \";background-color:#4c719433;height:100%;width:1px;top:0;left:14px}::ng-deep .table .has-sub-rows.is-open>td:first-child:before{top:25px}::ng-deep .table .is-sub-row .open-node>he-svg-icon,::ng-deep .table .is-sub-row .sub-sub-row-icon{height:16px!important;width:16px!important}::ng-deep .table .is-sub-sub-row td:first-child{padding-left:24px}::ng-deep .table th:last-child,::ng-deep .table td:last-child{min-width:12rem}\n"] }]
12583
12746
  }], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: false }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], termType: [{ type: i0.Input, args: [{ isSignal: true, alias: "termType", required: false }] }], termId: [{ type: i0.Input, args: [{ isSignal: true, alias: "termId", required: false }] }], jlogParentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentKey", required: false }] }], jlogParentIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "jlogParentIndex", required: false }] }] } });
12584
12747
 
12585
12748
  const isValidDate = (date) => (date || '').trim().length === 10;
@@ -12978,13 +13141,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
12978
13141
  ], template: "@if (isGroupNode()) {\n <div class=\"tabs is-mb-1 | group-nodes-tabs\">\n <ul>\n @for (value of groupNodeValues(); track value) {\n <li\n [class.is-active]=\"selectedGroup() === value.value\"\n [ngbTooltip]=\"value.term.name\"\n placement=\"top\"\n container=\"body\">\n <a (click)=\"selectedGroup.set(value.value)\">\n <span class=\"is-capitalized is-pr-1\">{{ nodeKeyGroup() | pluralize: 1 }}:</span>\n <span>{{ value.value }}</span>\n </a>\n </li>\n }\n </ul>\n </div>\n}\n\n@if (isNodeKeyAllowed()) {\n @switch (selectedView()) {\n @case (View.table) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (hasData()) {\n @if (hasVisibleData() || !hideIdentical()) {\n <he-data-table class=\"is-mt-3 is-bordered\" [small]=\"true\" maxHeight=\"320\">\n <table class=\"table is-fullwidth is-narrow is-striped\">\n <thead>\n @if (dataKeys().length > 1) {\n <tr class=\"has-text-weight-bold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @let blankNodesCount = countGroupVisibleNodes(blankNodes);\n @if (blankNodesCount > 0) {\n <th [attr.colspan]=\"blankNodesCount\" [class.has-border-right]=\"!dataKeyLast\">\n <span>{{ dataKey | keyToLabel }}</span>\n </th>\n }\n }\n </tr>\n }\n <tr class=\"has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\"></th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.name\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <he-node-link [node]=\"node.value.term\">\n <span\n [innerHtml]=\"\n node.value.term.name | ellipsis: 30 | compound: node.value.term.termType\n \"></span>\n </he-node-link>\n </th>\n }\n }\n }\n </tr>\n <tr class=\"is-italic has-text-weight-semibold\">\n <th class=\"width-auto has-border-right\"></th>\n <th class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <a [href]=\"schemaBaseUrl + '/Cycle#functionalUnit'\" target=\"_blank\">Functional unit</a>\n </th>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n <th\n [attr.title]=\"node.value.term.units\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n <span [innerHtml]=\"node.value.term.units | compound\"></span>\n <he-terms-units-description class=\"is-inline-block is-ml-2\" [term]=\"node.value.term\" />\n </th>\n }\n }\n }\n </tr>\n </thead>\n <tbody>\n @for (cycle of cycles(); track nodeVersionKey(cycle); let cycleIndex = $index) {\n <tr [class.is-suggested]=\"$any(cycle).suggested\">\n <td class=\"width-auto has-border-right\" [attr.title]=\"defaultLabel(cycle)\">\n <he-node-link [node]=\"cycleNode(cycle)\">\n <span class=\"has-text-ellipsis is-ellipsis-3\">\n @if ($any(cycle).suggested) {\n <he-svg-icon name=\"compare\" />\n } @else {\n <span>{{ cycleIndex + 1 }}.</span>\n }\n <span class=\"is-pl-1\">{{ defaultLabel(cycle) }}</span>\n </span>\n </he-node-link>\n </td>\n <td class=\"has-border-right\" [class.is-hidden]=\"isGroupNode()\">\n <he-cycles-functional-unit-measure [cycle]=\"cycle\" />\n </td>\n @for (dataKey of dataKeys(); track dataKey; let dataKeyLast = $last) {\n @let blankNodes = data()[dataKey];\n @for (node of blankNodes; track node.value.term.name; let nodeLast = $last) {\n @if (node.value.visible) {\n @let cycleData = node.value.values[nodeVersionKey(cycle)];\n <td\n class=\"is-nowrap\"\n [class.has-border-right]=\"dataKeys().length > 1 && !dataKeyLast && nodeLast\">\n @if (cycleData) {\n <span\n class=\"trigger-popover\"\n [ngbPopover]=\"details\"\n autoClose=\"outside\"\n popoverClass=\"is-narrow is-overflow-visible\"\n placement=\"left bottom auto\"\n container=\"body\"\n [popoverContext]=\"{ data: cycleData, cycle, key: dataKey }\">\n <span pointer>\n {{ cycleData.propertyValue | precision: 3 | default: '-' }}\n </span>\n <he-blank-node-state\n class=\"ml-1\"\n [dataState]=\"dataState()\"\n [node]=\"cycleData.node\"\n key=\"value\" />\n </span>\n } @else {\n <span>-</span>\n }\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </he-data-table>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No differences between the nodes compared.</span>\n </div>\n }\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-justify-content-space-between is-mt-2\">\n <he-blank-node-state-notice\n [dataState]=\"dataState()\"\n [showDeleted]=\"firstNodeKey() === BlankNodesKey.emissions\" />\n <div class=\"is-flex is-flex-wrap-wrap is-gap-8\">\n @if (showHideZeroValues()) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideZeroValues'\"\n name=\"hideZeroValues\"\n [(ngModel)]=\"hideZeroValues\" />\n <label [for]=\"componentId() + 'hideZeroValues'\">\n <span>\n Hide\n <b>0</b>\n values\n </span>\n </label>\n </div>\n }\n @if (cycles().length > 1) {\n <div class=\"field is-relative is-mb-0\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded\"\n [id]=\"componentId() + 'hideIdenticalValues'\"\n name=\"hideIdenticalValues\"\n [(ngModel)]=\"hideIdenticalSelected\" />\n <label [for]=\"componentId() + 'hideIdenticalValues'\">\n <span>Hide identical values</span>\n </label>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"is-pt-3 has-text-centered\">\n <span>No data available</span>\n @if (filterTerm()) {\n <span class=\"is-pl-1\">matching your search criteria</span>\n }\n <span>.</span>\n @if (showSwitchToRecalculated()) {\n <span>\n Switch to\n <code>recalculated</code>\n version.\n </span>\n }\n </div>\n }\n }\n @case (View.chart) {\n @switch (firstNodeKey()) {\n @case (BlankNodesKey.emissions) {\n <he-cycles-emissions-chart [cycles]=\"cycles()\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-emissions-chart>\n }\n }\n }\n @case (View.aggregationLogs) {\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-aggregation-logs\n [node]=\"selectedNode()\"\n [nodeKey]=\"selectedNodeKey()\"\n [termType]=\"primaryProductTermType()\"\n [termId]=\"primaryProductTermId()\"\n [jlogParentKey]=\"nodeKeyGroup()\"\n [jlogParentIndex]=\"jlogParentIndex()\">\n @if (nodeKeys().length > 1) {\n <div class=\"tabs is-m-0\">\n <ul>\n @for (nodeKey of nodeKeys(); track nodeKey) {\n <li [class.is-active]=\"selectedNodeKey() === nodeKey\">\n <a (click)=\"selectedNodeKey.set(nodeKey)\">{{ nodeKey | keyToLabel }}</a>\n </li>\n }\n </ul>\n </div>\n }\n </he-node-aggregation-logs>\n }\n }\n @case (View.timeline) {\n <he-cycles-nodes-timeline\n [values]=\"timelineValues()\"\n [minDate]=\"selectedCycle().startDate\"\n [maxDate]=\"selectedCycle().endDate\">\n <ng-container *ngTemplateOutlet=\"selectView\" />\n </he-cycles-nodes-timeline>\n }\n @case (View.logs) {\n <!-- keep the cycle selector visible even when the selected cycle has no grouped node (e.g. a cycle\n with no transformations under \"Transformation: Inputs\"), so the user can switch to one that has -->\n <ng-container *ngTemplateOutlet=\"selectView\" />\n\n @if (selectedNode()) {\n <he-node-logs-models\n [node]=\"selectedNode()\"\n [cycle]=\"selectedNode()\"\n [nodeKey]=\"selectedNodeKey()\"\n [logsKey]=\"selectedLogsKey()\"\n [jlogParentKey]=\"nodeKeyGroup()\"\n [jlogParentIndex]=\"jlogParentIndex()\"\n [originalValues]=\"selectedOriginalValues()\"\n [recalculatedValues]=\"selectedRecalculatedValues()\"\n [filterTermTypes]=\"filterTermTypes()\">\n @if (nodeKeys().length > 1) {\n <div class=\"tabs is-m-0\">\n <ul>\n @for (nodeKey of nodeKeys(); track nodeKey) {\n <li [class.is-active]=\"selectedNodeKey() === nodeKey\">\n <a (click)=\"selectedNodeKey.set(nodeKey)\">{{ nodeKey | keyToLabel }}</a>\n </li>\n }\n </ul>\n </div>\n }\n </he-node-logs-models>\n } @else if (isGroupNode()) {\n <p class=\"is-p-3 has-text-grey\">\n No recalculation logs for the selected {{ nodeKeyGroup() | pluralize: 1 }} in this cycle \u2014 select another\n cycle above.\n </p>\n }\n }\n }\n}\n\n<ng-template #selectView>\n <div class=\"is-flex is-gap-8 is-align-items-center is-justify-content-space-between\">\n <div class=\"is-flex is-gap-8 is-align-items-center\">\n @if (selectedView() === View.table) {\n @if (hasData()) {\n <button class=\"button is-small is-ghost is-p-2\" (click)=\"showDownload()\">\n <he-svg-icon name=\"download\" />\n </button>\n }\n <he-search-extend\n class=\"is-secondary\"\n collapsedClass=\"is-p-2\"\n placeholder=\"Filter terms by name\"\n (searchText)=\"filterTerm.set($event)\" />\n } @else if (showSelectCycle()) {\n <ng-container *ngTemplateOutlet=\"selectCycle\" />\n }\n </div>\n\n @if (views()?.length > 1) {\n <div class=\"field has-addons button-segments\">\n @for (view of views(); track view) {\n <div class=\"control\">\n <button\n class=\"button is-small\"\n [class.is-selected]=\"selectedView() === view\"\n (click)=\"selectedView.set(view)\">\n <he-svg-icon\n name=\"checkmark\"\n aria-hidden=\"true\"\n class=\"is-hidden-mobile\"\n [class.is-hidden-tablet]=\"selectedView() !== view\" />\n <he-svg-icon\n [name]=\"viewIcon[view]\"\n aria-hidden=\"true\"\n [class.is-hidden-tablet]=\"selectedView() === view\" />\n <span class=\"is-hidden-mobile\">{{ view }}</span>\n </button>\n </div>\n }\n </div>\n }\n </div>\n</ng-template>\n\n<ng-template #selectCycle>\n @if (selectableCycles().length > 1) {\n <div class=\"field is-horizontal is-mb-0\">\n <div class=\"field-label is-normal\">\n <label class=\"label has-text-secondary is-nowrap\" for=\"selectCycle\">Cycle</label>\n </div>\n <div class=\"field-body\">\n <div class=\"field\">\n <div class=\"control is-expanded\">\n <div class=\"select is-small is-fullwidth\">\n <select [value]=\"selectedIndex()\" (change)=\"selectIndex($event)\" if=\"selectCycle\">\n @for (entry of selectableCycles(); track nodeVersionKey(entry.cycle)) {\n <option [value]=\"entry.index\">{{ entry.index + 1 }}. {{ defaultLabel(entry.cycle) }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n</ng-template>\n\n<ng-template #details let-node=\"cycle\" let-data=\"data\" let-key=\"key\">\n <p>\n <b>{{ defaultLabel(node) }}</b>\n </p>\n <he-node-value-details\n class=\"is-overflow-visible\"\n [data]=\"data\"\n [dataState]=\"dataState()\"\n [nodeType]=\"node['@type']\"\n [dataKey]=\"key\"\n [aggregated]=\"node.aggregated\" />\n</ng-template>\n", styles: [":host{display:block}he-data-table ::ng-deep .table thead tr th:nth-child(2),he-data-table ::ng-deep .table tbody tr td:nth-child(2){max-width:106px;width:106px}\n"] }]
12979
13142
  }], ctorParameters: () => [], propDecorators: { dataState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataState", required: false }] }], nodeKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKeys", required: true }] }], nodeKeyGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKeyGroup", required: false }] }], hideIdenticalValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideIdenticalValues", required: false }] }] } });
12980
13143
 
13144
+ const labelMaxLength = 25;
12981
13145
  class CyclesResultComponent {
12982
13146
  constructor() {
12983
13147
  this.cycles = input.required(...(ngDevMode ? [{ debugName: "cycles" }] : []));
12984
13148
  this.productsPerCycle = computed(() => groupNodesByTerm(this.cycles(), 'products'), ...(ngDevMode ? [{ debugName: "productsPerCycle" }] : []));
12985
- this.chartLabels = computed(() => this.cycles()
12986
- .map((cycle, index) => ({ cycle, index }))
12987
- .map(({ cycle, index }) => `${index + 1}. ${defaultLabel(cycle)}`), ...(ngDevMode ? [{ debugName: "chartLabels" }] : []));
13149
+ this.chartLabels = computed(() => nodeChartLabels(this.cycles(), labelMaxLength), ...(ngDevMode ? [{ debugName: "chartLabels" }] : []));
12988
13150
  this.chartDatasets = computed(() => Object.values(this.productsPerCycle() ?? {}).map(({ term: { name, units, '@id': termId }, values }, index) => {
12989
13151
  const color = getColor(index);
12990
13152
  return {
@@ -13032,7 +13194,7 @@ class CyclesResultComponent {
13032
13194
  };
13033
13195
  Chart.defaults.scales.category.ticks.callback = function (val) {
13034
13196
  const label = this.getLabelForValue(val);
13035
- return ellipsis(`${label}`, 25);
13197
+ return ellipsis(`${label}`, labelMaxLength);
13036
13198
  };
13037
13199
  }
13038
13200
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CyclesResultComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
@@ -15876,6 +16038,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
15876
16038
  }]
15877
16039
  }] });
15878
16040
 
16041
+ /**
16042
+ * The product of an impact assessment, which aggregated ones carry as a `term`.
16043
+ */
16044
+ const impactAssessmentProduct = (impactAssessment) => impactAssessment?.product?.term || impactAssessment?.term;
16045
+
15879
16046
  /* eslint-disable complexity */
15880
16047
  var ChartNodeType;
15881
16048
  (function (ChartNodeType) {
@@ -17089,7 +17256,7 @@ class ImpactAssessmentsIndicatorBreakdownChartComponent {
17089
17256
  return [blankNodeTermId, modelId, impactTermId].join('-');
17090
17257
  }
17091
17258
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ImpactAssessmentsIndicatorBreakdownChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
17092
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ImpactAssessmentsIndicatorBreakdownChartComponent, isStandalone: true, selector: "he-impact-assessments-indicator-breakdown-chart", inputs: { impactAssessment: { classPropertyName: "impactAssessment", publicName: "impactAssessment", isSignal: true, isRequired: true, transformFunction: null }, indicators: { classPropertyName: "indicators", publicName: "indicators", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart\" [chartExportFn]=\"chartExportFn\" [exportFormats]=\"exportFormats\" />\n\n <ng-content />\n</div>\n\n@if (logsResource.isLoading()) {\n <div class=\"has-text-center py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </div>\n} @else if (terms()?.length) {\n <div class=\"is-flex is-align-items-center is-gap-12 is-mb-2 | breakdown-actions-table\">\n @if (terms()?.length) {\n <div class=\"control is-expanded is-flex-grow-1\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedTermId\" name=\"selectedTermId\">\n @for (term of terms(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (methods()?.length) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedMethodId\" name=\"selectedMethodId\">\n @for (term of methods(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedCategory\" name=\"selectedCategory\">\n <option [ngValue]=\"undefined\">No Disaggregation</option>\n @for (category of categories; track category.key) {\n <option [value]=\"category.key\">{{ category.label }}</option>\n }\n </select>\n </div>\n </div>\n\n <div class=\"is-flex is-justify-content-space-between\">\n <div class=\"field is-relative is-mb-0 is-hidden-tablet\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n id=\"displayValue\"\n name=\"displayValue\"\n [(ngModel)]=\"displayValue\" />\n <label for=\"displayValue\">Show numerical value</label>\n </div>\n </div>\n </div>\n}\n\n@if (!logsResource.isLoading() && noData()) {\n <div class=\"chart-area-border\">\n <p class=\"has-text-centered\">\n <span>No breakdown available for</span>\n @if (selectedTerm()) {\n <span class=\"is-pl-1\">{{ selectedTerm().name }}</span>\n }\n @if (selectedMethod()) {\n <span class=\"is-pl-1\">({{ selectedMethod().name }})</span>\n }\n <span>.</span>\n </p>\n </div>\n}\n\n<he-horizontal-bar-chart\n #chart=\"horizontalBarChart\"\n class=\"is-relative h-100\"\n [class.is-hidden]=\"!logsResource.isLoading() && noData()\"\n [title]=\"selectedTerm()?.units\"\n [config]=\"configuration()\"\n [data]=\"chartData()\"\n [max]=\"totalPositive()\"\n [maximumValues]=\"maximumValues()\"\n [showExportButton]=\"false\"\n [showNegativeValues]=\"true\"\n [tooltipFn]=\"chartTooltipFn\"\n [afterBarDrawSettings]=\"afterBarDrawSettings()\" />\n", styles: [":host{display:block;overflow:visible}@media screen and (max-width:767px){.chart-area-border{padding:0}}@media screen and (max-width:767px){.breakdown-actions-table{flex-direction:column;align-items:flex-start!important}}.breakdown-legend{background:#fafafa}.breakdown-legend--color{width:8px;height:8px;border-radius:50%}\n"], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HorizontalBarChartComponent, selector: "he-horizontal-bar-chart", inputs: ["tooltipFn", "afterBarDrawSettings", "minHeight", "maxHeight"], exportAs: ["horizontalBarChart"] }, { kind: "component", type: ChartExportButtonComponent, selector: "he-chart-export-button", inputs: ["buttonClass", "chart", "config", "exportFormats", "chartExportFn"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
17259
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ImpactAssessmentsIndicatorBreakdownChartComponent, isStandalone: true, selector: "he-impact-assessments-indicator-breakdown-chart", inputs: { impactAssessment: { classPropertyName: "impactAssessment", publicName: "impactAssessment", isSignal: true, isRequired: true, transformFunction: null }, indicators: { classPropertyName: "indicators", publicName: "indicators", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart\" [chartExportFn]=\"chartExportFn\" [exportFormats]=\"exportFormats\" />\n\n <ng-content />\n</div>\n\n@if (logsResource.isLoading()) {\n <div class=\"has-text-center py-3\">\n <he-svg-icon name=\"loading\" animation=\"spin\" size=\"40\" />\n </div>\n} @else if (terms()?.length) {\n <div class=\"is-flex is-align-items-center is-gap-12 is-mb-2 | breakdown-actions-table\">\n @if (terms()?.length) {\n <div class=\"control is-expanded is-flex-grow-1\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedTermId\" name=\"selectedTermId\">\n @for (term of terms(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (methods()?.length) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedMethodId\" name=\"selectedMethodId\">\n @for (term of methods(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedCategory\" name=\"selectedCategory\">\n <option [ngValue]=\"undefined\">No Disaggregation</option>\n @for (category of categories; track category.key) {\n <option [value]=\"category.key\">{{ category.label }}</option>\n }\n </select>\n </div>\n </div>\n\n <div class=\"is-flex is-justify-content-space-between\">\n <div class=\"field is-relative is-mb-0 is-hidden-tablet\">\n <input\n type=\"checkbox\"\n class=\"switch is-small is-rounded is-secondary\"\n id=\"displayValue\"\n name=\"displayValue\"\n [(ngModel)]=\"displayValue\" />\n <label for=\"displayValue\">Show numerical value</label>\n </div>\n </div>\n </div>\n}\n\n@if (!logsResource.isLoading() && noData()) {\n <div class=\"chart-area-border\">\n <p class=\"has-text-centered\">\n <span>No breakdown available for</span>\n @if (selectedTerm()) {\n <span class=\"is-pl-1\">{{ selectedTerm().name }}</span>\n }\n @if (selectedMethod()) {\n <span class=\"is-pl-1\">({{ selectedMethod().name }})</span>\n }\n <span>.</span>\n </p>\n </div>\n}\n\n<he-horizontal-bar-chart\n #chart=\"horizontalBarChart\"\n class=\"is-relative h-100\"\n [class.is-hidden]=\"!logsResource.isLoading() && noData()\"\n [title]=\"selectedTerm()?.units\"\n [config]=\"configuration()\"\n [data]=\"chartData()\"\n [max]=\"totalPositive()\"\n [maximumValues]=\"maximumValues()\"\n [showExportButton]=\"false\"\n [showNegativeValues]=\"true\"\n [tooltipFn]=\"chartTooltipFn\"\n [afterBarDrawSettings]=\"afterBarDrawSettings()\" />\n", styles: [":host{display:block;overflow:visible}@media screen and (max-width:767px){.chart-area-border{padding:0}}@media screen and (max-width:767px){.breakdown-actions-table{flex-direction:column;align-items:flex-start!important}}.breakdown-legend{background:#fafafa}.breakdown-legend--color{width:8px;height:8px;border-radius:50%}\n"], dependencies: [{ kind: "component", type: HESvgIconComponent, selector: "he-svg-icon", inputs: ["name", "size", "animation"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HorizontalBarChartComponent, selector: "he-horizontal-bar-chart", inputs: ["tooltipFn", "afterBarDrawSettings", "axisTooltipFn", "axisLabelClickFn", "minHeight", "maxHeight"], exportAs: ["horizontalBarChart"] }, { kind: "component", type: ChartExportButtonComponent, selector: "he-chart-export-button", inputs: ["buttonClass", "chart", "config", "exportFormats", "chartExportFn"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
17093
17260
  }
17094
17261
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ImpactAssessmentsIndicatorBreakdownChartComponent, decorators: [{
17095
17262
  type: Component$1,
@@ -17097,10 +17264,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
17097
17264
  }], ctorParameters: () => [], propDecorators: { impactAssessment: [{ type: i0.Input, args: [{ isSignal: true, alias: "impactAssessment", required: true }] }], indicators: [{ type: i0.Input, args: [{ isSignal: true, alias: "indicators", required: false }] }] } });
17098
17265
 
17099
17266
  const impactValue = (impact, values) => (values[nodeVersionKey(impact)]?.nodes[0] || { value: 0 }).value;
17100
- const impactName = (impact, index) => `${index + 1}. ${defaultLabel(impact)}`;
17101
17267
  const termAllowed$1 = (filterTermTypes = [], term) => !filterTermTypes?.length || (filterTermTypes || []).includes(term?.termType);
17102
17268
  class ImpactAssessmentsIndicatorsChartComponent {
17103
17269
  constructor() {
17270
+ this.route = inject(ActivatedRoute);
17104
17271
  this.nodeStoreService = inject(HeNodeStoreService);
17105
17272
  this.nodeService = inject(HeNodeService);
17106
17273
  this.emissionCategoryService = inject(CyclesEmissionsCategoryService);
@@ -17111,9 +17278,19 @@ class ImpactAssessmentsIndicatorsChartComponent {
17111
17278
  this.filterTermTypes = input([], ...(ngDevMode ? [{ debugName: "filterTermTypes" }] : []));
17112
17279
  this.categories = this.emissionCategoryService.categories;
17113
17280
  this.selectedCategory = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedCategory" }] : []));
17114
- this.impactAssessments = computed(() => this.nodeStoreService.findByState(NodeType.ImpactAssessment, DataState.recalculated), ...(ngDevMode ? [{ debugName: "impactAssessments" }] : []));
17281
+ this.dataVersion = toSignal(this.route.queryParams.pipe(map(({ dataVersion }) => dataVersion)));
17282
+ this.allImpactAssessments = computed(() => this.nodeStoreService.findByState(NodeType.ImpactAssessment, DataState.recalculated), ...(ngDevMode ? [{ debugName: "allImpactAssessments" }] : []));
17283
+ this.products = computed(() => uniqBy(this.allImpactAssessments().map(impactAssessmentProduct).filter(Boolean), '@id').sort((a, b) => a.name.localeCompare(b.name)), ...(ngDevMode ? [{ debugName: "products" }] : []));
17284
+ this.selectedProductId = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedProductId" }] : []));
17285
+ // labels are built from every impact assessment, so a bar keeps the number it has in the table
17286
+ // view whichever product is selected
17287
+ this.labels = computed(() => nodeChartLabels(this.allImpactAssessments(), chartLabelMaxLength), ...(ngDevMode ? [{ debugName: "labels" }] : []));
17288
+ this.displayedImpactAssessments = computed(() => this.allImpactAssessments()
17289
+ .map((impactAssessment, index) => ({ impactAssessment, index, label: this.labels()[index] }))
17290
+ .filter(({ impactAssessment }) => !this.selectedProductId() || impactAssessmentProduct(impactAssessment)?.['@id'] === this.selectedProductId()), ...(ngDevMode ? [{ debugName: "displayedImpactAssessments" }] : []));
17291
+ this.impactAssessments = computed(() => this.displayedImpactAssessments().map(({ impactAssessment }) => impactAssessment), ...(ngDevMode ? [{ debugName: "impactAssessments" }] : []));
17115
17292
  this.contributionsResource = rxResource({
17116
- params: () => ({ impactAssessments: this.impactAssessments() }),
17293
+ params: () => ({ impactAssessments: this.allImpactAssessments() }),
17117
17294
  stream: ({ params: { impactAssessments } }) => from(impactAssessments).pipe(mergeMap(impactAssessment => this.nodeService.getContributions$(impactAssessment).pipe(map(contributions => ({
17118
17295
  impactAssessment,
17119
17296
  contributions: simplifyContributions(impactAssessment, contributions)
@@ -17123,7 +17300,7 @@ class ImpactAssessmentsIndicatorsChartComponent {
17123
17300
  }, {}))
17124
17301
  });
17125
17302
  this.contributionsPerImpactAssessment = computed(() => this.contributionsResource.value() ?? {}, ...(ngDevMode ? [{ debugName: "contributionsPerImpactAssessment" }] : []));
17126
- this.termsMap = computed(() => this.impactAssessments()
17303
+ this.termsMap = computed(() => this.allImpactAssessments()
17127
17304
  .flatMap(impact => [...impact.emissionsResourceUse, ...impact.impacts])
17128
17305
  .reduce((prev, curr) => {
17129
17306
  prev[curr.term['@id']] = curr.term;
@@ -17149,12 +17326,12 @@ class ImpactAssessmentsIndicatorsChartComponent {
17149
17326
  this.methods = computed(() => unique(this.termToMethods()[this.selectedTerm()?.['@id']]), ...(ngDevMode ? [{ debugName: "methods" }] : []));
17150
17327
  this.selectedMethod = computed(() => this.methods().find(term => term['@id'] == this.selectedMethodId()), ...(ngDevMode ? [{ debugName: "selectedMethod" }] : []));
17151
17328
  this.groupedValues = computed(() => this.indicatorPerImpactAssessment()?.[this.selectedTerm()?.name]?.values || {}, ...(ngDevMode ? [{ debugName: "groupedValues" }] : []));
17152
- this.selectedContributionData = computed(() => Object.fromEntries(Object.entries(this.contributionsPerImpactAssessment()).map(([id, contributions]) => [
17153
- id,
17154
- contributions?.[this.selectedTermId()]?.[this.selectedMethodId()] || {}
17329
+ this.selectedContributionData = computed(() => Object.fromEntries(this.impactAssessments().map(impact => [
17330
+ nodeVersionKey(impact),
17331
+ this.contributionsPerImpactAssessment()[nodeVersionKey(impact)]?.[this.selectedTermId()]?.[this.selectedMethodId()] || {}
17155
17332
  ])), ...(ngDevMode ? [{ debugName: "selectedContributionData" }] : []));
17156
17333
  this.contributionTerms = computed(() => unique(Object.values(this.selectedContributionData()).flatMap(v => Object.keys(v))), ...(ngDevMode ? [{ debugName: "contributionTerms" }] : []));
17157
- this.enableContributions = computed(() => Object.values(this.contributionsPerImpactAssessment())?.length === this.impactAssessments()?.length, ...(ngDevMode ? [{ debugName: "enableContributions" }] : []));
17334
+ this.enableContributions = computed(() => this.impactAssessments().every(impact => !!this.contributionsPerImpactAssessment()[nodeVersionKey(impact)]), ...(ngDevMode ? [{ debugName: "enableContributions" }] : []));
17158
17335
  this.contributionData = computed(() => this.contributionTerms().map((term, index) => ({
17159
17336
  id: term,
17160
17337
  label: this.termsMap()[term]?.name || term,
@@ -17165,12 +17342,14 @@ class ImpactAssessmentsIndicatorsChartComponent {
17165
17342
  ? this.contributionData()
17166
17343
  .filter(({ values }) => !values.every(v => !v))
17167
17344
  .sort((a, b) => Math.max(...b.values.map(v => Math.abs(v))) - Math.max(...a.values.map(v => Math.abs(v))))
17168
- : this.impactAssessments().map((impact, index) => ({
17169
- label: impactName(impact, index),
17170
- count: impactValue(impact, this.groupedValues()),
17171
- color: listColor(impact, index)
17345
+ : this.displayedImpactAssessments().map(({ impactAssessment, index, label }) => ({
17346
+ label,
17347
+ count: impactValue(impactAssessment, this.groupedValues()),
17348
+ color: listColor(impactAssessment, index)
17172
17349
  })), ...(ngDevMode ? [{ debugName: "chartData" }] : []));
17173
- this.chartLabels = computed(() => this.impactAssessments().map((impact, index) => impactName(impact, index)), ...(ngDevMode ? [{ debugName: "chartLabels" }] : []));
17350
+ this.chartLabels = computed(() => this.displayedImpactAssessments().map(({ label }) => label), ...(ngDevMode ? [{ debugName: "chartLabels" }] : []));
17351
+ this.axisTooltipFn = (label, index) => this.impactLabelTooltip(index) || label;
17352
+ this.axisLabelClickFn = index => this.openImpactAssessment(index);
17174
17353
  this.exportFormats = computed(() => [
17175
17354
  ...exportFormats,
17176
17355
  this.enableContributions()
@@ -17211,12 +17390,22 @@ class ImpactAssessmentsIndicatorsChartComponent {
17211
17390
  }
17212
17391
  });
17213
17392
  }
17393
+ impactLabelTooltip(index) {
17394
+ const impactAssessment = this.displayedImpactAssessments()[index]?.impactAssessment;
17395
+ return impactAssessment
17396
+ ? [`${index + 1}. ${defaultLabel(impactAssessment)}`, impactAssessment['@id']].filter(Boolean).join('<br />')
17397
+ : '';
17398
+ }
17399
+ openImpactAssessment(index) {
17400
+ const impactAssessment = this.displayedImpactAssessments()[index]?.impactAssessment;
17401
+ return impactAssessment ? openNodeUrl(impactAssessment, this.dataVersion()) : undefined;
17402
+ }
17214
17403
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ImpactAssessmentsIndicatorsChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
17215
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ImpactAssessmentsIndicatorsChartComponent, isStandalone: true, selector: "he-impact-assessments-indicators-chart", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypes: { classPropertyName: "filterTermTypes", publicName: "filterTermTypes", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "barChart", first: true, predicate: HorizontalBarChartComponent, descendants: true, isSignal: true }, { propertyName: "contributionChart", first: true, predicate: ContributionChartComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart()\" [chartExportFn]=\"chartExportFn\" [exportFormats]=\"exportFormats()\" />\n\n <ng-content />\n</div>\n\n@if (terms()?.length) {\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-gap-12 is-mb-2 | breakdown-actions-table\">\n @if (terms()?.length) {\n <div class=\"control is-expanded is-flex-grow-1\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedTermId\" name=\"selectedTermId\">\n @for (term of terms(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (methods()?.length) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedMethodId\" name=\"selectedMethodId\">\n @for (term of methods(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (enableContributions()) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedCategory\" name=\"selectedCategory\">\n <option [ngValue]=\"undefined\">No Disaggregation</option>\n @for (category of categories; track category.key) {\n <option [value]=\"category.key\">{{ category.label }}</option>\n }\n </select>\n </div>\n </div>\n }\n </div>\n}\n\n@if (enableContributions()) {\n <he-contribution-chart\n class=\"is-relative h-100\"\n [label]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"chartLabels()\"\n [category]=\"selectedCategory()\"\n [showExportButton]=\"false\"\n [showLegend]=\"false\" />\n} @else {\n <he-horizontal-bar-chart\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [showExportButton]=\"false\" />\n}\n", styles: [":host{display:block;overflow:visible}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HorizontalBarChartComponent, selector: "he-horizontal-bar-chart", inputs: ["tooltipFn", "afterBarDrawSettings", "minHeight", "maxHeight"], exportAs: ["horizontalBarChart"] }, { kind: "component", type: ContributionChartComponent, selector: "he-contribution-chart", inputs: ["data", "labels", "label", "showExportButton", "showLegend", "config", "minHeight", "maxHeight", "height", "category"], exportAs: ["contributionChart"] }, { kind: "component", type: ChartExportButtonComponent, selector: "he-chart-export-button", inputs: ["buttonClass", "chart", "config", "exportFormats", "chartExportFn"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
17404
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ImpactAssessmentsIndicatorsChartComponent, isStandalone: true, selector: "he-impact-assessments-indicators-chart", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, filterTermTypes: { classPropertyName: "filterTermTypes", publicName: "filterTermTypes", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "barChart", first: true, predicate: HorizontalBarChartComponent, descendants: true, isSignal: true }, { propertyName: "contributionChart", first: true, predicate: ContributionChartComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart()\" [chartExportFn]=\"chartExportFn\" [exportFormats]=\"exportFormats()\" />\n\n <ng-content />\n</div>\n\n@if (terms()?.length || products()?.length > 1) {\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-gap-12 is-mb-2 | breakdown-actions-table\">\n @if (terms()?.length) {\n <div class=\"control is-expanded is-flex-grow-1\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedTermId\" name=\"selectedTermId\">\n @for (term of terms(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (products()?.length > 1) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedProductId\" name=\"selectedProductId\">\n <option [ngValue]=\"undefined\">All products</option>\n @for (product of products(); track product['@id']) {\n <option [value]=\"product['@id']\">{{ product.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (methods()?.length) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedMethodId\" name=\"selectedMethodId\">\n @for (term of methods(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (enableContributions()) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedCategory\" name=\"selectedCategory\">\n <option [ngValue]=\"undefined\">No Disaggregation</option>\n @for (category of categories; track category.key) {\n <option [value]=\"category.key\">{{ category.label }}</option>\n }\n </select>\n </div>\n </div>\n }\n </div>\n}\n\n@if (enableContributions()) {\n <he-contribution-chart\n class=\"is-relative h-100\"\n [label]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"chartLabels()\"\n [category]=\"selectedCategory()\"\n [axisTooltipFn]=\"axisTooltipFn\"\n [axisLabelClickFn]=\"axisLabelClickFn\"\n [showExportButton]=\"false\"\n [showLegend]=\"false\" />\n} @else {\n <he-horizontal-bar-chart\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [axisTooltipFn]=\"axisTooltipFn\"\n [axisLabelClickFn]=\"axisLabelClickFn\"\n [showExportButton]=\"false\" />\n}\n", styles: [":host{display:block;overflow:visible}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HorizontalBarChartComponent, selector: "he-horizontal-bar-chart", inputs: ["tooltipFn", "afterBarDrawSettings", "axisTooltipFn", "axisLabelClickFn", "minHeight", "maxHeight"], exportAs: ["horizontalBarChart"] }, { kind: "component", type: ContributionChartComponent, selector: "he-contribution-chart", inputs: ["data", "labels", "label", "showExportButton", "showLegend", "config", "axisTooltipFn", "axisLabelClickFn", "minHeight", "maxHeight", "height", "category"], exportAs: ["contributionChart"] }, { kind: "component", type: ChartExportButtonComponent, selector: "he-chart-export-button", inputs: ["buttonClass", "chart", "config", "exportFormats", "chartExportFn"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
17216
17405
  }
17217
17406
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ImpactAssessmentsIndicatorsChartComponent, decorators: [{
17218
17407
  type: Component$1,
17219
- args: [{ selector: 'he-impact-assessments-indicators-chart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, HorizontalBarChartComponent, ContributionChartComponent, ChartExportButtonComponent], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart()\" [chartExportFn]=\"chartExportFn\" [exportFormats]=\"exportFormats()\" />\n\n <ng-content />\n</div>\n\n@if (terms()?.length) {\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-gap-12 is-mb-2 | breakdown-actions-table\">\n @if (terms()?.length) {\n <div class=\"control is-expanded is-flex-grow-1\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedTermId\" name=\"selectedTermId\">\n @for (term of terms(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (methods()?.length) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedMethodId\" name=\"selectedMethodId\">\n @for (term of methods(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (enableContributions()) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedCategory\" name=\"selectedCategory\">\n <option [ngValue]=\"undefined\">No Disaggregation</option>\n @for (category of categories; track category.key) {\n <option [value]=\"category.key\">{{ category.label }}</option>\n }\n </select>\n </div>\n </div>\n }\n </div>\n}\n\n@if (enableContributions()) {\n <he-contribution-chart\n class=\"is-relative h-100\"\n [label]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"chartLabels()\"\n [category]=\"selectedCategory()\"\n [showExportButton]=\"false\"\n [showLegend]=\"false\" />\n} @else {\n <he-horizontal-bar-chart\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [showExportButton]=\"false\" />\n}\n", styles: [":host{display:block;overflow:visible}\n"] }]
17408
+ args: [{ selector: 'he-impact-assessments-indicators-chart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, HorizontalBarChartComponent, ContributionChartComponent, ChartExportButtonComponent], template: "<div class=\"is-flex is-gap-8 is-justify-content-space-between is-align-items-center is-mb-3\">\n <he-chart-export-button [chart]=\"chart()\" [chartExportFn]=\"chartExportFn\" [exportFormats]=\"exportFormats()\" />\n\n <ng-content />\n</div>\n\n@if (terms()?.length || products()?.length > 1) {\n <div class=\"is-flex is-align-items-center is-flex-wrap-wrap is-gap-12 is-mb-2 | breakdown-actions-table\">\n @if (terms()?.length) {\n <div class=\"control is-expanded is-flex-grow-1\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedTermId\" name=\"selectedTermId\">\n @for (term of terms(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (products()?.length > 1) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedProductId\" name=\"selectedProductId\">\n <option [ngValue]=\"undefined\">All products</option>\n @for (product of products(); track product['@id']) {\n <option [value]=\"product['@id']\">{{ product.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (methods()?.length) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedMethodId\" name=\"selectedMethodId\">\n @for (term of methods(); track term['@id']) {\n <option [value]=\"term['@id']\">{{ term.name }}</option>\n }\n </select>\n </div>\n </div>\n }\n @if (enableContributions()) {\n <div class=\"control is-expanded is-flex-shrink-0\">\n <div class=\"select is-fullwidth is-small\">\n <select [(ngModel)]=\"selectedCategory\" name=\"selectedCategory\">\n <option [ngValue]=\"undefined\">No Disaggregation</option>\n @for (category of categories; track category.key) {\n <option [value]=\"category.key\">{{ category.label }}</option>\n }\n </select>\n </div>\n </div>\n }\n </div>\n}\n\n@if (enableContributions()) {\n <he-contribution-chart\n class=\"is-relative h-100\"\n [label]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [labels]=\"chartLabels()\"\n [category]=\"selectedCategory()\"\n [axisTooltipFn]=\"axisTooltipFn\"\n [axisLabelClickFn]=\"axisLabelClickFn\"\n [showExportButton]=\"false\"\n [showLegend]=\"false\" />\n} @else {\n <he-horizontal-bar-chart\n class=\"is-relative h-100\"\n [title]=\"selectedTerm()?.units\"\n [data]=\"chartData()\"\n [axisTooltipFn]=\"axisTooltipFn\"\n [axisLabelClickFn]=\"axisLabelClickFn\"\n [showExportButton]=\"false\" />\n}\n", styles: [":host{display:block;overflow:visible}\n"] }]
17220
17409
  }], ctorParameters: () => [], propDecorators: { barChart: [{ type: i0.ViewChild, args: [i0.forwardRef(() => HorizontalBarChartComponent), { isSignal: true }] }], contributionChart: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ContributionChartComponent), { isSignal: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: false }] }], filterTermTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterTermTypes", required: false }] }] } });
17221
17410
 
17222
17411
  var View$1;
@@ -17267,6 +17456,7 @@ class ImpactAssessmentsProductsComponent {
17267
17456
  this.filterTermTypes = input([], ...(ngDevMode ? [{ debugName: "filterTermTypes" }] : []));
17268
17457
  this.enableFilterMethodModel = input(false, ...(ngDevMode ? [{ debugName: "enableFilterMethodModel" }] : []));
17269
17458
  this.schemaBaseUrl = schemaBaseUrl();
17459
+ this.productTerm = impactAssessmentProduct;
17270
17460
  this.nodeVersionKey = nodeVersionKey;
17271
17461
  this.countGroupVisibleNodes = countGroupVisibleNodes;
17272
17462
  this.headerKeys = computed(() => [
@@ -17362,9 +17552,6 @@ class ImpactAssessmentsProductsComponent {
17362
17552
  cycleLabel({ '@id': id, name }) {
17363
17553
  return name || this.cycles().find(v => v['@id'] === id)?.name;
17364
17554
  }
17365
- productTerm(impactAssessment) {
17366
- return impactAssessment.product?.term || impactAssessment.term;
17367
- }
17368
17555
  selectIndex({ target: { value } }) {
17369
17556
  this.selectedIndex.set(+value);
17370
17557
  }
@@ -18065,5 +18252,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
18065
18252
  * Generated bundle index. Do not edit.
18066
18253
  */
18067
18254
 
18068
- export { ARRAY_DELIMITER, ApplyPurePipe, BarChartComponent, BibliographiesSearchConfirmComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, CapitalizePipe, ChartComponent, ChartConfigurationDirective, ChartExportButtonComponent, ChartTooltipComponent, ClickOutsideDirective, ClipboardComponent, CollapsibleBlockComponent, CollapsibleBoxComponent, CollapsibleBoxStyle, ColorPalette, 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, 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, afterBarDrawPlugin, allCountriesQuery, allGroups, allOptions, availableProperties, axisHoverPlugin, backgroundHoverPlugin, backgroundModelParams, baseApiUrl, baseUrl, bottom, buildSummary, bytesSize, calculateCycleDuration, calculateCycleDurationEnabled, calculateCycleStartDate, calculateCycleStartDateEnabled, capitalize, changelogUrl, clustererImage, code, colorToRgba, compoundToHtml, computeKeys, computeTerms, contactUsEmail, contactUsLink, convertToSvg, coordinatesToPoint, copyObject, countGroupVisibleNodes, countriesQuery, createMarker, cropsQuery, d3ellipse, d3wrap, dataPathLabel, dataPathToKey, dataVersionHeader, dataVersionHeaderKey, defaultFeature, defaultLabel, defaultSuggestionType, defaultTicksFont, definitionToSchemaType, distinctUntilChangedDeep, downloadFile, downloadPng, downloadSvg, ellipsis, engineGitBaseUrl, engineGitUrl, errorText, errorsLabel, evaluateSuccess, exportAsSVG, 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, getColor, getDatesBetween, gitBranch, gitHome, glossaryBaseUrl, glossaryLink, groupBlankNodesByTermIdentity, groupChanged, groupDataByCategory, groupJLogByField, groupJLogByTerm, groupLogsByTerm, groupNodesByTerm, groupdLogsByKey, grouppedKeys, grouppedValueKeys, groupsLogsByFields, guideModelUrl, guideNamespace, handleAPIError, handleGuideEvent, hasError, hasValidationError, hasWarning, hexToRgba, ignoreKeys$2 as ignoreKeys, increaseScaleLimits, initialFilterState, injectResizeEvent$, inputGroupsTermTypes, isAddPropertyEnabled, isChrome, isDateBetween, isEqual, isExternal, isIdenticalNodeValue, isKeyClosedVisible, isKeyHidden, isMaxStage, isMethodModelAllowed, isNonNodeModelKey, isSchemaIri, isScrolledBelow, isState, isTermTypeAllowed, isValidKey, jLogModelCount, keyToDataPath, levels, listColor, listColorContinuous, listColorWithAlpha, loadMapApi, locationQuery, logToCsv$2 as logToCsv, logValueArray, logsKey, lollipopChartPlugin, 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, nodeColours$1 as 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, optionsFromGroup, parentKey, parentProperty, parseColor, parseData, parseDataPath, parseLines, parseMessage, parseNewValue, pluralize, pointToCoordinates, polygonBounds, polygonToCoordinates, polygonToMap, polygonsFromFeature, populateWithTrackIdsFilterData, postGuideEvent, primaryProduct, productsQuery, propertyError, propertyId, recursiveProperties, refToSchemaType, refreshPropertyKeys, regionsQuery, registerChart, 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, sumValues, takeAfterViewInit, termLocation, termLocationName, termProperties, termTypeLabel, toSnakeCase, toTextParts, toThousands, typeToNewProperty, typeaheadFocus, uncapitalize, uniqueDatesBetween, updateProperties, valueLink, valueToString, valueTypeToDefault, valueValue, waitFor, wildcardQuery };
18255
+ export { ARRAY_DELIMITER, ApplyPurePipe, BarChartComponent, BibliographiesSearchConfirmComponent, BlankNodeStateComponent, BlankNodeStateNoticeComponent, BlankNodeValueDeltaComponent, CapitalizePipe, ChartComponent, ChartConfigurationDirective, ChartExportButtonComponent, ChartTooltipComponent, ClickOutsideDirective, ClipboardComponent, CollapsibleBlockComponent, CollapsibleBoxComponent, CollapsibleBoxStyle, ColorPalette, 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, 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, afterBarDrawPlugin, allCountriesQuery, allGroups, allOptions, availableProperties, axisHoverPlugin, axisLabelCursor, axisLabelSettings, backgroundHoverPlugin, backgroundModelParams, baseApiUrl, baseUrl, bottom, buildSummary, bytesSize, calculateCycleDuration, calculateCycleDurationEnabled, calculateCycleStartDate, calculateCycleStartDateEnabled, capitalize, changelogUrl, chartLabelMaxLength, clustererImage, code, colorToRgba, compoundToHtml, computeKeys, computeTerms, contactUsEmail, contactUsLink, convertToSvg, coordinatesToPoint, copyObject, countGroupVisibleNodes, countriesQuery, createMarker, cropsQuery, d3ellipse, d3wrap, dataPathLabel, dataPathToKey, dataVersionHeader, dataVersionHeaderKey, defaultFeature, defaultLabel, defaultSuggestionType, defaultTicksFont, definitionToSchemaType, distinctUntilChangedDeep, downloadFile, downloadPng, downloadSvg, ellipsis, engineGitBaseUrl, engineGitUrl, errorText, errorsLabel, evaluateSuccess, exportAsSVG, 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, getColor, getDatesBetween, gitBranch, gitHome, glossaryBaseUrl, glossaryLink, groupBlankNodesByTermIdentity, groupChanged, groupDataByCategory, groupJLogByField, groupJLogByTerm, groupLogsByTerm, groupNodesByTerm, groupdLogsByKey, grouppedKeys, grouppedValueKeys, groupsLogsByFields, guideModelUrl, guideNamespace, handleAPIError, handleGuideEvent, hasError, hasValidationError, hasWarning, hexToRgba, highestMethodTier, ignoreKeys$2 as ignoreKeys, impactAssessmentProduct, increaseScaleLimits, initialFilterState, injectResizeEvent$, inputGroupsTermTypes, isAddPropertyEnabled, isChrome, isDateBetween, isEqual, isExternal, isIdenticalNodeValue, isKeyClosedVisible, isKeyHidden, isMaxStage, isMethodModelAllowed, isNonNodeModelKey, isSchemaIri, isScrolledBelow, isState, isTermTypeAllowed, isValidKey, jLogModelCount, keyToDataPath, levels, listColor, listColorContinuous, listColorWithAlpha, loadMapApi, locationQuery, logToCsv$2 as logToCsv, logValueArray, logsKey, lollipopChartPlugin, 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$1 as 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, parseColor, parseData, parseDataPath, parseLines, parseMessage, parseNewValue, pluralize, pointToCoordinates, polygonBounds, polygonToCoordinates, polygonToMap, polygonsFromFeature, populateWithTrackIdsFilterData, postGuideEvent, primaryProduct, productsQuery, propertyError, propertyId, recursiveProperties, refToSchemaType, refreshPropertyKeys, regionsQuery, registerChart, 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, sumValues, takeAfterViewInit, termLocation, termLocationName, termProperties, termSearch, termTypeLabel, toSnakeCase, toTextParts, toThousands, typeToNewProperty, typeaheadFocus, uncapitalize, uniqueDatesBetween, updateProperties, valueLink, valueToString, valueTypeToDefault, valueValue, waitFor, wildcardQuery };
18069
18256
  //# sourceMappingURL=hestia-earth-ui-components.mjs.map