@porscheinformatik/clr-addons 19.17.0 → 19.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/charts/area-chart/area-chart.component.d.ts +2 -1
- package/charts/bar-chart/bar-chart.component.d.ts +7 -2
- package/charts/chart-export/chart-export-button.component.d.ts +2 -1
- package/charts/combo-chart/combo-chart.component.d.ts +2 -1
- package/charts/funnel-chart/funnel-chart.component.d.ts +2 -1
- package/charts/line-chart/line-chart.component.d.ts +2 -1
- package/charts/pie-chart/pie-chart.component.d.ts +2 -1
- package/clr-addons.module.d.ts +1 -2
- package/fesm2022/clr-addons.mjs +7 -2491
- package/fesm2022/clr-addons.mjs.map +1 -1
- package/fesm2022/porscheinformatik-clr-addons-charts.mjs +2507 -0
- package/fesm2022/porscheinformatik-clr-addons-charts.mjs.map +1 -0
- package/index.d.ts +0 -1
- package/package.json +16 -2
- package/styles/clr-addons-phs.css +32 -0
- package/styles/clr-addons-phs.css.map +1 -1
- package/styles/clr-addons-phs.min.css +1 -1
- package/styles/clr-addons-phs.min.css.map +1 -1
package/fesm2022/clr-addons.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Component, NgModule, Injectable, EventEmitter, Output, Input, Directive, ViewChild, ContentChildren, TemplateRef, ViewChildren, HostBinding, ElementRef, Renderer2, forwardRef, Inject, ContentChild, Optional, input, ChangeDetectionStrategy, signal, computed, SkipSelf, inject, model, contentChild, linkedSignal, DestroyRef, contentChildren, effect, output, viewChild, InjectionToken, isSignal, HostListener, LOCALE_ID, Self, Host, ChangeDetectorRef, NgZone, Injector, untracked, runInInjectionContext } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
|
-
import { CommonModule, DOCUMENT, NgComponentOutlet, NgTemplateOutlet, getLocaleDateFormat, FormatWidth,
|
|
5
|
-
import { ClarityIcons, arrowIcon, angleIcon, timesIcon, trashIcon, plusCircleIcon, exclamationCircleIcon, searchIcon, ellipsisVerticalIcon, filterGridCircleIcon, filterGridIcon, pencilIcon, historyIcon as historyIcon$1, treeViewIcon, organizationIcon, calendarIcon, checkCircleIcon, windowCloseIcon, exclamationTriangleIcon, infoStandardIcon,
|
|
4
|
+
import { CommonModule, DOCUMENT, NgComponentOutlet, NgTemplateOutlet, getLocaleDateFormat, FormatWidth, NgForOf, NgClass } from '@angular/common';
|
|
5
|
+
import { ClarityIcons, arrowIcon, angleIcon, timesIcon, trashIcon, plusCircleIcon, exclamationCircleIcon, searchIcon, ellipsisVerticalIcon, filterGridCircleIcon, filterGridIcon, pencilIcon, historyIcon as historyIcon$1, treeViewIcon, organizationIcon, calendarIcon, checkCircleIcon, windowCloseIcon, exclamationTriangleIcon, infoStandardIcon, copyToClipboardIcon, successStandardIcon, angleDoubleIcon } from '@cds/core/icon';
|
|
6
6
|
import * as i1$1 from '@clr/angular';
|
|
7
|
-
import { ClarityModule, ClrFormsModule, ClrDropdown, ClrIconModule, ClrForm, ClrAlert, ClrAlignment, ClrSide, ClrAxis, ClrPopoverToggleService, ClrPopoverEventsService, ClrPopoverPositionService, ClrPopoverHostDirective, ClrCommonStringsService, ClrDropdownModule, ClrDatagrid, ClrDatagridColumn, ClrDatagridSortOrder, DatagridPropertyComparator, ClrDatagridPagination, ClrDatagridFilter, ClrDateContainer, ClrLabel, ClrControlHelper, ClrControlError, ClrControlSuccess, ClrDatepickerModule, ClrSignpostContent, ClrSignpostModule,
|
|
7
|
+
import { ClarityModule, ClrFormsModule, ClrDropdown, ClrIconModule, ClrForm, ClrAlert, ClrAlignment, ClrSide, ClrAxis, ClrPopoverToggleService, ClrPopoverEventsService, ClrPopoverPositionService, ClrPopoverHostDirective, ClrCommonStringsService, ClrDropdownModule, ClrDatagrid, ClrDatagridColumn, ClrDatagridSortOrder, DatagridPropertyComparator, ClrDatagridPagination, ClrDatagridFilter, ClrDateContainer, ClrLabel, ClrControlHelper, ClrControlError, ClrControlSuccess, ClrDatepickerModule, ClrSignpostContent, ClrSignpostModule, ClrTooltipModule } from '@clr/angular';
|
|
8
8
|
import * as i3$1 from '@angular/forms';
|
|
9
9
|
import { FormsModule, NG_VALIDATORS, NgControl, NG_VALUE_ACCESSOR, ReactiveFormsModule, FormControl, NgModel } from '@angular/forms';
|
|
10
10
|
import { Subject, BehaviorSubject, timer as timer$1, asyncScheduler, interval, map as map$1, distinctUntilChanged, shareReplay, combineLatest, skip, auditTime, fromEvent, take as take$1, merge, debounceTime as debounceTime$1, tap, of, switchMap, filter as filter$2, ReplaySubject, takeUntil as takeUntil$1, delay } from 'rxjs';
|
|
11
|
-
import { takeUntil, take, observeOn, filter as filter$1, debounceTime
|
|
11
|
+
import { takeUntil, take, observeOn, filter as filter$1, debounceTime } from 'rxjs/operators';
|
|
12
12
|
import * as i3 from '@angular/router';
|
|
13
13
|
import { RouterModule } from '@angular/router';
|
|
14
14
|
import { trigger, transition, style, animate, state } from '@angular/animations';
|
|
@@ -16,8 +16,6 @@ import { takeUntilDestroyed, toObservable, outputFromObservable, toSignal } from
|
|
|
16
16
|
import * as i1$2 from '@angular/common/http';
|
|
17
17
|
import '@cds/core/icon/register.js';
|
|
18
18
|
import { CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
19
|
-
import { format, select, scaleBand, scaleLinear, max, axisBottom, axisLeft, group, sum, scalePoint, line, curveMonotoneX, area, axisRight, arc, pie, color } from 'd3';
|
|
20
|
-
import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
|
|
21
19
|
import { CdkCopyToClipboard } from '@angular/cdk/clipboard';
|
|
22
20
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
|
23
21
|
|
|
@@ -15603,2485 +15601,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImpo
|
|
|
15603
15601
|
* The full license information can be found in LICENSE in the root directory of this project.
|
|
15604
15602
|
*/
|
|
15605
15603
|
|
|
15606
|
-
/*
|
|
15607
|
-
* Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
|
|
15608
|
-
* This software is released under MIT license.
|
|
15609
|
-
* The full license information can be found in LICENSE in the root directory of this project.
|
|
15610
|
-
*/
|
|
15611
|
-
|
|
15612
|
-
const TOO_MANY_ITEMS_MESSAGE = 'too many items';
|
|
15613
|
-
const TOO_MANY_ITEMS_GROUPED_MESSAGE = 'too many items';
|
|
15614
|
-
const TOO_MANY_ITEMS_ALERT_TYPE = 'warning';
|
|
15615
|
-
const NO_ITEMS_MESSAGE = 'no items';
|
|
15616
|
-
const NO_ITEMS_ALERT_TYPE = 'info';
|
|
15617
|
-
|
|
15618
|
-
/**
|
|
15619
|
-
* Converts a chart color value to a CSS-compatible string.
|
|
15620
|
-
*
|
|
15621
|
-
* Supports:
|
|
15622
|
-
* - Hex values: '#e57200' → returned as-is
|
|
15623
|
-
* - CSS custom props: '--cds-global-color-lavender-1000' → wrapped in var(...)
|
|
15624
|
-
* - Any other string: 'rgb(...)' / 'hsl(...)' → returned as-is
|
|
15625
|
-
*/
|
|
15626
|
-
function toChartColor(color) {
|
|
15627
|
-
if (!color) {
|
|
15628
|
-
return '';
|
|
15629
|
-
}
|
|
15630
|
-
return color.startsWith('--') ? `var(${color})` : color;
|
|
15631
|
-
}
|
|
15632
|
-
|
|
15633
|
-
class TextRenderer {
|
|
15634
|
-
constructor() {
|
|
15635
|
-
this.cachedFont = '';
|
|
15636
|
-
this.cachedEllipsisWidth = 0;
|
|
15637
|
-
this.canvas = document.createElement('canvas');
|
|
15638
|
-
const ctx = this.canvas.getContext('2d');
|
|
15639
|
-
if (!ctx) {
|
|
15640
|
-
throw new Error('2D context error');
|
|
15641
|
-
}
|
|
15642
|
-
this.context = ctx;
|
|
15643
|
-
}
|
|
15644
|
-
render(text, availableHeight, availableWidth, fontSize = '12px', fontFamily = 'Arial') {
|
|
15645
|
-
const font = `${fontSize} ${fontFamily}`;
|
|
15646
|
-
if (this.cachedFont !== font) {
|
|
15647
|
-
this.context.font = font;
|
|
15648
|
-
this.cachedEllipsisWidth = this.context.measureText('...').width;
|
|
15649
|
-
this.cachedFont = font;
|
|
15650
|
-
}
|
|
15651
|
-
else {
|
|
15652
|
-
this.context.font = font;
|
|
15653
|
-
}
|
|
15654
|
-
const textMetrics = this.context.measureText(text);
|
|
15655
|
-
const textWidth = textMetrics.width;
|
|
15656
|
-
const textHeight = Math.abs(textMetrics.actualBoundingBoxAscent) + Math.abs(textMetrics.actualBoundingBoxDescent);
|
|
15657
|
-
if (textWidth <= availableWidth && textHeight <= availableHeight) {
|
|
15658
|
-
return text;
|
|
15659
|
-
}
|
|
15660
|
-
let remainingWidth = availableWidth - this.cachedEllipsisWidth;
|
|
15661
|
-
let truncatedText = '';
|
|
15662
|
-
for (const char of text) {
|
|
15663
|
-
const charWidth = this.context.measureText(char).width;
|
|
15664
|
-
if (remainingWidth - charWidth >= 0) {
|
|
15665
|
-
truncatedText += char;
|
|
15666
|
-
remainingWidth -= charWidth;
|
|
15667
|
-
}
|
|
15668
|
-
else {
|
|
15669
|
-
break;
|
|
15670
|
-
}
|
|
15671
|
-
}
|
|
15672
|
-
truncatedText = truncatedText.trim();
|
|
15673
|
-
if (truncatedText.length === 0) {
|
|
15674
|
-
return '';
|
|
15675
|
-
}
|
|
15676
|
-
return truncatedText + '...';
|
|
15677
|
-
}
|
|
15678
|
-
}
|
|
15679
|
-
|
|
15680
|
-
const percentage = (value, total) => {
|
|
15681
|
-
if (total <= 0) {
|
|
15682
|
-
return 0;
|
|
15683
|
-
}
|
|
15684
|
-
return Math.min(100, Math.max(0, Math.round((value / total) * 100)));
|
|
15685
|
-
};
|
|
15686
|
-
const d3percentFormat = (f => (d) => `${f(d)}%`)(format('.1f'));
|
|
15687
|
-
|
|
15688
|
-
/*
|
|
15689
|
-
* Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
|
|
15690
|
-
* This software is released under MIT license.
|
|
15691
|
-
* The full license information can be found in LICENSE in the root directory of this project.
|
|
15692
|
-
*/
|
|
15693
|
-
/**
|
|
15694
|
-
* Abstract base class shared by all chart components.
|
|
15695
|
-
* ...
|
|
15696
|
-
*/
|
|
15697
|
-
class ChartBase {
|
|
15698
|
-
constructor() {
|
|
15699
|
-
// ── Common Input ───────────────────────────────────────────────────────────
|
|
15700
|
-
/** Whether the chart is in loading state (shows skeleton). */
|
|
15701
|
-
this.loading = input(false);
|
|
15702
|
-
// ── View references ────────────────────────────────────────────────────────
|
|
15703
|
-
/** Reference to the `<svg #chart>` element in the component template. */
|
|
15704
|
-
this.chartRef = viewChild('chart');
|
|
15705
|
-
/** Reference to the `<div #container>` host element in the component template. */
|
|
15706
|
-
this.containerRef = viewChild('container');
|
|
15707
|
-
/** Exposes the raw SVG element for the chart-export button. */
|
|
15708
|
-
this.svgElement = computed(() => this.chartRef()?.nativeElement);
|
|
15709
|
-
// ── Tooltip State ──────────────────────────────────────────────────────────
|
|
15710
|
-
this.selectedItem = signal(undefined);
|
|
15711
|
-
this.tooltipPosition = signal(undefined);
|
|
15712
|
-
}
|
|
15713
|
-
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
15714
|
-
/** Schedules the first render after the view is ready. */
|
|
15715
|
-
ngAfterViewInit() {
|
|
15716
|
-
requestAnimationFrame(() => this.updateChart());
|
|
15717
|
-
}
|
|
15718
|
-
// ── Common Implementations ─────────────────────────────────────────────────
|
|
15719
|
-
/** Returns the pixel dimensions of the container div. */
|
|
15720
|
-
getContainerDimensions() {
|
|
15721
|
-
const el = this.containerRef().nativeElement;
|
|
15722
|
-
return { width: el.clientWidth, height: el.clientHeight };
|
|
15723
|
-
}
|
|
15724
|
-
/**
|
|
15725
|
-
* Clears the active tooltip. Override in subclasses that have additional
|
|
15726
|
-
* selection state
|
|
15727
|
-
*/
|
|
15728
|
-
resetTooltip() {
|
|
15729
|
-
if (this.selectedItem() || this.tooltipPosition()) {
|
|
15730
|
-
this.selectedItem.set(undefined);
|
|
15731
|
-
this.tooltipPosition.set(undefined);
|
|
15732
|
-
}
|
|
15733
|
-
}
|
|
15734
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
15735
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "19.2.18", type: ChartBase, isStandalone: true, inputs: { loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "chartRef", first: true, predicate: ["chart"], descendants: true, isSignal: true }, { propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, isSignal: true }], ngImport: i0 }); }
|
|
15736
|
-
}
|
|
15737
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartBase, decorators: [{
|
|
15738
|
-
type: Directive
|
|
15739
|
-
}] });
|
|
15740
|
-
|
|
15741
|
-
class ChartAlertOverlayComponent {
|
|
15742
|
-
constructor() {
|
|
15743
|
-
this.alertMessage = input.required();
|
|
15744
|
-
this.alertType = input('info');
|
|
15745
|
-
}
|
|
15746
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartAlertOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15747
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.18", type: ChartAlertOverlayComponent, isStandalone: true, selector: "cng-chart-alert-overlay", inputs: { alertMessage: { classPropertyName: "alertMessage", publicName: "alertMessage", isSignal: true, isRequired: true, transformFunction: null }, alertType: { classPropertyName: "alertType", publicName: "alertType", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
15748
|
-
<clr-alert [clrAlertSizeSmall]="true" [clrAlertClosable]="false" [clrAlertType]="alertType()">
|
|
15749
|
-
<clr-alert-item>
|
|
15750
|
-
<span class="alert-text">
|
|
15751
|
-
{{ alertMessage() }}
|
|
15752
|
-
</span>
|
|
15753
|
-
</clr-alert-item>
|
|
15754
|
-
</clr-alert>
|
|
15755
|
-
<div class="overlay"></div>
|
|
15756
|
-
`, isInline: true, styles: [":host{position:absolute;top:0;left:0;right:0;pointer-events:none;z-index:1;clr-alert{pointer-events:auto;::ng-deep .alert{margin-bottom:0}}.overlay{height:2rem;background:linear-gradient(to bottom,#fff,#fff0)}}\n"], dependencies: [{ kind: "ngmodule", type: ClrAlertModule }, { kind: "component", type: i1$1.ClrAlert, selector: "clr-alert", inputs: ["clrAlertSizeSmall", "clrAlertClosable", "clrAlertAppLevel", "clrCloseButtonAriaLabel", "clrAlertLightweight", "clrAlertType", "clrAlertIcon", "clrAlertClosed"], outputs: ["clrAlertClosedChange"] }, { kind: "component", type: i1$1.ClrAlertItem, selector: "clr-alert-item" }, { kind: "directive", type: i1$1.ClrAlertText, selector: ".alert-text" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15757
|
-
}
|
|
15758
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartAlertOverlayComponent, decorators: [{
|
|
15759
|
-
type: Component,
|
|
15760
|
-
args: [{ selector: 'cng-chart-alert-overlay', template: `
|
|
15761
|
-
<clr-alert [clrAlertSizeSmall]="true" [clrAlertClosable]="false" [clrAlertType]="alertType()">
|
|
15762
|
-
<clr-alert-item>
|
|
15763
|
-
<span class="alert-text">
|
|
15764
|
-
{{ alertMessage() }}
|
|
15765
|
-
</span>
|
|
15766
|
-
</clr-alert-item>
|
|
15767
|
-
</clr-alert>
|
|
15768
|
-
<div class="overlay"></div>
|
|
15769
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ClrAlertModule], styles: [":host{position:absolute;top:0;left:0;right:0;pointer-events:none;z-index:1;clr-alert{pointer-events:auto;::ng-deep .alert{margin-bottom:0}}.overlay{height:2rem;background:linear-gradient(to bottom,#fff,#fff0)}}\n"] }]
|
|
15770
|
-
}] });
|
|
15771
|
-
|
|
15772
|
-
class ChartExportService {
|
|
15773
|
-
exportSvg(svgEl, filename) {
|
|
15774
|
-
const clone = this.cloneWithDimensions(svgEl);
|
|
15775
|
-
const svgStr = new XMLSerializer().serializeToString(clone);
|
|
15776
|
-
this.download(new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' }), `${filename}.svg`);
|
|
15777
|
-
}
|
|
15778
|
-
exportPng(svgEl, filename) {
|
|
15779
|
-
this.toCanvas(svgEl).then(canvas => canvas.toBlob(blob => this.download(blob, `${filename}.png`), 'image/png'));
|
|
15780
|
-
}
|
|
15781
|
-
toCanvas(svgEl) {
|
|
15782
|
-
return new Promise(resolve => {
|
|
15783
|
-
const w = svgEl.clientWidth || Number(svgEl.getAttribute('width')) || 800;
|
|
15784
|
-
const h = svgEl.clientHeight || Number(svgEl.getAttribute('height')) || 600;
|
|
15785
|
-
const clone = this.cloneWithDimensions(svgEl, w, h);
|
|
15786
|
-
const url = URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(clone)], { type: 'image/svg+xml;charset=utf-8' }));
|
|
15787
|
-
const img = new Image();
|
|
15788
|
-
img.onload = () => {
|
|
15789
|
-
const scale = 2; // 2× for retina quality
|
|
15790
|
-
const canvas = document.createElement('canvas');
|
|
15791
|
-
canvas.width = w * scale;
|
|
15792
|
-
canvas.height = h * scale;
|
|
15793
|
-
const ctx = canvas.getContext('2d');
|
|
15794
|
-
ctx.scale(scale, scale);
|
|
15795
|
-
ctx.fillStyle = '#ffffff';
|
|
15796
|
-
ctx.fillRect(0, 0, w, h);
|
|
15797
|
-
ctx.drawImage(img, 0, 0, w, h);
|
|
15798
|
-
URL.revokeObjectURL(url);
|
|
15799
|
-
resolve(canvas);
|
|
15800
|
-
};
|
|
15801
|
-
img.src = url;
|
|
15802
|
-
});
|
|
15803
|
-
}
|
|
15804
|
-
cloneWithDimensions(svgEl, w, h) {
|
|
15805
|
-
const clone = svgEl.cloneNode(true);
|
|
15806
|
-
const width = w ?? svgEl.clientWidth ?? Number(svgEl.getAttribute('width')) ?? 800;
|
|
15807
|
-
const height = h ?? svgEl.clientHeight ?? Number(svgEl.getAttribute('height')) ?? 600;
|
|
15808
|
-
clone.setAttribute('width', String(width));
|
|
15809
|
-
clone.setAttribute('height', String(height));
|
|
15810
|
-
clone.style.background = '#ffffff';
|
|
15811
|
-
return clone;
|
|
15812
|
-
}
|
|
15813
|
-
download(blob, filename) {
|
|
15814
|
-
const url = URL.createObjectURL(blob);
|
|
15815
|
-
const a = document.createElement('a');
|
|
15816
|
-
a.href = url;
|
|
15817
|
-
a.download = filename;
|
|
15818
|
-
a.click();
|
|
15819
|
-
setTimeout(() => URL.revokeObjectURL(url), 100);
|
|
15820
|
-
}
|
|
15821
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartExportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
15822
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartExportService, providedIn: 'root' }); }
|
|
15823
|
-
}
|
|
15824
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartExportService, decorators: [{
|
|
15825
|
-
type: Injectable,
|
|
15826
|
-
args: [{ providedIn: 'root' }]
|
|
15827
|
-
}] });
|
|
15828
|
-
|
|
15829
|
-
ClarityIcons.addIcons(downloadIcon);
|
|
15830
|
-
class ChartExportButtonComponent {
|
|
15831
|
-
constructor() {
|
|
15832
|
-
this.svgRef = input(undefined);
|
|
15833
|
-
this.filename = input('chart');
|
|
15834
|
-
this.exportService = inject(ChartExportService);
|
|
15835
|
-
}
|
|
15836
|
-
export(format) {
|
|
15837
|
-
const svg = this.svgRef();
|
|
15838
|
-
if (!svg) {
|
|
15839
|
-
return;
|
|
15840
|
-
}
|
|
15841
|
-
switch (format) {
|
|
15842
|
-
case 'svg':
|
|
15843
|
-
this.exportService.exportSvg(svg, this.filename());
|
|
15844
|
-
break;
|
|
15845
|
-
case 'png':
|
|
15846
|
-
this.exportService.exportPng(svg, this.filename());
|
|
15847
|
-
break;
|
|
15848
|
-
}
|
|
15849
|
-
}
|
|
15850
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartExportButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15851
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.18", type: ChartExportButtonComponent, isStandalone: true, selector: "cng-chart-export-button", inputs: { svgRef: { classPropertyName: "svgRef", publicName: "svgRef", isSignal: true, isRequired: false, transformFunction: null }, filename: { classPropertyName: "filename", publicName: "filename", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
15852
|
-
<clr-dropdown>
|
|
15853
|
-
<button class="btn btn-sm btn-icon btn-link export-trigger" clrDropdownTrigger title="Export chart">
|
|
15854
|
-
<cds-icon shape="download" size="16"></cds-icon>
|
|
15855
|
-
</button>
|
|
15856
|
-
<clr-dropdown-menu *clrIfOpen clrPosition="bottom-left">
|
|
15857
|
-
<button clrDropdownItem (click)="export('svg')">SVG</button>
|
|
15858
|
-
<button clrDropdownItem (click)="export('png')">PNG</button>
|
|
15859
|
-
</clr-dropdown-menu>
|
|
15860
|
-
</clr-dropdown>
|
|
15861
|
-
`, isInline: true, styles: [":host{position:absolute;bottom:-15px;left:-5px;z-index:10;opacity:1;pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: ClrDropdownModule }, { kind: "component", type: i1$1.ClrDropdown, selector: "clr-dropdown", inputs: ["clrCloseMenuOnItemClick"] }, { kind: "component", type: i1$1.ClrDropdownMenu, selector: "clr-dropdown-menu", inputs: ["clrPosition"] }, { kind: "directive", type: i1$1.ClrDropdownTrigger, selector: "[clrDropdownTrigger],[clrDropdownToggle]" }, { kind: "directive", type: i1$1.ClrDropdownItem, selector: "[clrDropdownItem]", inputs: ["clrDisabled", "id"] }, { kind: "directive", type: i1$1.ClrIfOpen, selector: "[clrIfOpen]", inputs: ["clrIfOpen"], outputs: ["clrIfOpenChange"] }, { kind: "directive", type: i1$1.CdsIconCustomTag, selector: "cds-icon" }, { kind: "ngmodule", type: ClrIconModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15862
|
-
}
|
|
15863
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartExportButtonComponent, decorators: [{
|
|
15864
|
-
type: Component,
|
|
15865
|
-
args: [{ selector: 'cng-chart-export-button', template: `
|
|
15866
|
-
<clr-dropdown>
|
|
15867
|
-
<button class="btn btn-sm btn-icon btn-link export-trigger" clrDropdownTrigger title="Export chart">
|
|
15868
|
-
<cds-icon shape="download" size="16"></cds-icon>
|
|
15869
|
-
</button>
|
|
15870
|
-
<clr-dropdown-menu *clrIfOpen clrPosition="bottom-left">
|
|
15871
|
-
<button clrDropdownItem (click)="export('svg')">SVG</button>
|
|
15872
|
-
<button clrDropdownItem (click)="export('png')">PNG</button>
|
|
15873
|
-
</clr-dropdown-menu>
|
|
15874
|
-
</clr-dropdown>
|
|
15875
|
-
`, imports: [ClrDropdownModule, ClrIconModule], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{position:absolute;bottom:-15px;left:-5px;z-index:10;opacity:1;pointer-events:auto}\n"] }]
|
|
15876
|
-
}] });
|
|
15877
|
-
|
|
15878
|
-
class ChartLegendComponent {
|
|
15879
|
-
constructor() {
|
|
15880
|
-
this.items = input.required();
|
|
15881
|
-
this.toChartColor = toChartColor;
|
|
15882
|
-
}
|
|
15883
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartLegendComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15884
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: ChartLegendComponent, isStandalone: true, selector: "cng-chart-legend", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
15885
|
-
<div class="chart-legend">
|
|
15886
|
-
@for (item of items(); track item.label) {
|
|
15887
|
-
<div class="legend-item">
|
|
15888
|
-
<span class="legend-color-square" [style.background-color]="toChartColor(item.color)"></span>
|
|
15889
|
-
<span class="legend-label">{{ item.label }}</span>
|
|
15890
|
-
</div>
|
|
15891
|
-
}
|
|
15892
|
-
</div>
|
|
15893
|
-
`, isInline: true, styles: [":host{display:block}.chart-legend{display:flex;flex-wrap:wrap;gap:.25rem 1rem;padding:.5rem 0 .25rem;font-size:11px;color:var(--clr-color-neutral-600, #666)}.legend-item{display:flex;align-items:center;gap:.35rem}.legend-color-square{width:10px;height:10px;border-radius:2px;flex-shrink:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15894
|
-
}
|
|
15895
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartLegendComponent, decorators: [{
|
|
15896
|
-
type: Component,
|
|
15897
|
-
args: [{ selector: 'cng-chart-legend', template: `
|
|
15898
|
-
<div class="chart-legend">
|
|
15899
|
-
@for (item of items(); track item.label) {
|
|
15900
|
-
<div class="legend-item">
|
|
15901
|
-
<span class="legend-color-square" [style.background-color]="toChartColor(item.color)"></span>
|
|
15902
|
-
<span class="legend-label">{{ item.label }}</span>
|
|
15903
|
-
</div>
|
|
15904
|
-
}
|
|
15905
|
-
</div>
|
|
15906
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.chart-legend{display:flex;flex-wrap:wrap;gap:.25rem 1rem;padding:.5rem 0 .25rem;font-size:11px;color:var(--clr-color-neutral-600, #666)}.legend-item{display:flex;align-items:center;gap:.35rem}.legend-color-square{width:10px;height:10px;border-radius:2px;flex-shrink:0}\n"] }]
|
|
15907
|
-
}] });
|
|
15908
|
-
|
|
15909
|
-
class ChartSkeletonComponent {
|
|
15910
|
-
constructor() {
|
|
15911
|
-
this.skeletonType = input('loading');
|
|
15912
|
-
this.orientation = input.required();
|
|
15913
|
-
this.animationStyle = computed(() => (this.skeletonType() === 'loading' ? 'pulse' : undefined));
|
|
15914
|
-
}
|
|
15915
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartSkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15916
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: ChartSkeletonComponent, isStandalone: true, selector: "cng-bar-chart-skeleton", inputs: { skeletonType: { classPropertyName: "skeletonType", publicName: "skeletonType", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"chart-skeleton\" [class.placeholder-skeleton]=\"skeletonType() === 'placeholder'\">\n <div\n class=\"skeleton-bars\"\n [class.horizontal]=\"orientation() === 'horizontal'\"\n [class.vertical]=\"orientation() === 'vertical'\"\n >\n @for (_item of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"skeleton-bar bar-{{ $index + 1 }}\">\n <ngx-skeleton-loader\n [appearance]=\"'circle'\"\n [count]=\"1\"\n [animation]=\"animationStyle()\"\n [theme]=\"{\n width: '100%',\n height: '100%',\n 'border-radius': 'var(--clr-base-border-radius-xxs)',\n }\"\n />\n </div>\n }\n </div>\n</div>\n", styles: [".chart-skeleton{width:100%;height:100%}.placeholder-skeleton ::ng-deep .skeleton-loader{cursor:default}.skeleton-bars{display:flex;gap:var(--clr-base-gap-l);width:100%;height:100%;padding:var(--cds-global-layout-space-xs) var(--cds-global-layout-space-xl)}.skeleton-bars.vertical{flex-direction:row;align-items:flex-end;justify-content:center}.skeleton-bars.vertical .skeleton-bar{width:var(--cds-global-layout-space-md)}.skeleton-bars.vertical .skeleton-bar.bar-1{height:60%}.skeleton-bars.vertical .skeleton-bar.bar-2{height:95%}.skeleton-bars.vertical .skeleton-bar.bar-3{height:40%}.skeleton-bars.vertical .skeleton-bar.bar-4{height:75%}.skeleton-bars.vertical .skeleton-bar.bar-5{height:50%}.skeleton-bars.vertical .skeleton-bar.bar-6{height:25%}.skeleton-bars.horizontal{flex-direction:column;justify-content:center}.skeleton-bars.horizontal .skeleton-bar{height:var(--cds-global-layout-space-md)}.skeleton-bars.horizontal .skeleton-bar.bar-1{width:60%}.skeleton-bars.horizontal .skeleton-bar.bar-2{width:95%}.skeleton-bars.horizontal .skeleton-bar.bar-3{width:40%}.skeleton-bars.horizontal .skeleton-bar.bar-4{width:75%}.skeleton-bars.horizontal .skeleton-bar.bar-5{width:50%}.skeleton-bars.horizontal .skeleton-bar.bar-6{width:25%}\n"], dependencies: [{ kind: "component", type: NgxSkeletonLoaderComponent, selector: "ngx-skeleton-loader", inputs: ["count", "loadingText", "appearance", "animation", "ariaLabel", "theme"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15917
|
-
}
|
|
15918
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartSkeletonComponent, decorators: [{
|
|
15919
|
-
type: Component,
|
|
15920
|
-
args: [{ selector: 'cng-bar-chart-skeleton', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgxSkeletonLoaderComponent], template: "<div class=\"chart-skeleton\" [class.placeholder-skeleton]=\"skeletonType() === 'placeholder'\">\n <div\n class=\"skeleton-bars\"\n [class.horizontal]=\"orientation() === 'horizontal'\"\n [class.vertical]=\"orientation() === 'vertical'\"\n >\n @for (_item of [1, 2, 3, 4, 5, 6]; track $index) {\n <div class=\"skeleton-bar bar-{{ $index + 1 }}\">\n <ngx-skeleton-loader\n [appearance]=\"'circle'\"\n [count]=\"1\"\n [animation]=\"animationStyle()\"\n [theme]=\"{\n width: '100%',\n height: '100%',\n 'border-radius': 'var(--clr-base-border-radius-xxs)',\n }\"\n />\n </div>\n }\n </div>\n</div>\n", styles: [".chart-skeleton{width:100%;height:100%}.placeholder-skeleton ::ng-deep .skeleton-loader{cursor:default}.skeleton-bars{display:flex;gap:var(--clr-base-gap-l);width:100%;height:100%;padding:var(--cds-global-layout-space-xs) var(--cds-global-layout-space-xl)}.skeleton-bars.vertical{flex-direction:row;align-items:flex-end;justify-content:center}.skeleton-bars.vertical .skeleton-bar{width:var(--cds-global-layout-space-md)}.skeleton-bars.vertical .skeleton-bar.bar-1{height:60%}.skeleton-bars.vertical .skeleton-bar.bar-2{height:95%}.skeleton-bars.vertical .skeleton-bar.bar-3{height:40%}.skeleton-bars.vertical .skeleton-bar.bar-4{height:75%}.skeleton-bars.vertical .skeleton-bar.bar-5{height:50%}.skeleton-bars.vertical .skeleton-bar.bar-6{height:25%}.skeleton-bars.horizontal{flex-direction:column;justify-content:center}.skeleton-bars.horizontal .skeleton-bar{height:var(--cds-global-layout-space-md)}.skeleton-bars.horizontal .skeleton-bar.bar-1{width:60%}.skeleton-bars.horizontal .skeleton-bar.bar-2{width:95%}.skeleton-bars.horizontal .skeleton-bar.bar-3{width:40%}.skeleton-bars.horizontal .skeleton-bar.bar-4{width:75%}.skeleton-bars.horizontal .skeleton-bar.bar-5{width:50%}.skeleton-bars.horizontal .skeleton-bar.bar-6{width:25%}\n"] }]
|
|
15921
|
-
}] });
|
|
15922
|
-
|
|
15923
|
-
class ChartTooltipComponent {
|
|
15924
|
-
constructor() {
|
|
15925
|
-
this.tooltipPosition = input.required();
|
|
15926
|
-
this.tooltipOrientation = input('top');
|
|
15927
|
-
this.squareColor = input.required();
|
|
15928
|
-
this.tooltipClickable = input(true);
|
|
15929
|
-
this.tooltipClosed = output();
|
|
15930
|
-
this.tooltipHeaderClicked = output();
|
|
15931
|
-
this.toChartColor = toChartColor;
|
|
15932
|
-
}
|
|
15933
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
15934
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: ChartTooltipComponent, isStandalone: true, selector: "cng-chart-tooltip", inputs: { tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: true, transformFunction: null }, tooltipOrientation: { classPropertyName: "tooltipOrientation", publicName: "tooltipOrientation", isSignal: true, isRequired: false, transformFunction: null }, squareColor: { classPropertyName: "squareColor", publicName: "squareColor", isSignal: true, isRequired: true, transformFunction: null }, tooltipClickable: { classPropertyName: "tooltipClickable", publicName: "tooltipClickable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tooltipClosed: "tooltipClosed", tooltipHeaderClicked: "tooltipHeaderClicked" }, ngImport: i0, template: "<div\n class=\"tooltip-container\"\n [class.orientation-bottom]=\"tooltipOrientation() === 'bottom'\"\n (click)=\"$event.stopPropagation()\"\n [style.left.px]=\"tooltipPosition().x\"\n [style.top.px]=\"tooltipPosition().y\"\n>\n <div class=\"tooltip-header\">\n <div class=\"color-label-wrapper\">\n @if (squareColor()) {\n <div class=\"color-square\" [style.background-color]=\"toChartColor(squareColor())\"></div>\n }\n <h5\n [class.has-more-info]=\"tooltipClickable()\"\n (click)=\"$event.stopPropagation(); tooltipClickable() && tooltipHeaderClicked.emit()\"\n >\n <ng-content select=\"cng-title\" />\n </h5>\n </div>\n <cds-icon shape=\"times\" class=\"tooltip-close-btn\" (click)=\"$event.stopPropagation(); tooltipClosed.emit()\" />\n </div>\n <div class=\"tooltip-content\">\n <ng-content />\n </div>\n <hr />\n <div class=\"tooltip-footer\"><ng-content select=\"cng-footer\" /></div>\n</div>\n", styles: [".tooltip-container{position:absolute;background:var(--clr-color-neutral-0, #fff);border:1px solid var(--clr-color-neutral-400, #ccc);border-radius:4px;box-shadow:0 2px 8px #00000026;padding:12px 16px;z-index:1000;min-width:150px;transform:translate(-50%,-100%) translateY(-10px)}.tooltip-container.orientation-bottom{transform:translate(-50%) translateY(10px)}.tooltip-header{display:flex;flex-direction:row;justify-content:space-between;gap:8px}.tooltip-header h5{margin:0;font-size:14px;font-weight:600;color:var(--clr-color-neutral-1000, #000);flex-grow:1;max-width:200px}.tooltip-close-btn{cursor:pointer;color:var(--clr-close-color)}.tooltip-close-btn:hover,.tooltip-close-btn:focus{color:var(--clr-close-color-hover)}.color-label-wrapper{display:flex;flex-direction:row;gap:8px}.tooltip-content{font-size:12px;color:var(--clr-color-neutral-700, #565656)}hr:has(+.tooltip-footer:empty){display:none}hr{margin:6px -16px}\n"], dependencies: [{ kind: "ngmodule", type: ClrIconModule }, { kind: "directive", type: i1$1.CdsIconCustomTag, selector: "cds-icon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
15935
|
-
}
|
|
15936
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ChartTooltipComponent, decorators: [{
|
|
15937
|
-
type: Component,
|
|
15938
|
-
args: [{ selector: 'cng-chart-tooltip', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ClrIconModule], template: "<div\n class=\"tooltip-container\"\n [class.orientation-bottom]=\"tooltipOrientation() === 'bottom'\"\n (click)=\"$event.stopPropagation()\"\n [style.left.px]=\"tooltipPosition().x\"\n [style.top.px]=\"tooltipPosition().y\"\n>\n <div class=\"tooltip-header\">\n <div class=\"color-label-wrapper\">\n @if (squareColor()) {\n <div class=\"color-square\" [style.background-color]=\"toChartColor(squareColor())\"></div>\n }\n <h5\n [class.has-more-info]=\"tooltipClickable()\"\n (click)=\"$event.stopPropagation(); tooltipClickable() && tooltipHeaderClicked.emit()\"\n >\n <ng-content select=\"cng-title\" />\n </h5>\n </div>\n <cds-icon shape=\"times\" class=\"tooltip-close-btn\" (click)=\"$event.stopPropagation(); tooltipClosed.emit()\" />\n </div>\n <div class=\"tooltip-content\">\n <ng-content />\n </div>\n <hr />\n <div class=\"tooltip-footer\"><ng-content select=\"cng-footer\" /></div>\n</div>\n", styles: [".tooltip-container{position:absolute;background:var(--clr-color-neutral-0, #fff);border:1px solid var(--clr-color-neutral-400, #ccc);border-radius:4px;box-shadow:0 2px 8px #00000026;padding:12px 16px;z-index:1000;min-width:150px;transform:translate(-50%,-100%) translateY(-10px)}.tooltip-container.orientation-bottom{transform:translate(-50%) translateY(10px)}.tooltip-header{display:flex;flex-direction:row;justify-content:space-between;gap:8px}.tooltip-header h5{margin:0;font-size:14px;font-weight:600;color:var(--clr-color-neutral-1000, #000);flex-grow:1;max-width:200px}.tooltip-close-btn{cursor:pointer;color:var(--clr-close-color)}.tooltip-close-btn:hover,.tooltip-close-btn:focus{color:var(--clr-close-color-hover)}.color-label-wrapper{display:flex;flex-direction:row;gap:8px}.tooltip-content{font-size:12px;color:var(--clr-color-neutral-700, #565656)}hr:has(+.tooltip-footer:empty){display:none}hr{margin:6px -16px}\n"] }]
|
|
15939
|
-
}] });
|
|
15940
|
-
|
|
15941
|
-
// Clarity actually has an "outside-click" directive, but they don't export it 🤷
|
|
15942
|
-
class OutsideClickDirective {
|
|
15943
|
-
constructor() {
|
|
15944
|
-
this.outsideClick = output({ alias: 'cngOutsideClick' });
|
|
15945
|
-
this.renderer = inject(Renderer2);
|
|
15946
|
-
this.destroyRef = inject(DestroyRef);
|
|
15947
|
-
}
|
|
15948
|
-
ngAfterViewInit() {
|
|
15949
|
-
const listener = this.renderer.listen('document', 'click', () => this.outsideClick.emit());
|
|
15950
|
-
this.destroyRef.onDestroy(listener);
|
|
15951
|
-
}
|
|
15952
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: OutsideClickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
15953
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.18", type: OutsideClickDirective, isStandalone: true, selector: "[cngOutsideClick]", outputs: { outsideClick: "cngOutsideClick" }, ngImport: i0 }); }
|
|
15954
|
-
}
|
|
15955
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: OutsideClickDirective, decorators: [{
|
|
15956
|
-
type: Directive,
|
|
15957
|
-
args: [{
|
|
15958
|
-
selector: '[cngOutsideClick]',
|
|
15959
|
-
}]
|
|
15960
|
-
}] });
|
|
15961
|
-
|
|
15962
|
-
/**
|
|
15963
|
-
* Enum for the Screen size in pixels, defined by clarity here https://vmware.github.io/clarity/documentation/v0.12/grid
|
|
15964
|
-
* We consider screens above 1200px to be a desktop, between 576px and 1199px to be a tablet and below 576px to be a phone.
|
|
15965
|
-
*/
|
|
15966
|
-
var ScreenWidth;
|
|
15967
|
-
(function (ScreenWidth) {
|
|
15968
|
-
ScreenWidth[ScreenWidth["EXTRA_LARGE"] = 1200] = "EXTRA_LARGE";
|
|
15969
|
-
ScreenWidth[ScreenWidth["LARGE"] = 992] = "LARGE";
|
|
15970
|
-
ScreenWidth[ScreenWidth["MEDIUM"] = 768] = "MEDIUM";
|
|
15971
|
-
ScreenWidth[ScreenWidth["SMALL"] = 576] = "SMALL";
|
|
15972
|
-
ScreenWidth[ScreenWidth["EXTRA_SMALL"] = 575] = "EXTRA_SMALL";
|
|
15973
|
-
})(ScreenWidth || (ScreenWidth = {}));
|
|
15974
|
-
var ScreenOrientation;
|
|
15975
|
-
(function (ScreenOrientation) {
|
|
15976
|
-
ScreenOrientation["PORTRAIT"] = "PORTRAIT";
|
|
15977
|
-
ScreenOrientation["LANDSCAPE"] = "LANDSCAPE";
|
|
15978
|
-
})(ScreenOrientation || (ScreenOrientation = {}));
|
|
15979
|
-
const DATEPICKER_ENABLE_BREAKPOINT = 768;
|
|
15980
|
-
const MOBILE_USERAGENT_REGEX = /Mobi/i;
|
|
15981
|
-
class ScreenStateService {
|
|
15982
|
-
constructor(_document) {
|
|
15983
|
-
this._document = _document;
|
|
15984
|
-
this.isUserAgentMobile = false;
|
|
15985
|
-
this.screenWidthSubject = new ReplaySubject(1);
|
|
15986
|
-
this.screenOrientationSubject = new ReplaySubject(1);
|
|
15987
|
-
window.addEventListener('resize', () => {
|
|
15988
|
-
this.onResize();
|
|
15989
|
-
});
|
|
15990
|
-
window.addEventListener('orientationchange', () => {
|
|
15991
|
-
this.onOrientationChange();
|
|
15992
|
-
});
|
|
15993
|
-
this.determineMobileUserAgent();
|
|
15994
|
-
// Execute them once for initial values
|
|
15995
|
-
this.onResize();
|
|
15996
|
-
this.onOrientationChange();
|
|
15997
|
-
}
|
|
15998
|
-
getScreenWidthChanged() {
|
|
15999
|
-
return this.screenWidthSubject;
|
|
16000
|
-
}
|
|
16001
|
-
getMobileStateChanged() {
|
|
16002
|
-
return this.getScreenWidthChanged().pipe(map$2(screenWidth => screenWidth < DATEPICKER_ENABLE_BREAKPOINT && this.isUserAgentMobile), distinctUntilChanged$1());
|
|
16003
|
-
}
|
|
16004
|
-
getScreenOrientationChanged() {
|
|
16005
|
-
return this.screenOrientationSubject;
|
|
16006
|
-
}
|
|
16007
|
-
onResize() {
|
|
16008
|
-
const windowWidth = window.innerWidth;
|
|
16009
|
-
this.screenWidthSubject.next(windowWidth);
|
|
16010
|
-
}
|
|
16011
|
-
onOrientationChange() {
|
|
16012
|
-
const prevOrientation = this.screenOrientation;
|
|
16013
|
-
const orientation = window.orientation;
|
|
16014
|
-
// Probably not always correct since this depends on the devices default orientation.
|
|
16015
|
-
// Better way to identify this would probably be comparing height & width of the screen.
|
|
16016
|
-
if (orientation === -90 || orientation === 90) {
|
|
16017
|
-
this.screenOrientation = ScreenOrientation.LANDSCAPE;
|
|
16018
|
-
}
|
|
16019
|
-
else {
|
|
16020
|
-
this.screenOrientation = ScreenOrientation.PORTRAIT;
|
|
16021
|
-
}
|
|
16022
|
-
if (this.screenOrientation !== prevOrientation) {
|
|
16023
|
-
this.screenOrientationSubject.next(this.screenOrientation);
|
|
16024
|
-
}
|
|
16025
|
-
}
|
|
16026
|
-
determineMobileUserAgent() {
|
|
16027
|
-
if (this._document) {
|
|
16028
|
-
this.isUserAgentMobile = MOBILE_USERAGENT_REGEX.test(this._document.defaultView.navigator.userAgent);
|
|
16029
|
-
}
|
|
16030
|
-
}
|
|
16031
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ScreenStateService, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
16032
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ScreenStateService, providedIn: 'root' }); }
|
|
16033
|
-
}
|
|
16034
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ScreenStateService, decorators: [{
|
|
16035
|
-
type: Injectable,
|
|
16036
|
-
args: [{ providedIn: 'root' }]
|
|
16037
|
-
}], ctorParameters: () => [{ type: Document, decorators: [{
|
|
16038
|
-
type: Inject,
|
|
16039
|
-
args: [DOCUMENT]
|
|
16040
|
-
}] }] });
|
|
16041
|
-
|
|
16042
|
-
class WindowResizeDirective {
|
|
16043
|
-
constructor() {
|
|
16044
|
-
this.debounce = input(250);
|
|
16045
|
-
this.includeFirst = input(false);
|
|
16046
|
-
this.windowResize = output({ alias: 'cngWindowResize' });
|
|
16047
|
-
this.screenStateService = inject(ScreenStateService);
|
|
16048
|
-
this.destroyRef = inject(DestroyRef);
|
|
16049
|
-
}
|
|
16050
|
-
ngOnInit() {
|
|
16051
|
-
const skipCount = this.includeFirst() ? 0 : 1;
|
|
16052
|
-
this.screenStateService
|
|
16053
|
-
.getScreenWidthChanged()
|
|
16054
|
-
.pipe(takeUntilDestroyed(this.destroyRef), skip(skipCount), debounceTime(this.debounce()))
|
|
16055
|
-
.subscribe(width => this.windowResize.emit(width));
|
|
16056
|
-
}
|
|
16057
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: WindowResizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
16058
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.18", type: WindowResizeDirective, isStandalone: true, selector: "[cngWindowResize]", inputs: { debounce: { classPropertyName: "debounce", publicName: "debounce", isSignal: true, isRequired: false, transformFunction: null }, includeFirst: { classPropertyName: "includeFirst", publicName: "includeFirst", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { windowResize: "cngWindowResize" }, ngImport: i0 }); }
|
|
16059
|
-
}
|
|
16060
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: WindowResizeDirective, decorators: [{
|
|
16061
|
-
type: Directive,
|
|
16062
|
-
args: [{
|
|
16063
|
-
selector: '[cngWindowResize]',
|
|
16064
|
-
}]
|
|
16065
|
-
}] });
|
|
16066
|
-
|
|
16067
|
-
class BarChartComponent extends ChartBase {
|
|
16068
|
-
static { this.HORIZONTAL_BAR_MIN_HEIGHT_PX = 25; }
|
|
16069
|
-
constructor() {
|
|
16070
|
-
super();
|
|
16071
|
-
this.data = input.required();
|
|
16072
|
-
this.stackLabels = input(undefined);
|
|
16073
|
-
this.orientation = input.required();
|
|
16074
|
-
this.tooltipOrientation = input('top');
|
|
16075
|
-
this.barSizePx = input(15);
|
|
16076
|
-
this.barAreaSizePx = input(40);
|
|
16077
|
-
this.tooltipPercentOfTotal = input('of total');
|
|
16078
|
-
this.tooltipPercentOf = input('of');
|
|
16079
|
-
this.noItemsMessage = input(NO_ITEMS_MESSAGE);
|
|
16080
|
-
this.tooManyItemsMessage = input(TOO_MANY_ITEMS_MESSAGE);
|
|
16081
|
-
this.tooManyItemsGroupedMessage = input(TOO_MANY_ITEMS_GROUPED_MESSAGE);
|
|
16082
|
-
this.showLegend = input(true);
|
|
16083
|
-
this.showExportButton = input(false);
|
|
16084
|
-
this.exportFilename = input('bar-chart');
|
|
16085
|
-
/** Optional label rendered below the X axis. */
|
|
16086
|
-
this.xAxisLabel = input('');
|
|
16087
|
-
/** Optional label rendered rotated to the left of the Y axis. */
|
|
16088
|
-
this.yAxisLabel = input('');
|
|
16089
|
-
this.valueClicked = output();
|
|
16090
|
-
this.textRenderer = new TextRenderer();
|
|
16091
|
-
this.MARGIN = { top: 10, right: 20, bottom: 30, left: 65 };
|
|
16092
|
-
this.toChartColor = toChartColor;
|
|
16093
|
-
this.total = computed(() => this.data().reduce((acc, v) => acc + v.value, 0));
|
|
16094
|
-
this.totalByStack = computed(() => {
|
|
16095
|
-
const totals = {};
|
|
16096
|
-
for (const d of this.slicedDataPoints()) {
|
|
16097
|
-
totals[d.stackKey] = (totals[d.stackKey] ?? 0) + d.value;
|
|
16098
|
-
}
|
|
16099
|
-
return totals;
|
|
16100
|
-
});
|
|
16101
|
-
this.keysByStack = computed(() => {
|
|
16102
|
-
const keys = {};
|
|
16103
|
-
for (const d of this.slicedDataPoints()) {
|
|
16104
|
-
if (!keys[d.stackKey]) {
|
|
16105
|
-
keys[d.stackKey] = [];
|
|
16106
|
-
}
|
|
16107
|
-
keys[d.stackKey].push(d.key);
|
|
16108
|
-
}
|
|
16109
|
-
return keys;
|
|
16110
|
-
});
|
|
16111
|
-
// Count the total amount of bars by either the stacKey (if stacked) or the key (if not stacked)
|
|
16112
|
-
this.totalBarCount = computed(() => new Set(this.data()
|
|
16113
|
-
.filter(d => d.value > 0)
|
|
16114
|
-
.map(d => d.stackKey ?? d.key)).size);
|
|
16115
|
-
this.showingBarCount = computed(() => new Set(this.slicedDataPoints()
|
|
16116
|
-
.filter(d => d.value > 0)
|
|
16117
|
-
.map(d => d.stackKey)).size);
|
|
16118
|
-
this.alertMessageAndType = computed(() => {
|
|
16119
|
-
if (this.loading()) {
|
|
16120
|
-
return undefined;
|
|
16121
|
-
}
|
|
16122
|
-
if (!this.showingBarCount()) {
|
|
16123
|
-
return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
|
|
16124
|
-
}
|
|
16125
|
-
else if (this.totalBarCount() !== this.showingBarCount()) {
|
|
16126
|
-
return [
|
|
16127
|
-
this.stackLabels() ? this.tooManyItemsGroupedMessage() : this.tooManyItemsMessage(),
|
|
16128
|
-
TOO_MANY_ITEMS_ALERT_TYPE,
|
|
16129
|
-
];
|
|
16130
|
-
}
|
|
16131
|
-
return undefined;
|
|
16132
|
-
});
|
|
16133
|
-
this.legendItems = computed(() => {
|
|
16134
|
-
if (!this.showLegend() || !this.data()?.length) {
|
|
16135
|
-
return [];
|
|
16136
|
-
}
|
|
16137
|
-
if (this.stackLabels()?.length) {
|
|
16138
|
-
// Stacked: one legend entry per distinct label (= each layer in the stack)
|
|
16139
|
-
const seen = new Set();
|
|
16140
|
-
const items = [];
|
|
16141
|
-
for (const item of this.data()) {
|
|
16142
|
-
const label = item.fullLabel ?? item.label;
|
|
16143
|
-
if (!seen.has(label)) {
|
|
16144
|
-
seen.add(label);
|
|
16145
|
-
items.push({ label, color: item.color });
|
|
16146
|
-
}
|
|
16147
|
-
}
|
|
16148
|
-
return items;
|
|
16149
|
-
}
|
|
16150
|
-
// Non-stacked: one entry per bar
|
|
16151
|
-
return this.data().map(item => ({ label: item.fullLabel ?? item.label, color: item.color }));
|
|
16152
|
-
});
|
|
16153
|
-
/** Computed values used by the tooltip to avoid inline logic in the template. */
|
|
16154
|
-
this.tooltipKey = computed(() => {
|
|
16155
|
-
const item = this.selectedItem();
|
|
16156
|
-
if (!item) {
|
|
16157
|
-
return undefined;
|
|
16158
|
-
}
|
|
16159
|
-
return this.stackLabels()?.length ? this.keysByStack()[item.stackKey] ?? [] : [item.key];
|
|
16160
|
-
});
|
|
16161
|
-
this.tooltipLabel = computed(() => {
|
|
16162
|
-
const item = this.selectedItem();
|
|
16163
|
-
if (!item) {
|
|
16164
|
-
return undefined;
|
|
16165
|
-
}
|
|
16166
|
-
return this.stackLabels()?.length ? item.stackKey : item.fullLabel ?? item.label;
|
|
16167
|
-
});
|
|
16168
|
-
this.tooltipValue = computed(() => {
|
|
16169
|
-
const item = this.selectedItem();
|
|
16170
|
-
if (!item) {
|
|
16171
|
-
return undefined;
|
|
16172
|
-
}
|
|
16173
|
-
return this.stackLabels()?.length ? this.totalByStack()[item.stackKey] ?? 0 : item.value;
|
|
16174
|
-
});
|
|
16175
|
-
/** Slices belonging to the currently selected stack – used by the tooltip @for loop. */
|
|
16176
|
-
this.selectedStackSlices = computed(() => {
|
|
16177
|
-
const item = this.selectedItem();
|
|
16178
|
-
if (!item || !this.stackLabels()?.length) {
|
|
16179
|
-
return [];
|
|
16180
|
-
}
|
|
16181
|
-
return this.slicedDataPoints().filter(d => d.stackKey === item.stackKey);
|
|
16182
|
-
});
|
|
16183
|
-
this.maxAmountOfItems = signal(undefined);
|
|
16184
|
-
this.slicedDataPoints = signal([]);
|
|
16185
|
-
this.barSelection = null;
|
|
16186
|
-
this.labelSelection = null;
|
|
16187
|
-
}
|
|
16188
|
-
ngOnChanges(_changes) {
|
|
16189
|
-
if (!this.svg) {
|
|
16190
|
-
return;
|
|
16191
|
-
}
|
|
16192
|
-
requestAnimationFrame(() => this.updateChart());
|
|
16193
|
-
}
|
|
16194
|
-
ngAfterViewInit() {
|
|
16195
|
-
this.createChart();
|
|
16196
|
-
super.ngAfterViewInit(); // schedules the initial requestAnimationFrame(() => updateChart())
|
|
16197
|
-
}
|
|
16198
|
-
createChart() {
|
|
16199
|
-
const element = this.chartRef().nativeElement;
|
|
16200
|
-
this.svg = select(element);
|
|
16201
|
-
}
|
|
16202
|
-
updateChart() {
|
|
16203
|
-
this.svg.selectAll('*').remove();
|
|
16204
|
-
if (this.loading()) {
|
|
16205
|
-
this.slicedDataPoints.set([]);
|
|
16206
|
-
return;
|
|
16207
|
-
}
|
|
16208
|
-
const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
|
|
16209
|
-
const extraBottom = this.xAxisLabel() ? 16 : 0;
|
|
16210
|
-
const extraLeft = this.yAxisLabel() ? 16 : 0;
|
|
16211
|
-
const leftMargin = (this.orientation() === 'horizontal' ? this.MARGIN.left : 30) + extraLeft;
|
|
16212
|
-
const width = containerWidth - leftMargin - this.MARGIN.right;
|
|
16213
|
-
const height = containerHeight - this.MARGIN.top - (this.MARGIN.bottom + extraBottom);
|
|
16214
|
-
this.maxAmountOfItems.set(this.getMaxAmountOfItems(width, height));
|
|
16215
|
-
if (!this.data()?.length) {
|
|
16216
|
-
this.slicedDataPoints.set([]);
|
|
16217
|
-
return;
|
|
16218
|
-
}
|
|
16219
|
-
const stacks = this.stackItems(this.data());
|
|
16220
|
-
const slicedStacks = stacks.slice(0, this.maxAmountOfItems());
|
|
16221
|
-
const flatStacks = slicedStacks.flat();
|
|
16222
|
-
this.slicedDataPoints.set(flatStacks);
|
|
16223
|
-
const slicedStackKeys = new Set(slicedStacks.map(stack => stack[0].stackKey));
|
|
16224
|
-
const labels = this.stackLabels()?.length
|
|
16225
|
-
? this.stackLabels()
|
|
16226
|
-
.filter(label => slicedStackKeys.has(label))
|
|
16227
|
-
.map(label => ({ stackKey: label, label }))
|
|
16228
|
-
: flatStacks.map(d => ({ stackKey: d.stackKey, label: d.label }));
|
|
16229
|
-
const g = this.svg
|
|
16230
|
-
.attr('width', width)
|
|
16231
|
-
.attr('height', height)
|
|
16232
|
-
.append('g')
|
|
16233
|
-
.attr('transform', `translate(${leftMargin},${this.MARGIN.top})`);
|
|
16234
|
-
if (this.orientation() === 'vertical') {
|
|
16235
|
-
this.createVerticalChart(g, labels, width, height, this.xAxisLabel(), this.yAxisLabel(), leftMargin);
|
|
16236
|
-
}
|
|
16237
|
-
else {
|
|
16238
|
-
this.createHorizontalChart(g, labels, width, height, this.xAxisLabel(), this.yAxisLabel(), leftMargin);
|
|
16239
|
-
}
|
|
16240
|
-
this.styleGridLines(g);
|
|
16241
|
-
}
|
|
16242
|
-
createVerticalChart(g, labels, width, height, xAxisLabel, yAxisLabel, leftMargin) {
|
|
16243
|
-
const keys = labels.map(d => d.stackKey);
|
|
16244
|
-
const x = scaleBand().domain(keys).range([0, width]);
|
|
16245
|
-
const y = scaleLinear()
|
|
16246
|
-
.domain([0, max(this.slicedDataPoints(), (d) => d.stackValue1) || 0])
|
|
16247
|
-
.nice()
|
|
16248
|
-
.range([height, 0]);
|
|
16249
|
-
// X Axis
|
|
16250
|
-
const labelSelection = g
|
|
16251
|
-
.append('g')
|
|
16252
|
-
.attr('transform', `translate(0,${height})`)
|
|
16253
|
-
.call(axisBottom(x))
|
|
16254
|
-
.selectAll('text');
|
|
16255
|
-
this.labelSelection = labelSelection
|
|
16256
|
-
.data(labels)
|
|
16257
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16258
|
-
.call(this.addTextCommonInfo)
|
|
16259
|
-
.style('cursor', 'pointer')
|
|
16260
|
-
.call(this.addTextAndTitle.bind(this), x.bandwidth())
|
|
16261
|
-
.call(this.addTextHoverHandlers.bind(this))
|
|
16262
|
-
.call(this.addTextClickHandler.bind(this));
|
|
16263
|
-
// Y Axis
|
|
16264
|
-
const tickValues = y.ticks(5).filter((tick) => Number.isInteger(tick));
|
|
16265
|
-
const yAxis = g
|
|
16266
|
-
.append('g')
|
|
16267
|
-
.call(axisLeft(y).tickValues(tickValues).tickSize(-width).tickFormat(format('~s')))
|
|
16268
|
-
.selectAll('text');
|
|
16269
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16270
|
-
yAxis.call(this.addTextCommonInfo);
|
|
16271
|
-
this.createBarSelectionGroups(g);
|
|
16272
|
-
// Add highlight rect (behind)
|
|
16273
|
-
this.addVerticalBarRectangle(x, y, 2)
|
|
16274
|
-
.attr('class', 'bar-highlight')
|
|
16275
|
-
.attr('fill', '#fff')
|
|
16276
|
-
.style('stroke', (d) => toChartColor(d.color))
|
|
16277
|
-
.attr('stroke-width', 2)
|
|
16278
|
-
.style('opacity', 0); // hidden by default;
|
|
16279
|
-
// Add main bar rect (on top)
|
|
16280
|
-
this.addVerticalBarRectangle(x, y)
|
|
16281
|
-
.attr('class', 'bar')
|
|
16282
|
-
.style('fill', (d) => toChartColor(d.color));
|
|
16283
|
-
this.appendAxisLabel(g, xAxisLabel, width / 2, height + 40);
|
|
16284
|
-
this.appendAxisLabel(g, yAxisLabel, -height / 2, -(leftMargin - 8), 'rotate(-90)');
|
|
16285
|
-
}
|
|
16286
|
-
createHorizontalChart(g, labels, width, height, xAxisLabel, yAxisLabel, leftMargin) {
|
|
16287
|
-
const x = scaleLinear()
|
|
16288
|
-
.domain([0, max(this.slicedDataPoints(), (d) => d.stackValue1) || 0])
|
|
16289
|
-
.nice()
|
|
16290
|
-
.range([0, width]);
|
|
16291
|
-
const keys = labels.map(d => d.stackKey);
|
|
16292
|
-
const y = scaleBand().domain(keys).range([0, height]);
|
|
16293
|
-
// X Axis
|
|
16294
|
-
const tickValues = x.ticks(5).filter((tick) => Number.isInteger(tick));
|
|
16295
|
-
const xAxis = g
|
|
16296
|
-
.append('g')
|
|
16297
|
-
.attr('transform', `translate(0,${height})`)
|
|
16298
|
-
.call(axisBottom(x).tickValues(tickValues).tickSize(-height).tickFormat(format('~s')))
|
|
16299
|
-
.selectAll('text');
|
|
16300
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16301
|
-
xAxis.call(this.addTextCommonInfo);
|
|
16302
|
-
// Y Axis
|
|
16303
|
-
const labelSelection = g
|
|
16304
|
-
.append('g')
|
|
16305
|
-
.attr('transform', `translate(0,0)`)
|
|
16306
|
-
.call(axisLeft(y))
|
|
16307
|
-
.selectAll('text');
|
|
16308
|
-
this.labelSelection = labelSelection
|
|
16309
|
-
.data(labels)
|
|
16310
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16311
|
-
.call(this.addTextCommonInfo)
|
|
16312
|
-
.style('cursor', 'pointer')
|
|
16313
|
-
.call(this.addTextAndTitle.bind(this), this.MARGIN.left - 10)
|
|
16314
|
-
.call(this.addTextHoverHandlers.bind(this))
|
|
16315
|
-
.call(this.addTextClickHandler.bind(this));
|
|
16316
|
-
this.createBarSelectionGroups(g);
|
|
16317
|
-
// Add highlight rect (behind)
|
|
16318
|
-
this.addHorizontalBarRectangle(x, y, 2)
|
|
16319
|
-
.attr('class', 'bar-highlight')
|
|
16320
|
-
.attr('fill', '#fff')
|
|
16321
|
-
.style('stroke', (d) => toChartColor(d.color))
|
|
16322
|
-
.attr('stroke-width', 2)
|
|
16323
|
-
.style('opacity', 0); // hidden by default;
|
|
16324
|
-
// Add main bar rect (on top)
|
|
16325
|
-
this.addHorizontalBarRectangle(x, y)
|
|
16326
|
-
.attr('class', 'bar')
|
|
16327
|
-
.style('fill', (d) => toChartColor(d.color));
|
|
16328
|
-
// X axis description label (value axis at bottom)
|
|
16329
|
-
this.appendAxisLabel(g, xAxisLabel, width / 2, height + 40);
|
|
16330
|
-
// Y axis description label: centred in the extra band reserved for it
|
|
16331
|
-
this.appendAxisLabel(g, yAxisLabel, -height / 2, -((this.MARGIN.left + leftMargin) / 2), 'rotate(-90)');
|
|
16332
|
-
}
|
|
16333
|
-
createBarSelectionGroups(g) {
|
|
16334
|
-
this.barSelection = g
|
|
16335
|
-
.selectAll('.bar-group')
|
|
16336
|
-
.data(this.slicedDataPoints(), (d) => d.key)
|
|
16337
|
-
.join('g')
|
|
16338
|
-
.attr('class', 'bar-group')
|
|
16339
|
-
.style('cursor', 'pointer')
|
|
16340
|
-
.on('mouseover', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, d.key, true))
|
|
16341
|
-
.on('mouseout', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, d.key, false))
|
|
16342
|
-
.call(this.addBarClickHandler.bind(this));
|
|
16343
|
-
}
|
|
16344
|
-
addTextCommonInfo(g) {
|
|
16345
|
-
g.style('font-size', '11px').style('fill', 'var(--clr-color-neutral-600, #666)');
|
|
16346
|
-
}
|
|
16347
|
-
addTextHoverHandlers(g) {
|
|
16348
|
-
g.on('mouseover', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, undefined, true)) //
|
|
16349
|
-
.on('mouseout', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, undefined, false));
|
|
16350
|
-
}
|
|
16351
|
-
setHoverStylesByStackKey(stackKey, key, isHover) {
|
|
16352
|
-
// When hovering a stack section, we also need to highlight the label, but not the other sections of the same stack.
|
|
16353
|
-
// But when hovering a label, we need to highlight all sections of the stack.
|
|
16354
|
-
const barGroup = this.barSelection.filter((d) => d.stackKey === stackKey && (key == null || d.key === key));
|
|
16355
|
-
barGroup.select('.bar').style('mix-blend-mode', isHover ? 'multiply' : 'unset');
|
|
16356
|
-
barGroup.select('.bar-highlight').style('opacity', isHover ? '1' : '0');
|
|
16357
|
-
this.labelSelection
|
|
16358
|
-
.filter((d) => d.stackKey === stackKey)
|
|
16359
|
-
.style('font-weight', isHover ? 'bold' : 'unset');
|
|
16360
|
-
}
|
|
16361
|
-
addBarClickHandler(g) {
|
|
16362
|
-
g.on('click', (event, d) => {
|
|
16363
|
-
event.stopPropagation();
|
|
16364
|
-
this.openTooltipByKey(d.key);
|
|
16365
|
-
});
|
|
16366
|
-
}
|
|
16367
|
-
addTextClickHandler(g) {
|
|
16368
|
-
// no click handler for labels in stacked charts
|
|
16369
|
-
if (this.stackLabels()?.length) {
|
|
16370
|
-
return;
|
|
16371
|
-
}
|
|
16372
|
-
g.on('click', (event, d) => {
|
|
16373
|
-
event.stopPropagation();
|
|
16374
|
-
// Note: In non-stacked charts, the stackKey is equal to the key, so this works - but the logic is confusing [VU3REQ-4790]
|
|
16375
|
-
this.openTooltipByKey(d.stackKey);
|
|
16376
|
-
});
|
|
16377
|
-
}
|
|
16378
|
-
openTooltipByKey(key) {
|
|
16379
|
-
const index = this.slicedDataPoints().findIndex(item => item.key === key);
|
|
16380
|
-
const rect = this.barSelection
|
|
16381
|
-
.filter((_d, i) => i === index)
|
|
16382
|
-
// we actually want the bar, not the group (because if value is 0, then the group is not positioned correctly
|
|
16383
|
-
.node()
|
|
16384
|
-
.children[0].getBoundingClientRect();
|
|
16385
|
-
const container = this.chartRef().nativeElement.getBoundingClientRect();
|
|
16386
|
-
this.tooltipPosition.set({
|
|
16387
|
-
x: rect.left - container.left + rect.width / 2,
|
|
16388
|
-
y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
|
|
16389
|
-
});
|
|
16390
|
-
this.selectedItem.set(this.slicedDataPoints()[index]);
|
|
16391
|
-
}
|
|
16392
|
-
addTextAndTitle(g, availableWidth) {
|
|
16393
|
-
g.each((d, index, nodes) => {
|
|
16394
|
-
const target = select(nodes[index]);
|
|
16395
|
-
target.text(this.textRenderer.render(d.label, 15, availableWidth, target.style('font-size'), target.style('font-family')));
|
|
16396
|
-
target.append('title').text(d.label);
|
|
16397
|
-
});
|
|
16398
|
-
}
|
|
16399
|
-
styleGridLines(g) {
|
|
16400
|
-
g.selectAll('.tick line').style('stroke', 'var(--clr-color-neutral-200, #e8e8e8)');
|
|
16401
|
-
}
|
|
16402
|
-
addVerticalBarRectangle(x, y, extraSize = 0) {
|
|
16403
|
-
return this.barSelection
|
|
16404
|
-
.append('rect')
|
|
16405
|
-
.attr('x', (d) => {
|
|
16406
|
-
return (x(d.stackKey) || 0) + (x.bandwidth() - this.barSizePx()) / 2 + extraSize;
|
|
16407
|
-
})
|
|
16408
|
-
.attr('y', (d) => y(d.stackValue1))
|
|
16409
|
-
.attr('width', this.barSizePx() - 2 * extraSize)
|
|
16410
|
-
.attr('height', (d) => y(d.stackValue0) - y(d.stackValue1))
|
|
16411
|
-
.attr('rx', 1)
|
|
16412
|
-
.attr('ry', 1);
|
|
16413
|
-
}
|
|
16414
|
-
addHorizontalBarRectangle(x, y, extraSize = 0) {
|
|
16415
|
-
return this.barSelection
|
|
16416
|
-
.append('rect')
|
|
16417
|
-
.attr('x', (d) => x(d.stackValue0))
|
|
16418
|
-
.attr('y', (d) => {
|
|
16419
|
-
return (y(d.stackKey) || 0) + (y.bandwidth() - this.barSizePx()) / 2 + extraSize;
|
|
16420
|
-
})
|
|
16421
|
-
.attr('width', (d) => x(d.stackValue1) - x(d.stackValue0))
|
|
16422
|
-
.attr('height', this.barSizePx() - 2 * extraSize)
|
|
16423
|
-
.attr('rx', 1)
|
|
16424
|
-
.attr('ry', 1);
|
|
16425
|
-
}
|
|
16426
|
-
/** Appends a shared-style axis description label to the chart group. */
|
|
16427
|
-
appendAxisLabel(g, text, x, y, transform) {
|
|
16428
|
-
if (!text) {
|
|
16429
|
-
return;
|
|
16430
|
-
}
|
|
16431
|
-
const el = g
|
|
16432
|
-
.append('text')
|
|
16433
|
-
.attr('text-anchor', 'middle')
|
|
16434
|
-
.style('font-size', '12px')
|
|
16435
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
16436
|
-
.text(text)
|
|
16437
|
-
.attr('x', x)
|
|
16438
|
-
.attr('y', y);
|
|
16439
|
-
if (transform) {
|
|
16440
|
-
el.attr('transform', transform);
|
|
16441
|
-
}
|
|
16442
|
-
}
|
|
16443
|
-
stackItems(data) {
|
|
16444
|
-
let groups;
|
|
16445
|
-
if (this.stackLabels()?.length) {
|
|
16446
|
-
groups = Array.from(group(data, (d) => d.stackKey ?? d.key));
|
|
16447
|
-
}
|
|
16448
|
-
else {
|
|
16449
|
-
groups = data.map(item => [item.key, [item]]);
|
|
16450
|
-
}
|
|
16451
|
-
return this.mapItems(groups);
|
|
16452
|
-
}
|
|
16453
|
-
mapItems(groups) {
|
|
16454
|
-
const result = [];
|
|
16455
|
-
for (const [_, items] of groups) {
|
|
16456
|
-
let stackValue = 0;
|
|
16457
|
-
const stackSum = sum(items, (d) => d.value);
|
|
16458
|
-
result.push(items.map(item => {
|
|
16459
|
-
const stackValue0 = stackValue;
|
|
16460
|
-
stackValue += item.value;
|
|
16461
|
-
return {
|
|
16462
|
-
...item,
|
|
16463
|
-
stackValue0,
|
|
16464
|
-
stackValue1: stackValue,
|
|
16465
|
-
stackKey: item.stackKey ?? item.key,
|
|
16466
|
-
percentageOfStack: stackSum === 0 ? 0 : (100 * item.value) / stackSum,
|
|
16467
|
-
};
|
|
16468
|
-
}));
|
|
16469
|
-
}
|
|
16470
|
-
return result;
|
|
16471
|
-
}
|
|
16472
|
-
getMaxAmountOfItems(width, height) {
|
|
16473
|
-
if (this.orientation() === 'vertical') {
|
|
16474
|
-
return Math.floor(width / this.barAreaSizePx());
|
|
16475
|
-
}
|
|
16476
|
-
return Math.floor(height / BarChartComponent.HORIZONTAL_BAR_MIN_HEIGHT_PX);
|
|
16477
|
-
}
|
|
16478
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: BarChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
16479
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: BarChartComponent, isStandalone: false, selector: "clr-bar-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, stackLabels: { classPropertyName: "stackLabels", publicName: "stackLabels", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: true, transformFunction: null }, tooltipOrientation: { classPropertyName: "tooltipOrientation", publicName: "tooltipOrientation", isSignal: true, isRequired: false, transformFunction: null }, barSizePx: { classPropertyName: "barSizePx", publicName: "barSizePx", isSignal: true, isRequired: false, transformFunction: null }, barAreaSizePx: { classPropertyName: "barAreaSizePx", publicName: "barAreaSizePx", isSignal: true, isRequired: false, transformFunction: null }, tooltipPercentOfTotal: { classPropertyName: "tooltipPercentOfTotal", publicName: "tooltipPercentOfTotal", isSignal: true, isRequired: false, transformFunction: null }, tooltipPercentOf: { classPropertyName: "tooltipPercentOf", publicName: "tooltipPercentOf", isSignal: true, isRequired: false, transformFunction: null }, noItemsMessage: { classPropertyName: "noItemsMessage", publicName: "noItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, tooManyItemsMessage: { classPropertyName: "tooManyItemsMessage", publicName: "tooManyItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, tooManyItemsGroupedMessage: { classPropertyName: "tooManyItemsGroupedMessage", publicName: "tooManyItemsGroupedMessage", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, exportFilename: { classPropertyName: "exportFilename", publicName: "exportFilename", isSignal: true, isRequired: false, transformFunction: null }, xAxisLabel: { classPropertyName: "xAxisLabel", publicName: "xAxisLabel", isSignal: true, isRequired: false, transformFunction: null }, yAxisLabel: { classPropertyName: "yAxisLabel", publicName: "yAxisLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueClicked: "valueClicked" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !data()?.length\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"!stackLabels() ? selectedItem()?.color : undefined\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"valueClicked.emit({ key: tooltipKey(), label: tooltipLabel(), value: tooltipValue() })\"\n >\n <ng-container ngProjectAs=\"cng-title\"> ({{ tooltipValue() }}) {{ tooltipLabel() }}</ng-container>\n\n <p class=\"mt-0\">\n {{ (100 * tooltipValue()) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n\n @if (stackLabels()) { @let stackKey = selectedItem().stackKey; @for (slice of selectedStackSlices(); track\n slice.key) {\n <div class=\"mt-0-5 d-flex\">\n <div class=\"color-square mr-0-5\" [style.background-color]=\"toChartColor(slice.color)\"></div>\n <strong>{{ slice.fullLabel || slice.label }}: </strong>\n <span\n class=\"has-more-info\"\n (click)=\"\n valueClicked.emit({\n key: [slice.key],\n label: slice.fullLabel || slice.label,\n value: slice.value,\n })\n \"\n >\n {{ slice.value }}\n </span>\n </div>\n <p class=\"mt-0-25 percentage-info\">\n {{ (100 * slice.value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n {{ slice.percentageOfStack | number : '1.0-2' }}% {{ tooltipPercentOf() }} \"{{ stackKey }}\"\n </p>\n } }\n </cng-chart-tooltip>\n } @if (loading() || !data()?.length) {\n <cng-bar-chart-skeleton [orientation]=\"orientation()\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && data()?.length) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.percentage-info{margin-left:15px}\n"], dependencies: [{ kind: "component", type: ChartAlertOverlayComponent, selector: "cng-chart-alert-overlay", inputs: ["alertMessage", "alertType"] }, { kind: "component", type: ChartExportButtonComponent, selector: "cng-chart-export-button", inputs: ["svgRef", "filename"] }, { kind: "component", type: ChartLegendComponent, selector: "cng-chart-legend", inputs: ["items"] }, { kind: "component", type: ChartSkeletonComponent, selector: "cng-bar-chart-skeleton", inputs: ["skeletonType", "orientation"] }, { kind: "component", type: ChartTooltipComponent, selector: "cng-chart-tooltip", inputs: ["tooltipPosition", "tooltipOrientation", "squareColor", "tooltipClickable"], outputs: ["tooltipClosed", "tooltipHeaderClicked"] }, { kind: "directive", type: OutsideClickDirective, selector: "[cngOutsideClick]", outputs: ["cngOutsideClick"] }, { kind: "directive", type: WindowResizeDirective, selector: "[cngWindowResize]", inputs: ["debounce", "includeFirst"], outputs: ["cngWindowResize"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
16480
|
-
}
|
|
16481
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: BarChartComponent, decorators: [{
|
|
16482
|
-
type: Component,
|
|
16483
|
-
args: [{ selector: 'clr-bar-chart', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !data()?.length\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"!stackLabels() ? selectedItem()?.color : undefined\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"valueClicked.emit({ key: tooltipKey(), label: tooltipLabel(), value: tooltipValue() })\"\n >\n <ng-container ngProjectAs=\"cng-title\"> ({{ tooltipValue() }}) {{ tooltipLabel() }}</ng-container>\n\n <p class=\"mt-0\">\n {{ (100 * tooltipValue()) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n\n @if (stackLabels()) { @let stackKey = selectedItem().stackKey; @for (slice of selectedStackSlices(); track\n slice.key) {\n <div class=\"mt-0-5 d-flex\">\n <div class=\"color-square mr-0-5\" [style.background-color]=\"toChartColor(slice.color)\"></div>\n <strong>{{ slice.fullLabel || slice.label }}: </strong>\n <span\n class=\"has-more-info\"\n (click)=\"\n valueClicked.emit({\n key: [slice.key],\n label: slice.fullLabel || slice.label,\n value: slice.value,\n })\n \"\n >\n {{ slice.value }}\n </span>\n </div>\n <p class=\"mt-0-25 percentage-info\">\n {{ (100 * slice.value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n {{ slice.percentageOfStack | number : '1.0-2' }}% {{ tooltipPercentOf() }} \"{{ stackKey }}\"\n </p>\n } }\n </cng-chart-tooltip>\n } @if (loading() || !data()?.length) {\n <cng-bar-chart-skeleton [orientation]=\"orientation()\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && data()?.length) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.percentage-info{margin-left:15px}\n"] }]
|
|
16484
|
-
}], ctorParameters: () => [] });
|
|
16485
|
-
|
|
16486
|
-
/*
|
|
16487
|
-
* Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
|
|
16488
|
-
* This software is released under MIT license.
|
|
16489
|
-
* The full license information can be found in LICENSE in the root directory of this project.
|
|
16490
|
-
*/
|
|
16491
|
-
/**
|
|
16492
|
-
* Renders interactive dot circles for a single XY series onto the given D3 group.
|
|
16493
|
-
*
|
|
16494
|
-
* - Default radius: 4px, hover radius: 6px.
|
|
16495
|
-
* - Calls `onClick` with the clicked element, data point and series when a dot is clicked.
|
|
16496
|
-
*
|
|
16497
|
-
*/
|
|
16498
|
-
function renderDots(g, series, x, y, onClick) {
|
|
16499
|
-
g.selectAll(`.dot-${series.key}`)
|
|
16500
|
-
.data(series.data, (d) => d.x)
|
|
16501
|
-
.join('circle')
|
|
16502
|
-
.attr('class', `dot dot-${series.key}`)
|
|
16503
|
-
.attr('cx', (d) => x(d.x) ?? 0)
|
|
16504
|
-
.attr('cy', (d) => y(d.value))
|
|
16505
|
-
.attr('r', 4)
|
|
16506
|
-
.style('fill', toChartColor(series.color))
|
|
16507
|
-
.attr('stroke', '#fff')
|
|
16508
|
-
.attr('stroke-width', 2)
|
|
16509
|
-
.style('cursor', 'pointer')
|
|
16510
|
-
.on('mouseover', (e) => {
|
|
16511
|
-
select(e.currentTarget).attr('r', 6);
|
|
16512
|
-
})
|
|
16513
|
-
.on('mouseout', (e) => {
|
|
16514
|
-
select(e.currentTarget).attr('r', 4);
|
|
16515
|
-
})
|
|
16516
|
-
.on('click', (event, d) => {
|
|
16517
|
-
event.stopPropagation();
|
|
16518
|
-
onClick(event.currentTarget, d, series);
|
|
16519
|
-
});
|
|
16520
|
-
}
|
|
16521
|
-
|
|
16522
|
-
/*
|
|
16523
|
-
* Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
|
|
16524
|
-
* This software is released under MIT license.
|
|
16525
|
-
* The full license information can be found in LICENSE in the root directory of this project.
|
|
16526
|
-
*/
|
|
16527
|
-
/**
|
|
16528
|
-
* Draws the X and Y axes (with integer-only Y ticks and grid lines) plus
|
|
16529
|
-
* optional description labels onto the given D3 group.
|
|
16530
|
-
*
|
|
16531
|
-
* @param g The D3 group element to append into.
|
|
16532
|
-
* @param x Any D3 axis scale keyed on strings (`ScalePoint` or `ScaleBand`).
|
|
16533
|
-
* @param y The linear Y scale.
|
|
16534
|
-
* @param opts Axis configuration options.
|
|
16535
|
-
*/
|
|
16536
|
-
function drawXYAxes(g, x, y, opts) {
|
|
16537
|
-
const { xLabelMap, width, height, xAxisLabel, yAxisLabel, effectiveLeft } = opts;
|
|
16538
|
-
// X axis
|
|
16539
|
-
g.append('g')
|
|
16540
|
-
.attr('transform', `translate(0,${height})`)
|
|
16541
|
-
.call(axisBottom(x).tickFormat((d) => xLabelMap.get(d) ?? d))
|
|
16542
|
-
.selectAll('text')
|
|
16543
|
-
.style('font-size', '11px')
|
|
16544
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)');
|
|
16545
|
-
// Y axis
|
|
16546
|
-
const tickValues = y.ticks(5).filter((t) => Number.isInteger(t));
|
|
16547
|
-
g.append('g')
|
|
16548
|
-
.call(axisLeft(y).tickValues(tickValues).tickSize(-width).tickFormat(format('~s')))
|
|
16549
|
-
.selectAll('text')
|
|
16550
|
-
.style('font-size', '11px')
|
|
16551
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)');
|
|
16552
|
-
// Optional X axis description label
|
|
16553
|
-
if (xAxisLabel) {
|
|
16554
|
-
g.append('text')
|
|
16555
|
-
.attr('x', width / 2)
|
|
16556
|
-
.attr('y', height + 40)
|
|
16557
|
-
.attr('text-anchor', 'middle')
|
|
16558
|
-
.style('font-size', '12px')
|
|
16559
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
16560
|
-
.text(xAxisLabel);
|
|
16561
|
-
}
|
|
16562
|
-
// Optional Y axis description label (rotated, centred in the left-margin band)
|
|
16563
|
-
if (yAxisLabel) {
|
|
16564
|
-
g.append('text')
|
|
16565
|
-
.attr('transform', 'rotate(-90)')
|
|
16566
|
-
.attr('x', -height / 2)
|
|
16567
|
-
.attr('y', -(effectiveLeft / 2))
|
|
16568
|
-
.attr('text-anchor', 'middle')
|
|
16569
|
-
.style('font-size', '12px')
|
|
16570
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
16571
|
-
.text(yAxisLabel);
|
|
16572
|
-
}
|
|
16573
|
-
}
|
|
16574
|
-
/**
|
|
16575
|
-
* Styles all D3 grid tick lines inside `g` with the neutral chart grid color.
|
|
16576
|
-
*/
|
|
16577
|
-
function styleGridLines(g) {
|
|
16578
|
-
g.selectAll('.tick line').style('stroke', 'var(--clr-color-neutral-200, #e8e8e8)');
|
|
16579
|
-
}
|
|
16580
|
-
|
|
16581
|
-
class LineChartComponent extends ChartBase {
|
|
16582
|
-
constructor() {
|
|
16583
|
-
super(...arguments);
|
|
16584
|
-
this.series = input.required();
|
|
16585
|
-
this.tooltipOrientation = input('top');
|
|
16586
|
-
this.showArea = input(false);
|
|
16587
|
-
this.showLegend = input(true);
|
|
16588
|
-
this.showValues = input(false);
|
|
16589
|
-
this.showExportButton = input(false);
|
|
16590
|
-
this.exportFilename = input('line-chart');
|
|
16591
|
-
this.noItemsMessage = input(NO_ITEMS_MESSAGE);
|
|
16592
|
-
this.tooltipPercentOfTotal = input('of total');
|
|
16593
|
-
/** Optional label rendered below the X axis. */
|
|
16594
|
-
this.xAxisLabel = input('');
|
|
16595
|
-
/** Optional label rendered rotated to the left of the Y axis. */
|
|
16596
|
-
this.yAxisLabel = input('');
|
|
16597
|
-
this.valueClicked = output();
|
|
16598
|
-
this.MARGIN = { top: 20, right: 30, bottom: 30, left: 65 };
|
|
16599
|
-
this.hasData = computed(() => this.series().some(s => s.data.length > 0));
|
|
16600
|
-
this.total = computed(() => this.series().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0));
|
|
16601
|
-
this.alertMessageAndType = computed(() => {
|
|
16602
|
-
if (this.loading()) {
|
|
16603
|
-
return undefined;
|
|
16604
|
-
}
|
|
16605
|
-
if (!this.hasData()) {
|
|
16606
|
-
return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
|
|
16607
|
-
}
|
|
16608
|
-
return undefined;
|
|
16609
|
-
});
|
|
16610
|
-
this.legendItems = computed(() => {
|
|
16611
|
-
if (!this.showLegend()) {
|
|
16612
|
-
return [];
|
|
16613
|
-
}
|
|
16614
|
-
return this.series().map(s => ({ label: s.label, color: s.color }));
|
|
16615
|
-
});
|
|
16616
|
-
}
|
|
16617
|
-
ngOnChanges(_changes) {
|
|
16618
|
-
if (!this.svg) {
|
|
16619
|
-
return;
|
|
16620
|
-
}
|
|
16621
|
-
requestAnimationFrame(() => this.updateChart());
|
|
16622
|
-
}
|
|
16623
|
-
ngAfterViewInit() {
|
|
16624
|
-
this.svg = select(this.chartRef().nativeElement);
|
|
16625
|
-
super.ngAfterViewInit();
|
|
16626
|
-
}
|
|
16627
|
-
updateChart() {
|
|
16628
|
-
this.svg.selectAll('*').remove();
|
|
16629
|
-
if (this.loading()) {
|
|
16630
|
-
return;
|
|
16631
|
-
}
|
|
16632
|
-
if (!this.hasData()) {
|
|
16633
|
-
return;
|
|
16634
|
-
}
|
|
16635
|
-
const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
|
|
16636
|
-
const extraBottom = this.xAxisLabel() ? 16 : 0;
|
|
16637
|
-
const extraLeft = this.yAxisLabel() ? 16 : 0;
|
|
16638
|
-
const extraTop = this.showValues() ? 14 : 0;
|
|
16639
|
-
const effectiveLeft = this.MARGIN.left + extraLeft;
|
|
16640
|
-
const width = containerWidth - effectiveLeft - this.MARGIN.right;
|
|
16641
|
-
const height = containerHeight - (this.MARGIN.top + extraTop) - (this.MARGIN.bottom + extraBottom);
|
|
16642
|
-
// Collect all unique X keys in the order they appear across all series
|
|
16643
|
-
const xKeys = [...new Set(this.series().flatMap(s => s.data.map(d => d.x)))];
|
|
16644
|
-
// Build a display-label map
|
|
16645
|
-
const xLabelMap = new Map();
|
|
16646
|
-
this.series().forEach(s => s.data.forEach(d => xLabelMap.set(d.x, d.xLabel ?? d.x)));
|
|
16647
|
-
const x = scalePoint().domain(xKeys).range([0, width]).padding(0.5);
|
|
16648
|
-
const maxY = max(this.series().flatMap(s => s.data.map(d => d.value))) ?? 0;
|
|
16649
|
-
const y = scaleLinear().domain([0, maxY]).nice().range([height, 0]);
|
|
16650
|
-
const g = this.svg
|
|
16651
|
-
.attr('width', width)
|
|
16652
|
-
.attr('height', height)
|
|
16653
|
-
.append('g')
|
|
16654
|
-
.attr('transform', `translate(${effectiveLeft},${this.MARGIN.top + extraTop})`);
|
|
16655
|
-
drawXYAxes(g, x, y, {
|
|
16656
|
-
xLabelMap,
|
|
16657
|
-
width,
|
|
16658
|
-
height,
|
|
16659
|
-
xAxisLabel: this.xAxisLabel(),
|
|
16660
|
-
yAxisLabel: this.yAxisLabel(),
|
|
16661
|
-
effectiveLeft,
|
|
16662
|
-
});
|
|
16663
|
-
styleGridLines(g);
|
|
16664
|
-
this.drawSeries(g, x, y);
|
|
16665
|
-
}
|
|
16666
|
-
drawSeries(g, x, y) {
|
|
16667
|
-
const lineGenerator = line()
|
|
16668
|
-
.x((d) => x(d.x) ?? 0)
|
|
16669
|
-
.y((d) => y(d.value))
|
|
16670
|
-
.curve(curveMonotoneX);
|
|
16671
|
-
for (const series of this.series()) {
|
|
16672
|
-
if (!series.data.length) {
|
|
16673
|
-
continue;
|
|
16674
|
-
}
|
|
16675
|
-
// Optional area fill
|
|
16676
|
-
if (this.showArea()) {
|
|
16677
|
-
g.append('path')
|
|
16678
|
-
.datum(series.data)
|
|
16679
|
-
.attr('class', `area area-${series.key}`)
|
|
16680
|
-
.style('fill', toChartColor(series.color))
|
|
16681
|
-
.attr('fill-opacity', 0.1)
|
|
16682
|
-
.attr('d', lineGenerator);
|
|
16683
|
-
}
|
|
16684
|
-
// Line path
|
|
16685
|
-
g.append('path')
|
|
16686
|
-
.datum(series.data)
|
|
16687
|
-
.attr('class', `line line-${series.key}`)
|
|
16688
|
-
.attr('fill', 'none')
|
|
16689
|
-
.style('stroke', toChartColor(series.color))
|
|
16690
|
-
.attr('stroke-width', 2)
|
|
16691
|
-
.attr('stroke-linejoin', 'round')
|
|
16692
|
-
.attr('stroke-linecap', 'round')
|
|
16693
|
-
.attr('d', lineGenerator);
|
|
16694
|
-
// Dots
|
|
16695
|
-
renderDots(g, series, x, y, (el, point, s) => this.openTooltip(el, point, s));
|
|
16696
|
-
// Value labels above each dot
|
|
16697
|
-
if (this.showValues()) {
|
|
16698
|
-
g.selectAll(`.value-label-${series.key}`)
|
|
16699
|
-
.data(series.data, (d) => d.x)
|
|
16700
|
-
.join('text')
|
|
16701
|
-
.attr('class', `value-label value-label-${series.key}`)
|
|
16702
|
-
.attr('x', (d) => x(d.x) ?? 0)
|
|
16703
|
-
.attr('y', (d) => y(d.value) - 9)
|
|
16704
|
-
.attr('text-anchor', 'middle')
|
|
16705
|
-
.style('font-size', '11px')
|
|
16706
|
-
.style('font-weight', '600')
|
|
16707
|
-
.style('fill', toChartColor(series.color))
|
|
16708
|
-
.attr('stroke', '#fff')
|
|
16709
|
-
.attr('stroke-width', 3)
|
|
16710
|
-
.attr('paint-order', 'stroke fill')
|
|
16711
|
-
.style('pointer-events', 'none')
|
|
16712
|
-
.text((d) => format('~s')(d.value));
|
|
16713
|
-
}
|
|
16714
|
-
}
|
|
16715
|
-
}
|
|
16716
|
-
openTooltip(el, point, series) {
|
|
16717
|
-
const rect = el.getBoundingClientRect();
|
|
16718
|
-
const container = this.chartRef().nativeElement.getBoundingClientRect();
|
|
16719
|
-
this.tooltipPosition.set({
|
|
16720
|
-
x: rect.left - container.left + rect.width / 2,
|
|
16721
|
-
y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
|
|
16722
|
-
});
|
|
16723
|
-
this.selectedItem.set({ ...point, seriesKey: series.key, seriesLabel: series.label, color: series.color });
|
|
16724
|
-
}
|
|
16725
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: LineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
16726
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: LineChartComponent, isStandalone: false, selector: "clr-line-chart", inputs: { series: { classPropertyName: "series", publicName: "series", isSignal: true, isRequired: true, transformFunction: null }, tooltipOrientation: { classPropertyName: "tooltipOrientation", publicName: "tooltipOrientation", isSignal: true, isRequired: false, transformFunction: null }, showArea: { classPropertyName: "showArea", publicName: "showArea", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, exportFilename: { classPropertyName: "exportFilename", publicName: "exportFilename", isSignal: true, isRequired: false, transformFunction: null }, noItemsMessage: { classPropertyName: "noItemsMessage", publicName: "noItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, tooltipPercentOfTotal: { classPropertyName: "tooltipPercentOfTotal", publicName: "tooltipPercentOfTotal", isSignal: true, isRequired: false, transformFunction: null }, xAxisLabel: { classPropertyName: "xAxisLabel", publicName: "xAxisLabel", isSignal: true, isRequired: false, transformFunction: null }, yAxisLabel: { classPropertyName: "yAxisLabel", publicName: "yAxisLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueClicked: "valueClicked" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n seriesKey: selectedItem().seriesKey,\n seriesLabel: selectedItem().seriesLabel,\n x: selectedItem().x,\n xLabel: selectedItem().xLabel,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().xLabel || selectedItem().x }}\n </ng-container>\n\n <p class=\"mt-0\">\n <strong>{{ selectedItem().seriesLabel }}:</strong>\n {{ selectedItem().value }}\n </p>\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.line,.area{pointer-events:none}.dot{transition:r .1s ease}\n"], dependencies: [{ kind: "component", type: ChartAlertOverlayComponent, selector: "cng-chart-alert-overlay", inputs: ["alertMessage", "alertType"] }, { kind: "component", type: ChartExportButtonComponent, selector: "cng-chart-export-button", inputs: ["svgRef", "filename"] }, { kind: "component", type: ChartLegendComponent, selector: "cng-chart-legend", inputs: ["items"] }, { kind: "component", type: ChartSkeletonComponent, selector: "cng-bar-chart-skeleton", inputs: ["skeletonType", "orientation"] }, { kind: "component", type: ChartTooltipComponent, selector: "cng-chart-tooltip", inputs: ["tooltipPosition", "tooltipOrientation", "squareColor", "tooltipClickable"], outputs: ["tooltipClosed", "tooltipHeaderClicked"] }, { kind: "directive", type: OutsideClickDirective, selector: "[cngOutsideClick]", outputs: ["cngOutsideClick"] }, { kind: "directive", type: WindowResizeDirective, selector: "[cngWindowResize]", inputs: ["debounce", "includeFirst"], outputs: ["cngWindowResize"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
16727
|
-
}
|
|
16728
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: LineChartComponent, decorators: [{
|
|
16729
|
-
type: Component,
|
|
16730
|
-
args: [{ selector: 'clr-line-chart', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n seriesKey: selectedItem().seriesKey,\n seriesLabel: selectedItem().seriesLabel,\n x: selectedItem().x,\n xLabel: selectedItem().xLabel,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().xLabel || selectedItem().x }}\n </ng-container>\n\n <p class=\"mt-0\">\n <strong>{{ selectedItem().seriesLabel }}:</strong>\n {{ selectedItem().value }}\n </p>\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.line,.area{pointer-events:none}.dot{transition:r .1s ease}\n"] }]
|
|
16731
|
-
}] });
|
|
16732
|
-
|
|
16733
|
-
class AreaChartComponent extends ChartBase {
|
|
16734
|
-
constructor() {
|
|
16735
|
-
super(...arguments);
|
|
16736
|
-
this.series = input.required();
|
|
16737
|
-
this.tooltipOrientation = input('top');
|
|
16738
|
-
this.showLegend = input(true);
|
|
16739
|
-
this.showExportButton = input(false);
|
|
16740
|
-
this.exportFilename = input('area-chart');
|
|
16741
|
-
/** Area fill opacity (0–1). Default: 0.2. */
|
|
16742
|
-
this.areaOpacity = input(0.2);
|
|
16743
|
-
this.noItemsMessage = input(NO_ITEMS_MESSAGE);
|
|
16744
|
-
this.tooltipPercentOfTotal = input('of total');
|
|
16745
|
-
/** Optional label rendered below the X axis. */
|
|
16746
|
-
this.xAxisLabel = input('');
|
|
16747
|
-
/** Optional label rendered rotated to the left of the Y axis. */
|
|
16748
|
-
this.yAxisLabel = input('');
|
|
16749
|
-
this.valueClicked = output();
|
|
16750
|
-
this.MARGIN = { top: 20, right: 30, bottom: 30, left: 65 };
|
|
16751
|
-
this.hasData = computed(() => this.series().some(s => s.data.length > 0));
|
|
16752
|
-
this.total = computed(() => this.series().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0));
|
|
16753
|
-
this.alertMessageAndType = computed(() => {
|
|
16754
|
-
if (this.loading()) {
|
|
16755
|
-
return undefined;
|
|
16756
|
-
}
|
|
16757
|
-
if (!this.hasData()) {
|
|
16758
|
-
return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
|
|
16759
|
-
}
|
|
16760
|
-
return undefined;
|
|
16761
|
-
});
|
|
16762
|
-
this.legendItems = computed(() => {
|
|
16763
|
-
if (!this.showLegend()) {
|
|
16764
|
-
return [];
|
|
16765
|
-
}
|
|
16766
|
-
return this.series().map(s => ({ label: s.label, color: s.color }));
|
|
16767
|
-
});
|
|
16768
|
-
}
|
|
16769
|
-
ngOnChanges(_changes) {
|
|
16770
|
-
if (!this.svg) {
|
|
16771
|
-
return;
|
|
16772
|
-
}
|
|
16773
|
-
requestAnimationFrame(() => this.updateChart());
|
|
16774
|
-
}
|
|
16775
|
-
ngAfterViewInit() {
|
|
16776
|
-
this.svg = select(this.chartRef().nativeElement);
|
|
16777
|
-
super.ngAfterViewInit();
|
|
16778
|
-
}
|
|
16779
|
-
updateChart() {
|
|
16780
|
-
this.svg.selectAll('*').remove();
|
|
16781
|
-
if (this.loading() || !this.hasData()) {
|
|
16782
|
-
return;
|
|
16783
|
-
}
|
|
16784
|
-
const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
|
|
16785
|
-
const extraBottom = this.xAxisLabel() ? 16 : 0;
|
|
16786
|
-
const extraLeft = this.yAxisLabel() ? 16 : 0;
|
|
16787
|
-
const effectiveLeft = this.MARGIN.left + extraLeft;
|
|
16788
|
-
const width = containerWidth - effectiveLeft - this.MARGIN.right;
|
|
16789
|
-
const height = containerHeight - this.MARGIN.top - (this.MARGIN.bottom + extraBottom);
|
|
16790
|
-
const xKeys = [...new Set(this.series().flatMap(s => s.data.map(d => d.x)))];
|
|
16791
|
-
const xLabelMap = new Map();
|
|
16792
|
-
this.series().forEach(s => s.data.forEach(d => xLabelMap.set(d.x, d.xLabel ?? d.x)));
|
|
16793
|
-
const x = scalePoint().domain(xKeys).range([0, width]).padding(0.5);
|
|
16794
|
-
const maxY = max(this.series().flatMap(s => s.data.map(d => d.value))) ?? 0;
|
|
16795
|
-
const y = scaleLinear().domain([0, maxY]).nice().range([height, 0]);
|
|
16796
|
-
const g = this.svg
|
|
16797
|
-
.attr('width', width)
|
|
16798
|
-
.attr('height', height)
|
|
16799
|
-
.append('g')
|
|
16800
|
-
.attr('transform', `translate(${effectiveLeft},${this.MARGIN.top})`);
|
|
16801
|
-
drawXYAxes(g, x, y, {
|
|
16802
|
-
xLabelMap,
|
|
16803
|
-
width,
|
|
16804
|
-
height,
|
|
16805
|
-
xAxisLabel: this.xAxisLabel(),
|
|
16806
|
-
yAxisLabel: this.yAxisLabel(),
|
|
16807
|
-
effectiveLeft,
|
|
16808
|
-
});
|
|
16809
|
-
styleGridLines(g);
|
|
16810
|
-
this.drawSeries(g, x, y, height);
|
|
16811
|
-
}
|
|
16812
|
-
drawSeries(g, x, y, height) {
|
|
16813
|
-
const areaGenerator = area()
|
|
16814
|
-
.x((d) => x(d.x) ?? 0)
|
|
16815
|
-
.y0(height)
|
|
16816
|
-
.y1((d) => y(d.value))
|
|
16817
|
-
.curve(curveMonotoneX);
|
|
16818
|
-
const lineGenerator = line()
|
|
16819
|
-
.x((d) => x(d.x) ?? 0)
|
|
16820
|
-
.y((d) => y(d.value))
|
|
16821
|
-
.curve(curveMonotoneX);
|
|
16822
|
-
// Draw areas first (behind lines)
|
|
16823
|
-
for (const series of this.series()) {
|
|
16824
|
-
if (!series.data.length) {
|
|
16825
|
-
continue;
|
|
16826
|
-
}
|
|
16827
|
-
g.append('path')
|
|
16828
|
-
.datum(series.data)
|
|
16829
|
-
.attr('class', `area area-${series.key}`)
|
|
16830
|
-
.style('fill', toChartColor(series.color))
|
|
16831
|
-
.attr('fill-opacity', this.areaOpacity())
|
|
16832
|
-
.attr('stroke', 'none')
|
|
16833
|
-
.attr('d', areaGenerator);
|
|
16834
|
-
}
|
|
16835
|
-
// Draw lines and dots on top
|
|
16836
|
-
for (const series of this.series()) {
|
|
16837
|
-
if (!series.data.length) {
|
|
16838
|
-
continue;
|
|
16839
|
-
}
|
|
16840
|
-
g.append('path')
|
|
16841
|
-
.datum(series.data)
|
|
16842
|
-
.attr('class', `line line-${series.key}`)
|
|
16843
|
-
.attr('fill', 'none')
|
|
16844
|
-
.style('stroke', toChartColor(series.color))
|
|
16845
|
-
.attr('stroke-width', 2)
|
|
16846
|
-
.attr('stroke-linejoin', 'round')
|
|
16847
|
-
.attr('stroke-linecap', 'round')
|
|
16848
|
-
.attr('d', lineGenerator);
|
|
16849
|
-
renderDots(g, series, x, y, (el, point, s) => this.openTooltip(el, point, s));
|
|
16850
|
-
}
|
|
16851
|
-
}
|
|
16852
|
-
openTooltip(el, point, series) {
|
|
16853
|
-
const rect = el.getBoundingClientRect();
|
|
16854
|
-
const container = this.chartRef().nativeElement.getBoundingClientRect();
|
|
16855
|
-
this.tooltipPosition.set({
|
|
16856
|
-
x: rect.left - container.left + rect.width / 2,
|
|
16857
|
-
y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
|
|
16858
|
-
});
|
|
16859
|
-
this.selectedItem.set({ ...point, seriesKey: series.key, seriesLabel: series.label, color: series.color });
|
|
16860
|
-
}
|
|
16861
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: AreaChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
16862
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: AreaChartComponent, isStandalone: false, selector: "clr-area-chart", inputs: { series: { classPropertyName: "series", publicName: "series", isSignal: true, isRequired: true, transformFunction: null }, tooltipOrientation: { classPropertyName: "tooltipOrientation", publicName: "tooltipOrientation", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, exportFilename: { classPropertyName: "exportFilename", publicName: "exportFilename", isSignal: true, isRequired: false, transformFunction: null }, areaOpacity: { classPropertyName: "areaOpacity", publicName: "areaOpacity", isSignal: true, isRequired: false, transformFunction: null }, noItemsMessage: { classPropertyName: "noItemsMessage", publicName: "noItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, tooltipPercentOfTotal: { classPropertyName: "tooltipPercentOfTotal", publicName: "tooltipPercentOfTotal", isSignal: true, isRequired: false, transformFunction: null }, xAxisLabel: { classPropertyName: "xAxisLabel", publicName: "xAxisLabel", isSignal: true, isRequired: false, transformFunction: null }, yAxisLabel: { classPropertyName: "yAxisLabel", publicName: "yAxisLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueClicked: "valueClicked" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n seriesKey: selectedItem().seriesKey,\n seriesLabel: selectedItem().seriesLabel,\n x: selectedItem().x,\n xLabel: selectedItem().xLabel,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().xLabel || selectedItem().x }}\n </ng-container>\n\n <p class=\"mt-0\">\n <strong>{{ selectedItem().seriesLabel }}:</strong>\n {{ selectedItem().value }}\n </p>\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.line,.area{pointer-events:none}.dot{transition:r .1s ease}\n"], dependencies: [{ kind: "component", type: ChartAlertOverlayComponent, selector: "cng-chart-alert-overlay", inputs: ["alertMessage", "alertType"] }, { kind: "component", type: ChartExportButtonComponent, selector: "cng-chart-export-button", inputs: ["svgRef", "filename"] }, { kind: "component", type: ChartLegendComponent, selector: "cng-chart-legend", inputs: ["items"] }, { kind: "component", type: ChartSkeletonComponent, selector: "cng-bar-chart-skeleton", inputs: ["skeletonType", "orientation"] }, { kind: "component", type: ChartTooltipComponent, selector: "cng-chart-tooltip", inputs: ["tooltipPosition", "tooltipOrientation", "squareColor", "tooltipClickable"], outputs: ["tooltipClosed", "tooltipHeaderClicked"] }, { kind: "directive", type: OutsideClickDirective, selector: "[cngOutsideClick]", outputs: ["cngOutsideClick"] }, { kind: "directive", type: WindowResizeDirective, selector: "[cngWindowResize]", inputs: ["debounce", "includeFirst"], outputs: ["cngWindowResize"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
16863
|
-
}
|
|
16864
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: AreaChartComponent, decorators: [{
|
|
16865
|
-
type: Component,
|
|
16866
|
-
args: [{ selector: 'clr-area-chart', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n seriesKey: selectedItem().seriesKey,\n seriesLabel: selectedItem().seriesLabel,\n x: selectedItem().x,\n xLabel: selectedItem().xLabel,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().xLabel || selectedItem().x }}\n </ng-container>\n\n <p class=\"mt-0\">\n <strong>{{ selectedItem().seriesLabel }}:</strong>\n {{ selectedItem().value }}\n </p>\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.line,.area{pointer-events:none}.dot{transition:r .1s ease}\n"] }]
|
|
16867
|
-
}] });
|
|
16868
|
-
|
|
16869
|
-
class ComboChartComponent extends ChartBase {
|
|
16870
|
-
constructor() {
|
|
16871
|
-
super(...arguments);
|
|
16872
|
-
// ── Inputs ──────────────────────────────────────────────────────────────────
|
|
16873
|
-
this.barSeries = input([]);
|
|
16874
|
-
this.lineSeries = input([]);
|
|
16875
|
-
this.tooltipOrientation = input('top');
|
|
16876
|
-
this.showLegend = input(true);
|
|
16877
|
-
this.showExportButton = input(false);
|
|
16878
|
-
this.exportFilename = input('combo-chart');
|
|
16879
|
-
this.noItemsMessage = input(NO_ITEMS_MESSAGE);
|
|
16880
|
-
this.tooltipPercentOfTotal = input('of total');
|
|
16881
|
-
/** Optional label rendered below the X axis. */
|
|
16882
|
-
this.xAxisLabel = input('');
|
|
16883
|
-
/** Optional label rendered rotated to the left of the Y axis (bar scale). */
|
|
16884
|
-
this.yAxisLabel = input('');
|
|
16885
|
-
/** Optional label rendered rotated to the right of the Y axis (line scale). */
|
|
16886
|
-
this.yLineAxisLabel = input('');
|
|
16887
|
-
// ── Outputs ─────────────────────────────────────────────────────────────────
|
|
16888
|
-
this.valueClicked = output();
|
|
16889
|
-
// ── Layout ───────────────────────────────────────────────────────────────────
|
|
16890
|
-
this.MARGIN = { top: 20, right: 30, bottom: 30, left: 65 };
|
|
16891
|
-
// ── Computed ─────────────────────────────────────────────────────────────────
|
|
16892
|
-
this.hasData = computed(() => this.barSeries().some(s => s.data.length > 0) || this.lineSeries().some(s => s.data.length > 0));
|
|
16893
|
-
this.total = computed(() => {
|
|
16894
|
-
const barTotal = this.barSeries().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0);
|
|
16895
|
-
const lineTotal = this.lineSeries().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0);
|
|
16896
|
-
return barTotal + lineTotal;
|
|
16897
|
-
});
|
|
16898
|
-
this.alertMessageAndType = computed(() => {
|
|
16899
|
-
if (this.loading()) {
|
|
16900
|
-
return undefined;
|
|
16901
|
-
}
|
|
16902
|
-
if (!this.hasData()) {
|
|
16903
|
-
return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
|
|
16904
|
-
}
|
|
16905
|
-
return undefined;
|
|
16906
|
-
});
|
|
16907
|
-
this.legendItems = computed(() => {
|
|
16908
|
-
if (!this.showLegend()) {
|
|
16909
|
-
return [];
|
|
16910
|
-
}
|
|
16911
|
-
const barItems = this.barSeries().map(s => ({ label: s.label, color: s.color }));
|
|
16912
|
-
const lineItems = this.lineSeries().map(s => ({ label: s.label, color: s.color }));
|
|
16913
|
-
return [...barItems, ...lineItems];
|
|
16914
|
-
});
|
|
16915
|
-
}
|
|
16916
|
-
// ── Lifecycle ────────────────────────────────────────────────────────────────
|
|
16917
|
-
ngOnChanges(_changes) {
|
|
16918
|
-
if (!this.svg) {
|
|
16919
|
-
return;
|
|
16920
|
-
}
|
|
16921
|
-
requestAnimationFrame(() => this.updateChart());
|
|
16922
|
-
}
|
|
16923
|
-
ngAfterViewInit() {
|
|
16924
|
-
this.svg = select(this.chartRef().nativeElement);
|
|
16925
|
-
super.ngAfterViewInit();
|
|
16926
|
-
}
|
|
16927
|
-
// ── Chart rendering ──────────────────────────────────────────────────────────
|
|
16928
|
-
updateChart() {
|
|
16929
|
-
this.svg.selectAll('*').remove();
|
|
16930
|
-
if (this.loading() || !this.hasData()) {
|
|
16931
|
-
return;
|
|
16932
|
-
}
|
|
16933
|
-
const { width: cw, height: ch } = this.getContainerDimensions();
|
|
16934
|
-
const extraBottom = this.xAxisLabel() ? 16 : 0;
|
|
16935
|
-
const extraLeft = this.yAxisLabel() ? 16 : 0;
|
|
16936
|
-
const effectiveLeft = this.MARGIN.left + extraLeft;
|
|
16937
|
-
// Reserve extra right margin when line series are present (right Y axis)
|
|
16938
|
-
const hasLines = this.lineSeries().some(s => s.data.length > 0);
|
|
16939
|
-
const extraRight = hasLines ? (this.yLineAxisLabel() ? 50 : 40) : 0;
|
|
16940
|
-
const width = cw - effectiveLeft - this.MARGIN.right - extraRight;
|
|
16941
|
-
const height = ch - this.MARGIN.top - (this.MARGIN.bottom + extraBottom);
|
|
16942
|
-
// Collect all unique X keys preserving order
|
|
16943
|
-
const allBarKeys = this.barSeries().flatMap(s => s.data.map(d => d.x));
|
|
16944
|
-
const allLineKeys = this.lineSeries().flatMap(s => s.data.map(d => d.x));
|
|
16945
|
-
const xKeys = [...new Set([...allBarKeys, ...allLineKeys])];
|
|
16946
|
-
// Build X-label map
|
|
16947
|
-
const xLabelMap = new Map();
|
|
16948
|
-
this.barSeries().forEach(s => s.data.forEach(d => xLabelMap.set(d.x, d.xLabel ?? d.x)));
|
|
16949
|
-
// Outer X scale (one band per category)
|
|
16950
|
-
const x = scaleBand().domain(xKeys).range([0, width]).paddingInner(0.35).paddingOuter(0.15);
|
|
16951
|
-
// ── Separate Y scales ────────────────────────────────────────────────────
|
|
16952
|
-
// Bar Y scale (left axis)
|
|
16953
|
-
const xStackTotals = new Map();
|
|
16954
|
-
for (const series of this.barSeries()) {
|
|
16955
|
-
for (const point of series.data) {
|
|
16956
|
-
xStackTotals.set(point.x, (xStackTotals.get(point.x) ?? 0) + point.value);
|
|
16957
|
-
}
|
|
16958
|
-
}
|
|
16959
|
-
const maxBar = max([...xStackTotals.values()]) ?? 0;
|
|
16960
|
-
const yBar = scaleLinear()
|
|
16961
|
-
.domain([0, maxBar || 1])
|
|
16962
|
-
.nice()
|
|
16963
|
-
.range([height, 0]);
|
|
16964
|
-
// Line Y scale (right axis)
|
|
16965
|
-
const maxLine = max(this.lineSeries().flatMap(s => s.data.map(d => d.value))) ?? 0;
|
|
16966
|
-
const yLine = scaleLinear()
|
|
16967
|
-
.domain([0, maxLine || 1])
|
|
16968
|
-
.nice()
|
|
16969
|
-
.range([height, 0]);
|
|
16970
|
-
const g = this.svg
|
|
16971
|
-
.attr('width', width)
|
|
16972
|
-
.attr('height', height)
|
|
16973
|
-
.append('g')
|
|
16974
|
-
.attr('transform', `translate(${effectiveLeft},${this.MARGIN.top})`);
|
|
16975
|
-
drawXYAxes(g, x, yBar, {
|
|
16976
|
-
xLabelMap,
|
|
16977
|
-
width,
|
|
16978
|
-
height,
|
|
16979
|
-
xAxisLabel: this.xAxisLabel(),
|
|
16980
|
-
yAxisLabel: this.yAxisLabel(),
|
|
16981
|
-
effectiveLeft,
|
|
16982
|
-
});
|
|
16983
|
-
styleGridLines(g);
|
|
16984
|
-
// ── Right Y axis for line series ─────────────────────────────────────────
|
|
16985
|
-
if (hasLines) {
|
|
16986
|
-
const lineTickValues = yLine.ticks(5).filter((t) => Number.isInteger(t));
|
|
16987
|
-
const rightAxisG = g
|
|
16988
|
-
.append('g')
|
|
16989
|
-
.attr('transform', `translate(${width},0)`)
|
|
16990
|
-
.call(axisRight(yLine).tickValues(lineTickValues).tickFormat(format('~s')).tickSize(-width) // draw grid lines across the chart
|
|
16991
|
-
);
|
|
16992
|
-
rightAxisG.selectAll('text').style('font-size', '11px').style('fill', 'var(--clr-color-neutral-600, #666)');
|
|
16993
|
-
// Style the line-axis grid lines with a distinct dashed color
|
|
16994
|
-
rightAxisG
|
|
16995
|
-
.selectAll('.tick line')
|
|
16996
|
-
.style('stroke', 'var(--clr-color-neutral-400, #b3b3b3)')
|
|
16997
|
-
.style('stroke-dasharray', '4 3')
|
|
16998
|
-
.style('stroke-opacity', '0.8');
|
|
16999
|
-
// Remove the default domain line so it doesn't overlap the chart
|
|
17000
|
-
rightAxisG.select('.domain').remove();
|
|
17001
|
-
if (this.yLineAxisLabel()) {
|
|
17002
|
-
g.append('text')
|
|
17003
|
-
.attr('transform', 'rotate(-90)')
|
|
17004
|
-
.attr('x', -height / 2)
|
|
17005
|
-
.attr('y', width + extraRight - 4)
|
|
17006
|
-
.attr('text-anchor', 'middle')
|
|
17007
|
-
.style('font-size', '12px')
|
|
17008
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
17009
|
-
.text(this.yLineAxisLabel());
|
|
17010
|
-
}
|
|
17011
|
-
}
|
|
17012
|
-
this.drawBars(g, x, yBar);
|
|
17013
|
-
this.drawLines(g, x, yLine);
|
|
17014
|
-
}
|
|
17015
|
-
drawBars(g, x, y) {
|
|
17016
|
-
// Running stack base (cumulative sum) per X key across all bar series
|
|
17017
|
-
const stackBase = new Map();
|
|
17018
|
-
for (const series of this.barSeries()) {
|
|
17019
|
-
if (!series.data.length) {
|
|
17020
|
-
continue;
|
|
17021
|
-
}
|
|
17022
|
-
const stacked = series.data.map(d => ({
|
|
17023
|
-
...d,
|
|
17024
|
-
base: stackBase.get(d.x) ?? 0,
|
|
17025
|
-
}));
|
|
17026
|
-
// Advance running totals for the next series
|
|
17027
|
-
for (const d of series.data) {
|
|
17028
|
-
stackBase.set(d.x, (stackBase.get(d.x) ?? 0) + d.value);
|
|
17029
|
-
}
|
|
17030
|
-
g.selectAll(`.bar-${series.key}`)
|
|
17031
|
-
.data(stacked, (d) => d.x)
|
|
17032
|
-
.join('rect')
|
|
17033
|
-
.attr('class', `bar bar-${series.key}`)
|
|
17034
|
-
.attr('x', (d) => x(d.x) ?? 0)
|
|
17035
|
-
.attr('y', (d) => y(d.base + d.value))
|
|
17036
|
-
.attr('width', x.bandwidth())
|
|
17037
|
-
.attr('height', (d) => y(d.base) - y(d.base + d.value))
|
|
17038
|
-
.attr('rx', 2)
|
|
17039
|
-
.attr('ry', 2)
|
|
17040
|
-
.style('fill', toChartColor(series.color))
|
|
17041
|
-
.style('cursor', 'pointer')
|
|
17042
|
-
.on('mouseover', (e) => {
|
|
17043
|
-
select(e.currentTarget).attr('fill-opacity', 0.75);
|
|
17044
|
-
})
|
|
17045
|
-
.on('mouseout', (e) => {
|
|
17046
|
-
select(e.currentTarget).attr('fill-opacity', 1);
|
|
17047
|
-
})
|
|
17048
|
-
.on('click', (event, d) => {
|
|
17049
|
-
event.stopPropagation();
|
|
17050
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
17051
|
-
const container = this.chartRef().nativeElement.getBoundingClientRect();
|
|
17052
|
-
this.tooltipPosition.set({
|
|
17053
|
-
x: rect.left - container.left + rect.width / 2,
|
|
17054
|
-
y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
|
|
17055
|
-
});
|
|
17056
|
-
this.selectedItem.set({
|
|
17057
|
-
seriesKey: series.key,
|
|
17058
|
-
seriesLabel: series.label,
|
|
17059
|
-
seriesType: 'bar',
|
|
17060
|
-
color: series.color,
|
|
17061
|
-
x: d.x,
|
|
17062
|
-
xLabel: d.xLabel,
|
|
17063
|
-
value: d.value,
|
|
17064
|
-
total: this.total(),
|
|
17065
|
-
});
|
|
17066
|
-
});
|
|
17067
|
-
}
|
|
17068
|
-
}
|
|
17069
|
-
drawLines(g, x, y) {
|
|
17070
|
-
// Map X key → band center position for line/dot placement
|
|
17071
|
-
const lineX = (key) => (x(key) ?? 0) + x.bandwidth() / 2;
|
|
17072
|
-
const lineGenerator = line()
|
|
17073
|
-
.x((d) => lineX(d.x))
|
|
17074
|
-
.y((d) => y(d.value))
|
|
17075
|
-
.curve(curveMonotoneX);
|
|
17076
|
-
for (const series of this.lineSeries()) {
|
|
17077
|
-
if (!series.data.length) {
|
|
17078
|
-
continue;
|
|
17079
|
-
}
|
|
17080
|
-
// Line path
|
|
17081
|
-
g.append('path')
|
|
17082
|
-
.datum(series.data)
|
|
17083
|
-
.attr('class', `combo-line combo-line-${series.key}`)
|
|
17084
|
-
.attr('fill', 'none')
|
|
17085
|
-
.style('stroke', toChartColor(series.color))
|
|
17086
|
-
.attr('stroke-width', 2.5)
|
|
17087
|
-
.attr('stroke-linejoin', 'round')
|
|
17088
|
-
.attr('stroke-linecap', 'round')
|
|
17089
|
-
.attr('d', lineGenerator);
|
|
17090
|
-
// Dots
|
|
17091
|
-
g.selectAll(`.combo-dot-${series.key}`)
|
|
17092
|
-
.data(series.data, (d) => d.x)
|
|
17093
|
-
.join('circle')
|
|
17094
|
-
.attr('class', `combo-dot combo-dot-${series.key}`)
|
|
17095
|
-
.attr('cx', (d) => lineX(d.x))
|
|
17096
|
-
.attr('cy', (d) => y(d.value))
|
|
17097
|
-
.attr('r', 4)
|
|
17098
|
-
.style('fill', toChartColor(series.color))
|
|
17099
|
-
.attr('stroke', '#fff')
|
|
17100
|
-
.attr('stroke-width', 2)
|
|
17101
|
-
.style('cursor', 'pointer')
|
|
17102
|
-
.on('mouseover', (e) => {
|
|
17103
|
-
select(e.currentTarget).attr('r', 6);
|
|
17104
|
-
})
|
|
17105
|
-
.on('mouseout', (e) => {
|
|
17106
|
-
select(e.currentTarget).attr('r', 4);
|
|
17107
|
-
})
|
|
17108
|
-
.on('click', (event, d) => {
|
|
17109
|
-
event.stopPropagation();
|
|
17110
|
-
const el = event.currentTarget;
|
|
17111
|
-
const rect = el.getBoundingClientRect();
|
|
17112
|
-
const container = this.chartRef().nativeElement.getBoundingClientRect();
|
|
17113
|
-
this.tooltipPosition.set({
|
|
17114
|
-
x: rect.left - container.left + rect.width / 2,
|
|
17115
|
-
y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
|
|
17116
|
-
});
|
|
17117
|
-
this.selectedItem.set({
|
|
17118
|
-
seriesKey: series.key,
|
|
17119
|
-
seriesLabel: series.label,
|
|
17120
|
-
seriesType: 'line',
|
|
17121
|
-
color: series.color,
|
|
17122
|
-
x: d.x,
|
|
17123
|
-
value: d.value,
|
|
17124
|
-
total: this.total(),
|
|
17125
|
-
});
|
|
17126
|
-
});
|
|
17127
|
-
}
|
|
17128
|
-
}
|
|
17129
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ComboChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
17130
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: ComboChartComponent, isStandalone: false, selector: "clr-combo-chart", inputs: { barSeries: { classPropertyName: "barSeries", publicName: "barSeries", isSignal: true, isRequired: false, transformFunction: null }, lineSeries: { classPropertyName: "lineSeries", publicName: "lineSeries", isSignal: true, isRequired: false, transformFunction: null }, tooltipOrientation: { classPropertyName: "tooltipOrientation", publicName: "tooltipOrientation", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, exportFilename: { classPropertyName: "exportFilename", publicName: "exportFilename", isSignal: true, isRequired: false, transformFunction: null }, noItemsMessage: { classPropertyName: "noItemsMessage", publicName: "noItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, tooltipPercentOfTotal: { classPropertyName: "tooltipPercentOfTotal", publicName: "tooltipPercentOfTotal", isSignal: true, isRequired: false, transformFunction: null }, xAxisLabel: { classPropertyName: "xAxisLabel", publicName: "xAxisLabel", isSignal: true, isRequired: false, transformFunction: null }, yAxisLabel: { classPropertyName: "yAxisLabel", publicName: "yAxisLabel", isSignal: true, isRequired: false, transformFunction: null }, yLineAxisLabel: { classPropertyName: "yLineAxisLabel", publicName: "yLineAxisLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueClicked: "valueClicked" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n seriesKey: selectedItem().seriesKey,\n seriesLabel: selectedItem().seriesLabel,\n seriesType: selectedItem().seriesType,\n x: selectedItem().x,\n xLabel: selectedItem().xLabel,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().xLabel || selectedItem().x }}\n </ng-container>\n\n <p class=\"mt-0\">\n <strong>{{ selectedItem().seriesLabel }}:</strong>\n {{ selectedItem().value }}\n </p>\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / selectedItem().total | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.combo-line{pointer-events:none}.combo-dot{transition:r .1s ease}\n"], dependencies: [{ kind: "component", type: ChartAlertOverlayComponent, selector: "cng-chart-alert-overlay", inputs: ["alertMessage", "alertType"] }, { kind: "component", type: ChartExportButtonComponent, selector: "cng-chart-export-button", inputs: ["svgRef", "filename"] }, { kind: "component", type: ChartLegendComponent, selector: "cng-chart-legend", inputs: ["items"] }, { kind: "component", type: ChartSkeletonComponent, selector: "cng-bar-chart-skeleton", inputs: ["skeletonType", "orientation"] }, { kind: "component", type: ChartTooltipComponent, selector: "cng-chart-tooltip", inputs: ["tooltipPosition", "tooltipOrientation", "squareColor", "tooltipClickable"], outputs: ["tooltipClosed", "tooltipHeaderClicked"] }, { kind: "directive", type: OutsideClickDirective, selector: "[cngOutsideClick]", outputs: ["cngOutsideClick"] }, { kind: "directive", type: WindowResizeDirective, selector: "[cngWindowResize]", inputs: ["debounce", "includeFirst"], outputs: ["cngWindowResize"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
17131
|
-
}
|
|
17132
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ComboChartComponent, decorators: [{
|
|
17133
|
-
type: Component,
|
|
17134
|
-
args: [{ selector: 'clr-combo-chart', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n seriesKey: selectedItem().seriesKey,\n seriesLabel: selectedItem().seriesLabel,\n seriesType: selectedItem().seriesType,\n x: selectedItem().x,\n xLabel: selectedItem().xLabel,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().xLabel || selectedItem().x }}\n </ng-container>\n\n <p class=\"mt-0\">\n <strong>{{ selectedItem().seriesLabel }}:</strong>\n {{ selectedItem().value }}\n </p>\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / selectedItem().total | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep .domain{display:none}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.combo-line{pointer-events:none}.combo-dot{transition:r .1s ease}\n"] }]
|
|
17135
|
-
}] });
|
|
17136
|
-
|
|
17137
|
-
class PieChartComponent extends ChartBase {
|
|
17138
|
-
constructor() {
|
|
17139
|
-
super(...arguments);
|
|
17140
|
-
this.data = input.required();
|
|
17141
|
-
this.donut = input(true);
|
|
17142
|
-
this.showLegend = input(true);
|
|
17143
|
-
this.showExportButton = input(false);
|
|
17144
|
-
this.exportFilename = input('pie-chart');
|
|
17145
|
-
this.tooltipOrientation = input('top');
|
|
17146
|
-
this.noItemsMessage = input(NO_ITEMS_MESSAGE);
|
|
17147
|
-
this.tooltipPercentOfTotal = input('of total');
|
|
17148
|
-
this.valueClicked = output();
|
|
17149
|
-
this.hasData = computed(() => this.data()?.some(d => d.value > 0));
|
|
17150
|
-
this.total = computed(() => this.data()?.reduce((acc, d) => acc + d.value, 0) ?? 0);
|
|
17151
|
-
this.alertMessageAndType = computed(() => {
|
|
17152
|
-
if (this.loading()) {
|
|
17153
|
-
return undefined;
|
|
17154
|
-
}
|
|
17155
|
-
if (!this.hasData()) {
|
|
17156
|
-
return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
|
|
17157
|
-
}
|
|
17158
|
-
return undefined;
|
|
17159
|
-
});
|
|
17160
|
-
this.legendItems = computed(() => {
|
|
17161
|
-
if (!this.showLegend()) {
|
|
17162
|
-
return [];
|
|
17163
|
-
}
|
|
17164
|
-
return (this.data() ?? []).filter(d => d.value > 0).map(d => ({ label: d.fullLabel ?? d.label, color: d.color }));
|
|
17165
|
-
});
|
|
17166
|
-
}
|
|
17167
|
-
ngOnChanges(_changes) {
|
|
17168
|
-
if (!this.svg) {
|
|
17169
|
-
return;
|
|
17170
|
-
}
|
|
17171
|
-
requestAnimationFrame(() => this.updateChart());
|
|
17172
|
-
}
|
|
17173
|
-
ngAfterViewInit() {
|
|
17174
|
-
this.svg = select(this.chartRef().nativeElement);
|
|
17175
|
-
super.ngAfterViewInit();
|
|
17176
|
-
}
|
|
17177
|
-
updateChart() {
|
|
17178
|
-
this.svg.selectAll('*').remove();
|
|
17179
|
-
if (this.loading() || !this.hasData()) {
|
|
17180
|
-
return;
|
|
17181
|
-
}
|
|
17182
|
-
const { width, height } = this.getContainerDimensions();
|
|
17183
|
-
const radius = Math.min(width, height) / 2;
|
|
17184
|
-
const innerRadius = this.donut() ? radius * 0.55 : 0;
|
|
17185
|
-
const outerRadius = radius * 0.9;
|
|
17186
|
-
this.arcGen = arc().innerRadius(innerRadius).outerRadius(outerRadius);
|
|
17187
|
-
const hoverArc = arc()
|
|
17188
|
-
.innerRadius(innerRadius)
|
|
17189
|
-
.outerRadius(outerRadius * 1.06);
|
|
17190
|
-
const pieGen = pie()
|
|
17191
|
-
.value(d => d.value)
|
|
17192
|
-
.sort(null);
|
|
17193
|
-
const arcs = pieGen(this.data().filter(d => d.value > 0));
|
|
17194
|
-
const g = this.svg
|
|
17195
|
-
.attr('width', width)
|
|
17196
|
-
.attr('height', height)
|
|
17197
|
-
.append('g')
|
|
17198
|
-
.attr('transform', `translate(${width / 2},${height / 2})`);
|
|
17199
|
-
// Slices
|
|
17200
|
-
g.selectAll('.slice')
|
|
17201
|
-
.data(arcs, (d) => d.data.key)
|
|
17202
|
-
.join('path')
|
|
17203
|
-
.attr('class', 'slice')
|
|
17204
|
-
.attr('d', this.arcGen)
|
|
17205
|
-
.style('fill', (d) => toChartColor(d.data.color))
|
|
17206
|
-
.attr('stroke', '#fff')
|
|
17207
|
-
.attr('stroke-width', 2)
|
|
17208
|
-
.style('cursor', 'pointer')
|
|
17209
|
-
.on('mouseover', (e) => {
|
|
17210
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17211
|
-
select(e.currentTarget).attr('d', hoverArc);
|
|
17212
|
-
})
|
|
17213
|
-
.on('mouseout', (e) => {
|
|
17214
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17215
|
-
select(e.currentTarget).attr('d', this.arcGen);
|
|
17216
|
-
})
|
|
17217
|
-
.on('click', (event, d) => {
|
|
17218
|
-
event.stopPropagation();
|
|
17219
|
-
this.openTooltip(d, width, height);
|
|
17220
|
-
});
|
|
17221
|
-
// Slice labels – only show when the slice angle is large enough to fit text
|
|
17222
|
-
const MIN_ANGLE = 0.4; // ~23°
|
|
17223
|
-
const labelRadius = this.donut() ? (innerRadius + outerRadius) / 2 : outerRadius * 0.65;
|
|
17224
|
-
const labelArc = arc().innerRadius(labelRadius).outerRadius(labelRadius);
|
|
17225
|
-
const total = this.total();
|
|
17226
|
-
const labelGroups = g
|
|
17227
|
-
.selectAll('.slice-label')
|
|
17228
|
-
.data(arcs.filter((d) => d.endAngle - d.startAngle >= MIN_ANGLE), (d) => d.data.key)
|
|
17229
|
-
.join('g')
|
|
17230
|
-
.attr('class', 'slice-label')
|
|
17231
|
-
.attr('transform', (d) => {
|
|
17232
|
-
const [x, y] = labelArc.centroid(d);
|
|
17233
|
-
return `translate(${x},${y})`;
|
|
17234
|
-
})
|
|
17235
|
-
.style('pointer-events', 'none');
|
|
17236
|
-
labelGroups
|
|
17237
|
-
.append('text')
|
|
17238
|
-
.attr('class', 'label-value')
|
|
17239
|
-
.attr('text-anchor', 'middle')
|
|
17240
|
-
.attr('dy', '-0.3em')
|
|
17241
|
-
.style('font-size', '11px')
|
|
17242
|
-
.style('font-weight', '600')
|
|
17243
|
-
.style('fill', '#fff')
|
|
17244
|
-
.style('pointer-events', 'none')
|
|
17245
|
-
.text((d) => format('~s')(d.data.value));
|
|
17246
|
-
labelGroups
|
|
17247
|
-
.append('text')
|
|
17248
|
-
.attr('class', 'label-percent')
|
|
17249
|
-
.attr('text-anchor', 'middle')
|
|
17250
|
-
.attr('dy', '0.9em')
|
|
17251
|
-
.style('font-size', '10px')
|
|
17252
|
-
.style('fill', 'rgba(255, 255, 255, 0.9)')
|
|
17253
|
-
.style('pointer-events', 'none')
|
|
17254
|
-
.text((d) => `${((100 * d.data.value) / total).toFixed(1)}%`);
|
|
17255
|
-
// Center label (donut only)
|
|
17256
|
-
if (this.donut()) {
|
|
17257
|
-
g.append('text')
|
|
17258
|
-
.attr('class', 'center-label')
|
|
17259
|
-
.attr('text-anchor', 'middle')
|
|
17260
|
-
.attr('dy', '-0.1em')
|
|
17261
|
-
.style('font-size', '1.4rem')
|
|
17262
|
-
.style('font-weight', '600')
|
|
17263
|
-
.style('fill', 'var(--clr-color-neutral-900, #21333b)')
|
|
17264
|
-
.text(format('~s')(this.total()));
|
|
17265
|
-
g.append('text')
|
|
17266
|
-
.attr('class', 'center-sublabel')
|
|
17267
|
-
.attr('text-anchor', 'middle')
|
|
17268
|
-
.attr('dy', '1.2em')
|
|
17269
|
-
.style('font-size', '0.65rem')
|
|
17270
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
17271
|
-
.style('text-transform', 'uppercase')
|
|
17272
|
-
.style('letter-spacing', '0.05em')
|
|
17273
|
-
.text('Total');
|
|
17274
|
-
}
|
|
17275
|
-
}
|
|
17276
|
-
openTooltip(d, width, height) {
|
|
17277
|
-
const [cx, cy] = this.arcGen.centroid(d);
|
|
17278
|
-
this.tooltipPosition.set({
|
|
17279
|
-
x: width / 2 + cx,
|
|
17280
|
-
y: height / 2 + cy,
|
|
17281
|
-
});
|
|
17282
|
-
this.selectedItem.set(d.data);
|
|
17283
|
-
}
|
|
17284
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: PieChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
17285
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: PieChartComponent, isStandalone: false, selector: "clr-pie-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, donut: { classPropertyName: "donut", publicName: "donut", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, exportFilename: { classPropertyName: "exportFilename", publicName: "exportFilename", isSignal: true, isRequired: false, transformFunction: null }, tooltipOrientation: { classPropertyName: "tooltipOrientation", publicName: "tooltipOrientation", isSignal: true, isRequired: false, transformFunction: null }, noItemsMessage: { classPropertyName: "noItemsMessage", publicName: "noItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, tooltipPercentOfTotal: { classPropertyName: "tooltipPercentOfTotal", publicName: "tooltipPercentOfTotal", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueClicked: "valueClicked" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n key: selectedItem().key,\n label: selectedItem().fullLabel || selectedItem().label,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().fullLabel || selectedItem().label }}\n </ng-container>\n\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.slice{transition:d .15s ease}.label-value{font-size:11px;font-weight:600;fill:#fff}.label-percent{font-size:10px;fill:#ffffffe6}.center-label{font-size:1.4rem;font-weight:600;fill:var(--clr-color-neutral-900, #21333b)}.center-sublabel{font-size:.65rem;fill:var(--clr-color-neutral-600, #666);text-transform:uppercase;letter-spacing:.05em}\n"], dependencies: [{ kind: "component", type: ChartAlertOverlayComponent, selector: "cng-chart-alert-overlay", inputs: ["alertMessage", "alertType"] }, { kind: "component", type: ChartExportButtonComponent, selector: "cng-chart-export-button", inputs: ["svgRef", "filename"] }, { kind: "component", type: ChartLegendComponent, selector: "cng-chart-legend", inputs: ["items"] }, { kind: "component", type: ChartSkeletonComponent, selector: "cng-bar-chart-skeleton", inputs: ["skeletonType", "orientation"] }, { kind: "component", type: ChartTooltipComponent, selector: "cng-chart-tooltip", inputs: ["tooltipPosition", "tooltipOrientation", "squareColor", "tooltipClickable"], outputs: ["tooltipClosed", "tooltipHeaderClicked"] }, { kind: "directive", type: OutsideClickDirective, selector: "[cngOutsideClick]", outputs: ["cngOutsideClick"] }, { kind: "directive", type: WindowResizeDirective, selector: "[cngWindowResize]", inputs: ["debounce", "includeFirst"], outputs: ["cngWindowResize"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
17286
|
-
}
|
|
17287
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: PieChartComponent, decorators: [{
|
|
17288
|
-
type: Component,
|
|
17289
|
-
args: [{ selector: 'clr-pie-chart', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"chart-container\">\n <div class=\"chart-area\" #container (cngWindowResize)=\"updateChart()\" [class.pt-3]=\"alertMessageAndType()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (tooltipPosition()) {\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n [tooltipOrientation]=\"tooltipOrientation()\"\n [squareColor]=\"selectedItem()?.color\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n valueClicked.emit({\n key: selectedItem().key,\n label: selectedItem().fullLabel || selectedItem().label,\n value: selectedItem().value,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n ({{ selectedItem().value }}) {{ selectedItem().fullLabel || selectedItem().label }}\n </ng-container>\n\n <p class=\"mt-0\">\n {{ (100 * selectedItem().value) / total() | number : '1.0-2' }}%\n {{ tooltipPercentOfTotal() }}\n </p>\n </cng-chart-tooltip>\n } @if (loading() || !hasData()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" [skeletonType]=\"loading() ? 'loading' : 'placeholder'\" />\n } @if (alertMessageAndType()) {\n <cng-chart-alert-overlay [alertType]=\"alertMessageAndType()[1]\" [alertMessage]=\"alertMessageAndType()[0]\" />\n }\n </div>\n\n @if (showLegend() && !loading() && legendItems().length) {\n <cng-chart-legend [items]=\"legendItems()\" />\n } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"chart-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.chart-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative}.chart-area{flex:1;min-height:0;position:relative}svg{height:100%;width:100%}cng-bar-chart-skeleton{height:100%}.slice{transition:d .15s ease}.label-value{font-size:11px;font-weight:600;fill:#fff}.label-percent{font-size:10px;fill:#ffffffe6}.center-label{font-size:1.4rem;font-weight:600;fill:var(--clr-color-neutral-900, #21333b)}.center-sublabel{font-size:.65rem;fill:var(--clr-color-neutral-600, #666);text-transform:uppercase;letter-spacing:.05em}\n"] }]
|
|
17290
|
-
}] });
|
|
17291
|
-
|
|
17292
|
-
/*
|
|
17293
|
-
* Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
|
|
17294
|
-
* This software is released under MIT license.
|
|
17295
|
-
* The full license information can be found in LICENSE in the root directory of this project.
|
|
17296
|
-
*/
|
|
17297
|
-
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
17298
|
-
const DEFAULT_SECTION_COLORS = ['#009ADB', '#66D1FF', '#D9D9D9', '#A6A6A6'];
|
|
17299
|
-
const DEFAULT_TEXT_COLORS = ['#eee', '#eee', '#555', '#eee'];
|
|
17300
|
-
const DEFAULT_CHART_LABELS = {
|
|
17301
|
-
total: 'Total',
|
|
17302
|
-
all: 'All',
|
|
17303
|
-
};
|
|
17304
|
-
class FunnelChartComponent extends ChartBase {
|
|
17305
|
-
constructor() {
|
|
17306
|
-
super(...arguments);
|
|
17307
|
-
// ── Inputs ──────────────────────────────────────────────────────────────────
|
|
17308
|
-
this.data = input([]);
|
|
17309
|
-
this.showExportButton = input(false);
|
|
17310
|
-
this.exportFilename = input('funnel-chart');
|
|
17311
|
-
/** Rendering mode. 'default' = horizontal bars with sections; 'centered' = centered trapezoid funnel. */
|
|
17312
|
-
this.orientation = input('default');
|
|
17313
|
-
/** Width reserved on each side for labels (px). */
|
|
17314
|
-
this.textSize = input(200);
|
|
17315
|
-
/** Gap between funnel bars and their side-line labels (px). */
|
|
17316
|
-
this.lineTextPadding = input(8);
|
|
17317
|
-
/** Vertical gap between funnel bars – default mode only (px). */
|
|
17318
|
-
this.barGap = input(2);
|
|
17319
|
-
/** Vertical padding inside the side measurement lines (px). */
|
|
17320
|
-
this.sideLineVerticalPadding = input(4);
|
|
17321
|
-
// ── Centered-mode specific inputs ────────────────────────────────────────────
|
|
17322
|
-
/** Minimum length of the horizontal measurement lines on each side (px). Centered mode only. */
|
|
17323
|
-
this.minLineSize = input(30);
|
|
17324
|
-
/** Ratio of bar height to spacer height. Centered mode only. */
|
|
17325
|
-
this.barToSpacerRatio = input(2);
|
|
17326
|
-
/** Vertical padding inside the center divider lines (px). Centered mode only. */
|
|
17327
|
-
this.middleLineVerticalPadding = input(4);
|
|
17328
|
-
/** Fill color of the centered-mode bars. Accepts hex or CSS custom property. Centered mode only. */
|
|
17329
|
-
this.barColor = input(DEFAULT_SECTION_COLORS[0]);
|
|
17330
|
-
/** Fill color of the trapezoid spacers. Accepts hex or CSS custom property. Centered mode only. */
|
|
17331
|
-
this.spacerColor = input('#D9D9D9');
|
|
17332
|
-
/**
|
|
17333
|
-
* Override the chart labels shown in the tooltip (total / all rows).
|
|
17334
|
-
* Partial – any key not provided falls back to the English default.
|
|
17335
|
-
*/
|
|
17336
|
-
this.chartLabels = input({});
|
|
17337
|
-
/**
|
|
17338
|
-
* Override section colors by section key.
|
|
17339
|
-
* Accepts hex values ('#009ADB') or CSS custom properties ('--cds-global-color-blue-800').
|
|
17340
|
-
* Per-section `color` in the data takes priority over this map; this map takes priority over
|
|
17341
|
-
* the built-in default palette.
|
|
17342
|
-
*/
|
|
17343
|
-
this.sectionColors = input({});
|
|
17344
|
-
// ── Outputs ─────────────────────────────────────────────────────────────────
|
|
17345
|
-
this.valueClicked = output();
|
|
17346
|
-
// ── Computed ─────────────────────────────────────────────────────────────────
|
|
17347
|
-
this.resolvedChartLabels = computed(() => ({
|
|
17348
|
-
...DEFAULT_CHART_LABELS,
|
|
17349
|
-
...this.chartLabels(),
|
|
17350
|
-
}));
|
|
17351
|
-
/** CSS-ready color for the centered-mode bar. */
|
|
17352
|
-
this.centeredBarCssColor = computed(() => toChartColor(this.barColor()));
|
|
17353
|
-
/** CSS-ready spacer color. */
|
|
17354
|
-
this.centeredSpacerCssColor = computed(() => toChartColor(this.spacerColor()));
|
|
17355
|
-
this.total = computed(() => this.data()[0]?.value ?? 0);
|
|
17356
|
-
this.hasData = computed(() => this.data().length > 0);
|
|
17357
|
-
// ── State ─────────────────────────────────────────────────────────────────────
|
|
17358
|
-
this.selectedItemLabel = computed(() => this.selectedItem()?.fullLabel ?? this.selectedItem()?.label ?? '');
|
|
17359
|
-
this.selectedSpacerItem = signal(undefined);
|
|
17360
|
-
this.sectionTooltips = signal([]);
|
|
17361
|
-
}
|
|
17362
|
-
// ── Lifecycle ────────────────────────────────────────────────────────────────
|
|
17363
|
-
ngOnChanges(_changes) {
|
|
17364
|
-
if (!this.svg) {
|
|
17365
|
-
return;
|
|
17366
|
-
}
|
|
17367
|
-
requestAnimationFrame(() => this.updateChart());
|
|
17368
|
-
}
|
|
17369
|
-
ngAfterViewInit() {
|
|
17370
|
-
this.svg = select(this.chartRef().nativeElement);
|
|
17371
|
-
super.ngAfterViewInit();
|
|
17372
|
-
}
|
|
17373
|
-
// ── Chart rendering ──────────────────────────────────────────────────────────
|
|
17374
|
-
updateChart() {
|
|
17375
|
-
if (!this.svg) {
|
|
17376
|
-
return;
|
|
17377
|
-
}
|
|
17378
|
-
this.svg.selectAll('*').remove();
|
|
17379
|
-
if (this.loading() || !this.hasData()) {
|
|
17380
|
-
return;
|
|
17381
|
-
}
|
|
17382
|
-
const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
|
|
17383
|
-
if (this.orientation() === 'centered') {
|
|
17384
|
-
this.renderCenteredChart(containerWidth, containerHeight);
|
|
17385
|
-
}
|
|
17386
|
-
else {
|
|
17387
|
-
const funnelWidth = this.getFunnelWidth(containerWidth);
|
|
17388
|
-
const barHeight = this.getBarHeight(containerHeight);
|
|
17389
|
-
const dataPoints = this.calculateDataPoints(funnelWidth, barHeight);
|
|
17390
|
-
this.renderRightSideText(dataPoints, containerWidth, barHeight);
|
|
17391
|
-
this.renderLeftSideText(dataPoints, barHeight);
|
|
17392
|
-
this.renderSideLines(dataPoints, containerWidth, funnelWidth, barHeight);
|
|
17393
|
-
this.renderFunnel(dataPoints, containerWidth, containerHeight, funnelWidth, barHeight);
|
|
17394
|
-
}
|
|
17395
|
-
}
|
|
17396
|
-
// ── Data calculation ─────────────────────────────────────────────────────────
|
|
17397
|
-
calculateDataPoints(funnelWidth, barHeight) {
|
|
17398
|
-
const maxValue = max(this.data(), d => d.value) ?? 1;
|
|
17399
|
-
const widthScale = scaleLinear().domain([0, maxValue]).range([0, funnelWidth]);
|
|
17400
|
-
const sectionColorOverrides = this.sectionColors();
|
|
17401
|
-
return this.data().map((d, i) => {
|
|
17402
|
-
let xOffset = 0;
|
|
17403
|
-
const sections = (d.sections ?? []).map((s, si) => {
|
|
17404
|
-
const rawColor = s.color ?? sectionColorOverrides[s.key] ?? DEFAULT_SECTION_COLORS[si % DEFAULT_SECTION_COLORS.length];
|
|
17405
|
-
const cssColor = toChartColor(rawColor);
|
|
17406
|
-
const hoverColor = rawColor.startsWith('--')
|
|
17407
|
-
? cssColor
|
|
17408
|
-
: color(rawColor)?.darker(0.5)?.formatHex() ?? cssColor;
|
|
17409
|
-
const textColor = s.textColor ?? DEFAULT_TEXT_COLORS[si % DEFAULT_TEXT_COLORS.length];
|
|
17410
|
-
const sWidth = widthScale(s.value);
|
|
17411
|
-
const sec = {
|
|
17412
|
-
key: s.key,
|
|
17413
|
-
label: s.label,
|
|
17414
|
-
x: xOffset,
|
|
17415
|
-
width: sWidth,
|
|
17416
|
-
value: s.value,
|
|
17417
|
-
percentage: d.value ? this.round((s.value / d.value) * 100) : 0,
|
|
17418
|
-
cssColor,
|
|
17419
|
-
hoverColor,
|
|
17420
|
-
textColor,
|
|
17421
|
-
};
|
|
17422
|
-
xOffset += sWidth;
|
|
17423
|
-
return sec;
|
|
17424
|
-
});
|
|
17425
|
-
const point = {
|
|
17426
|
-
...d,
|
|
17427
|
-
sections,
|
|
17428
|
-
y: i * (barHeight + this.barGap()),
|
|
17429
|
-
width: widthScale(d.value),
|
|
17430
|
-
percentage: this.total() ? this.round((d.value / this.total()) * 100) : 0,
|
|
17431
|
-
};
|
|
17432
|
-
if (i > 0) {
|
|
17433
|
-
const previousValue = this.data()[i - 1].value;
|
|
17434
|
-
point.delta = previousValue - d.value;
|
|
17435
|
-
point.deltaPercentage = previousValue === 0 ? 0 : this.round((point.delta / previousValue) * 100);
|
|
17436
|
-
}
|
|
17437
|
-
return point;
|
|
17438
|
-
});
|
|
17439
|
-
}
|
|
17440
|
-
// ── Rendering helpers ────────────────────────────────────────────────────────
|
|
17441
|
-
renderRightSideText(dataPoints, containerWidth, barHeight) {
|
|
17442
|
-
const rightG = this.svg.append('g').attr('transform', `translate(${containerWidth - this.textSize()},0)`);
|
|
17443
|
-
dataPoints.forEach((d, i) => {
|
|
17444
|
-
if (i === 0) {
|
|
17445
|
-
return;
|
|
17446
|
-
}
|
|
17447
|
-
const prev = dataPoints[i - 1];
|
|
17448
|
-
const labelY = (prev.y + barHeight + d.y) / 2 + 4;
|
|
17449
|
-
rightG
|
|
17450
|
-
.append('text')
|
|
17451
|
-
.attr('x', 0)
|
|
17452
|
-
.attr('y', labelY)
|
|
17453
|
-
.attr('text-anchor', 'start')
|
|
17454
|
-
.style('font-size', '12px')
|
|
17455
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
17456
|
-
.text(`-${this.round(d.deltaPercentage)}% (${d.delta})`);
|
|
17457
|
-
});
|
|
17458
|
-
}
|
|
17459
|
-
renderLeftSideText(dataPoints, barHeight) {
|
|
17460
|
-
const leftG = this.svg.append('g');
|
|
17461
|
-
const ICON_SPACE = 20;
|
|
17462
|
-
const positions = [];
|
|
17463
|
-
dataPoints.forEach(d => {
|
|
17464
|
-
const labelY = (d.y + barHeight + d.y) / 2 + 4;
|
|
17465
|
-
const iconSpacing = d.description ? ICON_SPACE : 0;
|
|
17466
|
-
const textLength = leftG
|
|
17467
|
-
.append('text')
|
|
17468
|
-
.attr('x', this.textSize() - iconSpacing)
|
|
17469
|
-
.attr('y', labelY - 8)
|
|
17470
|
-
.attr('text-anchor', 'end')
|
|
17471
|
-
.style('font-size', '13px')
|
|
17472
|
-
.style('font-weight', '600')
|
|
17473
|
-
.style('fill', 'var(--clr-color-neutral-900, #21333b)')
|
|
17474
|
-
.style('cursor', 'pointer')
|
|
17475
|
-
.text(d.label)
|
|
17476
|
-
.on('click', () => this.valueClicked.emit({ value: d.value, label: d.label, key: d.key }))
|
|
17477
|
-
.node()
|
|
17478
|
-
.getComputedTextLength();
|
|
17479
|
-
if (d.description) {
|
|
17480
|
-
positions.push({
|
|
17481
|
-
x: this.textSize() - iconSpacing + 5,
|
|
17482
|
-
y: labelY - 24,
|
|
17483
|
-
key: d.key,
|
|
17484
|
-
description: d.description,
|
|
17485
|
-
});
|
|
17486
|
-
}
|
|
17487
|
-
leftG
|
|
17488
|
-
.append('line')
|
|
17489
|
-
.attr('x1', this.textSize() - textLength - iconSpacing)
|
|
17490
|
-
.attr('y1', labelY - 5)
|
|
17491
|
-
.attr('x2', this.textSize() - iconSpacing)
|
|
17492
|
-
.attr('y2', labelY - 5)
|
|
17493
|
-
.attr('stroke', 'var(--clr-color-neutral-600, #666)')
|
|
17494
|
-
.attr('stroke-width', 2)
|
|
17495
|
-
.attr('stroke-dasharray', '2,2');
|
|
17496
|
-
leftG
|
|
17497
|
-
.append('text')
|
|
17498
|
-
.attr('x', this.textSize())
|
|
17499
|
-
.attr('y', labelY + 10)
|
|
17500
|
-
.attr('text-anchor', 'end')
|
|
17501
|
-
.style('font-size', '12px')
|
|
17502
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
17503
|
-
.text(`${this.round(d.percentage)}% (${d.value})`);
|
|
17504
|
-
});
|
|
17505
|
-
this.sectionTooltips.set(positions);
|
|
17506
|
-
}
|
|
17507
|
-
renderSideLines(dataPoints, containerWidth, funnelWidth, barHeight) {
|
|
17508
|
-
const lineG = this.svg.append('g').attr('transform', `translate(${(containerWidth - funnelWidth) / 2},0)`);
|
|
17509
|
-
dataPoints.forEach(d => {
|
|
17510
|
-
const midY = d.y + barHeight / 2;
|
|
17511
|
-
lineG
|
|
17512
|
-
.append('line')
|
|
17513
|
-
.attr('x1', 0)
|
|
17514
|
-
.attr('y1', midY)
|
|
17515
|
-
.attr('x2', funnelWidth)
|
|
17516
|
-
.attr('y2', midY)
|
|
17517
|
-
.attr('stroke', '#D9D9D9')
|
|
17518
|
-
.attr('stroke-width', 1);
|
|
17519
|
-
this.renderSideLine(lineG, funnelWidth, d.y, barHeight);
|
|
17520
|
-
if (d.value === 0) {
|
|
17521
|
-
this.renderSideLine(lineG, 0, d.y, barHeight);
|
|
17522
|
-
}
|
|
17523
|
-
});
|
|
17524
|
-
}
|
|
17525
|
-
renderSideLine(g, x, y, barHeight) {
|
|
17526
|
-
g.append('line')
|
|
17527
|
-
.attr('x1', x)
|
|
17528
|
-
.attr('y1', y + this.sideLineVerticalPadding())
|
|
17529
|
-
.attr('x2', x)
|
|
17530
|
-
.attr('y2', y + barHeight - this.sideLineVerticalPadding())
|
|
17531
|
-
.attr('stroke', '#D9D9D9')
|
|
17532
|
-
.attr('stroke-width', 1);
|
|
17533
|
-
}
|
|
17534
|
-
renderFunnel(dataPoints, containerWidth, containerHeight, funnelWidth, barHeight) {
|
|
17535
|
-
const funnelG = this.svg
|
|
17536
|
-
.attr('width', containerWidth)
|
|
17537
|
-
.attr('height', containerHeight)
|
|
17538
|
-
.append('g')
|
|
17539
|
-
.attr('transform', `translate(${(containerWidth - funnelWidth) / 2},0)`);
|
|
17540
|
-
// First pass: all rects (z-order: rects below texts)
|
|
17541
|
-
dataPoints.forEach(dp => {
|
|
17542
|
-
dp.sections.forEach(section => {
|
|
17543
|
-
funnelG
|
|
17544
|
-
.append('rect')
|
|
17545
|
-
.attr('x', section.x)
|
|
17546
|
-
.attr('y', dp.y)
|
|
17547
|
-
.attr('width', section.width)
|
|
17548
|
-
.attr('height', barHeight)
|
|
17549
|
-
.style('fill', section.cssColor)
|
|
17550
|
-
.style('cursor', 'pointer')
|
|
17551
|
-
.on('mouseover', e => {
|
|
17552
|
-
select(e.currentTarget).style('fill', section.hoverColor);
|
|
17553
|
-
})
|
|
17554
|
-
.on('mouseout', e => {
|
|
17555
|
-
select(e.currentTarget).style('fill', section.cssColor);
|
|
17556
|
-
})
|
|
17557
|
-
.on('click', (event) => {
|
|
17558
|
-
this.handleSectionClick(event, dp, section.key);
|
|
17559
|
-
});
|
|
17560
|
-
});
|
|
17561
|
-
});
|
|
17562
|
-
// Second pass: texts on top
|
|
17563
|
-
dataPoints.forEach(dp => {
|
|
17564
|
-
dp.sections.forEach(section => {
|
|
17565
|
-
if (section.width > 40) {
|
|
17566
|
-
funnelG
|
|
17567
|
-
.append('text')
|
|
17568
|
-
.attr('x', section.x + section.width / 2)
|
|
17569
|
-
.attr('y', dp.y + barHeight / 2)
|
|
17570
|
-
.attr('text-anchor', 'middle')
|
|
17571
|
-
.attr('dominant-baseline', 'middle')
|
|
17572
|
-
.style('font-size', '13px')
|
|
17573
|
-
.style('font-weight', '600')
|
|
17574
|
-
.style('pointer-events', 'none')
|
|
17575
|
-
.style('fill', section.textColor)
|
|
17576
|
-
.text(`${section.percentage}%`);
|
|
17577
|
-
}
|
|
17578
|
-
});
|
|
17579
|
-
});
|
|
17580
|
-
}
|
|
17581
|
-
// ── Event handlers ────────────────────────────────────────────────────────────
|
|
17582
|
-
handleSectionClick(event, d, _sectionKey) {
|
|
17583
|
-
event.stopPropagation();
|
|
17584
|
-
const target = event.currentTarget;
|
|
17585
|
-
const rect = target.getBoundingClientRect();
|
|
17586
|
-
const container = this.chartRef().nativeElement.getBoundingClientRect();
|
|
17587
|
-
this.tooltipPosition.set({
|
|
17588
|
-
x: rect.left - container.left + rect.width / 2,
|
|
17589
|
-
y: rect.bottom - container.top,
|
|
17590
|
-
});
|
|
17591
|
-
this.selectedItem.set(d);
|
|
17592
|
-
}
|
|
17593
|
-
resetTooltip() {
|
|
17594
|
-
if (this.selectedItem() || this.selectedSpacerItem() || this.tooltipPosition()) {
|
|
17595
|
-
this.selectedItem.set(undefined);
|
|
17596
|
-
this.selectedSpacerItem.set(undefined);
|
|
17597
|
-
this.tooltipPosition.set(undefined);
|
|
17598
|
-
}
|
|
17599
|
-
}
|
|
17600
|
-
/** Formats a percentage to 2 decimal places for template use. */
|
|
17601
|
-
pct(value, total) {
|
|
17602
|
-
return this.round(total ? (value / total) * 100 : 0);
|
|
17603
|
-
}
|
|
17604
|
-
// ── Centered chart ────────────────────────────────────────────────────────────
|
|
17605
|
-
renderCenteredChart(containerWidth, containerHeight) {
|
|
17606
|
-
const maxFunnelWidth = this.getMaxFunnelWidth(containerWidth);
|
|
17607
|
-
const barHeight = this.getCenteredBarHeight(containerHeight);
|
|
17608
|
-
const spacerHeight = this.getCenteredSpacerHeight(containerHeight);
|
|
17609
|
-
const dataPoints = this.calculateCenteredDataPoints(maxFunnelWidth, barHeight, spacerHeight);
|
|
17610
|
-
const spacerPoints = this.calculateSpacerDataPoints(dataPoints, maxFunnelWidth, barHeight);
|
|
17611
|
-
this.renderCenteredLeftSideText(dataPoints, barHeight);
|
|
17612
|
-
this.renderCenteredLeftSideLines(dataPoints, maxFunnelWidth, barHeight);
|
|
17613
|
-
this.renderCenteredRightSideText(dataPoints, containerWidth, barHeight);
|
|
17614
|
-
this.renderCenteredRightSideLines(dataPoints, containerWidth, maxFunnelWidth, barHeight);
|
|
17615
|
-
this.renderCenteredMiddleLines(dataPoints, spacerPoints, containerWidth, barHeight);
|
|
17616
|
-
this.renderCenteredFunnel(dataPoints, spacerPoints, containerWidth, containerHeight, maxFunnelWidth, barHeight, spacerHeight);
|
|
17617
|
-
}
|
|
17618
|
-
calculateCenteredDataPoints(maxFunnelWidth, barHeight, spacerHeight) {
|
|
17619
|
-
const total = this.data()[0]?.value ?? 1;
|
|
17620
|
-
const maxValue = max(this.data(), d => d.value) ?? 1;
|
|
17621
|
-
const scale = scaleLinear().domain([0, maxValue]).range([0, maxFunnelWidth]);
|
|
17622
|
-
return this.data().map((d, i) => {
|
|
17623
|
-
const point = {
|
|
17624
|
-
...d,
|
|
17625
|
-
y: i * (barHeight + spacerHeight),
|
|
17626
|
-
width: scale(d.value),
|
|
17627
|
-
percentage: total ? this.round((d.value / total) * 100) : 0,
|
|
17628
|
-
};
|
|
17629
|
-
if (i > 0) {
|
|
17630
|
-
const prev = this.data()[i - 1].value;
|
|
17631
|
-
point.delta = prev - d.value;
|
|
17632
|
-
point.deltaPercentage = prev === 0 ? 0 : this.round((point.delta / prev) * 100);
|
|
17633
|
-
}
|
|
17634
|
-
return point;
|
|
17635
|
-
});
|
|
17636
|
-
}
|
|
17637
|
-
calculateSpacerDataPoints(dataPoints, maxFunnelWidth, barHeight) {
|
|
17638
|
-
const spacers = [];
|
|
17639
|
-
for (let i = 0; i < dataPoints.length - 1; i++) {
|
|
17640
|
-
const cur = dataPoints[i];
|
|
17641
|
-
const next = dataPoints[i + 1];
|
|
17642
|
-
spacers.push({
|
|
17643
|
-
index: i,
|
|
17644
|
-
topWidth: cur.width,
|
|
17645
|
-
bottomWidth: next.width,
|
|
17646
|
-
topY: cur.y + barHeight,
|
|
17647
|
-
bottomY: next.y,
|
|
17648
|
-
topX: (maxFunnelWidth - cur.width) / 2,
|
|
17649
|
-
bottomX: (maxFunnelWidth - next.width) / 2,
|
|
17650
|
-
deltaPercentage: next.deltaPercentage ?? 0,
|
|
17651
|
-
});
|
|
17652
|
-
}
|
|
17653
|
-
return spacers;
|
|
17654
|
-
}
|
|
17655
|
-
renderCenteredLeftSideText(dataPoints, barHeight) {
|
|
17656
|
-
const g = this.svg.append('g').attr('transform', `translate(${this.textSize()},0)`);
|
|
17657
|
-
const ICON_SPACE = 20;
|
|
17658
|
-
const positions = [];
|
|
17659
|
-
dataPoints.forEach(d => {
|
|
17660
|
-
const cy = d.y + barHeight / 2;
|
|
17661
|
-
const iconSpacing = d.description ? ICON_SPACE : 0;
|
|
17662
|
-
const textLen = g
|
|
17663
|
-
.append('text')
|
|
17664
|
-
.attr('x', -iconSpacing)
|
|
17665
|
-
.attr('y', cy - 8)
|
|
17666
|
-
.attr('text-anchor', 'end')
|
|
17667
|
-
.style('font-size', '13px')
|
|
17668
|
-
.style('font-weight', '600')
|
|
17669
|
-
.style('fill', 'var(--clr-color-neutral-900, #21333b)')
|
|
17670
|
-
.style('cursor', 'pointer')
|
|
17671
|
-
.text(d.label)
|
|
17672
|
-
.on('click', () => this.valueClicked.emit({ value: d.value, label: d.label, key: d.key }))
|
|
17673
|
-
.node()
|
|
17674
|
-
.getComputedTextLength();
|
|
17675
|
-
if (d.description) {
|
|
17676
|
-
positions.push({ key: d.key, x: this.textSize() - iconSpacing + 5, y: cy - 24, description: d.description });
|
|
17677
|
-
}
|
|
17678
|
-
g.append('line')
|
|
17679
|
-
.attr('x1', -textLen - iconSpacing)
|
|
17680
|
-
.attr('y1', cy - 5)
|
|
17681
|
-
.attr('x2', -iconSpacing)
|
|
17682
|
-
.attr('y2', cy - 5)
|
|
17683
|
-
.attr('stroke', 'var(--clr-color-neutral-600, #666)')
|
|
17684
|
-
.attr('stroke-width', 2)
|
|
17685
|
-
.attr('stroke-dasharray', '2,2');
|
|
17686
|
-
g.append('text')
|
|
17687
|
-
.attr('x', 0)
|
|
17688
|
-
.attr('y', cy + 10)
|
|
17689
|
-
.attr('text-anchor', 'end')
|
|
17690
|
-
.style('font-size', '12px')
|
|
17691
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
17692
|
-
.text(`${this.round(d.percentage)}% (${d.value})`);
|
|
17693
|
-
});
|
|
17694
|
-
this.sectionTooltips.set(positions);
|
|
17695
|
-
}
|
|
17696
|
-
renderCenteredLeftSideLines(dataPoints, maxFunnelWidth, barHeight) {
|
|
17697
|
-
const g = this.svg.append('g').attr('transform', `translate(${this.textSize() + this.lineTextPadding()},0)`);
|
|
17698
|
-
dataPoints.forEach(d => {
|
|
17699
|
-
g.append('line')
|
|
17700
|
-
.attr('x1', 0)
|
|
17701
|
-
.attr('y1', d.y + barHeight / 2)
|
|
17702
|
-
.attr('x2', maxFunnelWidth / 2 + this.minLineSize())
|
|
17703
|
-
.attr('y2', d.y + barHeight / 2)
|
|
17704
|
-
.attr('stroke', '#D9D9D9')
|
|
17705
|
-
.attr('stroke-width', 1);
|
|
17706
|
-
});
|
|
17707
|
-
}
|
|
17708
|
-
renderCenteredRightSideText(dataPoints, containerWidth, barHeight) {
|
|
17709
|
-
const g = this.svg.append('g').attr('transform', `translate(${containerWidth - this.textSize()},0)`);
|
|
17710
|
-
dataPoints.forEach((d, i) => {
|
|
17711
|
-
if (i === 0) {
|
|
17712
|
-
return;
|
|
17713
|
-
}
|
|
17714
|
-
const prev = dataPoints[i - 1];
|
|
17715
|
-
const cy = (prev.y + barHeight + d.y) / 2 + 4;
|
|
17716
|
-
g.append('text')
|
|
17717
|
-
.attr('x', 0)
|
|
17718
|
-
.attr('y', cy)
|
|
17719
|
-
.attr('text-anchor', 'start')
|
|
17720
|
-
.style('font-size', '12px')
|
|
17721
|
-
.style('fill', 'var(--clr-color-neutral-600, #666)')
|
|
17722
|
-
.text(`-${this.round(d.deltaPercentage)}% (${d.delta})`);
|
|
17723
|
-
});
|
|
17724
|
-
}
|
|
17725
|
-
renderCenteredRightSideLines(dataPoints, containerWidth, maxFunnelWidth, barHeight) {
|
|
17726
|
-
const g = this.svg.append('g').attr('transform', `translate(${containerWidth / 2},0)`);
|
|
17727
|
-
dataPoints.forEach((d, i) => {
|
|
17728
|
-
if (i === 0) {
|
|
17729
|
-
return;
|
|
17730
|
-
}
|
|
17731
|
-
const prev = dataPoints[i - 1];
|
|
17732
|
-
const cy = (prev.y + barHeight + d.y) / 2;
|
|
17733
|
-
g.append('line')
|
|
17734
|
-
.attr('x1', 0)
|
|
17735
|
-
.attr('y1', cy)
|
|
17736
|
-
.attr('x2', maxFunnelWidth / 2 + this.minLineSize())
|
|
17737
|
-
.attr('y2', cy)
|
|
17738
|
-
.attr('stroke', '#D9D9D9')
|
|
17739
|
-
.attr('stroke-width', 1);
|
|
17740
|
-
});
|
|
17741
|
-
}
|
|
17742
|
-
renderCenteredMiddleLines(dataPoints, spacerPoints, containerWidth, barHeight) {
|
|
17743
|
-
const vPad = this.middleLineVerticalPadding();
|
|
17744
|
-
const g = this.svg.append('g').attr('transform', `translate(${containerWidth / 2},0)`);
|
|
17745
|
-
g.selectAll('.bar-mid')
|
|
17746
|
-
.data(dataPoints)
|
|
17747
|
-
.enter()
|
|
17748
|
-
.append('line')
|
|
17749
|
-
.attr('x1', 0)
|
|
17750
|
-
.attr('y1', d => d.y + vPad)
|
|
17751
|
-
.attr('x2', 0)
|
|
17752
|
-
.attr('y2', d => d.y + barHeight - vPad)
|
|
17753
|
-
.attr('stroke', '#D9D9D9')
|
|
17754
|
-
.attr('stroke-width', 1);
|
|
17755
|
-
g.selectAll('.spacer-mid')
|
|
17756
|
-
.data(spacerPoints)
|
|
17757
|
-
.enter()
|
|
17758
|
-
.append('line')
|
|
17759
|
-
.attr('x1', 0)
|
|
17760
|
-
.attr('y1', d => d.topY + vPad)
|
|
17761
|
-
.attr('x2', 0)
|
|
17762
|
-
.attr('y2', d => d.bottomY - vPad)
|
|
17763
|
-
.attr('stroke', '#D9D9D9')
|
|
17764
|
-
.attr('stroke-width', 1);
|
|
17765
|
-
}
|
|
17766
|
-
renderCenteredFunnel(dataPoints, spacerPoints, containerWidth, containerHeight, maxFunnelWidth, barHeight, spacerHeight) {
|
|
17767
|
-
const g = this.svg
|
|
17768
|
-
.attr('width', containerWidth)
|
|
17769
|
-
.attr('height', containerHeight)
|
|
17770
|
-
.append('g')
|
|
17771
|
-
.attr('transform', `translate(${(containerWidth - maxFunnelWidth) / 2},0)`);
|
|
17772
|
-
// ── Bars ──────────────────────────────────────────────────────────────────
|
|
17773
|
-
const rawBarColor = this.barColor();
|
|
17774
|
-
const cssBarColor = toChartColor(rawBarColor);
|
|
17775
|
-
const hoverBarColor = rawBarColor.startsWith('--')
|
|
17776
|
-
? cssBarColor
|
|
17777
|
-
: color(rawBarColor)?.darker(0.5)?.formatHex() ?? cssBarColor;
|
|
17778
|
-
const bars = g
|
|
17779
|
-
.selectAll('.c-bar')
|
|
17780
|
-
.data(dataPoints)
|
|
17781
|
-
.enter()
|
|
17782
|
-
.append('g')
|
|
17783
|
-
.attr('class', 'c-bar');
|
|
17784
|
-
bars
|
|
17785
|
-
.append('rect')
|
|
17786
|
-
.attr('x', d => (maxFunnelWidth - d.width) / 2)
|
|
17787
|
-
.attr('y', d => d.y)
|
|
17788
|
-
.attr('width', d => d.width)
|
|
17789
|
-
.attr('height', barHeight)
|
|
17790
|
-
.style('fill', cssBarColor)
|
|
17791
|
-
.style('cursor', 'pointer')
|
|
17792
|
-
.on('mouseover', e => {
|
|
17793
|
-
select(e.currentTarget).style('fill', hoverBarColor);
|
|
17794
|
-
})
|
|
17795
|
-
.on('mouseout', e => {
|
|
17796
|
-
select(e.currentTarget).style('fill', cssBarColor);
|
|
17797
|
-
})
|
|
17798
|
-
.on('click', (event, d) => {
|
|
17799
|
-
event.stopPropagation();
|
|
17800
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
17801
|
-
const cont = this.chartRef().nativeElement.getBoundingClientRect();
|
|
17802
|
-
this.tooltipPosition.set({ x: rect.left - cont.left + rect.width / 2, y: rect.top - cont.top });
|
|
17803
|
-
this.selectedSpacerItem.set(undefined);
|
|
17804
|
-
this.selectedItem.set(d);
|
|
17805
|
-
});
|
|
17806
|
-
bars
|
|
17807
|
-
.selectAll('.c-bar-label')
|
|
17808
|
-
.data(dataPoints.filter(d => d.width > 40))
|
|
17809
|
-
.enter()
|
|
17810
|
-
.append('text')
|
|
17811
|
-
.attr('x', maxFunnelWidth / 2)
|
|
17812
|
-
.attr('y', d => d.y + barHeight / 2)
|
|
17813
|
-
.attr('text-anchor', 'middle')
|
|
17814
|
-
.attr('dominant-baseline', 'middle')
|
|
17815
|
-
.style('font-size', '13px')
|
|
17816
|
-
.style('font-weight', '600')
|
|
17817
|
-
.style('pointer-events', 'none')
|
|
17818
|
-
.style('fill', '#eee')
|
|
17819
|
-
.text(d => `${this.round(d.percentage)}%`);
|
|
17820
|
-
// ── Spacers ───────────────────────────────────────────────────────────────
|
|
17821
|
-
const rawSpacerColor = this.spacerColor();
|
|
17822
|
-
const cssSpacerColor = toChartColor(rawSpacerColor);
|
|
17823
|
-
const hoverSpacerColor = rawSpacerColor.startsWith('--')
|
|
17824
|
-
? cssSpacerColor
|
|
17825
|
-
: color(rawSpacerColor)?.darker(0.2)?.formatHex() ?? cssSpacerColor;
|
|
17826
|
-
g.selectAll('.c-spacer')
|
|
17827
|
-
.data(spacerPoints)
|
|
17828
|
-
.enter()
|
|
17829
|
-
.append('polygon')
|
|
17830
|
-
.attr('class', 'c-spacer')
|
|
17831
|
-
.attr('points', d => `${d.topX},${d.topY} ${d.topX + d.topWidth},${d.topY} ${d.bottomX + d.bottomWidth},${d.bottomY} ${d.bottomX},${d.bottomY}`)
|
|
17832
|
-
.style('fill', cssSpacerColor)
|
|
17833
|
-
.style('cursor', 'pointer')
|
|
17834
|
-
.on('mouseover', e => {
|
|
17835
|
-
select(e.currentTarget).style('fill', hoverSpacerColor);
|
|
17836
|
-
})
|
|
17837
|
-
.on('mouseout', e => {
|
|
17838
|
-
select(e.currentTarget).style('fill', cssSpacerColor);
|
|
17839
|
-
})
|
|
17840
|
-
.on('click', (event, d) => {
|
|
17841
|
-
event.stopPropagation();
|
|
17842
|
-
const rect = event.currentTarget.getBoundingClientRect();
|
|
17843
|
-
const cont = this.chartRef().nativeElement.getBoundingClientRect();
|
|
17844
|
-
this.tooltipPosition.set({ x: rect.left - cont.left + rect.width / 2, y: rect.top - cont.top });
|
|
17845
|
-
this.selectedItem.set(undefined);
|
|
17846
|
-
this.selectedSpacerItem.set(d);
|
|
17847
|
-
});
|
|
17848
|
-
g.selectAll('.c-spacer-label')
|
|
17849
|
-
.data(spacerPoints.filter(d => d.bottomWidth > 40))
|
|
17850
|
-
.enter()
|
|
17851
|
-
.append('text')
|
|
17852
|
-
.attr('x', maxFunnelWidth / 2)
|
|
17853
|
-
.attr('y', d => d.topY + spacerHeight / 2)
|
|
17854
|
-
.attr('text-anchor', 'middle')
|
|
17855
|
-
.attr('dominant-baseline', 'middle')
|
|
17856
|
-
.style('font-size', '13px')
|
|
17857
|
-
.style('font-weight', '600')
|
|
17858
|
-
.style('pointer-events', 'none')
|
|
17859
|
-
.style('fill', '#333')
|
|
17860
|
-
.text(d => `-${this.round(d.deltaPercentage)}%`);
|
|
17861
|
-
}
|
|
17862
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
17863
|
-
getMaxFunnelWidth(containerWidth) {
|
|
17864
|
-
return containerWidth - this.textSize() * 2 - this.minLineSize() * 2 - this.lineTextPadding() * 2;
|
|
17865
|
-
}
|
|
17866
|
-
getCenteredFractionHeight(containerHeight) {
|
|
17867
|
-
return containerHeight / (this.data().length * (this.barToSpacerRatio() + 1) - 1);
|
|
17868
|
-
}
|
|
17869
|
-
getCenteredBarHeight(containerHeight) {
|
|
17870
|
-
return this.getCenteredFractionHeight(containerHeight) * this.barToSpacerRatio();
|
|
17871
|
-
}
|
|
17872
|
-
getCenteredSpacerHeight(containerHeight) {
|
|
17873
|
-
return this.getCenteredFractionHeight(containerHeight);
|
|
17874
|
-
}
|
|
17875
|
-
getFunnelWidth(containerWidth) {
|
|
17876
|
-
return containerWidth - this.textSize() * 2 - this.lineTextPadding() * 2;
|
|
17877
|
-
}
|
|
17878
|
-
getBarHeight(containerHeight) {
|
|
17879
|
-
const n = this.data().length;
|
|
17880
|
-
if (n === 0) {
|
|
17881
|
-
return 0;
|
|
17882
|
-
}
|
|
17883
|
-
return (containerHeight - (n - 1) * this.barGap()) / n;
|
|
17884
|
-
}
|
|
17885
|
-
round(value, decimals = 2) {
|
|
17886
|
-
const factor = 10 ** decimals;
|
|
17887
|
-
return Math.round(factor * value) / factor;
|
|
17888
|
-
}
|
|
17889
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: FunnelChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
17890
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: FunnelChartComponent, isStandalone: false, selector: "clr-funnel-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, showExportButton: { classPropertyName: "showExportButton", publicName: "showExportButton", isSignal: true, isRequired: false, transformFunction: null }, exportFilename: { classPropertyName: "exportFilename", publicName: "exportFilename", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, textSize: { classPropertyName: "textSize", publicName: "textSize", isSignal: true, isRequired: false, transformFunction: null }, lineTextPadding: { classPropertyName: "lineTextPadding", publicName: "lineTextPadding", isSignal: true, isRequired: false, transformFunction: null }, barGap: { classPropertyName: "barGap", publicName: "barGap", isSignal: true, isRequired: false, transformFunction: null }, sideLineVerticalPadding: { classPropertyName: "sideLineVerticalPadding", publicName: "sideLineVerticalPadding", isSignal: true, isRequired: false, transformFunction: null }, minLineSize: { classPropertyName: "minLineSize", publicName: "minLineSize", isSignal: true, isRequired: false, transformFunction: null }, barToSpacerRatio: { classPropertyName: "barToSpacerRatio", publicName: "barToSpacerRatio", isSignal: true, isRequired: false, transformFunction: null }, middleLineVerticalPadding: { classPropertyName: "middleLineVerticalPadding", publicName: "middleLineVerticalPadding", isSignal: true, isRequired: false, transformFunction: null }, barColor: { classPropertyName: "barColor", publicName: "barColor", isSignal: true, isRequired: false, transformFunction: null }, spacerColor: { classPropertyName: "spacerColor", publicName: "spacerColor", isSignal: true, isRequired: false, transformFunction: null }, chartLabels: { classPropertyName: "chartLabels", publicName: "chartLabels", isSignal: true, isRequired: false, transformFunction: null }, sectionColors: { classPropertyName: "sectionColors", publicName: "sectionColors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueClicked: "valueClicked" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"funnel-container\" #container (cngWindowResize)=\"updateChart()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (!loading()) { @for (pos of sectionTooltips(); track pos.key) {\n <clr-signpost [style.left.px]=\"pos.x\" [style.top.px]=\"pos.y\">\n <cds-icon clrSignpostTrigger shape=\"info-circle\" size=\"18\" />\n <clr-signpost-content clrPosition=\"bottom-left\">\n <p class=\"mt-0\">{{ pos.description }}</p>\n </clr-signpost-content>\n </clr-signpost>\n } } @if (loading()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" skeletonType=\"loading\" />\n } @if (tooltipPosition()) { @if (orientation() === 'centered') {\n\n <!-- \u2500\u2500 Centered mode tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n tooltipOrientation=\"bottom\"\n [squareColor]=\"selectedSpacerItem() ? centeredSpacerCssColor() : centeredBarCssColor()\"\n [tooltipClickable]=\"!selectedSpacerItem()\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n selectedItem() && valueClicked.emit({\n value: selectedItem().value,\n label: selectedItem().label,\n key: selectedItem().key,\n section: undefined,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n @if (selectedSpacerItem()) {\n {{ data()[selectedSpacerItem().index].label }} \u2192 {{ data()[selectedSpacerItem().index + 1].label }} } @else { ({{\n selectedItem().value\n }}) {{ selectedItem().label }}\n }\n </ng-container>\n\n @if (selectedSpacerItem()) {\n <p class=\"mt-0\">\n <strong\n >{{ data()[selectedSpacerItem().index].value }} \u2192 {{ data()[selectedSpacerItem().index + 1].value }}</strong\n >\n </p>\n @for (section of (data()[selectedSpacerItem().index].sections ?? []); track section.key) { @let drillable =\n section.value > 0;\n <p class=\"tooltip-row mt-0 mt-0-5 d-flex\">\n <strong>{{ section.label }}: </strong>\n <span\n class=\"row-right\"\n [class.has-more-info]=\"drillable\"\n (click)=\"\n drillable && valueClicked.emit({\n value: section.value,\n label: data()[selectedSpacerItem().index].label + ': ' + section.label,\n key: data()[selectedSpacerItem().index].key,\n section: section.key,\n })\n \"\n >{{ section.value }}</span\n >\n ({{ pct(section.value, data()[selectedSpacerItem().index].value) }}%)\n </p>\n } } @else {\n <p class=\"mt-0\">{{ pct(selectedItem().value, total()) }}% {{ resolvedChartLabels().all }}</p>\n }\n </cng-chart-tooltip>\n\n } @else {\n\n <!-- \u2500\u2500 Default mode tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n tooltipOrientation=\"bottom\"\n [squareColor]=\"undefined\"\n [tooltipClickable]=\"false\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n >\n <ng-container ngProjectAs=\"cng-title\">{{ selectedItemLabel() }}</ng-container>\n @for (section of selectedItem().sections; track section.key) { @let drillable = section.value > 0;\n <p class=\"tooltip-row mt-0 mt-0-5 d-flex\">\n <span>\n <div class=\"color-square mr-0-5\" [style.background-color]=\"section.cssColor\"></div>\n </span>\n <strong>{{ section.label }}: </strong>\n <span\n class=\"row-right\"\n [class.has-more-info]=\"drillable\"\n (click)=\"\n drillable && valueClicked.emit({\n value: section.value,\n label: selectedItemLabel() + ': ' + section.label,\n key: selectedItem().key,\n section: section.key,\n })\n \"\n >{{ section.value }}</span\n >\n ({{ section.percentage }}%)\n </p>\n }\n\n <p class=\"tooltip-row mt-0-5\">\n <strong>{{ resolvedChartLabels().total }}: </strong>\n <span\n class=\"has-more-info\"\n (click)=\"\n valueClicked.emit({\n value: selectedItem().value,\n label: selectedItemLabel(),\n key: selectedItem().key,\n section: undefined\n })\n \"\n >{{ selectedItem().value }}</span\n >\n </p>\n\n <ng-container ngProjectAs=\"cng-footer\">\n <p class=\"tooltip-row mt-0\">\n <strong>{{ resolvedChartLabels().all }}: </strong>\n <span class=\"row-right\">{{ total() }}</span>\n </p>\n </ng-container>\n </cng-chart-tooltip>\n\n } } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"funnel-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.funnel-container{height:100%;width:100%;max-width:1400px;margin:auto;position:relative}svg{width:100%;height:100%}clr-signpost{position:absolute!important}.tooltip-row{display:flex;justify-content:space-between;align-items:center}.row-right{margin-left:auto}.funnel-export-btn{position:absolute;bottom:0;right:0;opacity:0;pointer-events:none;transition:opacity .2s ease}.funnel-container:hover .funnel-export-btn{opacity:1;pointer-events:auto}\n"], dependencies: [{ kind: "directive", type: i1$1.CdsIconCustomTag, selector: "cds-icon" }, { kind: "component", type: i1$1.ClrSignpost, selector: "clr-signpost", inputs: ["clrSignpostTriggerAriaLabel"] }, { kind: "component", type: i1$1.ClrSignpostContent, selector: "clr-signpost-content", inputs: ["clrSignpostCloseAriaLabel", "clrPosition"] }, { kind: "directive", type: i1$1.ClrSignpostTrigger, selector: "[clrSignpostTrigger]" }, { kind: "component", type: ChartExportButtonComponent, selector: "cng-chart-export-button", inputs: ["svgRef", "filename"] }, { kind: "component", type: ChartSkeletonComponent, selector: "cng-bar-chart-skeleton", inputs: ["skeletonType", "orientation"] }, { kind: "component", type: ChartTooltipComponent, selector: "cng-chart-tooltip", inputs: ["tooltipPosition", "tooltipOrientation", "squareColor", "tooltipClickable"], outputs: ["tooltipClosed", "tooltipHeaderClicked"] }, { kind: "directive", type: OutsideClickDirective, selector: "[cngOutsideClick]", outputs: ["cngOutsideClick"] }, { kind: "directive", type: WindowResizeDirective, selector: "[cngWindowResize]", inputs: ["debounce", "includeFirst"], outputs: ["cngWindowResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
17891
|
-
}
|
|
17892
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: FunnelChartComponent, decorators: [{
|
|
17893
|
-
type: Component,
|
|
17894
|
-
args: [{ selector: 'clr-funnel-chart', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<div class=\"funnel-container\" #container (cngWindowResize)=\"updateChart()\">\n <svg [class.d-none]=\"loading() || !hasData()\" #chart></svg>\n\n @if (!loading()) { @for (pos of sectionTooltips(); track pos.key) {\n <clr-signpost [style.left.px]=\"pos.x\" [style.top.px]=\"pos.y\">\n <cds-icon clrSignpostTrigger shape=\"info-circle\" size=\"18\" />\n <clr-signpost-content clrPosition=\"bottom-left\">\n <p class=\"mt-0\">{{ pos.description }}</p>\n </clr-signpost-content>\n </clr-signpost>\n } } @if (loading()) {\n <cng-bar-chart-skeleton orientation=\"vertical\" skeletonType=\"loading\" />\n } @if (tooltipPosition()) { @if (orientation() === 'centered') {\n\n <!-- \u2500\u2500 Centered mode tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n tooltipOrientation=\"bottom\"\n [squareColor]=\"selectedSpacerItem() ? centeredSpacerCssColor() : centeredBarCssColor()\"\n [tooltipClickable]=\"!selectedSpacerItem()\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n (tooltipHeaderClicked)=\"\n selectedItem() && valueClicked.emit({\n value: selectedItem().value,\n label: selectedItem().label,\n key: selectedItem().key,\n section: undefined,\n })\n \"\n >\n <ng-container ngProjectAs=\"cng-title\">\n @if (selectedSpacerItem()) {\n {{ data()[selectedSpacerItem().index].label }} \u2192 {{ data()[selectedSpacerItem().index + 1].label }} } @else { ({{\n selectedItem().value\n }}) {{ selectedItem().label }}\n }\n </ng-container>\n\n @if (selectedSpacerItem()) {\n <p class=\"mt-0\">\n <strong\n >{{ data()[selectedSpacerItem().index].value }} \u2192 {{ data()[selectedSpacerItem().index + 1].value }}</strong\n >\n </p>\n @for (section of (data()[selectedSpacerItem().index].sections ?? []); track section.key) { @let drillable =\n section.value > 0;\n <p class=\"tooltip-row mt-0 mt-0-5 d-flex\">\n <strong>{{ section.label }}: </strong>\n <span\n class=\"row-right\"\n [class.has-more-info]=\"drillable\"\n (click)=\"\n drillable && valueClicked.emit({\n value: section.value,\n label: data()[selectedSpacerItem().index].label + ': ' + section.label,\n key: data()[selectedSpacerItem().index].key,\n section: section.key,\n })\n \"\n >{{ section.value }}</span\n >\n ({{ pct(section.value, data()[selectedSpacerItem().index].value) }}%)\n </p>\n } } @else {\n <p class=\"mt-0\">{{ pct(selectedItem().value, total()) }}% {{ resolvedChartLabels().all }}</p>\n }\n </cng-chart-tooltip>\n\n } @else {\n\n <!-- \u2500\u2500 Default mode tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <cng-chart-tooltip\n [tooltipPosition]=\"tooltipPosition()\"\n tooltipOrientation=\"bottom\"\n [squareColor]=\"undefined\"\n [tooltipClickable]=\"false\"\n (tooltipClosed)=\"resetTooltip()\"\n (cngOutsideClick)=\"resetTooltip()\"\n >\n <ng-container ngProjectAs=\"cng-title\">{{ selectedItemLabel() }}</ng-container>\n @for (section of selectedItem().sections; track section.key) { @let drillable = section.value > 0;\n <p class=\"tooltip-row mt-0 mt-0-5 d-flex\">\n <span>\n <div class=\"color-square mr-0-5\" [style.background-color]=\"section.cssColor\"></div>\n </span>\n <strong>{{ section.label }}: </strong>\n <span\n class=\"row-right\"\n [class.has-more-info]=\"drillable\"\n (click)=\"\n drillable && valueClicked.emit({\n value: section.value,\n label: selectedItemLabel() + ': ' + section.label,\n key: selectedItem().key,\n section: section.key,\n })\n \"\n >{{ section.value }}</span\n >\n ({{ section.percentage }}%)\n </p>\n }\n\n <p class=\"tooltip-row mt-0-5\">\n <strong>{{ resolvedChartLabels().total }}: </strong>\n <span\n class=\"has-more-info\"\n (click)=\"\n valueClicked.emit({\n value: selectedItem().value,\n label: selectedItemLabel(),\n key: selectedItem().key,\n section: undefined\n })\n \"\n >{{ selectedItem().value }}</span\n >\n </p>\n\n <ng-container ngProjectAs=\"cng-footer\">\n <p class=\"tooltip-row mt-0\">\n <strong>{{ resolvedChartLabels().all }}: </strong>\n <span class=\"row-right\">{{ total() }}</span>\n </p>\n </ng-container>\n </cng-chart-tooltip>\n\n } } @if (showExportButton() && !loading() && hasData()) {\n <cng-chart-export-button class=\"funnel-export-btn\" [svgRef]=\"svgElement()\" [filename]=\"exportFilename()\" />\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.funnel-container{height:100%;width:100%;max-width:1400px;margin:auto;position:relative}svg{width:100%;height:100%}clr-signpost{position:absolute!important}.tooltip-row{display:flex;justify-content:space-between;align-items:center}.row-right{margin-left:auto}.funnel-export-btn{position:absolute;bottom:0;right:0;opacity:0;pointer-events:none;transition:opacity .2s ease}.funnel-container:hover .funnel-export-btn{opacity:1;pointer-events:auto}\n"] }]
|
|
17895
|
-
}] });
|
|
17896
|
-
|
|
17897
|
-
const PREFERRED_ORDER = ['top-right', 'top-left', 'bottom-right', 'bottom-left'];
|
|
17898
|
-
class AutoPositionDirective {
|
|
17899
|
-
constructor() {
|
|
17900
|
-
this.signpostContent = inject(ClrSignpostContent, { optional: true });
|
|
17901
|
-
this.elementRef = inject(ElementRef);
|
|
17902
|
-
this.handleMouseDown = () => {
|
|
17903
|
-
// Only calculate when signpost is closed (about to open)
|
|
17904
|
-
if (this.elementRef.nativeElement.classList.contains('is-off-screen')) {
|
|
17905
|
-
this.updatePosition();
|
|
17906
|
-
}
|
|
17907
|
-
};
|
|
17908
|
-
}
|
|
17909
|
-
ngOnInit() {
|
|
17910
|
-
const signpost = this.elementRef.nativeElement.closest('clr-signpost');
|
|
17911
|
-
this.trigger = signpost?.querySelector('[clrSignpostTrigger]');
|
|
17912
|
-
if (this.trigger) {
|
|
17913
|
-
this.trigger.addEventListener('mousedown', this.handleMouseDown);
|
|
17914
|
-
}
|
|
17915
|
-
this.resizeObserver = new ResizeObserver(() => {
|
|
17916
|
-
if (!this.elementRef.nativeElement.classList.contains('is-off-screen')) {
|
|
17917
|
-
this.updatePosition();
|
|
17918
|
-
}
|
|
17919
|
-
});
|
|
17920
|
-
this.resizeObserver.observe(document.body);
|
|
17921
|
-
}
|
|
17922
|
-
updatePosition() {
|
|
17923
|
-
if (!this.signpostContent || !this.trigger) {
|
|
17924
|
-
return;
|
|
17925
|
-
}
|
|
17926
|
-
const triggerRect = this.trigger.getBoundingClientRect();
|
|
17927
|
-
this.signpostContent.position = this.getBestPosition(triggerRect);
|
|
17928
|
-
}
|
|
17929
|
-
getBestPosition(triggerRect) {
|
|
17930
|
-
const contentWidth = 300;
|
|
17931
|
-
const contentHeight = 200;
|
|
17932
|
-
const buffer = 50;
|
|
17933
|
-
const contentHeaderHeight = document.querySelector('.content-header')?.getBoundingClientRect().height ?? 0;
|
|
17934
|
-
const appHeaderHight = document.querySelector('.header')?.getBoundingClientRect().height ?? 0;
|
|
17935
|
-
const headerHeight = contentHeaderHeight + appHeaderHight;
|
|
17936
|
-
// Find first position with enough space
|
|
17937
|
-
for (const pos of PREFERRED_ORDER) {
|
|
17938
|
-
if (this.hasSpace(triggerRect, pos, contentWidth, contentHeight, headerHeight, buffer)) {
|
|
17939
|
-
return pos;
|
|
17940
|
-
}
|
|
17941
|
-
}
|
|
17942
|
-
return PREFERRED_ORDER[0]; // Fallback to first preference
|
|
17943
|
-
}
|
|
17944
|
-
hasSpace(rect, position, width, height, headerHeight, buffer) {
|
|
17945
|
-
const [vertical, horizontal] = position.split('-');
|
|
17946
|
-
const verticalSpace = vertical === 'top'
|
|
17947
|
-
? rect.top - headerHeight - height - buffer
|
|
17948
|
-
: window.innerHeight - rect.bottom - height - buffer;
|
|
17949
|
-
const horizontalSpace = horizontal === 'left' ? rect.left - width - buffer : window.innerWidth - rect.right - width - buffer;
|
|
17950
|
-
return Math.min(verticalSpace, horizontalSpace) > 0;
|
|
17951
|
-
}
|
|
17952
|
-
ngOnDestroy() {
|
|
17953
|
-
if (this.trigger) {
|
|
17954
|
-
this.trigger.removeEventListener('mousedown', this.handleMouseDown);
|
|
17955
|
-
}
|
|
17956
|
-
this.resizeObserver?.disconnect();
|
|
17957
|
-
}
|
|
17958
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: AutoPositionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
17959
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.18", type: AutoPositionDirective, isStandalone: true, selector: "clr-signpost-content[autoPosition]", ngImport: i0 }); }
|
|
17960
|
-
}
|
|
17961
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: AutoPositionDirective, decorators: [{
|
|
17962
|
-
type: Directive,
|
|
17963
|
-
args: [{
|
|
17964
|
-
selector: 'clr-signpost-content[autoPosition]',
|
|
17965
|
-
standalone: true,
|
|
17966
|
-
}]
|
|
17967
|
-
}] });
|
|
17968
|
-
|
|
17969
|
-
class TenantFullDateRangeDirective {
|
|
17970
|
-
constructor() {
|
|
17971
|
-
this.start = contentChild(ClrStartDateInput);
|
|
17972
|
-
this.end = contentChild(ClrEndDateInput);
|
|
17973
|
-
}
|
|
17974
|
-
ngAfterContentInit() {
|
|
17975
|
-
if (this.start()) {
|
|
17976
|
-
const originalStartEmit = this.start().dateChange.emit.bind(this.start().dateChange);
|
|
17977
|
-
this.start().dateChange.emit = date => {
|
|
17978
|
-
originalStartEmit(this.toTenantDateTime(date, '00:00'));
|
|
17979
|
-
};
|
|
17980
|
-
}
|
|
17981
|
-
if (this.end()) {
|
|
17982
|
-
const originalEndEmit = this.end().dateChange.emit.bind(this.end().dateChange);
|
|
17983
|
-
this.end().dateChange.emit = date => {
|
|
17984
|
-
originalEndEmit(this.toTenantDateTime(date, '23:59'));
|
|
17985
|
-
};
|
|
17986
|
-
}
|
|
17987
|
-
}
|
|
17988
|
-
toTenantDateTime(date, time) {
|
|
17989
|
-
if (!date) {
|
|
17990
|
-
return date;
|
|
17991
|
-
}
|
|
17992
|
-
const [hours, minutes] = time.split(':').map(Number);
|
|
17993
|
-
const result = new Date(date);
|
|
17994
|
-
result.setHours(hours, minutes, 0, 0);
|
|
17995
|
-
return result;
|
|
17996
|
-
}
|
|
17997
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TenantFullDateRangeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
17998
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "19.2.18", type: TenantFullDateRangeDirective, isStandalone: true, selector: "clr-date-range-container[cngTenantFullDateRange]", queries: [{ propertyName: "start", first: true, predicate: ClrStartDateInput, descendants: true, isSignal: true }, { propertyName: "end", first: true, predicate: ClrEndDateInput, descendants: true, isSignal: true }], ngImport: i0 }); }
|
|
17999
|
-
}
|
|
18000
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TenantFullDateRangeDirective, decorators: [{
|
|
18001
|
-
type: Directive,
|
|
18002
|
-
args: [{
|
|
18003
|
-
selector: 'clr-date-range-container[cngTenantFullDateRange]',
|
|
18004
|
-
standalone: true,
|
|
18005
|
-
}]
|
|
18006
|
-
}] });
|
|
18007
|
-
|
|
18008
|
-
/*
|
|
18009
|
-
* Copyright (c) 2026 Porsche Informatik. All Rights Reserved.
|
|
18010
|
-
* This software is released under MIT license.
|
|
18011
|
-
* The full license information can be found in LICENSE in the root directory of this project.
|
|
18012
|
-
*/
|
|
18013
|
-
const CLR_CHARTS_DECLARATIONS = [
|
|
18014
|
-
AreaChartComponent,
|
|
18015
|
-
BarChartComponent,
|
|
18016
|
-
ComboChartComponent,
|
|
18017
|
-
FunnelChartComponent,
|
|
18018
|
-
LineChartComponent,
|
|
18019
|
-
PieChartComponent,
|
|
18020
|
-
];
|
|
18021
|
-
class ClrChartsModule {
|
|
18022
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ClrChartsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
18023
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.18", ngImport: i0, type: ClrChartsModule, declarations: [AreaChartComponent,
|
|
18024
|
-
BarChartComponent,
|
|
18025
|
-
ComboChartComponent,
|
|
18026
|
-
FunnelChartComponent,
|
|
18027
|
-
LineChartComponent,
|
|
18028
|
-
PieChartComponent], imports: [CommonModule,
|
|
18029
|
-
DecimalPipe,
|
|
18030
|
-
ClrAlertModule,
|
|
18031
|
-
ClrIconModule,
|
|
18032
|
-
ClrSignpostModule,
|
|
18033
|
-
// standalone helpers consumed by the non-standalone chart components
|
|
18034
|
-
ChartAlertOverlayComponent,
|
|
18035
|
-
ChartExportButtonComponent,
|
|
18036
|
-
ChartLegendComponent,
|
|
18037
|
-
ChartSkeletonComponent,
|
|
18038
|
-
ChartTooltipComponent,
|
|
18039
|
-
OutsideClickDirective,
|
|
18040
|
-
WindowResizeDirective], exports: [AreaChartComponent,
|
|
18041
|
-
BarChartComponent,
|
|
18042
|
-
ComboChartComponent,
|
|
18043
|
-
FunnelChartComponent,
|
|
18044
|
-
LineChartComponent,
|
|
18045
|
-
PieChartComponent] }); }
|
|
18046
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ClrChartsModule, imports: [CommonModule,
|
|
18047
|
-
ClrAlertModule,
|
|
18048
|
-
ClrIconModule,
|
|
18049
|
-
ClrSignpostModule,
|
|
18050
|
-
// standalone helpers consumed by the non-standalone chart components
|
|
18051
|
-
ChartAlertOverlayComponent,
|
|
18052
|
-
ChartExportButtonComponent,
|
|
18053
|
-
ChartSkeletonComponent,
|
|
18054
|
-
ChartTooltipComponent] }); }
|
|
18055
|
-
}
|
|
18056
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ClrChartsModule, decorators: [{
|
|
18057
|
-
type: NgModule,
|
|
18058
|
-
args: [{
|
|
18059
|
-
imports: [
|
|
18060
|
-
CommonModule,
|
|
18061
|
-
DecimalPipe,
|
|
18062
|
-
ClrAlertModule,
|
|
18063
|
-
ClrIconModule,
|
|
18064
|
-
ClrSignpostModule,
|
|
18065
|
-
// standalone helpers consumed by the non-standalone chart components
|
|
18066
|
-
ChartAlertOverlayComponent,
|
|
18067
|
-
ChartExportButtonComponent,
|
|
18068
|
-
ChartLegendComponent,
|
|
18069
|
-
ChartSkeletonComponent,
|
|
18070
|
-
ChartTooltipComponent,
|
|
18071
|
-
OutsideClickDirective,
|
|
18072
|
-
WindowResizeDirective,
|
|
18073
|
-
],
|
|
18074
|
-
declarations: [...CLR_CHARTS_DECLARATIONS],
|
|
18075
|
-
exports: [...CLR_CHARTS_DECLARATIONS],
|
|
18076
|
-
}]
|
|
18077
|
-
}] });
|
|
18078
|
-
|
|
18079
|
-
/*
|
|
18080
|
-
* Copyright (c) 2026 Porsche Informatik. All Rights Reserved.
|
|
18081
|
-
* This software is released under MIT license.
|
|
18082
|
-
* The full license information can be found in LICENSE in the root directory of this project.
|
|
18083
|
-
*/
|
|
18084
|
-
|
|
18085
15604
|
/*
|
|
18086
15605
|
* Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
|
|
18087
15606
|
* This software is released under MIT license.
|
|
@@ -18127,8 +15646,7 @@ class ClrAddonsModule {
|
|
|
18127
15646
|
ClrDatagridColumnReorderModule,
|
|
18128
15647
|
ClrSignpostAddonModule,
|
|
18129
15648
|
ClrFocusFirstInvalidFieldDirective,
|
|
18130
|
-
ClrControlEnterSubmitDirective
|
|
18131
|
-
ClrChartsModule] }); }
|
|
15649
|
+
ClrControlEnterSubmitDirective] }); }
|
|
18132
15650
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ClrAddonsModule, imports: [ClrViewEditSectionModule,
|
|
18133
15651
|
ClrPagerModule,
|
|
18134
15652
|
ClrDotPagerModule,
|
|
@@ -18165,8 +15683,7 @@ class ClrAddonsModule {
|
|
|
18165
15683
|
ClrActionPanelModule,
|
|
18166
15684
|
ClrReadonlyDirectiveModule,
|
|
18167
15685
|
ClrDatagridColumnReorderModule,
|
|
18168
|
-
ClrSignpostAddonModule
|
|
18169
|
-
ClrChartsModule] }); }
|
|
15686
|
+
ClrSignpostAddonModule] }); }
|
|
18170
15687
|
}
|
|
18171
15688
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: ClrAddonsModule, decorators: [{
|
|
18172
15689
|
type: NgModule,
|
|
@@ -18213,7 +15730,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImpo
|
|
|
18213
15730
|
ClrSignpostAddonModule,
|
|
18214
15731
|
ClrFocusFirstInvalidFieldDirective,
|
|
18215
15732
|
ClrControlEnterSubmitDirective,
|
|
18216
|
-
ClrChartsModule,
|
|
18217
15733
|
],
|
|
18218
15734
|
}]
|
|
18219
15735
|
}] });
|
|
@@ -19475,5 +16991,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImpo
|
|
|
19475
16991
|
* Generated bundle index. Do not edit.
|
|
19476
16992
|
*/
|
|
19477
16993
|
|
|
19478
|
-
export { ACShape, AcceptanceDateShape, AcceptedBrands, AccessoryPartsShape, AreaChartComponent, AudiBrandShape, AwardWinnerPremiumShape, BIG_ENDIAN, BarChartComponent, BlocksGroupForwardShape, BundleForwardShape, BusinessCustomersCommercialShape, BusinessCustomersPrivateShape, BusinessPartnerWithCar, CLR_BLANK_OPTION, CONTENT_PROVIDER, CalculatorForwardShape, CaliforniaServiceShape, CampaignOutdatedShape, CampaignShape, CarOffSite, CarOnSite, CircleFilled, CircleHalfFilled, CircleQuarterFilled, CircleThreeQuartersFilled, ClrActionPanel, ClrActionPanelContainer, ClrActionPanelContainerContent, ClrActionPanelContainerFooter, ClrActionPanelModule, ClrActiveNotification, ClrAddonsLabel, ClrAddonsModule, ClrAutocompleteOff, ClrAutocompleteOffModule, ClrBackButton, ClrBackButtonModule, ClrBrandAvatar, ClrBrandAvatarModule, ClrBreadcrumb, ClrBreadcrumbModule, ClrBreadcrumbService, ClrChartsModule, ClrCollapseExpandSection, ClrCollapseExpandSectionModule, ClrContentPanel, ClrContentPanelContainer, ClrContentPanelContainerContent, ClrContentPanelContainerFooter, ClrContentPanelModule, ClrContentRef, ClrControlEnterSubmitDirective, ClrCopyToClipboard, ClrDataListPredefinedValidatorDirective, ClrDataListValidatorModule, ClrDataListValidators, ClrDatagridColumnReorderModule, ClrDatagridStatePersistenceModule, ClrDateFilterComponent, ClrDateFilterModule, ClrDateTimeContainer, ClrDateTimeModule, ClrDaterangeMaxValidator, ClrDaterangeMinValidator, ClrDaterangeOrderValidator, ClrDaterangeRequiredValidator, ClrDaterangepickerContainerComponent, ClrDaterangepickerDirective, ClrDaterangepickerModule, ClrDotPager, ClrDotPagerModule, ClrDropdownOverflowDirective, ClrDropdownOverflowModule, ClrEnumFilterComponent, ClrEnumFilterModule, ClrExportDatagridButtonModule, ClrFlowBar, ClrFlowBarModule, ClrFocusFirstInvalidFieldDirective, ClrFormModule, ClrGenericQuickList, ClrGenericQuickListModule, ClrHistory, ClrHistoryModule, ClrHistoryPinned, ClrHistoryService, ClrIconAvatar, ClrIconAvatarModule, ClrIfDaterangeErrorDirective, ClrIfWarning, ClrIfWarningModule, ClrLetterAvatar, ClrLetterAvatarModule, ClrLocationBarModule, ClrMainNavGroup, ClrMainNavGroupItem, ClrMainNavGroupModule, ClrMaxNumeric, ClrMinNumeric, ClrMultilingualInput, ClrMultilingualInputValidators, ClrMultilingualModule, ClrMultilingualSelector, ClrMultilingualTextarea, ClrNotification, ClrNotificationModule, ClrNotificationRef, ClrNotificationService, ClrNumericField, ClrNumericFieldModule, ClrNumericFieldValidators, ClrPagedSearchResultList, ClrPagedSearchResultListModule, ClrPager, ClrPagerModule, ClrProgressSpinnerComponent, ClrProgressSpinnerModule, ClrQuickList, ClrQuickListModule, ClrReadonlyDirective, ClrReadonlyDirectiveModule, ClrRequiredAllMultilang, ClrRequiredOneMultilang, ClrSearchField, ClrSearchFieldModule, ClrSignpostAddonComponent, ClrSignpostAddonModule, ClrSummaryArea, ClrSummaryAreaStateService, ClrSummaryAreaToggle, ClrSummaryItem, ClrSummaryItemValue, ClrTimeInput, ClrTreetable, ClrTreetableActionOverflow, ClrTreetableCell, ClrTreetableColumn, ClrTreetableFilter, ClrTreetableModule, ClrTreetablePlaceholder, ClrTreetableRow, ClrTreetableSortOrder, ClrTreetableStringFilter, ClrTreetableTreeNode, ClrViewEditSection, ClrViewEditSectionModule, ColumnHiddenStatePersistenceDirective, ComboChartComponent, CompletedByDateShape, CupraBrandShape, Customer, CustomerVip, CustomerVipCollection, CustomerWaiting, CustomerWaitingCollection, DATE, DELIMITER_REGEX, DWABrandShape, DatagridColumnReorderDirective, DatagridFieldDirective, DayModel, DeliveryDate, DieselShape, DollarBillForwardShape, DollarBillPartialShape, DynamicCellContentComponent, EnergyShape, ExportDatagridButtonComponent, ExportDatagridService, ExportTypeEnum, ExternalPartForwardShape, FirstRegistrationDate, FunnelChartComponent, GasCarsServiceShape, GasShape, HISTORY_NOTIFICATION_URL_PROVIDER, HISTORY_PROVIDER, HISTORY_TOKEN, HistoryProvider, InternalPartForwardShape, InvoiceReadyShape, InvoiceRecipient, InvoiceShape, ItemsForwardShape, ItemsReceiveShape, LITTLE_ENDIAN, LITTLE_ENDIAN_REGEX, LineChartComponent, LocationBarComponent, LocationBarContentProvider, LocationBarNode, LogoCommissionModule, LogoCommissionModuleFavIcon, LogoCommissionModuleNegative, LogoCommissionModuleNegativeFavIcon, LogoCostApproval, LogoCostApprovalFavIcon, LogoCostApprovalNegative, LogoCostApprovalNegativeFavIcon, LogoCostControlling, LogoCostControllingFavIcon, LogoCostControllingNegative, LogoCostControllingNegativeFavIcon, LogoDigitalServiceReception, LogoDigitalServiceReceptionFavIcon, LogoDigitalServiceReceptionNegative, LogoDigitalServiceReceptionNegativeFavIcon, LogoDocFlow, LogoDocFlowFavIcon, LogoDocFlowNegative, LogoDocFlowNegativeFavIcon, LogoDocScan, LogoDocScanFavIcon, LogoDocScanNegative, LogoDocScanNegativeFavIcon, LogoDocStore, LogoDocStoreFavIcon, LogoDocStoreNegative, LogoDocStoreNegativeFavIcon, LogoEBilling, LogoEBillingFavIcon, LogoEBillingNegative, LogoEBillingNegativeFavIcon, LogoEPayment, LogoEPaymentFavIcon, LogoEPaymentNegative, LogoEPaymentNegativeFavIcon, LogoMobilityPlanner, LogoMobilityPlannerFavIcon, LogoMobilityPlannerNegative, LogoMobilityPlannerNegativeFavIcon, LogoPartsMobile, LogoPartsMobileFavIcon, LogoPartsMobileNegative, LogoPartsMobileNegativeFavIcon, LogoSBO, LogoSBOFavIcon, LogoSBONegative, LogoSBONegativeFavIcon, LogoServiceCube, LogoServiceCubeFavIcon, LogoServiceCubeNegative, LogoServiceCubeNegativeFavIcon, LogoWCP, LogoWCPFavIcon, LogoWCPNegative, LogoWCPNegativeFavIcon, LogoWorkshopOrderTracker, LogoWorkshopOrderTrackerFavIcon, LogoWorkshopOrderTrackerNegative, LogoWorkshopOrderTrackerNegativeFavIcon, MIDDLE_ENDIAN, MIDDLE_ENDIAN_REGEX, MONTH, Mechanic, NewCarUtilityVehicleShape, NodeId, Number0, Number1, Number10, Number11, Number12, Number13, Number14, Number15, Number16, Number17, Number18, Number19, Number2, Number20, Number3, Number4, Number5, Number6, Number7, Number8, Number9, OrderShape, OrderStatusShape, PaintMaterialForwardShape, PaintMaterialShape, ParkingLocation, PartAvailabilityInfoShape, PartAvailabilityNoShape, PartAvailabilityUnknownShape, PartAvailabilityWarningShape, PartAvailabilityYesShape, PartIdenticalPredecessorShape, PartIdenticalShape, PartIdenticalSuccessorShape, PartIdenticalSuccpredecessorShape, PartNonStockForwardShape, PartPredecessorShape, PartSuccessorPredecessorShape, PartSuccessorShape, PartsChangelocation, PartsForwardShape, PartsInventory, PartsNonStockShape, PartsPicking, PartsPickingPlus, PartsReceiving, PartsShape, PieChartComponent, PlusServiceShape, PopoverPositions, PorscheBrandShape, PriceTypeSwitchShape, RepeatRepairCollection, RepeatRepairShape, ReplacementVehicleCollection, ReplacementVehicleShape, ReturnDateShape, SEPARATOR_TEXT_DEFAULT, SeatBrandShape, ServiceAdvisor, SkodaBrandShape, Sort, StatePersistenceKeyDirective, TRANSLATIONS, TaskAndAppointment, TextForward, TimeModel, TopcardShape, TouaregServiceShape, TreetableCellRenderer, TreetableDataStateService, TreetableHeaderRenderer, TreetableItemsDirective, TreetableMainRenderer, TreetableRowRenderer, USER_INPUT_REGEX, VWBrandShape, VWNBrandShape, VWShape, VehicleConversionShape, VinShape, VsfSearchShape, VsfSearchShape48, WCPShape, WrenchForward, YEAR, acceleration, accelerationIcon, acceptanceDateIcon, accessories, accessoriesIcon, accessoryPartsIcon, adblueAppIcon, adblue_app, add, addIcon, airConditionerIcon, air_conditioning, alert, alertFilledAppIcon, alertIcon, alertNotificationFilledAppIcon, alert_filled_app, alert_notification_filled_app, allIcons, ambientLightAppIcon, ambient_light_app, amplifier, amplifierIcon, appConnectAppIcon, app_connect_app, archive, archiveIcon, arrowDownIcon, arrowLeftAlignedAppIcon, arrowLeftIcon, arrowRightIcon, arrowSliderAppIcon, arrowUpIcon, arrow_down, arrow_left, arrow_left_aligned_app, arrow_right, arrow_slider_app, arrow_up, attachment, attachmentIcon, audiBrandIcon, authentPlugChargeAppIcon, authentQrAppIcon, authentRfidAppIcon, authentTouchidAppIcon, authent_plug_charge_app, authent_qr_app, authent_rfid_app, authent_touch_id_app, automaticTempAppIcon, automatic_temp_app, awardWinnerPremiumIcon, award_winner_premium, back, backIcon, battery, batteryIcon, batterySocChargingAppIcon, batterySocDepartureAppIcon, batterySocDestinationAppIcon, battery_soc_charging_app, battery_soc_departure_app, battery_soc_destination_app, bin, binIcon, blocksGroupForwardIcon, bluetooth, bluetoothIcon, bookmark, bookmarkFilledIcon, bookmarkIcon, bookmark_filled, brakeAppIcon, brake_app, brochure, brochureIcon, bulletpointAppIcon, bulletpoint_app, bundleForwardIcon, businessCustomersCommercialIcon, businessCustomersPrivateIcon, businessPartnerWithCarIcon, business_customers_commercial, business_customers_private, calc, calcIcon, calculatorForwardIcon, calendar, calendarCustomIcon, californiaServiceIcon, californiaSpecialistIcon, california_specialist, cameraScanIcon, camera_scan, campaignIcon, campaignOutdatedIcon, carDocumentsIcon, carErrorAppIcon, carInsuranceIcon, carOffSite, carOnSite, carPickupServiceIcon, carPlusIcon, carSettingsIcon, carVerifiedAppIcon, carWashIcon, carWheelAppIcon, car_documents, car_error_app, car_insurance, car_pickup_service, car_plus, car_settings, car_verified_app, car_wheel_app, carwash, certifiedRepairIcon, certifiedRetailerIcon, certified_repair, certified_retailer, challengeAppIcon, challenge_app, charging, chargingIcon, chargingPduAppIcon, chargingStationIcon, chargingTarifOverviewAppIcon, charging_pdu_app, charging_station, charging_tarif_overview_app, chat, chatAppIcon, chatIcon, chat_app, checkboxCheckedAppIcon, checkboxCheckedIcon, checkboxUncheckedAppIcon, checkboxUncheckedIcon, checkbox_checked, checkbox_checked_app, checkbox_unchecked, checkbox_unchecked_app, checkmark, checkmarkAppIcon, checkmarkFilledAppIcon, checkmarkIcon, checkmark_app, checkmark_filled_app, chevronDownIcon, chevronLeftAlignedappIcon, chevronLeftIcon, chevronRightAlignedappIcon, chevronRightIcon, chevronSmallLeftAlignedappIcon, chevronSmallRightAlignedappIcon, chevronUpIcon, chevron_down, chevron_left, chevron_left_alignedapp, chevron_right, chevron_right_alignedapp, chevron_small_left_alignedapp, chevron_small_right_alignedapp, chevron_up, circleFilledIcon, circleHalfFilledIcon, circleQuarterFilledIcon, circleThreeQuartersFilledIcon, city, cityIcon, clearAppIcon, clearRightAlignedappIcon, clear_app, clear_right_alignedapp, clock, clockIcon, close, closeAppIcon, closeCircleIcon, closeIcon, closeLeftAlignedappIcon, closeRightAlignedappIcon, close_app, close_circle, close_left_alignedapp, close_right_alignedapp, clrIconSVG, coffeeFilledAppIcon, coffee_filled_app, compassAppIcon, compass_app, completedByDateIcon, configuratorCommercialIcon, configuratorPrivateIcon, configurator_commercial, configurator_private, construction, constructionIcon, consumptionFuelFilledAppIcon, consumptionIcon, consumption_fuel, consumption_fuel_filled_app, contact, contactDealerFilledAppIcon, contactDealerIcon, contactIcon, contact_dealer, contact_dealer_filled_app, countryRoadIcon, country_road, craft, craftIcon, cupraBrandIcon, customerIcon, customerVipIcon, customerWaitingIcon, customersCenterIcon, customers_center, dataCopyAppIcon, dataExpiredIcon, dataFilledIcon, dataInputIcon, dataPlugAppIcon, dataSearchIcon, dataTimeExtensionIcon, data_copy_app, data_expired, data_filled, data_input, data_plug_app, data_search, data_time_extension, defaultSummaryAreaCollapsedKey, defogDefrostAutoAppIcon, defogDefrostIcon, defog_defrost, defog_defrost_auto_app, deliveryDateIcon, destinationAppIcon, destination_app, dieselIcon, direction, directionIcon, dischargingAppIcon, discharging_app, discountAppIcon, discount_app, discoveryAppIcon, discovery_app, dollarBillForwardIcon, dollarBillPartialIcon, download, downloadCustomIcon, dragIndicatorIcon, drag_indicator, driversAssistanceIcon, drivers_assistance, dropFilledAppIcon, drop_filled_app, dwaBrandIcon, eco, ecoIcon, edit, editIcon, editSmallRightAlignedAppIcon, edit_small_right_aligned_app, efficiency, efficiencyIcon, electricCarsIcon, electricCarsServiceIcon, electric_cars, electric_cars_service, electricity, electricityFilledAppIcon, electricityIcon, electricity_filled_app, emergency, emergencyIcon, emission, emissionIcon, energyIcon, engine, engineIcon, entertainment, entertainmentIcon, escapeHtml, escapeRegex, exportAppIcon, export_app, expressServiceIcon, express_service, exterior, exterior360Icon, exteriorIcon, exterior_360, externalPartForwardIcon, faq, faqIcon, fastForwardIcon, fast_forward, fax, faxIcon, filter, filterIcon, findACarIcon, findADealerIcon, find_a_car, find_a_dealer, firstRegistrationDateIcon, fleetServiceCommercialIcon, fleetServicePrivateIcon, fleet_service_commercial, fleet_service_private, folderFilledAppIcon, folder_filled_app, foodFilledAppIcon, food_filled_app, formatNumber, fullscreenEnterIcon, fullscreenExitIcon, fullscreen_enter, fullscreen_exit, gallery, galleryIcon, garageAppIcon, garage_app, gasAppIcon, gasCarsServiceIcon, gasIcon, gas_app, glassDamageAppIcon, glass_damage_app, gte, gteIcon, heart, heartFilledAppIcon, heartIcon, heart_filled_app, heightAppIcon, height_app, highwayRoadIcon, highway_road, history, historyIcon, homeAppIcon, homeEnergyAppIcon, homeFilledAppIcon, home_app, home_energy_app, home_filled_app, hornAppIcon, hornFilledAppIcon, horn_app, horn_filled_app, hybrid, hybridIcon, immediateChargingAppIcon, immediate_charging_app, info, infoFilledIcon, infoIcon, info_filled, inputHideIcon, inputShowIcon, input_hide, input_show, interior, interior360Icon, interiorIcon, interior_360, internalPartForwardIcon, internet, internetIcon, invitationAppIcon, invitation_app, invoiceIcon, invoiceReadyIcon, invoiceRecipientIcon, itemsForwardIcon, itemsRecieveIcon, jobportal, jobportalIcon, keyAppIcon, keyCardAppIcon, keyDigitalAppIcon, key_app, key_card_app, key_digital_app, keyboardAppIcon, keyboard_app, layerCollapseAppIcon, layerExpandAppIcon, layer_collapse_app, layer_expand_app, layersAppIcon, layers_app, legalTermsAndConditionsAppIcon, legal_terms_and_conditions_app, licencePlateAppIcon, licence_plate_app, lightAssistappIcon, light_assistapp, lightingAppIcon, lighting_app, linkExternAppIcon, link_extern_app, list, listIcon, loadingVolumeIcon, loading_volume, localBusinessIcon, local_business, locate, locateIcon, lock, lockIcon, lockOpenIcon, lock_open, login, loginIcon, logistic, logisticIcon, logoCommissionModuleFavIcon, logoCommissionModuleIcon, logoCommissionModuleNegativeFavIcon, logoCommissionModuleNegativeIcon, logoCostApprovalFavIcon, logoCostApprovalIcon, logoCostApprovalNegativeFavIcon, logoCostApprovalNegativeIcon, logoCrossControllingFavIcon, logoCrossControllingIcon, logoCrossControllingNegativeFavIcon, logoCrossControllingNegativeIcon, logoDigitalServiceReceptionFavIcon, logoDigitalServiceReceptionIcon, logoDigitalServiceReceptionNegativeFavIcon, logoDigitalServiceReceptionNegativeIcon, logoDocFlowFavIcon, logoDocFlowIcon, logoDocFlowNegativeFavIcon, logoDocFlowNegativeIcon, logoDocScanFavIcon, logoDocScanIcon, logoDocScanNegativeFavIcon, logoDocScanNegativeIcon, logoDocStoreFavIcon, logoDocStoreIcon, logoDocStoreNegativeFavIcon, logoDocStoreNegativeIcon, logoEBillingFavIcon, logoEBillingIcon, logoEBillingNegativeFavIcon, logoEBillingNegativeIcon, logoEPaymentFavIcon, logoEPaymentIcon, logoEPaymentNegativeFavIcon, logoEPaymentNegativeIcon, logoMobilityPlannerFavIcon, logoMobilityPlannerIcon, logoMobilityPlannerNegativeFavIcon, logoMobilityPlannerNegativeIcon, logoPartsMobileFavIcon, logoPartsMobileIcon, logoPartsMobileNegativeFavIcon, logoPartsMobileNegativeIcon, logoSBOFavIcon, logoSBOIcon, logoSBONegativeFavIcon, logoSBONegativeIcon, logoServiceCubeFavIcon, logoServiceCubeIcon, logoServiceCubeNegativeFavIcon, logoServiceCubeNegativeIcon, logoWCPFavIcon, logoWCPIcon, logoWCPNegativeFavIcon, logoWCPNegativeIcon, logoWorkshopOrderTrackerFavIcon, logoWorkshopOrderTrackerIcon, logoWorkshopOrderTrackerNegativeFavIcon, logoWorkshopOrderTrackerNegativeIcon, logout, logoutIcon, magnifier, magnifierIcon, magnifierMinusIcon, magnifierPlusIcon, magnifier_minus, magnifier_plus, mail, mailIcon, mailResendAppIcon, mail_resend_app, manual, manualIcon, map, mapIcon, mapToInternalTree, mechanicIcon, media, mediaIcon, menu, menuAppAppIcon, menuIcon, menu_app_app, microphoneAppIcon, microphone_app, mobile, mobileIcon, moreAppIcon, moreAppbarAppIcon, more_app, more_appbar_app, mot, motIcon, motability, motabilityIcon, navigate, navigateFilledAppIcon, navigateIcon, navigate_filled_app, newCarCommercialIcon, newCarPrivateFilledAppIcon, newCarPrivateIcon, newCarUtilityVehicleIcon, new_car_commercial, new_car_private, new_car_private_filled_app, nightServiceIcon, night_service, notification, notificationFilledIcon, notificationIcon, notification_filled, number0Icon, number10Icon, number11Icon, number12Icon, number13Icon, number14Icon, number15Icon, number16Icon, number17Icon, number18Icon, number19Icon, number1Icon, number20Icon, number2Icon, number3Icon, number4Icon, number5Icon, number6Icon, number7Icon, number8Icon, number9Icon, offers, offersFilledAppIcon, offersIcon, offers_filled_app, officeAppIcon, officeFilledAppIcon, office_app, office_filled_app, oilLevelIcon, oilLevelWarningIcon, oilTemperatureAppIcon, oil_level, oil_level_warning, oil_temperature_app, onCallDutyIcon, on_call_duty, openSatIcon, open_sat, orderIcon, orderStatusIcon, paintMaterialForwardIcon, paintMaterialIcon, paintShopIcon, paint_shop, paragraphAppIcon, paragraph_app, parkHeaterAppIcon, park_heater_app, parking, parkingFilledAppIcon, parkingGarageAppIcon, parkingIcon, parkingLocationIcon, parkingRouteAppIcon, parkingValetAppIcon, parking_filled_app, parking_garage_app, parking_route_app, parking_valet_app, partAvailabilityInfoIcon, partAvailabilityNoIcon, partAvailabilityUnknownIcon, partAvailabilityWarningIcon, partAvailabilityYesIcon, partIdenticalIcon, partIdenticalPredecessorIcon, partIdenticalSuccessorIcon, partIdenticalSuccpredecessorIcon, partPredecessorIcon, partSuccessorIcon, partSuccessorPredecessorIcon, partsChangelocationIcon, partsForwardIcon, partsIcon, partsInventoryIcon, partsNonStockForwardIcon, partsNonStockIcon, partsPickingIcon, partsPickingPlusIcon, partsReceivingIcon, pause, pauseIcon, payload, payloadIcon, paymentAppIcon, paymentCashAppIcon, paymentChargingCardAppIcon, paymentCreditcardAppIcon, paymentMachineAppIcon, payment_app, payment_cash_app, payment_charging_card_app, payment_creditcard_app, payment_machine_app, performance, performanceIcon, petrol, petrolIcon, phone, phoneIcon, pin, pinFilledAppIcon, pinGenericFilledAppIcon, pinIcon, pin_filled_app, pin_generic_filled_app, play, playIcon, plugCcsAppIcon, plugChademoAppIcon, plugChargeAppIcon, plugGenericAppIcon, plugSchukoAppIcon, plugType1AppIcon, plugType2AppIcon, plug_ccs_app, plug_chademo_app, plug_charge_app, plug_generic_app, plug_schuko_app, plug_type1_app, plug_type2_app, plusServiceIcon, porscheBrandIcon, power, powerIcon, powerTrainIcon, powertrain, preHeaterAppIcon, pre_heater_app, preciseLaneNavigationAppIcon, precise_lane_navigation_app, presentAppIcon, present_app, priceTypeSwitchIcon, printer, printerIcon, privacyAppIcon, privacy_app, profile, profileIcon, profileRegisterAppIcon, profileVerifiedIcon, profile_register_app, profile_verified, publicServiceIcon, publicTransportAppIcon, public_service, public_transport_app, qualifiedWorkshopIcon, qualified_workshop, questionnaireAppIcon, questionnaire_app, radio, radioButtonInselectedIcon, radioButtonSelectedForDefIcon, radioButtonSelectedIcon, radioIcon, radio_button_inselected, radio_button_selected, radio_button_selected_for_development, range, rangeIcon, reload, reloadIcon, remove, removeIcon, repeat, repeatIcon, repeatRepairIcon, replacementVehicleIcon, returnDateIcon, rewind, rewindIcon, roadsideAssistanceIcon, roadside_assistance, route, routeArrowAppIcon, routeIcon, route_arrow_app, routesHistoryAppIcon, routes_history_app, rss, rssIcon, safety, safetyIcon, save, saveAppIcon, saveIcon, save_app, seat, seatAirIcon, seatBrandIcon, seatIcon, seat_air, secretTipAppIcon, secretTipFilledAppIcon, secret_tip_app, secret_tip_filled_app, selected, selectedIcon, selectedPartnerNetworkAppIcon, selected_partner_network_app, sendToCarAppIcon, send_to_car_app, service, serviceAdvisorIcon, serviceBellIcon, serviceFilledAppIcon, serviceIcon, service_bell, service_filled_app, settings, settingsIcon, shareAndroidIcon, shareIosIcon, share_android, share_ios, shoppingCartFilledAppIcon, shoppingCartIcon, shopping_cart, shopping_cart_filled_app, shuffle, shuffleIcon, size, sizeIcon, skillAppIcon, skill_app, skipBackwardIcon, skipForwardIcon, skip_backward, skip_forward, skodaBrandIcon, softwareDownloadAppIcon, software_download_app, sortingAppIcon, sorting_app, sound, soundIcon, standardEquipmentIcon, standard_equipment, starFilledIcon, starOutlineIcon, star_filled, star_outline, statisticAppIcon, statistic_app, stockLocatorCommercialIcon, stockLocatorPrivateIcon, stock_locator_commercial, stock_locator_private, stop, stopIcon, strip, switchPositionAppIcon, switch_position_app, syncAppIcon, sync_app, taskAndAppointmentIcon, taxiDealerIcon, taxi_dealer, technicalSpecificationIcon, technical_specification, temperatureAppIcon, temperature_app, testDriveIcon, test_drive, textForwardIcon, thumbsdownAppIcon, thumbsdownFilledAppIcon, thumbsdown_app, thumbsdown_filled_app, thumbsupAppIcon, thumbsupFilledAppIcon, thumbsup_app, thumbsup_filled_app, timeClimatisationAppIcon, timePreferredAppIcon, time_climatisation_app, time_preferred_app, timer, timerIcon, topcardIcon, touaregServiceIcon, transcriptDownloadIcon, transcript_download, transmissionAutomaticIcon, transmissionManualIcon, transmission_automatic, transmission_manual, tripAppIcon, tripLongAppIcon, tripPartedAppIcon, tripShortAppIcon, trip_app, trip_long_app, trip_parted_app, trip_short_app, turnSignalsIcon, turn_signals, unselected, unselectedIcon, updateRefreshAppIcon, update_refresh_app, upload, uploadAppIcon, uploadCustomIcon, upload_app, usedCarCommercialIcon, usedCarPrivateIcon, used_car_commercial, used_car_private, vehicleAmarokIcon, vehicleCaddyIcon, vehicleConversionIcon, vehicleCrafterIcon, vehicleHightIcon, vehicleIdBuzzIcon, vehicleMultivanIcon, vehicleTransporterIcon, vehicle_amarok, vehicle_caddy, vehicle_crafter, vehicle_hight, vehicle_idbuzz, vehicle_multivan, vehicle_transporter, videoChatIcon, video_chat, view360Icon, view_360, vinIcon, virtualRealityIcon, virtual_reality, voiceMessageAppIcon, voice_message_app, volkswagenAppIcon, volkswagenIcon, volkswagen_app, volumeMaximumIcon, volumeMediumIcon, volumeMuteIcon, volume_maximum, volume_medium, volume_mute, vsfSearch48Icon, vsfSearchIcon, vwBrandIcon, vwConnectLicenseAppIcon, vw_connect_license_app, vwnBrandIcon, walkingAppIcon, walkingFilledAppIcon, walking_app, walking_filled_app, wallbox, wallboxIcon, wcAppIcon, wc_app, wcpIcon, weAssistAppIcon, weChargeAppIcon, weDeliverAppIcon, weExperienceAppIcon, weParkAppIcon, weUpgradeAppIcon, we_assist_app, we_charge_app, we_deliver_app, we_experience_app, we_park_app, we_upgrade_app, weatherSunAppIcon, weather_sun_app, wheelToWheelIcon, wheel_to_wheel, windscreenWashIcon, windscreen_wash, wlanHotspotIcon, wlan_hotspot, wrenchForwardIcon };
|
|
16994
|
+
export { ACShape, AcceptanceDateShape, AcceptedBrands, AccessoryPartsShape, AudiBrandShape, AwardWinnerPremiumShape, BIG_ENDIAN, BlocksGroupForwardShape, BundleForwardShape, BusinessCustomersCommercialShape, BusinessCustomersPrivateShape, BusinessPartnerWithCar, CLR_BLANK_OPTION, CONTENT_PROVIDER, CalculatorForwardShape, CaliforniaServiceShape, CampaignOutdatedShape, CampaignShape, CarOffSite, CarOnSite, CircleFilled, CircleHalfFilled, CircleQuarterFilled, CircleThreeQuartersFilled, ClrActionPanel, ClrActionPanelContainer, ClrActionPanelContainerContent, ClrActionPanelContainerFooter, ClrActionPanelModule, ClrActiveNotification, ClrAddonsLabel, ClrAddonsModule, ClrAutocompleteOff, ClrAutocompleteOffModule, ClrBackButton, ClrBackButtonModule, ClrBrandAvatar, ClrBrandAvatarModule, ClrBreadcrumb, ClrBreadcrumbModule, ClrBreadcrumbService, ClrCollapseExpandSection, ClrCollapseExpandSectionModule, ClrContentPanel, ClrContentPanelContainer, ClrContentPanelContainerContent, ClrContentPanelContainerFooter, ClrContentPanelModule, ClrContentRef, ClrControlEnterSubmitDirective, ClrCopyToClipboard, ClrDataListPredefinedValidatorDirective, ClrDataListValidatorModule, ClrDataListValidators, ClrDatagridColumnReorderModule, ClrDatagridStatePersistenceModule, ClrDateFilterComponent, ClrDateFilterModule, ClrDateTimeContainer, ClrDateTimeModule, ClrDaterangeMaxValidator, ClrDaterangeMinValidator, ClrDaterangeOrderValidator, ClrDaterangeRequiredValidator, ClrDaterangepickerContainerComponent, ClrDaterangepickerDirective, ClrDaterangepickerModule, ClrDotPager, ClrDotPagerModule, ClrDropdownOverflowDirective, ClrDropdownOverflowModule, ClrEnumFilterComponent, ClrEnumFilterModule, ClrExportDatagridButtonModule, ClrFlowBar, ClrFlowBarModule, ClrFocusFirstInvalidFieldDirective, ClrFormModule, ClrGenericQuickList, ClrGenericQuickListModule, ClrHistory, ClrHistoryModule, ClrHistoryPinned, ClrHistoryService, ClrIconAvatar, ClrIconAvatarModule, ClrIfDaterangeErrorDirective, ClrIfWarning, ClrIfWarningModule, ClrLetterAvatar, ClrLetterAvatarModule, ClrLocationBarModule, ClrMainNavGroup, ClrMainNavGroupItem, ClrMainNavGroupModule, ClrMaxNumeric, ClrMinNumeric, ClrMultilingualInput, ClrMultilingualInputValidators, ClrMultilingualModule, ClrMultilingualSelector, ClrMultilingualTextarea, ClrNotification, ClrNotificationModule, ClrNotificationRef, ClrNotificationService, ClrNumericField, ClrNumericFieldModule, ClrNumericFieldValidators, ClrPagedSearchResultList, ClrPagedSearchResultListModule, ClrPager, ClrPagerModule, ClrProgressSpinnerComponent, ClrProgressSpinnerModule, ClrQuickList, ClrQuickListModule, ClrReadonlyDirective, ClrReadonlyDirectiveModule, ClrRequiredAllMultilang, ClrRequiredOneMultilang, ClrSearchField, ClrSearchFieldModule, ClrSignpostAddonComponent, ClrSignpostAddonModule, ClrSummaryArea, ClrSummaryAreaStateService, ClrSummaryAreaToggle, ClrSummaryItem, ClrSummaryItemValue, ClrTimeInput, ClrTreetable, ClrTreetableActionOverflow, ClrTreetableCell, ClrTreetableColumn, ClrTreetableFilter, ClrTreetableModule, ClrTreetablePlaceholder, ClrTreetableRow, ClrTreetableSortOrder, ClrTreetableStringFilter, ClrTreetableTreeNode, ClrViewEditSection, ClrViewEditSectionModule, ColumnHiddenStatePersistenceDirective, CompletedByDateShape, CupraBrandShape, Customer, CustomerVip, CustomerVipCollection, CustomerWaiting, CustomerWaitingCollection, DATE, DELIMITER_REGEX, DWABrandShape, DatagridColumnReorderDirective, DatagridFieldDirective, DayModel, DeliveryDate, DieselShape, DollarBillForwardShape, DollarBillPartialShape, DynamicCellContentComponent, EnergyShape, ExportDatagridButtonComponent, ExportDatagridService, ExportTypeEnum, ExternalPartForwardShape, FirstRegistrationDate, GasCarsServiceShape, GasShape, HISTORY_NOTIFICATION_URL_PROVIDER, HISTORY_PROVIDER, HISTORY_TOKEN, HistoryProvider, InternalPartForwardShape, InvoiceReadyShape, InvoiceRecipient, InvoiceShape, ItemsForwardShape, ItemsReceiveShape, LITTLE_ENDIAN, LITTLE_ENDIAN_REGEX, LocationBarComponent, LocationBarContentProvider, LocationBarNode, LogoCommissionModule, LogoCommissionModuleFavIcon, LogoCommissionModuleNegative, LogoCommissionModuleNegativeFavIcon, LogoCostApproval, LogoCostApprovalFavIcon, LogoCostApprovalNegative, LogoCostApprovalNegativeFavIcon, LogoCostControlling, LogoCostControllingFavIcon, LogoCostControllingNegative, LogoCostControllingNegativeFavIcon, LogoDigitalServiceReception, LogoDigitalServiceReceptionFavIcon, LogoDigitalServiceReceptionNegative, LogoDigitalServiceReceptionNegativeFavIcon, LogoDocFlow, LogoDocFlowFavIcon, LogoDocFlowNegative, LogoDocFlowNegativeFavIcon, LogoDocScan, LogoDocScanFavIcon, LogoDocScanNegative, LogoDocScanNegativeFavIcon, LogoDocStore, LogoDocStoreFavIcon, LogoDocStoreNegative, LogoDocStoreNegativeFavIcon, LogoEBilling, LogoEBillingFavIcon, LogoEBillingNegative, LogoEBillingNegativeFavIcon, LogoEPayment, LogoEPaymentFavIcon, LogoEPaymentNegative, LogoEPaymentNegativeFavIcon, LogoMobilityPlanner, LogoMobilityPlannerFavIcon, LogoMobilityPlannerNegative, LogoMobilityPlannerNegativeFavIcon, LogoPartsMobile, LogoPartsMobileFavIcon, LogoPartsMobileNegative, LogoPartsMobileNegativeFavIcon, LogoSBO, LogoSBOFavIcon, LogoSBONegative, LogoSBONegativeFavIcon, LogoServiceCube, LogoServiceCubeFavIcon, LogoServiceCubeNegative, LogoServiceCubeNegativeFavIcon, LogoWCP, LogoWCPFavIcon, LogoWCPNegative, LogoWCPNegativeFavIcon, LogoWorkshopOrderTracker, LogoWorkshopOrderTrackerFavIcon, LogoWorkshopOrderTrackerNegative, LogoWorkshopOrderTrackerNegativeFavIcon, MIDDLE_ENDIAN, MIDDLE_ENDIAN_REGEX, MONTH, Mechanic, NewCarUtilityVehicleShape, NodeId, Number0, Number1, Number10, Number11, Number12, Number13, Number14, Number15, Number16, Number17, Number18, Number19, Number2, Number20, Number3, Number4, Number5, Number6, Number7, Number8, Number9, OrderShape, OrderStatusShape, PaintMaterialForwardShape, PaintMaterialShape, ParkingLocation, PartAvailabilityInfoShape, PartAvailabilityNoShape, PartAvailabilityUnknownShape, PartAvailabilityWarningShape, PartAvailabilityYesShape, PartIdenticalPredecessorShape, PartIdenticalShape, PartIdenticalSuccessorShape, PartIdenticalSuccpredecessorShape, PartNonStockForwardShape, PartPredecessorShape, PartSuccessorPredecessorShape, PartSuccessorShape, PartsChangelocation, PartsForwardShape, PartsInventory, PartsNonStockShape, PartsPicking, PartsPickingPlus, PartsReceiving, PartsShape, PlusServiceShape, PopoverPositions, PorscheBrandShape, PriceTypeSwitchShape, RepeatRepairCollection, RepeatRepairShape, ReplacementVehicleCollection, ReplacementVehicleShape, ReturnDateShape, SEPARATOR_TEXT_DEFAULT, SeatBrandShape, ServiceAdvisor, SkodaBrandShape, Sort, StatePersistenceKeyDirective, TRANSLATIONS, TaskAndAppointment, TextForward, TimeModel, TopcardShape, TouaregServiceShape, TreetableCellRenderer, TreetableDataStateService, TreetableHeaderRenderer, TreetableItemsDirective, TreetableMainRenderer, TreetableRowRenderer, USER_INPUT_REGEX, VWBrandShape, VWNBrandShape, VWShape, VehicleConversionShape, VinShape, VsfSearchShape, VsfSearchShape48, WCPShape, WrenchForward, YEAR, acceleration, accelerationIcon, acceptanceDateIcon, accessories, accessoriesIcon, accessoryPartsIcon, adblueAppIcon, adblue_app, add, addIcon, airConditionerIcon, air_conditioning, alert, alertFilledAppIcon, alertIcon, alertNotificationFilledAppIcon, alert_filled_app, alert_notification_filled_app, allIcons, ambientLightAppIcon, ambient_light_app, amplifier, amplifierIcon, appConnectAppIcon, app_connect_app, archive, archiveIcon, arrowDownIcon, arrowLeftAlignedAppIcon, arrowLeftIcon, arrowRightIcon, arrowSliderAppIcon, arrowUpIcon, arrow_down, arrow_left, arrow_left_aligned_app, arrow_right, arrow_slider_app, arrow_up, attachment, attachmentIcon, audiBrandIcon, authentPlugChargeAppIcon, authentQrAppIcon, authentRfidAppIcon, authentTouchidAppIcon, authent_plug_charge_app, authent_qr_app, authent_rfid_app, authent_touch_id_app, automaticTempAppIcon, automatic_temp_app, awardWinnerPremiumIcon, award_winner_premium, back, backIcon, battery, batteryIcon, batterySocChargingAppIcon, batterySocDepartureAppIcon, batterySocDestinationAppIcon, battery_soc_charging_app, battery_soc_departure_app, battery_soc_destination_app, bin, binIcon, blocksGroupForwardIcon, bluetooth, bluetoothIcon, bookmark, bookmarkFilledIcon, bookmarkIcon, bookmark_filled, brakeAppIcon, brake_app, brochure, brochureIcon, bulletpointAppIcon, bulletpoint_app, bundleForwardIcon, businessCustomersCommercialIcon, businessCustomersPrivateIcon, businessPartnerWithCarIcon, business_customers_commercial, business_customers_private, calc, calcIcon, calculatorForwardIcon, calendar, calendarCustomIcon, californiaServiceIcon, californiaSpecialistIcon, california_specialist, cameraScanIcon, camera_scan, campaignIcon, campaignOutdatedIcon, carDocumentsIcon, carErrorAppIcon, carInsuranceIcon, carOffSite, carOnSite, carPickupServiceIcon, carPlusIcon, carSettingsIcon, carVerifiedAppIcon, carWashIcon, carWheelAppIcon, car_documents, car_error_app, car_insurance, car_pickup_service, car_plus, car_settings, car_verified_app, car_wheel_app, carwash, certifiedRepairIcon, certifiedRetailerIcon, certified_repair, certified_retailer, challengeAppIcon, challenge_app, charging, chargingIcon, chargingPduAppIcon, chargingStationIcon, chargingTarifOverviewAppIcon, charging_pdu_app, charging_station, charging_tarif_overview_app, chat, chatAppIcon, chatIcon, chat_app, checkboxCheckedAppIcon, checkboxCheckedIcon, checkboxUncheckedAppIcon, checkboxUncheckedIcon, checkbox_checked, checkbox_checked_app, checkbox_unchecked, checkbox_unchecked_app, checkmark, checkmarkAppIcon, checkmarkFilledAppIcon, checkmarkIcon, checkmark_app, checkmark_filled_app, chevronDownIcon, chevronLeftAlignedappIcon, chevronLeftIcon, chevronRightAlignedappIcon, chevronRightIcon, chevronSmallLeftAlignedappIcon, chevronSmallRightAlignedappIcon, chevronUpIcon, chevron_down, chevron_left, chevron_left_alignedapp, chevron_right, chevron_right_alignedapp, chevron_small_left_alignedapp, chevron_small_right_alignedapp, chevron_up, circleFilledIcon, circleHalfFilledIcon, circleQuarterFilledIcon, circleThreeQuartersFilledIcon, city, cityIcon, clearAppIcon, clearRightAlignedappIcon, clear_app, clear_right_alignedapp, clock, clockIcon, close, closeAppIcon, closeCircleIcon, closeIcon, closeLeftAlignedappIcon, closeRightAlignedappIcon, close_app, close_circle, close_left_alignedapp, close_right_alignedapp, clrIconSVG, coffeeFilledAppIcon, coffee_filled_app, compassAppIcon, compass_app, completedByDateIcon, configuratorCommercialIcon, configuratorPrivateIcon, configurator_commercial, configurator_private, construction, constructionIcon, consumptionFuelFilledAppIcon, consumptionIcon, consumption_fuel, consumption_fuel_filled_app, contact, contactDealerFilledAppIcon, contactDealerIcon, contactIcon, contact_dealer, contact_dealer_filled_app, countryRoadIcon, country_road, craft, craftIcon, cupraBrandIcon, customerIcon, customerVipIcon, customerWaitingIcon, customersCenterIcon, customers_center, dataCopyAppIcon, dataExpiredIcon, dataFilledIcon, dataInputIcon, dataPlugAppIcon, dataSearchIcon, dataTimeExtensionIcon, data_copy_app, data_expired, data_filled, data_input, data_plug_app, data_search, data_time_extension, defaultSummaryAreaCollapsedKey, defogDefrostAutoAppIcon, defogDefrostIcon, defog_defrost, defog_defrost_auto_app, deliveryDateIcon, destinationAppIcon, destination_app, dieselIcon, direction, directionIcon, dischargingAppIcon, discharging_app, discountAppIcon, discount_app, discoveryAppIcon, discovery_app, dollarBillForwardIcon, dollarBillPartialIcon, download, downloadCustomIcon, dragIndicatorIcon, drag_indicator, driversAssistanceIcon, drivers_assistance, dropFilledAppIcon, drop_filled_app, dwaBrandIcon, eco, ecoIcon, edit, editIcon, editSmallRightAlignedAppIcon, edit_small_right_aligned_app, efficiency, efficiencyIcon, electricCarsIcon, electricCarsServiceIcon, electric_cars, electric_cars_service, electricity, electricityFilledAppIcon, electricityIcon, electricity_filled_app, emergency, emergencyIcon, emission, emissionIcon, energyIcon, engine, engineIcon, entertainment, entertainmentIcon, escapeHtml, escapeRegex, exportAppIcon, export_app, expressServiceIcon, express_service, exterior, exterior360Icon, exteriorIcon, exterior_360, externalPartForwardIcon, faq, faqIcon, fastForwardIcon, fast_forward, fax, faxIcon, filter, filterIcon, findACarIcon, findADealerIcon, find_a_car, find_a_dealer, firstRegistrationDateIcon, fleetServiceCommercialIcon, fleetServicePrivateIcon, fleet_service_commercial, fleet_service_private, folderFilledAppIcon, folder_filled_app, foodFilledAppIcon, food_filled_app, formatNumber, fullscreenEnterIcon, fullscreenExitIcon, fullscreen_enter, fullscreen_exit, gallery, galleryIcon, garageAppIcon, garage_app, gasAppIcon, gasCarsServiceIcon, gasIcon, gas_app, glassDamageAppIcon, glass_damage_app, gte, gteIcon, heart, heartFilledAppIcon, heartIcon, heart_filled_app, heightAppIcon, height_app, highwayRoadIcon, highway_road, history, historyIcon, homeAppIcon, homeEnergyAppIcon, homeFilledAppIcon, home_app, home_energy_app, home_filled_app, hornAppIcon, hornFilledAppIcon, horn_app, horn_filled_app, hybrid, hybridIcon, immediateChargingAppIcon, immediate_charging_app, info, infoFilledIcon, infoIcon, info_filled, inputHideIcon, inputShowIcon, input_hide, input_show, interior, interior360Icon, interiorIcon, interior_360, internalPartForwardIcon, internet, internetIcon, invitationAppIcon, invitation_app, invoiceIcon, invoiceReadyIcon, invoiceRecipientIcon, itemsForwardIcon, itemsRecieveIcon, jobportal, jobportalIcon, keyAppIcon, keyCardAppIcon, keyDigitalAppIcon, key_app, key_card_app, key_digital_app, keyboardAppIcon, keyboard_app, layerCollapseAppIcon, layerExpandAppIcon, layer_collapse_app, layer_expand_app, layersAppIcon, layers_app, legalTermsAndConditionsAppIcon, legal_terms_and_conditions_app, licencePlateAppIcon, licence_plate_app, lightAssistappIcon, light_assistapp, lightingAppIcon, lighting_app, linkExternAppIcon, link_extern_app, list, listIcon, loadingVolumeIcon, loading_volume, localBusinessIcon, local_business, locate, locateIcon, lock, lockIcon, lockOpenIcon, lock_open, login, loginIcon, logistic, logisticIcon, logoCommissionModuleFavIcon, logoCommissionModuleIcon, logoCommissionModuleNegativeFavIcon, logoCommissionModuleNegativeIcon, logoCostApprovalFavIcon, logoCostApprovalIcon, logoCostApprovalNegativeFavIcon, logoCostApprovalNegativeIcon, logoCrossControllingFavIcon, logoCrossControllingIcon, logoCrossControllingNegativeFavIcon, logoCrossControllingNegativeIcon, logoDigitalServiceReceptionFavIcon, logoDigitalServiceReceptionIcon, logoDigitalServiceReceptionNegativeFavIcon, logoDigitalServiceReceptionNegativeIcon, logoDocFlowFavIcon, logoDocFlowIcon, logoDocFlowNegativeFavIcon, logoDocFlowNegativeIcon, logoDocScanFavIcon, logoDocScanIcon, logoDocScanNegativeFavIcon, logoDocScanNegativeIcon, logoDocStoreFavIcon, logoDocStoreIcon, logoDocStoreNegativeFavIcon, logoDocStoreNegativeIcon, logoEBillingFavIcon, logoEBillingIcon, logoEBillingNegativeFavIcon, logoEBillingNegativeIcon, logoEPaymentFavIcon, logoEPaymentIcon, logoEPaymentNegativeFavIcon, logoEPaymentNegativeIcon, logoMobilityPlannerFavIcon, logoMobilityPlannerIcon, logoMobilityPlannerNegativeFavIcon, logoMobilityPlannerNegativeIcon, logoPartsMobileFavIcon, logoPartsMobileIcon, logoPartsMobileNegativeFavIcon, logoPartsMobileNegativeIcon, logoSBOFavIcon, logoSBOIcon, logoSBONegativeFavIcon, logoSBONegativeIcon, logoServiceCubeFavIcon, logoServiceCubeIcon, logoServiceCubeNegativeFavIcon, logoServiceCubeNegativeIcon, logoWCPFavIcon, logoWCPIcon, logoWCPNegativeFavIcon, logoWCPNegativeIcon, logoWorkshopOrderTrackerFavIcon, logoWorkshopOrderTrackerIcon, logoWorkshopOrderTrackerNegativeFavIcon, logoWorkshopOrderTrackerNegativeIcon, logout, logoutIcon, magnifier, magnifierIcon, magnifierMinusIcon, magnifierPlusIcon, magnifier_minus, magnifier_plus, mail, mailIcon, mailResendAppIcon, mail_resend_app, manual, manualIcon, map, mapIcon, mapToInternalTree, mechanicIcon, media, mediaIcon, menu, menuAppAppIcon, menuIcon, menu_app_app, microphoneAppIcon, microphone_app, mobile, mobileIcon, moreAppIcon, moreAppbarAppIcon, more_app, more_appbar_app, mot, motIcon, motability, motabilityIcon, navigate, navigateFilledAppIcon, navigateIcon, navigate_filled_app, newCarCommercialIcon, newCarPrivateFilledAppIcon, newCarPrivateIcon, newCarUtilityVehicleIcon, new_car_commercial, new_car_private, new_car_private_filled_app, nightServiceIcon, night_service, notification, notificationFilledIcon, notificationIcon, notification_filled, number0Icon, number10Icon, number11Icon, number12Icon, number13Icon, number14Icon, number15Icon, number16Icon, number17Icon, number18Icon, number19Icon, number1Icon, number20Icon, number2Icon, number3Icon, number4Icon, number5Icon, number6Icon, number7Icon, number8Icon, number9Icon, offers, offersFilledAppIcon, offersIcon, offers_filled_app, officeAppIcon, officeFilledAppIcon, office_app, office_filled_app, oilLevelIcon, oilLevelWarningIcon, oilTemperatureAppIcon, oil_level, oil_level_warning, oil_temperature_app, onCallDutyIcon, on_call_duty, openSatIcon, open_sat, orderIcon, orderStatusIcon, paintMaterialForwardIcon, paintMaterialIcon, paintShopIcon, paint_shop, paragraphAppIcon, paragraph_app, parkHeaterAppIcon, park_heater_app, parking, parkingFilledAppIcon, parkingGarageAppIcon, parkingIcon, parkingLocationIcon, parkingRouteAppIcon, parkingValetAppIcon, parking_filled_app, parking_garage_app, parking_route_app, parking_valet_app, partAvailabilityInfoIcon, partAvailabilityNoIcon, partAvailabilityUnknownIcon, partAvailabilityWarningIcon, partAvailabilityYesIcon, partIdenticalIcon, partIdenticalPredecessorIcon, partIdenticalSuccessorIcon, partIdenticalSuccpredecessorIcon, partPredecessorIcon, partSuccessorIcon, partSuccessorPredecessorIcon, partsChangelocationIcon, partsForwardIcon, partsIcon, partsInventoryIcon, partsNonStockForwardIcon, partsNonStockIcon, partsPickingIcon, partsPickingPlusIcon, partsReceivingIcon, pause, pauseIcon, payload, payloadIcon, paymentAppIcon, paymentCashAppIcon, paymentChargingCardAppIcon, paymentCreditcardAppIcon, paymentMachineAppIcon, payment_app, payment_cash_app, payment_charging_card_app, payment_creditcard_app, payment_machine_app, performance, performanceIcon, petrol, petrolIcon, phone, phoneIcon, pin, pinFilledAppIcon, pinGenericFilledAppIcon, pinIcon, pin_filled_app, pin_generic_filled_app, play, playIcon, plugCcsAppIcon, plugChademoAppIcon, plugChargeAppIcon, plugGenericAppIcon, plugSchukoAppIcon, plugType1AppIcon, plugType2AppIcon, plug_ccs_app, plug_chademo_app, plug_charge_app, plug_generic_app, plug_schuko_app, plug_type1_app, plug_type2_app, plusServiceIcon, porscheBrandIcon, power, powerIcon, powerTrainIcon, powertrain, preHeaterAppIcon, pre_heater_app, preciseLaneNavigationAppIcon, precise_lane_navigation_app, presentAppIcon, present_app, priceTypeSwitchIcon, printer, printerIcon, privacyAppIcon, privacy_app, profile, profileIcon, profileRegisterAppIcon, profileVerifiedIcon, profile_register_app, profile_verified, publicServiceIcon, publicTransportAppIcon, public_service, public_transport_app, qualifiedWorkshopIcon, qualified_workshop, questionnaireAppIcon, questionnaire_app, radio, radioButtonInselectedIcon, radioButtonSelectedForDefIcon, radioButtonSelectedIcon, radioIcon, radio_button_inselected, radio_button_selected, radio_button_selected_for_development, range, rangeIcon, reload, reloadIcon, remove, removeIcon, repeat, repeatIcon, repeatRepairIcon, replacementVehicleIcon, returnDateIcon, rewind, rewindIcon, roadsideAssistanceIcon, roadside_assistance, route, routeArrowAppIcon, routeIcon, route_arrow_app, routesHistoryAppIcon, routes_history_app, rss, rssIcon, safety, safetyIcon, save, saveAppIcon, saveIcon, save_app, seat, seatAirIcon, seatBrandIcon, seatIcon, seat_air, secretTipAppIcon, secretTipFilledAppIcon, secret_tip_app, secret_tip_filled_app, selected, selectedIcon, selectedPartnerNetworkAppIcon, selected_partner_network_app, sendToCarAppIcon, send_to_car_app, service, serviceAdvisorIcon, serviceBellIcon, serviceFilledAppIcon, serviceIcon, service_bell, service_filled_app, settings, settingsIcon, shareAndroidIcon, shareIosIcon, share_android, share_ios, shoppingCartFilledAppIcon, shoppingCartIcon, shopping_cart, shopping_cart_filled_app, shuffle, shuffleIcon, size, sizeIcon, skillAppIcon, skill_app, skipBackwardIcon, skipForwardIcon, skip_backward, skip_forward, skodaBrandIcon, softwareDownloadAppIcon, software_download_app, sortingAppIcon, sorting_app, sound, soundIcon, standardEquipmentIcon, standard_equipment, starFilledIcon, starOutlineIcon, star_filled, star_outline, statisticAppIcon, statistic_app, stockLocatorCommercialIcon, stockLocatorPrivateIcon, stock_locator_commercial, stock_locator_private, stop, stopIcon, strip, switchPositionAppIcon, switch_position_app, syncAppIcon, sync_app, taskAndAppointmentIcon, taxiDealerIcon, taxi_dealer, technicalSpecificationIcon, technical_specification, temperatureAppIcon, temperature_app, testDriveIcon, test_drive, textForwardIcon, thumbsdownAppIcon, thumbsdownFilledAppIcon, thumbsdown_app, thumbsdown_filled_app, thumbsupAppIcon, thumbsupFilledAppIcon, thumbsup_app, thumbsup_filled_app, timeClimatisationAppIcon, timePreferredAppIcon, time_climatisation_app, time_preferred_app, timer, timerIcon, topcardIcon, touaregServiceIcon, transcriptDownloadIcon, transcript_download, transmissionAutomaticIcon, transmissionManualIcon, transmission_automatic, transmission_manual, tripAppIcon, tripLongAppIcon, tripPartedAppIcon, tripShortAppIcon, trip_app, trip_long_app, trip_parted_app, trip_short_app, turnSignalsIcon, turn_signals, unselected, unselectedIcon, updateRefreshAppIcon, update_refresh_app, upload, uploadAppIcon, uploadCustomIcon, upload_app, usedCarCommercialIcon, usedCarPrivateIcon, used_car_commercial, used_car_private, vehicleAmarokIcon, vehicleCaddyIcon, vehicleConversionIcon, vehicleCrafterIcon, vehicleHightIcon, vehicleIdBuzzIcon, vehicleMultivanIcon, vehicleTransporterIcon, vehicle_amarok, vehicle_caddy, vehicle_crafter, vehicle_hight, vehicle_idbuzz, vehicle_multivan, vehicle_transporter, videoChatIcon, video_chat, view360Icon, view_360, vinIcon, virtualRealityIcon, virtual_reality, voiceMessageAppIcon, voice_message_app, volkswagenAppIcon, volkswagenIcon, volkswagen_app, volumeMaximumIcon, volumeMediumIcon, volumeMuteIcon, volume_maximum, volume_medium, volume_mute, vsfSearch48Icon, vsfSearchIcon, vwBrandIcon, vwConnectLicenseAppIcon, vw_connect_license_app, vwnBrandIcon, walkingAppIcon, walkingFilledAppIcon, walking_app, walking_filled_app, wallbox, wallboxIcon, wcAppIcon, wc_app, wcpIcon, weAssistAppIcon, weChargeAppIcon, weDeliverAppIcon, weExperienceAppIcon, weParkAppIcon, weUpgradeAppIcon, we_assist_app, we_charge_app, we_deliver_app, we_experience_app, we_park_app, we_upgrade_app, weatherSunAppIcon, weather_sun_app, wheelToWheelIcon, wheel_to_wheel, windscreenWashIcon, windscreen_wash, wlanHotspotIcon, wlan_hotspot, wrenchForwardIcon };
|
|
19479
16995
|
//# sourceMappingURL=clr-addons.mjs.map
|