UNPKG

@porscheinformatik/clr-addons

Version:
2,719 lines 222 kB
import * as i0 from '@angular/core';
import { input, viewChild, computed, signal, Directive, ChangeDetectionStrategy, Component, Injectable, inject, output, Renderer2, DestroyRef, DOCUMENT, Inject, ElementRef, contentChild, NgModule } from '@angular/core';
import { format, select, scaleBand, scaleLinear, max, axisBottom, axisLeft, group, sum, scalePoint, min, line, curveMonotoneX, area, axisRight, arc, pie, color } from 'd3';
import * as i1 from '@clr/angular';
import { ClrAlertModule, ClrDropdownModule, ClrIcon, ClrSignpostContent, ClrStartDateInput, ClrEndDateInput, ClrSignpostModule } from '@clr/angular';
import * as i3 from '@clr/angular/icon';
import { ClarityIcons, downloadIcon } from '@clr/angular/icon';
import * as i2 from '@clr/angular/popover/common';
import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ReplaySubject, skip } from 'rxjs';
import { map, distinctUntilChanged, debounceTime } from 'rxjs/operators';
import * as i8 from '@angular/common';
import { CommonModule, DecimalPipe } from '@angular/common';

/*
 * Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
 * This software is released under MIT license.
 * The full license information can be found in LICENSE in the root directory of this project.
 */

const TOO_MANY_ITEMS_MESSAGE = 'too many items';
const TOO_MANY_ITEMS_GROUPED_MESSAGE = 'too many items';
const TOO_MANY_ITEMS_ALERT_TYPE = 'warning';
const NO_ITEMS_MESSAGE = 'no items';
const NO_ITEMS_ALERT_TYPE = 'info';

/**
 * Converts a chart color value to a CSS-compatible string.
 *
 * Supports:
 *  - Hex values:        '#e57200'                        → returned as-is
 *  - CSS custom props:  '--cds-global-color-lavender-1000' → wrapped in var(...)
 *  - Any other string:  'rgb(...)' / 'hsl(...)'          → returned as-is
 */
function toChartColor(color) {
    if (!color) {
        return '';
    }
    return color.startsWith('--') ? `var(${color})` : color;
}

class TextRenderer {
    constructor() {
        this.cachedFont = '';
        this.cachedEllipsisWidth = 0;
        this.canvas = document.createElement('canvas');
        const ctx = this.canvas.getContext('2d');
        if (!ctx) {
            throw new Error('2D context error');
        }
        this.context = ctx;
    }
    render(text, availableHeight, availableWidth, fontSize = '12px', fontFamily = 'Arial') {
        const font = `${fontSize} ${fontFamily}`;
        if (this.cachedFont !== font) {
            this.context.font = font;
            this.cachedEllipsisWidth = this.context.measureText('...').width;
            this.cachedFont = font;
        }
        else {
            this.context.font = font;
        }
        const textMetrics = this.context.measureText(text);
        const textWidth = textMetrics.width;
        const textHeight = Math.abs(textMetrics.actualBoundingBoxAscent) + Math.abs(textMetrics.actualBoundingBoxDescent);
        if (textWidth <= availableWidth && textHeight <= availableHeight) {
            return text;
        }
        let remainingWidth = availableWidth - this.cachedEllipsisWidth;
        let truncatedText = '';
        for (const char of text) {
            const charWidth = this.context.measureText(char).width;
            if (remainingWidth - charWidth >= 0) {
                truncatedText += char;
                remainingWidth -= charWidth;
            }
            else {
                break;
            }
        }
        truncatedText = truncatedText.trim();
        if (truncatedText.length === 0) {
            return '';
        }
        return truncatedText + '...';
    }
}

const percentage = (value, total) => {
    if (total <= 0) {
        return 0;
    }
    return Math.min(100, Math.max(0, Math.round((value / total) * 100)));
};
const d3percentFormat = (f => (d) => `${f(d)}%`)(format('.1f'));

/*
 * Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
 * This software is released under MIT license.
 * The full license information can be found in LICENSE in the root directory of this project.
 */
/**
 * Abstract base class shared by all chart components.
 * ...
 */
class ChartBase {
    constructor() {
        // ── Common Input ───────────────────────────────────────────────────────────
        /** Whether the chart is in loading state (shows skeleton). */
        this.loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
        // ── View references ────────────────────────────────────────────────────────
        /** Reference to the `<svg #chart>` element in the component template. */
        this.chartRef = viewChild('chart', ...(ngDevMode ? [{ debugName: "chartRef" }] : /* istanbul ignore next */ []));
        /** Reference to the `<div #container>` host element in the component template. */
        this.containerRef = viewChild('container', ...(ngDevMode ? [{ debugName: "containerRef" }] : /* istanbul ignore next */ []));
        /** Exposes the raw SVG element for the chart-export button. */
        this.svgElement = computed(() => this.chartRef()?.nativeElement, ...(ngDevMode ? [{ debugName: "svgElement" }] : /* istanbul ignore next */ []));
        // ── Tooltip State ──────────────────────────────────────────────────────────
        this.selectedItem = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedItem" }] : /* istanbul ignore next */ []));
        this.tooltipPosition = signal(undefined, ...(ngDevMode ? [{ debugName: "tooltipPosition" }] : /* istanbul ignore next */ []));
    }
    // ── Lifecycle ──────────────────────────────────────────────────────────────
    /** Schedules the first render after the view is ready. */
    ngAfterViewInit() {
        requestAnimationFrame(() => this.updateChart());
    }
    // ── Common Implementations ─────────────────────────────────────────────────
    /** Returns the pixel dimensions of the container div. */
    getContainerDimensions() {
        const el = this.containerRef().nativeElement;
        return { width: el.clientWidth, height: el.clientHeight };
    }
    /**
     * Clears the active tooltip.  Override in subclasses that have additional
     * selection state
     */
    resetTooltip() {
        if (this.selectedItem() || this.tooltipPosition()) {
            this.selectedItem.set(undefined);
            this.tooltipPosition.set(undefined);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.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 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartBase, decorators: [{
            type: Directive
        }], propDecorators: { loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], chartRef: [{ type: i0.ViewChild, args: ['chart', { isSignal: true }] }], containerRef: [{ type: i0.ViewChild, args: ['container', { isSignal: true }] }] } });

class ChartAlertOverlayComponent {
    constructor() {
        this.alertMessage = input.required(...(ngDevMode ? [{ debugName: "alertMessage" }] : /* istanbul ignore next */ []));
        this.alertType = input('info', ...(ngDevMode ? [{ debugName: "alertType" }] : /* istanbul ignore next */ []));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartAlertOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.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: `
    <clr-alert [clrAlertSizeSmall]="true" [clrAlertClosable]="false" [clrAlertType]="alertType()">
      <clr-alert-item>
        <span class="alert-text">
          {{ alertMessage() }}
        </span>
      </clr-alert-item>
    </clr-alert>
    <div class="overlay"></div>
  `, isInline: true, styles: [":host{position:absolute;top:0;left:0;right:0;pointer-events:none;z-index:1}:host clr-alert{pointer-events:auto}:host clr-alert ::ng-deep .alert{margin-bottom:0}:host .overlay{height:2rem;background:linear-gradient(to bottom,#fff,#fff0)}\n"], dependencies: [{ kind: "ngmodule", type: ClrAlertModule }, { kind: "component", type: i1.ClrAlert, selector: "clr-alert", inputs: ["clrAlertSizeSmall", "clrAlertClosable", "clrAlertAppLevel", "clrCloseButtonAriaLabel", "clrAlertLightweight", "clrAlertType", "clrAlertIcon", "clrAlertClosed"], outputs: ["clrAlertClosedChange"] }, { kind: "component", type: i1.ClrAlertItem, selector: "clr-alert-item" }, { kind: "directive", type: i1.ClrAlertText, selector: ".alert-text" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartAlertOverlayComponent, decorators: [{
            type: Component,
            args: [{ selector: 'cng-chart-alert-overlay', template: `
    <clr-alert [clrAlertSizeSmall]="true" [clrAlertClosable]="false" [clrAlertType]="alertType()">
      <clr-alert-item>
        <span class="alert-text">
          {{ alertMessage() }}
        </span>
      </clr-alert-item>
    </clr-alert>
    <div class="overlay"></div>
  `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ClrAlertModule], styles: [":host{position:absolute;top:0;left:0;right:0;pointer-events:none;z-index:1}:host clr-alert{pointer-events:auto}:host clr-alert ::ng-deep .alert{margin-bottom:0}:host .overlay{height:2rem;background:linear-gradient(to bottom,#fff,#fff0)}\n"] }]
        }], propDecorators: { alertMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "alertMessage", required: true }] }], alertType: [{ type: i0.Input, args: [{ isSignal: true, alias: "alertType", required: false }] }] } });

// ── Legend layout constants (mirror chart-legend.component styles) ─────────────
const LEGEND_PADDING_TOP = 12;
const LEGEND_PADDING_H = 4;
const LEGEND_ITEM_HEIGHT = 22;
const LEGEND_COLOR_SIZE = 10;
const LEGEND_FONT_SIZE = 11;
const LEGEND_TEXT_COLOR = '#666666';
/** Approximate pixel width reserved per legend item (color square + gap + label). */
const LEGEND_APPROX_ITEM_WIDTH = 130;
class ChartExportService {
    exportSvg(svgEl, filename, legendItems) {
        const root = this.buildExportSvg(svgEl, legendItems);
        const svgStr = new XMLSerializer().serializeToString(root);
        this.download(new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' }), `${filename}.svg`);
    }
    exportPng(svgEl, filename, legendItems) {
        const root = this.buildExportSvg(svgEl, legendItems);
        this.toCanvas(root).then(canvas => canvas.toBlob(blob => this.download(blob, `${filename}.png`), 'image/png'));
    }
    // ── Core builder ──────────────────────────────────────────────────────────────
    buildExportSvg(svgEl, legendItems) {
        const w = svgEl.clientWidth || Number(svgEl.getAttribute('width')) || 800;
        const chartH = svgEl.clientHeight || Number(svgEl.getAttribute('height')) || 600;
        const fontFamily = this.getDocumentFontFamily();
        const itemsToRender = legendItems?.length ? legendItems : [];
        const legendH = itemsToRender.length
            ? LEGEND_PADDING_TOP +
                Math.ceil(itemsToRender.length / this.legendItemsPerRow(w, itemsToRender)) * LEGEND_ITEM_HEIGHT
            : 0;
        const totalH = chartH + legendH;
        const ns = 'http://www.w3.org/2000/svg';
        // Root SVG
        const root = document.createElementNS(ns, 'svg');
        root.setAttribute('xmlns', ns);
        root.setAttribute('width', String(w));
        root.setAttribute('height', String(totalH));
        root.style.background = '#ffffff';
        if (fontFamily) {
            root.setAttribute('font-family', fontFamily);
        }
        // Chart contents – clone, resolve CSS vars, inline fonts, then wrap in <g>
        const chartClone = svgEl.cloneNode(true);
        chartClone.querySelectorAll('.domain').forEach(el => el.remove());
        this.resolveStyleVariables(chartClone);
        this.inlineFontFamily(chartClone, fontFamily);
        const chartGroup = document.createElementNS(ns, 'g');
        Array.from(chartClone.childNodes).forEach(n => chartGroup.appendChild(n.cloneNode(true)));
        root.appendChild(chartGroup);
        // Legend group below the chart
        if (itemsToRender.length) {
            root.appendChild(this.buildLegendGroup(itemsToRender, w, chartH, fontFamily));
        }
        return root;
    }
    // ── Legend SVG builder ────────────────────────────────────────────────────────
    buildLegendGroup(items, width, offsetY, fontFamily) {
        const ns = 'http://www.w3.org/2000/svg';
        const g = document.createElementNS(ns, 'g');
        g.setAttribute('transform', `translate(0,${offsetY + LEGEND_PADDING_TOP})`);
        const perRow = this.legendItemsPerRow(width, items);
        const colWidth = Math.floor((width - 2 * LEGEND_PADDING_H) / perRow);
        let col = 0;
        let row = 0;
        for (const item of items) {
            const x = LEGEND_PADDING_H + col * colWidth;
            const y = row * LEGEND_ITEM_HEIGHT;
            // Color square
            const rect = document.createElementNS(ns, 'rect');
            rect.setAttribute('x', String(x));
            rect.setAttribute('y', String(y));
            rect.setAttribute('width', String(LEGEND_COLOR_SIZE));
            rect.setAttribute('height', String(LEGEND_COLOR_SIZE));
            rect.setAttribute('rx', '2');
            rect.setAttribute('ry', '2');
            rect.setAttribute('fill', this.resolveColor(item.color));
            g.appendChild(rect);
            // Label text – vertically centred with the square
            const text = document.createElementNS(ns, 'text');
            text.setAttribute('x', String(x + LEGEND_COLOR_SIZE + 5));
            text.setAttribute('y', String(y + LEGEND_COLOR_SIZE - 1));
            text.setAttribute('font-size', String(LEGEND_FONT_SIZE));
            text.setAttribute('fill', LEGEND_TEXT_COLOR);
            if (fontFamily) {
                text.setAttribute('font-family', fontFamily);
            }
            text.textContent = item.label;
            g.appendChild(text);
            col++;
            if (col >= perRow) {
                col = 0;
                row++;
            }
        }
        return g;
    }
    legendItemsPerRow(width, items) {
        const usable = width - 2 * LEGEND_PADDING_H;
        const perRow = Math.max(1, Math.floor(usable / LEGEND_APPROX_ITEM_WIDTH));
        return Math.min(perRow, items.length);
    }
    // ── Canvas / PNG ──────────────────────────────────────────────────────────────
    toCanvas(svgEl) {
        return new Promise(resolve => {
            const w = Number(svgEl.getAttribute('width')) || 800;
            const h = Number(svgEl.getAttribute('height')) || 600;
            const url = URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(svgEl)], { type: 'image/svg+xml;charset=utf-8' }));
            const img = new Image();
            img.onload = () => {
                const scale = 2; // 2× for retina quality
                const canvas = document.createElement('canvas');
                canvas.width = w * scale;
                canvas.height = h * scale;
                const ctx = canvas.getContext('2d');
                ctx.scale(scale, scale);
                ctx.fillStyle = '#ffffff';
                ctx.fillRect(0, 0, w, h);
                ctx.drawImage(img, 0, 0, w, h);
                URL.revokeObjectURL(url);
                resolve(canvas);
            };
            img.src = url;
        });
    }
    // ── CSS variable resolution ───────────────────────────────────────────────────
    /** Recursively resolves `var(--x)` references in inline style and presentation attributes. */
    resolveStyleVariables(el) {
        const style = el.getAttribute('style');
        if (style) {
            el.setAttribute('style', this.resolveVarsInString(style));
        }
        for (const attr of ['fill', 'stroke', 'color', 'background-color']) {
            const val = el.getAttribute(attr);
            if (val) {
                el.setAttribute(attr, this.resolveVarsInString(val));
            }
        }
        // Also resolve CSS var() in inline style properties directly set via D3 .style()
        const inlineStyle = el.style;
        if (inlineStyle) {
            const fill = inlineStyle.fill;
            if (fill?.includes('var(')) {
                inlineStyle.fill = this.resolveVarsInString(fill);
            }
            const stroke = inlineStyle.stroke;
            if (stroke?.includes('var(')) {
                inlineStyle.stroke = this.resolveVarsInString(stroke);
            }
        }
        Array.from(el.children).forEach(child => this.resolveStyleVariables(child));
    }
    /** Inlines `font-family` on every `<text>` / `<tspan>` element that doesn't already have one. */
    inlineFontFamily(el, fontFamily) {
        if (!fontFamily) {
            return;
        }
        if (el.tagName === 'text' || el.tagName === 'tspan') {
            if (!el.getAttribute('font-family')) {
                el.setAttribute('font-family', fontFamily);
            }
        }
        Array.from(el.children).forEach(child => this.inlineFontFamily(child, fontFamily));
    }
    resolveVarsInString(value) {
        return value.replace(/var\(\s*(--[^,)]+?)\s*(?:,\s*([^)]+?))?\s*\)/g, (_match, varName, fallback) => {
            const computed = getComputedStyle(document.documentElement).getPropertyValue(varName.trim()).trim();
            return computed || fallback?.trim() || '';
        });
    }
    resolveColor(color) {
        if (!color) {
            return '#cccccc';
        }
        if (color.startsWith('--')) {
            const computed = getComputedStyle(document.documentElement).getPropertyValue(color).trim();
            return computed || '#cccccc';
        }
        // Handle var(...) wrapper
        if (color.startsWith('var(')) {
            return this.resolveVarsInString(color) || '#cccccc';
        }
        return color;
    }
    getDocumentFontFamily() {
        return getComputedStyle(document.body).fontFamily || 'sans-serif';
    }
    // ── Download ──────────────────────────────────────────────────────────────────
    download(blob, filename) {
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        a.click();
        setTimeout(() => URL.revokeObjectURL(url), 100);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartExportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartExportService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartExportService, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }] });

ClarityIcons.addIcons(downloadIcon);
class ChartExportButtonComponent {
    constructor() {
        this.svgRef = input(undefined, ...(ngDevMode ? [{ debugName: "svgRef" }] : /* istanbul ignore next */ []));
        this.filename = input('chart', ...(ngDevMode ? [{ debugName: "filename" }] : /* istanbul ignore next */ []));
        this.buttonTitle = input('Export', ...(ngDevMode ? [{ debugName: "buttonTitle" }] : /* istanbul ignore next */ []));
        /** Legend items to include below the chart in the exported file. */
        this.legendItems = input(undefined, ...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
        this.exportService = inject(ChartExportService);
    }
    export(format) {
        const svg = this.svgRef();
        if (!svg) {
            return;
        }
        switch (format) {
            case 'svg':
                this.exportService.exportSvg(svg, this.filename(), this.legendItems());
                break;
            case 'png':
                this.exportService.exportPng(svg, this.filename(), this.legendItems());
                break;
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartExportButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.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 }, buttonTitle: { classPropertyName: "buttonTitle", publicName: "buttonTitle", isSignal: true, isRequired: false, transformFunction: null }, legendItems: { classPropertyName: "legendItems", publicName: "legendItems", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
    <clr-dropdown>
      <button class="btn btn-sm btn-icon btn-link export-trigger" clrDropdownTrigger title="Export chart">
        <cds-icon shape="download" size="16"></cds-icon>
        {{ buttonTitle() }}
      </button>
      <clr-dropdown-menu *clrIfOpen clrPosition="bottom-left">
        <button clrDropdownItem (click)="export('svg')">SVG</button>
        <button clrDropdownItem (click)="export('png')">PNG</button>
      </clr-dropdown-menu>
    </clr-dropdown>
  `, isInline: true, styles: [":host{position:absolute;top:15px;right:15px;z-index:10;opacity:1;pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: ClrDropdownModule }, { kind: "component", type: i1.ClrDropdown, selector: "clr-dropdown", inputs: ["clrCloseMenuOnItemClick"] }, { kind: "component", type: i1.ClrDropdownMenu, selector: "clr-dropdown-menu", inputs: ["clrPosition"] }, { kind: "directive", type: i1.ClrDropdownTrigger, selector: "[clrDropdownTrigger],[clrDropdownToggle]" }, { kind: "directive", type: i1.ClrDropdownItem, selector: "[clrDropdownItem]", inputs: ["clrDisabled", "id"] }, { kind: "directive", type: i2.ClrIfOpen, selector: "[clrIfOpen]", inputs: ["clrIfOpen"], outputs: ["clrIfOpenChange"] }, { kind: "component", type: i3.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartExportButtonComponent, decorators: [{
            type: Component,
            args: [{ selector: 'cng-chart-export-button', template: `
    <clr-dropdown>
      <button class="btn btn-sm btn-icon btn-link export-trigger" clrDropdownTrigger title="Export chart">
        <cds-icon shape="download" size="16"></cds-icon>
        {{ buttonTitle() }}
      </button>
      <clr-dropdown-menu *clrIfOpen clrPosition="bottom-left">
        <button clrDropdownItem (click)="export('svg')">SVG</button>
        <button clrDropdownItem (click)="export('png')">PNG</button>
      </clr-dropdown-menu>
    </clr-dropdown>
  `, imports: [ClrDropdownModule, ClrIcon], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{position:absolute;top:15px;right:15px;z-index:10;opacity:1;pointer-events:auto}\n"] }]
        }], propDecorators: { svgRef: [{ type: i0.Input, args: [{ isSignal: true, alias: "svgRef", required: false }] }], filename: [{ type: i0.Input, args: [{ isSignal: true, alias: "filename", required: false }] }], buttonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonTitle", required: false }] }], legendItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "legendItems", required: false }] }] } });

class ChartLegendComponent {
    constructor() {
        this.items = input.required(...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
        this.toChartColor = toChartColor;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartLegendComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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: `
    <div class="chart-legend">
      @for (item of items(); track item.label) {
        <div class="legend-item">
          <span class="legend-color-square" [style.background-color]="toChartColor(item.color)"></span>
          <span class="legend-label">{{ item.label }}</span>
        </div>
      }
    </div>
  `, 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(--cds-global-color-construction-400, #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 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartLegendComponent, decorators: [{
            type: Component,
            args: [{ selector: 'cng-chart-legend', template: `
    <div class="chart-legend">
      @for (item of items(); track item.label) {
        <div class="legend-item">
          <span class="legend-color-square" [style.background-color]="toChartColor(item.color)"></span>
          <span class="legend-label">{{ item.label }}</span>
        </div>
      }
    </div>
  `, 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(--cds-global-color-construction-400, #666)}.legend-item{display:flex;align-items:center;gap:.35rem}.legend-color-square{width:10px;height:10px;border-radius:2px;flex-shrink:0}\n"] }]
        }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }] } });

class ChartSkeletonComponent {
    constructor() {
        this.skeletonType = input('loading', ...(ngDevMode ? [{ debugName: "skeletonType" }] : /* istanbul ignore next */ []));
        this.orientation = input.required(...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
        this.animationStyle = computed(() => (this.skeletonType() === 'loading' ? 'pulse' : undefined), ...(ngDevMode ? [{ debugName: "animationStyle" }] : /* istanbul ignore next */ []));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartSkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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", "size", "measureUnit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartSkeletonComponent, decorators: [{
            type: Component,
            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"] }]
        }], propDecorators: { skeletonType: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonType", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: true }] }] } });

class ChartTooltipComponent {
    constructor() {
        this.tooltipPosition = input.required(...(ngDevMode ? [{ debugName: "tooltipPosition" }] : /* istanbul ignore next */ []));
        this.tooltipOrientation = input('top', ...(ngDevMode ? [{ debugName: "tooltipOrientation" }] : /* istanbul ignore next */ []));
        this.squareColor = input.required(...(ngDevMode ? [{ debugName: "squareColor" }] : /* istanbul ignore next */ []));
        this.tooltipClickable = input(true, ...(ngDevMode ? [{ debugName: "tooltipClickable" }] : /* istanbul ignore next */ []));
        this.tooltipClosed = output();
        this.tooltipHeaderClicked = output();
        this.toChartColor = toChartColor;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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(--cds-global-color-gray-0, #fff);border:1px solid var(--cds-global-color-gray-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(--cds-global-color-gray-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(--cds-global-color-gray-700, #565656)}hr:has(+.tooltip-footer:empty){display:none}hr{margin:6px -16px}\n"], dependencies: [{ kind: "component", type: ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ChartTooltipComponent, decorators: [{
            type: Component,
            args: [{ selector: 'cng-chart-tooltip', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ClrIcon], 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(--cds-global-color-gray-0, #fff);border:1px solid var(--cds-global-color-gray-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(--cds-global-color-gray-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(--cds-global-color-gray-700, #565656)}hr:has(+.tooltip-footer:empty){display:none}hr{margin:6px -16px}\n"] }]
        }], propDecorators: { tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: true }] }], tooltipOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipOrientation", required: false }] }], squareColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "squareColor", required: true }] }], tooltipClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipClickable", required: false }] }], tooltipClosed: [{ type: i0.Output, args: ["tooltipClosed"] }], tooltipHeaderClicked: [{ type: i0.Output, args: ["tooltipHeaderClicked"] }] } });

// Clarity actually has an "outside-click" directive, but they don't export it 🤷
class OutsideClickDirective {
    constructor() {
        this.outsideClick = output({ alias: 'cngOutsideClick' });
        this.renderer = inject(Renderer2);
        this.destroyRef = inject(DestroyRef);
    }
    ngAfterViewInit() {
        const listener = this.renderer.listen('document', 'click', () => this.outsideClick.emit());
        this.destroyRef.onDestroy(listener);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: OutsideClickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.18", type: OutsideClickDirective, isStandalone: true, selector: "[cngOutsideClick]", outputs: { outsideClick: "cngOutsideClick" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: OutsideClickDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[cngOutsideClick]',
                }]
        }], propDecorators: { outsideClick: [{ type: i0.Output, args: ["cngOutsideClick"] }] } });

/**
 * Enum for the Screen size in pixels, defined by clarity here https://vmware.github.io/clarity/documentation/v0.12/grid
 * We consider screens above 1200px to be a desktop, between 576px and 1199px to be a tablet and below 576px to be a phone.
 */
var ScreenWidth;
(function (ScreenWidth) {
    ScreenWidth[ScreenWidth["EXTRA_LARGE"] = 1200] = "EXTRA_LARGE";
    ScreenWidth[ScreenWidth["LARGE"] = 992] = "LARGE";
    ScreenWidth[ScreenWidth["MEDIUM"] = 768] = "MEDIUM";
    ScreenWidth[ScreenWidth["SMALL"] = 576] = "SMALL";
    ScreenWidth[ScreenWidth["EXTRA_SMALL"] = 575] = "EXTRA_SMALL";
})(ScreenWidth || (ScreenWidth = {}));
var ScreenOrientation;
(function (ScreenOrientation) {
    ScreenOrientation["PORTRAIT"] = "PORTRAIT";
    ScreenOrientation["LANDSCAPE"] = "LANDSCAPE";
})(ScreenOrientation || (ScreenOrientation = {}));
const DATEPICKER_ENABLE_BREAKPOINT = 768;
const MOBILE_USERAGENT_REGEX = /Mobi/i;
class ScreenStateService {
    constructor(_document) {
        this._document = _document;
        this.isUserAgentMobile = false;
        this.screenWidthSubject = new ReplaySubject(1);
        this.screenOrientationSubject = new ReplaySubject(1);
        window.addEventListener('resize', () => {
            this.onResize();
        });
        window.addEventListener('orientationchange', () => {
            this.onOrientationChange();
        });
        this.determineMobileUserAgent();
        // Execute them once for initial values
        this.onResize();
        this.onOrientationChange();
    }
    getScreenWidthChanged() {
        return this.screenWidthSubject;
    }
    getMobileStateChanged() {
        return this.getScreenWidthChanged().pipe(map(screenWidth => screenWidth < DATEPICKER_ENABLE_BREAKPOINT && this.isUserAgentMobile), distinctUntilChanged());
    }
    getScreenOrientationChanged() {
        return this.screenOrientationSubject;
    }
    onResize() {
        const windowWidth = window.innerWidth;
        this.screenWidthSubject.next(windowWidth);
    }
    onOrientationChange() {
        const prevOrientation = this.screenOrientation;
        const orientation = window.orientation;
        // Probably not always correct since this depends on the devices default orientation.
        // Better way to identify this would probably be comparing height & width of the screen.
        if (orientation === -90 || orientation === 90) {
            this.screenOrientation = ScreenOrientation.LANDSCAPE;
        }
        else {
            this.screenOrientation = ScreenOrientation.PORTRAIT;
        }
        if (this.screenOrientation !== prevOrientation) {
            this.screenOrientationSubject.next(this.screenOrientation);
        }
    }
    determineMobileUserAgent() {
        if (this._document) {
            this.isUserAgentMobile = MOBILE_USERAGENT_REGEX.test(this._document.defaultView.navigator.userAgent);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ScreenStateService, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ScreenStateService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ScreenStateService, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }], ctorParameters: () => [{ type: Document, decorators: [{
                    type: Inject,
                    args: [DOCUMENT]
                }] }] });

class WindowResizeDirective {
    constructor() {
        this.debounce = input(250, ...(ngDevMode ? [{ debugName: "debounce" }] : /* istanbul ignore next */ []));
        this.includeFirst = input(false, ...(ngDevMode ? [{ debugName: "includeFirst" }] : /* istanbul ignore next */ []));
        this.windowResize = output({ alias: 'cngWindowResize' });
        this.screenStateService = inject(ScreenStateService);
        this.destroyRef = inject(DestroyRef);
    }
    ngOnInit() {
        const skipCount = this.includeFirst() ? 0 : 1;
        this.screenStateService
            .getScreenWidthChanged()
            .pipe(takeUntilDestroyed(this.destroyRef), skip(skipCount), debounceTime(this.debounce()))
            .subscribe(width => this.windowResize.emit(width));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: WindowResizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.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 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: WindowResizeDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[cngWindowResize]',
                }]
        }], propDecorators: { debounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounce", required: false }] }], includeFirst: [{ type: i0.Input, args: [{ isSignal: true, alias: "includeFirst", required: false }] }], windowResize: [{ type: i0.Output, args: ["cngWindowResize"] }] } });

class BarChartComponent extends ChartBase {
    static { this.HORIZONTAL_BAR_MIN_HEIGHT_PX = 25; }
    constructor() {
        super();
        this.data = input.required(...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
        this.stacks = input(undefined, ...(ngDevMode ? [{ debugName: "stacks" }] : /* istanbul ignore next */ []));
        this.orientation = input.required(...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
        this.tooltipOrientation = input('top', ...(ngDevMode ? [{ debugName: "tooltipOrientation" }] : /* istanbul ignore next */ []));
        this.barSizePx = input(15, ...(ngDevMode ? [{ debugName: "barSizePx" }] : /* istanbul ignore next */ []));
        this.barAreaSizePx = input(40, ...(ngDevMode ? [{ debugName: "barAreaSizePx" }] : /* istanbul ignore next */ []));
        this.tooltipPercentOfTotal = input('of total', ...(ngDevMode ? [{ debugName: "tooltipPercentOfTotal" }] : /* istanbul ignore next */ []));
        this.tooltipPercentOf = input('of', ...(ngDevMode ? [{ debugName: "tooltipPercentOf" }] : /* istanbul ignore next */ []));
        this.noItemsMessage = input(NO_ITEMS_MESSAGE, ...(ngDevMode ? [{ debugName: "noItemsMessage" }] : /* istanbul ignore next */ []));
        this.tooManyItemsMessage = input(TOO_MANY_ITEMS_MESSAGE, ...(ngDevMode ? [{ debugName: "tooManyItemsMessage" }] : /* istanbul ignore next */ []));
        this.tooManyItemsGroupedMessage = input(TOO_MANY_ITEMS_GROUPED_MESSAGE, ...(ngDevMode ? [{ debugName: "tooManyItemsGroupedMessage" }] : /* istanbul ignore next */ []));
        this.showLegend = input(true, ...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
        this.showExportButton = input(false, ...(ngDevMode ? [{ debugName: "showExportButton" }] : /* istanbul ignore next */ []));
        this.exportButtonTitle = input('Export', ...(ngDevMode ? [{ debugName: "exportButtonTitle" }] : /* istanbul ignore next */ []));
        this.exportFilename = input('bar-chart', ...(ngDevMode ? [{ debugName: "exportFilename" }] : /* istanbul ignore next */ []));
        /** Optional label rendered below the X axis. */
        this.xAxisLabel = input('', ...(ngDevMode ? [{ debugName: "xAxisLabel" }] : /* istanbul ignore next */ []));
        /** Optional label rendered rotated to the left of the Y axis. */
        this.yAxisLabel = input('', ...(ngDevMode ? [{ debugName: "yAxisLabel" }] : /* istanbul ignore next */ []));
        this.valueClicked = output();
        this.textRenderer = new TextRenderer();
        this.MARGIN = { top: 10, right: 20, bottom: 30, left: 65 };
        this.toChartColor = toChartColor;
        this.total = computed(() => this.data().reduce((acc, v) => acc + v.value, 0), ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
        this.totalByStack = computed(() => {
            const totals = {};
            for (const d of this.slicedDataPoints()) {
                totals[d.stackKey] = (totals[d.stackKey] ?? 0) + d.value;
            }
            return totals;
        }, ...(ngDevMode ? [{ debugName: "totalByStack" }] : /* istanbul ignore next */ []));
        this.keysByStack = computed(() => {
            const keys = {};
            for (const d of this.slicedDataPoints()) {
                if (!keys[d.stackKey]) {
                    keys[d.stackKey] = [];
                }
                keys[d.stackKey].push(d.key);
            }
            return keys;
        }, ...(ngDevMode ? [{ debugName: "keysByStack" }] : /* istanbul ignore next */ []));
        // Count the total amount of bars by either the stacKey (if stacked) or the key (if not stacked)
        this.totalBarCount = computed(() => new Set(this.data()
            .filter(d => d.value > 0)
            .map(d => d.stackKey ?? d.key)).size, ...(ngDevMode ? [{ debugName: "totalBarCount" }] : /* istanbul ignore next */ []));
        this.showingBarCount = computed(() => new Set(this.slicedDataPoints()
            .filter(d => d.value > 0)
            .map(d => d.stackKey)).size, ...(ngDevMode ? [{ debugName: "showingBarCount" }] : /* istanbul ignore next */ []));
        this.alertMessageAndType = computed(() => {
            if (this.loading()) {
                return undefined;
            }
            if (!this.showingBarCount()) {
                return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
            }
            else if (this.totalBarCount() !== this.showingBarCount()) {
                return [
                    this.stacks() ? this.tooManyItemsGroupedMessage() : this.tooManyItemsMessage(),
                    TOO_MANY_ITEMS_ALERT_TYPE,
                ];
            }
            return undefined;
        }, ...(ngDevMode ? [{ debugName: "alertMessageAndType" }] : /* istanbul ignore next */ []));
        this.legendItems = computed(() => {
            if (!this.showLegend() || !this.data()?.length) {
                return [];
            }
            if (this.stacks()?.length) {
                // Stacked: one legend entry per distinct label (= each layer in the stack)
                const seen = new Set();
                const items = [];
                for (const item of this.data()) {
                    const label = item.fullLabel ?? item.label;
                    if (!seen.has(label)) {
                        seen.add(label);
                        items.push({ label, color: item.color });
                    }
                }
                return items;
            }
            // Non-stacked: one entry per bar
            return this.data().map(item => ({ label: item.fullLabel ?? item.label, color: item.color }));
        }, ...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
        /** Computed values used by the tooltip to avoid inline logic in the template. */
        this.tooltipKey = computed(() => {
            const item = this.selectedItem();
            if (!item) {
                return undefined;
            }
            return this.stacks()?.length ? (this.keysByStack()[item.stackKey] ?? []) : [item.key];
        }, ...(ngDevMode ? [{ debugName: "tooltipKey" }] : /* istanbul ignore next */ []));
        this.tooltipLabel = computed(() => {
            const item = this.selectedItem();
            if (!item) {
                return undefined;
            }
            return this.stacks()?.find(stack => stack.stackKey === item.stackKey)?.label ?? item.fullLabel ?? item.label;
        }, ...(ngDevMode ? [{ debugName: "tooltipLabel" }] : /* istanbul ignore next */ []));
        this.tooltipValue = computed(() => {
            const item = this.selectedItem();
            if (!item) {
                return undefined;
            }
            return this.stacks()?.length ? (this.totalByStack()[item.stackKey] ?? 0) : item.value;
        }, ...(ngDevMode ? [{ debugName: "tooltipValue" }] : /* istanbul ignore next */ []));
        /** Slices belonging to the currently selected stack – used by the tooltip @for loop. */
        this.selectedStackSlices = computed(() => {
            const item = this.selectedItem();
            if (!item || !this.stacks()?.length) {
                return [];
            }
            return this.slicedDataPoints().filter(d => d.stackKey === item.stackKey);
        }, ...(ngDevMode ? [{ debugName: "selectedStackSlices" }] : /* istanbul ignore next */ []));
        this.maxAmountOfItems = signal(undefined, ...(ngDevMode ? [{ debugName: "maxAmountOfItems" }] : /* istanbul ignore next */ []));
        this.slicedDataPoints = signal([], ...(ngDevMode ? [{ debugName: "slicedDataPoints" }] : /* istanbul ignore next */ []));
        this.barSelection = null;
        this.labelSelection = null;
    }
    ngOnChanges(_changes) {
        if (!this.svg) {
            return;
        }
        requestAnimationFrame(() => this.updateChart());
    }
    ngAfterViewInit() {
        this.createChart();
        super.ngAfterViewInit(); // schedules the initial requestAnimationFrame(() => updateChart())
    }
    createChart() {
        const element = this.chartRef().nativeElement;
        this.svg = select(element);
    }
    updateChart() {
        this.svg.selectAll('*').remove();
        if (this.loading()) {
            this.slicedDataPoints.set([]);
            return;
        }
        const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
        const extraBottom = this.xAxisLabel() ? 16 : 0;
        const extraLeft = this.yAxisLabel() ? 16 : 0;
        const leftMargin = (this.orientation() === 'horizontal' ? this.MARGIN.left : 30) + extraLeft;
        const width = containerWidth - leftMargin - this.MARGIN.right;
        const height = containerHeight - this.MARGIN.top - (this.MARGIN.bottom + extraBottom);
        this.maxAmountOfItems.set(this.getMaxAmountOfItems(width, height));
        if (!this.data()?.length) {
            this.slicedDataPoints.set([]);
            return;
        }
        const stacks = this.stackItems(this.data());
        const slicedStacks = stacks.slice(0, this.maxAmountOfItems());
        const flatStacks = slicedStacks.flat();
        this.slicedDataPoints.set(flatStacks);
        const slicedStackKeys = new Set(slicedStacks.map(stack => stack[0].stackKey));
        const labels = this.stacks()?.length
            ? this.stacks()
                .filter(stack => slicedStackKeys.has(stack.stackKey))
                .map(stack => ({ stackKey: stack.stackKey, label: stack.label }))
            : flatStacks.map(d => ({ stackKey: d.stackKey, label: d.label }));
        const g = this.svg
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', `translate(${leftMargin},${this.MARGIN.top})`);
        if (this.orientation() === 'vertical') {
            this.createVerticalChart(g, labels, width, height, this.xAxisLabel(), this.yAxisLabel(), leftMargin);
        }
        else {
            this.createHorizontalChart(g, labels, width, height, this.xAxisLabel(), this.yAxisLabel(), leftMargin);
        }
        this.styleGridLines(g);
    }
    createVerticalChart(g, labels, width, height, xAxisLabel, yAxisLabel, leftMargin) {
        const keys = labels.map(d => d.stackKey);
        const x = scaleBand().domain(keys).range([0, width]);
        const y = scaleLinear()
            .domain([0, max(this.slicedDataPoints(), (d) => d.stackValue1) || 0])
            .nice()
            .range([height, 0]);
        // X Axis
        const labelSelection = g
            .append('g')
            .attr('transform', `translate(0,${height})`)
            .call(axisBottom(x))
            .selectAll('text');
        this.labelSelection = labelSelection
            .data(labels)
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            .call(this.addTextCommonInfo)
            .style('cursor', 'pointer')
            .call(this.addTextAndTitle.bind(this), x.bandwidth())
            .call(this.addTextHoverHandlers.bind(this))
            .call(this.addTextClickHandler.bind(this));
        // Y Axis
        const tickValues = y.ticks(5).filter((tick) => Number.isInteger(tick));
        const yAxis = g
            .append('g')
            .call(axisLeft(y).tickValues(tickValues).tickSize(-width).tickFormat(format('~s')))
            .selectAll('text');
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        yAxis.call(this.addTextCommonInfo);
        this.createBarSelectionGroups(g);
        // Add highlight rect (behind)
        this.addVerticalBarRectangle(x, y, 2)
            .attr('class', 'bar-highlight')
            .attr('fill', '#fff')
            .style('stroke', (d) => toChartColor(d.color))
            .attr('stroke-width', 2)
            .style('opacity', 0); // hidden by default;
        // Add main bar rect (on top)
        this.addVerticalBarRectangle(x, y)
            .attr('class', 'bar')
            .style('fill', (d) => toChartColor(d.color));
        this.appendAxisLabel(g, xAxisLabel, width / 2, height + 40);
        this.appendAxisLabel(g, yAxisLabel, -height / 2, -(leftMargin - 10), 'rotate(-90)');
    }
    createHorizontalChart(g, labels, width, height, xAxisLabel, yAxisLabel, leftMargin) {
        const x = scaleLinear()
            .domain([0, max(this.slicedDataPoints(), (d) => d.stackValue1) || 0])
            .nice()
            .range([0, width]);
        const keys = labels.map(d => d.stackKey);
        const y = scaleBand().domain(keys).range([0, height]);
        // X Axis
        const tickValues = x.ticks(5).filter((tick) => Number.isInteger(tick));
        const xAxis = g
            .append('g')
            .attr('transform', `translate(0,${height})`)
            .call(axisBottom(x).tickValues(tickValues).tickSize(-height).tickFormat(format('~s')))
            .selectAll('text');
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        xAxis.call(this.addTextCommonInfo);
        // Y Axis
        const labelSelection = g
            .append('g')
            .attr('transform', `translate(0,0)`)
            .call(axisLeft(y))
            .selectAll('text');
        this.labelSelection = labelSelection
            .data(labels)
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            .call(this.addTextCommonInfo)
            .style('cursor', 'pointer')
            .call(this.addTextAndTitle.bind(this), this.MARGIN.left - 10)
            .call(this.addTextHoverHandlers.bind(this))
            .call(this.addTextClickHandler.bind(this));
        this.createBarSelectionGroups(g);
        // Add highlight rect (behind)
        this.addHorizontalBarRectangle(x, y, 2)
            .attr('class', 'bar-highlight')
            .attr('fill', '#fff')
            .style('stroke', (d) => toChartColor(d.color))
            .attr('stroke-width', 2)
            .style('opacity', 0); // hidden by default;
        // Add main bar rect (on top)
        this.addHorizontalBarRectangle(x, y)
            .attr('class', 'bar')
            .style('fill', (d) => toChartColor(d.color));
        // X axis description label (value axis at bottom)
        this.appendAxisLabel(g, xAxisLabel, width / 2, height + 40);
        // Y axis description label: centred in the extra band reserved for it
        this.appendAxisLabel(g, yAxisLabel, -height / 2, -((this.MARGIN.left + leftMargin) / 2), 'rotate(-90)');
    }
    createBarSelectionGroups(g) {
        this.barSelection = g
            .selectAll('.bar-group')
            .data(this.slicedDataPoints(), (d) => d.key)
            .join('g')
            .attr('class', 'bar-group')
            .style('cursor', 'pointer')
            .on('mouseover', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, d.key, true))
            .on('mouseout', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, d.key, false))
            .call(this.addBarClickHandler.bind(this));
    }
    addTextCommonInfo(g) {
        g.style('font-size', '11px').style('fill', 'var(--cds-global-color-construction-400, #666)');
    }
    addTextHoverHandlers(g) {
        g.on('mouseover', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, undefined, true)) //
            .on('mouseout', (_e, d) => this.setHoverStylesByStackKey(d.stackKey, undefined, false));
    }
    setHoverStylesByStackKey(stackKey, key, isHover) {
        // When hovering a stack section, we also need to highlight the label, but not the other sections of the same stack.
        // But when hovering a label, we need to highlight all sections of the stack.
        const barGroup = this.barSelection.filter((d) => d.stackKey === stackKey && (key == null || d.key === key));
        barGroup.select('.bar').style('mix-blend-mode', isHover ? 'multiply' : 'unset');
        barGroup.select('.bar-highlight').style('opacity', isHover ? '1' : '0');
        this.labelSelection
            .filter((d) => d.stackKey === stackKey)
            .style('font-weight', isHover ? 'bold' : 'unset');
    }
    addBarClickHandler(g) {
        g.on('click', (event, d) => {
            event.stopPropagation();
            this.openTooltipByKey(d.key);
        });
    }
    addTextClickHandler(g) {
        // no click handler for labels in stacked charts
        if (this.stacks()?.length) {
            return;
        }
        g.on('click', (event, d) => {
            event.stopPropagation();
            // Note: In non-stacked charts, the stackKey is equal to the key, so this works - but the logic is confusing [VU3REQ-4790]
            this.openTooltipByKey(d.stackKey);
        });
    }
    openTooltipByKey(key) {
        const index = this.slicedDataPoints().findIndex(item => item.key === key);
        const rect = this.barSelection
            .filter((_d, i) => i === index)
            // we actually want the bar, not the group (because if value is 0, then the group is not positioned correctly
            .node()
            .children[0].getBoundingClientRect();
        const container = this.chartRef().nativeElement.getBoundingClientRect();
        this.tooltipPosition.set({
            x: rect.left - container.left + rect.width / 2,
            y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
        });
        this.selectedItem.set(this.slicedDataPoints()[index]);
    }
    addTextAndTitle(g, availableWidth) {
        g.each((d, index, nodes) => {
            const target = select(nodes[index]);
            target.text(this.textRenderer.render(d.label, 15, availableWidth, target.style('font-size'), target.style('font-family')));
            target.append('title').text(d.label);
        });
    }
    styleGridLines(g) {
        g.selectAll('.tick line').style('stroke', 'var(--cds-global-color-gray-200, #e8e8e8)');
    }
    addVerticalBarRectangle(x, y, extraSize = 0) {
        return this.barSelection
            .append('rect')
            .attr('x', (d) => {
            return (x(d.stackKey) || 0) + (x.bandwidth() - this.barSizePx()) / 2 + extraSize;
        })
            .attr('y', (d) => y(d.stackValue1))
            .attr('width', this.barSizePx() - 2 * extraSize)
            .attr('height', (d) => y(d.stackValue0) - y(d.stackValue1))
            .attr('rx', 1)
            .attr('ry', 1);
    }
    addHorizontalBarRectangle(x, y, extraSize = 0) {
        return this.barSelection
            .append('rect')
            .attr('x', (d) => x(d.stackValue0))
            .attr('y', (d) => {
            return (y(d.stackKey) || 0) + (y.bandwidth() - this.barSizePx()) / 2 + extraSize;
        })
            .attr('width', (d) => x(d.stackValue1) - x(d.stackValue0))
            .attr('height', this.barSizePx() - 2 * extraSize)
            .attr('rx', 1)
            .attr('ry', 1);
    }
    /** Appends a shared-style axis description label to the chart group. */
    appendAxisLabel(g, text, x, y, transform) {
        if (!text) {
            return;
        }
        const el = g
            .append('text')
            .attr('text-anchor', 'middle')
            .style('font-size', '12px')
            .style('fill', 'var(--cds-global-color-construction-400, #666)')
            .text(text)
            .attr('x', x)
            .attr('y', y);
        if (transform) {
            el.attr('transform', transform);
        }
    }
    stackItems(data) {
        let groups;
        if (this.stacks()?.length) {
            groups = Array.from(group(data, (d) => d.stackKey ?? d.key));
        }
        else {
            groups = data.map(item => [item.key, [item]]);
        }
        return this.mapItems(groups);
    }
    mapItems(groups) {
        const result = [];
        for (const [_, items] of groups) {
            let stackValue = 0;
            const stackSum = sum(items, (d) => d.value);
            result.push(items.map(item => {
                const stackValue0 = stackValue;
                stackValue += item.value;
                return {
                    ...item,
                    stackValue0,
                    stackValue1: stackValue,
                    stackKey: item.stackKey ?? item.key,
                    percentageOfStack: stackSum === 0 ? 0 : (100 * item.value) / stackSum,
                };
            }));
        }
        return result;
    }
    getMaxAmountOfItems(width, height) {
        if (this.orientation() === 'vertical') {
            return Math.floor(width / this.barAreaSizePx());
        }
        return Math.floor(height / BarChartComponent.HORIZONTAL_BAR_MIN_HEIGHT_PX);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BarChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: BarChartComponent, isStandalone: false, selector: "clr-bar-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, stacks: { classPropertyName: "stacks", publicName: "stacks", 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 }, exportButtonTitle: { classPropertyName: "exportButtonTitle", publicName: "exportButtonTitle", 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]=\"!stacks() ? 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 (stacks()) { @for (slice of selectedStackSlices(); track 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 }}:&nbsp;</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() }} \"{{ tooltipLabel() }}\"\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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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", "buttonTitle", "legendItems"] }, { 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: i8.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BarChartComponent, decorators: [{
            type: Component,
            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]=\"!stacks() ? 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 (stacks()) { @for (slice of selectedStackSlices(); track 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 }}:&nbsp;</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() }} \"{{ tooltipLabel() }}\"\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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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"] }]
        }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], stacks: [{ type: i0.Input, args: [{ isSignal: true, alias: "stacks", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: true }] }], tooltipOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipOrientation", required: false }] }], barSizePx: [{ type: i0.Input, args: [{ isSignal: true, alias: "barSizePx", required: false }] }], barAreaSizePx: [{ type: i0.Input, args: [{ isSignal: true, alias: "barAreaSizePx", required: false }] }], tooltipPercentOfTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPercentOfTotal", required: false }] }], tooltipPercentOf: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPercentOf", required: false }] }], noItemsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noItemsMessage", required: false }] }], tooManyItemsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooManyItemsMessage", required: false }] }], tooManyItemsGroupedMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooManyItemsGroupedMessage", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showExportButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportButton", required: false }] }], exportButtonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportButtonTitle", required: false }] }], exportFilename: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportFilename", required: false }] }], xAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "xAxisLabel", required: false }] }], yAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "yAxisLabel", required: false }] }], valueClicked: [{ type: i0.Output, args: ["valueClicked"] }] } });

/*
 * Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
 * This software is released under MIT license.
 * The full license information can be found in LICENSE in the root directory of this project.
 */
/**
 * Renders interactive dot circles for a single XY series onto the given D3 group.
 *
 * - Default radius: 4px, hover radius: 6px.
 * - Calls `onClick` with the clicked element, data point and series when a dot is clicked.
 *
 */
function renderDots(g, series, x, y, onClick) {
    g.selectAll(`.dot-${series.key}`)
        .data(series.data, (d) => d.x)
        .join('circle')
        .attr('class', `dot dot-${series.key}`)
        .attr('cx', (d) => x(d.x) ?? 0)
        .attr('cy', (d) => y(d.value))
        .attr('r', 4)
        .style('fill', toChartColor(series.color))
        .attr('stroke', '#fff')
        .attr('stroke-width', 2)
        .style('cursor', 'pointer')
        .on('mouseover', (e) => {
        select(e.currentTarget).attr('r', 6);
    })
        .on('mouseout', (e) => {
        select(e.currentTarget).attr('r', 4);
    })
        .on('click', (event, d) => {
        event.stopPropagation();
        onClick(event.currentTarget, d, series);
    });
}

/*
 * Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
 * This software is released under MIT license.
 * The full license information can be found in LICENSE in the root directory of this project.
 */
/**
 * Computes sensible tick values for a linear Y scale.
 *
 * When the domain contains enough integer values (≥ 3) the ticks are filtered
 * to integers only (avoids fractional labels for count data).  For small-range
 * domains – e.g. 0–1 ratios / probabilities – the raw D3 ticks are returned
 * unchanged so that decimal labels are still visible.
 */
function computeYTickValues(y, count = 5) {
    const raw = y.ticks(count);
    const integers = raw.filter(Number.isInteger);
    // Keep integer-only ticks only when there are at least 3 of them; otherwise
    // the domain is too small (e.g. 0–1) and we need the decimal ticks.
    return integers.length >= 3 ? integers : raw;
}
/**
 * Returns a D3 tick-format function that matches the decision made in
 * {@link computeYTickValues}:
 *
 * - **Integer / large-value domain** (≥ 3 integer ticks): SI prefix format
 *   (`~s`), e.g. `1k`, `200`.
 * - **Small / decimal domain** (< 3 integer ticks, e.g. 0–1 ratios): plain
 *   fixed-point format (`.3~f`) – avoids the SI "milli" prefix (`m`) that
 *   D3 would otherwise apply to values < 1.
 */
function computeYTickFormat(y, count = 5) {
    const raw = y.ticks(count);
    const integers = raw.filter(Number.isInteger);
    return integers.length >= 3 ? format('~s') : format('.3~f');
}
/**
 * Draws the X and Y axes (with integer-only Y ticks and grid lines) plus
 * optional description labels onto the given D3 group.
 *
 * @param g          The D3 group element to append into.
 * @param x          Any D3 axis scale keyed on strings (`ScalePoint` or `ScaleBand`).
 * @param y          The linear Y scale.
 * @param opts       Axis configuration options.
 */
function drawXYAxes(g, x, y, opts) {
    const { xLabelMap, width, height, xAxisLabel, yAxisLabel, effectiveLeft } = opts;
    // X axis
    g.append('g')
        .attr('transform', `translate(0,${height})`)
        .call(axisBottom(x).tickFormat((d) => xLabelMap.get(d) ?? d))
        .selectAll('text')
        .style('font-size', '11px')
        .style('fill', 'var(--cds-global-color-construction-400, #666)');
    // Y axis
    const tickValues = computeYTickValues(y);
    const tickFormat = computeYTickFormat(y);
    g.append('g')
        .call(axisLeft(y).tickValues(tickValues).tickSize(-width).tickFormat(tickFormat))
        .selectAll('text')
        .style('font-size', '11px')
        .style('fill', 'var(--cds-global-color-construction-400, #666)');
    // Optional X axis description label
    if (xAxisLabel) {
        g.append('text')
            .attr('x', width / 2)
            .attr('y', height + 40)
            .attr('text-anchor', 'middle')
            .style('font-size', '12px')
            .style('fill', 'var(--cds-global-color-construction-400, #666)')
            .text(xAxisLabel);
    }
    // Optional Y axis description label (rotated, centred in the left-margin band)
    if (yAxisLabel) {
        g.append('text')
            .attr('transform', 'rotate(-90)')
            .attr('x', -height / 2)
            .attr('y', -(effectiveLeft / 2))
            .attr('text-anchor', 'middle')
            .style('font-size', '12px')
            .style('fill', 'var(--cds-global-color-construction-400, #666)')
            .text(yAxisLabel);
    }
}
/**
 * Styles all D3 grid tick lines inside `g` with the neutral chart grid color.
 */
function styleGridLines(g) {
    g.selectAll('.tick line').style('stroke', 'var(--cds-global-color-gray-200, #e8e8e8)');
}

class LineChartComponent extends ChartBase {
    constructor() {
        super(...arguments);
        this.series = input.required(...(ngDevMode ? [{ debugName: "series" }] : /* istanbul ignore next */ []));
        this.tooltipOrientation = input('top', ...(ngDevMode ? [{ debugName: "tooltipOrientation" }] : /* istanbul ignore next */ []));
        this.showArea = input(false, ...(ngDevMode ? [{ debugName: "showArea" }] : /* istanbul ignore next */ []));
        this.showLegend = input(true, ...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
        this.showValues = input(false, ...(ngDevMode ? [{ debugName: "showValues" }] : /* istanbul ignore next */ []));
        this.showExportButton = input(false, ...(ngDevMode ? [{ debugName: "showExportButton" }] : /* istanbul ignore next */ []));
        this.exportButtonTitle = input('Export', ...(ngDevMode ? [{ debugName: "exportButtonTitle" }] : /* istanbul ignore next */ []));
        this.exportFilename = input('line-chart', ...(ngDevMode ? [{ debugName: "exportFilename" }] : /* istanbul ignore next */ []));
        this.noItemsMessage = input(NO_ITEMS_MESSAGE, ...(ngDevMode ? [{ debugName: "noItemsMessage" }] : /* istanbul ignore next */ []));
        this.tooltipPercentOfTotal = input('of total', ...(ngDevMode ? [{ debugName: "tooltipPercentOfTotal" }] : /* istanbul ignore next */ []));
        /** Optional label rendered below the X axis. */
        this.xAxisLabel = input('', ...(ngDevMode ? [{ debugName: "xAxisLabel" }] : /* istanbul ignore next */ []));
        /** Optional label rendered rotated to the left of the Y axis. */
        this.yAxisLabel = input('', ...(ngDevMode ? [{ debugName: "yAxisLabel" }] : /* istanbul ignore next */ []));
        this.yMin = input(0, ...(ngDevMode ? [{ debugName: "yMin" }] : /* istanbul ignore next */ []));
        this.autoscaleYAxis = input(false, ...(ngDevMode ? [{ debugName: "autoscaleYAxis" }] : /* istanbul ignore next */ []));
        this.valueClicked = output();
        this.MARGIN = { top: 20, right: 30, bottom: 30, left: 65 };
        this.hasData = computed(() => this.series().some(s => s.data.length > 0), ...(ngDevMode ? [{ debugName: "hasData" }] : /* istanbul ignore next */ []));
        this.total = computed(() => this.series().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0), ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
        this.alertMessageAndType = computed(() => {
            if (this.loading()) {
                return undefined;
            }
            if (!this.hasData()) {
                return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
            }
            return undefined;
        }, ...(ngDevMode ? [{ debugName: "alertMessageAndType" }] : /* istanbul ignore next */ []));
        this.legendItems = computed(() => {
            if (!this.showLegend()) {
                return [];
            }
            return this.series().map(s => ({ label: s.label, color: s.color }));
        }, ...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
    }
    ngOnChanges(_changes) {
        if (!this.svg) {
            return;
        }
        requestAnimationFrame(() => this.updateChart());
    }
    ngAfterViewInit() {
        this.svg = select(this.chartRef().nativeElement);
        super.ngAfterViewInit();
    }
    updateChart() {
        this.svg.selectAll('*').remove();
        if (this.loading()) {
            return;
        }
        if (!this.hasData()) {
            return;
        }
        const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
        const extraBottom = this.xAxisLabel() ? 16 : 0;
        const extraLeft = this.yAxisLabel() ? 16 : 0;
        const extraTop = this.showValues() ? 14 : 0;
        const effectiveLeft = this.MARGIN.left + extraLeft;
        const width = containerWidth - effectiveLeft - this.MARGIN.right;
        const height = containerHeight - (this.MARGIN.top + extraTop) - (this.MARGIN.bottom + extraBottom);
        // Collect all unique X keys in the order they appear across all series
        const xKeys = [...new Set(this.series().flatMap(s => s.data.map(d => d.x)))];
        // Build a display-label map
        const xLabelMap = new Map();
        this.series().forEach(s => s.data.forEach(d => xLabelMap.set(d.x, d.xLabel ?? d.x)));
        const x = scalePoint().domain(xKeys).range([0, width]).padding(0.5);
        const maxY = max(this.series().flatMap(s => s.data.map(d => d.value))) ?? 0;
        const minY = this.autoscaleYAxis()
            ? (min(this.series().flatMap(s => s.data.map(d => d.value))) ?? 0)
            : this.yMin();
        const y = scaleLinear().domain([minY, maxY]).nice().range([height, 0]);
        const g = this.svg
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', `translate(${effectiveLeft},${this.MARGIN.top + extraTop})`);
        drawXYAxes(g, x, y, {
            xLabelMap,
            width,
            height,
            xAxisLabel: this.xAxisLabel(),
            yAxisLabel: this.yAxisLabel(),
            effectiveLeft,
        });
        styleGridLines(g);
        this.drawSeries(g, x, y);
    }
    drawSeries(g, x, y) {
        const lineGenerator = line()
            .x((d) => x(d.x) ?? 0)
            .y((d) => y(d.value))
            .curve(curveMonotoneX);
        for (const series of this.series()) {
            if (!series.data.length) {
                continue;
            }
            // Optional area fill
            if (this.showArea()) {
                g.append('path')
                    .datum(series.data)
                    .attr('class', `area area-${series.key}`)
                    .style('fill', toChartColor(series.color))
                    .attr('fill-opacity', 0.1)
                    .attr('d', lineGenerator);
            }
            // Line path
            g.append('path')
                .datum(series.data)
                .attr('class', `line line-${series.key}`)
                .attr('fill', 'none')
                .style('stroke', toChartColor(series.color))
                .attr('stroke-width', 2)
                .attr('stroke-linejoin', 'round')
                .attr('stroke-linecap', 'round')
                .attr('d', lineGenerator);
            // Dots
            renderDots(g, series, x, y, (el, point, s) => this.openTooltip(el, point, s));
            // Value labels above each dot
            if (this.showValues()) {
                g.selectAll(`.value-label-${series.key}`)
                    .data(series.data, (d) => d.x)
                    .join('text')
                    .attr('class', `value-label value-label-${series.key}`)
                    .attr('x', (d) => x(d.x) ?? 0)
                    .attr('y', (d) => y(d.value) - 9)
                    .attr('text-anchor', 'middle')
                    .style('font-size', '11px')
                    .style('font-weight', '600')
                    .style('fill', toChartColor(series.color))
                    .attr('stroke', '#fff')
                    .attr('stroke-width', 3)
                    .attr('paint-order', 'stroke fill')
                    .style('pointer-events', 'none')
                    .text((d) => format('~s')(d.value));
            }
        }
    }
    openTooltip(el, point, series) {
        const rect = el.getBoundingClientRect();
        const container = this.chartRef().nativeElement.getBoundingClientRect();
        this.tooltipPosition.set({
            x: rect.left - container.left + rect.width / 2,
            y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
        });
        this.selectedItem.set({ ...point, seriesKey: series.key, seriesLabel: series.label, color: series.color });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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 }, exportButtonTitle: { classPropertyName: "exportButtonTitle", publicName: "exportButtonTitle", 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 }, yMin: { classPropertyName: "yMin", publicName: "yMin", isSignal: true, isRequired: false, transformFunction: null }, autoscaleYAxis: { classPropertyName: "autoscaleYAxis", publicName: "autoscaleYAxis", 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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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", "buttonTitle", "legendItems"] }, { 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: i8.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LineChartComponent, decorators: [{
            type: Component,
            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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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"] }]
        }], propDecorators: { series: [{ type: i0.Input, args: [{ isSignal: true, alias: "series", required: true }] }], tooltipOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipOrientation", required: false }] }], showArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "showArea", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValues", required: false }] }], showExportButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportButton", required: false }] }], exportButtonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportButtonTitle", required: false }] }], exportFilename: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportFilename", required: false }] }], noItemsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noItemsMessage", required: false }] }], tooltipPercentOfTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPercentOfTotal", required: false }] }], xAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "xAxisLabel", required: false }] }], yAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "yAxisLabel", required: false }] }], yMin: [{ type: i0.Input, args: [{ isSignal: true, alias: "yMin", required: false }] }], autoscaleYAxis: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoscaleYAxis", required: false }] }], valueClicked: [{ type: i0.Output, args: ["valueClicked"] }] } });

class AreaChartComponent extends ChartBase {
    constructor() {
        super(...arguments);
        this.series = input.required(...(ngDevMode ? [{ debugName: "series" }] : /* istanbul ignore next */ []));
        this.tooltipOrientation = input('top', ...(ngDevMode ? [{ debugName: "tooltipOrientation" }] : /* istanbul ignore next */ []));
        this.showLegend = input(true, ...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
        this.showExportButton = input(false, ...(ngDevMode ? [{ debugName: "showExportButton" }] : /* istanbul ignore next */ []));
        this.exportButtonTitle = input('Export', ...(ngDevMode ? [{ debugName: "exportButtonTitle" }] : /* istanbul ignore next */ []));
        this.exportFilename = input('area-chart', ...(ngDevMode ? [{ debugName: "exportFilename" }] : /* istanbul ignore next */ []));
        /** Area fill opacity (0–1). Default: 0.2. */
        this.areaOpacity = input(0.2, ...(ngDevMode ? [{ debugName: "areaOpacity" }] : /* istanbul ignore next */ []));
        this.noItemsMessage = input(NO_ITEMS_MESSAGE, ...(ngDevMode ? [{ debugName: "noItemsMessage" }] : /* istanbul ignore next */ []));
        this.tooltipPercentOfTotal = input('of total', ...(ngDevMode ? [{ debugName: "tooltipPercentOfTotal" }] : /* istanbul ignore next */ []));
        /** Optional label rendered below the X axis. */
        this.xAxisLabel = input('', ...(ngDevMode ? [{ debugName: "xAxisLabel" }] : /* istanbul ignore next */ []));
        /** Optional label rendered rotated to the left of the Y axis. */
        this.yAxisLabel = input('', ...(ngDevMode ? [{ debugName: "yAxisLabel" }] : /* istanbul ignore next */ []));
        this.valueClicked = output();
        this.MARGIN = { top: 20, right: 30, bottom: 30, left: 65 };
        this.hasData = computed(() => this.series().some(s => s.data.length > 0), ...(ngDevMode ? [{ debugName: "hasData" }] : /* istanbul ignore next */ []));
        this.total = computed(() => this.series().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0), ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
        this.alertMessageAndType = computed(() => {
            if (this.loading()) {
                return undefined;
            }
            if (!this.hasData()) {
                return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
            }
            return undefined;
        }, ...(ngDevMode ? [{ debugName: "alertMessageAndType" }] : /* istanbul ignore next */ []));
        this.legendItems = computed(() => {
            if (!this.showLegend()) {
                return [];
            }
            return this.series().map(s => ({ label: s.label, color: s.color }));
        }, ...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
    }
    ngOnChanges(_changes) {
        if (!this.svg) {
            return;
        }
        requestAnimationFrame(() => this.updateChart());
    }
    ngAfterViewInit() {
        this.svg = select(this.chartRef().nativeElement);
        super.ngAfterViewInit();
    }
    updateChart() {
        this.svg.selectAll('*').remove();
        if (this.loading() || !this.hasData()) {
            return;
        }
        const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
        const extraBottom = this.xAxisLabel() ? 16 : 0;
        const extraLeft = this.yAxisLabel() ? 16 : 0;
        const effectiveLeft = this.MARGIN.left + extraLeft;
        const width = containerWidth - effectiveLeft - this.MARGIN.right;
        const height = containerHeight - this.MARGIN.top - (this.MARGIN.bottom + extraBottom);
        const xKeys = [...new Set(this.series().flatMap(s => s.data.map(d => d.x)))];
        const xLabelMap = new Map();
        this.series().forEach(s => s.data.forEach(d => xLabelMap.set(d.x, d.xLabel ?? d.x)));
        const x = scalePoint().domain(xKeys).range([0, width]).padding(0.5);
        const maxY = max(this.series().flatMap(s => s.data.map(d => d.value))) ?? 0;
        const y = scaleLinear().domain([0, maxY]).nice().range([height, 0]);
        const g = this.svg
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', `translate(${effectiveLeft},${this.MARGIN.top})`);
        drawXYAxes(g, x, y, {
            xLabelMap,
            width,
            height,
            xAxisLabel: this.xAxisLabel(),
            yAxisLabel: this.yAxisLabel(),
            effectiveLeft,
        });
        styleGridLines(g);
        this.drawSeries(g, x, y, height);
    }
    drawSeries(g, x, y, height) {
        const areaGenerator = area()
            .x((d) => x(d.x) ?? 0)
            .y0(height)
            .y1((d) => y(d.value))
            .curve(curveMonotoneX);
        const lineGenerator = line()
            .x((d) => x(d.x) ?? 0)
            .y((d) => y(d.value))
            .curve(curveMonotoneX);
        // Draw areas first (behind lines)
        for (const series of this.series()) {
            if (!series.data.length) {
                continue;
            }
            g.append('path')
                .datum(series.data)
                .attr('class', `area area-${series.key}`)
                .style('fill', toChartColor(series.color))
                .attr('fill-opacity', this.areaOpacity())
                .attr('stroke', 'none')
                .attr('d', areaGenerator);
        }
        // Draw lines and dots on top
        for (const series of this.series()) {
            if (!series.data.length) {
                continue;
            }
            g.append('path')
                .datum(series.data)
                .attr('class', `line line-${series.key}`)
                .attr('fill', 'none')
                .style('stroke', toChartColor(series.color))
                .attr('stroke-width', 2)
                .attr('stroke-linejoin', 'round')
                .attr('stroke-linecap', 'round')
                .attr('d', lineGenerator);
            renderDots(g, series, x, y, (el, point, s) => this.openTooltip(el, point, s));
        }
    }
    openTooltip(el, point, series) {
        const rect = el.getBoundingClientRect();
        const container = this.chartRef().nativeElement.getBoundingClientRect();
        this.tooltipPosition.set({
            x: rect.left - container.left + rect.width / 2,
            y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
        });
        this.selectedItem.set({ ...point, seriesKey: series.key, seriesLabel: series.label, color: series.color });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: AreaChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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 }, exportButtonTitle: { classPropertyName: "exportButtonTitle", publicName: "exportButtonTitle", 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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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", "buttonTitle", "legendItems"] }, { 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: i8.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: AreaChartComponent, decorators: [{
            type: Component,
            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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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"] }]
        }], propDecorators: { series: [{ type: i0.Input, args: [{ isSignal: true, alias: "series", required: true }] }], tooltipOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipOrientation", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showExportButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportButton", required: false }] }], exportButtonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportButtonTitle", required: false }] }], exportFilename: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportFilename", required: false }] }], areaOpacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "areaOpacity", required: false }] }], noItemsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noItemsMessage", required: false }] }], tooltipPercentOfTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPercentOfTotal", required: false }] }], xAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "xAxisLabel", required: false }] }], yAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "yAxisLabel", required: false }] }], valueClicked: [{ type: i0.Output, args: ["valueClicked"] }] } });

class ComboChartComponent extends ChartBase {
    constructor() {
        super(...arguments);
        // ── Inputs ──────────────────────────────────────────────────────────────────
        this.barSeries = input([], ...(ngDevMode ? [{ debugName: "barSeries" }] : /* istanbul ignore next */ []));
        this.lineSeries = input([], ...(ngDevMode ? [{ debugName: "lineSeries" }] : /* istanbul ignore next */ []));
        this.tooltipOrientation = input('top', ...(ngDevMode ? [{ debugName: "tooltipOrientation" }] : /* istanbul ignore next */ []));
        this.showLegend = input(true, ...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
        this.showExportButton = input(false, ...(ngDevMode ? [{ debugName: "showExportButton" }] : /* istanbul ignore next */ []));
        this.exportButtonTitle = input('Export', ...(ngDevMode ? [{ debugName: "exportButtonTitle" }] : /* istanbul ignore next */ []));
        this.exportFilename = input('combo-chart', ...(ngDevMode ? [{ debugName: "exportFilename" }] : /* istanbul ignore next */ []));
        this.noItemsMessage = input(NO_ITEMS_MESSAGE, ...(ngDevMode ? [{ debugName: "noItemsMessage" }] : /* istanbul ignore next */ []));
        this.tooltipPercentOfTotal = input('of total', ...(ngDevMode ? [{ debugName: "tooltipPercentOfTotal" }] : /* istanbul ignore next */ []));
        /** Optional label rendered below the X axis. */
        this.xAxisLabel = input('', ...(ngDevMode ? [{ debugName: "xAxisLabel" }] : /* istanbul ignore next */ []));
        /** Optional label rendered rotated to the left of the Y axis (bar scale). */
        this.yAxisLabel = input('', ...(ngDevMode ? [{ debugName: "yAxisLabel" }] : /* istanbul ignore next */ []));
        /** Optional label rendered rotated to the right of the Y axis (line scale). */
        this.yLineAxisLabel = input('', ...(ngDevMode ? [{ debugName: "yLineAxisLabel" }] : /* istanbul ignore next */ []));
        /** Optional fixed maximum value for the bar Y axis (left). Defaults to auto. */
        this.yBarMax = input(undefined, ...(ngDevMode ? [{ debugName: "yBarMax" }] : /* istanbul ignore next */ []));
        /** Optional fixed maximum value for the line Y axis (right). Defaults to auto. */
        this.yLineMax = input(undefined, ...(ngDevMode ? [{ debugName: "yLineMax" }] : /* istanbul ignore next */ []));
        // ── Outputs ─────────────────────────────────────────────────────────────────
        this.valueClicked = output();
        // ── Layout ───────────────────────────────────────────────────────────────────
        this.MARGIN = { top: 20, right: 30, bottom: 30, left: 65 };
        // ── Computed ─────────────────────────────────────────────────────────────────
        this.hasData = computed(() => this.barSeries().some(s => s.data.length > 0) || this.lineSeries().some(s => s.data.length > 0), ...(ngDevMode ? [{ debugName: "hasData" }] : /* istanbul ignore next */ []));
        this.total = computed(() => {
            const barTotal = this.barSeries().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0);
            const lineTotal = this.lineSeries().reduce((acc, s) => acc + s.data.reduce((a, d) => a + d.value, 0), 0);
            return barTotal + lineTotal;
        }, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
        this.alertMessageAndType = computed(() => {
            if (this.loading()) {
                return undefined;
            }
            if (!this.hasData()) {
                return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
            }
            return undefined;
        }, ...(ngDevMode ? [{ debugName: "alertMessageAndType" }] : /* istanbul ignore next */ []));
        this.legendItems = computed(() => {
            if (!this.showLegend()) {
                return [];
            }
            const barItems = this.barSeries().map(s => ({ label: s.label, color: s.color }));
            const lineItems = this.lineSeries().map(s => ({ label: s.label, color: s.color }));
            return [...barItems, ...lineItems];
        }, ...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
        this._clipIdCounter = 0;
    }
    // ── Lifecycle ────────────────────────────────────────────────────────────────
    ngOnChanges(_changes) {
        if (!this.svg) {
            return;
        }
        requestAnimationFrame(() => this.updateChart());
    }
    ngAfterViewInit() {
        this.svg = select(this.chartRef().nativeElement);
        super.ngAfterViewInit();
    }
    // ── Chart rendering ──────────────────────────────────────────────────────────
    updateChart() {
        this.svg.selectAll('*').remove();
        if (this.loading() || !this.hasData()) {
            return;
        }
        const { width: cw, height: ch } = this.getContainerDimensions();
        const extraBottom = this.xAxisLabel() ? 16 : 0;
        const extraLeft = this.yAxisLabel() ? 16 : 0;
        const effectiveLeft = this.MARGIN.left + extraLeft;
        // Reserve extra right margin when line series are present (right Y axis)
        const hasLines = this.lineSeries().some(s => s.data.length > 0);
        const extraRight = hasLines ? (this.yLineAxisLabel() ? 50 : 40) : 0;
        const width = cw - effectiveLeft - this.MARGIN.right - extraRight;
        const height = ch - this.MARGIN.top - (this.MARGIN.bottom + extraBottom);
        // Collect all unique X keys preserving order
        const allBarKeys = this.barSeries().flatMap(s => s.data.map(d => d.x));
        const allLineKeys = this.lineSeries().flatMap(s => s.data.map(d => d.x));
        const xKeys = [...new Set([...allBarKeys, ...allLineKeys])];
        // Build X-label map
        const xLabelMap = new Map();
        this.barSeries().forEach(s => s.data.forEach(d => xLabelMap.set(d.x, d.xLabel ?? d.x)));
        // Outer X scale (one band per category)
        const x = scaleBand().domain(xKeys).range([0, width]).paddingInner(0.35).paddingOuter(0.15);
        // ── Separate Y scales ────────────────────────────────────────────────────
        // Bar Y scale (left axis)
        const xStackTotals = new Map();
        for (const series of this.barSeries()) {
            for (const point of series.data) {
                xStackTotals.set(point.x, (xStackTotals.get(point.x) ?? 0) + point.value);
            }
        }
        const maxBarAuto = max([...xStackTotals.values()]) ?? 0;
        const maxBar = this.yBarMax() ?? maxBarAuto;
        const yBar = scaleLinear()
            .domain([0, maxBar || 1])
            .nice(this.yBarMax() === undefined ? undefined : 0)
            .range([height, 0]);
        // Line Y scale (right axis)
        const maxLineAuto = max(this.lineSeries().flatMap(s => s.data.map(d => d.value))) ?? 0;
        const maxLine = this.yLineMax() ?? maxLineAuto;
        const yLine = scaleLinear()
            .domain([0, maxLine || 1])
            .nice(this.yLineMax() === undefined ? undefined : 0)
            .range([height, 0]);
        // ── SVG ClipPath ─────────────────────────────────────────────────────────
        // Ensures bars/lines that exceed the Y-axis maximum (yBarMax/yLineMax) are
        // visually clipped at the chart boundary without modifying the data values.
        const clipId = `combo-clip-${this._clipIdCounter++}`;
        this.svg
            .append('defs')
            .append('clipPath')
            .attr('id', clipId)
            .append('rect')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', width)
            .attr('height', height);
        const g = this.svg
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', `translate(${effectiveLeft},${this.MARGIN.top})`);
        drawXYAxes(g, x, yBar, {
            xLabelMap,
            width,
            height,
            xAxisLabel: this.xAxisLabel(),
            yAxisLabel: this.yAxisLabel(),
            effectiveLeft,
        });
        styleGridLines(g);
        // ── Right Y axis for line series ─────────────────────────────────────────
        if (hasLines) {
            const lineTickValues = computeYTickValues(yLine);
            const lineTickFormat = computeYTickFormat(yLine);
            const rightAxisG = g
                .append('g')
                .attr('transform', `translate(${width},0)`)
                .call(axisRight(yLine).tickValues(lineTickValues).tickFormat(lineTickFormat).tickSize(-width));
            rightAxisG
                .selectAll('text')
                .style('font-size', '11px')
                .style('fill', 'var(--cds-global-color-construction-400, #666)');
            // Style the line-axis grid lines with a distinct dashed color
            rightAxisG
                .selectAll('.tick line')
                .style('stroke', 'var(--cds-global-color-gray-400, #b3b3b3)')
                .style('stroke-dasharray', '4 3')
                .style('stroke-opacity', '0.8');
            // Remove the default domain line so it doesn't overlap the chart
            rightAxisG.select('.domain').remove();
            if (this.yLineAxisLabel()) {
                g.append('text')
                    .attr('transform', 'rotate(-90)')
                    .attr('x', -height / 2)
                    .attr('y', width + extraRight - 4)
                    .attr('text-anchor', 'middle')
                    .style('font-size', '12px')
                    .style('fill', 'var(--cds-global-color-construction-400, #666)')
                    .text(this.yLineAxisLabel());
            }
        }
        // Wrap chart content in a clipped group so elements exceeding the Y max
        // are not visible beyond the chart boundary.
        const contentGroup = g.append('g').attr('clip-path', `url(#${clipId})`);
        this.drawBars(contentGroup, x, yBar);
        this.drawLines(contentGroup, x, yLine);
    }
    drawBars(g, x, y) {
        // Running stack base (cumulative sum) per X key across all bar series
        const stackBase = new Map();
        for (const series of this.barSeries()) {
            if (!series.data.length) {
                continue;
            }
            const stacked = series.data.map(d => ({
                ...d,
                base: stackBase.get(d.x) ?? 0,
            }));
            // Advance running totals for the next series
            for (const d of series.data) {
                stackBase.set(d.x, (stackBase.get(d.x) ?? 0) + d.value);
            }
            g.selectAll(`.bar-${series.key}`)
                .data(stacked, (d) => d.x)
                .join('rect')
                .attr('class', `bar bar-${series.key}`)
                .attr('x', (d) => x(d.x) ?? 0)
                .attr('y', (d) => y(d.base + d.value))
                .attr('width', x.bandwidth())
                .attr('height', (d) => y(d.base) - y(d.base + d.value))
                .attr('rx', 2)
                .attr('ry', 2)
                .style('fill', toChartColor(series.color))
                .style('cursor', 'pointer')
                .on('mouseover', (e) => {
                select(e.currentTarget).attr('fill-opacity', 0.75);
            })
                .on('mouseout', (e) => {
                select(e.currentTarget).attr('fill-opacity', 1);
            })
                .on('click', (event, d) => {
                event.stopPropagation();
                const rect = event.currentTarget.getBoundingClientRect();
                const container = this.chartRef().nativeElement.getBoundingClientRect();
                this.tooltipPosition.set({
                    x: rect.left - container.left + rect.width / 2,
                    y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
                });
                this.selectedItem.set({
                    seriesKey: series.key,
                    seriesLabel: series.label,
                    seriesType: 'bar',
                    color: series.color,
                    x: d.x,
                    xLabel: d.xLabel,
                    value: d.value,
                    total: this.total(),
                });
            });
        }
    }
    drawLines(g, x, y) {
        // Map X key → band center position for line/dot placement
        const lineX = (key) => (x(key) ?? 0) + x.bandwidth() / 2;
        const lineGenerator = line()
            .x((d) => lineX(d.x))
            .y((d) => y(d.value))
            .curve(curveMonotoneX);
        for (const series of this.lineSeries()) {
            if (!series.data.length) {
                continue;
            }
            // Line path
            g.append('path')
                .datum(series.data)
                .attr('class', `combo-line combo-line-${series.key}`)
                .attr('fill', 'none')
                .style('stroke', toChartColor(series.color))
                .attr('stroke-width', 2.5)
                .attr('stroke-linejoin', 'round')
                .attr('stroke-linecap', 'round')
                .attr('d', lineGenerator);
            // Dots
            g.selectAll(`.combo-dot-${series.key}`)
                .data(series.data, (d) => d.x)
                .join('circle')
                .attr('class', `combo-dot combo-dot-${series.key}`)
                .attr('cx', (d) => lineX(d.x))
                .attr('cy', (d) => y(d.value))
                .attr('r', 4)
                .style('fill', toChartColor(series.color))
                .attr('stroke', '#fff')
                .attr('stroke-width', 2)
                .style('cursor', 'pointer')
                .on('mouseover', (e) => {
                select(e.currentTarget).attr('r', 6);
            })
                .on('mouseout', (e) => {
                select(e.currentTarget).attr('r', 4);
            })
                .on('click', (event, d) => {
                event.stopPropagation();
                const el = event.currentTarget;
                const rect = el.getBoundingClientRect();
                const container = this.chartRef().nativeElement.getBoundingClientRect();
                this.tooltipPosition.set({
                    x: rect.left - container.left + rect.width / 2,
                    y: (this.tooltipOrientation() === 'top' ? rect.top : rect.bottom) - container.top,
                });
                this.selectedItem.set({
                    seriesKey: series.key,
                    seriesLabel: series.label,
                    seriesType: 'line',
                    color: series.color,
                    x: d.x,
                    value: d.value,
                    total: this.total(),
                });
            });
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ComboChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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 }, exportButtonTitle: { classPropertyName: "exportButtonTitle", publicName: "exportButtonTitle", 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 }, yBarMax: { classPropertyName: "yBarMax", publicName: "yBarMax", isSignal: true, isRequired: false, transformFunction: null }, yLineMax: { classPropertyName: "yLineMax", publicName: "yLineMax", 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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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", "buttonTitle", "legendItems"] }, { 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: i8.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ComboChartComponent, decorators: [{
            type: Component,
            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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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"] }]
        }], propDecorators: { barSeries: [{ type: i0.Input, args: [{ isSignal: true, alias: "barSeries", required: false }] }], lineSeries: [{ type: i0.Input, args: [{ isSignal: true, alias: "lineSeries", required: false }] }], tooltipOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipOrientation", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showExportButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportButton", required: false }] }], exportButtonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportButtonTitle", required: false }] }], exportFilename: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportFilename", required: false }] }], noItemsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noItemsMessage", required: false }] }], tooltipPercentOfTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPercentOfTotal", required: false }] }], xAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "xAxisLabel", required: false }] }], yAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "yAxisLabel", required: false }] }], yLineAxisLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "yLineAxisLabel", required: false }] }], yBarMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "yBarMax", required: false }] }], yLineMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "yLineMax", required: false }] }], valueClicked: [{ type: i0.Output, args: ["valueClicked"] }] } });

class PieChartComponent extends ChartBase {
    constructor() {
        super(...arguments);
        this.data = input.required(...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
        this.donut = input(true, ...(ngDevMode ? [{ debugName: "donut" }] : /* istanbul ignore next */ []));
        this.showLegend = input(true, ...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
        this.showExportButton = input(false, ...(ngDevMode ? [{ debugName: "showExportButton" }] : /* istanbul ignore next */ []));
        this.exportButtonTitle = input('Export', ...(ngDevMode ? [{ debugName: "exportButtonTitle" }] : /* istanbul ignore next */ []));
        this.exportFilename = input('pie-chart', ...(ngDevMode ? [{ debugName: "exportFilename" }] : /* istanbul ignore next */ []));
        this.tooltipOrientation = input('top', ...(ngDevMode ? [{ debugName: "tooltipOrientation" }] : /* istanbul ignore next */ []));
        this.noItemsMessage = input(NO_ITEMS_MESSAGE, ...(ngDevMode ? [{ debugName: "noItemsMessage" }] : /* istanbul ignore next */ []));
        this.tooltipPercentOfTotal = input('of total', ...(ngDevMode ? [{ debugName: "tooltipPercentOfTotal" }] : /* istanbul ignore next */ []));
        this.valueClicked = output();
        this.hasData = computed(() => this.data()?.some(d => d.value > 0), ...(ngDevMode ? [{ debugName: "hasData" }] : /* istanbul ignore next */ []));
        this.total = computed(() => this.data()?.reduce((acc, d) => acc + d.value, 0) ?? 0, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
        this.alertMessageAndType = computed(() => {
            if (this.loading()) {
                return undefined;
            }
            if (!this.hasData()) {
                return [this.noItemsMessage(), NO_ITEMS_ALERT_TYPE];
            }
            return undefined;
        }, ...(ngDevMode ? [{ debugName: "alertMessageAndType" }] : /* istanbul ignore next */ []));
        this.legendItems = computed(() => {
            if (!this.showLegend()) {
                return [];
            }
            return (this.data() ?? []).filter(d => d.value > 0).map(d => ({ label: d.fullLabel ?? d.label, color: d.color }));
        }, ...(ngDevMode ? [{ debugName: "legendItems" }] : /* istanbul ignore next */ []));
    }
    ngOnChanges(_changes) {
        if (!this.svg) {
            return;
        }
        requestAnimationFrame(() => this.updateChart());
    }
    ngAfterViewInit() {
        this.svg = select(this.chartRef().nativeElement);
        super.ngAfterViewInit();
    }
    updateChart() {
        this.svg.selectAll('*').remove();
        if (this.loading() || !this.hasData()) {
            return;
        }
        const { width, height } = this.getContainerDimensions();
        const radius = Math.min(width, height) / 2;
        const innerRadius = this.donut() ? radius * 0.55 : 0;
        const outerRadius = radius * 0.9;
        this.arcGen = arc().innerRadius(innerRadius).outerRadius(outerRadius);
        const hoverArc = arc()
            .innerRadius(innerRadius)
            .outerRadius(outerRadius * 1.06);
        const pieGen = pie()
            .value(d => d.value)
            .sort(null);
        const arcs = pieGen(this.data().filter(d => d.value > 0));
        const g = this.svg
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', `translate(${width / 2},${height / 2})`);
        // Slices
        g.selectAll('.slice')
            .data(arcs, (d) => d.data.key)
            .join('path')
            .attr('class', 'slice')
            .attr('d', this.arcGen)
            .style('fill', (d) => toChartColor(d.data.color))
            .attr('stroke', '#fff')
            .attr('stroke-width', 2)
            .style('cursor', 'pointer')
            .on('mouseover', (e) => {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            select(e.currentTarget).attr('d', hoverArc);
        })
            .on('mouseout', (e) => {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            select(e.currentTarget).attr('d', this.arcGen);
        })
            .on('click', (event, d) => {
            event.stopPropagation();
            this.openTooltip(d, width, height);
        });
        // Slice labels – only show when the slice angle is large enough to fit text
        const MIN_ANGLE = 0.4; // ~23°
        const labelRadius = this.donut() ? (innerRadius + outerRadius) / 2 : outerRadius * 0.65;
        const labelArc = arc().innerRadius(labelRadius).outerRadius(labelRadius);
        const total = this.total();
        const labelGroups = g
            .selectAll('.slice-label')
            .data(arcs.filter((d) => d.endAngle - d.startAngle >= MIN_ANGLE), (d) => d.data.key)
            .join('g')
            .attr('class', 'slice-label')
            .attr('transform', (d) => {
            const [x, y] = labelArc.centroid(d);
            return `translate(${x},${y})`;
        })
            .style('pointer-events', 'none');
        labelGroups
            .append('text')
            .attr('class', 'label-value')
            .attr('text-anchor', 'middle')
            .attr('dy', '-0.3em')
            .style('font-size', '11px')
            .style('font-weight', '600')
            .style('fill', '#fff')
            .style('pointer-events', 'none')
            .text((d) => format('~s')(d.data.value));
        labelGroups
            .append('text')
            .attr('class', 'label-percent')
            .attr('text-anchor', 'middle')
            .attr('dy', '0.9em')
            .style('font-size', '10px')
            .style('fill', 'rgba(255, 255, 255, 0.9)')
            .style('pointer-events', 'none')
            .text((d) => `${((100 * d.data.value) / total).toFixed(1)}%`);
        // Center label (donut only)
        if (this.donut()) {
            g.append('text')
                .attr('class', 'center-label')
                .attr('text-anchor', 'middle')
                .attr('dy', '-0.1em')
                .style('font-size', '1.4rem')
                .style('font-weight', '600')
                .style('fill', 'var(--cds-global-color-gray-900, #21333b)')
                .text(format('~s')(this.total()));
            g.append('text')
                .attr('class', 'center-sublabel')
                .attr('text-anchor', 'middle')
                .attr('dy', '1.2em')
                .style('font-size', '0.65rem')
                .style('fill', 'var(--cds-global-color-construction-400, #666)')
                .style('text-transform', 'uppercase')
                .style('letter-spacing', '0.05em')
                .text('Total');
        }
    }
    openTooltip(d, width, height) {
        const [cx, cy] = this.arcGen.centroid(d);
        this.tooltipPosition.set({
            x: width / 2 + cx,
            y: height / 2 + cy,
        });
        this.selectedItem.set(d.data);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: PieChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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 }, exportButtonTitle: { classPropertyName: "exportButtonTitle", publicName: "exportButtonTitle", 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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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(--cds-global-color-gray-900, #21333b)}.center-sublabel{font-size:.65rem;fill:var(--cds-global-color-construction-400, #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", "buttonTitle", "legendItems"] }, { 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: i8.DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: PieChartComponent, decorators: [{
            type: Component,
            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\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n    [legendItems]=\"legendItems()\"\n  />\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(--cds-global-color-gray-900, #21333b)}.center-sublabel{font-size:.65rem;fill:var(--cds-global-color-construction-400, #666);text-transform:uppercase;letter-spacing:.05em}\n"] }]
        }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], donut: [{ type: i0.Input, args: [{ isSignal: true, alias: "donut", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showExportButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportButton", required: false }] }], exportButtonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportButtonTitle", required: false }] }], exportFilename: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportFilename", required: false }] }], tooltipOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipOrientation", required: false }] }], noItemsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noItemsMessage", required: false }] }], tooltipPercentOfTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPercentOfTotal", required: false }] }], valueClicked: [{ type: i0.Output, args: ["valueClicked"] }] } });

/*
 * Copyright (c) 2018-2026 Porsche Informatik. All Rights Reserved.
 * This software is released under MIT license.
 * The full license information can be found in LICENSE in the root directory of this project.
 */
// ─── Constants ────────────────────────────────────────────────────────────────
const DEFAULT_SECTION_COLORS = ['#009ADB', '#66D1FF', '#D9D9D9', '#A6A6A6'];
const DEFAULT_TEXT_COLORS = ['#eee', '#eee', '#555', '#eee'];
const DEFAULT_CHART_LABELS = {
    total: 'Total',
    all: 'All',
};
class FunnelChartComponent extends ChartBase {
    constructor() {
        super(...arguments);
        // ── Inputs ──────────────────────────────────────────────────────────────────
        this.data = input([], ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
        this.showExportButton = input(false, ...(ngDevMode ? [{ debugName: "showExportButton" }] : /* istanbul ignore next */ []));
        this.exportButtonTitle = input('Export', ...(ngDevMode ? [{ debugName: "exportButtonTitle" }] : /* istanbul ignore next */ []));
        this.exportFilename = input('funnel-chart', ...(ngDevMode ? [{ debugName: "exportFilename" }] : /* istanbul ignore next */ []));
        /** Rendering mode. 'default' = horizontal bars with sections; 'centered' = centered trapezoid funnel. */
        this.orientation = input('default', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
        /** Width reserved on each side for labels (px). */
        this.textSize = input(200, ...(ngDevMode ? [{ debugName: "textSize" }] : /* istanbul ignore next */ []));
        /** Gap between funnel bars and their side-line labels (px). */
        this.lineTextPadding = input(8, ...(ngDevMode ? [{ debugName: "lineTextPadding" }] : /* istanbul ignore next */ []));
        /** Vertical gap between funnel bars – default mode only (px). */
        this.barGap = input(2, ...(ngDevMode ? [{ debugName: "barGap" }] : /* istanbul ignore next */ []));
        /** Vertical padding inside the side measurement lines (px). */
        this.sideLineVerticalPadding = input(4, ...(ngDevMode ? [{ debugName: "sideLineVerticalPadding" }] : /* istanbul ignore next */ []));
        // ── Centered-mode specific inputs ────────────────────────────────────────────
        /** Minimum length of the horizontal measurement lines on each side (px). Centered mode only. */
        this.minLineSize = input(30, ...(ngDevMode ? [{ debugName: "minLineSize" }] : /* istanbul ignore next */ []));
        /** Ratio of bar height to spacer height. Centered mode only. */
        this.barToSpacerRatio = input(2, ...(ngDevMode ? [{ debugName: "barToSpacerRatio" }] : /* istanbul ignore next */ []));
        /** Vertical padding inside the center divider lines (px). Centered mode only. */
        this.middleLineVerticalPadding = input(4, ...(ngDevMode ? [{ debugName: "middleLineVerticalPadding" }] : /* istanbul ignore next */ []));
        /** Fill color of the centered-mode bars. Accepts hex or CSS custom property. Centered mode only. */
        this.barColor = input(DEFAULT_SECTION_COLORS[0], ...(ngDevMode ? [{ debugName: "barColor" }] : /* istanbul ignore next */ []));
        /** Fill color of the trapezoid spacers. Accepts hex or CSS custom property. Centered mode only. */
        this.spacerColor = input('#D9D9D9', ...(ngDevMode ? [{ debugName: "spacerColor" }] : /* istanbul ignore next */ []));
        /**
         * Override the chart labels shown in the tooltip (total / all rows).
         * Partial – any key not provided falls back to the English default.
         */
        this.chartLabels = input({}, ...(ngDevMode ? [{ debugName: "chartLabels" }] : /* istanbul ignore next */ []));
        /**
         * Override section colors by section key.
         * Accepts hex values ('#009ADB') or CSS custom properties ('--cds-global-color-blue-800').
         * Per-section `color` in the data takes priority over this map; this map takes priority over
         * the built-in default palette.
         */
        this.sectionColors = input({}, ...(ngDevMode ? [{ debugName: "sectionColors" }] : /* istanbul ignore next */ []));
        // ── Outputs ─────────────────────────────────────────────────────────────────
        this.valueClicked = output();
        // ── Computed ─────────────────────────────────────────────────────────────────
        this.resolvedChartLabels = computed(() => ({
            ...DEFAULT_CHART_LABELS,
            ...this.chartLabels(),
        }), ...(ngDevMode ? [{ debugName: "resolvedChartLabels" }] : /* istanbul ignore next */ []));
        /** CSS-ready color for the centered-mode bar. */
        this.centeredBarCssColor = computed(() => toChartColor(this.barColor()), ...(ngDevMode ? [{ debugName: "centeredBarCssColor" }] : /* istanbul ignore next */ []));
        /** CSS-ready spacer color. */
        this.centeredSpacerCssColor = computed(() => toChartColor(this.spacerColor()), ...(ngDevMode ? [{ debugName: "centeredSpacerCssColor" }] : /* istanbul ignore next */ []));
        this.total = computed(() => this.data()[0]?.value ?? 0, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
        this.hasData = computed(() => this.data().length > 0, ...(ngDevMode ? [{ debugName: "hasData" }] : /* istanbul ignore next */ []));
        // ── State ─────────────────────────────────────────────────────────────────────
        this.selectedItemLabel = computed(() => this.selectedItem()?.fullLabel ?? this.selectedItem()?.label ?? '', ...(ngDevMode ? [{ debugName: "selectedItemLabel" }] : /* istanbul ignore next */ []));
        this.selectedSpacerItem = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedSpacerItem" }] : /* istanbul ignore next */ []));
        this.sectionTooltips = signal([], ...(ngDevMode ? [{ debugName: "sectionTooltips" }] : /* istanbul ignore next */ []));
    }
    // ── Lifecycle ────────────────────────────────────────────────────────────────
    ngOnChanges(_changes) {
        if (!this.svg) {
            return;
        }
        requestAnimationFrame(() => this.updateChart());
    }
    ngAfterViewInit() {
        this.svg = select(this.chartRef().nativeElement);
        super.ngAfterViewInit();
    }
    // ── Chart rendering ──────────────────────────────────────────────────────────
    updateChart() {
        if (!this.svg) {
            return;
        }
        this.svg.selectAll('*').remove();
        if (this.loading() || !this.hasData()) {
            return;
        }
        const { width: containerWidth, height: containerHeight } = this.getContainerDimensions();
        if (this.orientation() === 'centered') {
            this.renderCenteredChart(containerWidth, containerHeight);
        }
        else {
            const funnelWidth = this.getFunnelWidth(containerWidth);
            const barHeight = this.getBarHeight(containerHeight);
            const dataPoints = this.calculateDataPoints(funnelWidth, barHeight);
            this.renderRightSideText(dataPoints, containerWidth, barHeight);
            this.renderLeftSideText(dataPoints, barHeight);
            this.renderSideLines(dataPoints, containerWidth, funnelWidth, barHeight);
            this.renderFunnel(dataPoints, containerWidth, containerHeight, funnelWidth, barHeight);
        }
    }
    // ── Data calculation ─────────────────────────────────────────────────────────
    calculateDataPoints(funnelWidth, barHeight) {
        const maxValue = max(this.data(), d => d.value) ?? 1;
        const widthScale = scaleLinear().domain([0, maxValue]).range([0, funnelWidth]);
        const sectionColorOverrides = this.sectionColors();
        return this.data().map((d, i) => {
            let xOffset = 0;
            const sections = (d.sections ?? []).map((s, si) => {
                const rawColor = s.color ?? sectionColorOverrides[s.key] ?? DEFAULT_SECTION_COLORS[si % DEFAULT_SECTION_COLORS.length];
                const cssColor = toChartColor(rawColor);
                const hoverColor = rawColor.startsWith('--')
                    ? cssColor
                    : (color(rawColor)?.darker(0.5)?.formatHex() ?? cssColor);
                const textColor = s.textColor ?? DEFAULT_TEXT_COLORS[si % DEFAULT_TEXT_COLORS.length];
                const sWidth = widthScale(s.value);
                const sec = {
                    key: s.key,
                    label: s.label,
                    x: xOffset,
                    width: sWidth,
                    value: s.value,
                    percentage: d.value ? this.round((s.value / d.value) * 100) : 0,
                    cssColor,
                    hoverColor,
                    textColor,
                };
                xOffset += sWidth;
                return sec;
            });
            const point = {
                ...d,
                sections,
                y: i * (barHeight + this.barGap()),
                width: widthScale(d.value),
                percentage: this.total() ? this.round((d.value / this.total()) * 100) : 0,
            };
            if (i > 0) {
                const previousValue = this.data()[i - 1].value;
                point.delta = previousValue - d.value;
                point.deltaPercentage = previousValue === 0 ? 0 : this.round((point.delta / previousValue) * 100);
            }
            return point;
        });
    }
    // ── Rendering helpers ────────────────────────────────────────────────────────
    renderRightSideText(dataPoints, containerWidth, barHeight) {
        const rightG = this.svg.append('g').attr('transform', `translate(${containerWidth - this.textSize()},0)`);
        dataPoints.forEach((d, i) => {
            if (i === 0) {
                return;
            }
            const prev = dataPoints[i - 1];
            const labelY = (prev.y + barHeight + d.y) / 2 + 4;
            rightG
                .append('text')
                .attr('x', 0)
                .attr('y', labelY)
                .attr('text-anchor', 'start')
                .style('font-size', '12px')
                .style('fill', 'var(--cds-global-color-construction-400, #666)')
                .text(`-${this.round(d.deltaPercentage)}% (${d.delta})`);
        });
    }
    renderLeftSideText(dataPoints, barHeight) {
        const leftG = this.svg.append('g');
        const ICON_SPACE = 20;
        const positions = [];
        dataPoints.forEach(d => {
            const labelY = (d.y + barHeight + d.y) / 2 + 4;
            const iconSpacing = d.description ? ICON_SPACE : 0;
            const textLength = leftG
                .append('text')
                .attr('x', this.textSize() - iconSpacing)
                .attr('y', labelY - 8)
                .attr('text-anchor', 'end')
                .style('font-size', '13px')
                .style('font-weight', '600')
                .style('fill', 'var(--cds-global-color-gray-900, #21333b)')
                .style('cursor', 'pointer')
                .text(d.label)
                .on('click', () => this.valueClicked.emit({ value: d.value, label: d.label, key: d.key }))
                .node()
                .getComputedTextLength();
            if (d.description) {
                positions.push({
                    x: this.textSize() - iconSpacing + 5,
                    y: labelY - 24,
                    key: d.key,
                    description: d.description,
                });
            }
            leftG
                .append('line')
                .attr('x1', this.textSize() - textLength - iconSpacing)
                .attr('y1', labelY - 5)
                .attr('x2', this.textSize() - iconSpacing)
                .attr('y2', labelY - 5)
                .attr('stroke', 'var(--cds-global-color-construction-400, #666)')
                .attr('stroke-width', 2)
                .attr('stroke-dasharray', '2,2');
            leftG
                .append('text')
                .attr('x', this.textSize())
                .attr('y', labelY + 10)
                .attr('text-anchor', 'end')
                .style('font-size', '12px')
                .style('fill', 'var(--cds-global-color-construction-400, #666)')
                .text(`${this.round(d.percentage)}% (${d.value})`);
        });
        this.sectionTooltips.set(positions);
    }
    renderSideLines(dataPoints, containerWidth, funnelWidth, barHeight) {
        const lineG = this.svg.append('g').attr('transform', `translate(${(containerWidth - funnelWidth) / 2},0)`);
        dataPoints.forEach(d => {
            const midY = d.y + barHeight / 2;
            lineG
                .append('line')
                .attr('x1', 0)
                .attr('y1', midY)
                .attr('x2', funnelWidth)
                .attr('y2', midY)
                .attr('stroke', '#D9D9D9')
                .attr('stroke-width', 1);
            this.renderSideLine(lineG, funnelWidth, d.y, barHeight);
            if (d.value === 0) {
                this.renderSideLine(lineG, 0, d.y, barHeight);
            }
        });
    }
    renderSideLine(g, x, y, barHeight) {
        g.append('line')
            .attr('x1', x)
            .attr('y1', y + this.sideLineVerticalPadding())
            .attr('x2', x)
            .attr('y2', y + barHeight - this.sideLineVerticalPadding())
            .attr('stroke', '#D9D9D9')
            .attr('stroke-width', 1);
    }
    renderFunnel(dataPoints, containerWidth, containerHeight, funnelWidth, barHeight) {
        const funnelG = this.svg
            .attr('width', containerWidth)
            .attr('height', containerHeight)
            .append('g')
            .attr('transform', `translate(${(containerWidth - funnelWidth) / 2},0)`);
        // First pass: all rects (z-order: rects below texts)
        dataPoints.forEach(dp => {
            dp.sections.forEach(section => {
                funnelG
                    .append('rect')
                    .attr('x', section.x)
                    .attr('y', dp.y)
                    .attr('width', section.width)
                    .attr('height', barHeight)
                    .style('fill', section.cssColor)
                    .style('cursor', 'pointer')
                    .on('mouseover', e => {
                    select(e.currentTarget).style('fill', section.hoverColor);
                })
                    .on('mouseout', e => {
                    select(e.currentTarget).style('fill', section.cssColor);
                })
                    .on('click', (event) => {
                    this.handleSectionClick(event, dp, section.key);
                });
            });
        });
        // Second pass: texts on top
        dataPoints.forEach(dp => {
            dp.sections.forEach(section => {
                if (section.width > 40) {
                    funnelG
                        .append('text')
                        .attr('x', section.x + section.width / 2)
                        .attr('y', dp.y + barHeight / 2)
                        .attr('text-anchor', 'middle')
                        .attr('dominant-baseline', 'middle')
                        .style('font-size', '13px')
                        .style('font-weight', '600')
                        .style('pointer-events', 'none')
                        .style('fill', section.textColor)
                        .text(`${section.percentage}%`);
                }
            });
        });
    }
    // ── Event handlers ────────────────────────────────────────────────────────────
    handleSectionClick(event, d, _sectionKey) {
        event.stopPropagation();
        const target = event.currentTarget;
        const rect = target.getBoundingClientRect();
        const container = this.chartRef().nativeElement.getBoundingClientRect();
        this.tooltipPosition.set({
            x: rect.left - container.left + rect.width / 2,
            y: rect.bottom - container.top,
        });
        this.selectedItem.set(d);
    }
    resetTooltip() {
        if (this.selectedItem() || this.selectedSpacerItem() || this.tooltipPosition()) {
            this.selectedItem.set(undefined);
            this.selectedSpacerItem.set(undefined);
            this.tooltipPosition.set(undefined);
        }
    }
    /** Formats a percentage to 2 decimal places for template use. */
    pct(value, total) {
        return this.round(total ? (value / total) * 100 : 0);
    }
    // ── Centered chart ────────────────────────────────────────────────────────────
    renderCenteredChart(containerWidth, containerHeight) {
        const maxFunnelWidth = this.getMaxFunnelWidth(containerWidth);
        const barHeight = this.getCenteredBarHeight(containerHeight);
        const spacerHeight = this.getCenteredSpacerHeight(containerHeight);
        const dataPoints = this.calculateCenteredDataPoints(maxFunnelWidth, barHeight, spacerHeight);
        const spacerPoints = this.calculateSpacerDataPoints(dataPoints, maxFunnelWidth, barHeight);
        this.renderCenteredLeftSideText(dataPoints, barHeight);
        this.renderCenteredLeftSideLines(dataPoints, maxFunnelWidth, barHeight);
        this.renderCenteredRightSideText(dataPoints, containerWidth, barHeight);
        this.renderCenteredRightSideLines(dataPoints, containerWidth, maxFunnelWidth, barHeight);
        this.renderCenteredMiddleLines(dataPoints, spacerPoints, containerWidth, barHeight);
        this.renderCenteredFunnel(dataPoints, spacerPoints, containerWidth, containerHeight, maxFunnelWidth, barHeight, spacerHeight);
    }
    calculateCenteredDataPoints(maxFunnelWidth, barHeight, spacerHeight) {
        const total = this.data()[0]?.value ?? 1;
        const maxValue = max(this.data(), d => d.value) ?? 1;
        const scale = scaleLinear().domain([0, maxValue]).range([0, maxFunnelWidth]);
        return this.data().map((d, i) => {
            const point = {
                ...d,
                y: i * (barHeight + spacerHeight),
                width: scale(d.value),
                percentage: total ? this.round((d.value / total) * 100) : 0,
            };
            if (i > 0) {
                const prev = this.data()[i - 1].value;
                point.delta = prev - d.value;
                point.deltaPercentage = prev === 0 ? 0 : this.round((point.delta / prev) * 100);
            }
            return point;
        });
    }
    calculateSpacerDataPoints(dataPoints, maxFunnelWidth, barHeight) {
        const spacers = [];
        for (let i = 0; i < dataPoints.length - 1; i++) {
            const cur = dataPoints[i];
            const next = dataPoints[i + 1];
            spacers.push({
                index: i,
                topWidth: cur.width,
                bottomWidth: next.width,
                topY: cur.y + barHeight,
                bottomY: next.y,
                topX: (maxFunnelWidth - cur.width) / 2,
                bottomX: (maxFunnelWidth - next.width) / 2,
                deltaPercentage: next.deltaPercentage ?? 0,
            });
        }
        return spacers;
    }
    renderCenteredLeftSideText(dataPoints, barHeight) {
        const g = this.svg.append('g').attr('transform', `translate(${this.textSize()},0)`);
        const ICON_SPACE = 20;
        const positions = [];
        dataPoints.forEach(d => {
            const cy = d.y + barHeight / 2;
            const iconSpacing = d.description ? ICON_SPACE : 0;
            const textLen = g
                .append('text')
                .attr('x', -iconSpacing)
                .attr('y', cy - 8)
                .attr('text-anchor', 'end')
                .style('font-size', '13px')
                .style('font-weight', '600')
                .style('fill', 'var(--cds-global-color-gray-900, #21333b)')
                .style('cursor', 'pointer')
                .text(d.label)
                .on('click', () => this.valueClicked.emit({ value: d.value, label: d.label, key: d.key }))
                .node()
                .getComputedTextLength();
            if (d.description) {
                positions.push({ key: d.key, x: this.textSize() - iconSpacing + 5, y: cy - 24, description: d.description });
            }
            g.append('line')
                .attr('x1', -textLen - iconSpacing)
                .attr('y1', cy - 5)
                .attr('x2', -iconSpacing)
                .attr('y2', cy - 5)
                .attr('stroke', 'var(--cds-global-color-construction-400, #666)')
                .attr('stroke-width', 2)
                .attr('stroke-dasharray', '2,2');
            g.append('text')
                .attr('x', 0)
                .attr('y', cy + 10)
                .attr('text-anchor', 'end')
                .style('font-size', '12px')
                .style('fill', 'var(--cds-global-color-construction-400, #666)')
                .text(`${this.round(d.percentage)}% (${d.value})`);
        });
        this.sectionTooltips.set(positions);
    }
    renderCenteredLeftSideLines(dataPoints, maxFunnelWidth, barHeight) {
        const g = this.svg.append('g').attr('transform', `translate(${this.textSize() + this.lineTextPadding()},0)`);
        dataPoints.forEach(d => {
            g.append('line')
                .attr('x1', 0)
                .attr('y1', d.y + barHeight / 2)
                .attr('x2', maxFunnelWidth / 2 + this.minLineSize())
                .attr('y2', d.y + barHeight / 2)
                .attr('stroke', '#D9D9D9')
                .attr('stroke-width', 1);
        });
    }
    renderCenteredRightSideText(dataPoints, containerWidth, barHeight) {
        const g = this.svg.append('g').attr('transform', `translate(${containerWidth - this.textSize()},0)`);
        dataPoints.forEach((d, i) => {
            if (i === 0) {
                return;
            }
            const prev = dataPoints[i - 1];
            const cy = (prev.y + barHeight + d.y) / 2 + 4;
            g.append('text')
                .attr('x', 0)
                .attr('y', cy)
                .attr('text-anchor', 'start')
                .style('font-size', '12px')
                .style('fill', 'var(--cds-global-color-construction-400, #666)')
                .text(`-${this.round(d.deltaPercentage)}% (${d.delta})`);
        });
    }
    renderCenteredRightSideLines(dataPoints, containerWidth, maxFunnelWidth, barHeight) {
        const g = this.svg.append('g').attr('transform', `translate(${containerWidth / 2},0)`);
        dataPoints.forEach((d, i) => {
            if (i === 0) {
                return;
            }
            const prev = dataPoints[i - 1];
            const cy = (prev.y + barHeight + d.y) / 2;
            g.append('line')
                .attr('x1', 0)
                .attr('y1', cy)
                .attr('x2', maxFunnelWidth / 2 + this.minLineSize())
                .attr('y2', cy)
                .attr('stroke', '#D9D9D9')
                .attr('stroke-width', 1);
        });
    }
    renderCenteredMiddleLines(dataPoints, spacerPoints, containerWidth, barHeight) {
        const vPad = this.middleLineVerticalPadding();
        const g = this.svg.append('g').attr('transform', `translate(${containerWidth / 2},0)`);
        g.selectAll('.bar-mid')
            .data(dataPoints)
            .enter()
            .append('line')
            .attr('x1', 0)
            .attr('y1', d => d.y + vPad)
            .attr('x2', 0)
            .attr('y2', d => d.y + barHeight - vPad)
            .attr('stroke', '#D9D9D9')
            .attr('stroke-width', 1);
        g.selectAll('.spacer-mid')
            .data(spacerPoints)
            .enter()
            .append('line')
            .attr('x1', 0)
            .attr('y1', d => d.topY + vPad)
            .attr('x2', 0)
            .attr('y2', d => d.bottomY - vPad)
            .attr('stroke', '#D9D9D9')
            .attr('stroke-width', 1);
    }
    renderCenteredFunnel(dataPoints, spacerPoints, containerWidth, containerHeight, maxFunnelWidth, barHeight, spacerHeight) {
        const g = this.svg
            .attr('width', containerWidth)
            .attr('height', containerHeight)
            .append('g')
            .attr('transform', `translate(${(containerWidth - maxFunnelWidth) / 2},0)`);
        // ── Bars ──────────────────────────────────────────────────────────────────
        const rawBarColor = this.barColor();
        const cssBarColor = toChartColor(rawBarColor);
        const hoverBarColor = rawBarColor.startsWith('--')
            ? cssBarColor
            : (color(rawBarColor)?.darker(0.5)?.formatHex() ?? cssBarColor);
        const bars = g
            .selectAll('.c-bar')
            .data(dataPoints)
            .enter()
            .append('g')
            .attr('class', 'c-bar');
        bars
            .append('rect')
            .attr('x', d => (maxFunnelWidth - d.width) / 2)
            .attr('y', d => d.y)
            .attr('width', d => d.width)
            .attr('height', barHeight)
            .style('fill', cssBarColor)
            .style('cursor', 'pointer')
            .on('mouseover', e => {
            select(e.currentTarget).style('fill', hoverBarColor);
        })
            .on('mouseout', e => {
            select(e.currentTarget).style('fill', cssBarColor);
        })
            .on('click', (event, d) => {
            event.stopPropagation();
            const rect = event.currentTarget.getBoundingClientRect();
            const cont = this.chartRef().nativeElement.getBoundingClientRect();
            this.tooltipPosition.set({ x: rect.left - cont.left + rect.width / 2, y: rect.top - cont.top });
            this.selectedSpacerItem.set(undefined);
            this.selectedItem.set(d);
        });
        bars
            .selectAll('.c-bar-label')
            .data(dataPoints.filter(d => d.width > 40))
            .enter()
            .append('text')
            .attr('x', maxFunnelWidth / 2)
            .attr('y', d => d.y + barHeight / 2)
            .attr('text-anchor', 'middle')
            .attr('dominant-baseline', 'middle')
            .style('font-size', '13px')
            .style('font-weight', '600')
            .style('pointer-events', 'none')
            .style('fill', '#eee')
            .text(d => `${this.round(d.percentage)}%`);
        // ── Spacers ───────────────────────────────────────────────────────────────
        const rawSpacerColor = this.spacerColor();
        const cssSpacerColor = toChartColor(rawSpacerColor);
        const hoverSpacerColor = rawSpacerColor.startsWith('--')
            ? cssSpacerColor
            : (color(rawSpacerColor)?.darker(0.2)?.formatHex() ?? cssSpacerColor);
        g.selectAll('.c-spacer')
            .data(spacerPoints)
            .enter()
            .append('polygon')
            .attr('class', 'c-spacer')
            .attr('points', d => `${d.topX},${d.topY} ${d.topX + d.topWidth},${d.topY} ${d.bottomX + d.bottomWidth},${d.bottomY} ${d.bottomX},${d.bottomY}`)
            .style('fill', cssSpacerColor)
            .style('cursor', 'pointer')
            .on('mouseover', e => {
            select(e.currentTarget).style('fill', hoverSpacerColor);
        })
            .on('mouseout', e => {
            select(e.currentTarget).style('fill', cssSpacerColor);
        })
            .on('click', (event, d) => {
            event.stopPropagation();
            const rect = event.currentTarget.getBoundingClientRect();
            const cont = this.chartRef().nativeElement.getBoundingClientRect();
            this.tooltipPosition.set({ x: rect.left - cont.left + rect.width / 2, y: rect.top - cont.top });
            this.selectedItem.set(undefined);
            this.selectedSpacerItem.set(d);
        });
        g.selectAll('.c-spacer-label')
            .data(spacerPoints.filter(d => d.bottomWidth > 40))
            .enter()
            .append('text')
            .attr('x', maxFunnelWidth / 2)
            .attr('y', d => d.topY + spacerHeight / 2)
            .attr('text-anchor', 'middle')
            .attr('dominant-baseline', 'middle')
            .style('font-size', '13px')
            .style('font-weight', '600')
            .style('pointer-events', 'none')
            .style('fill', '#333')
            .text(d => `-${this.round(d.deltaPercentage)}%`);
    }
    // ── Helpers ───────────────────────────────────────────────────────────────────
    getMaxFunnelWidth(containerWidth) {
        return containerWidth - this.textSize() * 2 - this.minLineSize() * 2 - this.lineTextPadding() * 2;
    }
    getCenteredFractionHeight(containerHeight) {
        return containerHeight / (this.data().length * (this.barToSpacerRatio() + 1) - 1);
    }
    getCenteredBarHeight(containerHeight) {
        return this.getCenteredFractionHeight(containerHeight) * this.barToSpacerRatio();
    }
    getCenteredSpacerHeight(containerHeight) {
        return this.getCenteredFractionHeight(containerHeight);
    }
    getFunnelWidth(containerWidth) {
        return containerWidth - this.textSize() * 2 - this.lineTextPadding() * 2;
    }
    getBarHeight(containerHeight) {
        const n = this.data().length;
        if (n === 0) {
            return 0;
        }
        return (containerHeight - (n - 1) * this.barGap()) / n;
    }
    round(value, decimals = 2) {
        const factor = 10 ** decimals;
        return Math.round(factor * value) / factor;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FunnelChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.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 }, exportButtonTitle: { classPropertyName: "exportButtonTitle", publicName: "exportButtonTitle", 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 }}:&nbsp;</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      &nbsp;({{ 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 }}:&nbsp;</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      &nbsp;({{ section.percentage }}%)\n    </p>\n    }\n\n    <p class=\"tooltip-row mt-0-5\">\n      <strong>{{ resolvedChartLabels().total }}:&nbsp;</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 }}:&nbsp;</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\n    class=\"funnel-export-btn\"\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n  />\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: "component", type: i1.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "component", type: i1.ClrSignpost, selector: "clr-signpost", inputs: ["clrSignpostTriggerAriaLabel", "clrSignpostHideTrigger"] }, { kind: "component", type: i1.ClrSignpostContent, selector: "clr-signpost-content", inputs: ["clrSignpostCloseAriaLabel", "clrPosition"] }, { kind: "directive", type: i1.ClrSignpostTrigger, selector: "[clrSignpostTrigger]" }, { kind: "component", type: ChartExportButtonComponent, selector: "cng-chart-export-button", inputs: ["svgRef", "filename", "buttonTitle", "legendItems"] }, { 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 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FunnelChartComponent, decorators: [{
            type: Component,
            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 }}:&nbsp;</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      &nbsp;({{ 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 }}:&nbsp;</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      &nbsp;({{ section.percentage }}%)\n    </p>\n    }\n\n    <p class=\"tooltip-row mt-0-5\">\n      <strong>{{ resolvedChartLabels().total }}:&nbsp;</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 }}:&nbsp;</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\n    class=\"funnel-export-btn\"\n    [svgRef]=\"svgElement()\"\n    [filename]=\"exportFilename()\"\n    [buttonTitle]=\"exportButtonTitle()\"\n  />\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"] }]
        }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], showExportButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExportButton", required: false }] }], exportButtonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportButtonTitle", required: false }] }], exportFilename: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportFilename", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], textSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "textSize", required: false }] }], lineTextPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "lineTextPadding", required: false }] }], barGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "barGap", required: false }] }], sideLineVerticalPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideLineVerticalPadding", required: false }] }], minLineSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLineSize", required: false }] }], barToSpacerRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "barToSpacerRatio", required: false }] }], middleLineVerticalPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "middleLineVerticalPadding", required: false }] }], barColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "barColor", required: false }] }], spacerColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "spacerColor", required: false }] }], chartLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartLabels", required: false }] }], sectionColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionColors", required: false }] }], valueClicked: [{ type: i0.Output, args: ["valueClicked"] }] } });

const PREFERRED_ORDER = ['top-right', 'top-left', 'bottom-right', 'bottom-left'];
class AutoPositionDirective {
    constructor() {
        this.signpostContent = inject(ClrSignpostContent, { optional: true });
        this.elementRef = inject(ElementRef);
        this.handleMouseDown = () => {
            // Only calculate when signpost is closed (about to open)
            if (this.elementRef.nativeElement.classList.contains('is-off-screen')) {
                this.updatePosition();
            }
        };
    }
    ngOnInit() {
        const signpost = this.elementRef.nativeElement.closest('clr-signpost');
        this.trigger = signpost?.querySelector('[clrSignpostTrigger]');
        if (this.trigger) {
            this.trigger.addEventListener('mousedown', this.handleMouseDown);
        }
        this.resizeObserver = new ResizeObserver(() => {
            if (!this.elementRef.nativeElement.classList.contains('is-off-screen')) {
                this.updatePosition();
            }
        });
        this.resizeObserver.observe(document.body);
    }
    updatePosition() {
        if (!this.signpostContent || !this.trigger) {
            return;
        }
        const triggerRect = this.trigger.getBoundingClientRect();
        this.signpostContent.position = this.getBestPosition(triggerRect);
    }
    getBestPosition(triggerRect) {
        const contentWidth = 300;
        const contentHeight = 200;
        const buffer = 50;
        const contentHeaderHeight = document.querySelector('.content-header')?.getBoundingClientRect().height ?? 0;
        const appHeaderHight = document.querySelector('.header')?.getBoundingClientRect().height ?? 0;
        const headerHeight = contentHeaderHeight + appHeaderHight;
        // Find first position with enough space
        for (const pos of PREFERRED_ORDER) {
            if (this.hasSpace(triggerRect, pos, contentWidth, contentHeight, headerHeight, buffer)) {
                return pos;
            }
        }
        return PREFERRED_ORDER[0]; // Fallback to first preference
    }
    hasSpace(rect, position, width, height, headerHeight, buffer) {
        const [vertical, horizontal] = position.split('-');
        const verticalSpace = vertical === 'top'
            ? rect.top - headerHeight - height - buffer
            : window.innerHeight - rect.bottom - height - buffer;
        const horizontalSpace = horizontal === 'left' ? rect.left - width - buffer : window.innerWidth - rect.right - width - buffer;
        return Math.min(verticalSpace, horizontalSpace) > 0;
    }
    ngOnDestroy() {
        if (this.trigger) {
            this.trigger.removeEventListener('mousedown', this.handleMouseDown);
        }
        this.resizeObserver?.disconnect();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: AutoPositionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.18", type: AutoPositionDirective, isStandalone: true, selector: "clr-signpost-content[autoPosition]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: AutoPositionDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'clr-signpost-content[autoPosition]',
                    standalone: true,
                }]
        }] });

class TenantFullDateRangeDirective {
    constructor() {
        this.start = contentChild(ClrStartDateInput, ...(ngDevMode ? [{ debugName: "start" }] : /* istanbul ignore next */ []));
        this.end = contentChild(ClrEndDateInput, ...(ngDevMode ? [{ debugName: "end" }] : /* istanbul ignore next */ []));
    }
    ngAfterContentInit() {
        if (this.start()) {
            const originalStartEmit = this.start().dateChange.emit.bind(this.start().dateChange);
            this.start().dateChange.emit = date => {
                originalStartEmit(this.toTenantDateTime(date, '00:00'));
            };
        }
        if (this.end()) {
            const originalEndEmit = this.end().dateChange.emit.bind(this.end().dateChange);
            this.end().dateChange.emit = date => {
                originalEndEmit(this.toTenantDateTime(date, '23:59'));
            };
        }
    }
    toTenantDateTime(date, time) {
        if (!date) {
            return date;
        }
        const [hours, minutes] = time.split(':').map(Number);
        const result = new Date(date);
        result.setHours(hours, minutes, 0, 0);
        return result;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TenantFullDateRangeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.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 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TenantFullDateRangeDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'clr-date-range-container[cngTenantFullDateRange]',
                    standalone: true,
                }]
        }], propDecorators: { start: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClrStartDateInput), { isSignal: true }] }], end: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClrEndDateInput), { isSignal: true }] }] } });

/*
 * Copyright (c) 2026 Porsche Informatik. All Rights Reserved.
 * This software is released under MIT license.
 * The full license information can be found in LICENSE in the root directory of this project.
 */
const CLR_CHARTS_DECLARATIONS = [
    AreaChartComponent,
    BarChartComponent,
    ComboChartComponent,
    FunnelChartComponent,
    LineChartComponent,
    PieChartComponent,
];
class ClrChartsModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ClrChartsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.18", ngImport: i0, type: ClrChartsModule, declarations: [AreaChartComponent,
            BarChartComponent,
            ComboChartComponent,
            FunnelChartComponent,
            LineChartComponent,
            PieChartComponent], imports: [CommonModule,
            DecimalPipe,
            ClrAlertModule,
            ClrIcon,
            ClrSignpostModule,
            // standalone helpers consumed by the non-standalone chart components
            ChartAlertOverlayComponent,
            ChartExportButtonComponent,
            ChartLegendComponent,
            ChartSkeletonComponent,
            ChartTooltipComponent,
            OutsideClickDirective,
            WindowResizeDirective], exports: [AreaChartComponent,
            BarChartComponent,
            ComboChartComponent,
            FunnelChartComponent,
            LineChartComponent,
            PieChartComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ClrChartsModule, imports: [CommonModule,
            ClrAlertModule,
            ClrIcon,
            ClrSignpostModule,
            // standalone helpers consumed by the non-standalone chart components
            ChartAlertOverlayComponent,
            ChartExportButtonComponent,
            ChartSkeletonComponent,
            ChartTooltipComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ClrChartsModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [
                        CommonModule,
                        DecimalPipe,
                        ClrAlertModule,
                        ClrIcon,
                        ClrSignpostModule,
                        // standalone helpers consumed by the non-standalone chart components
                        ChartAlertOverlayComponent,
                        ChartExportButtonComponent,
                        ChartLegendComponent,
                        ChartSkeletonComponent,
                        ChartTooltipComponent,
                        OutsideClickDirective,
                        WindowResizeDirective,
                    ],
                    declarations: [...CLR_CHARTS_DECLARATIONS],
                    exports: [...CLR_CHARTS_DECLARATIONS],
                }]
        }] });

/*
 * Copyright (c) 2026 Porsche Informatik. All Rights Reserved.
 * This software is released under MIT license.
 * The full license information can be found in LICENSE in the root directory of this project.
 */

/**
 * Generated bundle index. Do not edit.
 */

export { AreaChartComponent, BarChartComponent, ClrChartsModule, ComboChartComponent, FunnelChartComponent, LineChartComponent, PieChartComponent };
//# sourceMappingURL=porscheinformatik-clr-addons-charts.mjs.map