Browse Source

fixed latest limit type display

pull/14488/head
Maksym Tsymbarov 10 months ago
parent
commit
ba6d84c40b
  1. 32
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts
  2. 132
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html
  3. 32
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/polar-area-chart-basic-config.component.ts
  4. 5
      ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-widget.models.ts
  5. 5
      ui-ngx/src/app/modules/home/components/widget/lib/chart/bars-chart.models.ts
  6. 225
      ui-ngx/src/app/modules/home/components/widget/lib/chart/bars-chart.ts
  7. 1
      ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts
  8. 12
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
  9. 133
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
  10. 30
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-widget-settings.component.ts
  11. 94
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html
  12. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.ts
  13. 22
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/polar-area-chart-widget-settings.component.ts
  14. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.html
  15. 129
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts
  16. 104
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts

32
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts

@ -29,8 +29,6 @@ import {
import {
LatestChartBasicConfigComponent
} from '@home/components/widget/config/basic/chart/latest-chart-basic-config.component';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
import { ValueSourceType } from '@shared/models/widget-settings.models';
@Component({
selector: 'tb-bar-chart-basic-config',
@ -43,7 +41,7 @@ export class BarChartBasicConfigComponent extends LatestChartBasicConfigComponen
barChartConfigTemplate: TemplateRef<any>;
predefinedValues = widgetTitleAutocompleteValues;
constructor(protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent,
protected fb: UntypedFormBuilder) {
@ -81,32 +79,4 @@ export class BarChartBasicConfigComponent extends LatestChartBasicConfigComponen
this.widgetConfig.config.settings.axisTickLabelFont = config.axisTickLabelFont;
this.widgetConfig.config.settings.axisTickLabelColor = config.axisTickLabelColor;
}
protected onConfigSet(configData: WidgetConfigComponentData) {
configData.config.settings.axisMin = this.normalizeAxisLimit(configData.config.settings.axisMin);
configData.config.settings.axisMax = this.normalizeAxisLimit(configData.config.settings.axisMax);
super.onConfigSet(configData);
}
private normalizeAxisLimit(limit: any): AxisLimitConfig {
if (limit && typeof limit === 'object' && 'type' in limit) {
return {
type: limit.type || ValueSourceType.constant,
value: limit.value ?? null,
entityAlias: limit.entityAlias ?? null
};
}
if (typeof limit === 'number') {
return {
type: ValueSourceType.constant,
value: limit,
entityAlias: null
};
}
return {
type: ValueSourceType.constant,
value: null,
entityAlias: null
};
}
}

132
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html

@ -21,27 +21,27 @@
formControlName="timewindowConfig">
</tb-timewindow-config-panel>
<tb-datasources
[configMode]="basicMode"
hideDatasourceLabel
hideDataKeys
forceSingleDatasource
formControlName="datasources">
[configMode]="basicMode"
hideDatasourceLabel
hideDataKeys
forceSingleDatasource
formControlName="datasources">
</tb-datasources>
<tb-data-keys-panel
panelTitle="{{ 'widgets.chart.series' | translate }}"
addKeyTitle="{{ 'widgets.chart.add-series' | translate }}"
keySettingsTitle="{{ 'widgets.chart.series-settings' | translate }}"
removeKeyTitle="{{ 'widgets.chart.remove-series' | translate }}"
noKeysText="{{ 'widgets.chart.no-series' | translate }}"
requiredKeysText="{{ 'widgets.chart.no-series-error' | translate }}"
hideUnits
hideDecimals
hideDataKeyUnits
hideDataKeyDecimals
[datasourceType]="datasource?.type"
[deviceId]="datasource?.deviceId"
[entityAliasId]="datasource?.entityAliasId"
formControlName="series">
panelTitle="{{ 'widgets.chart.series' | translate }}"
addKeyTitle="{{ 'widgets.chart.add-series' | translate }}"
keySettingsTitle="{{ 'widgets.chart.series-settings' | translate }}"
removeKeyTitle="{{ 'widgets.chart.remove-series' | translate }}"
noKeysText="{{ 'widgets.chart.no-series' | translate }}"
requiredKeysText="{{ 'widgets.chart.no-series-error' | translate }}"
hideUnits
hideDecimals
hideDataKeyUnits
hideDataKeyDecimals
[datasourceType]="datasource?.type"
[deviceId]="datasource?.deviceId"
[entityAliasId]="datasource?.entityAliasId"
formControlName="series">
</tb-data-keys-panel>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.appearance</div>
@ -210,7 +210,7 @@
</mat-expansion-panel>
</div>
<tb-chart-animation-settings
formControlName="animation">
formControlName="animation">
</tb-chart-animation-settings>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div>
@ -239,7 +239,7 @@
</div>
</div>
<tb-widget-actions-panel
formControlName="actions">
formControlName="actions">
</tb-widget-actions-panel>
</ng-container>
@ -346,15 +346,25 @@
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div>
<tb-chart-bar-settings
formControlName="barSettings"
[series]="false">
formControlName="barSettings"
[series]="false">
</tb-chart-bar-settings>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-axis</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.chart.chart-axis.scale-appearance</div>
<div translate>widgets.chart.chart-axis.scale</div>
<div class="flex flex-row items-center justify-start gap-2">
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMin"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMax"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="axisTickLabelFont"
clearButton
disabledLineHeight
@ -367,33 +377,6 @@
</tb-color-input>
</div>
</div>
<div class="tb-form-row column align-start">
<div translate>widgets.chart.chart-axis.scale-limits</div>
<div class="tb-form-table w-full">
<div class="tb-form-table-header w-full">
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-1/5" translate>widgets.chart.chart-axis.limit</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.source</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.key-value</div>
</div>
<div class="tb-form-table-body">
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMin"
labelKey="widgets.chart.chart-axis.scale-min">
</tb-axis-scale-row>
<mat-divider class="mt-2 gt-sm:hidden"></mat-divider>
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMax"
labelKey="widgets.chart.chart-axis.scale-max">
</tb-axis-scale-row>
</div>
</div>
</div>
</div>
</ng-container>
</ng-template>
@ -402,16 +385,26 @@
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div>
<tb-chart-bar-settings
formControlName="barSettings"
pieLabelPosition
[series]="false">
formControlName="barSettings"
pieLabelPosition
[series]="false">
</tb-chart-bar-settings>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.polar-area-chart.polar-axis</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.chart.chart-axis.scale-appearance</div>
<div translate>widgets.chart.chart-axis.scale</div>
<div class="flex flex-row items-center justify-start gap-2">
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMin"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMax"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="axisTickLabelFont"
clearButton
disabledLineHeight
@ -430,33 +423,6 @@
<input matInput formControlName="angleAxisStartAngle" type="number" min="0" max="360" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
<div class="tb-form-row column align-start">
<div translate>widgets.chart.chart-axis.scale-limits</div>
<div class="tb-form-table w-full">
<div class="tb-form-table-header w-full">
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-1/5" translate>widgets.chart.chart-axis.limit</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.source</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.key-value</div>
</div>
<div class="tb-form-table-body">
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMin"
labelKey="widgets.chart.chart-axis.scale-min">
</tb-axis-scale-row>
<mat-divider class="mt-2 gt-sm:hidden"></mat-divider>
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMax"
labelKey="widgets.chart.chart-axis.scale-max">
</tb-axis-scale-row>
</div>
</div>
</div>
</div>
</ng-container>
</ng-template>
@ -542,7 +508,7 @@
</div>
</div>
<tb-chart-fill-settings
formControlName="fillAreaSettings">
formControlName="fillAreaSettings">
</tb-chart-fill-settings>
</div>
<div class="tb-form-panel">

32
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/polar-area-chart-basic-config.component.ts

@ -29,8 +29,6 @@ import {
import {
LatestChartBasicConfigComponent
} from '@home/components/widget/config/basic/chart/latest-chart-basic-config.component';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
import { ValueSourceType } from '@shared/models/widget-settings.models';
@Component({
selector: 'tb-polar-area-chart-basic-config',
@ -43,7 +41,7 @@ export class PolarAreaChartBasicConfigComponent extends LatestChartBasicConfigCo
polarAreaChartConfigTemplate: TemplateRef<any>;
predefinedValues = widgetTitleAutocompleteValues;
constructor(protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent,
protected fb: UntypedFormBuilder) {
@ -83,32 +81,4 @@ export class PolarAreaChartBasicConfigComponent extends LatestChartBasicConfigCo
this.widgetConfig.config.settings.axisTickLabelColor = config.axisTickLabelColor;
this.widgetConfig.config.settings.angleAxisStartAngle = config.angleAxisStartAngle;
}
protected onConfigSet(configData: WidgetConfigComponentData) {
configData.config.settings.axisMin = this.normalizeAxisLimit(configData.config.settings.axisMin);
configData.config.settings.axisMax = this.normalizeAxisLimit(configData.config.settings.axisMax);
super.onConfigSet(configData);
}
private normalizeAxisLimit(limit: any): AxisLimitConfig {
if (limit && typeof limit === 'object' && 'type' in limit) {
return {
type: limit.type || ValueSourceType.constant,
value: limit.value ?? null,
entityAlias: limit.entityAlias ?? null
};
}
if (typeof limit === 'number') {
return {
type: ValueSourceType.constant,
value: limit,
entityAlias: null
};
}
return {
type: ValueSourceType.constant,
value: null,
entityAlias: null
};
}
}

5
ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-widget.models.ts

@ -31,11 +31,10 @@ import {
ChartBarSettings,
chartColorScheme
} from '@home/components/widget/lib/chart/chart.models';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
export interface BarChartWidgetSettings extends LatestChartWidgetSettings {
axisMin?: number | string | AxisLimitConfig;
axisMax?: number | string | AxisLimitConfig;
axisMin?: number;
axisMax?: number;
axisTickLabelFont: Font;
axisTickLabelColor: string;
barSettings: ChartBarSettings;

5
ui-ngx/src/app/modules/home/components/widget/lib/chart/bars-chart.models.ts

@ -24,12 +24,11 @@ import {
chartColorScheme
} from '@home/components/widget/lib/chart/chart.models';
import { Font } from '@shared/models/widget-settings.models';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
export interface BarsChartSettings extends LatestChartSettings {
polar: boolean;
axisMin?: number | string | AxisLimitConfig;
axisMax?: number | string | AxisLimitConfig;
axisMin?: number | string;
axisMax?: number | string;
axisTickLabelFont: Font;
axisTickLabelColor: string;
angleAxisStartAngle?: number;

225
ui-ngx/src/app/modules/home/components/widget/lib/chart/bars-chart.ts

@ -25,7 +25,7 @@ import { ComponentStyle } from '@shared/models/widget-settings.models';
import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient';
import tinycolor from 'tinycolor2';
import { BarDataItemOption, BarSeriesLabelOption } from 'echarts/types/src/chart/bar/BarSeries';
import { isDefinedAndNotNull, isNumber, isString } from '@core/utils';
import { isDefinedAndNotNull } from '@core/utils';
import {
ChartFillType,
ChartLabelPosition,
@ -35,19 +35,9 @@ import {
} from '@home/components/widget/lib/chart/chart.models';
import { ValueAxisBaseOption } from 'echarts/types/src/coord/axisCommonTypes';
import { RadiusAxisOption, YAXisOption } from 'echarts/types/dist/shared';
import { DataKey, Datasource, DatasourceType, widgetType } from '@shared/models/widget.models';
import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { ValueSourceType } from '@shared/models/widget-settings.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
export class TbBarsChart extends TbLatestChart<BarsChartSettings> {
private dynamicAxisMin: number | string = null;
private dynamicAxisMax: number | string = null;
private minLatestDataKey: DataKey = null;
private maxLatestDataKey: DataKey = null;
constructor(ctx: WidgetContext,
inputSettings: DeepPartial<PieChartSettings>,
chartElement: HTMLElement,
@ -62,212 +52,6 @@ export class TbBarsChart extends TbLatestChart<BarsChartSettings> {
return barsChartDefaultSettings;
}
protected initSettings() {
super.initSettings();
this.setupAxisLimits();
}
private setupAxisLimits(): void {
const axisLimitDatasources: Datasource[] = [];
if (isDefinedAndNotNull(this.settings.axisMin)) {
this.processAxisLimit(this.settings.axisMin, 'min', axisLimitDatasources);
}
if (isDefinedAndNotNull(this.settings.axisMax)) {
this.processAxisLimit(this.settings.axisMax, 'max', axisLimitDatasources);
}
this.subscribeForAxisLimits(axisLimitDatasources);
}
private processAxisLimit(
limit: any,
limitType: 'min' | 'max',
axisLimitDatasources: Datasource[]
): void {
if (limit && typeof limit === 'object' && 'type' in limit) {
const axisLimit = limit as AxisLimitConfig;
if (axisLimit.type === ValueSourceType.latestKey) {
let latestDataKey: DataKey = null;
if (this.ctx.datasources.length) {
for (const datasource of this.ctx.datasources) {
latestDataKey = datasource.latestDataKeys?.find(d =>
(d.type === DataKeyType.function && d.label === (axisLimit.value as DataKey).label) ||
(d.type !== DataKeyType.function && d.name === (axisLimit.value as DataKey).name &&
d.type === (axisLimit.value as DataKey).type));
if (latestDataKey) {
break;
}
}
}
if (latestDataKey) {
if (limitType === 'min') {
this.minLatestDataKey = latestDataKey;
} else {
this.maxLatestDataKey = latestDataKey;
}
}
} else if (axisLimit.type === ValueSourceType.entity) {
const entityAliasId = this.ctx.aliasController.getEntityAliasId(axisLimit.entityAlias);
if (entityAliasId) {
let datasource = axisLimitDatasources.find(d => d.entityAliasId === entityAliasId);
const entityDataKey: DataKey = {
type: (axisLimit.value as DataKey).type,
name: (axisLimit.value as DataKey).name,
label: (axisLimit.value as DataKey).name,
settings: {
axisLimit: limitType
}
};
if (datasource) {
datasource.dataKeys.push(entityDataKey);
} else {
datasource = {
type: DatasourceType.entity,
name: axisLimit.entityAlias,
aliasName: axisLimit.entityAlias,
entityAliasId,
dataKeys: [entityDataKey]
};
axisLimitDatasources.push(datasource);
}
}
} else if (axisLimit.type === ValueSourceType.constant) {
const value = axisLimit.value as number;
if (limitType === 'min') {
this.dynamicAxisMin = value;
} else {
this.dynamicAxisMax = value;
}
}
} else if (typeof limit === 'number' || typeof limit === 'string') {
if (limitType === 'min') {
this.dynamicAxisMin = limit;
} else {
this.dynamicAxisMax = limit;
}
}
}
private subscribeForAxisLimits(datasources: Datasource[]) {
if (datasources.length) {
const axisLimitsSubscriptionOptions: WidgetSubscriptionOptions = {
datasources,
useDashboardTimewindow: false,
type: widgetType.latest,
callbacks: {
onDataUpdated: (subscription) => {
let update = false;
if (subscription.data) {
for (const data of subscription.data) {
const limitType = data.dataKey.settings.axisLimit as ('min' | 'max');
if (data.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1]);
if (limitType === 'min') {
if (this.dynamicAxisMin !== value) {
this.dynamicAxisMin = value;
update = true;
}
} else {
if (this.dynamicAxisMax !== value) {
this.dynamicAxisMax = value;
update = true;
}
}
}
}
}
if (this.latestChart && update) {
this.updateAxisLimits();
}
}
}
};
this.ctx.subscriptionApi.createSubscription(axisLimitsSubscriptionOptions, true).subscribe();
}
}
private parseAxisLimitData(data: any): number | null {
let value: number;
if (isDefinedAndNotNull(data)) {
if (isNumber(data)) {
value = data;
} else if (isString(data)) {
value = Number(data);
}
}
if (isDefinedAndNotNull(value) && !isNaN(value)) {
return value;
}
return null;
}
private updateAxisLimitsFromLatest(): boolean {
let update = false;
if (this.ctx.latestData) {
if (this.minLatestDataKey) {
const data = this.ctx.latestData.find(d => d.dataKey === this.minLatestDataKey);
if (data?.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1]);
if (this.dynamicAxisMin !== value) {
this.dynamicAxisMin = value;
update = true;
}
}
}
if (this.maxLatestDataKey) {
const data = this.ctx.latestData.find(d => d.dataKey === this.maxLatestDataKey);
if (data?.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1]);
if (this.dynamicAxisMax !== value) {
this.dynamicAxisMax = value;
update = true;
}
}
}
}
return update;
}
private updateAxisLimits(): void {
if (this.latestChart && !this.latestChart.isDisposed()) {
const axisTickLabelStyle = createChartTextStyle(this.settings.axisTickLabelFont,
this.settings.axisTickLabelColor, false, 'axis.tickLabel');
const valueAxis: ValueAxisBaseOption = {
type: 'value',
min: this.dynamicAxisMin,
max: this.dynamicAxisMax,
axisLabel: {
color: axisTickLabelStyle.color,
fontStyle: axisTickLabelStyle.fontStyle,
fontWeight: axisTickLabelStyle.fontWeight,
fontFamily: axisTickLabelStyle.fontFamily,
fontSize: axisTickLabelStyle.fontSize,
formatter: (value: any) => this.valueFormatter.format(value)
}
};
if (this.settings.polar) {
this.latestChartOption.radiusAxis = valueAxis as RadiusAxisOption;
} else {
this.latestChartOption.yAxis = valueAxis as YAXisOption;
}
this.latestChart.setOption(this.latestChartOption);
}
}
public latestUpdated() {
if (this.updateAxisLimitsFromLatest()) {
this.updateAxisLimits();
}
}
protected prepareLatestChartOption() {
let labelStyle: ComponentStyle = {};
if (this.settings.barSettings.showLabel) {
@ -305,12 +89,10 @@ export class TbBarsChart extends TbLatestChart<BarsChartSettings> {
const axisTickLabelStyle = createChartTextStyle(this.settings.axisTickLabelFont,
this.settings.axisTickLabelColor, false, 'axis.tickLabel');
const minValue = isDefinedAndNotNull(this.dynamicAxisMin) ? this.dynamicAxisMin : undefined;
const maxValue = isDefinedAndNotNull(this.dynamicAxisMax) ? this.dynamicAxisMax : undefined;
const valueAxis: ValueAxisBaseOption = {
type: 'value',
min: minValue,
max: maxValue,
min: this.settings.axisMin,
max: this.settings.axisMax,
axisLabel: {
color: axisTickLabelStyle.color,
fontStyle: axisTickLabelStyle.fontStyle,
@ -320,7 +102,6 @@ export class TbBarsChart extends TbLatestChart<BarsChartSettings> {
formatter: (value: any) => this.valueFormatter.format(value)
}
};
if (this.settings.polar) {
this.latestChartOption.polar = {
radius: '100%'

1
ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts

@ -142,6 +142,7 @@ export abstract class TbLatestChart<S extends LatestChartSettings> {
public update(): void {
for (const dsData of this.ctx.data) {
console.log("this.ctx.data",this.ctx.data)
let value = 0;
const tsValue = dsData.data[0];
const dataItem = this.dataItems.find(item => item.dataKey === dsData.dataKey);

12
ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts

@ -381,18 +381,12 @@ export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSetting
decimals?: number;
interval?: number;
splitNumber?: number;
min?: number | string | AxisLimitConfig;
max?: number | string | AxisLimitConfig;
min?: string | ValueSourceConfig;
max?: string | ValueSourceConfig;
ticksGenerator?: TimeSeriesChartTicksGenerator | string;
ticksFormatter?: TimeSeriesChartTicksFormatter | string;
}
export interface AxisLimitConfig {
entityAlias: string;
type: ValueSourceType;
value: number | DataKey;
}
export const timeSeriesChartYAxisValid = (axis: TimeSeriesChartYAxisSettings): boolean =>
!(!axis.id || isUndefinedOrNull(axis.order));
@ -873,8 +867,6 @@ export interface TimeSeriesChartAxis {
id: string;
settings: TimeSeriesChartAxisSettings;
option: CartesianAxisOption;
dynamicMin?: string| number;
dynamicMax?: string| number;
minLatestDataKey?: DataKey;
maxLatestDataKey?: DataKey;
unitConvertor?: (value: number) => number;

133
ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts

@ -17,7 +17,6 @@
import { WidgetContext } from '@home/models/widget-component.models';
import {
adjustTimeAxisExtentToData,
AxisLimitConfig,
calculateThresholdsOffset,
createTimeSeriesVisualMapOption,
createTimeSeriesXAxis,
@ -55,7 +54,7 @@ import {
getFocusedSeriesIndex,
measureAxisNameSize
} from '@home/components/widget/lib/chart/echarts-widget.models';
import { DateFormatProcessor, ValueSourceType } from '@shared/models/widget-settings.models';
import { DateFormatProcessor, ValueSourceConfig, ValueSourceType } from '@shared/models/widget-settings.models';
import {
formattedDataFormDatasourceData,
formatValue,
@ -289,15 +288,36 @@ export class TbTimeSeriesChart {
}
}
}
}
if (this.updateAxisLimitsFromLatest()) {
update = true;
for (const yAxis of this.yAxisList) {
const minType = (yAxis.settings.min as ValueSourceConfig).type;
const maxType = (yAxis.settings.max as ValueSourceConfig).type;
if (minType === ValueSourceType.latestKey && yAxis.minLatestDataKey) {
const data = this.ctx.latestData.find(d => d.dataKey === yAxis.minLatestDataKey);
if (data?.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor);
if (yAxis.option.min !== value) {
yAxis.option.min = value;
update = true;
}
}
}
if (maxType === ValueSourceType.latestKey && yAxis.maxLatestDataKey) {
const data = this.ctx.latestData.find(d => d.dataKey === yAxis.maxLatestDataKey);
if (data?.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor);
if (yAxis.option.max !== value) {
yAxis.option.max = value;
update = true;
}
}
}
}
}
if (this.timeSeriesChart && update) {
this.updateSeriesData();
if (this.updateAxisLimitsFromLatest()) {
this.updateAxisLimits();
}
this.updateAxisLimits();
}
}
@ -574,20 +594,17 @@ export class TbTimeSeriesChart {
}
const yAxis = createTimeSeriesYAxis(unitSymbol, decimals, axisSettings, this.ctx.utilsService, this.darkMode, unitConvertor);
if (isDefinedAndNotNull(axisSettings.min)) {
this.processAxisLimit(axisSettings.min, 'min', yAxis, axisLimitDatasources, unitConvertor);
this.processYAxisLimit(axisSettings.min, 'min', yAxis, axisLimitDatasources, unitConvertor);
}
if (isDefinedAndNotNull(axisSettings.max)) {
this.processAxisLimit(axisSettings.max, 'max', yAxis, axisLimitDatasources, unitConvertor);
}
if (yAxis.minLatestDataKey || yAxis.maxLatestDataKey) {
yAxis.unitConvertor = unitConvertor;
this.processYAxisLimit(axisSettings.max, 'max', yAxis, axisLimitDatasources, unitConvertor);
}
this.yAxisList.push(yAxis);
}
this.subscribeForAxisLimits(axisLimitDatasources);
}
private processAxisLimit(
private processYAxisLimit(
limit: any,
limitType: 'min' | 'max',
yAxis: TimeSeriesChartYAxis,
@ -595,15 +612,15 @@ export class TbTimeSeriesChart {
unitConvertor?: (value: number) => number
): void {
if (limit && typeof limit === 'object' && 'type' in limit) {
const axisLimit = limit as AxisLimitConfig;
const axisLimit = limit as ValueSourceConfig;
if (axisLimit.type === ValueSourceType.latestKey) {
let latestDataKey: DataKey = null;
if (this.ctx.datasources.length) {
for (const datasource of this.ctx.datasources) {
latestDataKey = datasource.latestDataKeys?.find(d =>
(d.type === DataKeyType.function && d.label === (axisLimit.value as DataKey).label) ||
(d.type !== DataKeyType.function && d.name === (axisLimit.value as DataKey).name &&
d.type === (axisLimit.value as DataKey).type));
(d.type === DataKeyType.function && d.label === axisLimit.latestKey) ||
(d.type !== DataKeyType.function && d.name === axisLimit.latestKey &&
d.type === axisLimit.latestKeyType));
if (latestDataKey) {
break;
}
@ -621,9 +638,9 @@ export class TbTimeSeriesChart {
if (entityAliasId) {
let datasource = axisLimitDatasources.find(d => d.entityAliasId === entityAliasId);
const entityDataKey: DataKey = {
type: (axisLimit.value as DataKey).type,
name: (axisLimit.value as DataKey).name,
label: (axisLimit.value as DataKey).name,
type: limit.entityKeyType,
name: limit.entityKey,
label: limit.entityKey,
settings: {
yAxisId: yAxis.id,
axisLimit: limitType
@ -643,27 +660,14 @@ export class TbTimeSeriesChart {
}
}
} else if (axisLimit.type === ValueSourceType.constant) {
const value = unitConvertor ? unitConvertor(axisLimit.value as number) : axisLimit.value as number;
const value = unitConvertor ? unitConvertor(axisLimit.value) : axisLimit.value;
if (limitType === 'min') {
yAxis.dynamicMin = value;
yAxis.option.min = value;
} else {
yAxis.dynamicMax = value;
yAxis.option.max = value;
}
}
} else if (typeof limit === 'number' || typeof limit === 'string') {
let value = limit;
if (typeof value === 'number' && unitConvertor) {
value = unitConvertor(value);
}
if (limitType === 'min') {
yAxis.dynamicMin = value;
yAxis.option.min = value;
} else {
yAxis.dynamicMax = value;
yAxis.option.max = value;
}
return;
}
}
@ -738,25 +742,30 @@ export class TbTimeSeriesChart {
const limitType = data.dataKey.settings.axisLimit as ('min' | 'max');
if (data.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor);
if (isDefinedAndNotNull(value)) {
if (limitType === 'min') {
yAxis.dynamicMin = value;
yAxis.option.min = value;
if (yAxis.option.min !== value) {
yAxis.option.min = value;
update = true;
}
} else {
yAxis.dynamicMax = value;
yAxis.option.max = value;
if (yAxis.option.max !== value) {
yAxis.option.max = value;
update = true;
}
}
update = true;
}
}
}
}
}
if (this.timeSeriesChart && update) {
this.updateAxisLimits();
}
}
if (this.timeSeriesChart && update) {
this.updateAxisLimits();
}
}
};
}
};
this.ctx.subscriptionApi.createSubscription(axisLimitsSubscriptionOptions, true).subscribe();
}
}
@ -853,40 +862,6 @@ export class TbTimeSeriesChart {
}
}
private updateAxisLimitsFromLatest(): boolean {
let update = false;
if (this.ctx.latestData) {
for (const yAxis of this.yAxisList) {
if (yAxis.minLatestDataKey) {
const data = this.ctx.latestData.find(d => d.dataKey === yAxis.minLatestDataKey);
if (data?.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor);
if (yAxis.dynamicMin !== value) {
yAxis.dynamicMin = value;
yAxis.option.min = value;
update = true;
}
}
}
if (yAxis.maxLatestDataKey) {
const data = this.ctx.latestData.find(d => d.dataKey === yAxis.maxLatestDataKey);
if (data?.data[0]) {
const value = this.parseAxisLimitData(data.data[0][1], yAxis.unitConvertor);
if (yAxis.dynamicMax !== value) {
yAxis.dynamicMax = value;
yAxis.option.max = value;
update = true;
}
}
}
}
}
return update;
}
private parseAxisLimitData = (data: any, unitConvertor?: (value: number) => number): number => {
let value: number;
if (isDefinedAndNotNull(data)) {

30
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-widget-settings.component.ts

@ -26,8 +26,6 @@ import {
import {
LatestChartWidgetSettingsComponent
} from '@home/components/widget/lib/settings/chart/latest-chart-widget-settings.component';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
import { ValueSourceType } from '@shared/models/widget-settings.models';
@Component({
selector: 'tb-bar-chart-widget-settings',
@ -59,32 +57,4 @@ export class BarChartWidgetSettingsComponent extends LatestChartWidgetSettingsCo
latestChartWidgetSettingsForm.addControl('axisTickLabelFont', this.fb.control(settings.axisTickLabelFont, []));
latestChartWidgetSettingsForm.addControl('axisTickLabelColor', this.fb.control(settings.axisTickLabelColor, []));
}
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
settings.axisMin = this.normalizeAxisLimit(settings.axisMin);
settings.axisMax = this.normalizeAxisLimit(settings.axisMax);
return super.prepareInputSettings(settings);
}
private normalizeAxisLimit(limit: any): AxisLimitConfig {
if (limit && typeof limit === 'object' && 'type' in limit) {
return {
type: limit.type || ValueSourceType.constant,
value: limit.value ?? null,
entityAlias: limit.entityAlias ?? null
};
}
if (typeof limit === 'number') {
return {
type: ValueSourceType.constant,
value: limit,
entityAlias: null
};
}
return {
type: ValueSourceType.constant,
value: null,
entityAlias: null
};
}
}

94
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html

@ -120,7 +120,7 @@
</mat-expansion-panel>
</div>
<tb-chart-animation-settings
formControlName="animation">
formControlName="animation">
</tb-chart-animation-settings>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div>
@ -256,15 +256,25 @@
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div>
<tb-chart-bar-settings
formControlName="barSettings"
[series]="false">
formControlName="barSettings"
[series]="false">
</tb-chart-bar-settings>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-axis</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.chart.chart-axis.scale-appearance</div>
<div translate>widgets.chart.chart-axis.scale</div>
<div class="flex flex-row items-center justify-start gap-2">
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMin"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMax"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="axisTickLabelFont"
clearButton
disabledLineHeight
@ -277,35 +287,6 @@
</tb-color-input>
</div>
</div>
<div class="tb-form-row column align-start">
<div translate>widgets.chart.chart-axis.scale-limits</div>
<div class="tb-form-table w-full">
<div class="tb-form-table-header w-full">
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-1/5" translate>widgets.chart.chart-axis.limit</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.source</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.key-value</div>
</div>
<div class="tb-form-table-body">
<div class="tb-form-table-body">
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMin"
labelKey="widgets.chart.chart-axis.scale-min">
</tb-axis-scale-row>
<mat-divider class="mt-2 gt-sm:hidden"></mat-divider>
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMax"
labelKey="widgets.chart.chart-axis.scale-max">
</tb-axis-scale-row>
</div>
</div>
</div>
</div>
</div>
</div>
</ng-container>
@ -322,16 +303,26 @@
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.bar-chart.bar-appearance</div>
<tb-chart-bar-settings
formControlName="barSettings"
pieLabelPosition
[series]="false">
formControlName="barSettings"
pieLabelPosition
[series]="false">
</tb-chart-bar-settings>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.polar-area-chart.polar-axis</div>
<div class="tb-form-row space-between column-xs">
<div translate>widgets.chart.chart-axis.scale-appearance</div>
<div translate>widgets.chart.chart-axis.scale</div>
<div class="flex flex-row items-center justify-start gap-2">
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-min</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMin"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.chart.chart-axis.scale-max</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="axisMax"
type="number" placeholder="{{ 'widgets.chart.chart-axis.scale-auto' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="axisTickLabelFont"
clearButton
disabledLineHeight
@ -350,33 +341,6 @@
<input matInput formControlName="angleAxisStartAngle" type="number" min="0" max="360" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
<div class="tb-form-row column align-start">
<div translate>widgets.chart.chart-axis.scale-limits</div>
<div class="tb-form-table w-full">
<div class="tb-form-table-header w-full">
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-1/5" translate>widgets.chart.chart-axis.limit</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.source</div>
<div class="tb-form-table-header-cell flex-grow flex-shrink basis-40%" translate>widgets.chart.chart-axis.key-value</div>
</div>
<div class="tb-form-table-body">
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMin"
labelKey="widgets.chart.chart-axis.scale-min">
</tb-axis-scale-row>
<mat-divider class="mt-2 gt-sm:hidden"></mat-divider>
<tb-axis-scale-row
[datasource]="datasource"
[callbacks]="callbacks"
[aliasController]="aliasController"
formControlName="axisMax"
labelKey="widgets.chart.chart-axis.scale-max">
</tb-axis-scale-row>
</div>
</div>
</div>
</div>
</div>
</ng-container>
@ -470,7 +434,7 @@
</div>
</div>
<tb-chart-fill-settings
formControlName="fillAreaSettings">
formControlName="fillAreaSettings">
</tb-chart-fill-settings>
</div>
<div class="tb-form-panel">

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.ts

@ -22,7 +22,6 @@ import {
LatestChartWidgetSettings
} from '@home/components/widget/lib/chart/latest-chart.models';
import {
Datasource,
legendPositions,
legendPositionTranslationMap,
WidgetSettings,
@ -104,15 +103,6 @@ export abstract class LatestChartWidgetSettingsComponent<S extends LatestChartWi
return layout === DoughnutLayout.with_total;
}
public get datasource(): Datasource {
const datasources: Datasource[] = this.widgetConfig.config.datasources;
if (datasources && datasources.length) {
return datasources[0];
} else {
return null;
}
}
constructor(protected store: Store<AppState>,
protected fb: UntypedFormBuilder) {
super(store);

22
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/polar-area-chart-widget-settings.component.ts

@ -26,8 +26,7 @@ import {
import {
LatestChartWidgetSettingsComponent
} from '@home/components/widget/lib/settings/chart/latest-chart-widget-settings.component';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
import { ValueSourceType } from '@shared/models/widget-settings.models';
import { ValueSourceConfig, ValueSourceType } from '@shared/models/widget-settings.models';
@Component({
selector: 'tb-polar-area-chart-widget-settings',
@ -66,25 +65,20 @@ export class PolarAreaChartWidgetSettingsComponent extends LatestChartWidgetSett
return super.prepareInputSettings(settings);
}
private normalizeAxisLimit(limit: any): AxisLimitConfig {
if (limit && typeof limit === 'object' && 'type' in limit) {
private normalizeAxisLimit(limit: any): ValueSourceConfig {
if (!limit) {
return {
type: limit.type || ValueSourceType.constant,
value: limit.value ?? null,
entityAlias: limit.entityAlias ?? null
type: ValueSourceType.constant,
value: null,
entityAlias: null
};
}
if (typeof limit === 'number') {
} else if (typeof limit === 'number') {
return {
type: ValueSourceType.constant,
value: limit,
entityAlias: null
};
}
return {
type: ValueSourceType.constant,
value: null,
entityAlias: null
};
return limit;
}
}

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.html

@ -53,7 +53,7 @@
[dataKeyTypes]="[DataKeyType.attribute, DataKeyType.timeseries]"
[callbacks]="callbacks"
[editable]="false"
formControlName="value">
[formControl]="latestKeyFormControl">
</tb-data-key-input>
<tb-data-key-input
required
@ -65,7 +65,7 @@
[dataKeyTypes]="[DataKeyType.attribute, DataKeyType.timeseries]"
[callbacks]="callbacks"
[editable]="false"
formControlName="value">
[formControl]="entityKeyFormControl">
</tb-data-key-input>
</div>
</div>

129
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts

@ -18,18 +18,22 @@ import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'
import {
ControlValueAccessor, NG_VALIDATORS,
NG_VALUE_ACCESSOR,
UntypedFormBuilder,
UntypedFormBuilder, UntypedFormControl,
UntypedFormGroup, ValidationErrors, Validator,
Validators,
} from '@angular/forms';
import { ValueSourceType, ValueSourceTypes, ValueSourceTypeTranslation } from '@shared/models/widget-settings.models';
import {
ValueSourceConfig,
ValueSourceType,
ValueSourceTypes,
ValueSourceTypeTranslation
} from '@shared/models/widget-settings.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { AxisLimitConfig } from '@home/components/widget/lib/chart/time-series-chart.models';
import { Datasource, DatasourceType, } from '@shared/models/widget.models';
import { DataKey, Datasource, DatasourceType, } from '@shared/models/widget.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { IAliasController } from '@core/api/widget-api.models';
import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models';
import { isEqual } from '@core/utils';
import { merge } from 'rxjs';
@Component({
selector: 'tb-axis-scale-row',
@ -77,9 +81,13 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali
limitForm: UntypedFormGroup;
latestKeyFormControl: UntypedFormControl;
entityKeyFormControl: UntypedFormControl;
private propagateChanges: (value: any) => void = () => {};
private modelValue: AxisLimitConfig | null = null;
private modelValue: ValueSourceConfig | null = null;
constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
@ -91,42 +99,44 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali
value: [null],
entityAlias: [null]
});
this.latestKeyFormControl = this.fb.control(null, [Validators.required]);
this.entityKeyFormControl = this.fb.control(null, [Validators.required]);
this.subscribeToTypeChanges();
this.limitForm.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(value => {
merge(
this.latestKeyFormControl.valueChanges,
this.entityKeyFormControl.valueChanges,
this.limitForm.valueChanges
).pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.updateValidators();
this.updateView(value);
this.updateModel();
});
}
writeValue(value: AxisLimitConfig) {
writeValue(value: ValueSourceConfig) {
console.log("value", value);
this.modelValue = value;
if (!this.limitForm) {
return;
}
if (value) {
this.limitForm.patchValue({
type: value.type ?? ValueSourceType.constant,
value: value.value ?? null,
entityAlias: value.entityAlias ?? null
},
{ emitEvent: false }
);
} else {
this.limitForm.patchValue({
type: ValueSourceType.constant,
value: null,
entityAlias: null
},
{ emitEvent: false }
);
this.limitForm.patchValue(
{
type: value.type || ValueSourceType.constant,
value: value.value,
entityAlias: value.entityAlias,
}, {emitEvent: false}
);
if (value.type === ValueSourceType.latestKey) {
this.latestKeyFormControl.patchValue({
type: value.latestKeyType,
name: value.latestKey
}, {emitEvent: false});
} else if (value.type === ValueSourceType.entity) {
this.entityKeyFormControl.patchValue({
type: value.entityKeyType,
name: value.entityKey
}, {emitEvent: false});
}
this.updateValidators();
this.limitForm.markAllAsTouched();
}
registerOnChange(fn: any) {
@ -158,28 +168,22 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali
private updateValidators() {
const axisTypeControl = this.limitForm.get('type');
const axisValueControl = this.limitForm.get('value');
const axisEntityAliasControl = this.limitForm.get('entityAlias');
if (axisTypeControl && axisValueControl && axisEntityAliasControl) {
if (axisTypeControl && this.entityKeyFormControl && this.latestKeyFormControl) {
const type = axisTypeControl.value;
if (type === ValueSourceType.latestKey || type === ValueSourceType.entity) {
axisValueControl.setValidators([Validators.required]);
} else {
axisValueControl.clearValidators();
}
if (type === ValueSourceType.entity) {
axisEntityAliasControl.setValidators([Validators.required]);
if (type === ValueSourceType.latestKey) {
this.latestKeyFormControl.setValidators([Validators.required]);
this.entityKeyFormControl.clearValidators();
} else if (type === ValueSourceType.entity) {
this.latestKeyFormControl.clearValidators();
this.entityKeyFormControl.setValidators([Validators.required]);
} else {
axisEntityAliasControl.clearValidators();
this.latestKeyFormControl.clearValidators();
this.entityKeyFormControl.clearValidators();
}
axisValueControl.updateValueAndValidity({ emitEvent: false });
axisEntityAliasControl.updateValueAndValidity({ emitEvent: false });
this.latestKeyFormControl.updateValueAndValidity({ emitEvent: false });
this.entityKeyFormControl.updateValueAndValidity({ emitEvent: false });
}
}
private subscribeToTypeChanges() {
this.limitForm.controls.type.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
@ -187,18 +191,31 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali
const axisValueControl = this.limitForm.get('value');
const axisEntityAliasControl = this.limitForm.get('entityAlias');
if (axisValueControl) {
axisValueControl.setValue(null, { emitEvent: true });
axisValueControl.setValue(null, { emitEvent: false });
}
if (axisEntityAliasControl) {
axisEntityAliasControl.setValue(null, { emitEvent: true });
axisEntityAliasControl.setValue(null, { emitEvent: false });
}
this.latestKeyFormControl.setValue(null, { emitEvent: false });
this.entityKeyFormControl.setValue(null, { emitEvent: false });
});
}
private updateView(value: AxisLimitConfig) {
if (!isEqual(this.modelValue, value)) {
this.modelValue = value;
this.propagateChanges(value);
private updateModel() {
const value = this.limitForm.value;
this.modelValue.type = value.type ?? ValueSourceType.constant;
this.modelValue.value = value?.value;
this.modelValue.entityAlias = value?.entityAlias;
if (value.type === ValueSourceType.latestKey) {
const latestKey: DataKey = this.latestKeyFormControl.value;
this.modelValue.latestKey = latestKey?.name;
this.modelValue.latestKeyType = (latestKey?.type as any);
} else if (value.type === ValueSourceType.entity) {
const entityKey: DataKey = this.entityKeyFormControl.value;
this.modelValue.entityKey = entityKey?.name;
this.modelValue.entityKeyType = (entityKey?.type as any);
}
this.propagateChanges(this.modelValue);
}
}

104
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts

@ -38,7 +38,8 @@ import {
import {
defaultTimeSeriesChartYAxisSettings,
getNextTimeSeriesYAxisId,
TimeSeriesChartYAxes, TimeSeriesChartYAxisId,
TimeSeriesChartYAxes,
TimeSeriesChartYAxisId,
TimeSeriesChartYAxisSettings,
timeSeriesChartYAxisValid,
timeSeriesChartYAxisValidator
@ -49,7 +50,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { IAliasController } from '@app/core/public-api';
import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models';
import { Datasource } from '@app/shared/public-api';
import { DataKey, DataKeyType, Datasource, ValueSourceConfig, ValueSourceType } from '@app/shared/public-api';
@Component({
selector: 'tb-time-series-chart-y-axes-panel',
@ -125,6 +126,7 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
for (const axis of axes) {
yAxes[axis.id] = axis;
}
this.updateLatestDataKeys(Object.values(yAxes));
this.propagateChange(yAxes);
}
);
@ -147,7 +149,7 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
}
writeValue(value: TimeSeriesChartYAxes | undefined): void {
const yAxes: TimeSeriesChartYAxes = value || {};
const yAxes: TimeSeriesChartYAxes = this.checkLatestDataKeys(value || {});
if (!yAxes.default) {
yAxes.default = mergeDeep({} as TimeSeriesChartYAxisSettings, defaultTimeSeriesChartYAxisSettings,
{id: 'default', order: 0} as TimeSeriesChartYAxisSettings);
@ -202,8 +204,104 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
private prepareAxesFormArray(axes: TimeSeriesChartYAxisSettings[]): UntypedFormArray {
const axesControls: Array<AbstractControl> = [];
axes.forEach((axis) => {
axis.min = this.normalizeAxisLimit(axis.min);
axis.max = this.normalizeAxisLimit(axis.max);
axesControls.push(this.fb.control(axis, [timeSeriesChartYAxisValidator]));
});
return this.fb.array(axesControls);
}
private checkLatestDataKeys(yAxes: TimeSeriesChartYAxes): TimeSeriesChartYAxes {
const latestKeys = this.datasource?.latestDataKeys || [];
const result: TimeSeriesChartYAxes = {};
for (const [id, axis] of Object.entries(yAxes)) {
const minCfg = axis.min as unknown as ValueSourceConfig;
const maxCfg = axis.max as unknown as ValueSourceConfig;
const minValid =
minCfg?.type !== ValueSourceType.latestKey ||
latestKeys.some(k => this.isYAxisKey(k, minCfg));
const maxValid =
maxCfg?.type !== ValueSourceType.latestKey ||
latestKeys.some(k => this.isYAxisKey(k, maxCfg));
if (minValid && maxValid) {
result[id] = axis;
}
}
return result;
}
private updateLatestDataKeys(yAxes: TimeSeriesChartYAxisSettings[]) {
if (this.datasource) {
let latestKeys = this.datasource.latestDataKeys;
if (!latestKeys) {
latestKeys = [];
this.datasource.latestDataKeys = latestKeys;
}
const existingYAxisKeys = latestKeys.filter(k => k.settings?.__yAxisMinKey === true || k.settings?.__yAxisMaxKey === true);
const foundYAxisKeys: DataKey[] = [];
for(const yAxis of yAxes) {
const min = yAxis.min as ValueSourceConfig;
const max = yAxis.max as ValueSourceConfig;
if (min.type === ValueSourceType.latestKey) {
const found = existingYAxisKeys.find(k => this.isYAxisKey(k, min));
if (!found) {
const newKey = this.dataKeyCallbacks.generateDataKey(min.latestKey, min.latestKeyType,
null, true, null);
newKey.settings.__yAxisMinKey = true;
latestKeys.push(newKey);
} else if (foundYAxisKeys.indexOf(found) === -1) {
foundYAxisKeys.push(found);
}
}
if (max.type === ValueSourceType.latestKey) {
const found = existingYAxisKeys.find(k => this.isYAxisKey(k, max));
if (!found) {
const newKey = this.dataKeyCallbacks.generateDataKey(max.latestKey, max.latestKeyType,
null, true, null);
newKey.settings.__yAxisMaxKey = true;
latestKeys.push(newKey);
} else if (foundYAxisKeys.indexOf(found) === -1) {
foundYAxisKeys.push(found);
}
}
}
const toRemove = existingYAxisKeys.filter(k => foundYAxisKeys.indexOf(k) === -1);
for (const key of toRemove) {
const index = latestKeys.indexOf(key);
if (index > -1) {
latestKeys.splice(index, 1);
}
}
}
}
private isYAxisKey(d: DataKey, limit: ValueSourceConfig): boolean {
return (d.type === DataKeyType.function && d.label === limit.latestKey) ||
(d.type !== DataKeyType.function && d.name === limit.latestKey &&
d.type === limit.latestKeyType);
}
private normalizeAxisLimit(limit: any): ValueSourceConfig {
if (!limit) {
return {
type: ValueSourceType.constant,
value: null,
entityAlias: null
};
} else if (typeof limit === 'number') {
return {
type: ValueSourceType.constant,
value: limit,
entityAlias: null
};
}
return limit;
}
}

Loading…
Cancel
Save