@nova-ui/dashboards
Version:
Nova Dashboards is a framework designed to provide feature developers with a common solution for presenting data coming from various sources within a single view, as well as a set of predefined widget visualizations that are 100% configuration-driven and
333 lines • 84.9 kB
JavaScript
// © 2022 SolarWinds Worldwide, LLC. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import { ChangeDetectorRef, Component, ElementRef, HostBinding, Inject, Input, KeyValueDiffers, NgZone, ViewChild, ViewEncapsulation, } from "@angular/core";
import isEqual from "lodash/isEqual";
import some from "lodash/some";
import { EventBus, LoggerService, UnitConversionService, } from "@nova-ui/bits";
import { Chart, ChartAssist, ChartDonutContentPlugin, ChartPalette, defaultColorProvider, MappedValueProvider, SELECT_DATA_POINT_EVENT, SequentialColorProvider, } from "@nova-ui/charts";
import { DashboardUnitConversionPipe } from "../../common/pipes/dashboard-unit-conversion-pipe";
import { CategoryChartUtilService } from "../../services/category-chart-util.service";
import { INTERACTION } from "../../services/types";
import { DATA_SOURCE, PIZZAGNA_EVENT_BUS, WellKnownDataSourceFeatures, } from "../../types";
import { LegendPlacement } from "../../widget-types/common/widget/legend";
import { ProportionalWidgetChartTypes, } from "./types";
import * as i0 from "@angular/core";
import * as i1 from "@nova-ui/bits";
import * as i2 from "@angular/common";
import * as i3 from "@nova-ui/charts";
import * as i4 from "@angular/cdk/portal";
import * as i5 from "../../pizzagna/directives/component-portal/component-portal.directive";
import * as i6 from "./proportional-donut-content/proportional-donut-content.component";
import * as i7 from "../../common/pipes/dashboard-unit-conversion-pipe";
/** @ignore */
export class ProportionalWidgetComponent {
static { this.lateLoadKey = "ProportionalWidgetComponent"; }
static { this.NO_SWITCH_LAYOUT_INTERVAL_SIZE = 20; }
static { this.MAX_ROW_LAYOUT_SIZE = 360; }
static { this.TICK_LABEL_MAX_WIDTH = 75; }
get interactive() {
return (this.configuration?.interactive ||
this.dataSource?.features?.getFeatureConfig(WellKnownDataSourceFeatures.Interactivity)?.enabled ||
false);
}
constructor(changeDetector, ngZone, kvDiffers, eventBus, dataSource, logger, unitConversionService) {
this.changeDetector = changeDetector;
this.ngZone = ngZone;
this.kvDiffers = kvDiffers;
this.eventBus = eventBus;
this.dataSource = dataSource;
this.logger = logger;
this.prioritizedGridRows = {
right: false,
bottom: false,
};
this.chartPalette = new ChartPalette(defaultColorProvider());
this.differ = this.kvDiffers.find(this.prioritizedGridRows).create();
this.unitConversionPipe = new DashboardUnitConversionPipe(unitConversionService);
}
// Note: Using this helper method to be able to use
// optional method chaining and prevent strict mode error
computeLegendTileValue(legendSeries) {
// @ts-ignore: Suppressing series null parameter value error to avoid breaking default flow
return this.accessors.data?.value?.(legendSeries, 0, null, null);
}
ngOnChanges(changes) {
const newChartColors = changes.configuration?.currentValue?.chartColors;
const prevChartColors = changes.configuration?.previousValue?.chartColors;
if (changes.widgetData || !isEqual(newChartColors, prevChartColors)) {
this.updateChartColors();
}
if (changes.configuration) {
const newChartType = changes.configuration.currentValue.chartOptions.type;
const prevChartType = changes.configuration.previousValue &&
changes.configuration.previousValue.chartOptions.type;
// configure the chart
if (newChartType && newChartType !== prevChartType) {
this.buildChart(newChartType);
if (this.widgetData) {
this.updateChart();
}
}
this.legendFormatter =
this.configuration.chartOptions.legendFormatter;
this.contentFormatter =
this.configuration.chartOptions.contentFormatter;
this.chartFormatterComponentType =
this.configuration.chartOptions.chartFormatterComponentType;
this.getContentFormatterProperties();
this.changeDetector.markForCheck();
}
if (changes.widgetData) {
if (this.chartAssist) {
this.updateChart();
this.getContentFormatterProperties();
this.changeDetector.markForCheck();
}
}
}
ngAfterViewInit() {
this.handleGridFlowOnResize();
}
ngOnDestroy() {
this.proportionalWidgetResizeObserver?.disconnect();
this.chartTypeSubscription$?.unsubscribe();
}
getContentFormatterProperties() {
this.contentFormatterProperties = {
data: this.widgetData,
config: this.configuration,
chartAssist: this.chartAssist,
properties: this.contentFormatter?.properties,
};
}
/** Checks if chart is donut. */
isDonutChart() {
return (this.configuration.chartOptions.type ===
ProportionalWidgetChartTypes.DonutChart);
}
/** Checks if chart is radial. */
isRadialChart() {
return ([
ProportionalWidgetChartTypes.DonutChart,
ProportionalWidgetChartTypes.PieChart,
].indexOf(this.configuration.chartOptions.type) !== -1);
}
/** Checks if legend should be shown. */
hasLegend() {
return (this.configuration.chartOptions.legendPlacement !==
LegendPlacement.None);
}
/** Checks if legend should be aligned to right. */
legendShouldBeAlignedRight() {
return (this.configuration.chartOptions.legendPlacement ===
LegendPlacement.Right);
}
onInteraction(data) {
if (!this.interactive) {
return;
}
this.eventBus.getStream(INTERACTION).next({ payload: { data } });
}
/** Configures the chart options */
buildChart(chartType) {
this.donutContentPlugin = null;
const { grid, accessors, renderer, scales, preprocessor } = CategoryChartUtilService.getChartAttributes(chartType, this.chartPalette?.standardColors);
// TODO: Refactor this to be able to pass different types of preprocessor to get rid of the any
this.chartAssist = new ChartAssist(new Chart(grid), preprocessor, this.chartPalette);
this.renderer = renderer;
this.accessors = accessors;
this.scales = scales;
if (this.isDonutChart()) {
this.donutContentPlugin = new ChartDonutContentPlugin();
this.chartAssist.chart.addPlugin(this.donutContentPlugin);
}
if (this.configuration.chartOptions.type ===
ProportionalWidgetChartTypes.HorizontalBarChart) {
this.scales.x.formatters.tick = (value) => this.unitConversionPipe.transform(value);
this.applyTickLabelMaxWidths();
}
else if (this.configuration.chartOptions.type ===
ProportionalWidgetChartTypes.VerticalBarChart) {
this.scales.y.formatters.tick = (value) => this.unitConversionPipe.transform(value);
}
this.chartTypeSubscription$?.unsubscribe();
this.chartTypeSubscription$ = this.chartAssist.chart
.getEventBus()
.getStream(SELECT_DATA_POINT_EVENT)
.subscribe((event) => {
// event payload is a data point from the chart - since we display one data point for every series,
// we convert the data point to the original series
const series = this.widgetData.find((s) => s.id === event.data.seriesId);
this.onInteraction(series);
});
}
handleGridFlowOnResize() {
this.proportionalWidgetResizeObserver = new ResizeObserver(() => this.onResize());
this.ngZone.runOutsideAngular(() => {
this.proportionalWidgetResizeObserver.observe(this.gridContainer.nativeElement);
});
}
applyTickLabelMaxWidths() {
const gridConfigAxis = this.chartAssist.chart.getGrid().config().axis;
gridConfigAxis.left.tickLabel.maxWidth =
this.configuration.chartOptions.horizontalBarTickLabelConfig
?.maxWidth?.left ??
ProportionalWidgetComponent.TICK_LABEL_MAX_WIDTH;
gridConfigAxis.right.tickLabel.maxWidth =
this.configuration.chartOptions.horizontalBarTickLabelConfig
?.maxWidth?.right ??
ProportionalWidgetComponent.TICK_LABEL_MAX_WIDTH;
}
onResize() {
if (this.isContainerInNoSwitchLayoutInterval()) {
return;
}
switch (this.configuration.chartOptions.legendPlacement) {
case LegendPlacement.Bottom:
this.prioritizedGridRows.bottom =
this.containerHasRowLayoutWidth();
break;
case LegendPlacement.Right:
this.prioritizedGridRows.right =
this.containerHasRowLayoutWidth();
break;
}
if (this.differ.diff(this.prioritizedGridRows)) {
this.changeDetector.detectChanges();
}
}
isContainerInNoSwitchLayoutInterval() {
const containerWidth = this.gridContainer.nativeElement.getBoundingClientRect().width;
return (containerWidth >
ProportionalWidgetComponent.MAX_ROW_LAYOUT_SIZE -
ProportionalWidgetComponent.NO_SWITCH_LAYOUT_INTERVAL_SIZE /
2 &&
containerWidth <
ProportionalWidgetComponent.MAX_ROW_LAYOUT_SIZE +
ProportionalWidgetComponent.NO_SWITCH_LAYOUT_INTERVAL_SIZE /
2);
}
containerHasRowLayoutWidth() {
const containerWidth = this.gridContainer.nativeElement.getBoundingClientRect().width;
return containerWidth < ProportionalWidgetComponent.MAX_ROW_LAYOUT_SIZE;
}
/** Builds the chart */
updateChart() {
this.chartAssist.update(CategoryChartUtilService.buildChartSeries(this.widgetData, this.accessors, this.renderer, this.scales));
}
updateChartColors() {
let colorProvider;
const dataColors = this.widgetData?.map((v) => v.color);
const configurationColors = this.configuration.chartColors;
if (!this.configuration.prioritizeWidgetColors && some(dataColors)) {
colorProvider = this.getDataDriverColorProvider(this.widgetData);
}
else if (configurationColors) {
colorProvider =
this.getConfigurationColorProvider(configurationColors);
}
else {
colorProvider = defaultColorProvider();
}
this.chartPalette = new ChartPalette(colorProvider);
this.buildChart(this.configuration?.chartOptions.type);
if (this.chartAssist) {
this.chartAssist.palette = this.chartPalette;
this.updateChart();
}
}
getDataDriverColorProvider(widgetData) {
let colorProvider;
const dataColors = widgetData?.map((v) => v.color).filter((v) => !!v);
if (dataColors.length === widgetData.length) {
const colorMap = widgetData.reduce((acc, next) => {
acc[next.id] = next.color;
return acc;
}, {});
colorProvider = new MappedValueProvider(colorMap);
}
else {
const widgetDataWithColor = widgetData.filter((series) => series.color);
this.logger.warn(`Not all series have colors set, setting default pallette. Current series color config: ${JSON.stringify(widgetDataWithColor)}`);
colorProvider = defaultColorProvider();
}
return colorProvider;
}
getConfigurationColorProvider(configurationColors) {
let colorProvider;
// remove data colors since nui-chart takes them into consideration no matter what
if (this.configuration.prioritizeWidgetColors && this.widgetData) {
this.widgetData = this.widgetData.map((origin) => {
const series = { ...origin };
if (series.color) {
delete series.color;
}
return series;
});
}
if (Array.isArray(configurationColors)) {
colorProvider = new SequentialColorProvider(configurationColors);
}
else {
const setupColorsLength = Object.keys(configurationColors).length;
if (setupColorsLength === this.widgetData?.length) {
colorProvider = new MappedValueProvider(configurationColors);
}
else {
// eslint-disable-next-line max-len
this.logger.warn(`Not all series have colors set, setting default pallette. Current series color config: ${JSON.stringify(configurationColors)}`);
colorProvider = defaultColorProvider();
}
}
return colorProvider;
}
get isEmpty() {
return (!this.widgetData ||
this.widgetData.length === 0 ||
!this.chartAssist);
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ProportionalWidgetComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i0.KeyValueDiffers }, { token: PIZZAGNA_EVENT_BUS }, { token: DATA_SOURCE }, { token: i1.LoggerService }, { token: i1.UnitConversionService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ProportionalWidgetComponent, selector: "nui-proportional-widget", inputs: { widgetData: "widgetData", configuration: "configuration", elementClass: "elementClass", seriesToIconMap: "seriesToIconMap" }, host: { properties: { "class": "this.elementClass" } }, viewQueries: [{ propertyName: "gridContainer", first: true, predicate: ["gridContainer"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div\n #gridContainer\n class=\"w-100 nui-chart-layout flex-grow-1 p-3\"\n [ngClass]=\"{\n 'prioritize-rows__right': prioritizedGridRows.right,\n 'prioritize-rows__bottom': prioritizedGridRows.bottom,\n 'empty-grid': !(widgetData && widgetData.length > 0 && chartAssist)\n }\"\n>\n <div class=\"d-flex chart has-overlay\" *ngIf=\"!isEmpty\">\n <nui-chart class=\"w-100\" [chart]=\"chartAssist.chart\"></nui-chart>\n <nui-chart-donut-content\n *ngIf=\"donutContentPlugin\"\n class=\"nui-proportional-widget__chart-donut-content\"\n [plugin]=\"donutContentPlugin\"\n >\n <div\n class=\"h-100 w-100 d-flex justify-content-center align-items-center text-center\"\n >\n <div class=\"d-inline-block\" nuiZoomContent [useZoom]=\"false\">\n <!-- OLD CONFIG HERE -->\n <ng-container\n *ngIf=\"!configuration?.chartOptions?.donutContentConfig\"\n >\n <ng-container\n nuiComponentPortal\n [componentType]=\"\n contentFormatter?.componentType ||\n 'DonutContentRawFormatterComponent'\n \"\n [properties]=\"contentFormatterProperties\"\n #componentPortal2=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal2.portal\"\n (attached)=\"componentPortal2.attached($event)\"\n ></ng-template>\n </ng-container>\n </ng-container>\n\n <!-- NEW CONFIG HERE -->\n <ng-container\n *ngIf=\"configuration?.chartOptions?.donutContentConfig\"\n >\n <nui-proportional-donut-content\n [widgetData]=\"widgetData\"\n [donutConfig]=\"\n configuration?.chartOptions?.donutContentConfig\n \"\n [chartAssist]=\"chartAssist\"\n ></nui-proportional-donut-content>\n </ng-container>\n </div>\n </div>\n </nui-chart-donut-content>\n </div>\n <div\n class=\"w-100 d-flex legend\"\n [ngClass]=\"[\n legendShouldBeAlignedRight() ? 'scrollable' : 'legend-bottom'\n ]\"\n *ngIf=\"hasLegend() && chartAssist\"\n >\n <nui-legend\n class=\"w-100 m-auto\"\n [interactive]=\"false\"\n (mouseleave)=\"chartAssist.resetVisibleSeries()\"\n [orientation]=\"\n legendShouldBeAlignedRight() ? 'vertical' : 'horizontal'\n \"\n >\n <nui-legend-series\n class=\"proportional-chart__legend-series\"\n *ngFor=\"\n let legendSeries of chartAssist.legendSeriesSet;\n trackBy: chartAssist.seriesTrackByFn;\n let i = index\n \"\n [ngClass]=\"\n (prioritizedGridRows.right || prioritizedGridRows.bottom) &&\n 'description-min-width--unset'\n \"\n [isSelected]=\"!chartAssist.isSeriesHidden(legendSeries.id)\"\n (isSelectedChange)=\"\n chartAssist.toggleSeries(legendSeries.id, $event)\n \"\n [seriesRenderState]=\"\n chartAssist.renderStatesIndex[legendSeries.id]?.state\n \"\n (mouseenter)=\"chartAssist.emphasizeSeries(legendSeries.id)\"\n (click)=\"onInteraction(legendSeries)\"\n [class.nui-proportional-widget__legend--interactive]=\"\n interactive\n \"\n >\n <div\n *ngIf=\"\n legendFormatter && legendFormatter.componentType;\n else legendDefaultTile\n \"\n >\n <div description>\n <ng-container\n nuiComponentPortal\n [componentType]=\"legendFormatter.componentType\"\n [properties]=\"{ data: widgetData[i] }\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n ></ng-template>\n </ng-container>\n </div>\n </div>\n <ng-template #legendDefaultTile>\n <nui-rich-legend-tile\n [value]=\"\n computeLegendTileValue(legendSeries.data[0])\n | nuiDashboardUnitConversion\n \"\n [unitLabel]=\"legendUnitLabel\"\n [color]=\"\n chartAssist.palette.textColors.get(legendSeries.id)\n \"\n [backgroundColor]=\"\n chartAssist.palette.standardColors.get(\n legendSeries.id\n )\n \"\n >\n </nui-rich-legend-tile>\n\n <nui-icon\n legendTransclusion\n *ngIf=\"\n seriesToIconMap && seriesToIconMap[legendSeries.id]\n \"\n class=\"align-items-center\"\n [icon]=\"seriesToIconMap[legendSeries.id]\"\n ></nui-icon>\n\n <div\n description\n class=\"description-container d-flex flex-column nui-text-small justify-content-center\"\n >\n <div\n *ngIf=\"!legendSeries.link\"\n class=\"description description-primary\"\n [ngClass]=\"{\n 'nui-text-link-small link': interactive\n }\"\n >\n {{ legendSeries.name }}\n </div>\n <a\n class=\"description description-primary nui-text-link-small link\"\n (click)=\"$event.stopPropagation()\"\n *ngIf=\"legendSeries.link\"\n [href]=\"legendSeries.link\"\n rel=\"noopener noreferrer\"\n [title]=\"legendSeries.name\"\n >\n {{ legendSeries.name }}\n </a>\n </div>\n </ng-template>\n </nui-legend-series>\n </nui-legend>\n </div>\n <div *ngIf=\"isEmpty\" class=\"is-empty\">\n <nui-image image=\"no-data-to-show\"></nui-image>\n </div>\n</div>\n", styles: [".nui-proportional-widget__chart-donut-content{position:absolute}.nui-proportional-widget__legend--interactive{cursor:pointer!important}.has-overlay{overflow:auto}.nui-chart-layout{grid-template:\"axis-label-left axis-label-right .\" 0fr \"chart chart legend\" minmax(60%,auto) \"axis-label-bottom axis-label-bottom .\" 0fr \"legend-bottom legend-bottom .\" minmax(auto,min-content) / 1fr 1fr auto}.nui-chart-layout .legend-bottom{overflow:auto;align-self:auto;padding-left:0}.prioritize-rows__right .nui-legend-series,.prioritize-rows__bottom .nui-legend-series{max-width:unset;width:100%}.prioritize-rows .legend{justify-self:start}.prioritize-rows__right{grid-template:\"chart chart .\" 60% \"legend legend .\" 40%}.prioritize-rows__bottom{grid-template:\"chart chart .\" 60% \"legend-bottom legend-bottom .\" 40%}.empty-grid{grid-template-columns:1fr;grid-template-rows:1fr}.proportional-chart__legend-series .link.description-primary{color:var(--nui-color-text-link,#0079aa)}.is-empty{display:grid;place-content:center;height:100%}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.LegendComponent, selector: "nui-legend", inputs: ["active", "interactive", "orientation", "seriesColor", "seriesIcon", "seriesUnitLabel"] }, { kind: "component", type: i3.LegendSeriesComponent, selector: "nui-legend-series", inputs: ["active", "interactive", "isSelected", "descriptionPrimary", "descriptionSecondary", "icon", "color", "seriesRenderState"], outputs: ["isSelectedChange"] }, { kind: "component", type: i3.RichLegendTileComponent, selector: "nui-rich-legend-tile", inputs: ["unitLabel", "value", "backgroundColor", "color"] }, { kind: "component", type: i3.ChartComponent, selector: "nui-chart", inputs: ["chart"] }, { kind: "component", type: i3.ChartDonutContentComponent, selector: "nui-chart-donut-content", inputs: ["plugin"] }, { kind: "component", type: i1.IconComponent, selector: "nui-icon", inputs: ["iconColor", "brushType", "iconHoverColor", "iconSize", "cssClass", "fillContainer", "status", "childStatus", "icon", "counter"] }, { kind: "component", type: i1.ImageComponent, selector: "nui-image", inputs: ["image", "description", "float", "margin", "isWatermark", "width", "height", "autoFill"] }, { kind: "directive", type: i4.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: i5.ComponentPortalDirective, selector: "[nuiComponentPortal]", inputs: ["componentId", "componentType", "properties", "providers", "outputs"], outputs: ["output"], exportAs: ["nuiComponentPortal"] }, { kind: "directive", type: i1.ZoomContentDirective, selector: "[nuiZoomContent]", inputs: ["zoomRatio", "minZoomDifference", "maxZoom", "minZoom", "useZoom", "margin", "minScale", "maxScale", "scaleIN$", "scaleOUT$"] }, { kind: "component", type: i6.ProportionalDonutContentComponent, selector: "nui-proportional-donut-content", inputs: ["widgetData", "donutConfig", "chartAssist"] }, { kind: "pipe", type: i7.DashboardUnitConversionPipe, name: "nuiDashboardUnitConversion" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ProportionalWidgetComponent, decorators: [{
type: Component,
args: [{ selector: "nui-proportional-widget", encapsulation: ViewEncapsulation.Emulated, template: "<div\n #gridContainer\n class=\"w-100 nui-chart-layout flex-grow-1 p-3\"\n [ngClass]=\"{\n 'prioritize-rows__right': prioritizedGridRows.right,\n 'prioritize-rows__bottom': prioritizedGridRows.bottom,\n 'empty-grid': !(widgetData && widgetData.length > 0 && chartAssist)\n }\"\n>\n <div class=\"d-flex chart has-overlay\" *ngIf=\"!isEmpty\">\n <nui-chart class=\"w-100\" [chart]=\"chartAssist.chart\"></nui-chart>\n <nui-chart-donut-content\n *ngIf=\"donutContentPlugin\"\n class=\"nui-proportional-widget__chart-donut-content\"\n [plugin]=\"donutContentPlugin\"\n >\n <div\n class=\"h-100 w-100 d-flex justify-content-center align-items-center text-center\"\n >\n <div class=\"d-inline-block\" nuiZoomContent [useZoom]=\"false\">\n <!-- OLD CONFIG HERE -->\n <ng-container\n *ngIf=\"!configuration?.chartOptions?.donutContentConfig\"\n >\n <ng-container\n nuiComponentPortal\n [componentType]=\"\n contentFormatter?.componentType ||\n 'DonutContentRawFormatterComponent'\n \"\n [properties]=\"contentFormatterProperties\"\n #componentPortal2=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal2.portal\"\n (attached)=\"componentPortal2.attached($event)\"\n ></ng-template>\n </ng-container>\n </ng-container>\n\n <!-- NEW CONFIG HERE -->\n <ng-container\n *ngIf=\"configuration?.chartOptions?.donutContentConfig\"\n >\n <nui-proportional-donut-content\n [widgetData]=\"widgetData\"\n [donutConfig]=\"\n configuration?.chartOptions?.donutContentConfig\n \"\n [chartAssist]=\"chartAssist\"\n ></nui-proportional-donut-content>\n </ng-container>\n </div>\n </div>\n </nui-chart-donut-content>\n </div>\n <div\n class=\"w-100 d-flex legend\"\n [ngClass]=\"[\n legendShouldBeAlignedRight() ? 'scrollable' : 'legend-bottom'\n ]\"\n *ngIf=\"hasLegend() && chartAssist\"\n >\n <nui-legend\n class=\"w-100 m-auto\"\n [interactive]=\"false\"\n (mouseleave)=\"chartAssist.resetVisibleSeries()\"\n [orientation]=\"\n legendShouldBeAlignedRight() ? 'vertical' : 'horizontal'\n \"\n >\n <nui-legend-series\n class=\"proportional-chart__legend-series\"\n *ngFor=\"\n let legendSeries of chartAssist.legendSeriesSet;\n trackBy: chartAssist.seriesTrackByFn;\n let i = index\n \"\n [ngClass]=\"\n (prioritizedGridRows.right || prioritizedGridRows.bottom) &&\n 'description-min-width--unset'\n \"\n [isSelected]=\"!chartAssist.isSeriesHidden(legendSeries.id)\"\n (isSelectedChange)=\"\n chartAssist.toggleSeries(legendSeries.id, $event)\n \"\n [seriesRenderState]=\"\n chartAssist.renderStatesIndex[legendSeries.id]?.state\n \"\n (mouseenter)=\"chartAssist.emphasizeSeries(legendSeries.id)\"\n (click)=\"onInteraction(legendSeries)\"\n [class.nui-proportional-widget__legend--interactive]=\"\n interactive\n \"\n >\n <div\n *ngIf=\"\n legendFormatter && legendFormatter.componentType;\n else legendDefaultTile\n \"\n >\n <div description>\n <ng-container\n nuiComponentPortal\n [componentType]=\"legendFormatter.componentType\"\n [properties]=\"{ data: widgetData[i] }\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n ></ng-template>\n </ng-container>\n </div>\n </div>\n <ng-template #legendDefaultTile>\n <nui-rich-legend-tile\n [value]=\"\n computeLegendTileValue(legendSeries.data[0])\n | nuiDashboardUnitConversion\n \"\n [unitLabel]=\"legendUnitLabel\"\n [color]=\"\n chartAssist.palette.textColors.get(legendSeries.id)\n \"\n [backgroundColor]=\"\n chartAssist.palette.standardColors.get(\n legendSeries.id\n )\n \"\n >\n </nui-rich-legend-tile>\n\n <nui-icon\n legendTransclusion\n *ngIf=\"\n seriesToIconMap && seriesToIconMap[legendSeries.id]\n \"\n class=\"align-items-center\"\n [icon]=\"seriesToIconMap[legendSeries.id]\"\n ></nui-icon>\n\n <div\n description\n class=\"description-container d-flex flex-column nui-text-small justify-content-center\"\n >\n <div\n *ngIf=\"!legendSeries.link\"\n class=\"description description-primary\"\n [ngClass]=\"{\n 'nui-text-link-small link': interactive\n }\"\n >\n {{ legendSeries.name }}\n </div>\n <a\n class=\"description description-primary nui-text-link-small link\"\n (click)=\"$event.stopPropagation()\"\n *ngIf=\"legendSeries.link\"\n [href]=\"legendSeries.link\"\n rel=\"noopener noreferrer\"\n [title]=\"legendSeries.name\"\n >\n {{ legendSeries.name }}\n </a>\n </div>\n </ng-template>\n </nui-legend-series>\n </nui-legend>\n </div>\n <div *ngIf=\"isEmpty\" class=\"is-empty\">\n <nui-image image=\"no-data-to-show\"></nui-image>\n </div>\n</div>\n", styles: [".nui-proportional-widget__chart-donut-content{position:absolute}.nui-proportional-widget__legend--interactive{cursor:pointer!important}.has-overlay{overflow:auto}.nui-chart-layout{grid-template:\"axis-label-left axis-label-right .\" 0fr \"chart chart legend\" minmax(60%,auto) \"axis-label-bottom axis-label-bottom .\" 0fr \"legend-bottom legend-bottom .\" minmax(auto,min-content) / 1fr 1fr auto}.nui-chart-layout .legend-bottom{overflow:auto;align-self:auto;padding-left:0}.prioritize-rows__right .nui-legend-series,.prioritize-rows__bottom .nui-legend-series{max-width:unset;width:100%}.prioritize-rows .legend{justify-self:start}.prioritize-rows__right{grid-template:\"chart chart .\" 60% \"legend legend .\" 40%}.prioritize-rows__bottom{grid-template:\"chart chart .\" 60% \"legend-bottom legend-bottom .\" 40%}.empty-grid{grid-template-columns:1fr;grid-template-rows:1fr}.proportional-chart__legend-series .link.description-primary{color:var(--nui-color-text-link,#0079aa)}.is-empty{display:grid;place-content:center;height:100%}\n"] }]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i0.KeyValueDiffers }, { type: i1.EventBus, decorators: [{
type: Inject,
args: [PIZZAGNA_EVENT_BUS]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [DATA_SOURCE]
}] }, { type: i1.LoggerService }, { type: i1.UnitConversionService }], propDecorators: { widgetData: [{
type: Input
}], configuration: [{
type: Input
}], elementClass: [{
type: Input
}, {
type: HostBinding,
args: ["class"]
}], seriesToIconMap: [{
type: Input
}], gridContainer: [{
type: ViewChild,
args: ["gridContainer", { static: true }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvcG9ydGlvbmFsLXdpZGdldC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvcHJvcG9ydGlvbmFsLXdpZGdldC9wcm9wb3J0aW9uYWwtd2lkZ2V0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9wcm9wb3J0aW9uYWwtd2lkZ2V0L3Byb3BvcnRpb25hbC13aWRnZXQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEseURBQXlEO0FBQ3pELEVBQUU7QUFDRiwrRUFBK0U7QUFDL0UsNEVBQTRFO0FBQzVFLDhFQUE4RTtBQUM5RSwrRUFBK0U7QUFDL0UsOEVBQThFO0FBQzlFLDREQUE0RDtBQUM1RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLHVEQUF1RDtBQUN2RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLDRFQUE0RTtBQUM1RSwrRUFBK0U7QUFDL0UsMEVBQTBFO0FBQzFFLGlGQUFpRjtBQUNqRiw2RUFBNkU7QUFDN0UsaUJBQWlCO0FBRWpCLE9BQU8sRUFFSCxpQkFBaUIsRUFDakIsU0FBUyxFQUNULFVBQVUsRUFDVixXQUFXLEVBQ1gsTUFBTSxFQUNOLEtBQUssRUFFTCxlQUFlLEVBQ2YsTUFBTSxFQUlOLFNBQVMsRUFDVCxpQkFBaUIsR0FDcEIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxPQUFPLE1BQU0sZ0JBQWdCLENBQUM7QUFDckMsT0FBTyxJQUFJLE1BQU0sYUFBYSxDQUFDO0FBRy9CLE9BQU8sRUFDSCxRQUFRLEVBR1IsYUFBYSxFQUNiLHFCQUFxQixHQUN4QixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQ0gsS0FBSyxFQUNMLFdBQVcsRUFDWCx1QkFBdUIsRUFDdkIsWUFBWSxFQUNaLG9CQUFvQixFQUtwQixtQkFBbUIsRUFHbkIsdUJBQXVCLEVBQ3ZCLHVCQUF1QixHQUUxQixN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