Merge branch 'develop/3.5.2' into improvement/move-form-table-style
This commit is contained in:
commit
afcebf2eda
@ -180,6 +180,7 @@ import * as SlackConversationAutocompleteComponent from '@shared/components/slac
|
|||||||
import * as StringItemsListComponent from '@shared/components/string-items-list.component';
|
import * as StringItemsListComponent from '@shared/components/string-items-list.component';
|
||||||
import * as ToggleHeaderComponent from '@shared/components/toggle-header.component';
|
import * as ToggleHeaderComponent from '@shared/components/toggle-header.component';
|
||||||
import * as ToggleSelectComponent from '@shared/components/toggle-select.component';
|
import * as ToggleSelectComponent from '@shared/components/toggle-select.component';
|
||||||
|
import * as UnitInputComponent from '@shared/components/unit-input.component';
|
||||||
|
|
||||||
import * as AddEntityDialogComponent from '@home/components/entity/add-entity-dialog.component';
|
import * as AddEntityDialogComponent from '@home/components/entity/add-entity-dialog.component';
|
||||||
import * as EntitiesTableComponent from '@home/components/entity/entities-table.component';
|
import * as EntitiesTableComponent from '@home/components/entity/entities-table.component';
|
||||||
@ -480,6 +481,7 @@ class ModulesMap implements IModulesMap {
|
|||||||
'@shared/components/string-items-list.component': StringItemsListComponent,
|
'@shared/components/string-items-list.component': StringItemsListComponent,
|
||||||
'@shared/components/toggle-header.component': ToggleHeaderComponent,
|
'@shared/components/toggle-header.component': ToggleHeaderComponent,
|
||||||
'@shared/components/toggle-select.component': ToggleSelectComponent,
|
'@shared/components/toggle-select.component': ToggleSelectComponent,
|
||||||
|
'@shared/components/unit-input.component': UnitInputComponent,
|
||||||
|
|
||||||
'@home/components/entity/add-entity-dialog.component': AddEntityDialogComponent,
|
'@home/components/entity/add-entity-dialog.component': AddEntityDialogComponent,
|
||||||
'@home/components/entity/entities-table.component': EntitiesTableComponent,
|
'@home/components/entity/entities-table.component': EntitiesTableComponent,
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
#entityAliasAutocomplete="matAutocomplete"
|
#entityAliasAutocomplete="matAutocomplete"
|
||||||
[displayWith]="displayEntityAliasFn">
|
[displayWith]="displayEntityAliasFn">
|
||||||
<mat-option *ngFor="let entityAlias of filteredEntityAliases | async" [value]="entityAlias">
|
<mat-option *ngFor="let entityAlias of filteredEntityAliases | async" [value]="entityAlias">
|
||||||
<span [innerHTML]="entityAlias.alias | highlight:searchText"></span>
|
<span [innerHTML]="entityAlias.alias | highlight:searchText:true:'ig'"></span>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
<mat-option *ngIf="!(filteredEntityAliases | async)?.length" [value]="null" class="tb-not-found">
|
<mat-option *ngIf="!(filteredEntityAliases | async)?.length" [value]="null" class="tb-not-found">
|
||||||
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
|
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
|
||||||
|
|||||||
@ -51,9 +51,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tb-form-row space-between">
|
<div class="tb-form-row space-between">
|
||||||
<div translate>widget-config.units-short</div>
|
<div translate>widget-config.units-short</div>
|
||||||
<tb-widget-units
|
<tb-unit-input
|
||||||
formControlName="units">
|
formControlName="units">
|
||||||
</tb-widget-units>
|
</tb-unit-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-row space-between">
|
<div class="tb-form-row space-between">
|
||||||
<div translate>widget-config.decimals-short</div>
|
<div translate>widget-config.decimals-short</div>
|
||||||
|
|||||||
@ -148,9 +148,9 @@
|
|||||||
</tb-color-input>
|
</tb-color-input>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!hideUnits" class="tb-units-field">
|
<div *ngIf="!hideUnits" class="tb-units-field">
|
||||||
<tb-widget-units *ngIf="displayUnitsOrDigits"
|
<tb-unit-input *ngIf="displayUnitsOrDigits"
|
||||||
formControlName="units">
|
formControlName="units">
|
||||||
</tb-widget-units>
|
</tb-unit-input>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!hideDecimals" class="tb-decimals-field">
|
<div *ngIf="!hideDecimals" class="tb-decimals-field">
|
||||||
<mat-form-field *ngIf="displayUnitsOrDigits" appearance="outline" class="tb-inline-field number" subscriptSizing="dynamic">
|
<mat-form-field *ngIf="displayUnitsOrDigits" appearance="outline" class="tb-inline-field number" subscriptSizing="dynamic">
|
||||||
|
|||||||
@ -42,10 +42,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tb-color-field, .tb-units-field, .tb-decimals-field {
|
.tb-color-field, .tb-units-field, .tb-decimals-field {
|
||||||
width: 60px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
place-content: center;
|
place-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tb-units-field {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-color-field, .tb-decimals-field {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,10 @@
|
|||||||
&.tb-source-header {
|
&.tb-source-header {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
}
|
}
|
||||||
&.tb-color-header, &.tb-units-header, &.tb-decimals-header {
|
&.tb-units-header {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
&.tb-color-header, &.tb-decimals-header {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
}
|
}
|
||||||
&.tb-actions-header {
|
&.tb-actions-header {
|
||||||
|
|||||||
@ -48,9 +48,9 @@
|
|||||||
<ng-container *ngIf="modelValue.type !== dataKeyTypes.alarm">
|
<ng-container *ngIf="modelValue.type !== dataKeyTypes.alarm">
|
||||||
<div class="tb-form-row space-between" *ngIf="!hideDataKeyUnits">
|
<div class="tb-form-row space-between" *ngIf="!hideDataKeyUnits">
|
||||||
<div translate>widget-config.units-short</div>
|
<div translate>widget-config.units-short</div>
|
||||||
<tb-widget-units
|
<tb-unit-input
|
||||||
formControlName="units">
|
formControlName="units">
|
||||||
</tb-widget-units>
|
</tb-unit-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-row space-between" *ngIf="!hideDataKeyDecimals">
|
<div class="tb-form-row space-between" *ngIf="!hideDataKeyDecimals">
|
||||||
<div translate>widget-config.decimals-short</div>
|
<div translate>widget-config.decimals-short</div>
|
||||||
|
|||||||
@ -29,7 +29,6 @@ import { FilterSelectComponent } from '@home/components/filter/filter-select.com
|
|||||||
import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module';
|
import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module';
|
||||||
import { WidgetSettingsComponent } from '@home/components/widget/config/widget-settings.component';
|
import { WidgetSettingsComponent } from '@home/components/widget/config/widget-settings.component';
|
||||||
import { TimewindowConfigPanelComponent } from '@home/components/widget/config/timewindow-config-panel.component';
|
import { TimewindowConfigPanelComponent } from '@home/components/widget/config/timewindow-config-panel.component';
|
||||||
import { WidgetUnitsComponent } from '@home/components/widget/config/widget-units.component';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations:
|
declarations:
|
||||||
@ -44,7 +43,6 @@ import { WidgetUnitsComponent } from '@home/components/widget/config/widget-unit
|
|||||||
EntityAliasSelectComponent,
|
EntityAliasSelectComponent,
|
||||||
FilterSelectComponent,
|
FilterSelectComponent,
|
||||||
TimewindowConfigPanelComponent,
|
TimewindowConfigPanelComponent,
|
||||||
WidgetUnitsComponent,
|
|
||||||
WidgetSettingsComponent
|
WidgetSettingsComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
@ -63,7 +61,6 @@ import { WidgetUnitsComponent } from '@home/components/widget/config/widget-unit
|
|||||||
EntityAliasSelectComponent,
|
EntityAliasSelectComponent,
|
||||||
FilterSelectComponent,
|
FilterSelectComponent,
|
||||||
TimewindowConfigPanelComponent,
|
TimewindowConfigPanelComponent,
|
||||||
WidgetUnitsComponent,
|
|
||||||
WidgetSettingsComponent
|
WidgetSettingsComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
<!--
|
|
||||||
|
|
||||||
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="outline" class="tb-inline-field" subscriptSizing="dynamic">
|
|
||||||
<input matInput [formControl]="unitsFormControl" placeholder="{{ 'widget-config.set' | translate }}">
|
|
||||||
</mat-form-field>
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
///
|
|
||||||
/// 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, forwardRef, Input, OnInit } from '@angular/core';
|
|
||||||
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, UntypedFormBuilder } from '@angular/forms';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'tb-widget-units',
|
|
||||||
templateUrl: './widget-units.component.html',
|
|
||||||
styleUrls: [],
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: NG_VALUE_ACCESSOR,
|
|
||||||
useExisting: forwardRef(() => WidgetUnitsComponent),
|
|
||||||
multi: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class WidgetUnitsComponent implements ControlValueAccessor, OnInit {
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
disabled: boolean;
|
|
||||||
|
|
||||||
unitsFormControl: FormControl;
|
|
||||||
|
|
||||||
private propagateChange = (_val: any) => {};
|
|
||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.unitsFormControl = this.fb.control('', []);
|
|
||||||
this.unitsFormControl.valueChanges.subscribe(val => this.propagateChange(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
writeValue(units?: string): void {
|
|
||||||
this.unitsFormControl.patchValue(units, {emitEvent: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
registerOnChange(fn: any): void {
|
|
||||||
this.propagateChange = fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerOnTouched(fn: any): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
setDisabledState(isDisabled: boolean): void {
|
|
||||||
this.disabled = isDisabled;
|
|
||||||
if (this.disabled) {
|
|
||||||
this.unitsFormControl.disable({emitEvent: false});
|
|
||||||
} else {
|
|
||||||
this.unitsFormControl.enable({emitEvent: false});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -267,9 +267,9 @@
|
|||||||
<div class="tb-form-panel-title" translate>widget-config.data-settings</div>
|
<div class="tb-form-panel-title" translate>widget-config.data-settings</div>
|
||||||
<div *ngIf="displayUnitsConfig" class="tb-form-row space-between">
|
<div *ngIf="displayUnitsConfig" class="tb-form-row space-between">
|
||||||
<div translate>widget-config.units</div>
|
<div translate>widget-config.units</div>
|
||||||
<tb-widget-units
|
<tb-unit-input
|
||||||
formControlName="units">
|
formControlName="units">
|
||||||
</tb-widget-units>
|
</tb-unit-input>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="displayUnitsConfig" class="tb-form-row space-between">
|
<div *ngIf="displayUnitsConfig" class="tb-form-row space-between">
|
||||||
<div translate>widget-config.decimals</div>
|
<div translate>widget-config.decimals</div>
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
#entityAutocomplete="matAutocomplete"
|
#entityAutocomplete="matAutocomplete"
|
||||||
[displayWith]="displayEntityFn">
|
[displayWith]="displayEntityFn">
|
||||||
<mat-option *ngFor="let entity of filteredEntities | async" [value]="entity">
|
<mat-option *ngFor="let entity of filteredEntities | async" [value]="entity">
|
||||||
<span [innerHTML]="entity.name | highlight:searchText"></span>
|
<span [innerHTML]="entity.name | highlight:searchText:true:'ig'"></span>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
<mat-option *ngIf="!(filteredEntities | async)?.length" [value]="null">
|
<mat-option *ngIf="!(filteredEntities | async)?.length" [value]="null">
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@ -25,3 +25,4 @@ export * from './notification/template-autocomplete.component';
|
|||||||
export * from './resource/resource-autocomplete.component';
|
export * from './resource/resource-autocomplete.component';
|
||||||
export * from './toggle-header.component';
|
export * from './toggle-header.component';
|
||||||
export * from './toggle-select.component';
|
export * from './toggle-select.component';
|
||||||
|
export * from './unit-input.component';
|
||||||
|
|||||||
39
ui-ngx/src/app/shared/components/unit-input.component.html
Normal file
39
ui-ngx/src/app/shared/components/unit-input.component.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
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="outline" class="tb-inline-field" subscriptSizing="dynamic">
|
||||||
|
<input matInput #unitInput [formControl]="unitsFormControl"
|
||||||
|
placeholder="{{ 'widget-config.set' | translate }}"
|
||||||
|
(focusin)="onFocus()"
|
||||||
|
[matAutocomplete]="unitsAutocomplete">
|
||||||
|
<button *ngIf="unitsFormControl.value && !disabled"
|
||||||
|
type="button"
|
||||||
|
matSuffix mat-icon-button aria-label="Clear"
|
||||||
|
(click)="clear()">
|
||||||
|
<mat-icon class="material-icons">close</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-autocomplete
|
||||||
|
#unitsAutocomplete="matAutocomplete"
|
||||||
|
class="tb-autocomplete tb-unit-input-autocomplete"
|
||||||
|
panelWidth="fit-content"
|
||||||
|
[displayWith]="displayUnitFn">
|
||||||
|
<mat-option *ngFor="let unit of filteredUnits | async" [value]="unit">
|
||||||
|
<span class="tb-unit-name" fxFlex [innerHTML]="unit.name | highlight:searchText:true:'ig'"></span>
|
||||||
|
<span class="tb-unit-symbol" [innerHTML]="unit.symbol | highlight:searchText:true:'ig'"></span>
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
</mat-form-field>
|
||||||
40
ui-ngx/src/app/shared/components/unit-input.component.scss
Normal file
40
ui-ngx/src/app/shared/components/unit-input.component.scss
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
.tb-autocomplete.tb-unit-input-autocomplete {
|
||||||
|
.mat-mdc-option {
|
||||||
|
border-bottom: none;
|
||||||
|
.mdc-list-item__primary-text {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
.tb-unit-name, .tb-unit-symbol {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
||||||
|
.tb-unit-symbol {
|
||||||
|
color: rgba(0, 0, 0, 0.38);
|
||||||
|
min-width: 22px;
|
||||||
|
text-align: end;
|
||||||
|
b {
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
148
ui-ngx/src/app/shared/components/unit-input.component.ts
Normal file
148
ui-ngx/src/app/shared/components/unit-input.component.ts
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
///
|
||||||
|
/// 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, ElementRef, forwardRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, UntypedFormBuilder } from '@angular/forms';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { searchUnits, Unit, unitBySymbol, units } from '@shared/models/unit.models';
|
||||||
|
import { map, mergeMap, startWith, tap } from 'rxjs/operators';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-unit-input',
|
||||||
|
templateUrl: './unit-input.component.html',
|
||||||
|
styleUrls: ['./unit-input.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => UnitInputComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class UnitInputComponent implements ControlValueAccessor, OnInit {
|
||||||
|
|
||||||
|
unitsFormControl: FormControl;
|
||||||
|
|
||||||
|
modelValue: string | null;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@ViewChild('unitInput', {static: true}) unitInput: ElementRef;
|
||||||
|
|
||||||
|
filteredUnits: Observable<Array<Unit | string>>;
|
||||||
|
|
||||||
|
searchText = '';
|
||||||
|
|
||||||
|
private dirty = false;
|
||||||
|
|
||||||
|
private translatedUnits: Array<Unit> = units.map(u => ({symbol: u.symbol,
|
||||||
|
name: this.translate.instant(u.name),
|
||||||
|
tags: u.tags}));
|
||||||
|
|
||||||
|
private propagateChange = (_val: any) => {};
|
||||||
|
|
||||||
|
constructor(private fb: UntypedFormBuilder,
|
||||||
|
private translate: TranslateService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.unitsFormControl = this.fb.control('', []);
|
||||||
|
this.filteredUnits = this.unitsFormControl.valueChanges
|
||||||
|
.pipe(
|
||||||
|
tap(value => {
|
||||||
|
this.updateView(value);
|
||||||
|
}),
|
||||||
|
startWith<string | Unit>(''),
|
||||||
|
map(value => (value as Unit)?.symbol ? (value as Unit).symbol : (value ? value as string : '')),
|
||||||
|
mergeMap(symbol => this.fetchUnits(symbol) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(symbol?: string): void {
|
||||||
|
this.searchText = '';
|
||||||
|
this.modelValue = symbol;
|
||||||
|
let res: Unit | string = null;
|
||||||
|
if (symbol) {
|
||||||
|
const unit = unitBySymbol(symbol);
|
||||||
|
res = unit ? unit : symbol;
|
||||||
|
}
|
||||||
|
this.unitsFormControl.patchValue(res, {emitEvent: false});
|
||||||
|
this.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus() {
|
||||||
|
if (this.dirty) {
|
||||||
|
this.unitsFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true});
|
||||||
|
this.dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateView(value: Unit | string | null) {
|
||||||
|
const res: string = (value as Unit)?.symbol ? (value as Unit)?.symbol : (value as string);
|
||||||
|
if (this.modelValue !== res) {
|
||||||
|
this.modelValue = res;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayUnitFn(unit?: Unit | string): string | undefined {
|
||||||
|
if (unit) {
|
||||||
|
if ((unit as Unit).symbol) {
|
||||||
|
return (unit as Unit).symbol;
|
||||||
|
} else {
|
||||||
|
return unit as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchUnits(searchText?: string): Observable<Array<Unit | string>> {
|
||||||
|
this.searchText = searchText;
|
||||||
|
const result = searchUnits(this.translatedUnits, searchText);
|
||||||
|
if (result.length) {
|
||||||
|
return of(result);
|
||||||
|
} else {
|
||||||
|
return of([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
if (this.disabled) {
|
||||||
|
this.unitsFormControl.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.unitsFormControl.enable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.unitsFormControl.patchValue(null, {emitEvent: true});
|
||||||
|
setTimeout(() => {
|
||||||
|
this.unitInput.nativeElement.blur();
|
||||||
|
this.unitInput.nativeElement.focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
ui-ngx/src/app/shared/models/unit.models.ts
Normal file
70
ui-ngx/src/app/shared/models/unit.models.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
|
||||||
|
export interface Unit {
|
||||||
|
name: string;
|
||||||
|
symbol: string;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const units: Array<Unit> = [
|
||||||
|
{
|
||||||
|
name: 'unit.celsius',
|
||||||
|
symbol: '°C',
|
||||||
|
tags: ['temperature']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unit.kelvin',
|
||||||
|
symbol: 'K',
|
||||||
|
tags: ['temperature']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unit.fahrenheit',
|
||||||
|
symbol: '°F',
|
||||||
|
tags: ['temperature']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unit.percentage',
|
||||||
|
symbol: '%',
|
||||||
|
tags: ['percentage']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unit.second',
|
||||||
|
symbol: 's',
|
||||||
|
tags: ['time']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unit.minute',
|
||||||
|
symbol: 'min',
|
||||||
|
tags: ['time']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unit.hour',
|
||||||
|
symbol: 'h',
|
||||||
|
tags: ['time']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const unitBySymbol = (symbol: string): Unit => units.find(u => u.symbol === symbol);
|
||||||
|
|
||||||
|
const searchUnitTags = (unit: Unit, searchText: string): boolean =>
|
||||||
|
!!unit.tags.find(t => t.toUpperCase().includes(searchText.toUpperCase()));
|
||||||
|
|
||||||
|
export const searchUnits = (_units: Array<Unit>, searchText: string): Array<Unit> => _units.filter(
|
||||||
|
u => u.symbol.toUpperCase().includes(searchText.toUpperCase()) ||
|
||||||
|
u.name.toUpperCase().includes(searchText.toUpperCase()) ||
|
||||||
|
searchUnitTags(u, searchText)
|
||||||
|
);
|
||||||
@ -18,11 +18,10 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||||||
|
|
||||||
@Pipe({ name: 'highlight' })
|
@Pipe({ name: 'highlight' })
|
||||||
export class HighlightPipe implements PipeTransform {
|
export class HighlightPipe implements PipeTransform {
|
||||||
transform(text: string, search): string {
|
transform(text: string, search: string, includes = false, flags = 'i'): string {
|
||||||
const pattern = search
|
const pattern = search
|
||||||
.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
||||||
const regex = new RegExp('^' + pattern, 'i');
|
const regex = new RegExp((!includes ? '^' : '') + pattern, flags);
|
||||||
|
|
||||||
return search ? text.replace(regex, match => `<b>${match}</b>`) : text;
|
return search ? text.replace(regex, match => `<b>${match}</b>`) : text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -194,6 +194,7 @@ import { ShortNumberPipe } from '@shared/pipe/short-number.pipe';
|
|||||||
import { ToggleHeaderComponent, ToggleOption } from '@shared/components/toggle-header.component';
|
import { ToggleHeaderComponent, ToggleOption } from '@shared/components/toggle-header.component';
|
||||||
import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-chain-select.component';
|
import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-chain-select.component';
|
||||||
import { ToggleSelectComponent } from '@shared/components/toggle-select.component';
|
import { ToggleSelectComponent } from '@shared/components/toggle-select.component';
|
||||||
|
import { UnitInputComponent } from '@shared/components/unit-input.component';
|
||||||
|
|
||||||
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
||||||
return markedOptionsService;
|
return markedOptionsService;
|
||||||
@ -367,6 +368,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
|||||||
ToggleHeaderComponent,
|
ToggleHeaderComponent,
|
||||||
ToggleOption,
|
ToggleOption,
|
||||||
ToggleSelectComponent,
|
ToggleSelectComponent,
|
||||||
|
UnitInputComponent,
|
||||||
RuleChainSelectComponent
|
RuleChainSelectComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
@ -597,6 +599,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
|||||||
ToggleHeaderComponent,
|
ToggleHeaderComponent,
|
||||||
ToggleOption,
|
ToggleOption,
|
||||||
ToggleSelectComponent,
|
ToggleSelectComponent,
|
||||||
|
UnitInputComponent,
|
||||||
RuleChainSelectComponent
|
RuleChainSelectComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -3860,6 +3860,15 @@
|
|||||||
"just-now": "Just now",
|
"just-now": "Just now",
|
||||||
"ago": "ago"
|
"ago": "ago"
|
||||||
},
|
},
|
||||||
|
"unit": {
|
||||||
|
"celsius": "Celsius",
|
||||||
|
"kelvin": "Kelvin",
|
||||||
|
"fahrenheit": "Fahrenheit",
|
||||||
|
"percentage": "Percentage",
|
||||||
|
"second": "Second",
|
||||||
|
"minute": "Minute",
|
||||||
|
"hour": "Hour"
|
||||||
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
|
|||||||
@ -177,9 +177,15 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&:not(.mat-mdc-form-field-has-icon-suffix) {
|
||||||
.mat-mdc-text-field-wrapper {
|
.mat-mdc-text-field-wrapper {
|
||||||
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mat-mdc-text-field-wrapper {
|
||||||
|
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
&:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) {
|
&:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) {
|
||||||
.mdc-notched-outline__leading, .mdc-notched-outline__trailing {
|
.mdc-notched-outline__leading, .mdc-notched-outline__trailing {
|
||||||
@ -233,7 +239,9 @@
|
|||||||
}
|
}
|
||||||
&.number {
|
&.number {
|
||||||
.mat-mdc-text-field-wrapper {
|
.mat-mdc-text-field-wrapper {
|
||||||
|
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
|
}
|
||||||
.mat-mdc-form-field-infix {
|
.mat-mdc-form-field-infix {
|
||||||
input.mdc-text-field__input[type=number]::-webkit-inner-spin-button,
|
input.mdc-text-field__input[type=number]::-webkit-inner-spin-button,
|
||||||
input.mdc-text-field__input[type=number]::-webkit-outer-spin-button {
|
input.mdc-text-field__input[type=number]::-webkit-outer-spin-button {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user