@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
JavaScript
"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