UNPKG

@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

343 lines 16 kB
"use strict"; // © 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. var ProportionalWidgetComponent_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProportionalWidgetComponent = void 0; const tslib_1 = require("tslib"); const core_1 = require("@angular/core"); const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual")); const some_1 = tslib_1.__importDefault(require("lodash/some")); const bits_1 = require("@nova-ui/bits"); const charts_1 = require("@nova-ui/charts"); const dashboard_unit_conversion_pipe_1 = require("../../common/pipes/dashboard-unit-conversion-pipe"); const category_chart_util_service_1 = require("../../services/category-chart-util.service"); const types_1 = require("../../services/types"); const types_2 = require("../../types"); const legend_1 = require("../../widget-types/common/widget/legend"); const types_3 = require("./types"); /** @ignore */ let ProportionalWidgetComponent = class ProportionalWidgetComponent { static { ProportionalWidgetComponent_1 = this; } 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(types_2.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 charts_1.ChartPalette((0, charts_1.defaultColorProvider)()); this.differ = this.kvDiffers.find(this.prioritizedGridRows).create(); this.unitConversionPipe = new dashboard_unit_conversion_pipe_1.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 || !(0, isEqual_1.default)(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 === types_3.ProportionalWidgetChartTypes.DonutChart); } /** Checks if chart is radial. */ isRadialChart() { return ([ types_3.ProportionalWidgetChartTypes.DonutChart, types_3.ProportionalWidgetChartTypes.PieChart, ].indexOf(this.configuration.chartOptions.type) !== -1); } /** Checks if legend should be shown. */ hasLegend() { return (this.configuration.chartOptions.legendPlacement !== legend_1.LegendPlacement.None); } /** Checks if legend should be aligned to right. */ legendShouldBeAlignedRight() { return (this.configuration.chartOptions.legendPlacement === legend_1.LegendPlacement.Right); } onInteraction(data) { if (!this.interactive) { return; } this.eventBus.getStream(types_1.INTERACTION).next({ payload: { data } }); } /** Configures the chart options */ buildChart(chartType) { this.donutContentPlugin = null; const { grid, accessors, renderer, scales, preprocessor } = category_chart_util_service_1.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 charts_1.ChartAssist(new charts_1.Chart(grid), preprocessor, this.chartPalette); this.renderer = renderer; this.accessors = accessors; this.scales = scales; if (this.isDonutChart()) { this.donutContentPlugin = new charts_1.ChartDonutContentPlugin(); this.chartAssist.chart.addPlugin(this.donutContentPlugin); } if (this.configuration.chartOptions.type === types_3.ProportionalWidgetChartTypes.HorizontalBarChart) { this.scales.x.formatters.tick = (value) => this.unitConversionPipe.transform(value); this.applyTickLabelMaxWidths(); } else if (this.configuration.chartOptions.type === types_3.ProportionalWidgetChartTypes.VerticalBarChart) { this.scales.y.formatters.tick = (value) => this.unitConversionPipe.transform(value); } this.chartTypeSubscription$?.unsubscribe(); this.chartTypeSubscription$ = this.chartAssist.chart .getEventBus() .getStream(charts_1.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_1.TICK_LABEL_MAX_WIDTH; gridConfigAxis.right.tickLabel.maxWidth = this.configuration.chartOptions.horizontalBarTickLabelConfig ?.maxWidth?.right ?? ProportionalWidgetComponent_1.TICK_LABEL_MAX_WIDTH; } onResize() { if (this.isContainerInNoSwitchLayoutInterval()) { return; } switch (this.configuration.chartOptions.legendPlacement) { case legend_1.LegendPlacement.Bottom: this.prioritizedGridRows.bottom = this.containerHasRowLayoutWidth(); break; case legend_1.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_1.MAX_ROW_LAYOUT_SIZE - ProportionalWidgetComponent_1.NO_SWITCH_LAYOUT_INTERVAL_SIZE / 2 && containerWidth < ProportionalWidgetComponent_1.MAX_ROW_LAYOUT_SIZE + ProportionalWidgetComponent_1.NO_SWITCH_LAYOUT_INTERVAL_SIZE / 2); } containerHasRowLayoutWidth() { const containerWidth = this.gridContainer.nativeElement.getBoundingClientRect().width; return containerWidth < ProportionalWidgetComponent_1.MAX_ROW_LAYOUT_SIZE; } /** Builds the chart */ updateChart() { this.chartAssist.update(category_chart_util_service_1.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 && (0, some_1.default)(dataColors)) { colorProvider = this.getDataDriverColorProvider(this.widgetData); } else if (configurationColors) { colorProvider = this.getConfigurationColorProvider(configurationColors); } else { colorProvider = (0, charts_1.defaultColorProvider)(); } this.chartPalette = new charts_1.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 charts_1.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 = (0, charts_1.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 charts_1.SequentialColorProvider(configurationColors); } else { const setupColorsLength = Object.keys(configurationColors).length; if (setupColorsLength === this.widgetData?.length) { colorProvider = new charts_1.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 = (0, charts_1.defaultColorProvider)(); } } return colorProvider; } get isEmpty() { return (!this.widgetData || this.widgetData.length === 0 || !this.chartAssist); } }; exports.ProportionalWidgetComponent = ProportionalWidgetComponent; tslib_1.__decorate([ (0, core_1.Input)(), tslib_1.__metadata("design:type", Array) ], ProportionalWidgetComponent.prototype, "widgetData", void 0); tslib_1.__decorate([ (0, core_1.Input)(), tslib_1.__metadata("design:type", Object) ], ProportionalWidgetComponent.prototype, "configuration", void 0); tslib_1.__decorate([ (0, core_1.Input)(), (0, core_1.HostBinding)("class"), tslib_1.__metadata("design:type", String) ], ProportionalWidgetComponent.prototype, "elementClass", void 0); tslib_1.__decorate([ (0, core_1.Input)(), tslib_1.__metadata("design:type", Object) ], ProportionalWidgetComponent.prototype, "seriesToIconMap", void 0); tslib_1.__decorate([ (0, core_1.ViewChild)("gridContainer", { static: true }), tslib_1.__metadata("design:type", core_1.ElementRef) ], ProportionalWidgetComponent.prototype, "gridContainer", void 0); exports.ProportionalWidgetComponent = ProportionalWidgetComponent = ProportionalWidgetComponent_1 = tslib_1.__decorate([ (0, core_1.Component)({ selector: "nui-proportional-widget", templateUrl: "./proportional-widget.component.html", styleUrls: ["./proportional-widget.component.less"], encapsulation: core_1.ViewEncapsulation.Emulated, standalone: false, }), tslib_1.__param(3, (0, core_1.Inject)(types_2.PIZZAGNA_EVENT_BUS)), tslib_1.__param(4, (0, core_1.Inject)(types_2.DATA_SOURCE)), tslib_1.__metadata("design:paramtypes", [core_1.ChangeDetectorRef, core_1.NgZone, core_1.KeyValueDiffers, bits_1.EventBus, Object, bits_1.LoggerService, bits_1.UnitConversionService]) ], ProportionalWidgetComponent); //# sourceMappingURL=proportional-widget.component.js.map