UI: Range chart widget config improvements.

This commit is contained in:
Igor Kulikov 2024-03-28 12:10:47 +02:00
parent 2fb604e398
commit ce3be06796
3 changed files with 189 additions and 172 deletions

View File

@ -30,116 +30,25 @@ import {
import { WidgetContext } from '@home/models/widget-component.models';
import {
backgroundStyle,
ColorRange,
ComponentStyle,
filterIncludingColorRanges,
getDataKey,
overlayStyle,
sortedColorRange,
textStyle
} from '@shared/models/widget-settings.models';
import { isDefinedAndNotNull, isNumber } from '@core/utils';
import { rangeChartDefaultSettings, RangeChartWidgetSettings } from './range-chart-widget.models';
import { isDefinedAndNotNull } from '@core/utils';
import {
rangeChartDefaultSettings,
rangeChartTimeSeriesKeySettings,
rangeChartTimeSeriesSettings,
RangeChartWidgetSettings,
RangeItem,
toRangeItems
} from './range-chart-widget.models';
import { Observable } from 'rxjs';
import { ImagePipe } from '@shared/pipe/image.pipe';
import { DomSanitizer } from '@angular/platform-browser';
import { DeepPartial } from '@shared/models/common';
import {
createTimeSeriesChartVisualMapPiece,
SeriesFillType,
TimeSeriesChartKeySettings,
TimeSeriesChartSettings,
TimeSeriesChartShape,
TimeSeriesChartThreshold,
TimeSeriesChartThresholdType, TimeSeriesChartVisualMapPiece
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart';
interface RangeItem {
index: number;
from?: number;
to?: number;
color: string;
label: string;
visible: boolean;
enabled: boolean;
piece: TimeSeriesChartVisualMapPiece;
}
const rangeItemLabel = (from?: number, to?: number): string => {
if (isNumber(from) && isNumber(to)) {
if (from === to) {
return `${from}`;
} else {
return `${from} - ${to}`;
}
} else if (isNumber(from)) {
return `${from}`;
} else if (isNumber(to)) {
return `< ${to}`;
} else {
return null;
}
};
const toRangeItems = (colorRanges: Array<ColorRange>): RangeItem[] => {
const rangeItems: RangeItem[] = [];
let counter = 0;
const ranges = sortedColorRange(filterIncludingColorRanges(colorRanges)).filter(r => isNumber(r.from) || isNumber(r.to));
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
let from = range.from;
const to = range.to;
if (i > 0) {
const prevRange = ranges[i - 1];
if (isNumber(prevRange.to) && isNumber(from) && from < prevRange.to) {
from = prevRange.to;
}
}
rangeItems.push(
{
index: counter++,
color: range.color,
enabled: true,
visible: true,
from,
to,
label: rangeItemLabel(from, to),
piece: createTimeSeriesChartVisualMapPiece(range.color, from, to)
}
);
if (!isNumber(from) || !isNumber(to)) {
const value = !isNumber(from) ? to : from;
rangeItems.push(
{
index: counter++,
color: 'transparent',
enabled: true,
visible: false,
label: '',
piece: { gt: value - 0.000000001, lt: value + 0.000000001, color: 'transparent'}
}
);
}
}
return rangeItems;
};
const getMarkPoints = (ranges: Array<RangeItem>): number[] => {
const points = new Set<number>();
for (const range of ranges) {
if (range.visible) {
if (isNumber(range.from)) {
points.add(range.from);
}
if (isNumber(range.to)) {
points.add(range.to);
}
}
}
return Array.from(points).sort();
};
@Component({
selector: 'tb-range-chart-widget',
templateUrl: './range-chart-widget.component.html',
@ -196,17 +105,7 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn
this.units = dataKey.units;
}
if (dataKey) {
dataKey.settings = {
type: 'line',
lineSettings: {
showLine: true,
smooth: false,
showPoints: false,
fillAreaSettings: {
type: this.settings.fillArea ? 'default' : SeriesFillType.none
}
}
} as DeepPartial<TimeSeriesChartKeySettings>;
dataKey.settings = rangeChartTimeSeriesKeySettings(this.settings);
}
this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer);
@ -226,64 +125,7 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn
}
ngAfterViewInit() {
const thresholds: DeepPartial<TimeSeriesChartThreshold>[] = getMarkPoints(this.rangeItems).map(item => ({
type: TimeSeriesChartThresholdType.constant,
yAxisId: 'default',
units: this.units,
decimals: this.decimals,
lineWidth: 1,
lineColor: '#37383b',
lineType: [3, 3],
startSymbol: TimeSeriesChartShape.circle,
startSymbolSize: 5,
endSymbol: TimeSeriesChartShape.arrow,
endSymbolSize: 7,
showLabel: true,
labelPosition: 'insideEndTop',
labelColor: '#37383b',
additionalLabelOption: {
backgroundColor: 'rgba(255,255,255,0.56)',
padding: [4, 5],
borderRadius: 4,
},
value: item
} as DeepPartial<TimeSeriesChartThreshold>));
const settings: DeepPartial<TimeSeriesChartSettings> = {
dataZoom: this.settings.dataZoom,
thresholds,
yAxes: {
default: {
show: true,
showLine: false,
showTicks: false,
showTickLabels: true,
showSplitLines: true,
decimals: this.decimals,
units: this.units
}
},
xAxis: {
show: true,
showLine: true,
showTicks: true,
showTickLabels: true,
showSplitLines: false
},
visualMapSettings: {
outOfRangeColor: this.settings.outOfRangeColor,
pieces: this.rangeItems.map(item => item.piece)
},
showTooltip: this.settings.showTooltip,
tooltipValueFont: this.settings.tooltipValueFont,
tooltipValueColor: this.settings.tooltipValueColor,
tooltipShowDate: this.settings.tooltipShowDate,
tooltipDateInterval: this.settings.tooltipDateInterval,
tooltipDateFormat: this.settings.tooltipDateFormat,
tooltipDateFont: this.settings.tooltipDateFont,
tooltipDateColor: this.settings.tooltipDateColor,
tooltipBackgroundColor: this.settings.tooltipBackgroundColor,
tooltipBackgroundBlur: this.settings.tooltipBackgroundBlur,
};
const settings = rangeChartTimeSeriesSettings(this.settings, this.rangeItems, this.decimals, this.units);
this.timeSeriesChart = new TbTimeSeriesChart(this.ctx, settings, this.chartShape.nativeElement, this.renderer);
}

View File

@ -18,11 +18,37 @@ import {
BackgroundSettings,
BackgroundType,
ColorRange,
filterIncludingColorRanges,
Font,
simpleDateFormat
simpleDateFormat,
sortedColorRange
} from '@shared/models/widget-settings.models';
import { LegendPosition } from '@shared/models/widget.models';
import { EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models';
import {
createTimeSeriesChartVisualMapPiece,
SeriesFillType,
TimeSeriesChartKeySettings,
TimeSeriesChartSeriesType,
TimeSeriesChartSettings,
TimeSeriesChartShape,
TimeSeriesChartThreshold,
TimeSeriesChartThresholdType,
TimeSeriesChartVisualMapPiece
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { isNumber } from '@core/utils';
import { DeepPartial } from '@shared/models/common';
export interface RangeItem {
index: number;
from?: number;
to?: number;
color: string;
label: string;
visible: boolean;
enabled: boolean;
piece: TimeSeriesChartVisualMapPiece;
}
export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings {
dataZoom: boolean;
@ -94,3 +120,152 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = {
}
}
};
export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[],
decimals: number, units: string): DeepPartial<TimeSeriesChartSettings> => {
const thresholds: DeepPartial<TimeSeriesChartThreshold>[] = getMarkPoints(rangeItems).map(item => ({
type: TimeSeriesChartThresholdType.constant,
yAxisId: 'default',
units,
decimals,
lineWidth: 1,
lineColor: '#37383b',
lineType: [3, 3],
startSymbol: TimeSeriesChartShape.circle,
startSymbolSize: 5,
endSymbol: TimeSeriesChartShape.arrow,
endSymbolSize: 7,
showLabel: true,
labelPosition: 'insideEndTop',
labelColor: '#37383b',
additionalLabelOption: {
backgroundColor: 'rgba(255,255,255,0.56)',
padding: [4, 5],
borderRadius: 4,
},
value: item
} as DeepPartial<TimeSeriesChartThreshold>));
return {
dataZoom: settings.dataZoom,
thresholds,
yAxes: {
default: {
show: true,
showLine: false,
showTicks: false,
showTickLabels: true,
showSplitLines: true,
decimals,
units
}
},
xAxis: {
show: true,
showLine: true,
showTicks: true,
showTickLabels: true,
showSplitLines: false
},
visualMapSettings: {
outOfRangeColor: settings.outOfRangeColor,
pieces: rangeItems.map(item => item.piece)
},
showTooltip: settings.showTooltip,
tooltipValueFont: settings.tooltipValueFont,
tooltipValueColor: settings.tooltipValueColor,
tooltipShowDate: settings.tooltipShowDate,
tooltipDateInterval: settings.tooltipDateInterval,
tooltipDateFormat: settings.tooltipDateFormat,
tooltipDateFont: settings.tooltipDateFont,
tooltipDateColor: settings.tooltipDateColor,
tooltipBackgroundColor: settings.tooltipBackgroundColor,
tooltipBackgroundBlur: settings.tooltipBackgroundBlur,
};
};
export const rangeChartTimeSeriesKeySettings = (settings: RangeChartWidgetSettings): DeepPartial<TimeSeriesChartKeySettings> => ({
type: TimeSeriesChartSeriesType.line,
lineSettings: {
showLine: true,
smooth: false,
showPoints: false,
fillAreaSettings: {
type: settings.fillArea ? SeriesFillType.opacity : SeriesFillType.none,
opacity: 0.7
}
}
});
export const toRangeItems = (colorRanges: Array<ColorRange>): RangeItem[] => {
const rangeItems: RangeItem[] = [];
let counter = 0;
const ranges = sortedColorRange(filterIncludingColorRanges(colorRanges)).filter(r => isNumber(r.from) || isNumber(r.to));
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
let from = range.from;
const to = range.to;
if (i > 0) {
const prevRange = ranges[i - 1];
if (isNumber(prevRange.to) && isNumber(from) && from < prevRange.to) {
from = prevRange.to;
}
}
rangeItems.push(
{
index: counter++,
color: range.color,
enabled: true,
visible: true,
from,
to,
label: rangeItemLabel(from, to),
piece: createTimeSeriesChartVisualMapPiece(range.color, from, to)
}
);
if (!isNumber(from) || !isNumber(to)) {
const value = !isNumber(from) ? to : from;
rangeItems.push(
{
index: counter++,
color: 'transparent',
enabled: true,
visible: false,
label: '',
piece: { gt: value - 0.000000001, lt: value + 0.000000001, color: 'transparent'}
}
);
}
}
return rangeItems;
};
const rangeItemLabel = (from?: number, to?: number): string => {
if (isNumber(from) && isNumber(to)) {
if (from === to) {
return `${from}`;
} else {
return `${from} - ${to}`;
}
} else if (isNumber(from)) {
return `${from}`;
} else if (isNumber(to)) {
return `< ${to}`;
} else {
return null;
}
};
const getMarkPoints = (ranges: Array<RangeItem>): number[] => {
const points = new Set<number>();
for (const range of ranges) {
if (range.visible) {
if (isNumber(range.from)) {
points.add(range.from);
}
if (isNumber(range.to)) {
points.add(range.to);
}
}
}
return Array.from(points).sort();
};

View File

@ -715,7 +715,7 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = {
};
export interface SeriesFillSettings {
type: SeriesFillType | 'default';
type: SeriesFillType;
opacity: number;
gradient: {
start: number;
@ -1322,7 +1322,7 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem,
borderWidth: barSettings.showBorder ? barSettings.borderWidth : 0,
borderRadius: barSettings.borderRadius
};
if (barSettings.backgroundSettings.type === SeriesFillType.none || barSettings.backgroundSettings.type === 'default') {
if (barSettings.backgroundSettings.type === SeriesFillType.none) {
barVisualSettings.color = seriesColor;
} else if (barSettings.backgroundSettings.type === SeriesFillType.opacity) {
barVisualSettings.color = tinycolor(seriesColor).setAlpha(barSettings.backgroundSettings.opacity).toRgbString();