UI: liquid level widget improvements

This commit is contained in:
Dmitriymush 2024-01-24 13:25:02 +02:00
parent 6c6b606fdf
commit 16fa1780c0
7 changed files with 127 additions and 26 deletions

View File

@ -184,10 +184,30 @@
formControlName="volumeAttributeName">
</tb-string-autocomplete>
</ng-template>
<tb-unit-input [tagFilter]="unitsType.capacity"
required style="max-width: 25%" class="flex"
</div>
</div>
<div class="tb-form-row space-between" [fxShow]="volumeInput">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.total-volume-units</div>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic" style="max-width: 25%">
<mat-select formControlName="volumeUnitsSource" placeholder="{{ 'widget-config.set' | translate }}">
<mat-option *ngFor="let type of DataSourceTypes" [value]="type">
{{ DataSourceTypeTranslations.get(type) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<tb-unit-input *ngIf="levelCardWidgetConfigForm.get('volumeUnitsSource').value === DataSourceType.static; else selectVolumeUnitsAttributes"
class="flex" required
[tagFilter]="unitsType.capacity"
formControlName="volumeUnits">
</tb-unit-input>
<ng-template #selectVolumeUnitsAttributes>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
required style="flex: 1"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
formControlName="volumeUnitsAttributeName">
</tb-string-autocomplete>
</ng-template>
</div>
</div>
</div>

View File

@ -194,7 +194,9 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
volumeSource: [settings.volumeSource, []],
volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]],
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]],
volumeUnitsSource: [settings.volumeUnitsSource, []],
volumeUnits: [settings.volumeUnits, [Validators.required]],
volumeUnitsAttributeName: [settings.volumeUnitsAttributeName, [Validators.required]],
volumeFont: [settings.volumeFont, []],
volumeColor: [settings.volumeColor, []],
units: [settings.units, [Validators.required]],
@ -260,6 +262,8 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
this.widgetConfig.config.settings.volumeSource = config.volumeSource;
this.widgetConfig.config.settings.volumeConstant = config.volumeConstant;
this.widgetConfig.config.settings.volumeAttributeName = config.volumeAttributeName;
this.widgetConfig.config.settings.volumeUnitsSource = config.volumeUnitsSource;
this.widgetConfig.config.settings.volumeUnitsAttributeName = config.volumeUnitsAttributeName;
this.widgetConfig.config.settings.volumeUnits = config.volumeUnits;
this.widgetConfig.config.settings.volumeFont = config.volumeFont;
this.widgetConfig.config.settings.volumeColor = config.volumeColor;
@ -294,7 +298,7 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
protected validatorTriggers(): string[] {
return [
'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits', 'showTitleIcon', 'volumeSource',
'showTooltipDate', 'layout', 'showTitle', 'widgetUnitsSource'
'showTooltipDate', 'layout', 'showTitle', 'widgetUnitsSource', 'volumeUnitsSource'
];
}

View File

@ -106,6 +106,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
private volume: number;
private tooltipContent: string;
private widgetUnits: string;
private volumeUnits: string;
private capacityUnits = Object.values(CapacityUnits);
@ -128,7 +129,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
this.getData().subscribe(data => {
if (data) {
const { svg, volume, units } = data;
const { svg, volume, units, volumeUnits } = data;
if (svg && isNotEmptyStr(svg) && this.liquidLevelContent.nativeElement) {
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement);
jQueryContainerElement.html(svg);
@ -145,6 +146,10 @@ export class LiquidLevelWidgetComponent implements OnInit {
this.volume = Number(volume);
}
if (volumeUnits) {
this.volumeUnits = volumeUnits;
}
if (units) {
this.widgetUnits = units;
}
@ -164,7 +169,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat);
}
private getData(): Observable<{ svg: string; volume: number; units: string }> {
private getData(): Observable<{ svg: string; volume: number; units: string; volumeUnits: string}> {
if (this.ctx.datasources?.length) {
const entityId: EntityId = {
entityType: this.ctx.datasources[0].entityType,
@ -308,7 +313,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
.pipe(map(attributes => {
const shape = extractValue<Shapes>(attributes, this.settings.shapeAttributeName);
if (!shape || !svgMapping.has(shape)) {
this.createdErrorMgs(this.settings.shapeAttributeName, isUndefinedOrNull(shape) || isEmptyStr(shape));
this.createdErrorMsg(this.settings.shapeAttributeName, isUndefinedOrNull(shape) || isEmptyStr(shape));
return this.settings.selectedShape;
}
return shape;
@ -318,12 +323,15 @@ export class LiquidLevelWidgetComponent implements OnInit {
return of(this.settings.selectedShape);
}
private getTankersParams(entityId: EntityId): Observable<{ volume: number; units: string }> {
private getTankersParams(entityId: EntityId): Observable<{ volume: number; units: string; volumeUnits: string }> {
const isVolumeStatic = this.settings.layout !== LevelCardLayout.absolute
&& this.settings.datasourceUnits === CapacityUnits.percent
|| this.settings.volumeSource === LiquidWidgetDataSourceType.static;
const isUnitStatic = this.settings.layout !== LevelCardLayout.absolute ||
this.settings.widgetUnitsSource === LiquidWidgetDataSourceType.static;
const isVolumeUnitStatic = this.settings.layout !== LevelCardLayout.absolute
&& this.settings.datasourceUnits === CapacityUnits.percent
|| this.settings.volumeUnitsSource === LiquidWidgetDataSourceType.static;
const attributeKeys: string[] = [];
@ -335,20 +343,29 @@ export class LiquidLevelWidgetComponent implements OnInit {
attributeKeys.push(this.settings.widgetUnitsAttributeName);
}
if (!isVolumeUnitStatic) {
attributeKeys.push(this.settings.volumeUnitsAttributeName);
}
if (!attributeKeys.length || entityId.id === NULL_UUID) {
return of({
volume: this.settings.volumeConstant,
volumeUnits: this.settings.volumeUnits,
units: this.settings.units
});
}
return this.ctx.attributeService.getEntityAttributes(entityId, null, attributeKeys).pipe(
map(attributes => {
let volume = isVolumeStatic ? this.settings.volumeConstant : extractValue<number>(attributes, this.settings.volumeAttributeName);
let units = isUnitStatic ? this.settings.units : extractValue<string>(attributes, this.settings.widgetUnitsAttributeName);
let volume = isVolumeStatic ? this.settings.volumeConstant :
extractValue<number>(attributes, this.settings.volumeAttributeName);
let volumeUnits = isVolumeUnitStatic ? this.settings.volumeUnits :
extractValue<string>(attributes, this.settings.volumeUnitsAttributeName);
let units = isUnitStatic ? this.settings.units :
extractValue<string>(attributes, this.settings.widgetUnitsAttributeName);
if (!isVolumeStatic && (!volume || !isNumeric(volume) || volume < 0.1)) {
this.createdErrorMgs(this.settings.volumeAttributeName, isUndefinedOrNull(volume) || isEmptyStr(volume));
this.createdErrorMsg(this.settings.volumeAttributeName, isUndefinedOrNull(volume) || isEmptyStr(volume));
volume = this.settings.volumeConstant;
}
@ -358,20 +375,33 @@ export class LiquidLevelWidgetComponent implements OnInit {
units = this.capacityUnits.find(unit => unit.normalize() === normalizeUnits);
}
if (isUndefinedOrNull(units) || !isNotEmptyStr(units)) {
this.createdErrorMgs(this.settings.widgetUnitsAttributeName, isUndefinedOrNull(units) || isEmptyStr(units));
this.createdErrorMsg(this.settings.widgetUnitsAttributeName, isUndefinedOrNull(units) || isEmptyStr(units));
units = this.settings.units;
}
}
if (!isVolumeUnitStatic) {
if (isNotEmptyStr(volumeUnits)) {
const normalizeUnits = volumeUnits.normalize().trim();
volumeUnits = this.capacityUnits.find(unit => unit.normalize() === normalizeUnits);
}
if (isUndefinedOrNull(volumeUnits) || !isNotEmptyStr(volumeUnits)) {
this.createdErrorMsg(this.settings.widgetUnitsAttributeName,
isUndefinedOrNull(volumeUnits) || isEmptyStr(volumeUnits));
volumeUnits = this.settings.volumeUnits;
}
}
return {
volume,
volumeUnits,
units
};
})
);
}
private createdErrorMgs(attributeName: string, isEmpty = false) {
private createdErrorMsg(attributeName: string, isEmpty = false) {
if (isEmpty) {
this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-not-set', {attributeName}));
} else {
@ -474,9 +504,14 @@ export class LiquidLevelWidgetComponent implements OnInit {
}
if (this.settings.layout === LevelCardLayout.absolute) {
const volumeInLiters: number = convertLiters(this.volume, this.settings.volumeUnits as CapacityUnits, ConversionType.to);
const volume = convertLiters(volumeInLiters, this.widgetUnits as CapacityUnits, ConversionType.from)
.toFixed(this.settings.decimals || 0);
let volume: number | string;
if (this.widgetUnits !== CapacityUnits.percent) {
const volumeInLiters: number = convertLiters(this.volume, this.volumeUnits as CapacityUnits, ConversionType.to);
volume = convertLiters(volumeInLiters, this.widgetUnits as CapacityUnits, ConversionType.from)
.toFixed(this.settings.decimals || 0);
} else {
volume = this.volume.toFixed(this.settings.decimals || 0);
}
const volumeTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.volumeFont),
color: this.settings.volumeColor});
@ -553,7 +588,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
private convertInputData(value: any): number {
if (this.settings.datasourceUnits !== CapacityUnits.percent) {
return (convertLiters(Number(value), this.settings.datasourceUnits, ConversionType.to) /
convertLiters(this.volume, this.settings.volumeUnits, ConversionType.to)) * 100;
convertLiters(this.volume, this.volumeUnits as CapacityUnits, ConversionType.to)) * 100;
}
return Number(value);
@ -561,7 +596,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
private convertOutputData(value: number): number {
if (this.widgetUnits !== CapacityUnits.percent) {
return convertLiters(this.volume * (value / 100), this.settings.volumeUnits, ConversionType.to);
return convertLiters(this.volume * (value / 100), this.volumeUnits as CapacityUnits, ConversionType.to);
}
return value;

View File

@ -56,6 +56,8 @@ export interface LevelCardWidgetSettings extends WidgetConfig {
volumeSource: LiquidWidgetDataSourceType;
volumeConstant: number;
volumeAttributeName: string;
volumeUnitsSource: LiquidWidgetDataSourceType;
volumeUnitsAttributeName: string;
volumeUnits: CapacityUnits;
volumeFont: Font;
volumeColor: string;
@ -257,8 +259,10 @@ export const levelCardDefaultSettings: LevelCardWidgetSettings = {
iconColor: '#5469FF',
volumeSource: LiquidWidgetDataSourceType.static,
volumeConstant: 500,
volumeUnits: CapacityUnits.liters,
volumeAttributeName: 'volume',
volumeUnitsSource: LiquidWidgetDataSourceType.static,
volumeUnitsAttributeName: 'volumeUnits',
volumeUnits: CapacityUnits.liters,
volumeFont: {
family: 'Roboto',
size: 14,
@ -375,9 +379,7 @@ export const convertLiters = (value: number, units: CapacityUnits, conversionTyp
return conversionType === ConversionType.to ? value / factor : value * factor;
};
export const extractValue = <T>(attributes: Array<AttributeData>, attributeName: string): T | undefined => {
return attributes.find(attr => attr.key === attributeName)?.value;
};
export const extractValue = <T>(attributes: Array<AttributeData>, attributeName: string): T | undefined => attributes.find(attr => attr.key === attributeName)?.value;
export const valueContainerStyleDefaults = cssTextFromInlineStyle({
width: '100%',
@ -494,6 +496,7 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => {
const datasourceUnits: string = formGroup.get('datasourceUnits').value;
const layout: LevelCardLayout = formGroup.get('layout').value;
const volumeSource: LiquidWidgetDataSourceType = formGroup.get('volumeSource').value;
const volumeUnitsSource: LiquidWidgetDataSourceType = formGroup.get('volumeUnitsSource').value;
const widgetUnitsSource: LiquidWidgetDataSourceType = formGroup.get('widgetUnitsSource').value;
const showTooltipLevel: boolean = formGroup.get('showTooltipLevel').value;
const showTooltipDate: boolean = formGroup.get('showTooltipDate').value;
@ -517,7 +520,7 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => {
if (datasourceUnits !== CapacityUnits.percent) {
formGroup.get('volumeSource').enable({emitEvent: false});
formGroup.get('volumeUnits').enable({emitEvent: false});
formGroup.get('volumeUnitsSource').enable({emitEvent: false});
if (volumeSource === LiquidWidgetDataSourceType.static) {
formGroup.get('volumeConstant').enable({emitEvent: false});
formGroup.get('volumeAttributeName').disable({emitEvent: false});
@ -525,11 +528,20 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => {
formGroup.get('volumeConstant').disable({emitEvent: false});
formGroup.get('volumeAttributeName').enable({emitEvent: false});
}
if (volumeUnitsSource === LiquidWidgetDataSourceType.static) {
formGroup.get('volumeUnits').enable({emitEvent: false});
formGroup.get('volumeUnitsAttributeName').disable({emitEvent: false});
} else {
formGroup.get('volumeUnits').disable({emitEvent: false});
formGroup.get('volumeUnitsAttributeName').enable({emitEvent: false});
}
} else {
formGroup.get('volumeSource').disable({emitEvent: false});
formGroup.get('volumeConstant').disable({emitEvent: false});
formGroup.get('volumeAttributeName').disable({emitEvent: false});
formGroup.get('volumeUnitsSource').disable({emitEvent: false});
formGroup.get('volumeUnits').disable({emitEvent: false});
formGroup.get('volumeUnitsAttributeName').disable({emitEvent: false});
}
if (layout === LevelCardLayout.simple) {
@ -557,7 +569,7 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => {
}
formGroup.get('volumeSource').enable({emitEvent: false});
formGroup.get('volumeUnits').enable({emitEvent: false});
formGroup.get('volumeUnitsSource').enable({emitEvent: false});
if (volumeSource === LiquidWidgetDataSourceType.static) {
formGroup.get('volumeConstant').enable({emitEvent: false});
formGroup.get('volumeAttributeName').disable({emitEvent: false});
@ -565,6 +577,13 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => {
formGroup.get('volumeConstant').disable({emitEvent: false});
formGroup.get('volumeAttributeName').enable({emitEvent: false});
}
if (volumeUnitsSource === LiquidWidgetDataSourceType.static) {
formGroup.get('volumeUnits').enable({emitEvent: false});
formGroup.get('volumeUnitsAttributeName').disable({emitEvent: false});
} else {
formGroup.get('volumeUnits').disable({emitEvent: false});
formGroup.get('volumeUnitsAttributeName').enable({emitEvent: false});
}
if (formGroup.get('decimals')) {
formGroup.get('decimals').enable({emitEvent: false});

View File

@ -141,10 +141,30 @@
formControlName="volumeAttributeName">
</tb-string-autocomplete>
</ng-template>
<tb-unit-input [tagFilter]="unitsType.capacity"
required style="max-width: 25%" class="flex"
</div>
</div>
<div class="tb-form-row space-between" [fxShow]="volumeInput">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.total-volume-units</div>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic" style="max-width: 25%">
<mat-select formControlName="volumeUnitsSource" placeholder="{{ 'widget-config.set' | translate }}">
<mat-option *ngFor="let type of DataSourceTypes" [value]="type">
{{ DataSourceTypeTranslations.get(type) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<tb-unit-input *ngIf="levelCardWidgetSettingsForm.get('volumeUnitsSource').value === DataSourceType.static; else selectVolumeUnitsAttributes"
class="flex" required
[tagFilter]="unitsType.capacity"
formControlName="volumeUnits">
</tb-unit-input>
<ng-template #selectVolumeUnitsAttributes>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
required style="flex: 1"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
formControlName="volumeUnitsAttributeName">
</tb-string-autocomplete>
</ng-template>
</div>
</div>
</div>

View File

@ -158,7 +158,9 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon
volumeSource: [settings.volumeSource, []],
volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]],
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]],
volumeUnitsSource: [settings.volumeUnitsSource, []],
volumeUnits: [settings.volumeUnits, [Validators.required]],
volumeUnitsAttributeName: [settings.volumeUnitsAttributeName, [Validators.required]],
volumeFont: [settings.volumeFont, []],
volumeColor: [settings.volumeColor, []],
valueFont: [settings.valueFont, []],
@ -195,7 +197,7 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon
protected validatorTriggers(): string[] {
return [
'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits',
'showTooltipDate', 'layout', 'volumeSource', 'widgetUnitsSource'
'showTooltipDate', 'layout', 'volumeSource', 'widgetUnitsSource', 'volumeUnitsSource'
];
}

View File

@ -6241,6 +6241,7 @@
"layout": "Layout",
"background-overlay": "Value background overlay",
"total-volume": "Total volume",
"total-volume-units": "Total volume units",
"tank": "Tank",
"shape": "Shape",
"datasource-units": "Source units",