Browse Source

Merge branch 'feature/level_widget' of github.com:Dmitriymush/thingsboard into feature/level_widget

pull/9456/head
Vladyslav_Prykhodko 3 years ago
parent
commit
73566db398
  1. 22
      application/src/main/data/json/system/widget_bundles/liquid_level.json
  2. 24
      application/src/main/data/json/system/widget_types/horizontal_2_1_elliptical_tank.json
  3. 24
      application/src/main/data/json/system/widget_types/horizontal_capsule_tank.json
  4. 24
      application/src/main/data/json/system/widget_types/horizontal_cylinder_tank.json
  5. 24
      application/src/main/data/json/system/widget_types/horizontal_dish_ends_tank.json
  6. 24
      application/src/main/data/json/system/widget_types/horizontal_ellipse_tank.json
  7. 24
      application/src/main/data/json/system/widget_types/horizontal_oval_tank.json
  8. 24
      application/src/main/data/json/system/widget_types/rectangle_tank.json
  9. 24
      application/src/main/data/json/system/widget_types/vertical_capsule_tank.json
  10. 24
      application/src/main/data/json/system/widget_types/vertical_cylinder_tank.json
  11. 24
      application/src/main/data/json/system/widget_types/vertical_oval_tank.json
  12. 10
      ui-ngx/src/app/core/http/attribute.service.ts
  13. 4
      ui-ngx/src/app/core/services/resources.service.ts
  14. 12
      ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts
  15. 334
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/liquid-level-card-basic-config.component.html
  16. 656
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/liquid-level-card-basic-config.component.ts
  17. 1
      ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts
  18. 428
      ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts
  19. 510
      ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.ts
  20. 267
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/liquid-level-card-widget-settings.component.html
  21. 516
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/liquid-level-card-widget-settings.component.ts
  22. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.html
  23. 12
      ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
  24. 7
      ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
  25. 51
      ui-ngx/src/app/shared/components/string-autocomplete.component.html
  26. 43
      ui-ngx/src/app/shared/components/string-autocomplete.component.scss
  27. 178
      ui-ngx/src/app/shared/components/string-autocomplete.component.ts
  28. 9
      ui-ngx/src/app/shared/components/unit-input.component.html
  29. 35
      ui-ngx/src/app/shared/components/unit-input.component.ts
  30. 9
      ui-ngx/src/app/shared/models/unit.models.ts
  31. 27
      ui-ngx/src/app/shared/models/widget-settings.models.ts
  32. 3
      ui-ngx/src/app/shared/shared.module.ts
  33. 69
      ui-ngx/src/assets/help/en_US/widget/lib/indicator/shape_attribute_fn.md
  34. 57
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  35. 4
      ui-ngx/src/assets/metadata/units.json
  36. 50
      ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg
  37. 49
      ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-capsule.svg
  38. 56
      ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-cylinder.svg
  39. 50
      ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-dish-ends.svg
  40. 61
      ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-ellipse.svg
  41. 61
      ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-oval.svg
  42. 49
      ui-ngx/src/assets/widget/liquid-level/shapes/rectangle.svg
  43. 49
      ui-ngx/src/assets/widget/liquid-level/shapes/vertical-capsule.svg
  44. 58
      ui-ngx/src/assets/widget/liquid-level/shapes/vertical-cylinder.svg
  45. 56
      ui-ngx/src/assets/widget/liquid-level/shapes/vertical-oval.svg
  46. 12
      ui-ngx/src/form.scss
  47. 13
      ui-ngx/src/styles.scss

22
application/src/main/data/json/system/widget_bundles/liquid_level.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/horizontal_2_1_elliptical_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/horizontal_capsule_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/horizontal_cylinder_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/horizontal_dish_ends_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/horizontal_ellipse_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/horizontal_oval_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/rectangle_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/vertical_capsule_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/vertical_cylinder_tank.json

File diff suppressed because one or more lines are too long

24
application/src/main/data/json/system/widget_types/vertical_oval_tank.json

File diff suppressed because one or more lines are too long

10
ui-ngx/src/app/core/http/attribute.service.ts

@ -32,12 +32,18 @@ export class AttributeService {
private http: HttpClient
) { }
public getEntityAttributes(entityId: EntityId, attributeScope: AttributeScope,
public getEntityAttributes(entityId: EntityId, attributeScope?: AttributeScope,
keys?: Array<string>, config?: RequestConfig): Observable<Array<AttributeData>> {
let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes/${attributeScope}`;
let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes`;
if (attributeScope) {
url += `/${attributeScope}`;
}
if (keys && keys.length) {
url += `?keys=${keys.join(',')}`;
}
return this.http.get<Array<AttributeData>>(url, defaultHttpOptionsFromConfig(config));
}

4
ui-ngx/src/app/core/services/resources.service.ts

@ -68,7 +68,9 @@ export class ResourcesService {
}
const subject = new ReplaySubject<any>();
this.loadedJsonResources[url] = subject;
this.http.get<T>(url).subscribe(
const req$ = (url.endsWith('.raw') || url.endsWith('.svg') ?
this.http.get(url, {responseType: 'text'}) : this.http.get<T>(url)) as Observable<T>;
req$.subscribe(
{
next: (o) => {
if (postProcess) {

12
ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts

@ -70,6 +70,9 @@ import {
import {
ProgressBarBasicConfigComponent
} from '@home/components/widget/config/basic/cards/progress-bar-basic-config.component';
import {
LiquidLevelCardBasicConfigComponent
} from '@home/components/widget/config/basic/cards/liquid-level-card-basic-config.component';
@NgModule({
declarations: [
@ -91,7 +94,8 @@ import {
WindSpeedDirectionBasicConfigComponent,
SignalStrengthBasicConfigComponent,
ValueChartCardBasicConfigComponent,
ProgressBarBasicConfigComponent
ProgressBarBasicConfigComponent,
LiquidLevelCardBasicConfigComponent
],
imports: [
CommonModule,
@ -117,7 +121,8 @@ import {
WindSpeedDirectionBasicConfigComponent,
SignalStrengthBasicConfigComponent,
ValueChartCardBasicConfigComponent,
ProgressBarBasicConfigComponent
ProgressBarBasicConfigComponent,
LiquidLevelCardBasicConfigComponent
]
})
export class BasicWidgetConfigModule {
@ -137,5 +142,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type<IBasicWidgetCo
'tb-wind-speed-direction-basic-config': WindSpeedDirectionBasicConfigComponent,
'tb-signal-strength-basic-config': SignalStrengthBasicConfigComponent,
'tb-value-chart-card-basic-config': ValueChartCardBasicConfigComponent,
'tb-progress-bar-basic-config': ProgressBarBasicConfigComponent
'tb-progress-bar-basic-config': ProgressBarBasicConfigComponent,
'tb-liquid-level-card-basic-config': LiquidLevelCardBasicConfigComponent
};

334
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/liquid-level-card-basic-config.component.html

@ -0,0 +1,334 @@
<!--
Copyright © 2016-2023 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ng-container *ngIf="levelCardWidgetConfigForm" [formGroup]="levelCardWidgetConfigForm">
<tb-timewindow-config-panel *ngIf="displayTimewindowConfig"
[onlyHistoryTimewindow]="onlyHistoryTimewindow()"
formControlName="timewindowConfig">
</tb-timewindow-config-panel>
<tb-datasources
[configMode]="basicMode"
hideDataKeyLabel
hideDataKeyColor
hideDataKeyUnits
hideDataKeyDecimals
formControlName="datasources">
</tb-datasources>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.appearance</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle">
{{ 'widgets.liquid-level-card.title' | translate }}
</mat-slide-toggle>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="titleFont"
[previewText]="levelCardWidgetConfigForm.get('title').value">
</tb-font-settings>
<tb-color-input asBoxInput colorClearButton formControlName="titleColor">
</tb-color-input>
</div>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitleIcon">
{{ 'widgets.liquid-level-card.icon' | translate }}
</mat-slide-toggle>
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput type="number" min="0" formControlName="iconSize" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<tb-css-unit-select fxFlex formControlName="iconSizeUnit"></tb-css-unit-select>
<tb-material-icon-select asBoxInput
iconClearButton
[color]="levelCardWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<tb-color-input asBoxInput colorClearButton formControlName="iconColor">
</tb-color-input>
</div>
</div>
<div fxFlex fxLayout="column" class="tb-form-row space-between">
<div fxFlex fxLayout="row" style="width: 100%;" fxLayoutAlign="space-between center">
<div class="fixed-title-width" translate>widgets.liquid-level-card.shape</div>
<tb-toggle-select formControlName="tankSelectionType">
<tb-toggle-option [value]="shapeSelectOptions.static">
{{ 'widgets.liquid-level-card.static' | translate }}
</tb-toggle-option>
<tb-toggle-option [value]="shapeSelectOptions.attribute">
{{ 'widgets.liquid-level-card.attribute' | translate }}
</tb-toggle-option>
</tb-toggle-select>
</div>
<tb-image-cards-select #shapesImageCardsSelect
[fxShow]="levelCardWidgetConfigForm.get('tankSelectionType')?.value === shapeSelectOptions.static"
rowHeight="{{ '1:1' }}"
[cols]="5"
[colsLtMd]="5"
style="width: 100%;"
label="{{ 'widgets.liquid-level-card.shape-type' | translate }}" formControlName="selectedShape">
<tb-image-cards-select-option *ngFor="let shape of shapes"
[value]="shape"
[image]="createShapeLayout(shapesImageMap.get(shape), this.levelCardLayouts.simple)">
{{ shapesTranslationMap.get(shape) | translate }}
</tb-image-cards-select-option>
</tb-image-cards-select>
<div class="tb-form-row space-between" style="width: 100%;"
[fxShow]="levelCardWidgetConfigForm.get('tankSelectionType')?.value === shapeSelectOptions.attribute">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.shape-attribute-name</div>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('shapeAttributeName')"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1; width: auto;"
asBoxInput colorClearButton class="flex"
formControlName="shapeAttributeName">
</tb-string-autocomplete>
</div>
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center" style="width: 100%;"
[fxShow]="levelCardWidgetConfigForm.get('tankSelectionType')?.value === shapeSelectOptions.attribute">
<div class="tb-hint" style="padding: 0;" translate>widgets.liquid-level-card.shape-by-attribute</div>
<div class="see-example"
[tb-help-popup]="'widget/lib/indicator/shape_attribute_fn'"
tb-help-popup-placement="left"
trigger-style="font-size: 12px"
[tb-help-popup-style]="{maxWidth: '820px'}"
trigger-text="{{ 'widgets.liquid-level-card.see-examples' | translate }}">
</div>
</div>
</div>
<tb-image-cards-select #layoutsImageCardsSelect rowHeight="{{ '1:1' }}"
[cols]="3"
[colsLtMd]="3"
label="{{ 'widgets.liquid-level-card.layout' | translate }}" formControlName="layout">
<tb-image-cards-select-option
*ngFor="let layout of [levelCardLayouts.simple, levelCardLayouts.percentage, levelCardLayouts.absolute]"
[value]="layout"
[image]="createShapeLayout(shapesImageMap.get(levelCardWidgetConfigForm.get('selectedShape').value), layout)">
{{ levelCardLayoutTranslationMap.get(layout) | translate }}
</tb-image-cards-select-option>
</tb-image-cards-select>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.units</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.datasource-units</div>
<tb-unit-input style="max-width: 30%;" class="flex"
[tagFilter]="unitsType.capacity"
[required]="isRequired('datasourceUnits')"
formControlName="datasourceUnits">
</tb-unit-input>
</div>
<div class="tb-form-row space-between" [fxShow]="widgetUnitsInput">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.widget-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="widgetUnitsSource" placeholder="{{ 'widget-config.set' | translate }}">
<mat-option *ngFor="let option of [levelOptions.static, levelOptions.attribute]" [value]="option">
{{ option }}
</mat-option>
</mat-select>
</mat-form-field>
<tb-unit-input [fxShow]="levelCardWidgetConfigForm.get('widgetUnitsSource')?.value !== levelOptions.attribute"
asBoxInput colorClearButton class="flex"
[tagFilter]="unitsType.capacity"
[required]="isRequired('units')"
formControlName="units">
</tb-unit-input>
<tb-string-autocomplete [fxShow]="levelCardWidgetConfigForm.get('widgetUnitsSource')?.value === levelOptions.attribute"
[fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('widgetUnitsAttributeName')"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1; width: auto;"
asBoxInput colorClearButton class="flex"
formControlName="widgetUnitsAttributeName">
</tb-string-autocomplete>
</div>
</div>
<div class="tb-form-row" [fxShow]="volumeInput">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.total-volume</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="volumeSource" placeholder="{{ 'widget-config.set' | translate }}">
<mat-option *ngFor="let option of [levelOptions.static, levelOptions.attribute]" [value]="option">
{{ option }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field [fxShow]="levelCardWidgetConfigForm.get('volumeSource')?.value === levelOptions.static"
appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="volumeConstant" type="number"
min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'widgets.liquid-level-card.total-volume-required' | translate"
*ngIf="levelCardWidgetConfigForm.get('volumeConstant').hasError('required')"
class="material-icons tb-suffix-show-always tb-error">
warning
</mat-icon>
</mat-form-field>
<tb-string-autocomplete style="max-width: 25%"
[fxShow]="levelCardWidgetConfigForm.get('volumeSource')?.value === levelOptions.attribute"
[fetchOptionsFn]="fetchOptions.bind(this)"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
[required]="isRequired('volumeAttributeName')"
style="flex: 1; width: auto;"
asBoxInput colorClearButton class="flex"
formControlName="volumeAttributeName">
</tb-string-autocomplete>
<tb-unit-input asBoxInput colorClearButton
[tagFilter]="unitsType.capacity"
[required]="isRequired('volumeUnits')"
style="max-width: 25%" class="flex"
formControlName="volumeUnits">
</tb-unit-input>
</div>
</div>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.color-and-font</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.tank-color</div>
<tb-color-settings formControlName="tankColor">
</tb-color-settings>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.liquid-color</div>
<tb-color-settings formControlName="liquidColor">
</tb-color-settings>
</div>
<div class="tb-form-row space-between"
*ngIf="levelCardWidgetConfigForm.get('layout')?.value !== this.levelCardLayouts.simple">
<div class="fixed-title-width" translate>widgets.liquid-level-card.value</div>
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<mat-form-field style="max-width: 12%;" appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="decimals" type="number"
min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="valueFont"
[previewText]="valuePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="valueColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row" [fxShow]="volumeInput">
<div class="fixed-title-width" translate>widgets.liquid-level-card.total-volume</div>
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<tb-font-settings formControlName="volumeFont"
[previewText]="totalVolumeValuePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="volumeColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showBackgroundOverlay">
{{ 'widgets.liquid-level-card.background-overlay' | translate }}
</mat-slide-toggle>
<tb-color-settings formControlName="backgroundOverlayColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-panel tb-slide-toggle">
<mat-expansion-panel class="tb-settings"
[expanded]="levelCardWidgetConfigForm.get('showTooltip').value"
[disabled]="!levelCardWidgetConfigForm.get('showTooltip').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widget-config.tooltip' | translate }}
</mat-slide-toggle>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipLevel">
{{ 'widgets.liquid-level-card.level' | translate }}
</mat-slide-toggle>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-unit-input [required]="isRequired('tooltipUnits')"
[tagFilter]="unitsType.capacity" class="flex"
formControlName="tooltipUnits">
</tb-unit-input>
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="tooltipLevelDecimals" type="number" min="0"
max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div>
</mat-form-field>
<tb-font-settings formControlName="tooltipLevelFont"
[previewText]="tooltipValuePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="tooltipLevelColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipDate">
{{ 'widgets.value-card.date' | translate }}
</mat-slide-toggle>
<div fxFlex.gt-xs fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-date-format-select fxFlex formControlName="tooltipDateFormat"></tb-date-format-select>
<tb-font-settings formControlName="tooltipDateFont"
[previewText]="datePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="tooltipDateColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.tooltip-background</div>
<tb-color-settings formControlName="tooltipBackgroundColor">
</tb-color-settings>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.background-blur</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1"
placeholder="{{ 'widget-config.set' | translate }}">
<div matSuffix>px</div>
</mat-form-field>
</div>
</ng-template>
</mat-expansion-panel>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div>
<div class="tb-form-row space-between">
<div>{{ 'widgets.background.background' | translate }}</div>
<tb-color-input asBoxInput colorClearButton formControlName="backgroundColor">
</tb-color-input>
</div>
<div class="tb-form-row space-between column-lt-md">
<div translate>widget-config.show-card-buttons</div>
<mat-chip-listbox multiple formControlName="cardButtons">
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</div>
<div class="tb-form-row space-between">
<div>{{ 'widget-config.card-border-radius' | translate }}</div>
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="borderRadius" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
<tb-widget-actions-panel
formControlName="actions">
</tb-widget-actions-panel>
</ng-container>

656
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/liquid-level-card-basic-config.component.ts

@ -0,0 +1,656 @@
///
/// Copyright © 2016-2023 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { ChangeDetectorRef, Component, Injector, ViewChild } from '@angular/core';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import {
DataKey,
Datasource,
datasourcesHasAggregation,
datasourcesHasOnlyComparisonAggregation,
DatasourceType,
WidgetConfig,
} from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import {
getTimewindowConfig,
setTimewindowConfig
} from '@home/components/widget/config/timewindow-config-panel.component';
import { formatValue, isDefined, isUndefined } from '@core/utils';
import {
cssSizeToStrSize,
DateFormatProcessor,
DateFormatSettings,
resolveCssSize
} from '@shared/models/widget-settings.models';
import {
CapacityUnits,
createAbsoluteLayout,
createPercentLayout,
levelCardDefaultSettings,
LevelCardLayout,
levelCardLayoutTranslations,
LevelCardWidgetSettings,
LevelSelectOptions,
optionsFilter,
Shapes,
shapesTranslations,
svgMapping
} from '@home/components/widget/lib/indicator/liquid-level-widget.models';
import { UnitsType } from '@shared/models/unit.models';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component';
import { map, publishReplay, refCount, tap } from 'rxjs/operators';
import { forkJoin, Observable, of } from 'rxjs';
import { ResourcesService } from '@core/services/resources.service';
import { UnitInputComponent } from '@shared/components/unit-input.component';
import { UtilsService } from '@core/services/utils.service';
@Component({
selector: 'liquid-level-card-basic-config',
templateUrl: './liquid-level-card-basic-config.component.html',
styleUrls: ['../basic-config.scss']
})
export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigComponent {
@ViewChild('layoutsImageCardsSelect', { static: false }) layoutsImageCardsSelect: ImageCardsSelectComponent;
@ViewChild('shapesImageCardsSelect', { static: false }) shapesImageCardsSelect: ImageCardsSelectComponent;
@ViewChild('widgetUnits', { static: false }) widgetUnits: UnitInputComponent;
public get datasource(): Datasource {
if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources) {
return this.widgetConfig.config.datasources[0];
} else {
return null;
}
}
public get volumeInput(): boolean {
const datasourceUnits = this.levelCardWidgetConfigForm.get('datasourceUnits').value;
const layout: LevelCardLayout = this.levelCardWidgetConfigForm.get('layout').value;
const units = this.levelCardWidgetConfigForm.get('units').value;
return !(datasourceUnits === CapacityUnits.percent && layout !== LevelCardLayout.absolute
|| (datasourceUnits === CapacityUnits.percent && units === CapacityUnits.percent));
}
public get widgetUnitsInput(): boolean {
const layout: LevelCardLayout = this.levelCardWidgetConfigForm.get('layout').value;
if (layout === LevelCardLayout.absolute) {
const datasourceUnits = this.levelCardWidgetConfigForm.get('datasourceUnits').value;
return !(datasourceUnits === CapacityUnits.percent);
}
return false;
}
public get displayTimewindowConfig(): boolean {
const datasources = this.levelCardWidgetConfigForm.get('datasources').value;
return datasourcesHasAggregation(datasources);
}
public onlyHistoryTimewindow(): boolean {
const datasources = this.levelCardWidgetConfigForm.get('datasources').value;
return datasourcesHasOnlyComparisonAggregation(datasources);
}
levelCardLayouts = LevelCardLayout;
shapes: Shapes[] = [];
levelOptions = LevelSelectOptions;
shapeSelectOptions = LevelSelectOptions;
levelCardLayoutTranslationMap = levelCardLayoutTranslations;
shapesTranslationMap = shapesTranslations;
shapesImageMap: Map<Shapes, string> = new Map();
unitsType = UnitsType;
levelCardWidgetConfigForm: UntypedFormGroup;
valuePreviewFn = this._valuePreviewFn.bind(this);
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this);
totalVolumeValuePreviewFn = this._totalVolumeValuePreviewFn.bind(this);
datePreviewFn = this._datePreviewFn.bind(this);
keySearchText: string;
latestKeySearchTextResult: Array<string>;
datasources: Array<Datasource>;
functionTypeKeys: Array<DataKey> = [];
lastKeysId: string;
lastFetchedKeys: Array<DataKey>;
constructor(protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent,
private cd: ChangeDetectorRef,
private $injector: Injector,
private fb: UntypedFormBuilder,
private resourcesService: ResourcesService,
private sanitizer: DomSanitizer,
private utils: UtilsService) {
super(store, widgetConfigComponent);
}
protected configForm(): UntypedFormGroup {
return this.levelCardWidgetConfigForm;
}
protected setupConfig(widgetConfig: WidgetConfigComponentData) {
this.shapes = Object.values(Shapes);
this.createSvgShapesMapping();
for (const type of this.utils.getPredefinedFunctionsList()) {
this.functionTypeKeys.push({
name: type,
type: DataKeyType.function
});
}
super.setupConfig(widgetConfig);
}
protected setupDefaults(configData: WidgetConfigComponentData) {
this.setupDefaultDatasource(configData, [{ name: 'fuelLevel', label: 'Fuel Level', type: DataKeyType.timeseries }]);
}
protected onConfigSet(configData: WidgetConfigComponentData) {
const settings: LevelCardWidgetSettings = {...configData.config, ...levelCardDefaultSettings(), ...(configData.config.settings || {})};
const iconSize = resolveCssSize(configData.config.iconSize);
this.levelCardWidgetConfigForm = this.fb.group({
timewindowConfig: [getTimewindowConfig(configData.config), []],
datasources: [configData.config.datasources, []],
tankSelectionType: [settings.tankSelectionType, []],
selectedShape: [settings.selectedShape, [Validators.required]],
shapeAttributeName: [settings.shapeAttributeName, [Validators.required]],
tankColor: [settings.tankColor, []],
datasourceUnits: [settings.datasourceUnits, [Validators.required]],
layout: [settings.layout, []],
showTitle: [configData.config.showTitle, []],
title: [settings.title, []],
titleFont: [settings.titleFont, []],
titleColor: [settings.titleColor, []],
showTitleIcon: [configData.config.showTitleIcon, []],
iconSize: [iconSize[0], [Validators.min(0)]],
iconSizeUnit: [iconSize[1], []],
titleIcon: [settings.titleIcon, []],
iconColor: [settings.iconColor, []],
volumeSource: [settings.volumeSource, []],
volumeConstant: [settings.volumeConstant, [Validators.required]],
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]],
volumeUnits: [settings.volumeUnits, [Validators.required]],
volumeFont: [settings.volumeFont, []],
volumeColor: [settings.volumeColor, []],
units: [settings.units, [Validators.required]],
widgetUnitsSource: [settings.widgetUnitsSource, [Validators.required]],
widgetUnitsAttributeName: [settings.widgetUnitsAttributeName, [Validators.required]],
decimals: [settings.decimals, []],
liquidColor: [settings.liquidColor, []],
valueFont: [settings.valueFont, []],
valueColor: [settings.valueColor, []],
showBackgroundOverlay: [settings.showBackgroundOverlay, []],
backgroundOverlayColor: [settings.backgroundOverlayColor, []],
showTooltip: [settings.showTooltip, []],
showTooltipLevel: [settings.showTooltipLevel, []],
tooltipUnits: [settings.tooltipUnits, []],
tooltipLevelDecimals: [settings.tooltipLevelDecimals, []],
tooltipLevelFont: [settings.tooltipLevelFont, []],
tooltipLevelColor: [settings.tooltipLevelColor, []],
showTooltipDate: [settings.showTooltipDate, []],
tooltipDateFormat: [settings.tooltipDateFormat, []],
tooltipDateFont: [settings.tooltipDateFont, []],
tooltipDateColor: [settings.tooltipDateColor, []],
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []],
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []],
backgroundColor: [settings.backgroundColor, []],
cardButtons: [this.getCardButtons(configData.config), []],
borderRadius: [configData.config.borderRadius, []],
actions: [configData.config.actions || {}, []]
});
this.levelCardWidgetConfigForm.get('selectedShape').valueChanges.subscribe((shape) => {
this.cd.detectChanges();
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges();
});
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.datasources = config.datasources;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
this.widgetConfig.config.settings.tankSelectionType = config.tankSelectionType;
this.widgetConfig.config.settings.selectedShape = config.selectedShape;
this.widgetConfig.config.settings.shapeAttributeName = config.shapeAttributeName;
this.widgetConfig.config.settings.tankColor = config.tankColor;
this.widgetConfig.config.settings.datasourceUnits = config.datasourceUnits;
this.widgetConfig.config.settings.layout = config.layout;
this.widgetConfig.config.showTitle = config.showTitle;
this.widgetConfig.config.title = config.title;
this.widgetConfig.config.titleFont = config.titleFont;
this.widgetConfig.config.titleColor = config.titleColor;
this.widgetConfig.config.showTitleIcon = config.showTitleIcon;
this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit);
this.widgetConfig.config.titleIcon = config.titleIcon;
this.widgetConfig.config.iconColor = config.iconColor;
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.volumeUnits = config.volumeUnits;
this.widgetConfig.config.settings.volumeFont = config.volumeFont;
this.widgetConfig.config.settings.volumeColor = config.volumeColor;
this.widgetConfig.config.settings.units = config.units;
this.widgetConfig.config.settings.widgetUnitsSource = config.widgetUnitsSource;
this.widgetConfig.config.settings.widgetUnitsAttributeName = config.widgetUnitsAttributeName;
this.widgetConfig.config.decimals = config.decimals;
this.widgetConfig.config.settings.liquidColor = config.liquidColor;
this.widgetConfig.config.settings.valueFont = config.valueFont;
this.widgetConfig.config.settings.valueColor = config.valueColor;
this.widgetConfig.config.settings.showBackgroundOverlay = config.showBackgroundOverlay;
this.widgetConfig.config.settings.backgroundOverlayColor = config.backgroundOverlayColor;
this.widgetConfig.config.settings.showTooltip = config.showTooltip;
this.widgetConfig.config.settings.showTooltipLevel = config.showTooltipLevel;
this.widgetConfig.config.settings.tooltipUnits = config.tooltipUnits;
this.widgetConfig.config.settings.tooltipLevelDecimals = config.tooltipLevelDecimals;
this.widgetConfig.config.settings.tooltipLevelFont = config.tooltipLevelFont;
this.widgetConfig.config.settings.tooltipLevelColor = config.tooltipLevelColor;
this.widgetConfig.config.settings.showTooltipDate = config.showTooltipDate;
this.widgetConfig.config.settings.tooltipDateFormat = config.tooltipDateFormat;
this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont;
this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor;
this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor;
this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur;
this.widgetConfig.config.backgroundColor = config.backgroundColor;
this.setCardButtons(config.cardButtons, this.widgetConfig.config);
this.widgetConfig.config.borderRadius = config.borderRadius;
this.widgetConfig.config.actions = config.actions;
return this.widgetConfig;
}
protected validatorTriggers(): string[] {
return [
'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel',
'tankSelectionType', 'datasourceUnits',
'showTitleIcon', 'volumeSource',
'showTooltipDate', 'units',
'layout', 'showTitle',
'widgetUnitsSource'
];
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
const emitEventFields = [];
const datasourceUnits: string = this.levelCardWidgetConfigForm.get('datasourceUnits').value;
const layout: LevelCardLayout = this.levelCardWidgetConfigForm.get('layout').value;
const showTitleIcon: AbstractControl<boolean> = this.levelCardWidgetConfigForm.get('showTitleIcon');
const volumeSource: AbstractControl<LevelSelectOptions> = this.levelCardWidgetConfigForm.get('volumeSource');
const units: AbstractControl<string> = this.levelCardWidgetConfigForm.get('units');
const tooltipUnits: AbstractControl<string> = this.levelCardWidgetConfigForm.get('tooltipUnits');
const widgetUnitsSource: AbstractControl<LevelSelectOptions> = this.levelCardWidgetConfigForm.get('widgetUnitsSource');
const showTooltipLevel: AbstractControl<boolean> = this.levelCardWidgetConfigForm.get('showTooltipLevel');
const showTooltipDate: AbstractControl<boolean> = this.levelCardWidgetConfigForm.get('showTooltipDate');
const showTooltip: boolean = this.levelCardWidgetConfigForm.get('showTooltip').value;
if (trigger === 'tankSelectionType') {
const tankSelectionType: LevelSelectOptions = this.levelCardWidgetConfigForm.get('tankSelectionType').value;
if (tankSelectionType === LevelSelectOptions.static) {
this.levelCardWidgetConfigForm.get('selectedShape').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('shapeAttributeName').disable({emitEvent: false});
} else {
this.levelCardWidgetConfigForm.get('selectedShape').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('shapeAttributeName').enable({emitEvent: false});
}
emitEventFields.push('selectedShape', 'shapeAttributeName');
}
if (trigger === 'datasourceUnits' || trigger === 'layout' || trigger === 'units') {
if (datasourceUnits === CapacityUnits.percent && (layout !== LevelCardLayout.absolute)
|| (datasourceUnits === CapacityUnits.percent && units?.value === CapacityUnits.percent)
) {
volumeSource.disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeConstant').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeAttributeName').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeUnits').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeFont').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeColor').disable({emitEvent: false});
} else {
volumeSource.enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeConstant').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeAttributeName').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeUnits').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeFont').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeColor').enable({emitEvent: false});
}
emitEventFields.push('volumeSource', 'volumeConstant', 'volumeAttributeName', 'volumeFont', 'volumeColor', 'volumeUnits');
} else if (trigger === 'volumeSource') {
if((datasourceUnits !== CapacityUnits.percent) ||
(layout === LevelCardLayout.absolute && units?.value !== CapacityUnits.percent)) {
if (volumeSource.value === LevelSelectOptions.static) {
this.levelCardWidgetConfigForm.get('volumeConstant').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeAttributeName').disable({emitEvent: false});
} else {
this.levelCardWidgetConfigForm.get('volumeConstant').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('volumeAttributeName').enable({emitEvent: false});
}
emitEventFields.push('volumeConstant', 'volumeAttributeName');
}
}
if (trigger === 'showTitle') {
const showTitle: boolean = this.levelCardWidgetConfigForm.get('showTitle').value;
if (showTitle) {
this.levelCardWidgetConfigForm.get('title').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('titleFont').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('titleColor').enable({emitEvent: false});
showTitleIcon.enable({emitEvent: false});
} else {
this.levelCardWidgetConfigForm.get('title').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('titleFont').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('titleColor').disable({emitEvent: false});
showTitleIcon.disable({emitEvent: false});
}
emitEventFields.push('title', 'titleFont', 'titleColor', 'showTitleIcon');
}
if (showTitleIcon?.value) {
this.levelCardWidgetConfigForm.get('titleIcon').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('iconColor').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('iconSize').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('iconSizeUnit').enable({emitEvent: false});
} else {
this.levelCardWidgetConfigForm.get('titleIcon').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('iconColor').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('iconSize').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('iconSizeUnit').disable({emitEvent: false});
}
emitEventFields.push('titleIcon', 'iconColor', 'iconSize', 'iconSizeUnit');
if (trigger === 'showBackgroundOverlay') {
const showBackgroundOverlay: boolean = this.levelCardWidgetConfigForm.get('showBackgroundOverlay').value;
if (showBackgroundOverlay) {
this.levelCardWidgetConfigForm.get('backgroundOverlayColor').enable({emitEvent: false});
} else {
this.levelCardWidgetConfigForm.get('backgroundOverlayColor').disable({emitEvent: false});
}
emitEventFields.push('backgroundOverlayColor');
}
if (trigger === 'showTooltip') {
if (showTooltip) {
showTooltipLevel.enable({emitEvent: false});
showTooltipDate.enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipBackgroundColor').enable();
this.levelCardWidgetConfigForm.get('tooltipBackgroundBlur').enable();
} else {
showTooltipLevel.disable({emitEvent: false});
showTooltipDate.disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipBackgroundColor').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipBackgroundBlur').disable({emitEvent: false});
}
emitEventFields.push('showTooltipLevel', 'showTooltipDate', 'tooltipBackgroundColor', 'tooltipBackgroundBlur');
}
if (trigger === 'showTooltipLevel') {
if (showTooltipLevel?.value && !showTooltipLevel.disabled) {
tooltipUnits.enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipLevelDecimals').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipLevelFont').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipLevelColor').enable({emitEvent: false});
} else {
tooltipUnits.disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipLevelDecimals').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipLevelFont').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipLevelColor').disable({emitEvent: false});
}
emitEventFields.push('tooltipUnits', 'tooltipLevelDecimals', 'tooltipLevelFont', 'tooltipLevelColor');
}
if (trigger === 'showTooltipDate') {
if (showTooltipDate?.value && !showTooltipDate.disabled) {
this.levelCardWidgetConfigForm.get('tooltipDateFormat').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipDateFont').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipDateColor').enable({emitEvent: false});
} else {
this.levelCardWidgetConfigForm.get('tooltipDateFormat').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipDateFont').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('tooltipDateColor').disable({emitEvent: false});
}
emitEventFields.push('tooltipDateFormat', 'tooltipDateFont', 'tooltipDateColor');
}
if (trigger === 'layout' || trigger === 'datasourceUnits') {
if (layout === LevelCardLayout.simple) {
units.disable({emitEvent: false});
tooltipUnits.disable({emitEvent: false});
widgetUnitsSource.setValue(LevelSelectOptions.static, {emitEvent: false});
widgetUnitsSource.disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('decimals').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('valueFont').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('valueColor').disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('widgetUnitsAttributeName').disable({emitEvent: false});
} else if (layout === LevelCardLayout.percentage) {
if (units.value !== CapacityUnits.percent) {
units.setValue(CapacityUnits.percent, {emitEvent: false});
}
widgetUnitsSource.setValue(LevelSelectOptions.static, {emitEvent: false});
units.disable({emitEvent: false});
widgetUnitsSource.disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('widgetUnitsAttributeName').disable({emitEvent: false});
if (tooltipUnits.value !== CapacityUnits.percent) {
tooltipUnits.setValue(CapacityUnits.percent, {emitEvent: false});
}
tooltipUnits.disable({emitEvent: false});
} else {
units.enable({emitEvent: false});
tooltipUnits.enable({emitEvent: false});
widgetUnitsSource.enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('decimals').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('valueFont').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('valueColor').enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('widgetUnitsAttributeName').enable({emitEvent: false});
}
emitEventFields.push('units', 'tooltipUnits', 'decimals', 'valueFont', 'valueColor', 'widgetUnitsSource', 'widgetUnitsAttributeName');
} else if (trigger === 'widgetUnitsSource') {
if (layout !== LevelCardLayout.percentage) {
if (widgetUnitsSource.value === LevelSelectOptions.static) {
units.enable({emitEvent: false});
this.levelCardWidgetConfigForm.get('widgetUnitsAttributeName').disable({emitEvent: false});
} else {
units.disable({emitEvent: false});
this.levelCardWidgetConfigForm.get('widgetUnitsAttributeName').enable({emitEvent: false});
}
emitEventFields.push('units', 'widgetUnitsAttributeName');
}
}
for (const controlKey in this.levelCardWidgetConfigForm.controls) {
if (emitEventFields.includes(controlKey)) {
this.levelCardWidgetConfigForm.controls[controlKey].updateValueAndValidity({emitEvent});
}
}
}
private getCardButtons(config: WidgetConfig): string[] {
const buttons: string[] = [];
if (isUndefined(config.enableDataExport) || config.enableDataExport) {
buttons.push('dataExport');
}
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) {
buttons.push('fullscreen');
}
return buttons;
}
private setCardButtons(buttons: string[], config: WidgetConfig) {
config.enableDataExport = buttons.includes('dataExport');
config.enableFullscreen = buttons.includes('fullscreen');
}
private _valuePreviewFn(): string {
const units: string = this.levelCardWidgetConfigForm.get('units').value;
const decimals: number = this.levelCardWidgetConfigForm.get('decimals').value;
return formatValue(22, decimals, units, true);
}
private _tooltipValuePreviewFn() {
const units: string = this.levelCardWidgetConfigForm.get('tooltipUnits').value;
const decimals: number = this.levelCardWidgetConfigForm.get('tooltipLevelDecimals').value;
return formatValue(32, decimals, units, true);
}
private _totalVolumeValuePreviewFn() {
const value = this.levelCardWidgetConfigForm.get('volumeConstant').value;
const datasourceUnits = this.levelCardWidgetConfigForm.get('datasourceUnits').value;
const decimals: number = this.widgetConfig.config.decimals;
let units: string = this.widgetConfig.config.units;
if (datasourceUnits !== CapacityUnits.percent) {
units = datasourceUnits;
}
return formatValue((isDefined(value) ? value : 500), decimals, units, true);
}
private _datePreviewFn(): string {
const dateFormat: DateFormatSettings = this.levelCardWidgetConfigForm.get('tooltipDateFormat').value;
const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat);
processor.update(Date.now());
return processor.formatted;
}
private createSvgShapesMapping(): void {
const obsArray: Array<Observable<{svg: string; shape: Shapes}>> = [];
for (const shape of this.shapes) {
const svgUrl = svgMapping.get(shape).svg;
const obs = this.resourcesService.loadJsonResource<string>(svgUrl).pipe(
map((svg) => ({svg, shape}))
);
obsArray.push(obs);
}
forkJoin(obsArray).subscribe((svgData) => {
for (const data of svgData) {
this.shapesImageMap.set(data.shape, data.svg);
}
this.cd.detectChanges();
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges();
this.shapesImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges();
});
}
public createShapeLayout(svg: string, layout: LevelCardLayout): SafeUrl {
if (svg && layout) {
const parser = new DOMParser();
const svgImage = parser.parseFromString(svg, 'image/svg+xml');
if (layout === this.levelCardLayouts.simple) {
svgImage.querySelector('.container-overlay').remove();
} else if (layout === this.levelCardLayouts.percentage) {
svgImage.querySelector('.absolute-overlay').remove();
svgImage.querySelector('.percentage-value-container').innerHTML = createPercentLayout();
} else {
svgImage.querySelector('.absolute-value-container').innerHTML = createAbsoluteLayout();
svgImage.querySelector('.percentage-overlay').remove();
}
const encodedSvg = encodeURIComponent(svgImage.documentElement.outerHTML);
return this.sanitizer.bypassSecurityTrustResourceUrl(`data:image/svg+xml,${encodedSvg}`);
}
}
public isRequired(formControlName: string): boolean {
return this.levelCardWidgetConfigForm.get(formControlName)?.hasValidator(Validators.required);
}
public fetchOptions(searchText: string): Observable<Array<string>> {
if (this.keySearchText !== searchText || !this.lastFetchedKeys) {
this.keySearchText = searchText;
const dataKeyFilter = optionsFilter(this.keySearchText);
return this.getKeys().pipe(
tap(res => this.lastFetchedKeys !== res ? this.lastFetchedKeys = res : []),
map(name => name?.filter(dataKeyFilter).map(key => key.name)),
tap(res => this.latestKeySearchTextResult = res)
);
}
return of(this.latestKeySearchTextResult);
}
private getKeys(): Observable<Array<DataKey>> {
let fetchObservable: Observable<Array<DataKey>>;
const callbacks = this.widgetConfigComponent.widgetConfigCallbacks;
if (this.datasource?.type === DatasourceType.function) {
fetchObservable = of(this.functionTypeKeys);
} else if (this.datasource?.type === DatasourceType.entity && this.datasource?.entityAliasId ||
this.datasource?.type === DatasourceType.device && this.datasource?.deviceId) {
if (this.datasource?.type === DatasourceType.device) {
if (this.lastKeysId !== this.datasource?.deviceId || !this.lastFetchedKeys) {
this.lastKeysId = this.datasource.deviceId;
fetchObservable = callbacks.fetchEntityKeysForDevice(this.datasource?.deviceId, [DataKeyType.attribute]);
} else {
fetchObservable = of(this.lastFetchedKeys);
}
} else {
if (this.lastKeysId !== this.datasource?.entityAliasId || !this.lastFetchedKeys) {
this.lastKeysId = this.datasource.entityAliasId;
fetchObservable = callbacks.fetchEntityKeys(this.datasource?.entityAliasId, [DataKeyType.attribute]);
} else {
fetchObservable = of(this.lastFetchedKeys);
}
}
} else {
this.lastKeysId = null;
fetchObservable = of([]);
}
return fetchObservable.pipe(
publishReplay(1),
refCount()
);
}
}

1
ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts

@ -25,7 +25,6 @@ import { AppState } from '@core/core.state';
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
import { DataKey, DatasourceType, KeyInfo, WidgetConfigMode } from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { isDefinedAndNotNull } from '@core/utils';
export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks;

428
ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts

@ -0,0 +1,428 @@
///
/// Copyright © 2016-2023 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
ColorSettings,
constantColor,
cssTextFromInlineStyle,
DateFormatSettings,
Font,
lastUpdateAgoDateFormat
} from '@shared/models/widget-settings.models';
import { DataKey, WidgetConfig } from '@shared/models/widget.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { Observable, of } from 'rxjs';
import { singleEntityFilterFromDeviceId } from '@shared/models/query/query.models';
import { EntityType } from '@shared/models/entity-type.models';
import { catchError, mergeMap } from 'rxjs/operators';
import { EntityService } from '@core/http/entity.service';
import { IAliasController } from '@core/api/widget-api.models';
export interface SvgInfo {
svg: string;
limits: SvgLimits;
}
export interface SvgLimits {
min: number;
max: number;
}
export interface LevelCardWidgetSettings extends WidgetConfig {
tankSelectionType: LevelSelectOptions;
selectedShape: Shapes;
shapeAttributeName: string;
tankColor: ColorSettings;
datasourceUnits: CapacityUnits;
layout: LevelCardLayout;
volumeSource: LevelSelectOptions;
volumeConstant: number;
volumeAttributeName: string;
volumeUnits: CapacityUnits;
volumeFont: Font;
volumeColor: ColorSettings;
liquidColor: ColorSettings;
valueFont: Font;
widgetUnitsSource: LevelSelectOptions;
widgetUnitsAttributeName: string;
valueColor: ColorSettings;
showBackgroundOverlay: boolean;
backgroundOverlayColor: ColorSettings;
showTooltip: boolean;
showTooltipLevel: boolean;
tooltipUnits: CapacityUnits;
tooltipLevelDecimals: number;
tooltipLevelFont: Font;
tooltipLevelColor: ColorSettings;
showTooltipDate: boolean;
tooltipDateFormat: DateFormatSettings;
tooltipDateFont: Font;
tooltipDateColor: ColorSettings;
tooltipBackgroundColor: ColorSettings;
tooltipBackgroundBlur: number;
}
export enum Shapes {
vOval = 'Vertical Oval',
vCylinder = 'Vertical Cylinder',
vCapsule = 'Vertical Capsule',
rectangle = 'Rectangle',
hOval = 'Horizontal Oval',
hEllipse = 'Horizontal Ellipse',
hDishEnds = 'Horizontal Dish Ends',
hCylinder = 'Horizontal Cylinder',
hCapsule = 'Horizontal Capsule',
hElliptical_2_1 = 'Horizontal 2:1 Elliptical'
}
export enum CapacityUnits {
percent = '%',
liters = 'L',
cubicMillimeter = 'mm³',
cubicCentimeter = 'cm³',
cubicMeter = 'm³',
cubicKilometer = 'km³',
milliliter = 'mL',
hectoliter = 'hl',
cubicInch = 'in³',
cubicFoot = 'ft³',
cubicYard = 'yd³',
fluidOunce = 'fl-oz',
pint = 'pt',
quart = 'qt',
gallon = 'gal',
oilBarrels = 'bbl'
}
export enum LevelCardLayout {
simple = 'simple',
percentage = 'percentage',
absolute = 'absolute'
}
export enum LevelSelectOptions {
attribute = 'Attribute',
static = 'Static'
}
export enum ConversionType {
to = 'to',
from = 'from',
}
export const svgMapping = new Map<string, SvgInfo>(
[
[
Shapes.vOval,
{
svg: 'assets/widget/liquid-level/shapes/vertical-oval.svg',
limits: { min: 170, max: 50 }
}
],
[
Shapes.vCylinder,
{
svg: 'assets/widget/liquid-level/shapes/vertical-cylinder.svg',
limits: { min: 161, max: 55 }
}
],
[
Shapes.vCapsule,
{
svg: 'assets/widget/liquid-level/shapes/vertical-capsule.svg',
limits: { min: 195, max: 25 }
}
],
[
Shapes.rectangle,
{
svg: 'assets/widget/liquid-level/shapes/rectangle.svg',
limits: { min: 168, max: 52 }
}
],
[
Shapes.hOval,
{
svg: 'assets/widget/liquid-level/shapes/horizontal-oval.svg',
limits: { min: 160, max: 63 }
}
],
[
Shapes.hEllipse,
{
svg: 'assets/widget/liquid-level/shapes/horizontal-ellipse.svg',
limits: { min: 160, max: 65 }
}
],
[
Shapes.hDishEnds,
{
svg: 'assets/widget/liquid-level/shapes/horizontal-dish-ends.svg',
limits: { min: 172, max: 50 }
}
],
[
Shapes.hCylinder,
{
svg: 'assets/widget/liquid-level/shapes/horizontal-cylinder.svg',
limits: { min: 165, max: 55 }
}
],
[
Shapes.hCapsule,
{
svg: 'assets/widget/liquid-level/shapes/horizontal-capsule.svg',
limits: { min: 158, max: 52 }
}
],
[
Shapes.hElliptical_2_1,
{
svg: 'assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg',
limits: { min: 172, max: 50 }
}
]
]
);
export const levelCardLayoutTranslations = new Map<LevelCardLayout, string>(
[
[LevelCardLayout.simple, 'widgets.liquid-level-card.layout-simple'],
[LevelCardLayout.percentage, 'widgets.liquid-level-card.layout-percentage'],
[LevelCardLayout.absolute, 'widgets.liquid-level-card.layout-absolute']
]
);
export const shapesTranslations = new Map<Shapes, string>(
[
[Shapes.vOval, 'widgets.liquid-level-card.v-oval'],
[Shapes.vCylinder, 'widgets.liquid-level-card.v-cylinder'],
[Shapes.vCapsule, 'widgets.liquid-level-card.v-capsule'],
[Shapes.rectangle, 'widgets.liquid-level-card.rectangle'],
[Shapes.hOval, 'widgets.liquid-level-card.h-oval'],
[Shapes.hEllipse, 'widgets.liquid-level-card.h-ellipse'],
[Shapes.hDishEnds, 'widgets.liquid-level-card.h-dish-ends'],
[Shapes.hCylinder, 'widgets.liquid-level-card.h-cylinder'],
[Shapes.hCapsule, 'widgets.liquid-level-card.h-capsule'],
[Shapes.hElliptical_2_1, 'widgets.liquid-level-card.h-elliptical_2_1']
]
);
export const levelCardDefaultSettings = (): LevelCardWidgetSettings => ({
tankSelectionType: LevelSelectOptions.static,
selectedShape: Shapes.vCylinder,
shapeAttributeName: 'tankShape',
tankColor: constantColor('#242770'),
datasourceUnits: CapacityUnits.percent,
layout: LevelCardLayout.percentage,
showTitle: false,
title: 'Liquid level',
titleFont: {
family: 'Roboto',
size: 16,
sizeUnit: 'px',
style: 'normal',
weight: '500',
lineHeight: '1.5'
},
titleColor: 'rgba(0, 0, 0, 0.87)',
showTitleIcon: false,
titleIcon: 'water_drop',
iconColor: '#5469FF',
volumeSource: LevelSelectOptions.static,
volumeConstant: 500,
volumeUnits: CapacityUnits.liters,
volumeAttributeName: 'volume',
volumeFont: {
family: 'Roboto',
size: 14,
sizeUnit: 'px',
style: 'normal',
weight: '500',
lineHeight: '100%'
},
volumeColor: constantColor('rgba(0, 0, 0, 0.18)'),
units: CapacityUnits.percent as string,
widgetUnitsSource: LevelSelectOptions.static,
widgetUnitsAttributeName: 'units',
decimals: 0,
liquidColor: constantColor('#7A8BFF'),
valueFont: {
family: 'Roboto',
size: 24,
sizeUnit: 'px',
style: 'normal',
weight: '500',
lineHeight: '100%'
},
valueColor: constantColor('rgba(0, 0, 0, 0.87)'),
showBackgroundOverlay: true,
backgroundOverlayColor: constantColor('rgba(255, 255, 255, 0.76)'),
showTooltip: true,
showTooltipLevel: true,
tooltipUnits: CapacityUnits.percent,
tooltipLevelDecimals: 0,
tooltipLevelFont: {
family: 'Roboto',
size: 13,
sizeUnit: 'px',
style: 'normal',
weight: '500',
lineHeight: '100%'
},
tooltipLevelColor: constantColor('rgba(0, 0, 0, 0.76)'),
showTooltipDate: true,
tooltipDateFormat: lastUpdateAgoDateFormat(),
tooltipDateFont: {
family: 'Roboto',
size: 13,
sizeUnit: 'px',
style: 'normal',
weight: '500',
lineHeight: '100%'
},
tooltipDateColor: constantColor('rgba(0, 0, 0, 0.76)'),
tooltipBackgroundColor: constantColor('rgba(255, 255, 255, 0.76)'),
tooltipBackgroundBlur: 3
});
export const convertLiters = (value: number, units: CapacityUnits, conversionType: ConversionType): number => {
let factor: number;
switch (units) {
case CapacityUnits.liters:
return value;
case CapacityUnits.cubicMillimeter:
factor = 1e6;
break;
case CapacityUnits.cubicCentimeter:
case CapacityUnits.milliliter:
factor = 1e3;
break;
case CapacityUnits.cubicMeter:
factor = 1e-3;
break;
case CapacityUnits.cubicKilometer:
factor = 1e-15;
break;
case CapacityUnits.hectoliter:
factor = 0.01;
break;
case CapacityUnits.cubicInch:
factor = 61.0237;
break;
case CapacityUnits.cubicFoot:
factor = 1 / 28.3168;
break;
case CapacityUnits.cubicYard:
factor = 1 / 764.555;
break;
case CapacityUnits.fluidOunce:
factor = 33.814;
break;
case CapacityUnits.pint:
factor = 1 / 0.473176;
break;
case CapacityUnits.quart:
factor = 1 / 0.946353;
break;
case CapacityUnits.gallon:
factor = 1 / 3.78541;
break;
case CapacityUnits.oilBarrels:
factor = 1 / 158.987;
break;
default:
return value;
// throw new Error(`Unknown unit: ${units}`);
}
return conversionType === ConversionType.to ? value / factor : value * factor;
};
export const extractValue = (attributes: any[], attributeName: string, defaultValue: any): any => {
const index = attributes.findIndex(attr => attr.key === attributeName);
return index !== -1 ? attributes[index].value : defaultValue;
};
export const valueContainerStyleDefaults = cssTextFromInlineStyle({
width: '100%',
height: '100%',
display: 'flex',
gap: '8px',
'text-align': 'center',
'align-items': 'center',
'align-content': 'center',
'justify-content': 'center',
});
export const valueTextStyleDefaults = 'letterSpacing: normal; font-style: normal; font-weight: 500; line-height: 100%;' +
' font-size: 24px; font-family: Roboto; color: #000000DE';
export const volumeTextStyleDefaults = 'letterSpacing: normal; font-style: normal; font-weight: 500;' +
' line-height: 100%; font-size: 14px; font-family: Roboto; color: rgba(0, 0, 0, 0.18)';
export const createAbsoluteLayout = (values?: {inputValue: number | string; volume: number | string},
styles?: {valueStyle: string; volumeStyle: string},
units?: string): string => {
const inputValue = values?.inputValue ? values?.inputValue : 500;
const valueTextStyle = styles?.valueStyle ? styles?.valueStyle : valueTextStyleDefaults;
const volume = values?.volume ? values?.volume : 1000;
const volumeTextStyle = styles?.volumeStyle ? styles?.volumeStyle : volumeTextStyleDefaults;
const displayUnits = units ? units : CapacityUnits.liters;
return `<div xmlns="http://www.w3.org/1999/xhtml" style="${valueContainerStyleDefaults}">
<div style="display: flex; flex-direction: column; justify-content: center; align-content: center;">
<label style="${valueTextStyle}">${inputValue}</label>
<div style="border-top: 1px solid rgba(0, 0, 0, 0.38); height: 1px; width: 100%; margin: 3px 0"></div>
<label style="${volumeTextStyle}">${volume}</label>
</div>
<label><b>${displayUnits}</b></label>
</div>`;
};
export const createPercentLayout = (value: number | string = 50, valueTextStyle: string = valueTextStyleDefaults): string =>
`<div xmlns="http://www.w3.org/1999/xhtml" style="${valueContainerStyleDefaults}">
<label><span style="${valueTextStyle}">${value}</span> ${CapacityUnits.percent}</label>
</div>`;
export const optionsFilter = (searchText: string): ((key: DataKey) => boolean) =>
key => key?.name.toUpperCase().includes(searchText?.toUpperCase());
export const fetchEntityKeysForDevice = (deviceId: string, dataKeyTypes: Array<DataKeyType>,
entityService: EntityService): Observable<Array<DataKey>> => {
const entityFilter = singleEntityFilterFromDeviceId(deviceId);
return entityService.getEntityKeysByEntityFilter(
entityFilter,
dataKeyTypes, [EntityType.DEVICE],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))
);
};
export const fetchEntityKeys = (entityAliasId: string, dataKeyTypes: Array<DataKeyType>,
entityService: EntityService, aliasController: IAliasController): Observable<Array<DataKey>> =>
aliasController.getAliasInfo(entityAliasId).pipe(
mergeMap((aliasInfo) => entityService.getEntityKeysByEntityFilter(
aliasInfo.entityFilter,
dataKeyTypes, [],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))
)),
catchError(() => of([] as Array<DataKey>))
);

510
ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.ts

@ -0,0 +1,510 @@
///
/// Copyright © 2016-2023 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { WidgetContext } from '@home/models/widget-component.models';
import { isDefined, isDefinedAndNotNull, isNumber, isString } from '@core/utils';
import {
CapacityUnits,
ConversionType,
convertLiters, createAbsoluteLayout, createPercentLayout,
extractValue,
levelCardDefaultSettings,
LevelCardLayout,
LevelCardWidgetSettings,
LevelSelectOptions,
Shapes,
SvgInfo,
SvgLimits,
svgMapping
} from '@home/components/widget/lib/indicator/liquid-level-widget.models';
import { Observable, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { EntityId } from '@shared/models/id/entity-id';
import {
ColorProcessor,
cssTextFromInlineStyle,
DateFormatProcessor,
inlineTextStyle
} from '@shared/models/widget-settings.models';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
import { ResourcesService } from '@core/services/resources.service';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'tb-liquid-level-widget',
template: ''
})
export class LiquidLevelWidgetComponent implements OnInit {
@Input()
ctx: WidgetContext;
svgParams: SvgInfo;
svg: JQuery<SVGElement>;
tooltip: ITooltipsterInstance;
overlayContainer: JQuery<HTMLElement>;
shape: Shapes;
settings: LevelCardWidgetSettings;
tankColor: ColorProcessor;
volumeColor: ColorProcessor;
valueColor: ColorProcessor;
liquidColor: ColorProcessor;
backgroundOverlayColor: ColorProcessor;
tooltipLevelColor: ColorProcessor;
tooltipDateFormat: DateFormatProcessor;
tooltipDateColor: ColorProcessor;
tooltipBackgroundColor: ColorProcessor;
volume: number;
tooltipContent: string;
widgetUnits: string;
constructor() {
}
ngOnInit(): void {
this.ctx.$scope.liquidLevelWidget = this;
this.settings = {...levelCardDefaultSettings(), ...this.ctx.widgetConfig, ...this.ctx.settings};
this.declareStyles();
}
public onInit() {
this.getData().subscribe(data => {
if (data) {
const { svg, volume, units } = data;
if (svg && isString(svg) && this.ctx.$container) {
this.ctx.$container.html(svg);
this.svg = this.ctx.$container.find('svg');
this.svg.on('click', this.cardClick.bind(this));
this.createSVG();
this.createValueElement();
if (this.settings.showTooltip) {
this.createTooltip();
}
}
if (isDefined(volume) && isNumber(volume)) {
this.volume = volume;
}
if (units) {
this.widgetUnits = units;
}
this.update();
}
});
}
private declareStyles():void {
this.tankColor = ColorProcessor.fromSettings(this.settings.tankColor);
this.volumeColor = ColorProcessor.fromSettings(this.settings.volumeColor);
this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor);
this.liquidColor = ColorProcessor.fromSettings(this.settings.liquidColor);
this.backgroundOverlayColor = ColorProcessor.fromSettings(this.settings.backgroundOverlayColor);
this.tooltipLevelColor = ColorProcessor.fromSettings(this.settings.tooltipLevelColor);
this.tooltipDateColor = ColorProcessor.fromSettings(this.settings.tooltipDateColor);
this.tooltipBackgroundColor = ColorProcessor.fromSettings(this.settings.tooltipBackgroundColor);
this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat);
}
private getData(): Observable<{ svg: string; volume: number; units: string }> {
const entityId: EntityId = {
entityType: this.ctx.datasources[0].entityType,
id: this.ctx.datasources[0].entityId
};
return this.getShape(entityId).pipe(
switchMap(shape => {
this.shape = shape;
this.svgParams = svgMapping.get(shape);
if (this.svgParams) {
return this.loadSVG(this.svgParams.svg).pipe(
switchMap( svg =>
this.getSecondaryResources(entityId).pipe(
map(({ volume, units }) =>
({ svg, volume, units })
)
)
)
);
}
return of(null);
})
);
}
public update() {
if (this.svg) {
this.updateData();
}
}
public destroy() {
}
private createSVG() {
if (this.svg) {
this.svg.css('display', 'block')
.css('overflow', 'hidden')
.css('width', '100%')
.css('height', '100%');
}
}
private updateData() {
const data = this.ctx.data[0]?.data[0]?.map(value => Number(value));
if (isDefinedAndNotNull(data) && data.length && typeof Number(data[1]) === 'number') {
const percentage = this.convertInputData(Number(data[1]));
this.updateSvg(percentage);
this.updateValueElement(this.convertOutputData(percentage), percentage);
if (this.settings.showTooltip) {
this.updateTooltip(data);
}
}
}
private createTooltip(): void {
this.tooltipContent = this.getTooltipContent();
import('tooltipster').then(() => {
if ($.tooltipster && this.tooltip) {
this.tooltip.destroy();
this.tooltip = null;
}
this.tooltip = this.svg.tooltipster({
contentAsHTML: true,
theme: 'tooltipster-shadow',
side: 'top',
delay: 10,
distance: -33,
triggerClose: {
click: true,
tap: true,
scroll: true,
mouseleave: true
},
animation: 'grow',
updateAnimation: null,
trackOrigin: true,
functionBefore: (instance, helper) => {
instance.content(this.tooltipContent);
},
functionReady: (instance, helper) => {
const tooltipsterBoxStyles: JQuery.PlainObject = {
backgroundColor: this.getTooltipBackground(),
backdropFilter: `blur(${this.settings.tooltipBackgroundBlur}px)`,
width: '100%'
};
$(helper.tooltip).css('max-width', this.svg.width() + 'px');
$(helper.tooltip).find('.tooltipster-box').css(tooltipsterBoxStyles);
$(helper.tooltip).find('.tooltipster-arrow').empty();
instance.reposition();
}
}).tooltipster('instance');
});
}
private createValueElement(): void {
const containerOverlay = this.ctx.$container.find('.container-overlay');
const percentageOverlay = this.ctx.$container.find('.percentage-overlay');
const absoluteOverlay = this.ctx.$container.find('.absolute-overlay');
if (this.settings.layout === LevelCardLayout.absolute) {
this.overlayContainer = absoluteOverlay;
percentageOverlay.css('visibility', 'hidden');
if (!this.settings.showBackgroundOverlay) {
absoluteOverlay.css('visibility', 'hidden');
}
} else if (this.settings.layout === LevelCardLayout.percentage) {
this.overlayContainer = percentageOverlay;
absoluteOverlay.css('visibility', 'hidden');
if (!this.settings.showBackgroundOverlay) {
percentageOverlay.css('visibility', 'hidden');
}
} else {
containerOverlay.css('visibility', 'hidden');
}
if (this.overlayContainer) {
this.overlayContainer.attr('fill', this.backgroundOverlayColor.color);
this.overlayContainer.removeAttr('fill-opacity');
}
}
private loadSVG(url: string): Observable<string> {
const resourcesService = this.ctx.$injector.get(ResourcesService);
return resourcesService.loadJsonResource(url);
}
private getShape(entityId: EntityId): Observable<Shapes> {
if (this.settings.tankSelectionType === LevelSelectOptions.attribute && entityId.id !== NULL_UUID) {
return this.ctx.attributeService.getEntityAttributes(entityId, null,
[this.settings.shapeAttributeName]).pipe(
map(attributes =>
extractValue(attributes, this.settings.shapeAttributeName, this.settings.selectedShape)
)
);
}
return of(this.settings.selectedShape);
}
private getSecondaryResources(entityId: EntityId): Observable<{ volume: number; units: string }> {
const attributeNames = this.prepareAttributeNames();
if (!attributeNames.length || entityId.id === NULL_UUID) {
return of({
volume: this.settings.volumeConstant,
units: this.settings.units
});
}
return this.ctx.attributeService.getEntityAttributes(entityId, null, attributeNames).pipe(
map(attributes => ({
volume: extractValue(attributes, this.settings.volumeAttributeName, this.settings.volumeConstant),
units: extractValue(attributes, this.settings.widgetUnitsAttributeName, this.settings.units)
}))
);
}
private prepareAttributeNames(): string[] {
const names = [];
if (this.settings.volumeSource !== LevelSelectOptions.static) {
names.push(this.settings.volumeAttributeName);
}
if (this.settings.widgetUnitsSource !== LevelSelectOptions.static) {
names.push(this.settings.widgetUnitsAttributeName);
}
return names;
}
private updateSvg(percentage: number) {
const yLimits: SvgLimits = {
min: this.svgParams.limits.min,
max: this.svgParams.limits.max
};
const newYPos = this.calculatePosition(percentage, yLimits);
this.updateShapeColor(percentage);
this.updateLevel(newYPos, percentage);
}
private calculatePosition(percentage, limits): number {
return limits.min + (percentage / 100) * (limits.max - limits.min);
}
private updateTooltip(value: number[]): void {
this.tooltipContent = this.getTooltipContent(value);
if (this.tooltip) {
this.tooltip.content(this.tooltipContent);
}
}
private updateLevel(newY: number, percentage: number): void {
this.liquidColor.update(percentage);
const fill = this.ctx.$container.find('.tb-liquid-fill');
const surfaces = this.ctx.$container.find('.tb-liquid-surface');
const surfacePositionAttr = this.shape !== Shapes.vCylinder ? 'y' : 'cy';
const animationSpeed = 500;
if (percentage > 1) {
fill.css('visibility', 'visible');
fill.animate({y : newY}, animationSpeed);
fill.attr('fill', this.liquidColor.color);
surfaces.each((index, element) => {
const $element = $(element);
$element.css('visibility', 'visible');
$element.animate({[surfacePositionAttr]: newY}, animationSpeed);
if ($element.hasClass('tb-liquid')) {
$element.attr('fill', this.liquidColor.color);
}
});
} else {
fill.css('visibility', 'hidden').stop().clearQueue();
surfaces.each((index, element) => {
$(element).css('visibility', 'hidden').stop().clearQueue();
});
}
}
private updateShapeColor(value): void {
const shapeStrokes = this.ctx.$container.find('.tb-shape-stroke');
const shapeFill = this.ctx.$container.find('.tb-shape-fill');
this.tankColor.update(value);
shapeStrokes.each((index, element) => {
$(element).attr('stroke', this.tankColor.color);
});
shapeFill.each((index, element) => {
$(element).attr('fill', this.tankColor.color);
});
}
private updateValueElement(data: number, percentage: number): void {
let content: string;
let container: JQuery<HTMLElement>;
const value = convertLiters(data, this.widgetUnits as CapacityUnits, ConversionType.from)
.toFixed(this.settings.decimals || 0);
const valueTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.valueFont),
color: this.valueColor.color});
this.backgroundOverlayColor.update(percentage);
if (this.overlayContainer) {
this.overlayContainer.attr('fill', this.backgroundOverlayColor.color);
}
if (this.settings.layout === LevelCardLayout.absolute) {
this.volumeColor.update(percentage);
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);
const volumeTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.volumeFont),
color: this.volumeColor.color});
container = this.ctx.$container.find('.absolute-value-container');
content = createAbsoluteLayout({inputValue: value, volume},
{valueStyle: valueTextStyle, volumeStyle: volumeTextStyle}, this.widgetUnits);
} else if (this.settings.layout === LevelCardLayout.percentage) {
container = this.ctx.$container.find('.percentage-value-container');
content = createPercentLayout(value, valueTextStyle);
}
if (content && container) {
container.html(content);
}
}
private getTooltipContent(value?: number[]): string {
const contentValue = value || [0, 0];
if (contentValue[1]) {
contentValue[1] = this.convertTooltipData(contentValue[1]);
}
this.tooltipLevelColor.update(contentValue[1]);
this.tooltipDateColor.update(contentValue[0]);
this.tooltipDateFormat.update(contentValue[0]);
this.tooltipBackgroundColor.update(contentValue);
const levelTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.tooltipLevelFont),
color: this.tooltipLevelColor.color});
const dateTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.tooltipDateFont),
color: this.tooltipDateColor.color, overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap'});
let content = `<div style="display: flex; flex-direction: column;
gap: 8px; background-color: transparent">`;
if (this.settings.showTooltipLevel) {
const levelValue = contentValue[1]?.toFixed(this.settings.tooltipLevelDecimals) + this.settings.tooltipUnits;
content += this.createTooltipContent(
this.ctx.translate.instant('widgets.liquid-level-card.level'),
levelValue,
levelTextStyle
);
}
if (this.settings.showTooltipDate) {
content += this.createTooltipContent(
this.ctx.translate.instant('widgets.liquid-level-card.last-update'),
this.tooltipDateFormat.formatted,
dateTextStyle
);
}
content += '</div>';
return content;
}
private createTooltipContent(labelText: string, contentValue: string, textStyle: string): string {
return `<div style="display: flex; justify-content: space-between; gap: 8px">
<label style="color: rgba(0, 0, 0, 0.38); font-size: 12px; font-weight: 400; white-space: nowrap;">
${labelText}
</label>
<label style="${textStyle}">
${contentValue}
</label>
</div>`;
}
private getTooltipBackground(): string {
return this.tooltipBackgroundColor.color;
}
private convertInputData(value: number): number {
if (this.settings.datasourceUnits !== CapacityUnits.percent) {
return (convertLiters(value, this.settings.datasourceUnits, ConversionType.to) /
convertLiters(this.volume, this.settings.volumeUnits, ConversionType.to)) * 100;
}
return value;
}
private convertOutputData(value: number): number {
if (this.widgetUnits !== CapacityUnits.percent) {
return convertLiters(this.volume * (value / 100), this.settings.volumeUnits, ConversionType.to);
}
return value;
}
private convertTooltipData(value: number): number {
const percentage = this.convertInputData(value);
if (this.settings.tooltipUnits !== CapacityUnits.percent) {
const liters = this.convertOutputData(percentage);
return convertLiters(liters, this.settings.tooltipUnits, ConversionType.from);
} else {
return percentage;
}
}
public cardClick($event) {
this.ctx.actionsApi.cardClick($event);
}
}

267
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/liquid-level-card-widget-settings.component.html

@ -0,0 +1,267 @@
<!--
Copyright © 2016-2023 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ng-container *ngIf="levelCardWidgetSettingsForm" [formGroup]="levelCardWidgetSettingsForm">
<div style="display: flex; flex-direction: column; gap: 16px;">
<div class="tb-form-panel">
<div fxFlex fxLayout="column" class="tb-form-row space-between">
<div fxFlex fxLayout="row" style="width: 100%;" fxLayoutAlign="space-between center">
<div class="fixed-title-width" translate>widgets.liquid-level-card.shape</div>
<tb-toggle-select formControlName="tankSelectionType">
<tb-toggle-option [value]="shapeSelectOptions.static">
{{ 'widgets.liquid-level-card.static' | translate }}
</tb-toggle-option>
<tb-toggle-option [value]="shapeSelectOptions.attribute">
{{ 'widgets.liquid-level-card.attribute' | translate }}
</tb-toggle-option>
</tb-toggle-select>
</div>
<tb-image-cards-select #shapesImageCardsSelect
[fxShow]="levelCardWidgetSettingsForm.get('tankSelectionType')?.value === shapeSelectOptions.static"
rowHeight="{{ '1:1' }}"
[cols]="5"
[colsLtMd]="5"
style="width: 100%;"
label="{{ 'widgets.liquid-level-card.shape-type' | translate }}" formControlName="selectedShape">
<tb-image-cards-select-option *ngFor="let shape of shapes"
[value]="shape"
[image]="createShapeLayout(shapesImageMap.get(shape), this.levelCardLayouts.simple)">
{{ shapesTranslationMap.get(shape) | translate }}
</tb-image-cards-select-option>
</tb-image-cards-select>
<div class="tb-form-row space-between" style="width: 100%;"
[fxShow]="levelCardWidgetSettingsForm.get('tankSelectionType')?.value === shapeSelectOptions.attribute">
<div class="fixed-title-width" translate>widgets.liquid-level-card.shape-attribute-name</div>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('shapeAttributeName')"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1; width: auto;"
asBoxInput colorClearButton class="flex"
formControlName="shapeAttributeName">
</tb-string-autocomplete>
</div>
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center" style="width: 100%;"
[fxShow]="levelCardWidgetSettingsForm.get('tankSelectionType')?.value === shapeSelectOptions.attribute">
<div class="tb-hint" style="padding: 0;" translate>widgets.liquid-level-card.shape-by-attribute</div>
<div class="see-example"
[tb-help-popup]="'widget/lib/indicator/shape_attribute_fn'"
tb-help-popup-placement="left"
trigger-style="font-size: 12px"
[tb-help-popup-style]="{maxWidth: '820px'}"
trigger-text="{{ 'widgets.liquid-level-card.see-examples' | translate }}">
</div>
</div>
</div>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.layout</div>
<tb-image-cards-select #layoutsImageCardsSelect
rowHeight="{{ '1:1' }}"
[cols]="3"
[colsLtMd]="3"
label="{{ 'widgets.liquid-level-card.layout' | translate }}" formControlName="layout">
<tb-image-cards-select-option
*ngFor="let layout of [levelCardLayouts.simple, levelCardLayouts.percentage, levelCardLayouts.absolute]"
[value]="layout"
[image]="createShapeLayout(shapesImageMap.get(levelCardWidgetSettingsForm.get('selectedShape').value), layout)">
{{ levelCardLayoutTranslationMap.get(layout) | translate }}
</tb-image-cards-select-option>
</tb-image-cards-select>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.units</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.datasource-units</div>
<tb-unit-input style="max-width: 30%;" class="flex"
[tagFilter]="unitsType.capacity"
[required]="isRequired('datasourceUnits')"
formControlName="datasourceUnits">
</tb-unit-input>
</div>
<div class="tb-form-row space-between" [fxShow]="widgetUnitsInput">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.widget-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="widgetUnitsSource" placeholder="{{ 'widget-config.set' | translate }}">
<mat-option *ngFor="let option of [volumeOptions.static, volumeOptions.attribute]" [value]="option">
{{ option }}
</mat-option>
</mat-select>
</mat-form-field>
<tb-unit-input [fxShow]="levelCardWidgetSettingsForm.get('widgetUnitsSource')?.value !== volumeOptions.attribute"
asBoxInput colorClearButton class="flex"
[tagFilter]="unitsType.capacity"
[required]="isRequired('units')"
formControlName="units">
</tb-unit-input>
<tb-string-autocomplete [fxShow]="levelCardWidgetSettingsForm.get('widgetUnitsSource')?.value === volumeOptions.attribute"
[fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('widgetUnitsAttributeName')"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1; width: auto;"
asBoxInput colorClearButton class="flex"
formControlName="widgetUnitsAttributeName">
</tb-string-autocomplete>
</div>
</div>
<div class="tb-form-row" [fxShow]="volumeInput">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.total-volume</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="volumeSource" placeholder="{{ 'widget-config.set' | translate }}">
<mat-option *ngFor="let option of [volumeOptions.static, volumeOptions.attribute]" [value]="option">
{{ option }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field [fxShow]="levelCardWidgetSettingsForm.get('volumeSource')?.value === volumeOptions.static"
appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="volumeConstant" type="number"
min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'widgets.liquid-level-card.total-volume-required' | translate"
*ngIf="levelCardWidgetSettingsForm.get('volumeConstant').hasError('required')"
class="material-icons tb-suffix-show-always tb-error">
warning
</mat-icon>
</mat-form-field>
<tb-string-autocomplete style="max-width: 25%"
[fxShow]="levelCardWidgetSettingsForm.get('volumeSource')?.value === volumeOptions.attribute"
[fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('volumeAttributeName')"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1; width: auto;"
asBoxInput colorClearButton class="flex"
formControlName="volumeAttributeName">
</tb-string-autocomplete>
<tb-unit-input asBoxInput colorClearButton
[tagFilter]="unitsType.capacity"
[required]="isRequired('volumeUnits')"
style="max-width: 25%" class="flex"
formControlName="volumeUnits">
</tb-unit-input>
</div>
</div>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.color-and-font</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.tank-color</div>
<tb-color-settings formControlName="tankColor">
</tb-color-settings>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.liquid-color</div>
<tb-color-settings formControlName="liquidColor">
</tb-color-settings>
</div>
<div class="tb-form-row space-between"
*ngIf="levelCardWidgetSettingsForm.get('layout')?.value !== this.levelCardLayouts.simple">
<div class="fixed-title-width" translate>widgets.liquid-level-card.value</div>
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<tb-font-settings formControlName="valueFont"
[previewText]="valuePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="valueColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row" [fxShow]="volumeInput">
<div class="fixed-title-width" translate>widgets.liquid-level-card.total-volume</div>
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<tb-font-settings formControlName="volumeFont"
[previewText]="totalVolumeValuePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="volumeColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showBackgroundOverlay">
{{ 'widgets.liquid-level-card.background-overlay' | translate }}
</mat-slide-toggle>
<tb-color-settings formControlName="backgroundOverlayColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-panel tb-slide-toggle">
<mat-expansion-panel class="tb-settings"
[expanded]="levelCardWidgetSettingsForm.get('showTooltip').value"
[disabled]="!levelCardWidgetSettingsForm.get('showTooltip').value">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()"
fxLayoutAlign="center">
{{ 'widget-config.tooltip' | translate }}
</mat-slide-toggle>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipLevel">
{{ 'widgets.liquid-level-card.level' | translate }}
</mat-slide-toggle>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-unit-input [tagFilter]="unitsType.capacity"
[required]="isRequired('tooltipUnits')"
class="flex" formControlName="tooltipUnits">
</tb-unit-input>
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="tooltipLevelDecimals" type="number" min="0"
max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div>
</mat-form-field>
<tb-font-settings formControlName="tooltipLevelFont"
[previewText]="tooltipValuePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="tooltipLevelColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipDate">
{{ 'widgets.value-card.date' | translate }}
</mat-slide-toggle>
<div fxFlex.gt-xs fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-date-format-select fxFlex formControlName="tooltipDateFormat"></tb-date-format-select>
<tb-font-settings formControlName="tooltipDateFont"
[previewText]="datePreviewFn">
</tb-font-settings>
<tb-color-settings formControlName="tooltipDateColor">
</tb-color-settings>
</div>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.tooltip-background</div>
<tb-color-settings formControlName="tooltipBackgroundColor">
</tb-color-settings>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" translate>widgets.liquid-level-card.background-blur</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1"
placeholder="{{ 'widget-config.set' | translate }}">
<div matSuffix>px</div>
</mat-form-field>
</div>
</ng-template>
</mat-expansion-panel>
</div>
</div>
</ng-container>

516
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/liquid-level-card-widget-settings.component.ts

@ -0,0 +1,516 @@
///
/// Copyright © 2016-2023 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { ChangeDetectorRef, Component, Injector, ViewChild } from '@angular/core';
import {
DataKey,
Datasource,
DatasourceType,
WidgetSettings,
WidgetSettingsComponent
} from '@shared/models/widget.models';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { formatValue, isDefined } from '@core/utils';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import {
DateFormatProcessor,
DateFormatSettings
} from '@shared/models/widget-settings.models';
import {
levelCardDefaultSettings,
LevelCardLayout,
levelCardLayoutTranslations,
Shapes,
shapesTranslations,
svgMapping,
CapacityUnits,
LevelSelectOptions,
createPercentLayout,
createAbsoluteLayout,
optionsFilter,
fetchEntityKeysForDevice,
fetchEntityKeys
} from '@home/components/widget/lib/indicator/liquid-level-widget.models';
import { UnitsType } from '@shared/models/unit.models';
import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { forkJoin, Observable, of } from 'rxjs';
import { map, publishReplay, refCount, tap } from 'rxjs/operators';
import { ResourcesService } from '@core/services/resources.service';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { UtilsService } from '@core/services/utils.service';
import { EntityService } from '@core/http/entity.service';
@Component({
selector: 'liquid-level-card-widget-settings',
templateUrl: './liquid-level-card-widget-settings.component.html',
styleUrls: []
})
export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsComponent {
@ViewChild('layoutsImageCardsSelect', { static: false }) layoutsImageCardsSelect: ImageCardsSelectComponent;
@ViewChild('shapesImageCardsSelect', { static: false }) shapesImageCardsSelect: ImageCardsSelectComponent;
public get volumeInput(): boolean {
const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value;
const layout: LevelCardLayout = this.levelCardWidgetSettingsForm.get('layout').value;
const widgetUnits = this.levelCardWidgetSettingsForm.get('units').value;
return !(datasourceUnits === CapacityUnits.percent && layout !== LevelCardLayout.absolute
|| (datasourceUnits === CapacityUnits.percent && widgetUnits === CapacityUnits.percent));
}
public get widgetUnitsInput(): boolean {
const layout: LevelCardLayout = this.levelCardWidgetSettingsForm.get('layout').value;
if (layout === LevelCardLayout.absolute) {
const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value;
return !(datasourceUnits === CapacityUnits.percent);
}
return false;
}
public get datasource(): Datasource {
if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources) {
return this.widgetConfig.config.datasources[0];
} else {
return null;
}
}
shapeSelectOptions = LevelSelectOptions;
shapes: Shapes[] = [];
shapesTranslationMap = shapesTranslations;
unitsType = UnitsType;
levelCardLayouts = LevelCardLayout;
levelCardLayoutTranslationMap = levelCardLayoutTranslations;
shapesImageMap: Map<Shapes, string> = new Map();
volumeOptions = LevelSelectOptions;
levelCardWidgetSettingsForm: UntypedFormGroup;
valuePreviewFn = this._valuePreviewFn.bind(this);
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this);
totalVolumeValuePreviewFn = this._totalVolumeValuePreviewFn.bind(this);
datePreviewFn = this._datePreviewFn.bind(this);
keySearchText: string;
latestKeySearchTextResult: Array<string>;
datasources: Array<Datasource>;
functionTypeKeys: Array<DataKey> = [];
lastKeysId: string;
lastFetchedKeys: Array<DataKey>;
constructor(protected store: Store<AppState>,
private $injector: Injector,
private fb: UntypedFormBuilder,
private resourcesService: ResourcesService,
private sanitizer: DomSanitizer,
private cd: ChangeDetectorRef,
private utils: UtilsService,
private entityService: EntityService) {
super(store);
}
protected settingsForm(): UntypedFormGroup {
return this.levelCardWidgetSettingsForm;
}
protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) {
this.shapes = Object.values(Shapes);
this.createSvgShapesMapping();
for (const type of this.utils.getPredefinedFunctionsList()) {
this.functionTypeKeys.push({
name: type,
type: DataKeyType.function
});
}
}
protected defaultSettings(): WidgetSettings {
return levelCardDefaultSettings();
}
protected onSettingsSet(settings: WidgetSettings) {
this.levelCardWidgetSettingsForm = this.fb.group({
tankSelectionType: [settings.tankSelectionType, []],
selectedShape: [settings.selectedShape, [Validators.required]],
shapeAttributeName: [settings.shapeAttributeName, [Validators.required]],
tankColor: [settings.tankColor, []],
datasourceUnits: [settings.datasourceUnits, [Validators.required]],
layout: [settings.layout, []],
volumeSource: [settings.volumeSource, []],
volumeConstant: [settings.volumeConstant, [Validators.required]],
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]],
volumeUnits: [settings.volumeUnits, [Validators.required]],
volumeFont: [settings.volumeFont, []],
volumeColor: [settings.volumeColor, []],
valueFont: [settings.valueFont, []],
valueColor: [settings.valueColor, []],
units: [settings.units, [Validators.required]],
widgetUnitsSource: [settings.widgetUnitsSource, [Validators.required]],
widgetUnitsAttributeName: [settings.widgetUnitsAttributeName, [Validators.required]],
showBackgroundOverlay: [settings.showBackgroundOverlay, []],
backgroundOverlayColor: [settings.backgroundOverlayColor, []],
liquidColor: [settings.liquidColor, []],
showTooltip: [settings.showTooltip, []],
showTooltipLevel: [settings.showTooltipLevel, []],
tooltipUnits: [settings.tooltipUnits, []],
tooltipLevelDecimals: [settings.tooltipLevelDecimals, []],
tooltipLevelFont: [settings.tooltipLevelFont, []],
tooltipLevelColor: [settings.tooltipLevelColor, []],
showTooltipDate: [settings.showTooltipDate, []],
tooltipDateFormat: [settings.tooltipDateFormat, []],
tooltipDateFont: [settings.tooltipDateFont, []],
tooltipDateColor: [settings.tooltipDateColor, []],
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []],
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []],
});
this.levelCardWidgetSettingsForm.get('selectedShape').valueChanges.subscribe((shape) => {
this.cd.detectChanges();
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges();
});
}
protected validatorTriggers(): string[] {
return [
'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel',
'tankSelectionType', 'datasourceUnits',
'showTooltipDate', 'units',
'layout', 'volumeSource',
'widgetUnitsSource'
];
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
const emitEventFields = [];
const datasourceUnits: string = this.levelCardWidgetSettingsForm.get('datasourceUnits').value;
const layout: LevelCardLayout = this.levelCardWidgetSettingsForm.get('layout').value;
const volumeSource: AbstractControl<LevelSelectOptions> = this.levelCardWidgetSettingsForm.get('volumeSource');
const widgetUnits: AbstractControl<string> = this.levelCardWidgetSettingsForm.get('units');
const tooltipUnits: AbstractControl<string> = this.levelCardWidgetSettingsForm.get('tooltipUnits');
const widgetUnitsSource: AbstractControl<LevelSelectOptions> = this.levelCardWidgetSettingsForm.get('widgetUnitsSource');
const showTooltipLevel: AbstractControl<boolean> = this.levelCardWidgetSettingsForm.get('showTooltipLevel');
const showTooltipDate: AbstractControl<boolean> = this.levelCardWidgetSettingsForm.get('showTooltipDate');
const showTooltip: boolean = this.levelCardWidgetSettingsForm.get('showTooltip').value;
if (trigger === 'tankSelectionType') {
const tankSelectionType: LevelSelectOptions = this.levelCardWidgetSettingsForm.get('tankSelectionType').value;
if (tankSelectionType === LevelSelectOptions.static) {
this.levelCardWidgetSettingsForm.get('selectedShape').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('shapeAttributeName').disable({emitEvent: false});
} else {
this.levelCardWidgetSettingsForm.get('selectedShape').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('shapeAttributeName').enable({emitEvent: false});
}
emitEventFields.push('selectedShape', 'shapeAttributeName');
}
if (trigger === 'datasourceUnits' || trigger === 'layout' || trigger === 'units') {
if (datasourceUnits === CapacityUnits.percent && (layout !== LevelCardLayout.absolute)
|| (datasourceUnits === CapacityUnits.percent && widgetUnits?.value === CapacityUnits.percent)
) {
volumeSource.disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeConstant').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeAttributeName').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeUnits').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeFont').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeColor').disable({emitEvent: false});
} else {
volumeSource.enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeConstant').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeAttributeName').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeUnits').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeFont').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeColor').enable({emitEvent: false});
}
if (layout === LevelCardLayout.simple && datasourceUnits === CapacityUnits.percent) {
this.levelCardWidgetSettingsForm.get('valueFont').disable();
this.levelCardWidgetSettingsForm.get('valueColor').disable();
} else {
this.levelCardWidgetSettingsForm.get('valueFont').enable();
this.levelCardWidgetSettingsForm.get('valueColor').enable();
}
emitEventFields.push('volumeSource', 'volumeConstant', 'volumeAttributeName', 'volumeFont', 'volumeColor', 'volumeUnits');
} else if (trigger === 'volumeSource') {
if((datasourceUnits !== CapacityUnits.percent) ||
(layout === LevelCardLayout.absolute && widgetUnits?.value !== CapacityUnits.percent)) {
if (volumeSource.value === LevelSelectOptions.static) {
this.levelCardWidgetSettingsForm.get('volumeConstant').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeAttributeName').disable({emitEvent: false});
} else {
this.levelCardWidgetSettingsForm.get('volumeConstant').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('volumeAttributeName').enable({emitEvent: false});
}
emitEventFields.push('volumeConstant', 'volumeAttributeName');
}
}
if (trigger === 'showBackgroundOverlay') {
const showBackgroundOverlay: boolean = this.levelCardWidgetSettingsForm.get('showBackgroundOverlay').value;
if (showBackgroundOverlay) {
this.levelCardWidgetSettingsForm.get('backgroundOverlayColor').enable({emitEvent: false});
} else {
this.levelCardWidgetSettingsForm.get('backgroundOverlayColor').disable({emitEvent: false});
}
emitEventFields.push('backgroundOverlayColor');
}
if (trigger === 'showTooltip') {
if (showTooltip) {
showTooltipLevel.enable({emitEvent: false});
showTooltipDate.enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipBackgroundColor').enable();
this.levelCardWidgetSettingsForm.get('tooltipBackgroundBlur').enable();
} else {
showTooltipLevel.disable({emitEvent: false});
showTooltipDate.disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipBackgroundColor').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipBackgroundBlur').disable({emitEvent: false});
}
emitEventFields.push('showTooltipLevel', 'showTooltipDate', 'tooltipBackgroundColor', 'tooltipBackgroundBlur');
}
if (trigger === 'showTooltipLevel') {
if (showTooltipLevel?.value && !showTooltipLevel.disabled) {
this.levelCardWidgetSettingsForm.get('tooltipUnits').enable();
this.levelCardWidgetSettingsForm.get('tooltipLevelDecimals').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipLevelFont').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipLevelColor').enable({emitEvent: false});
} else {
this.levelCardWidgetSettingsForm.get('tooltipUnits').disable();
this.levelCardWidgetSettingsForm.get('tooltipLevelDecimals').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipLevelFont').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipLevelColor').disable({emitEvent: false});
}
emitEventFields.push('tooltipUnits', 'tooltipLevelDecimals', 'tooltipLevelFont', 'tooltipLevelColor');
}
if (trigger === 'showTooltipDate') {
if (showTooltipDate?.value && !showTooltipDate.disabled) {
this.levelCardWidgetSettingsForm.get('tooltipDateFormat').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipDateFont').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipDateColor').enable({emitEvent: false});
} else {
this.levelCardWidgetSettingsForm.get('tooltipDateFormat').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipDateFont').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('tooltipDateColor').disable({emitEvent: false});
}
emitEventFields.push('tooltipDateFormat', 'tooltipDateFont', 'tooltipDateColor');
}
if (trigger === 'layout' || trigger === 'datasourceUnits') {
if (layout === LevelCardLayout.simple) {
widgetUnits.disable({emitEvent: false});
tooltipUnits.disable({emitEvent: false});
widgetUnitsSource.setValue(LevelSelectOptions.static, {emitEvent: false});
widgetUnitsSource.disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('valueFont').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('valueColor').disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('widgetUnitsAttributeName').disable({emitEvent: false});
} else if (layout === LevelCardLayout.percentage) {
if (widgetUnits.value !== CapacityUnits.percent) {
widgetUnits.setValue(CapacityUnits.percent, {emitEvent: false});
widgetUnitsSource.setValue(LevelSelectOptions.static, {emitEvent: false});
}
widgetUnits.disable({emitEvent: false});
widgetUnitsSource.disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('widgetUnitsAttributeName').disable({emitEvent: false});
if (tooltipUnits.value !== CapacityUnits.percent) {
tooltipUnits.setValue(CapacityUnits.percent, {emitEvent: false});
}
tooltipUnits.disable({emitEvent: false});
} else {
widgetUnits.enable({emitEvent: false});
tooltipUnits.enable({emitEvent: false});
widgetUnitsSource.enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('valueFont').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('valueColor').enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('widgetUnitsAttributeName').enable({emitEvent: false});
}
emitEventFields.push('units', 'tooltipUnits', 'valueFont', 'valueColor', 'widgetUnitsSource', 'widgetUnitsAttributeName');
} else if (trigger === 'widgetUnitsSource') {
if (layout !== LevelCardLayout.percentage) {
if (widgetUnitsSource.value === LevelSelectOptions.static) {
widgetUnits.enable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('widgetUnitsAttributeName').disable({emitEvent: false});
} else {
widgetUnits.disable({emitEvent: false});
this.levelCardWidgetSettingsForm.get('widgetUnitsAttributeName').enable({emitEvent: false});
}
emitEventFields.push('units', 'widgetUnitsAttributeName');
}
}
for (const controlKey in this.levelCardWidgetSettingsForm.controls) {
if (emitEventFields.includes(controlKey)) {
this.levelCardWidgetSettingsForm.controls[controlKey].updateValueAndValidity({emitEvent});
}
}
}
private createSvgShapesMapping(): void {
const obsArray: Array<Observable<{svg: string; shape: Shapes}>> = [];
for (const shape of this.shapes) {
const svgUrl = svgMapping.get(shape).svg;
const obs = this.resourcesService.loadJsonResource<string>(svgUrl).pipe(
map((svg) => ({svg, shape}))
);
obsArray.push(obs);
}
forkJoin(obsArray).subscribe((svgData) => {
for (const data of svgData) {
this.shapesImageMap.set(data.shape, data.svg);
}
this.cd.detectChanges();
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges();
this.shapesImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges();
});
}
public createShapeLayout(svg: string, layout: LevelCardLayout): SafeUrl {
if (svg && layout) {
const parser = new DOMParser();
const svgImage = parser.parseFromString(svg, 'image/svg+xml');
if (layout === this.levelCardLayouts.simple) {
svgImage.querySelector('.container-overlay').remove();
} else if (layout === this.levelCardLayouts.percentage) {
svgImage.querySelector('.absolute-overlay').remove();
svgImage.querySelector('.percentage-value-container').innerHTML = createPercentLayout();
} else {
svgImage.querySelector('.absolute-value-container').innerHTML = createAbsoluteLayout();
svgImage.querySelector('.percentage-overlay').remove();
}
const encodedSvg = encodeURIComponent(svgImage.documentElement.outerHTML);
return this.sanitizer.bypassSecurityTrustResourceUrl(`data:image/svg+xml,${encodedSvg}`);
}
}
public isRequired(formControlName: string): boolean {
return this.levelCardWidgetSettingsForm.get(formControlName)?.hasValidator(Validators.required);
}
private _valuePreviewFn(): string {
const units: string = this.widgetConfig.config.units;
const decimals: number = this.widgetConfig.config.decimals;
return formatValue(32, decimals, units, true);
}
private _tooltipValuePreviewFn() {
const units: string = this.levelCardWidgetSettingsForm.get('tooltipUnits').value;
const decimals: number = this.levelCardWidgetSettingsForm.get('tooltipLevelDecimals').value;
return formatValue(32, decimals, units, true);
}
private _totalVolumeValuePreviewFn() {
const value = this.levelCardWidgetSettingsForm.get('volumeConstant').value;
const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value;
const decimals: number = this.widgetConfig.config.decimals;
let units: string = this.widgetConfig.config.units;
if (datasourceUnits !== CapacityUnits.percent) {
units = datasourceUnits;
}
return formatValue((isDefined(value) ? value : 500), decimals, units, true);
}
private _datePreviewFn(): string {
const dateFormat: DateFormatSettings = this.levelCardWidgetSettingsForm.get('tooltipDateFormat').value;
const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat);
processor.update(Date.now());
return processor.formatted;
}
public fetchOptions(searchText: string): Observable<Array<string>> {
if (this.keySearchText !== searchText) {
this.keySearchText = searchText;
const dataKeyFilter = optionsFilter(this.keySearchText);
return this.getKeys().pipe(
tap(res => this.lastFetchedKeys !== res ? this.lastFetchedKeys = res : []),
map(name => name?.filter(dataKeyFilter).map(key => key.name)),
tap(res => this.latestKeySearchTextResult = res)
);
}
return of(this.latestKeySearchTextResult);
}
private getKeys(): Observable<Array<DataKey>> {
let fetchObservable: Observable<Array<DataKey>>;
if (this.datasource?.type === DatasourceType.function) {
fetchObservable = of(this.functionTypeKeys);
} else if (this.datasource?.type === DatasourceType.entity && this.datasource?.entityAliasId ||
this.datasource?.type === DatasourceType.device && this.datasource?.deviceId) {
if (this.datasource?.type === DatasourceType.device) {
if (this.lastKeysId !== this.datasource?.deviceId || !this.lastFetchedKeys) {
this.lastKeysId = this.datasource.deviceId;
fetchObservable = fetchEntityKeysForDevice(this.datasource?.deviceId, [DataKeyType.attribute],
this.entityService);
} else {
fetchObservable = of(this.lastFetchedKeys);
}
} else {
if (this.lastKeysId !== this.datasource?.entityAliasId || !this.lastFetchedKeys) {
this.lastKeysId = this.datasource.entityAliasId;
fetchObservable = fetchEntityKeys(this.datasource?.entityAliasId, [DataKeyType.attribute],
this.entityService, this.aliasController);
} else {
fetchObservable = of(this.lastFetchedKeys);
}
}
} else {
fetchObservable = of([]);
}
return fetchObservable.pipe(
publishReplay(1),
refCount()
);
}
}

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.html

@ -33,7 +33,7 @@
<div class="tb-image-cards-option-background"></div>
<div class="tb-image-cards-option-title">{{ option.name }}</div>
<div class="tb-image-cards-option-image-container">
<img class="tb-image-cards-option-image" src="{{ option.image }}"/>
<img class="tb-image-cards-option-image" [src]="option.image"/>
</div>
</div>
</mat-grid-tile>

12
ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts

@ -300,6 +300,9 @@ import {
import {
ProgressBarWidgetSettingsComponent
} from '@home/components/widget/lib/settings/cards/progress-bar-widget-settings.component';
import {
LiquidLevelCardWidgetSettingsComponent
} from '@home/components/widget/lib/settings/cards/liquid-level-card-widget-settings.component';
@NgModule({
declarations: [
@ -410,7 +413,8 @@ import {
WindSpeedDirectionWidgetSettingsComponent,
SignalStrengthWidgetSettingsComponent,
ValueChartCardWidgetSettingsComponent,
ProgressBarWidgetSettingsComponent
ProgressBarWidgetSettingsComponent,
LiquidLevelCardWidgetSettingsComponent
],
imports: [
CommonModule,
@ -526,7 +530,8 @@ import {
WindSpeedDirectionWidgetSettingsComponent,
SignalStrengthWidgetSettingsComponent,
ValueChartCardWidgetSettingsComponent,
ProgressBarWidgetSettingsComponent
ProgressBarWidgetSettingsComponent,
LiquidLevelCardWidgetSettingsComponent,
]
})
export class WidgetSettingsModule {
@ -607,5 +612,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type<IWidgetSettingsCo
'tb-wind-speed-direction-widget-settings': WindSpeedDirectionWidgetSettingsComponent,
'tb-signal-strength-widget-settings': SignalStrengthWidgetSettingsComponent,
'tb-value-chart-card-widget-settings': ValueChartCardWidgetSettingsComponent,
'tb-progress-bar-widget-settings': ProgressBarWidgetSettingsComponent
'tb-progress-bar-widget-settings': ProgressBarWidgetSettingsComponent,
'tb-liquid-level-card-widget-settings': LiquidLevelCardWidgetSettingsComponent,
};

7
ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts

@ -64,6 +64,7 @@ import {
import { SignalStrengthWidgetComponent } from '@home/components/widget/lib/indicator/signal-strength-widget.component';
import { ValueChartCardWidgetComponent } from '@home/components/widget/lib/cards/value-chart-card-widget.component';
import { ProgressBarWidgetComponent } from '@home/components/widget/lib/cards/progress-bar-widget.component';
import { LiquidLevelWidgetComponent } from '@home/components/widget/lib/indicator/liquid-level-widget';
@NgModule({
declarations:
@ -102,7 +103,8 @@ import { ProgressBarWidgetComponent } from '@home/components/widget/lib/cards/pr
WindSpeedDirectionWidgetComponent,
SignalStrengthWidgetComponent,
ValueChartCardWidgetComponent,
ProgressBarWidgetComponent
ProgressBarWidgetComponent,
LiquidLevelWidgetComponent
],
imports: [
CommonModule,
@ -145,7 +147,8 @@ import { ProgressBarWidgetComponent } from '@home/components/widget/lib/cards/pr
WindSpeedDirectionWidgetComponent,
SignalStrengthWidgetComponent,
ValueChartCardWidgetComponent,
ProgressBarWidgetComponent
ProgressBarWidgetComponent,
LiquidLevelWidgetComponent
],
providers: [
{provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule }

51
ui-ngx/src/app/shared/components/string-autocomplete.component.html

@ -0,0 +1,51 @@
<!--
Copyright © 2016-2023 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<mat-form-field [appearance]="appearance" [ngClass]="ngClass"
[subscriptSizing]="subscriptSizing" style="width: 100%">
<mat-label *ngIf="label">{{label}}</mat-label>
<input matInput #nameInput [formControl]="selectionFormControl"
[placeholder]="placeholderText"
(focusin)="onFocus()"
[matAutocomplete]="optionsAutocomplete">
<button *ngIf="selectionFormControl.value && !disabled"
type="button"
class="tb-icon-24"
matSuffix mat-icon-button aria-label="Clear"
(click)="clear()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<mat-icon *ngIf="selectionFormControl.hasError('required') && !showInlineError"
matIconSuffix
[matTooltip]="errorText"
matTooltipPosition="above"
[matTooltipClass]="tooltipClass"
class="material-icons tb-suffix-show-always tb-error">
warning
</mat-icon>
<mat-error *ngIf="selectionFormControl.hasError('required') && showInlineError">
{{errorText}}
</mat-error>
<mat-autocomplete
#optionsAutocomplete="matAutocomplete"
class="tb-autocomplete tb-options-input-autocomplete"
panelWidth="fit-content">
<mat-option *ngFor="let option of filteredOptions$ | async" [value]="option">
<span class="tb-option-name" fxFlex [innerHTML]="option | highlight:searchText:true:'ig'"></span>
</mat-option>
</mat-autocomplete>
</mat-form-field>

43
ui-ngx/src/app/shared/components/string-autocomplete.component.scss

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
mat-form-field {
display: flex;
flex: 1 1 0%;
max-width: 100%;
box-sizing: border-box;
.tb-autocomplete.tb-option-input-autocomplete {
.mat-mdc-option {
border-bottom: none;
.mdc-list-item__primary-text {
flex: 1;
display: flex;
flex-direction: row;
gap: 8px;
.tb-option {
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0.2px;
}
}
}
}
}
}

178
ui-ngx/src/app/shared/components/string-autocomplete.component.ts

@ -0,0 +1,178 @@
///
/// Copyright © 2016-2023 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
Component,
Input,
forwardRef,
OnInit,
ViewChild,
ElementRef
} from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
FormControl,
Validators,
FormBuilder
} from '@angular/forms';
import { Observable, of } from 'rxjs';
import { tap, map, switchMap, take } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
selector: 'tb-string-autocomplete',
templateUrl: './string-autocomplete.component.html',
styleUrls: ['./string-autocomplete.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => StringAutocompleteComponent),
multi: true
}
]
})
export class StringAutocompleteComponent implements ControlValueAccessor, OnInit {
@Input()
disabled: boolean;
@coerceBoolean()
@Input()
required: boolean = false;
@Input() fetchOptionsFn: (searchText?: string) => Observable<Array<string>>;
@ViewChild('nameInput', {static: true}) nameInput: ElementRef;
@Input()
placeholderText: string = this.translate.instant('widget-config.set');
@Input()
subscriptSizing: string = 'dynamic';
@Input()
ngClass: string | string[] | Set<string> | { [klass: string]: any; } = 'tb-inline-field tb-suffix-show-on-hover';
@Input()
appearance: string = 'outline';
@Input()
label: string;
@Input()
tooltipClass: string = 'tb-error-tooltip';
@Input()
errorText: string;
@coerceBoolean()
@Input()
showInlineError: boolean = false;
selectionFormControl: FormControl;
modelValue: string | null;
filteredOptions$: Observable<Array<string>>;
searchText = '';
private dirty = false;
private propagateChange = (_val: any) => {};
constructor(private fb: FormBuilder,
private translate: TranslateService) {
}
ngOnInit() {
this.selectionFormControl = this.fb.control('', this.required ? [Validators.required] : []);
this.filteredOptions$ = this.selectionFormControl.valueChanges
.pipe(
tap(value => this.updateView(value)),
map(value => value ? value : ''),
switchMap(value => this.fetchOptionsFn ? this.fetchOptionsFn(value) : of([]))
);
}
writeValue(option?: string): void {
this.searchText = '';
this.modelValue = option ? option : null;
if (this.fetchOptionsFn) {
this.fetchOptionsFn(option)
.pipe(
map(options => {
if (options) {
const foundOption = options.find(opt => opt === option);
return foundOption ? foundOption : option;
}
return option;
}),
take(1)
)
.subscribe(result => {
this.selectionFormControl.patchValue(result, { emitEvent: false });
this.dirty = true;
});
} else {
this.selectionFormControl.patchValue(null, { emitEvent: false });
this.dirty = true;
}
}
onFocus() {
if (this.dirty) {
this.selectionFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true});
this.dirty = false;
}
}
updateView(value: string) {
this.searchText = value ? value : '';
if (this.modelValue !== value) {
this.modelValue = value;
this.propagateChange(this.modelValue);
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.selectionFormControl.disable({emitEvent: false});
} else {
this.selectionFormControl.enable({emitEvent: false});
}
}
clear() {
this.selectionFormControl.patchValue(null, {emitEvent: true});
setTimeout(() => {
this.nameInput.nativeElement.blur();
this.nameInput.nativeElement.focus();
}, 0);
}
}

9
ui-ngx/src/app/shared/components/unit-input.component.html

@ -20,13 +20,20 @@
placeholder="{{ 'widget-config.set' | translate }}"
(focusin)="onFocus()"
[matAutocomplete]="unitsAutocomplete">
<button *ngIf="unitsFormControl.value && !disabled"
<button *ngIf="unitsFormControl.value && !disabled && unitsFormControl.valid"
type="button"
class="tb-icon-24"
matSuffix mat-icon-button aria-label="Clear"
(click)="clear()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="'widget-config.units-required' | translate"
*ngIf="unitsFormControl.hasError('required')" class="material-icons tb-suffix-show-always tb-error">
warning
</mat-icon>
<mat-autocomplete
#unitsAutocomplete="matAutocomplete"
class="tb-autocomplete tb-unit-input-autocomplete"

35
ui-ngx/src/app/shared/components/unit-input.component.ts

@ -24,12 +24,19 @@ import {
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
ControlValueAccessor,
FormBuilder,
FormControl,
NG_VALUE_ACCESSOR,
Validators
} from '@angular/forms';
import { Observable, of, shareReplay, switchMap } from 'rxjs';
import { getUnits, searchUnits, Unit, unitBySymbol } from '@shared/models/unit.models';
import { getUnits, searchUnits, Unit, unitBySymbol, UnitsType } from '@shared/models/unit.models';
import { map, mergeMap, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { ResourcesService } from '@core/services/resources.service';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
selector: 'tb-unit-input',
@ -55,6 +62,13 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit {
@Input()
disabled: boolean;
@coerceBoolean()
@Input()
required: boolean = false;
@Input()
tagFilter: UnitsType;
@ViewChild('unitInput', {static: true}) unitInput: ElementRef;
filteredUnits: Observable<Array<Unit | string>>;
@ -73,7 +87,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit {
}
ngOnInit() {
this.unitsFormControl = this.fb.control('', []);
this.unitsFormControl = this.fb.control('', this.required ? [Validators.required] : []);
this.filteredUnits = this.unitsFormControl.valueChanges
.pipe(
tap(value => {
@ -157,11 +171,16 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit {
private unitsConstant(): Observable<Array<Unit>> {
if (this.fetchUnits$ === null) {
this.fetchUnits$ = getUnits(this.resourcesService).pipe(
map(units => units.map(u => ({
symbol: u.symbol,
name: this.translate.instant(u.name),
tags: u.tags
}))),
map((units) => {
if (this.tagFilter) {
units = units.filter(u => u.tags.includes(this.tagFilter));
}
return units.map(u => ({
symbol: u.symbol,
name: this.translate.instant(u.name),
tags: u.tags
}));
}),
shareReplay(1)
);
}

9
ui-ngx/src/app/shared/models/unit.models.ts

@ -23,6 +23,15 @@ export interface Unit {
tags: string[];
}
export enum UnitsType {
capacity = 'capacity'
}
export enum Units {
percent = '%',
liters = 'L'
}
export const unitBySymbol = (_units: Array<Unit>, symbol: string): Unit => _units.find(u => u.symbol === symbol);
const searchUnitTags = (unit: Unit, searchText: string): boolean =>

27
ui-ngx/src/app/shared/models/widget-settings.models.ts

@ -401,6 +401,33 @@ export const textStyle = (font?: Font, letterSpacing = 'normal'): ComponentStyle
return style;
};
export const inlineTextStyle = (font?: Font, letterSpacing = 'normal'): ComponentStyle => {
const style: ComponentStyle = {
letterSpacing
};
if (font?.style) {
style['font-style'] = font.style;
}
if (font?.weight) {
style['font-weight'] = font.weight;
}
if (font?.lineHeight) {
style['line-height'] = font.lineHeight;
}
if (font?.size) {
style['font-size'] = (font.size + (font.sizeUnit || 'px'));
}
if (font?.family) {
style['font-family'] = font.family +
(font.family !== 'Roboto' ? ', Roboto' : '');
}
return style;
};
export const cssTextFromInlineStyle = (styleObj: { [key: string]: string | number }): string => Object.entries(styleObj)
.map(([key, value]) => `${key}: ${value}`)
.join('; ');
export const isFontSet = (font: Font): boolean => (!!font && !!font.style && !!font.weight && !!font.size && !!font.family);
export const isFontPartiallySet = (font: Font): boolean => (!!font && (!!font.style || !!font.weight || !!font.size || !!font.family));

3
ui-ngx/src/app/shared/shared.module.ts

@ -199,6 +199,7 @@ import { MaterialIconsComponent } from '@shared/components/material-icons.compon
import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component';
import { TbIconComponent } from '@shared/components/icon.component';
import { HintTooltipIconComponent } from '@shared/components/hint-tooltip-icon.component';
import { StringAutocompleteComponent } from '@shared/components/string-autocomplete.component';
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
return markedOptionsService;
@ -374,6 +375,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
ToggleOption,
ToggleSelectComponent,
UnitInputComponent,
StringAutocompleteComponent,
MaterialIconsComponent,
RuleChainSelectComponent,
TbIconComponent,
@ -609,6 +611,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
ToggleOption,
ToggleSelectComponent,
UnitInputComponent,
StringAutocompleteComponent,
MaterialIconsComponent,
RuleChainSelectComponent,
TbIconComponent,

69
ui-ngx/src/assets/help/en_US/widget/lib/indicator/shape_attribute_fn.md

@ -0,0 +1,69 @@
#### Shape attribute name
A string expression value that allows you dynamically select shape image depending on attribute name.
**Attribute values that could be used to add the image:**
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/vertical-oval.svg" alt="Alt text" />
```
Vertical Oval
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/vertical-cylinder.svg" alt="Alt text" />
```
Vertical Cylinder
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/vertical-capsule.svg" alt="Alt text" />
```
Vertical Capsule
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/rectangle.svg" alt="Alt text" />
```
Rectangle
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-oval.svg" alt="Alt text" />
```
Horizontal Oval
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-ellipse.svg" alt="Alt text" />
```
Horizontal Ellipse
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-dish-ends.svg" alt="Alt text" />
```
Horizontal Dish Ends
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-cylinder.svg" alt="Alt text" />
```
Horizontal Cylinder
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-capsule.svg" alt="Alt text" />
```
Horizontal Capsule
{:copy-code}
```
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg" alt="Alt text" />
```
Horizontal 2:1 Elliptical
{:copy-code}
```
<br>
<br>

57
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -4229,20 +4229,20 @@
"carat": "Carat",
"cubic-millimeter": "Cubic Millimeter",
"cubic-centimeter": "Cubic Centimeter",
"cubic-meter": "Cubic Meter/s",
"cubic-kilometer": "Cubic Kilometers",
"cubic-meter": "Cubic Meter",
"cubic-kilometer": "Cubic Kilometer",
"microliter": "Microliter",
"milliliter": "Milliliter",
"liter": "Liter",
"hectoliter": "Hectolitre",
"cubic-inch": "Cubic Inch",
"cubic-foot": "Cubic Foot",
"cubic-yard": "Cubic Yards",
"cubic-yard": "Cubic Yard",
"fluid-ounce": "Fluid Ounce",
"pint": "Pint",
"quart": "Quart",
"gallon": "Gallon",
"oil-barrels": "Oil Barrels",
"oil-barrels": "Oil Barrel",
"cubic-meter-per-kilogram": "Cubic Meter per Kilogram",
"gill": "Gill",
"hogshead": "Hogshead",
@ -4286,7 +4286,6 @@
"ton-force-per-square-inch": "Ton-force per Square Inch",
"kilonewton-per-square-meter": "Kilonewton per Square Meter",
"newton-per-square-millimeter": "Newton per Square Millimeter",
"microjoule": "Microjoule",
"millijoule": "Millijoule",
"joule": "Joule",
@ -5029,7 +5028,9 @@
"show-card-buttons": "Show card buttons",
"card-border-radius": "Card border radius",
"card-appearance": "Card appearance",
"color": "Color"
"color": "Color",
"tooltip": "Tooltip",
"units-required": "Unit is required."
},
"widget-type": {
"import": "Import widget type",
@ -6065,6 +6066,50 @@
"value-card-style": "Value card style",
"auto-scale": "Auto scale"
},
"liquid-level-card": {
"layout-simple": "Simple",
"layout-percentage": "Percentage",
"layout-absolute": "Absolute",
"layout": "Layout",
"background-overlay": "Value background overlay",
"total-volume": "Total volume",
"tank": "Tank",
"shape": "Shape",
"datasource-units": "Source units",
"widget-units": "Widget units",
"decimals": "Decimals",
"liquid": "Liquid",
"liquid-color": "Liquid color",
"value": "Value",
"value-font": "Value font",
"level": "Level",
"last-update": "Last update",
"shape-by-attribute": "Set tank shape by attribute name",
"tooltip-background": "Background color",
"background-blur": "Background blur",
"tank-color": "Tank color",
"static": "Static",
"see-examples": "See examples",
"attribute": "Attribute",
"shape-type": "Type",
"v-oval": "Vertical Oval",
"v-cylinder": "Vertical Cylinder",
"v-capsule": "Vertical Capsule",
"rectangle": "Rectangle",
"h-oval": "Horizontal Oval",
"h-ellipse": "Horizontal Ellipse",
"h-dish-ends": "Horizontal Dish Ends",
"h-cylinder": "Horizontal Cylinder",
"h-capsule": "Horizontal Capsule",
"h-elliptical_2_1": "Horizontal 2:1 Elliptical",
"icon": "Card icon",
"title": "Card title",
"units": "Units",
"color-and-font": "Color and font",
"shape-attribute-name": "Attribute name",
"total-volume-required": "Total volume is required.",
"attribute-name-required": "Attribute name is required."
},
"aggregated-value-card": {
"subtitle": "Subtitle",
"chart": "Chart",

4
ui-ngx/src/assets/metadata/units.json

@ -249,7 +249,7 @@
},
{
"name": "unit.liter",
"symbol": "l",
"symbol": "L",
"tags": ["volume","capacity","extent","liter","liters","l"]
},
{
@ -1113,7 +1113,7 @@
"symbol": "%",
"tags": ["power source","state of charge (SoC)","battery","battery level","level","humidity","moisture","percentage",
"relative humidity","water content","soil moisture","irrigation","water in soil","soil water content","VWC",
"Volumetric Water Content","Total Harmonic Distortion","THD","power quality","UV Transmittance","%"]
"Volumetric Water Content","Total Harmonic Distortion","THD","power quality","UV Transmittance","%", "capacity"]
},
{
"name": "unit.rssi",

50
ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg

@ -0,0 +1,50 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.5 175.5C18.5 175.5 1.5 156.5 1.5 111.5C1.5 66.5 19.5 47.5 27.4999 47.5H205.5C213.5 47.5 231.5 66.5 231.5 111.5C231.5 156.5 214.5 175.5 205.5 175.5H27.5Z" fill="#6216FF" fill-opacity="0.02"/>
<path class="tb-shape-fill" d="M0.5 111.5C0.5 134.119 4.77161 150.285 10.2371 160.835C12.969 166.108 16.01 169.998 18.9848 172.583C21.9415 175.152 24.9166 176.5 27.5 176.5V174.5C25.5834 174.5 23.0585 173.473 20.2965 171.073C17.5525 168.689 14.656 165.017 12.0129 159.915C6.72839 149.715 2.5 133.881 2.5 111.5H0.5ZM27.4999 46.5C25.1452 46.5 22.2907 47.8693 19.4004 50.4224C16.4803 53.0019 13.4107 56.8857 10.6165 62.1566C5.02539 72.7034 0.5 88.871 0.5 111.5H2.5C2.5 89.129 6.97461 73.2966 12.3835 63.0934C15.0893 57.9893 18.0197 54.3106 20.7245 51.9213C23.4592 49.5057 25.8546 48.5 27.4999 48.5V46.5ZM205.5 46.5H27.4999V48.5H205.5V46.5ZM232.5 111.5C232.5 88.871 227.975 72.7034 222.384 62.1566C219.589 56.8857 216.52 53.0019 213.6 50.4224C210.709 47.8693 207.855 46.5 205.5 46.5V48.5C207.145 48.5 209.541 49.5057 212.275 51.9213C214.98 54.3106 217.911 57.9893 220.616 63.0934C226.025 73.2966 230.5 89.129 230.5 111.5H232.5ZM205.5 176.5C208.083 176.5 211.058 175.152 214.015 172.583C216.99 169.998 220.031 166.108 222.763 160.835C228.228 150.285 232.5 134.119 232.5 111.5H230.5C230.5 133.881 226.272 149.715 220.987 159.915C218.344 165.017 215.447 168.689 212.704 171.073C209.942 173.473 207.417 174.5 205.5 174.5V176.5ZM27.5 176.5H205.5V174.5H27.5V176.5Z" fill="#242770"/>
<mask id="mask0_3572_11071" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="4" y="50" width="225" height="123">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.5 172.5C21.5 172.5 4.5 154.391 4.5 111.5C4.5 68.6094 21.5 50.5 27.4999 50.5H205.5C211.5 50.5 228.5 68.6094 228.5 111.5C228.5 154.391 211.5 172.5 205.5 172.5H27.5Z" fill="#F1F1F9"/>
</mask>
<g mask="url(#mask0_3572_11071)">
<rect class="tb-liquid-fill tb-liquid" x="4.5" y="118.5" width="224" height="122" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="4.5" y="118.5" width="224" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="4.5" y="118.5" width="224" height="1" fill="white" fill-opacity="0.1"/>
</g>
<path class="tb-shape-stroke" d="M48.5 47.5C41.7084 69.7745 32.2001 126.559 48.5 175.5" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M184.5 47.5C177.708 69.7745 168.2 126.559 184.5 175.5" stroke="#242770" stroke-width="2"/>
<mask id="path-8-inside-1_3572_11071" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M131.5 30.5H102.5V48.5C102.5 50.1569 108.992 51.5 117 51.5C125.008 51.5 131.5 50.1569 131.5 48.5V30.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M131.5 30.5H102.5V48.5C102.5 50.1569 108.992 51.5 117 51.5C125.008 51.5 131.5 50.1569 131.5 48.5V30.5Z"/>
<path class="tb-shape-fill" d="M102.5 30.5V28.5H100.5V30.5H102.5ZM131.5 30.5H133.5V28.5H131.5V30.5ZM102.5 32.5H131.5V28.5H102.5V32.5ZM104.5 48.5V30.5H100.5V48.5H104.5ZM117 49.5C113.09 49.5 109.607 49.1707 107.152 48.6628C105.908 48.4055 105.034 48.1232 104.518 47.8712C104.257 47.7431 104.191 47.6721 104.217 47.6982C104.248 47.7304 104.5 48.0048 104.5 48.5H100.5C100.5 49.4095 100.954 50.0882 101.373 50.511C101.785 50.9277 102.289 51.2334 102.761 51.4643C103.713 51.9301 104.962 52.2943 106.342 52.5798C109.135 53.1577 112.902 53.5 117 53.5V49.5ZM129.5 48.5C129.5 48.0048 129.752 47.7304 129.783 47.6982C129.809 47.6721 129.743 47.7431 129.482 47.8712C128.966 48.1232 128.092 48.4055 126.848 48.6628C124.393 49.1707 120.91 49.5 117 49.5V53.5C121.098 53.5 124.865 53.1577 127.658 52.5798C129.038 52.2943 130.287 51.9301 131.239 51.4643C131.711 51.2334 132.215 50.9277 132.627 50.511C133.046 50.0882 133.5 49.4095 133.5 48.5H129.5ZM129.5 30.5V48.5H133.5V30.5H129.5Z" fill="#242770" mask="url(#path-8-inside-1_3572_11071)"/>
<path class="tb-shape-stroke" d="M131.541 31.3323C131.541 31.3323 131.54 31.3332 131.539 31.3349C131.54 31.3332 131.541 31.3323 131.541 31.3323ZM130.866 31.2416C131.077 31.338 131.231 31.4258 131.341 31.5C131.231 31.5742 131.077 31.662 130.866 31.7584C130.187 32.0692 129.141 32.3742 127.77 32.6395C125.045 33.167 121.236 33.5 117 33.5C112.764 33.5 108.955 33.167 106.23 32.6395C104.859 32.3742 103.813 32.0692 103.134 31.7584C102.923 31.662 102.769 31.5742 102.659 31.5C102.769 31.4258 102.923 31.338 103.134 31.2416C103.813 30.9308 104.859 30.6258 106.23 30.3605C108.955 29.833 112.764 29.5 117 29.5C121.236 29.5 125.045 29.833 127.77 30.3605C129.141 30.6258 130.187 30.9308 130.866 31.2416ZM102.459 31.3323C102.46 31.3323 102.46 31.3332 102.461 31.3349C102.46 31.3332 102.459 31.3323 102.459 31.3323ZM102.459 31.6677C102.459 31.6677 102.46 31.6668 102.461 31.6651C102.46 31.6668 102.46 31.6677 102.459 31.6677ZM131.539 31.6651C131.54 31.6668 131.541 31.6677 131.541 31.6677C131.541 31.6677 131.54 31.6668 131.539 31.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M131.541 28.3323C131.541 28.3323 131.54 28.3332 131.539 28.3349C131.54 28.3332 131.541 28.3323 131.541 28.3323ZM130.866 28.2416C131.077 28.338 131.231 28.4258 131.341 28.5C131.231 28.5742 131.077 28.662 130.866 28.7584C130.187 29.0692 129.141 29.3742 127.77 29.6395C125.045 30.167 121.236 30.5 117 30.5C112.764 30.5 108.955 30.167 106.23 29.6395C104.859 29.3742 103.813 29.0692 103.134 28.7584C102.923 28.662 102.769 28.5742 102.659 28.5C102.769 28.4258 102.923 28.338 103.134 28.2416C103.813 27.9308 104.859 27.6258 106.23 27.3605C108.955 26.833 112.764 26.5 117 26.5C121.236 26.5 125.045 26.833 127.77 27.3605C129.141 27.6258 130.187 27.9308 130.866 28.2416ZM102.459 28.3323C102.46 28.3323 102.46 28.3332 102.461 28.3349C102.46 28.3332 102.459 28.3323 102.459 28.3323ZM102.459 28.6677C102.459 28.6677 102.46 28.6668 102.461 28.6651C102.46 28.6668 102.46 28.6677 102.459 28.6677ZM131.539 28.6651C131.54 28.6668 131.541 28.6677 131.541 28.6677C131.541 28.6677 131.54 28.6668 131.539 28.6651Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip0_3572_11071)">
<g filter="url(#filter0_b_3572_11071)">
<rect class="absolute-overlay" x="68" y="80" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="68" y="80" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_11071)">
<rect class="percentage-overlay" x="72.5" y="90.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="72.5" y="90.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_11071" x="62" y="74" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_11071"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_11071" result="shape"/>
</filter>
<filter id="filter1_b_3572_11071" x="66.5" y="84.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_11071"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_11071" result="shape"/>
</filter>
<clipPath id="clip0_3572_11071">
<rect width="81" height="61" fill="white" transform="translate(68 80)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

49
ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-capsule.svg

@ -0,0 +1,49 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect class="tb-shape-stroke" x="11.5" y="47.5" width="210" height="126" rx="63" fill="#6216FF" fill-opacity="0.02" stroke="#242770" stroke-width="2"/>
<mask id="mask0_3572_1208" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="14" y="50" width="205" height="121">
<rect class="tb-shape-stroke" x="15.5" y="51.5" width="202" height="118" rx="59" fill="#F1F1F9" stroke="#242770" stroke-width="2"/>
</mask>
<g mask="url(#mask0_3572_1208)">
<rect class="tb-liquid-fill tb-liquid" x="14.5" y="118.5" width="205" height="120" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="14.5" y="118.5" width="205" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="14.5" y="118.5" width="205" height="1" fill="white" fill-opacity="0.1"/>
</g>
<path class="tb-shape-stroke" d="M72.5 47.5C60.426 66.5757 43.5225 118.482 72.5 173.5" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M160.5 47.5C148.426 66.5757 131.522 118.482 160.5 173.5" stroke="#242770" stroke-width="2"/>
<mask id="path-7-inside-1_3572_1208" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M128.5 30.5H99.5V48.5C99.5 50.1569 105.992 51.5 114 51.5C122.008 51.5 128.5 50.1569 128.5 48.5V30.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M128.5 30.5H99.5V48.5C99.5 50.1569 105.992 51.5 114 51.5C122.008 51.5 128.5 50.1569 128.5 48.5V30.5Z"/>
<path class="tb-shape-fill" d="M99.5 30.5V28.5H97.5V30.5H99.5ZM128.5 30.5H130.5V28.5H128.5V30.5ZM99.5 32.5H128.5V28.5H99.5V32.5ZM101.5 48.5V30.5H97.5V48.5H101.5ZM114 49.5C110.09 49.5 106.607 49.1707 104.152 48.6628C102.908 48.4055 102.034 48.1232 101.518 47.8712C101.257 47.7431 101.191 47.6721 101.217 47.6982C101.248 47.7304 101.5 48.0048 101.5 48.5H97.5C97.5 49.4095 97.9544 50.0882 98.3726 50.511C98.7847 50.9277 99.2887 51.2334 99.7607 51.4643C100.713 51.9301 101.962 52.2943 103.342 52.5798C106.135 53.1577 109.902 53.5 114 53.5V49.5ZM126.5 48.5C126.5 48.0048 126.752 47.7304 126.783 47.6982C126.809 47.6721 126.743 47.7431 126.482 47.8712C125.966 48.1232 125.092 48.4055 123.848 48.6628C121.393 49.1707 117.91 49.5 114 49.5V53.5C118.098 53.5 121.865 53.1577 124.658 52.5798C126.038 52.2943 127.287 51.9301 128.239 51.4643C128.711 51.2334 129.215 50.9277 129.627 50.511C130.046 50.0882 130.5 49.4095 130.5 48.5H126.5ZM126.5 30.5V48.5H130.5V30.5H126.5Z" fill="#242770" mask="url(#path-7-inside-1_3572_1208)"/>
<path class="tb-shape-stroke" d="M128.541 31.3323C128.541 31.3323 128.54 31.3332 128.539 31.3349C128.54 31.3332 128.541 31.3323 128.541 31.3323ZM127.866 31.2416C128.077 31.338 128.231 31.4258 128.341 31.5C128.231 31.5742 128.077 31.662 127.866 31.7584C127.187 32.0692 126.141 32.3742 124.77 32.6395C122.045 33.167 118.236 33.5 114 33.5C109.764 33.5 105.955 33.167 103.23 32.6395C101.859 32.3742 100.813 32.0692 100.134 31.7584C99.9234 31.662 99.7686 31.5742 99.6586 31.5C99.7686 31.4258 99.9234 31.338 100.134 31.2416C100.813 30.9308 101.859 30.6258 103.23 30.3605C105.955 29.833 109.764 29.5 114 29.5C118.236 29.5 122.045 29.833 124.77 30.3605C126.141 30.6258 127.187 30.9308 127.866 31.2416ZM99.4595 31.3323C99.4595 31.3323 99.4602 31.3332 99.4613 31.3349C99.46 31.3332 99.4594 31.3323 99.4595 31.3323ZM99.4595 31.6677C99.4594 31.6677 99.46 31.6668 99.4613 31.6651C99.4602 31.6668 99.4595 31.6677 99.4595 31.6677ZM128.539 31.6651C128.54 31.6668 128.541 31.6677 128.541 31.6677C128.541 31.6677 128.54 31.6668 128.539 31.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M128.541 28.3323C128.541 28.3323 128.54 28.3332 128.539 28.3349C128.54 28.3332 128.541 28.3323 128.541 28.3323ZM127.866 28.2416C128.077 28.338 128.231 28.4258 128.341 28.5C128.231 28.5742 128.077 28.662 127.866 28.7584C127.187 29.0692 126.141 29.3742 124.77 29.6395C122.045 30.167 118.236 30.5 114 30.5C109.764 30.5 105.955 30.167 103.23 29.6395C101.859 29.3742 100.813 29.0692 100.134 28.7584C99.9234 28.662 99.7686 28.5742 99.6586 28.5C99.7686 28.4258 99.9234 28.338 100.134 28.2416C100.813 27.9308 101.859 27.6258 103.23 27.3605C105.955 26.833 109.764 26.5 114 26.5C118.236 26.5 122.045 26.833 124.77 27.3605C126.141 27.6258 127.187 27.9308 127.866 28.2416ZM99.4595 28.3323C99.4595 28.3323 99.4602 28.3332 99.4613 28.3349C99.46 28.3332 99.4594 28.3323 99.4595 28.3323ZM99.4595 28.6677C99.4594 28.6677 99.46 28.6668 99.4613 28.6651C99.4602 28.6668 99.4595 28.6677 99.4595 28.6677ZM128.539 28.6651C128.54 28.6668 128.541 28.6677 128.541 28.6677C128.541 28.6677 128.54 28.6668 128.539 28.6651Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip0_3572_1208)">
<g filter="url(#filter0_b_3572_1208)">
<rect class="absolute-overlay" x="62" y="80" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="62" y="80" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_1208)">
<rect class="percentage-overlay" x="66.5" y="90.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="66.5" y="90.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_1208" x="56" y="74" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_1208"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_1208" result="shape"/>
</filter>
<filter id="filter1_b_3572_1208" x="60.5" y="84.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_1208"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_1208" result="shape"/>
</filter>
<clipPath id="clip0_3572_1208">
<rect width="81" height="61" fill="white" transform="translate(62 80)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

56
ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-cylinder.svg

@ -0,0 +1,56 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="tb-shape-stroke" opacity="0.1" d="M178.5 53.5C189.41 53.5 199.386 59.7544 206.675 70.065C213.962 80.3731 218.5 94.6659 218.5 110.5C218.5 126.334 213.962 140.627 206.675 150.935C199.386 161.246 189.41 167.5 178.5 167.5C167.59 167.5 157.614 161.246 150.325 150.935C143.038 140.627 138.5 126.334 138.5 110.5C138.5 94.6659 143.038 80.3731 150.325 70.065C157.614 59.7544 167.59 53.5 178.5 53.5Z" fill="#6216FF" fill-opacity="0.02" stroke="#242770" stroke-width="2"/>
<mask id="mask0_3572_4876" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="17" y="56" width="199" height="109">
<mask id="path-2-inside-1_3572_4876" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M54.5281 56.5C54.5187 56.5 54.5094 56.5 54.5 56.5C54.4906 56.5 54.4813 56.5 54.4719 56.5L53.5 56.5V56.5193C33.5277 57.2928 17.5 81.1647 17.5 110.5C17.5 139.835 33.5278 163.707 53.5 164.481V164.5L54.5 164.5L178.486 164.5H178.5H178.514H179.5L179.5 164.481C199.472 163.707 215.5 139.835 215.5 110.5C215.5 81.1647 199.472 57.2928 179.5 56.5194V56.5L178.5 56.5L54.5281 56.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M54.5281 56.5C54.5187 56.5 54.5094 56.5 54.5 56.5C54.4906 56.5 54.4813 56.5 54.4719 56.5L53.5 56.5V56.5193C33.5277 57.2928 17.5 81.1647 17.5 110.5C17.5 139.835 33.5278 163.707 53.5 164.481V164.5L54.5 164.5L178.486 164.5H178.5H178.514H179.5L179.5 164.481C199.472 163.707 215.5 139.835 215.5 110.5C215.5 81.1647 199.472 57.2928 179.5 56.5194V56.5L178.5 56.5L54.5281 56.5Z" fill="#7F84FB"/>
<path class="tb-shape-fill" d="M54.5281 56.5L54.5259 58.5H54.5281V56.5ZM54.4719 56.5V58.5L54.4741 58.5L54.4719 56.5ZM53.5 56.5V54.5L51.5 54.5V56.5H53.5ZM53.5 56.5193L53.5774 58.5178L55.5 58.4434V56.5193H53.5ZM53.5 164.481H55.5V162.557L53.5774 162.482L53.5 164.481ZM53.5 164.5H51.5V166.5L53.5 166.5L53.5 164.5ZM54.5 164.5V162.5H54.5L54.5 164.5ZM178.486 164.5V166.5V164.5ZM179.5 164.5V166.5H181.502L181.5 164.498L179.5 164.5ZM179.5 164.481L179.423 162.482L177.498 162.557L177.5 164.482L179.5 164.481ZM179.5 56.5194H177.5V58.4434L179.423 58.5179L179.5 56.5194ZM179.5 56.5H181.5V54.5L179.5 54.5V56.5ZM178.5 56.5V54.5V56.5ZM54.5 58.5C54.5086 58.5 54.5173 58.5 54.5259 58.5L54.5302 54.5C54.5202 54.5 54.5101 54.5 54.5 54.5V58.5ZM54.4741 58.5C54.4827 58.5 54.4914 58.5 54.5 58.5V54.5C54.4899 54.5 54.4798 54.5 54.4698 54.5L54.4741 58.5ZM54.4719 54.5H53.5V58.5H54.4719V54.5ZM51.5 56.5V56.5193H55.5V56.5H51.5ZM19.5 110.5C19.5 96.1785 23.4156 83.274 29.6733 73.9102C35.9388 64.5346 44.4175 58.8726 53.5774 58.5178L53.4226 54.5208C42.6102 54.9395 33.0889 61.6001 26.3475 71.6877C19.5983 81.787 15.5 95.4862 15.5 110.5H19.5ZM53.5774 162.482C44.4175 162.127 35.9388 156.465 29.6733 147.09C23.4156 137.726 19.5 124.822 19.5 110.5H15.5C15.5 125.514 19.5983 139.213 26.3475 149.312C33.0889 159.4 42.6102 166.06 53.4226 166.479L53.5774 162.482ZM51.5 164.481V164.5H55.5V164.481H51.5ZM53.5 166.5L54.5 166.5L54.5 162.5L53.5 162.5L53.5 166.5ZM54.5 166.5L178.486 166.5V162.5L54.5 162.5V166.5ZM178.486 166.5H178.5V162.5H178.486V166.5ZM178.5 166.5H178.514V162.5H178.5V166.5ZM178.514 166.5H179.5V162.5H178.514V166.5ZM181.5 164.498L181.5 164.479L177.5 164.482L177.5 164.502L181.5 164.498ZM179.577 166.479C190.39 166.06 199.911 159.4 206.652 149.312C213.402 139.213 217.5 125.514 217.5 110.5H213.5C213.5 124.822 209.584 137.726 203.327 147.09C197.061 156.465 188.582 162.127 179.423 162.482L179.577 166.479ZM217.5 110.5C217.5 95.4862 213.402 81.787 206.652 71.6877C199.911 61.6001 190.39 54.9396 179.577 54.5208L179.423 58.5179C188.582 58.8726 197.061 64.5346 203.327 73.9102C209.584 83.274 213.5 96.1785 213.5 110.5H217.5ZM181.5 56.5194V56.5H177.5V56.5194H181.5ZM179.5 54.5H178.5V58.5H179.5V54.5ZM178.5 54.5L54.5281 54.5V58.5L178.5 58.5V54.5Z" fill="#242770" mask="url(#path-2-inside-1_3572_4876)"/>
</mask>
<g mask="url(#mask0_3572_4876)">
<rect class="tb-liquid-fill tb-liquid" x="17.5" y="118.5" width="198" height="108" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="17.5" y="118.5" width="198" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="17.5" y="118.5" width="198" height="1" fill="white" fill-opacity="0.1"/>
</g>
<mask id="path-6-inside-2_3572_4876" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M54.5213 52.5L178.5 52.5L179.5 52.5L179.5 52.5169C201.682 53.268 219.5 78.9404 219.5 110.5C219.5 142.06 201.682 167.732 179.5 168.483L179.5 168.5L178.518 168.5L178.5 168.5L178.482 168.5L54.5 168.5L53.5 168.5L53.5 168.483C31.3183 167.732 13.5 142.06 13.5 110.5C13.5 78.9404 31.3183 53.268 53.5 52.5169L53.5 52.5L54.4787 52.5C54.4858 52.5 54.4929 52.5 54.5 52.5C54.5071 52.5 54.5142 52.5 54.5213 52.5Z"/>
</mask>
<path class="tb-shape-fill" d="M178.5 52.5L178.5 54.5L178.5 52.5ZM54.5213 52.5L54.5213 50.5L54.5213 52.5ZM179.5 52.5L181.5 52.5L181.5 50.5L179.5 50.5L179.5 52.5ZM179.5 52.5169L177.5 52.5169L177.5 54.4503L179.432 54.5158L179.5 52.5169ZM179.5 168.483L179.432 166.484L177.5 166.55L177.5 168.483L179.5 168.483ZM179.5 168.5L179.5 170.5L181.5 170.5L181.5 168.5L179.5 168.5ZM178.518 168.5L178.518 166.5L178.517 166.5L178.518 168.5ZM178.5 168.5L178.499 170.5L178.501 170.5L178.5 168.5ZM178.482 168.5L178.483 166.5L178.482 166.5L178.482 168.5ZM53.5 168.5L51.5 168.5L51.5 170.5L53.5 170.5L53.5 168.5ZM53.5 168.483L55.5 168.483L55.5 166.55L53.5677 166.484L53.5 168.483ZM53.5 52.5169L53.5677 54.5158L55.5 54.4503L55.5 52.5169L53.5 52.5169ZM53.5 52.5L53.5 50.5L51.5 50.5L51.5 52.5L53.5 52.5ZM178.5 50.5L54.5213 50.5L54.5213 54.5L178.5 54.5L178.5 50.5ZM179.5 50.5L178.5 50.5L178.5 54.5L179.5 54.5L179.5 50.5ZM181.5 52.5169L181.5 52.5L177.5 52.5L177.5 52.5169L181.5 52.5169ZM221.5 110.5C221.5 94.3605 216.947 79.6457 209.463 68.8181C201.985 57.9978 191.461 50.9208 179.568 50.5181L179.432 54.5158C189.721 54.8642 199.197 60.9989 206.173 71.0924C213.144 81.1787 217.5 95.0798 217.5 110.5L221.5 110.5ZM179.568 170.482C191.461 170.079 201.985 163.002 209.463 152.182C216.947 141.354 221.5 126.639 221.5 110.5L217.5 110.5C217.5 125.92 213.144 139.821 206.173 149.908C199.197 160.001 189.721 166.136 179.432 166.484L179.568 170.482ZM181.5 168.5L181.5 168.483L177.5 168.483L177.5 168.5L181.5 168.5ZM178.518 170.5L179.5 170.5L179.5 166.5L178.518 166.5L178.518 170.5ZM178.501 170.5L178.519 170.5L178.517 166.5L178.499 166.5L178.501 170.5ZM178.481 170.5L178.499 170.5L178.501 166.5L178.483 166.5L178.481 170.5ZM54.5 170.5L178.482 170.5L178.482 166.5L54.5 166.5L54.5 170.5ZM53.5 170.5L54.5 170.5L54.5 166.5L53.5 166.5L53.5 170.5ZM51.5 168.483L51.5 168.5L55.5 168.5L55.5 168.483L51.5 168.483ZM53.5677 166.484C43.2788 166.136 33.8033 160.001 26.8272 149.908C19.856 139.821 15.5 125.92 15.5 110.5L11.5 110.5C11.5 126.639 16.0531 141.354 23.5366 152.182C31.0151 163.002 41.5395 170.079 53.4323 170.482L53.5677 166.484ZM15.5 110.5C15.5 95.0798 19.856 81.1787 26.8272 71.0924C33.8033 60.9989 43.2788 54.8642 53.5677 54.5158L53.4323 50.518C41.5395 50.9208 31.0151 57.9978 23.5366 68.8181C16.0531 79.6457 11.5 94.3606 11.5 110.5L15.5 110.5ZM51.5 52.5L51.5 52.5169L55.5 52.5169L55.5 52.5L51.5 52.5ZM54.4787 50.5L53.5 50.5L53.5 54.5L54.4787 54.5L54.4787 50.5ZM54.4787 54.5C54.4829 54.5 54.487 54.5 54.4904 54.5C54.4941 54.5 54.4971 54.5 54.5 54.5L54.5 50.5C54.4958 50.5 54.4917 50.5 54.4883 50.5C54.4847 50.5 54.4817 50.5 54.4787 50.5L54.4787 54.5ZM54.5 54.5C54.5029 54.5 54.5059 54.5 54.5096 54.5C54.513 54.5 54.5171 54.5 54.5213 54.5L54.5213 50.5C54.5183 50.5 54.5153 50.5 54.5117 50.5C54.5083 50.5 54.5042 50.5 54.5 50.5L54.5 54.5Z" fill="#242770" mask="url(#path-6-inside-2_3572_4876)"/>
<path class="tb-shape-stroke" d="M54.5 53.5C65.4104 53.5 75.3863 59.7544 82.6748 70.065C89.9615 80.3731 94.5 94.6659 94.5 110.5C94.5 126.334 89.9615 140.627 82.6748 150.935C75.3863 161.246 65.4104 167.5 54.5 167.5C43.5896 167.5 33.6137 161.246 26.3252 150.935C19.0385 140.627 14.5 126.334 14.5 110.5C14.5 94.6659 19.0385 80.3731 26.3252 70.065C33.6137 59.7544 43.5896 53.5 54.5 53.5Z" stroke="#242770" stroke-width="2"/>
<mask id="path-9-inside-3_3572_4876" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M134.5 36.5H105.5V54.5C105.5 56.1569 111.992 57.5 120 57.5C128.008 57.5 134.5 56.1569 134.5 54.5V36.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M134.5 36.5H105.5V54.5C105.5 56.1569 111.992 57.5 120 57.5C128.008 57.5 134.5 56.1569 134.5 54.5V36.5Z"/>
<path class="tb-shape-fill" d="M105.5 36.5V34.5H103.5V36.5H105.5ZM134.5 36.5H136.5V34.5H134.5V36.5ZM105.5 38.5H134.5V34.5H105.5V38.5ZM107.5 54.5V36.5H103.5V54.5H107.5ZM120 55.5C116.09 55.5 112.607 55.1707 110.152 54.6628C108.908 54.4055 108.034 54.1232 107.518 53.8712C107.257 53.7431 107.191 53.6721 107.217 53.6982C107.248 53.7304 107.5 54.0048 107.5 54.5H103.5C103.5 55.4095 103.954 56.0882 104.373 56.511C104.785 56.9277 105.289 57.2334 105.761 57.4643C106.713 57.9301 107.962 58.2943 109.342 58.5798C112.135 59.1577 115.902 59.5 120 59.5V55.5ZM132.5 54.5C132.5 54.0048 132.752 53.7304 132.783 53.6982C132.809 53.6721 132.743 53.7431 132.482 53.8712C131.966 54.1232 131.092 54.4055 129.848 54.6628C127.393 55.1707 123.91 55.5 120 55.5V59.5C124.098 59.5 127.865 59.1577 130.658 58.5798C132.038 58.2943 133.287 57.9301 134.239 57.4643C134.711 57.2334 135.215 56.9277 135.627 56.511C136.046 56.0882 136.5 55.4095 136.5 54.5H132.5ZM132.5 36.5V54.5H136.5V36.5H132.5Z" fill="#242770" mask="url(#path-9-inside-3_3572_4876)"/>
<path class="tb-shape-stroke" d="M134.541 37.3323C134.541 37.3323 134.54 37.3332 134.539 37.3349C134.54 37.3332 134.541 37.3323 134.541 37.3323ZM133.866 37.2416C134.077 37.338 134.231 37.4258 134.341 37.5C134.231 37.5742 134.077 37.662 133.866 37.7584C133.187 38.0692 132.141 38.3742 130.77 38.6395C128.045 39.167 124.236 39.5 120 39.5C115.764 39.5 111.955 39.167 109.23 38.6395C107.859 38.3742 106.813 38.0692 106.134 37.7584C105.923 37.662 105.769 37.5742 105.659 37.5C105.769 37.4258 105.923 37.338 106.134 37.2416C106.813 36.9308 107.859 36.6258 109.23 36.3605C111.955 35.833 115.764 35.5 120 35.5C124.236 35.5 128.045 35.833 130.77 36.3605C132.141 36.6258 133.187 36.9308 133.866 37.2416ZM105.459 37.3323C105.46 37.3323 105.46 37.3332 105.461 37.3349C105.46 37.3332 105.459 37.3323 105.459 37.3323ZM105.459 37.6677C105.459 37.6677 105.46 37.6668 105.461 37.6651C105.46 37.6668 105.46 37.6677 105.459 37.6677ZM134.539 37.6651C134.54 37.6668 134.541 37.6677 134.541 37.6677C134.541 37.6677 134.54 37.6668 134.539 37.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M134.541 34.3323C134.541 34.3323 134.54 34.3332 134.539 34.3349C134.54 34.3332 134.541 34.3323 134.541 34.3323ZM133.866 34.2416C134.077 34.338 134.231 34.4258 134.341 34.5C134.231 34.5742 134.077 34.662 133.866 34.7584C133.187 35.0692 132.141 35.3742 130.77 35.6395C128.045 36.167 124.236 36.5 120 36.5C115.764 36.5 111.955 36.167 109.23 35.6395C107.859 35.3742 106.813 35.0692 106.134 34.7584C105.923 34.662 105.769 34.5742 105.659 34.5C105.769 34.4258 105.923 34.338 106.134 34.2416C106.813 33.9308 107.859 33.6258 109.23 33.3605C111.955 32.833 115.764 32.5 120 32.5C124.236 32.5 128.045 32.833 130.77 33.3605C132.141 33.6258 133.187 33.9308 133.866 34.2416ZM105.459 34.3323C105.46 34.3323 105.46 34.3332 105.461 34.3349C105.46 34.3332 105.459 34.3323 105.459 34.3323ZM105.459 34.6677C105.459 34.6677 105.46 34.6668 105.461 34.6651C105.46 34.6668 105.46 34.6677 105.459 34.6677ZM134.539 34.6651C134.54 34.6668 134.541 34.6677 134.541 34.6677C134.541 34.6677 134.54 34.6668 134.539 34.6651Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip0_3572_4876)">
<g filter="url(#filter0_b_3572_4876)">
<rect class="absolute-overlay" x="112" y="80.5" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="112" y="80.5" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_4876)">
<rect class="percentage-overlay" x="116.5" y="91" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="116.5" y="91" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_4876" x="106" y="74.5" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_4876"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_4876" result="shape"/>
</filter>
<filter id="filter1_b_3572_4876" x="110.5" y="85" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_4876"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_4876" result="shape"/>
</filter>
<clipPath id="clip0_3572_4876">
<rect width="81" height="61" fill="white" transform="translate(112 80.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

50
ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-dish-ends.svg

@ -0,0 +1,50 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.5 175.5C27.5 175.5 9.5 156.5 9.5 111.5C9.5 66.5 27.4999 47.5 27.4999 47.5H205.5C205.5 47.5 223.5 66.5 223.5 111.5C223.5 156.5 205.5 175.5 205.5 175.5H27.5Z" fill="#6216FF" fill-opacity="0.02"/>
<path class="tb-shape-fill" d="M27.5 175.5L26.774 176.188L27.0699 176.5H27.5V175.5ZM27.4999 47.5V46.5H27.0698L26.774 46.8123L27.4999 47.5ZM205.5 175.5V176.5H205.93L206.226 176.188L205.5 175.5ZM8.5 111.5C8.5 134.138 13.0284 150.273 17.5826 160.773C19.8589 166.021 22.1398 169.857 23.8603 172.39C24.7205 173.656 25.4404 174.597 25.9496 175.225C26.2042 175.539 26.4061 175.776 26.5464 175.935C26.6166 176.015 26.6714 176.076 26.7097 176.118C26.7288 176.139 26.7439 176.155 26.7546 176.167C26.76 176.173 26.7644 176.177 26.7676 176.181C26.7692 176.183 26.7706 176.184 26.7716 176.185C26.7722 176.186 26.7728 176.186 26.773 176.187C26.7736 176.187 26.774 176.188 27.5 175.5C28.226 174.812 28.2263 174.813 28.2265 174.813C28.2265 174.813 28.2267 174.813 28.2267 174.813C28.2267 174.813 28.2264 174.813 28.2259 174.812C28.2247 174.811 28.2225 174.809 28.2193 174.805C28.2127 174.798 28.202 174.786 28.187 174.77C28.1572 174.737 28.111 174.686 28.0493 174.616C27.926 174.475 27.7411 174.259 27.5035 173.966C27.0283 173.38 26.342 172.484 25.5147 171.266C23.8602 168.83 21.6411 165.104 19.4174 159.977C14.9716 149.727 10.5 133.862 10.5 111.5H8.5ZM27.4999 47.5C26.774 46.8123 26.7735 46.8127 26.7729 46.8133C26.7727 46.8136 26.7721 46.8142 26.7715 46.8148C26.7705 46.816 26.7691 46.8174 26.7675 46.8191C26.7643 46.8226 26.7599 46.8272 26.7545 46.833C26.7438 46.8446 26.7287 46.8608 26.7096 46.8818C26.6713 46.9238 26.6165 46.9846 26.5463 47.0646C26.406 47.2244 26.2041 47.4606 25.9495 47.7747C25.4404 48.4031 24.7204 49.3436 23.8602 50.61C22.1398 53.1429 19.8589 56.9788 17.5825 62.2271C13.0284 72.727 8.5 88.8624 8.5 111.5H10.5C10.5 89.1376 14.9716 73.273 19.4174 63.0229C21.641 57.8962 23.8601 54.1696 25.5146 51.7338C26.342 50.5158 27.0282 49.6203 27.5034 49.0339C27.741 48.7406 27.9259 48.5246 28.0492 48.3842C28.1109 48.3139 28.1571 48.2626 28.187 48.2299C28.2019 48.2136 28.2126 48.2019 28.2192 48.1949C28.2224 48.1914 28.2246 48.189 28.2258 48.1879C28.2263 48.1873 28.2266 48.187 28.2266 48.1869C28.2266 48.1869 28.2264 48.1871 28.2264 48.1871C28.2262 48.1874 28.2259 48.1877 27.4999 47.5ZM205.5 46.5H27.4999V48.5H205.5V46.5ZM224.5 111.5C224.5 88.8624 219.972 72.727 215.417 62.2271C213.141 56.9788 210.86 53.1429 209.14 50.61C208.28 49.3436 207.56 48.4031 207.05 47.7747C206.796 47.4605 206.594 47.2244 206.454 47.0646C206.383 46.9846 206.329 46.9238 206.29 46.8818C206.271 46.8608 206.256 46.8445 206.245 46.8329C206.24 46.8271 206.236 46.8225 206.232 46.819C206.231 46.8173 206.229 46.8158 206.228 46.8146C206.228 46.814 206.227 46.8132 206.227 46.8128C206.226 46.812 205.895 46.5811 205.5 46.5V48.5C205.105 48.4189 204.774 48.1881 204.774 48.1876C204.774 48.1875 204.774 48.1872 204.773 48.1872C204.773 48.1871 204.774 48.1874 204.774 48.188C204.775 48.1891 204.778 48.1915 204.781 48.195C204.787 48.202 204.798 48.2136 204.813 48.23C204.843 48.2626 204.889 48.314 204.951 48.3842C205.074 48.5246 205.259 48.7406 205.497 49.0339C205.972 49.6203 206.658 50.5158 207.485 51.7338C209.14 54.1696 211.359 57.8962 213.583 63.0229C218.028 73.273 222.5 89.1376 222.5 111.5H224.5ZM205.5 175.5C206.226 176.188 206.226 176.187 206.227 176.187C206.227 176.186 206.228 176.186 206.228 176.185C206.229 176.184 206.231 176.183 206.232 176.181C206.236 176.177 206.24 176.173 206.245 176.167C206.256 176.155 206.271 176.139 206.29 176.118C206.329 176.076 206.383 176.015 206.454 175.935C206.594 175.776 206.796 175.539 207.05 175.225C207.56 174.597 208.28 173.656 209.14 172.39C210.86 169.857 213.141 166.021 215.417 160.773C219.972 150.273 224.5 134.138 224.5 111.5H222.5C222.5 133.862 218.028 149.727 213.583 159.977C211.359 165.104 209.14 168.83 207.485 171.266C206.658 172.484 205.972 173.38 205.496 173.966C205.259 174.259 205.074 174.475 204.951 174.616C204.889 174.686 204.843 174.737 204.813 174.77C204.798 174.786 204.787 174.798 204.781 174.805C204.777 174.809 204.775 174.811 204.774 174.812C204.774 174.813 204.773 174.813 204.773 174.813C204.773 174.813 204.773 174.813 204.773 174.813C204.774 174.813 204.774 174.812 205.5 175.5ZM27.5 176.5H205.5V174.5H27.5V176.5Z" fill="#242770"/>
<mask id="mask0_3572_5805" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="12" y="50" width="209" height="123">
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.5 172.5C28.5 172.5 12.5 154.391 12.5 111.5C12.5 68.6094 28.4999 50.5 28.4999 50.5H204.5C204.5 50.5 220.5 68.6094 220.5 111.5C220.5 154.391 204.5 172.5 204.5 172.5H28.5Z" fill="#F1F1F9"/>
</mask>
<g mask="url(#mask0_3572_5805)">
<rect class="tb-liquid-fill tb-liquid" x="12.5" y="118.5" width="208" height="122" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="12.5" y="118.5" width="208" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="12.5" y="118.5" width="208" height="1" fill="white" fill-opacity="0.1"/>
</g>
<path class="tb-shape-stroke" d="M27.4932 47.5V175.5" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M205.493 47.5V175.5" stroke="#242770" stroke-width="2"/>
<mask id="path-8-inside-1_3572_5805" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M131.5 30.5H102.5V48.5C102.5 50.1569 108.992 51.5 117 51.5C125.008 51.5 131.5 50.1569 131.5 48.5V30.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M131.5 30.5H102.5V48.5C102.5 50.1569 108.992 51.5 117 51.5C125.008 51.5 131.5 50.1569 131.5 48.5V30.5Z"/>
<path class="tb-shape-fill" d="M102.5 30.5V28.5H100.5V30.5H102.5ZM131.5 30.5H133.5V28.5H131.5V30.5ZM102.5 32.5H131.5V28.5H102.5V32.5ZM104.5 48.5V30.5H100.5V48.5H104.5ZM117 49.5C113.09 49.5 109.607 49.1707 107.152 48.6628C105.908 48.4055 105.034 48.1232 104.518 47.8712C104.257 47.7431 104.191 47.6721 104.217 47.6982C104.248 47.7304 104.5 48.0048 104.5 48.5H100.5C100.5 49.4095 100.954 50.0882 101.373 50.511C101.785 50.9277 102.289 51.2334 102.761 51.4643C103.713 51.9301 104.962 52.2943 106.342 52.5798C109.135 53.1577 112.902 53.5 117 53.5V49.5ZM129.5 48.5C129.5 48.0048 129.752 47.7304 129.783 47.6982C129.809 47.6721 129.743 47.7431 129.482 47.8712C128.966 48.1232 128.092 48.4055 126.848 48.6628C124.393 49.1707 120.91 49.5 117 49.5V53.5C121.098 53.5 124.865 53.1577 127.658 52.5798C129.038 52.2943 130.287 51.9301 131.239 51.4643C131.711 51.2334 132.215 50.9277 132.627 50.511C133.046 50.0882 133.5 49.4095 133.5 48.5H129.5ZM129.5 30.5V48.5H133.5V30.5H129.5Z" fill="#242770" mask="url(#path-8-inside-1_3572_5805)"/>
<path class="tb-shape-stroke" d="M131.541 31.3323C131.541 31.3323 131.54 31.3332 131.539 31.3349C131.54 31.3332 131.541 31.3323 131.541 31.3323ZM130.866 31.2416C131.077 31.338 131.231 31.4258 131.341 31.5C131.231 31.5742 131.077 31.662 130.866 31.7584C130.187 32.0692 129.141 32.3742 127.77 32.6395C125.045 33.167 121.236 33.5 117 33.5C112.764 33.5 108.955 33.167 106.23 32.6395C104.859 32.3742 103.813 32.0692 103.134 31.7584C102.923 31.662 102.769 31.5742 102.659 31.5C102.769 31.4258 102.923 31.338 103.134 31.2416C103.813 30.9308 104.859 30.6258 106.23 30.3605C108.955 29.833 112.764 29.5 117 29.5C121.236 29.5 125.045 29.833 127.77 30.3605C129.141 30.6258 130.187 30.9308 130.866 31.2416ZM102.459 31.3323C102.46 31.3323 102.46 31.3332 102.461 31.3349C102.46 31.3332 102.459 31.3323 102.459 31.3323ZM102.459 31.6677C102.459 31.6677 102.46 31.6668 102.461 31.6651C102.46 31.6668 102.46 31.6677 102.459 31.6677ZM131.539 31.6651C131.54 31.6668 131.541 31.6677 131.541 31.6677C131.541 31.6677 131.54 31.6668 131.539 31.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M131.541 28.3323C131.541 28.3323 131.54 28.3332 131.539 28.3349C131.54 28.3332 131.541 28.3323 131.541 28.3323ZM130.866 28.2416C131.077 28.338 131.231 28.4258 131.341 28.5C131.231 28.5742 131.077 28.662 130.866 28.7584C130.187 29.0692 129.141 29.3742 127.77 29.6395C125.045 30.167 121.236 30.5 117 30.5C112.764 30.5 108.955 30.167 106.23 29.6395C104.859 29.3742 103.813 29.0692 103.134 28.7584C102.923 28.662 102.769 28.5742 102.659 28.5C102.769 28.4258 102.923 28.338 103.134 28.2416C103.813 27.9308 104.859 27.6258 106.23 27.3605C108.955 26.833 112.764 26.5 117 26.5C121.236 26.5 125.045 26.833 127.77 27.3605C129.141 27.6258 130.187 27.9308 130.866 28.2416ZM102.459 28.3323C102.46 28.3323 102.46 28.3332 102.461 28.3349C102.46 28.3332 102.459 28.3323 102.459 28.3323ZM102.459 28.6677C102.459 28.6677 102.46 28.6668 102.461 28.6651C102.46 28.6668 102.46 28.6677 102.459 28.6677ZM131.539 28.6651C131.54 28.6668 131.541 28.6677 131.541 28.6677C131.541 28.6677 131.54 28.6668 131.539 28.6651Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip0_3572_5805)">
<g filter="url(#filter0_b_3572_5805)">
<rect class="absolute-overlay" x="76" y="82" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="76" y="82" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_5805)">
<rect class="percentage-overlay" x="80.5" y="92.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="80.5" y="92.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_5805" x="70" y="76" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_5805"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_5805" result="shape"/>
</filter>
<filter id="filter1_b_3572_5805" x="74.5" y="86.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_5805"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_5805" result="shape"/>
</filter>
<clipPath id="clip0_3572_5805">
<rect width="81" height="61" fill="white" transform="translate(76 82)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

61
ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-ellipse.svg

@ -0,0 +1,61 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3572_6212)">
<path class="tb-shape-stroke" opacity="0.1" d="M152.5 61.5C174.43 61.5 194.237 67.1687 208.531 76.2808C222.836 85.4003 231.5 97.8764 231.5 111.5C231.5 125.124 222.836 137.6 208.531 146.719C194.237 155.831 174.43 161.5 152.5 161.5C130.57 161.5 110.763 155.831 96.469 146.719C82.1639 137.6 73.5 125.124 73.5 111.5C73.5 97.8764 82.1639 85.4003 96.469 76.2808C110.763 67.1687 130.57 61.5 152.5 61.5Z" fill="#6216FF" fill-opacity="0.02" stroke="#242770" stroke-width="2"/>
<mask id="mask0_3572_6212" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="5" y="64" width="223" height="95">
<mask id="path-2-inside-1_3572_6212" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M71.5 64.8349V64.5L80.4756 64.5C80.4838 64.5 80.4919 64.5 80.5 64.5C80.5081 64.5 80.5162 64.5 80.5244 64.5L152.5 64.5L162.5 64.5V64.9141C199.194 67.9779 227.5 87.6674 227.5 111.5C227.5 135.333 199.194 155.022 162.5 158.086L162.5 158.5L152.5 158.5H80.5H71.5V158.165C34.322 155.379 5.5 135.549 5.5 111.5C5.5 87.4513 34.322 67.6212 71.5 64.8349Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M71.5 64.8349V64.5L80.4756 64.5C80.4838 64.5 80.4919 64.5 80.5 64.5C80.5081 64.5 80.5162 64.5 80.5244 64.5L152.5 64.5L162.5 64.5V64.9141C199.194 67.9779 227.5 87.6674 227.5 111.5C227.5 135.333 199.194 155.022 162.5 158.086L162.5 158.5L152.5 158.5H80.5H71.5V158.165C34.322 155.379 5.5 135.549 5.5 111.5C5.5 87.4513 34.322 67.6212 71.5 64.8349Z" fill="#7F84FB"/>
<path class="tb-shape-fill" d="M71.5 64.5V62.5L69.5 62.5V64.5H71.5ZM71.5 64.8349L71.6495 66.8293L73.5 66.6906V64.8349H71.5ZM80.4756 64.5V66.5H80.476L80.4756 64.5ZM80.5244 64.5L80.524 66.5H80.5244V64.5ZM152.5 64.5V66.5V64.5ZM162.5 64.5H164.5V62.5H162.5V64.5ZM162.5 64.9141H160.5V66.7541L162.334 66.9072L162.5 64.9141ZM162.5 158.086L162.334 156.093L160.5 156.246L160.5 158.086L162.5 158.086ZM162.5 158.5V160.5L164.5 160.5L164.5 158.5L162.5 158.5ZM152.5 158.5V160.5V158.5ZM71.5 158.5H69.5V160.5H71.5V158.5ZM71.5 158.165H73.5V156.309L71.6495 156.171L71.5 158.165ZM69.5 64.5V64.8349H73.5V64.5H69.5ZM80.4756 62.5L71.5 62.5V66.5L80.4756 66.5V62.5ZM80.476 66.5C80.484 66.5 80.492 66.5 80.5 66.5V62.5C80.4917 62.5 80.4835 62.5 80.4752 62.5L80.476 66.5ZM80.5 66.5C80.508 66.5 80.516 66.5 80.524 66.5L80.5248 62.5C80.5165 62.5 80.5083 62.5 80.5 62.5V66.5ZM152.5 62.5L80.5244 62.5V66.5L152.5 66.5V62.5ZM162.5 62.5L152.5 62.5V66.5L162.5 66.5V62.5ZM164.5 64.9141V64.5H160.5V64.9141H164.5ZM229.5 111.5C229.5 98.7275 221.913 87.3081 209.918 78.7882C197.912 70.2601 181.303 64.4771 162.666 62.9211L162.334 66.9072C180.391 68.4149 196.282 74.0085 207.602 82.0493C218.934 90.0982 225.5 100.44 225.5 111.5H229.5ZM162.666 160.079C181.303 158.523 197.912 152.74 209.918 144.212C221.913 135.692 229.5 124.272 229.5 111.5H225.5C225.5 122.56 218.934 132.902 207.602 140.951C196.282 148.992 180.391 154.585 162.334 156.093L162.666 160.079ZM164.5 158.5L164.5 158.086L160.5 158.086L160.5 158.5L164.5 158.5ZM152.5 160.5L162.5 160.5V156.5L152.5 156.5V160.5ZM80.5 160.5H152.5V156.5H80.5V160.5ZM71.5 160.5H80.5V156.5H71.5V160.5ZM69.5 158.165V158.5H73.5V158.165H69.5ZM71.6495 156.171C53.3524 154.799 37.2139 149.238 25.7066 141.168C14.1863 133.09 7.5 122.66 7.5 111.5H3.5C3.5 124.389 11.2247 135.898 23.4099 144.443C35.608 152.998 52.4696 158.744 71.3505 160.159L71.6495 156.171ZM7.5 111.5C7.5 100.34 14.1863 89.9103 25.7065 81.8316C37.2139 73.7618 53.3524 68.2006 71.6495 66.8293L71.3505 62.8405C52.4696 64.2555 35.608 70.0025 23.4099 78.5566C11.2247 87.1017 3.5 98.6114 3.5 111.5H7.5Z" fill="#242770" mask="url(#path-2-inside-1_3572_6212)"/>
</mask>
<g mask="url(#mask0_3572_6212)">
<rect class="tb-liquid-fill tb-liquid" x="4.5" y="118.5" width="224" height="94" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="4.5" y="118.5" width="224" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="4.5" y="118.5" width="224" height="1" fill="white" fill-opacity="0.1"/>
</g>
<mask id="path-6-inside-2_3572_6212" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M82.5 60.5156C81.8353 60.5052 81.1686 60.5 80.5 60.5C36.3172 60.5 0.499997 83.3335 0.499998 111.5C0.499999 139.667 36.3172 162.5 80.5 162.5C81.1686 162.5 81.8353 162.495 82.5 162.484L82.5 162.5L150.5 162.5L150.5 162.484C151.165 162.495 151.831 162.5 152.5 162.5C196.683 162.5 232.5 139.667 232.5 111.5C232.5 83.3335 196.683 60.5 152.5 60.5C151.831 60.5 151.165 60.5052 150.5 60.5156L150.5 60.5L82.5 60.5L82.5 60.5156Z"/>
</mask>
<path class="tb-shape-fill" d="M82.5 60.5156L82.4687 62.5154L84.5 62.5472L84.5 60.5156L82.5 60.5156ZM82.5 162.484L84.5 162.484L84.5 160.453L82.4687 160.485L82.5 162.484ZM82.5 162.5L80.5 162.5L80.5 164.5L82.5 164.5L82.5 162.5ZM150.5 162.5L150.5 164.5L152.5 164.5L152.5 162.5L150.5 162.5ZM150.5 162.484L150.531 160.485L148.5 160.453L148.5 162.484L150.5 162.484ZM152.5 162.5L152.5 160.5L152.5 162.5ZM150.5 60.5156L148.5 60.5156L148.5 62.5472L150.531 62.5154L150.5 60.5156ZM150.5 60.5L152.5 60.5L152.5 58.5L150.5 58.5L150.5 60.5ZM82.5 60.5L82.5 58.5L80.5 58.5L80.5 60.5L82.5 60.5ZM80.5 62.5C81.1582 62.5 81.8144 62.5051 82.4687 62.5154L82.5313 58.5159C81.8562 58.5053 81.179 58.5 80.5 58.5L80.5 62.5ZM2.5 111.5C2.5 98.336 10.8734 86.1339 25.0066 77.124C39.1164 68.129 58.7309 62.5 80.5 62.5L80.5 58.5C58.0863 58.5 37.7008 64.2878 22.8563 73.7511C8.0352 83.1996 -1.5 96.4975 -1.5 111.5L2.5 111.5ZM80.5 160.5C58.7309 160.5 39.1164 154.871 25.0066 145.876C10.8734 136.866 2.5 124.664 2.5 111.5L-1.5 111.5C-1.5 126.503 8.0352 139.8 22.8563 149.249C37.7008 158.712 58.0863 164.5 80.5 164.5L80.5 160.5ZM82.4687 160.485C81.8145 160.495 81.1582 160.5 80.5 160.5L80.5 164.5C81.179 164.5 81.8562 164.495 82.5313 164.484L82.4687 160.485ZM80.5 162.484L80.5 162.5L84.5 162.5L84.5 162.484L80.5 162.484ZM82.5 164.5L150.5 164.5L150.5 160.5L82.5 160.5L82.5 164.5ZM152.5 162.5L152.5 162.484L148.5 162.484L148.5 162.5L152.5 162.5ZM150.469 164.484C151.144 164.495 151.821 164.5 152.5 164.5L152.5 160.5C151.842 160.5 151.186 160.495 150.531 160.485L150.469 164.484ZM152.5 164.5C174.914 164.5 195.299 158.712 210.144 149.249C224.965 139.8 234.5 126.503 234.5 111.5L230.5 111.5C230.5 124.664 222.127 136.866 207.993 145.876C193.884 154.871 174.269 160.5 152.5 160.5L152.5 164.5ZM234.5 111.5C234.5 96.4975 224.965 83.1996 210.144 73.7511C195.299 64.2878 174.914 58.5 152.5 58.5L152.5 62.5C174.269 62.5 193.884 68.129 207.993 77.124C222.127 86.1339 230.5 98.336 230.5 111.5L234.5 111.5ZM152.5 58.5C151.821 58.5 151.144 58.5053 150.469 58.5159L150.531 62.5154C151.186 62.5051 151.842 62.5 152.5 62.5L152.5 58.5ZM152.5 60.5156L152.5 60.5L148.5 60.5L148.5 60.5156L152.5 60.5156ZM150.5 58.5L82.5 58.5L82.5 62.5L150.5 62.5L150.5 58.5ZM80.5 60.5L80.5 60.5156L84.5 60.5156L84.5 60.5L80.5 60.5Z" fill="#242770" mask="url(#path-6-inside-2_3572_6212)"/>
<path class="tb-shape-stroke" d="M80.5 61.5C102.43 61.5 122.237 67.1687 136.531 76.2808C150.836 85.4003 159.5 97.8764 159.5 111.5C159.5 125.124 150.836 137.6 136.531 146.719C122.237 155.831 102.43 161.5 80.5 161.5C58.5698 161.5 38.7625 155.831 24.469 146.719C10.1639 137.6 1.5 125.124 1.5 111.5C1.5 97.8764 10.1639 85.4003 24.469 76.2808C38.7625 67.1687 58.5698 61.5 80.5 61.5Z" stroke="#242770" stroke-width="2"/>
<mask id="path-9-inside-3_3572_6212" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M143.5 44.5H114.5V62.5C114.5 64.1569 120.992 65.5 129 65.5C137.008 65.5 143.5 64.1569 143.5 62.5V44.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M143.5 44.5H114.5V62.5C114.5 64.1569 120.992 65.5 129 65.5C137.008 65.5 143.5 64.1569 143.5 62.5V44.5Z"/>
<path class="tb-shape-fill" d="M114.5 44.5V42.5H112.5V44.5H114.5ZM143.5 44.5H145.5V42.5H143.5V44.5ZM114.5 46.5H143.5V42.5H114.5V46.5ZM116.5 62.5V44.5H112.5V62.5H116.5ZM129 63.5C125.09 63.5 121.607 63.1707 119.152 62.6628C117.908 62.4055 117.034 62.1232 116.518 61.8712C116.257 61.7431 116.191 61.6721 116.217 61.6982C116.248 61.7304 116.5 62.0048 116.5 62.5H112.5C112.5 63.4095 112.954 64.0882 113.373 64.511C113.785 64.9277 114.289 65.2334 114.761 65.4643C115.713 65.9301 116.962 66.2943 118.342 66.5798C121.135 67.1577 124.902 67.5 129 67.5V63.5ZM141.5 62.5C141.5 62.0048 141.752 61.7304 141.783 61.6982C141.809 61.6721 141.743 61.7431 141.482 61.8712C140.966 62.1232 140.092 62.4055 138.848 62.6628C136.393 63.1707 132.91 63.5 129 63.5V67.5C133.098 67.5 136.865 67.1577 139.658 66.5798C141.038 66.2943 142.287 65.9301 143.239 65.4643C143.711 65.2334 144.215 64.9277 144.627 64.511C145.046 64.0882 145.5 63.4095 145.5 62.5H141.5ZM141.5 44.5V62.5H145.5V44.5H141.5Z" fill="#242770" mask="url(#path-9-inside-3_3572_6212)"/>
<path class="tb-shape-stroke" d="M143.541 45.3323C143.541 45.3323 143.54 45.3332 143.539 45.3349C143.54 45.3332 143.541 45.3323 143.541 45.3323ZM142.866 45.2416C143.077 45.338 143.231 45.4258 143.341 45.5C143.231 45.5742 143.077 45.662 142.866 45.7584C142.187 46.0692 141.141 46.3742 139.77 46.6395C137.045 47.167 133.236 47.5 129 47.5C124.764 47.5 120.955 47.167 118.23 46.6395C116.859 46.3742 115.813 46.0692 115.134 45.7584C114.923 45.662 114.769 45.5742 114.659 45.5C114.769 45.4258 114.923 45.338 115.134 45.2416C115.813 44.9308 116.859 44.6258 118.23 44.3605C120.955 43.833 124.764 43.5 129 43.5C133.236 43.5 137.045 43.833 139.77 44.3605C141.141 44.6258 142.187 44.9308 142.866 45.2416ZM114.459 45.3323C114.46 45.3323 114.46 45.3332 114.461 45.3349C114.46 45.3332 114.459 45.3323 114.459 45.3323ZM114.459 45.6677C114.459 45.6677 114.46 45.6668 114.461 45.6651C114.46 45.6668 114.46 45.6677 114.459 45.6677ZM143.539 45.6651C143.54 45.6668 143.541 45.6677 143.541 45.6677C143.541 45.6677 143.54 45.6668 143.539 45.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M143.541 42.3323C143.541 42.3323 143.54 42.3332 143.539 42.3349C143.54 42.3332 143.541 42.3323 143.541 42.3323ZM142.866 42.2416C143.077 42.338 143.231 42.4258 143.341 42.5C143.231 42.5742 143.077 42.662 142.866 42.7584C142.187 43.0692 141.141 43.3742 139.77 43.6395C137.045 44.167 133.236 44.5 129 44.5C124.764 44.5 120.955 44.167 118.23 43.6395C116.859 43.3742 115.813 43.0692 115.134 42.7584C114.923 42.662 114.769 42.5742 114.659 42.5C114.769 42.4258 114.923 42.338 115.134 42.2416C115.813 41.9308 116.859 41.6258 118.23 41.3605C120.955 40.833 124.764 40.5 129 40.5C133.236 40.5 137.045 40.833 139.77 41.3605C141.141 41.6258 142.187 41.9308 142.866 42.2416ZM114.459 42.3323C114.46 42.3323 114.46 42.3332 114.461 42.3349C114.46 42.3332 114.459 42.3323 114.459 42.3323ZM114.459 42.6677C114.459 42.6677 114.46 42.6668 114.461 42.6651C114.46 42.6668 114.46 42.6677 114.459 42.6677ZM143.539 42.6651C143.54 42.6668 143.541 42.6677 143.541 42.6677C143.541 42.6677 143.54 42.6668 143.539 42.6651Z" stroke="#242770" stroke-width="2"/>
</g>
<g class="container-overlay" clip-path="url(#clip1_3572_6212)">
<g filter="url(#filter0_b_3572_6212)">
<rect class="absolute-overlay" x="40" y="81" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="40" y="81" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_6212)">
<rect class="percentage-overlay" x="44.5" y="91.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="44.5" y="91.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_6212" x="34" y="75" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_6212"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_6212" result="shape"/>
</filter>
<filter id="filter1_b_3572_6212" x="38.5" y="85.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_6212"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_6212" result="shape"/>
</filter>
<clipPath id="clip0_3572_6212">
<rect width="232" height="200" fill="white" transform="translate(0.5 0.5)"/>
</clipPath>
<clipPath id="clip1_3572_6212">
<rect width="81" height="61" fill="white" transform="translate(40 81)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

61
ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-oval.svg

@ -0,0 +1,61 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3572_10610)">
<rect class="tb-shape-stroke" opacity="0.1" x="91.5" y="60.5" width="140" height="100" rx="50" fill="#6216FF" fill-opacity="0.02" stroke="#242770" stroke-width="2"/>
<mask id="mask0_3572_10610" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="4" y="63" width="225" height="95">
<mask id="path-2-inside-1_3572_10610" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.5 63.5418C24.4703 64.5896 4.5 85.2127 4.5 110.5C4.5 135.787 24.4703 156.41 49.5 157.458V157.5L180.5 157.5L181.5 157.5C207.457 157.5 228.5 136.457 228.5 110.5C228.5 84.5426 207.457 63.5 181.5 63.5L145.5 63.5L145.428 63.5001L92.5754 63.5001C92.5503 63.5 92.5251 63.5 92.5 63.5H51.5C51.4745 63.5 51.449 63.5 51.4235 63.5001L49.5 63.5001V63.5418Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.5 63.5418C24.4703 64.5896 4.5 85.2127 4.5 110.5C4.5 135.787 24.4703 156.41 49.5 157.458V157.5L180.5 157.5L181.5 157.5C207.457 157.5 228.5 136.457 228.5 110.5C228.5 84.5426 207.457 63.5 181.5 63.5L145.5 63.5L145.428 63.5001L92.5754 63.5001C92.5503 63.5 92.5251 63.5 92.5 63.5H51.5C51.4745 63.5 51.449 63.5 51.4235 63.5001L49.5 63.5001V63.5418Z" fill="#7F84FB"/>
<path class="tb-shape-fill" d="M49.5 63.5418L49.5837 65.54L51.5 65.4598V63.5418H49.5ZM49.5 157.458H51.5V155.54L49.5837 155.46L49.5 157.458ZM49.5 157.5H47.5V159.5H49.5V157.5ZM180.5 157.5V159.5H180.5L180.5 157.5ZM181.5 157.5V155.5H181.5L181.5 157.5ZM181.5 63.5V61.5V63.5ZM145.5 63.5V61.5L145.499 61.5L145.5 63.5ZM145.428 63.5001V65.5001L145.43 65.5001L145.428 63.5001ZM92.5754 63.5001L92.5722 65.5001H92.5754V63.5001ZM51.4235 63.5001V65.5001L51.4267 65.5001L51.4235 63.5001ZM49.5 63.5001V61.5001L47.5 61.5001V63.5001H49.5ZM6.5 110.5C6.5 86.2892 25.6205 66.5432 49.5837 65.54L49.4163 61.5435C23.3201 62.636 2.5 84.1361 2.5 110.5H6.5ZM49.5837 155.46C25.6205 154.457 6.5 134.711 6.5 110.5H2.5C2.5 136.864 23.3201 158.364 49.4164 159.456L49.5837 155.46ZM47.5 157.458V157.5H51.5V157.458H47.5ZM49.5 159.5L180.5 159.5V155.5L49.5 155.5V159.5ZM180.5 159.5L181.5 159.5L181.5 155.5L180.5 155.5L180.5 159.5ZM181.5 159.5C208.562 159.5 230.5 137.562 230.5 110.5H226.5C226.5 135.353 206.353 155.5 181.5 155.5V159.5ZM230.5 110.5C230.5 83.4381 208.562 61.5 181.5 61.5V65.5C206.353 65.5 226.5 85.6472 226.5 110.5H230.5ZM181.5 61.5L145.5 61.5V65.5L181.5 65.5V61.5ZM145.499 61.5L145.427 61.5001L145.43 65.5001L145.501 65.5L145.499 61.5ZM145.428 61.5001L92.5754 61.5001V65.5001L145.428 65.5001V61.5001ZM92.5 65.5C92.5241 65.5 92.5482 65.5 92.5722 65.5001L92.5785 61.5001C92.5523 61.5 92.5262 61.5 92.5 61.5V65.5ZM51.5 65.5H92.5V61.5H51.5V65.5ZM51.4267 65.5001C51.4511 65.5 51.4756 65.5 51.5 65.5V61.5C51.4734 61.5 51.4469 61.5 51.4203 61.5001L51.4267 65.5001ZM51.4235 61.5001L49.5 61.5001V65.5001H51.4235V61.5001ZM47.5 63.5001V63.5418H51.5V63.5001H47.5Z" fill="#242770" mask="url(#path-2-inside-1_3572_10610)"/>
</mask>
<g mask="url(#mask0_3572_10610)">
<rect class="tb-liquid-fill tb-liquid" x="4.5" y="118.5" width="224" height="94" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="4.5" y="118.5" width="224" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="4.5" y="118.5" width="224" height="1" fill="white" fill-opacity="0.1"/>
</g>
<mask id="path-6-inside-2_3572_10610" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.5 59.5385C22.2605 60.5889 0.499999 83.0032 0.499998 110.5C0.499999 137.997 22.2605 160.411 49.5 161.461L49.5 161.5L51.4679 161.5C51.4786 161.5 51.4893 161.5 51.5 161.5L92.5 161.5C92.5116 161.5 92.5233 161.5 92.5349 161.5L141.468 161.5L141.5 161.5L181.5 161.5C209.667 161.5 232.5 138.667 232.5 110.5C232.5 82.3335 209.667 59.5 181.5 59.5L180.5 59.5L141.5 59.5L92.5 59.5L51.5 59.5L49.5 59.5L49.5 59.5385Z"/>
</mask>
<path class="tb-shape-fill" d="M0.499998 110.5L-1.5 110.5L0.499998 110.5ZM49.5 59.5385L49.5771 61.537L51.5 61.4629L51.5 59.5385L49.5 59.5385ZM49.5 161.461L51.5 161.461L51.5 159.537L49.5771 159.463L49.5 161.461ZM49.5 161.5L47.5 161.5L47.5 163.5L49.5 163.5L49.5 161.5ZM51.4679 161.5L51.4692 159.5L51.4679 159.5L51.4679 161.5ZM51.5 161.5L51.5 163.5L51.5 161.5ZM92.5 161.5L92.5 163.5L92.5 161.5ZM92.5349 161.5L92.5349 159.5L92.5335 159.5L92.5349 161.5ZM141.468 161.5L141.468 159.5L141.468 159.5L141.468 161.5ZM141.5 161.5L141.499 163.5L141.5 163.5L141.5 161.5ZM181.5 161.5L181.5 163.5L181.5 161.5ZM181.5 59.5L181.5 61.5L181.5 61.5L181.5 59.5ZM180.5 59.5L180.5 57.5L180.5 57.5L180.5 59.5ZM141.5 59.5L141.5 57.5L141.5 59.5ZM92.5 59.5L92.5 61.5L92.5 59.5ZM51.5 59.5L51.5 57.5L51.5 59.5ZM49.5 59.5L49.5 57.5L47.5 57.5L47.5 59.5L49.5 59.5ZM2.5 110.5C2.5 84.082 23.4072 62.5462 49.5771 61.537L49.4229 57.54C21.1138 58.6316 -1.5 81.9245 -1.5 110.5L2.5 110.5ZM49.5771 159.463C23.4072 158.454 2.5 136.918 2.5 110.5L-1.5 110.5C-1.5 139.076 21.1138 162.368 49.4229 163.46L49.5771 159.463ZM47.5 161.461L47.5 161.5L51.5 161.5L51.5 161.461L47.5 161.461ZM49.5 163.5L51.4679 163.5L51.4679 159.5L49.5 159.5L49.5 163.5ZM51.5 159.5C51.4897 159.5 51.4795 159.5 51.4692 159.5L51.4667 163.5C51.4778 163.5 51.4889 163.5 51.5 163.5L51.5 159.5ZM92.5 159.5L51.5 159.5L51.5 163.5L92.5 163.5L92.5 159.5ZM92.5335 159.5C92.5224 159.5 92.5112 159.5 92.5 159.5L92.5 163.5C92.5121 163.5 92.5242 163.5 92.5362 163.5L92.5335 159.5ZM92.5349 163.5L141.468 163.5L141.468 159.5L92.5349 159.5L92.5349 163.5ZM141.467 163.5L141.499 163.5L141.501 159.5L141.468 159.5L141.467 163.5ZM141.5 163.5L181.5 163.5L181.5 159.5L141.5 159.5L141.5 163.5ZM181.5 163.5C210.771 163.5 234.5 139.771 234.5 110.5L230.5 110.5C230.5 137.562 208.562 159.5 181.5 159.5L181.5 163.5ZM234.5 110.5C234.5 81.2289 210.771 57.5 181.5 57.5L181.5 61.5C208.562 61.5 230.5 83.438 230.5 110.5L234.5 110.5ZM181.5 57.5L180.5 57.5L180.5 61.5L181.5 61.5L181.5 57.5ZM180.5 57.5L141.5 57.5L141.5 61.5L180.5 61.5L180.5 57.5ZM141.5 57.5L92.5 57.5L92.5 61.5L141.5 61.5L141.5 57.5ZM51.5 61.5L92.5 61.5L92.5 57.5L51.5 57.5L51.5 61.5ZM51.5 57.5L49.5 57.5L49.5 61.5L51.5 61.5L51.5 57.5ZM47.5 59.5L47.5 59.5385L51.5 59.5385L51.5 59.5L47.5 59.5Z" fill="#242770" mask="url(#path-6-inside-2_3572_10610)"/>
<rect class="tb-shape-stroke" x="1.5" y="60.5" width="141" height="100" rx="50" stroke="#242770" stroke-width="2"/>
<mask id="path-9-inside-3_3572_10610" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M167.5 43.5H138.5V61.5C138.5 63.1569 144.992 64.5 153 64.5C161.008 64.5 167.5 63.1569 167.5 61.5V43.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M167.5 43.5H138.5V61.5C138.5 63.1569 144.992 64.5 153 64.5C161.008 64.5 167.5 63.1569 167.5 61.5V43.5Z"/>
<path class="tb-shape-fill" d="M138.5 43.5V41.5H136.5V43.5H138.5ZM167.5 43.5H169.5V41.5H167.5V43.5ZM138.5 45.5H167.5V41.5H138.5V45.5ZM140.5 61.5V43.5H136.5V61.5H140.5ZM153 62.5C149.09 62.5 145.607 62.1707 143.152 61.6628C141.908 61.4055 141.034 61.1232 140.518 60.8712C140.257 60.7431 140.191 60.6721 140.217 60.6982C140.248 60.7304 140.5 61.0048 140.5 61.5H136.5C136.5 62.4095 136.954 63.0882 137.373 63.511C137.785 63.9277 138.289 64.2334 138.761 64.4643C139.713 64.9301 140.962 65.2943 142.342 65.5798C145.135 66.1577 148.902 66.5 153 66.5V62.5ZM165.5 61.5C165.5 61.0048 165.752 60.7304 165.783 60.6982C165.809 60.6721 165.743 60.7431 165.482 60.8712C164.966 61.1232 164.092 61.4055 162.848 61.6628C160.393 62.1707 156.91 62.5 153 62.5V66.5C157.098 66.5 160.865 66.1577 163.658 65.5798C165.038 65.2943 166.287 64.9301 167.239 64.4643C167.711 64.2334 168.215 63.9277 168.627 63.511C169.046 63.0882 169.5 62.4095 169.5 61.5H165.5ZM165.5 43.5V61.5H169.5V43.5H165.5Z" fill="#242770" mask="url(#path-9-inside-3_3572_10610)"/>
<path class="tb-shape-stroke" d="M167.541 44.3323C167.541 44.3323 167.54 44.3332 167.539 44.3349C167.54 44.3332 167.541 44.3323 167.541 44.3323ZM166.866 44.2416C167.077 44.338 167.231 44.4258 167.341 44.5C167.231 44.5742 167.077 44.662 166.866 44.7584C166.187 45.0692 165.141 45.3742 163.77 45.6395C161.045 46.167 157.236 46.5 153 46.5C148.764 46.5 144.955 46.167 142.23 45.6395C140.859 45.3742 139.813 45.0692 139.134 44.7584C138.923 44.662 138.769 44.5742 138.659 44.5C138.769 44.4258 138.923 44.338 139.134 44.2416C139.813 43.9308 140.859 43.6258 142.23 43.3605C144.955 42.833 148.764 42.5 153 42.5C157.236 42.5 161.045 42.833 163.77 43.3605C165.141 43.6258 166.187 43.9308 166.866 44.2416ZM138.459 44.3323C138.46 44.3323 138.46 44.3332 138.461 44.3349C138.46 44.3332 138.459 44.3323 138.459 44.3323ZM138.459 44.6677C138.459 44.6677 138.46 44.6668 138.461 44.6651C138.46 44.6668 138.46 44.6677 138.459 44.6677ZM167.539 44.6651C167.54 44.6668 167.541 44.6677 167.541 44.6677C167.541 44.6677 167.54 44.6668 167.539 44.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M167.541 41.3323C167.541 41.3323 167.54 41.3332 167.539 41.3349C167.54 41.3332 167.541 41.3323 167.541 41.3323ZM166.866 41.2416C167.077 41.338 167.231 41.4258 167.341 41.5C167.231 41.5742 167.077 41.662 166.866 41.7584C166.187 42.0692 165.141 42.3742 163.77 42.6395C161.045 43.167 157.236 43.5 153 43.5C148.764 43.5 144.955 43.167 142.23 42.6395C140.859 42.3742 139.813 42.0692 139.134 41.7584C138.923 41.662 138.769 41.5742 138.659 41.5C138.769 41.4258 138.923 41.338 139.134 41.2416C139.813 40.9308 140.859 40.6258 142.23 40.3605C144.955 39.833 148.764 39.5 153 39.5C157.236 39.5 161.045 39.833 163.77 40.3605C165.141 40.6258 166.187 40.9308 166.866 41.2416ZM138.459 41.3323C138.46 41.3323 138.46 41.3332 138.461 41.3349C138.46 41.3332 138.459 41.3323 138.459 41.3323ZM138.459 41.6677C138.459 41.6677 138.46 41.6668 138.461 41.6651C138.46 41.6668 138.46 41.6677 138.459 41.6677ZM167.539 41.6651C167.54 41.6668 167.541 41.6677 167.541 41.6677C167.541 41.6677 167.54 41.6668 167.539 41.6651Z" stroke="#242770" stroke-width="2"/>
</g>
<g class="container-overlay" clip-path="url(#clip1_3572_10610)">
<g filter="url(#filter0_b_3572_10610)">
<rect class="absolute-overlay" x="32" y="80" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="32" y="80" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_10610)">
<rect class="percentage-overlay" x="36.5" y="90.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="36.5" y="90.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_10610" x="26" y="74" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_10610"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_10610" result="shape"/>
</filter>
<filter id="filter1_b_3572_10610" x="30.5" y="84.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_10610"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_10610" result="shape"/>
</filter>
<clipPath id="clip0_3572_10610">
<rect width="232" height="200" fill="white" transform="translate(0.5 0.5)"/>
</clipPath>
<clipPath id="clip1_3572_10610">
<rect width="81" height="61" fill="white" transform="translate(32 80)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

49
ui-ngx/src/assets/widget/liquid-level/shapes/rectangle.svg

@ -0,0 +1,49 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="tb-shape-stroke" opacity="0.1" d="M10.5 57.5C10.5 53.0817 14.0817 49.5 18.5 49.5H26.5C30.9183 49.5 34.5 53.0817 34.5 57.5V163.5C34.5 167.918 30.9183 171.5 26.5 171.5H18.5C14.0817 171.5 10.5 167.918 10.5 163.5V57.5Z" stroke="#242770" stroke-width="2"/>
<mask id="mask0_3572_5095" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="13" y="52" width="206" height="117">
<path d="M13.5 58.5001C13.5 55.1864 16.1863 52.5001 19.5 52.5001L212.5 52.5002C215.814 52.5002 218.5 55.1865 218.5 58.5002V162.5C218.5 165.814 215.814 168.5 212.5 168.5L19.5 168.5C16.1863 168.5 13.5 165.814 13.5 162.5V58.5001Z" fill="#7A8BFF"/>
</mask>
<g mask="url(#mask0_3572_5095)">
<rect class="tb-liquid-fill tb-liquid" x="13.5" y="118.5" width="205" height="116" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="13.5" y="118.5" width="205" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="13.5" y="118.5" width="205" height="1" fill="white" fill-opacity="0.1"/>
</g>
<rect class="tb-shape-stroke" x="10.5" y="49.5" width="211" height="122" rx="8" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M197.5 57.5C197.5 53.0817 201.082 49.5 205.5 49.5H213.5C217.918 49.5 221.5 53.0817 221.5 57.5V163.5C221.5 167.918 217.918 171.5 213.5 171.5H205.5C201.082 171.5 197.5 167.918 197.5 163.5V57.5Z" stroke="#242770" stroke-width="2"/>
<mask id="path-7-inside-1_3572_5095" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M127.5 32.5H98.5V50.5C98.5 52.1569 104.992 53.5 113 53.5C121.008 53.5 127.5 52.1569 127.5 50.5V32.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M127.5 32.5H98.5V50.5C98.5 52.1569 104.992 53.5 113 53.5C121.008 53.5 127.5 52.1569 127.5 50.5V32.5Z"/>
<path class="tb-shape-fill" d="M98.5 32.5V30.5H96.5V32.5H98.5ZM127.5 32.5H129.5V30.5H127.5V32.5ZM98.5 34.5H127.5V30.5H98.5V34.5ZM100.5 50.5V32.5H96.5V50.5H100.5ZM113 51.5C109.09 51.5 105.607 51.1707 103.152 50.6628C101.908 50.4055 101.034 50.1232 100.518 49.8712C100.257 49.7431 100.191 49.6721 100.217 49.6982C100.248 49.7304 100.5 50.0048 100.5 50.5H96.5C96.5 51.4095 96.9544 52.0882 97.3726 52.511C97.7847 52.9277 98.2887 53.2334 98.7607 53.4643C99.7129 53.9301 100.962 54.2943 102.342 54.5798C105.135 55.1577 108.902 55.5 113 55.5V51.5ZM125.5 50.5C125.5 50.0048 125.752 49.7304 125.783 49.6982C125.809 49.6721 125.743 49.7431 125.482 49.8712C124.966 50.1232 124.092 50.4055 122.848 50.6628C120.393 51.1707 116.91 51.5 113 51.5V55.5C117.098 55.5 120.865 55.1577 123.658 54.5798C125.038 54.2943 126.287 53.9301 127.239 53.4643C127.711 53.2334 128.215 52.9277 128.627 52.511C129.046 52.0882 129.5 51.4095 129.5 50.5H125.5ZM125.5 32.5V50.5H129.5V32.5H125.5Z" fill="#242770" mask="url(#path-7-inside-1_3572_5095)"/>
<path class="tb-shape-stroke" d="M127.541 33.3323C127.541 33.3323 127.54 33.3332 127.539 33.3349C127.54 33.3332 127.541 33.3323 127.541 33.3323ZM126.866 33.2416C127.077 33.338 127.231 33.4258 127.341 33.5C127.231 33.5742 127.077 33.662 126.866 33.7584C126.187 34.0692 125.141 34.3742 123.77 34.6395C121.045 35.167 117.236 35.5 113 35.5C108.764 35.5 104.955 35.167 102.23 34.6395C100.859 34.3742 99.8132 34.0692 99.1342 33.7584C98.9234 33.662 98.7686 33.5742 98.6586 33.5C98.7686 33.4258 98.9234 33.338 99.1342 33.2416C99.8132 32.9308 100.859 32.6258 102.23 32.3605C104.955 31.833 108.764 31.5 113 31.5C117.236 31.5 121.045 31.833 123.77 32.3605C125.141 32.6258 126.187 32.9308 126.866 33.2416ZM98.4595 33.3323C98.4595 33.3323 98.4602 33.3332 98.4613 33.3349C98.46 33.3332 98.4594 33.3323 98.4595 33.3323ZM98.4595 33.6677C98.4594 33.6677 98.46 33.6668 98.4613 33.6651C98.4602 33.6668 98.4595 33.6677 98.4595 33.6677ZM127.539 33.6651C127.54 33.6668 127.541 33.6677 127.541 33.6677C127.541 33.6677 127.54 33.6668 127.539 33.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M127.541 30.3323C127.541 30.3323 127.54 30.3332 127.539 30.3349C127.54 30.3332 127.541 30.3323 127.541 30.3323ZM126.866 30.2416C127.077 30.338 127.231 30.4258 127.341 30.5C127.231 30.5742 127.077 30.662 126.866 30.7584C126.187 31.0692 125.141 31.3742 123.77 31.6395C121.045 32.167 117.236 32.5 113 32.5C108.764 32.5 104.955 32.167 102.23 31.6395C100.859 31.3742 99.8132 31.0692 99.1342 30.7584C98.9234 30.662 98.7686 30.5742 98.6586 30.5C98.7686 30.4258 98.9234 30.338 99.1342 30.2416C99.8132 29.9308 100.859 29.6258 102.23 29.3605C104.955 28.833 108.764 28.5 113 28.5C117.236 28.5 121.045 28.833 123.77 29.3605C125.141 29.6258 126.187 29.9308 126.866 30.2416ZM98.4595 30.3323C98.4595 30.3323 98.4602 30.3332 98.4613 30.3349C98.46 30.3332 98.4594 30.3323 98.4595 30.3323ZM98.4595 30.6677C98.4594 30.6677 98.46 30.6668 98.4613 30.6651C98.4602 30.6668 98.4595 30.6677 98.4595 30.6677ZM127.539 30.6651C127.54 30.6668 127.541 30.6677 127.541 30.6677C127.541 30.6677 127.54 30.6668 127.539 30.6651Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip0_3572_5095)">
<g filter="url(#filter0_b_3572_5095)">
<rect class="absolute-overlay" x="64" y="80" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="64" y="80" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_5095)">
<rect class="percentage-overlay" x="68.5" y="90.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="68.5" y="90.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_5095" x="58" y="74" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_5095"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_5095" result="shape"/>
</filter>
<filter id="filter1_b_3572_5095" x="62.5" y="84.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_5095"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_5095" result="shape"/>
</filter>
<clipPath id="clip0_3572_5095">
<rect width="81" height="61" fill="white" transform="translate(64 80)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

49
ui-ngx/src/assets/widget/liquid-level/shapes/vertical-capsule.svg

@ -0,0 +1,49 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect class="tb-shape-stroke" x="58.5" y="199.5" width="178" height="116" rx="58" transform="rotate(-90 58.5 199.5)" fill="#6216FF" fill-opacity="0.02" stroke="#242770" stroke-width="2"/>
<mask id="mask0_3572_6660" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="61" y="24" width="111" height="173">
<rect class="tb-shape-stroke" x="62.5" y="195.5" width="170" height="108" rx="54" transform="rotate(-90 62.5 195.5)" fill="#F1F1F9" stroke="#242770" stroke-width="2"/>
</mask>
<g mask="url(#mask0_3572_6660)">
<rect class="tb-liquid-fill tb-liquid" x="61.5" y="118.5" width="110" height="172" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="61.5" y="118.5" width="110" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="61.5" y="118.5" width="110" height="1" fill="white" fill-opacity="0.1"/>
</g>
<path class="tb-shape-stroke" d="M58 146.5H174" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M59 74.5H175" stroke="#242770" stroke-width="2"/>
<mask id="path-7-inside-1_3572_6660" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M131.5 5.5H102.5V23.5C102.5 25.1569 108.992 26.5 117 26.5C125.008 26.5 131.5 25.1569 131.5 23.5V5.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M131.5 5.5H102.5V23.5C102.5 25.1569 108.992 26.5 117 26.5C125.008 26.5 131.5 25.1569 131.5 23.5V5.5Z"/>
<path class="tb-shape-fill" d="M102.5 5.5V3.5H100.5V5.5H102.5ZM131.5 5.5H133.5V3.5H131.5V5.5ZM102.5 7.5H131.5V3.5L102.5 3.5V7.5ZM104.5 23.5V5.5H100.5V23.5H104.5ZM117 24.5C113.09 24.5 109.607 24.1707 107.152 23.6628C105.908 23.4055 105.034 23.1232 104.518 22.8712C104.257 22.7431 104.191 22.6721 104.217 22.6982C104.248 22.7304 104.5 23.0048 104.5 23.5H100.5C100.5 24.4095 100.954 25.0882 101.373 25.511C101.785 25.9277 102.289 26.2334 102.761 26.4643C103.713 26.9301 104.962 27.2943 106.342 27.5798C109.135 28.1577 112.902 28.5 117 28.5V24.5ZM129.5 23.5C129.5 23.0048 129.752 22.7304 129.783 22.6982C129.809 22.6721 129.743 22.7431 129.482 22.8712C128.966 23.1232 128.092 23.4055 126.848 23.6628C124.393 24.1707 120.91 24.5 117 24.5V28.5C121.098 28.5 124.865 28.1577 127.658 27.5798C129.038 27.2943 130.287 26.9301 131.239 26.4643C131.711 26.2334 132.215 25.9277 132.627 25.511C133.046 25.0882 133.5 24.4095 133.5 23.5H129.5ZM129.5 5.5V23.5H133.5V5.5H129.5Z" fill="#242770" mask="url(#path-7-inside-1_3572_6660)"/>
<path class="tb-shape-stroke" d="M131.541 6.33233C131.541 6.33233 131.54 6.33319 131.539 6.33493C131.54 6.33319 131.541 6.33233 131.541 6.33233ZM130.866 6.24158C131.077 6.33801 131.231 6.42584 131.341 6.5C131.231 6.57417 131.077 6.66199 130.866 6.75842C130.187 7.06915 129.141 7.3742 127.77 7.63954C125.045 8.16703 121.236 8.5 117 8.5C112.764 8.5 108.955 8.16703 106.23 7.63954C104.859 7.3742 103.813 7.06915 103.134 6.75842C102.923 6.66199 102.769 6.57417 102.659 6.5C102.769 6.42583 102.923 6.33801 103.134 6.24158C103.813 5.93085 104.859 5.6258 106.23 5.36046C108.955 4.83297 112.764 4.5 117 4.5C121.236 4.5 125.045 4.83297 127.77 5.36046C129.141 5.6258 130.187 5.93085 130.866 6.24158ZM102.459 6.33233C102.46 6.33233 102.46 6.3332 102.461 6.33493C102.46 6.33319 102.459 6.33233 102.459 6.33233ZM102.459 6.66767C102.459 6.66767 102.46 6.66681 102.461 6.66507C102.46 6.6668 102.46 6.66767 102.459 6.66767ZM131.539 6.66507C131.54 6.66681 131.541 6.66767 131.541 6.66767C131.541 6.66767 131.54 6.6668 131.539 6.66507Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M131.541 3.33233C131.541 3.33233 131.54 3.33319 131.539 3.33493C131.54 3.33319 131.541 3.33233 131.541 3.33233ZM130.866 3.24158C131.077 3.33801 131.231 3.42584 131.341 3.5C131.231 3.57417 131.077 3.66199 130.866 3.75842C130.187 4.06915 129.141 4.3742 127.77 4.63954C125.045 5.16703 121.236 5.5 117 5.5C112.764 5.5 108.955 5.16703 106.23 4.63954C104.859 4.3742 103.813 4.06915 103.134 3.75842C102.923 3.66199 102.769 3.57417 102.659 3.5C102.769 3.42583 102.923 3.33801 103.134 3.24158C103.813 2.93085 104.859 2.6258 106.23 2.36046C108.955 1.83297 112.764 1.5 117 1.5C121.236 1.5 125.045 1.83297 127.77 2.36046C129.141 2.6258 130.187 2.93085 130.866 3.24158ZM102.459 3.33233C102.46 3.33233 102.46 3.3332 102.461 3.33493C102.46 3.33319 102.459 3.33233 102.459 3.33233ZM102.459 3.66767C102.459 3.66767 102.46 3.66681 102.461 3.66507C102.46 3.6668 102.46 3.66767 102.459 3.66767ZM131.539 3.66507C131.54 3.66681 131.541 3.66767 131.541 3.66767C131.541 3.66767 131.54 3.6668 131.539 3.66507Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip0_3572_6660)">
<g filter="url(#filter0_b_3572_6660)">
<rect class="absolute-overlay" x="76" y="80" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="76" y="80" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_6660)">
<rect class="percentage-overlay" x="80.5" y="90.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="80.5" y="90.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_6660" x="70" y="74" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_6660"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_6660" result="shape"/>
</filter>
<filter id="filter1_b_3572_6660" x="74.5" y="84.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_6660"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_6660" result="shape"/>
</filter>
<clipPath id="clip0_3572_6660">
<rect width="81" height="61" fill="white" transform="translate(76 80)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

58
ui-ngx/src/assets/widget/liquid-level/shapes/vertical-cylinder.svg

@ -0,0 +1,58 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_3572_1305" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="39" y="57" width="155" height="121">
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.5 57.6724V161.738C39.5 170 73.5 177.5 116.5 177.5C173.5 177.5 193.5 167 193.5 161.738V57.6724C184.016 65.6521 153.134 71.5001 116.5 71.5001C79.8664 71.5001 48.9838 65.6521 39.5 57.6724Z" fill="#7A8BFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.5 57.6724V161.738C39.5 170 73.5 177.5 116.5 177.5C173.5 177.5 193.5 167 193.5 161.738V57.6724C184.016 65.6521 153.134 71.5001 116.5 71.5001C79.8664 71.5001 48.9838 65.6521 39.5 57.6724Z" fill="white" fill-opacity="0.1"/>
</mask>
<g mask="url(#mask0_3572_1305)">
<g clip-path="url(#clip0_3572_1305)">
<rect class="tb-liquid-fill tb-liquid" x="39.5" y="109.5" width="154" height="122" fill="#7A8BFF"/>
<ellipse class="tb-liquid-surface tb-liquid" cx="116.5" cy="109.5" rx="77" ry="16" fill="#7A8BFF"/>
<ellipse class="tb-liquid-surface" cx="116.5" cy="109.5" rx="77" ry="16" fill="white" fill-opacity="0.1"/>
</g>
</g>
<mask id="path-4-inside-1_3572_1305" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M195.5 162.5V51.5H197.5V162.5C197.5 164.064 196.791 165.507 195.614 166.8C194.443 168.088 192.763 169.283 190.683 170.389C186.521 172.603 180.564 174.566 173.288 176.204C158.72 179.481 138.643 181.5 116.5 181.5C94.3574 181.5 74.2803 179.481 59.7119 176.204C52.4358 174.566 46.4792 172.603 42.3172 170.389C40.237 169.283 38.5568 168.088 37.3855 166.8C36.209 165.507 35.5 164.064 35.5 162.5V51.5H37.5V162.5C37.5 163.421 37.9103 164.405 38.8651 165.455C39.8251 166.51 41.2884 167.577 43.2564 168.624C47.1909 170.717 52.9499 172.632 60.151 174.252C74.5369 177.489 94.4598 179.5 116.5 179.5C138.54 179.5 158.463 177.489 172.849 174.252C180.05 172.632 185.809 170.717 189.744 168.624C191.712 167.577 193.175 166.51 194.135 165.455C195.09 164.405 195.5 163.421 195.5 162.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M195.5 162.5V51.5H197.5V162.5C197.5 164.064 196.791 165.507 195.614 166.8C194.443 168.088 192.763 169.283 190.683 170.389C186.521 172.603 180.564 174.566 173.288 176.204C158.72 179.481 138.643 181.5 116.5 181.5C94.3574 181.5 74.2803 179.481 59.7119 176.204C52.4358 174.566 46.4792 172.603 42.3172 170.389C40.237 169.283 38.5568 168.088 37.3855 166.8C36.209 165.507 35.5 164.064 35.5 162.5V51.5H37.5V162.5C37.5 163.421 37.9103 164.405 38.8651 165.455C39.8251 166.51 41.2884 167.577 43.2564 168.624C47.1909 170.717 52.9499 172.632 60.151 174.252C74.5369 177.489 94.4598 179.5 116.5 179.5C138.54 179.5 158.463 177.489 172.849 174.252C180.05 172.632 185.809 170.717 189.744 168.624C191.712 167.577 193.175 166.51 194.135 165.455C195.09 164.405 195.5 163.421 195.5 162.5Z" fill="#6216FF" fill-opacity="0.02"/>
<path class="tb-shape-fill" d="M195.5 51.5V49.5H193.5V51.5H195.5ZM197.5 51.5H199.5V49.5H197.5V51.5ZM195.614 166.8L197.094 168.146L195.614 166.8ZM190.683 170.389L189.744 168.624V168.624L190.683 170.389ZM173.288 176.204L172.849 174.252L173.288 176.204ZM59.7119 176.204L60.151 174.252L59.7119 176.204ZM42.3172 170.389L43.2564 168.624L43.2564 168.624L42.3172 170.389ZM37.3855 166.8L35.9059 168.146H35.9059L37.3855 166.8ZM35.5 51.5V49.5H33.5V51.5H35.5ZM37.5 51.5H39.5V49.5H37.5V51.5ZM38.8651 165.455L40.3447 164.109H40.3447L38.8651 165.455ZM43.2564 168.624L42.3172 170.389H42.3172L43.2564 168.624ZM60.151 174.252L60.59 172.301L60.151 174.252ZM172.849 174.252L172.41 172.301L172.849 174.252ZM189.744 168.624L190.683 170.389L189.744 168.624ZM194.135 165.455L195.614 166.8V166.8L194.135 165.455ZM193.5 51.5V162.5H197.5V51.5H193.5ZM197.5 49.5H195.5V53.5H197.5V49.5ZM199.5 162.5V51.5H195.5V162.5H199.5ZM197.094 168.146C198.491 166.61 199.5 164.709 199.5 162.5H195.5C195.5 163.42 195.091 164.404 194.135 165.455L197.094 168.146ZM191.622 172.155C193.814 170.989 195.711 169.666 197.094 168.146L194.135 165.455C193.175 166.51 191.712 167.577 189.744 168.624L191.622 172.155ZM173.727 178.155C181.078 176.501 187.233 174.49 191.622 172.155L189.744 168.624C185.809 170.717 180.05 172.632 172.849 174.252L173.727 178.155ZM116.5 183.5C138.745 183.5 158.976 181.474 173.727 178.155L172.849 174.252C158.463 177.489 138.54 179.5 116.5 179.5V183.5ZM59.2729 178.155C74.0238 181.474 94.2551 183.5 116.5 183.5V179.5C94.4598 179.5 74.5369 177.489 60.151 174.252L59.2729 178.155ZM41.3779 172.155C45.7674 174.49 51.9217 176.501 59.2729 178.155L60.151 174.252C52.95 172.632 47.1909 170.717 43.2564 168.624L41.3779 172.155ZM35.9059 168.146C37.2885 169.666 39.1856 170.989 41.3779 172.155L43.2564 168.624C41.2884 167.577 39.8252 166.51 38.8651 165.455L35.9059 168.146ZM33.5 162.5C33.5 164.709 34.5086 166.61 35.9059 168.146L38.8651 165.455C37.9095 164.404 37.5 163.42 37.5 162.5H33.5ZM37.5 162.5V51.5H33.5V162.5H37.5ZM35.5 53.5H37.5V49.5H35.5V53.5ZM35.5 51.5V162.5H39.5V51.5H35.5ZM40.3447 164.109C39.6126 163.304 39.5 162.779 39.5 162.5H35.5C35.5 164.063 36.2079 165.506 37.3855 166.8L40.3447 164.109ZM44.1957 166.858C42.3397 165.87 41.0934 164.932 40.3447 164.109L37.3855 166.8C38.5569 168.088 40.2371 169.283 42.3172 170.389L44.1957 166.858ZM60.59 172.301C53.464 170.698 47.9026 168.83 44.1957 166.858L42.3172 170.389C46.4792 172.603 52.4359 174.566 59.7119 176.204L60.59 172.301ZM116.5 177.5C94.5621 177.5 74.7935 175.497 60.59 172.301L59.7119 176.204C74.2803 179.481 94.3574 181.5 116.5 181.5V177.5ZM172.41 172.301C158.207 175.497 138.438 177.5 116.5 177.5V181.5C138.643 181.5 158.72 179.481 173.288 176.204L172.41 172.301ZM188.804 166.858C185.097 168.83 179.536 170.698 172.41 172.301L173.288 176.204C180.564 174.566 186.521 172.603 190.683 170.389L188.804 166.858ZM192.655 164.109C191.907 164.932 190.66 165.87 188.804 166.858L190.683 170.389C192.763 169.283 194.443 168.088 195.614 166.8L192.655 164.109ZM193.5 162.5C193.5 162.779 193.387 163.304 192.655 164.109L195.614 166.8C196.792 165.506 197.5 164.063 197.5 162.5H193.5Z" fill="#242770" mask="url(#path-4-inside-1_3572_1305)"/>
<path class="tb-shape-stroke" opacity="0.1" d="M196.5 51.5C196.5 42 159 33.5 116.5 33.5C72.3172 33.5 36.5 42.5 36.5 51.5" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M116.5 71.5C62.5 71.5 36.5 61.9457 36.5 51.6607C36.5 41.3757 91.0039 29.0147 97.4253 27.5" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M116.5 71.5C170.5 71.5 196.5 61.9457 196.5 51.6607C196.5 41.3757 141.996 29.0147 135.575 27.5" stroke="#242770" stroke-width="2"/>
<mask id="path-9-inside-2_3572_1305" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M98.5 23.5V27.3153C98.6396 27.4221 98.906 27.5678 99.3428 27.7309C100.212 28.0553 101.527 28.3661 103.221 28.6336C106.593 29.1661 111.289 29.5 116.5 29.5C121.711 29.5 126.407 29.1661 129.779 28.6336C131.473 28.3661 132.788 28.0553 133.657 27.7309C134.094 27.5678 134.36 27.4221 134.5 27.3153V23.5H136.5V27.5C136.5 28.1673 136.073 28.6278 135.725 28.8963C135.357 29.1798 134.878 29.41 134.357 29.6046C133.303 29.998 131.835 30.3337 130.091 30.6091C126.587 31.1624 121.782 31.5 116.5 31.5C111.218 31.5 106.413 31.1624 102.909 30.6091C101.165 30.3337 99.6972 29.998 98.6434 29.6046C98.1221 29.41 97.6426 29.1798 97.2751 28.8963C96.9271 28.6278 96.5 28.1673 96.5 27.5V23.5H98.5Z"/>
</mask>
<path class="tb-shape-fill" d="M98.5 27.3153H96.5V28.3031L97.2845 28.9035L98.5 27.3153ZM98.5 23.5H100.5V21.5H98.5V23.5ZM99.3428 27.7309L98.6434 29.6046L98.6434 29.6046L99.3428 27.7309ZM103.221 28.6336L103.533 26.658V26.658L103.221 28.6336ZM129.779 28.6336L130.091 30.6091L129.779 28.6336ZM133.657 27.7309L134.357 29.6046L134.357 29.6046L133.657 27.7309ZM134.5 27.3153L135.716 28.9035L136.5 28.3031V27.3153H134.5ZM134.5 23.5V21.5H132.5V23.5H134.5ZM136.5 23.5H138.5V21.5H136.5V23.5ZM135.725 28.8963L136.947 30.4797L136.947 30.4797L135.725 28.8963ZM134.357 29.6046L135.056 31.4783V31.4783L134.357 29.6046ZM130.091 30.6091L129.779 28.6336L130.091 30.6091ZM102.909 30.6091L103.221 28.6336H103.221L102.909 30.6091ZM98.6434 29.6046L97.9439 31.4783H97.9439L98.6434 29.6046ZM97.2751 28.8963L98.4969 27.3129L97.2751 28.8963ZM96.5 23.5V21.5H94.5V23.5H96.5ZM100.5 27.3153V23.5H96.5V27.3153H100.5ZM100.042 25.8572C99.8798 25.7965 99.7737 25.7482 99.713 25.7175C99.6453 25.6832 99.6579 25.6829 99.7155 25.7271L97.2845 28.9035C97.6501 29.1833 98.1257 29.4114 98.6434 29.6046L100.042 25.8572ZM103.533 26.658C101.889 26.3984 100.727 26.1126 100.042 25.8572L98.6434 29.6046C99.6973 29.998 101.165 30.3337 102.909 30.6091L103.533 26.658ZM116.5 27.5C111.36 27.5 106.774 27.1698 103.533 26.658L102.909 30.6091C106.413 31.1624 111.218 31.5 116.5 31.5V27.5ZM129.467 26.658C126.226 27.1698 121.64 27.5 116.5 27.5V31.5C121.782 31.5 126.587 31.1624 130.091 30.6091L129.467 26.658ZM132.958 25.8572C132.273 26.1126 131.111 26.3984 129.467 26.658L130.091 30.6091C131.835 30.3337 133.303 29.998 134.357 29.6046L132.958 25.8572ZM133.284 25.727C133.342 25.6829 133.355 25.6832 133.287 25.7175C133.226 25.7482 133.12 25.7965 132.958 25.8572L134.357 29.6046C134.874 29.4114 135.35 29.1833 135.716 28.9035L133.284 25.727ZM136.5 27.3153V23.5H132.5V27.3153H136.5ZM134.5 25.5H136.5V21.5H134.5V25.5ZM134.5 23.5V27.5H138.5V23.5H134.5ZM136.947 30.4797C137.476 30.0714 138.5 29.0945 138.5 27.5H134.5C134.5 27.4496 134.509 27.3969 134.524 27.3488C134.539 27.3029 134.556 27.2733 134.564 27.2607C134.572 27.249 134.572 27.2506 134.56 27.2632C134.548 27.2758 134.529 27.2929 134.503 27.3129L136.947 30.4797ZM135.056 31.4783C135.659 31.2532 136.351 30.9395 136.947 30.4797L134.503 27.3129C134.364 27.4202 134.097 27.5668 133.657 27.7309L135.056 31.4783ZM130.403 32.5846C132.197 32.3013 133.818 31.9406 135.056 31.4783L133.657 27.7309C132.788 28.0553 131.473 28.3661 129.779 28.6336L130.403 32.5846ZM116.5 33.5C121.853 33.5 126.767 33.1587 130.403 32.5846L129.779 28.6336C126.407 29.1661 121.711 29.5 116.5 29.5V33.5ZM102.597 32.5846C106.233 33.1587 111.147 33.5 116.5 33.5V29.5C111.289 29.5 106.593 29.1661 103.221 28.6336L102.597 32.5846ZM97.9439 31.4783C99.1825 31.9406 100.803 32.3013 102.597 32.5846L103.221 28.6336C101.527 28.3661 100.212 28.0553 99.3428 27.7309L97.9439 31.4783ZM96.0533 30.4797C96.6492 30.9395 97.3408 31.2532 97.9439 31.4783L99.3428 27.7309C98.9033 27.5668 98.6359 27.4202 98.4969 27.3129L96.0533 30.4797ZM94.5 27.5C94.5 29.0945 95.5241 30.0714 96.0533 30.4797L98.4969 27.3129C98.471 27.2929 98.4521 27.2758 98.4399 27.2632C98.4276 27.2506 98.4283 27.249 98.4359 27.2607C98.4441 27.2733 98.4611 27.3029 98.4759 27.3488C98.4914 27.3969 98.5 27.4496 98.5 27.5H94.5ZM94.5 23.5V27.5H98.5V23.5H94.5ZM98.5 21.5H96.5V25.5H98.5V21.5Z" fill="#242770" mask="url(#path-9-inside-2_3572_1305)"/>
<path class="tb-shape-stroke" d="M135.507 23.4506C135.507 23.4506 135.507 23.4524 135.505 23.4558C135.506 23.4524 135.507 23.4506 135.507 23.4506ZM135.395 23.409C135.434 23.4472 135.46 23.4775 135.477 23.5C135.46 23.5225 135.434 23.5528 135.395 23.591C135.239 23.7432 134.956 23.9378 134.501 24.153C133.596 24.5808 132.223 24.9925 130.446 25.3478C126.909 26.0553 121.978 26.5 116.5 26.5C111.022 26.5 106.091 26.0553 102.554 25.3478C100.777 24.9925 99.404 24.5808 98.4992 24.153C98.0442 23.9378 97.761 23.7432 97.6053 23.591C97.5662 23.5528 97.5402 23.5225 97.5231 23.5C97.5402 23.4775 97.5662 23.4472 97.6053 23.409C97.761 23.2568 98.0442 23.0622 98.4992 22.847C99.404 22.4192 100.777 22.0075 102.554 21.6522C106.091 20.9447 111.022 20.5 116.5 20.5C121.978 20.5 126.909 20.9447 130.446 21.6522C132.223 22.0075 133.596 22.4192 134.501 22.847C134.956 23.0622 135.239 23.2568 135.395 23.409ZM97.4927 23.4506C97.4929 23.4506 97.4937 23.4524 97.4947 23.4558C97.4931 23.4524 97.4926 23.4506 97.4927 23.4506ZM97.4927 23.5494C97.4926 23.5494 97.4931 23.5476 97.4947 23.5442C97.4937 23.5476 97.4929 23.5494 97.4927 23.5494ZM135.505 23.5442C135.507 23.5477 135.507 23.5494 135.507 23.5494C135.507 23.5494 135.506 23.5476 135.505 23.5442Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip1_3572_1305)">
<g filter="url(#filter0_b_3572_1305)">
<rect class="absolute-overlay" x="76" y="91" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="76" y="91" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_1305)">
<rect class="percentage-overlay" x="80.5" y="101.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="80.5" y="101.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_1305" x="70" y="85" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_1305"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_1305" result="shape"/>
</filter>
<filter id="filter1_b_3572_1305" x="74.5" y="95.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_1305"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_1305" result="shape"/>
</filter>
<clipPath id="clip0_3572_1305">
<rect width="154" height="138" fill="white" transform="translate(39.5 39.5)"/>
</clipPath>
<clipPath id="clip1_3572_1305">
<rect width="81" height="61" fill="white" transform="translate(76 91)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

56
ui-ngx/src/assets/widget/liquid-level/shapes/vertical-oval.svg

@ -0,0 +1,56 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 233 201" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect class="tb-shape-stroke" opacity="0.1" x="139.5" y="48.5" width="80" height="125" rx="40" fill="#6216FF" fill-opacity="0.02" stroke="#242770" stroke-width="2"/>
<mask id="mask0_3572_10305" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="17" y="51" width="200" height="120">
<mask id="path-2-inside-1_3572_10305" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.5 51.5133C33.5277 52.0432 17.5 68.3999 17.5 88.5V133.5C17.5 153.6 33.5278 169.957 53.5 170.487V170.5H54.476C54.484 170.5 54.492 170.5 54.5 170.5C54.508 170.5 54.516 170.5 54.524 170.5H179.462L179.5 170.5C199.935 170.5 216.5 153.935 216.5 133.5V88.5C216.5 68.0655 199.935 51.5 179.5 51.5L54.5239 51.5C54.5159 51.5 54.508 51.5 54.5 51.5C54.492 51.5 54.4841 51.5 54.4761 51.5L53.5 51.5V51.5133Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.5 51.5133C33.5277 52.0432 17.5 68.3999 17.5 88.5V133.5C17.5 153.6 33.5278 169.957 53.5 170.487V170.5H54.476C54.484 170.5 54.492 170.5 54.5 170.5C54.508 170.5 54.516 170.5 54.524 170.5H179.462L179.5 170.5C199.935 170.5 216.5 153.935 216.5 133.5V88.5C216.5 68.0655 199.935 51.5 179.5 51.5L54.5239 51.5C54.5159 51.5 54.508 51.5 54.5 51.5C54.492 51.5 54.4841 51.5 54.4761 51.5L53.5 51.5V51.5133Z" fill="#7F84FB"/>
<path class="tb-shape-fill" d="M53.5 51.5133L53.553 53.5125L55.5 53.4609V51.5133H53.5ZM53.5 170.487H55.5V168.539L53.553 168.487L53.5 170.487ZM53.5 170.5H51.5V172.5H53.5V170.5ZM179.462 170.5L179.463 168.5H179.462V170.5ZM179.5 170.5L179.499 172.5H179.5V170.5ZM179.5 51.5V49.5V51.5ZM54.5239 51.5L54.5227 53.5H54.5239V51.5ZM54.4761 51.5V53.5L54.4773 53.5L54.4761 51.5ZM53.5 51.5V49.5L51.5 49.5V51.5H53.5ZM19.5 88.5C19.5 69.4867 34.6614 54.0138 53.553 53.5125L53.4469 49.514C32.3941 50.0726 15.5 67.313 15.5 88.5H19.5ZM19.5 133.5V88.5H15.5V133.5H19.5ZM53.553 168.487C34.6614 167.986 19.5 152.513 19.5 133.5H15.5C15.5 154.687 32.3941 171.927 53.447 172.486L53.553 168.487ZM51.5 170.487V170.5H55.5V170.487H51.5ZM53.5 172.5H54.476V168.5H53.5V172.5ZM54.5 168.5C54.4966 168.5 54.493 168.5 54.4889 168.5C54.485 168.5 54.4805 168.5 54.476 168.5V172.5C54.4794 172.5 54.483 172.5 54.487 172.5C54.491 172.5 54.4954 172.5 54.5 172.5V168.5ZM54.524 168.5C54.5195 168.5 54.515 168.5 54.5111 168.5C54.507 168.5 54.5034 168.5 54.5 168.5V172.5C54.5046 172.5 54.509 172.5 54.513 172.5C54.517 172.5 54.5206 172.5 54.524 172.5V168.5ZM54.524 172.5H179.462V168.5H54.524V172.5ZM179.461 172.5L179.499 172.5L179.501 168.5L179.463 168.5L179.461 172.5ZM179.5 172.5C201.039 172.5 218.5 155.039 218.5 133.5H214.5C214.5 152.83 198.83 168.5 179.5 168.5V172.5ZM218.5 133.5V88.5H214.5V133.5H218.5ZM218.5 88.5C218.5 66.9609 201.039 49.5 179.5 49.5V53.5C198.83 53.5 214.5 69.17 214.5 88.5H218.5ZM179.5 49.5L54.5239 49.5V53.5L179.5 53.5V49.5ZM54.5 53.5C54.5075 53.5 54.5151 53.5 54.5227 53.5L54.5252 49.5C54.5168 49.5 54.5084 49.5 54.5 49.5V53.5ZM54.4773 53.5C54.4849 53.5 54.4924 53.5 54.5 53.5V49.5C54.4916 49.5 54.4832 49.5 54.4748 49.5L54.4773 53.5ZM54.4761 49.5H53.5V53.5H54.4761V49.5ZM51.5 51.5V51.5133H55.5V51.5H51.5Z" fill="#242770" mask="url(#path-2-inside-1_3572_10305)"/>
</mask>
<g mask="url(#mask0_3572_10305)">
<rect class="tb-liquid-fill tb-liquid" x="17.5" y="118.5" width="199" height="119" fill="#7A8BFF"/>
<rect class="tb-liquid-surface tb-liquid" x="17.5" y="118.5" width="199" height="1" fill="#7A8BFF"/>
<rect class="tb-liquid-surface" x="17.5" y="118.5" width="199" height="1" fill="white" fill-opacity="0.1"/>
</g>
<mask id="path-6-inside-2_3572_10305" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.5 47.512C31.3183 48.0429 13.5 66.1906 13.5 88.5L13.5 133.5C13.5 155.809 31.3183 173.957 53.5 174.488L53.5 174.5L54.4716 174.5C54.4811 174.5 54.4905 174.5 54.5 174.5C54.5094 174.5 54.5189 174.5 54.5283 174.5L179.472 174.5L179.5 174.5C202.144 174.5 220.5 156.144 220.5 133.5L220.5 88.5C220.5 65.8563 202.144 47.5 179.5 47.5L54.5 47.5L53.5 47.5L53.5 47.512Z"/>
</mask>
<path class="tb-shape-fill" d="M13.5 88.5L11.5 88.5L13.5 88.5ZM53.5 47.512L53.5479 49.5114L55.5 49.4647L55.5 47.512L53.5 47.512ZM13.5 133.5L15.5 133.5L13.5 133.5ZM53.5 174.488L55.5 174.488L55.5 172.535L53.5479 172.489L53.5 174.488ZM53.5 174.5L51.5 174.5L51.5 176.5L53.5 176.5L53.5 174.5ZM54.4716 174.5L54.473 172.5L54.4716 172.5L54.4716 174.5ZM54.5 174.5L54.5 176.5L54.5 174.5ZM54.5283 174.5L54.5283 172.5L54.527 172.5L54.5283 174.5ZM179.472 174.5L179.472 172.5L179.472 174.5ZM179.5 174.5L179.5 172.5L179.5 174.5ZM220.5 133.5L222.5 133.5L220.5 133.5ZM220.5 88.5L218.5 88.5L220.5 88.5ZM179.5 47.5L179.5 45.5L179.5 47.5ZM54.5 47.5L54.5 49.5L54.5 49.5L54.5 47.5ZM53.5 47.5L53.5 45.5L51.5 45.5L51.5 47.5L53.5 47.5ZM15.5 88.5C15.5 67.2792 32.4492 50.0164 53.5479 49.5114L53.4521 45.5125C30.1875 46.0694 11.5 65.1021 11.5 88.5L15.5 88.5ZM15.5 133.5L15.5 88.5L11.5 88.5L11.5 133.5L15.5 133.5ZM53.5479 172.489C32.4492 171.984 15.5 154.721 15.5 133.5L11.5 133.5C11.5 156.898 30.1875 175.931 53.4521 176.487L53.5479 172.489ZM51.5 174.488L51.5 174.5L55.5 174.5L55.5 174.488L51.5 174.488ZM53.5 176.5L54.4716 176.5L54.4716 172.5L53.5 172.5L53.5 176.5ZM54.5 172.5C54.491 172.5 54.482 172.5 54.473 172.5L54.4703 176.5C54.4802 176.5 54.4901 176.5 54.5 176.5L54.5 172.5ZM54.527 172.5C54.518 172.5 54.509 172.5 54.5 172.5L54.5 176.5C54.5099 176.5 54.5198 176.5 54.5297 176.5L54.527 172.5ZM54.5283 176.5L179.472 176.5L179.472 172.5L54.5283 172.5L54.5283 176.5ZM179.472 176.5L179.5 176.5L179.5 172.5L179.472 172.5L179.472 176.5ZM179.5 176.5C203.248 176.5 222.5 157.248 222.5 133.5L218.5 133.5C218.5 155.039 201.039 172.5 179.5 172.5L179.5 176.5ZM222.5 133.5L222.5 88.5L218.5 88.5L218.5 133.5L222.5 133.5ZM222.5 88.5C222.5 64.7518 203.248 45.5 179.5 45.5L179.5 49.5C201.039 49.5 218.5 66.9609 218.5 88.5L222.5 88.5ZM179.5 45.5L54.5 45.5L54.5 49.5L179.5 49.5L179.5 45.5ZM54.5 45.5L53.5 45.5L53.5 49.5L54.5 49.5L54.5 45.5ZM51.5 47.5L51.5 47.512L55.5 47.512L55.5 47.5L51.5 47.5Z" fill="#242770" mask="url(#path-6-inside-2_3572_10305)"/>
<rect class="tb-shape-stroke" x="14.5" y="48.5" width="80" height="125" rx="40" stroke="#242770" stroke-width="2"/>
<mask id="path-9-inside-3_3572_10305" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M141.5 30.5H112.5V48.5C112.5 50.1569 118.992 51.5 127 51.5C135.008 51.5 141.5 50.1569 141.5 48.5V30.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M141.5 30.5H112.5V48.5C112.5 50.1569 118.992 51.5 127 51.5C135.008 51.5 141.5 50.1569 141.5 48.5V30.5Z"/>
<path class="tb-shape-fill" d="M112.5 30.5V28.5H110.5V30.5H112.5ZM141.5 30.5H143.5V28.5H141.5V30.5ZM112.5 32.5H141.5V28.5H112.5V32.5ZM114.5 48.5V30.5H110.5V48.5H114.5ZM127 49.5C123.09 49.5 119.607 49.1707 117.152 48.6628C115.908 48.4055 115.034 48.1232 114.518 47.8712C114.257 47.7431 114.191 47.6721 114.217 47.6982C114.248 47.7304 114.5 48.0048 114.5 48.5H110.5C110.5 49.4095 110.954 50.0882 111.373 50.511C111.785 50.9277 112.289 51.2334 112.761 51.4643C113.713 51.9301 114.962 52.2943 116.342 52.5798C119.135 53.1577 122.902 53.5 127 53.5V49.5ZM139.5 48.5C139.5 48.0048 139.752 47.7304 139.783 47.6982C139.809 47.6721 139.743 47.7431 139.482 47.8712C138.966 48.1232 138.092 48.4055 136.848 48.6628C134.393 49.1707 130.91 49.5 127 49.5V53.5C131.098 53.5 134.865 53.1577 137.658 52.5798C139.038 52.2943 140.287 51.9301 141.239 51.4643C141.711 51.2334 142.215 50.9277 142.627 50.511C143.046 50.0882 143.5 49.4095 143.5 48.5H139.5ZM139.5 30.5V48.5H143.5V30.5H139.5Z" fill="#242770" mask="url(#path-9-inside-3_3572_10305)"/>
<path class="tb-shape-stroke" d="M141.541 31.3323C141.541 31.3323 141.54 31.3332 141.539 31.3349C141.54 31.3332 141.541 31.3323 141.541 31.3323ZM140.866 31.2416C141.077 31.338 141.231 31.4258 141.341 31.5C141.231 31.5742 141.077 31.662 140.866 31.7584C140.187 32.0692 139.141 32.3742 137.77 32.6395C135.045 33.167 131.236 33.5 127 33.5C122.764 33.5 118.955 33.167 116.23 32.6395C114.859 32.3742 113.813 32.0692 113.134 31.7584C112.923 31.662 112.769 31.5742 112.659 31.5C112.769 31.4258 112.923 31.338 113.134 31.2416C113.813 30.9308 114.859 30.6258 116.23 30.3605C118.955 29.833 122.764 29.5 127 29.5C131.236 29.5 135.045 29.833 137.77 30.3605C139.141 30.6258 140.187 30.9308 140.866 31.2416ZM112.459 31.3323C112.46 31.3323 112.46 31.3332 112.461 31.3349C112.46 31.3332 112.459 31.3323 112.459 31.3323ZM112.459 31.6677C112.459 31.6677 112.46 31.6668 112.461 31.6651C112.46 31.6668 112.46 31.6677 112.459 31.6677ZM141.539 31.6651C141.54 31.6668 141.541 31.6677 141.541 31.6677C141.541 31.6677 141.54 31.6668 141.539 31.6651Z" stroke="#242770" stroke-width="2"/>
<path class="tb-shape-stroke" d="M141.541 28.3323C141.541 28.3323 141.54 28.3332 141.539 28.3349C141.54 28.3332 141.541 28.3323 141.541 28.3323ZM140.866 28.2416C141.077 28.338 141.231 28.4258 141.341 28.5C141.231 28.5742 141.077 28.662 140.866 28.7584C140.187 29.0692 139.141 29.3742 137.77 29.6395C135.045 30.167 131.236 30.5 127 30.5C122.764 30.5 118.955 30.167 116.23 29.6395C114.859 29.3742 113.813 29.0692 113.134 28.7584C112.923 28.662 112.769 28.5742 112.659 28.5C112.769 28.4258 112.923 28.338 113.134 28.2416C113.813 27.9308 114.859 27.6258 116.23 27.3605C118.955 26.833 122.764 26.5 127 26.5C131.236 26.5 135.045 26.833 137.77 27.3605C139.141 27.6258 140.187 27.9308 140.866 28.2416ZM112.459 28.3323C112.46 28.3323 112.46 28.3332 112.461 28.3349C112.46 28.3332 112.459 28.3323 112.459 28.3323ZM112.459 28.6677C112.459 28.6677 112.46 28.6668 112.461 28.6651C112.46 28.6668 112.46 28.6677 112.459 28.6677ZM141.539 28.6651C141.54 28.6668 141.541 28.6677 141.541 28.6677C141.541 28.6677 141.54 28.6668 141.539 28.6651Z" stroke="#242770" stroke-width="2"/>
<g class="container-overlay" clip-path="url(#clip0_3572_10305)">
<g filter="url(#filter0_b_3572_10305)">
<rect class="absolute-overlay" x="116" y="81" width="81" height="61" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="absolute-value-container" x="116" y="81" width="81" height="61">
</foreignObject>
</g>
<g filter="url(#filter1_b_3572_10305)">
<rect class="percentage-overlay" x="120.5" y="91.5" width="71" height="40" rx="4" fill="white" fill-opacity="0.76"/>
<foreignObject class="percentage-value-container" x="120.5" y="91.5" width="71" height="40">
</foreignObject>
</g>
</g>
<defs>
<filter id="filter0_b_3572_10305" x="110" y="75" width="93" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_10305"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_10305" result="shape"/>
</filter>
<filter id="filter1_b_3572_10305" x="114.5" y="85.5" width="83" height="52" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feGaussianBlur in="BackgroundImageFix" stdDeviation="3"/>
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_3572_10305"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_3572_10305" result="shape"/>
</filter>
<clipPath id="clip0_3572_10305">
<rect width="81" height="61" fill="white" transform="translate(116 81)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

12
ui-ngx/src/form.scss

@ -368,14 +368,20 @@
.mat-mdc-text-field-wrapper {
.mat-mdc-form-field-icon-suffix {
padding: 0;
display: none;
display: flex;
align-items: center;
> *:not(.tb-suffix-show-always) {
display: none;
}
}
}
&:hover {
.mat-mdc-text-field-wrapper {
.mat-mdc-form-field-icon-suffix {
display: flex;
align-items: center;
> * {
display: block;
}
}
}
}

13
ui-ngx/src/styles.scss

@ -342,6 +342,12 @@ mat-label {
}
}
mat-icon {
&.tb-error {
color: rgb(221, 44, 0);
}
}
.tb-error-messages {
height: 24px; //30px
margin-top: -6px;
@ -356,6 +362,13 @@ mat-label {
color: rgb(221, 44, 0);
}
.mat-mdc-tooltip.tb-error-tooltip {
.mdc-tooltip__surface {
background-color: rgba(209, 39, 48, 0.12);
color: rgba(209, 39, 48, 1);
}
}
.tb-autocomplete {
.mat-mdc-option {
border-bottom: 1px solid #eee;

Loading…
Cancel
Save