UI: Implement units select component.
This commit is contained in:
		
							parent
							
								
									1d3a9a25b3
								
							
						
					
					
						commit
						4827589c48
					
				@ -180,6 +180,7 @@ import * as SlackConversationAutocompleteComponent from '@shared/components/slac
 | 
			
		||||
import * as StringItemsListComponent from '@shared/components/string-items-list.component';
 | 
			
		||||
import * as ToggleHeaderComponent from '@shared/components/toggle-header.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 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/toggle-header.component': ToggleHeaderComponent,
 | 
			
		||||
    '@shared/components/toggle-select.component': ToggleSelectComponent,
 | 
			
		||||
    '@shared/components/unit-input.component': UnitInputComponent,
 | 
			
		||||
 | 
			
		||||
    '@home/components/entity/add-entity-dialog.component': AddEntityDialogComponent,
 | 
			
		||||
    '@home/components/entity/entities-table.component': EntitiesTableComponent,
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@
 | 
			
		||||
                    #entityAliasAutocomplete="matAutocomplete"
 | 
			
		||||
                    [displayWith]="displayEntityAliasFn">
 | 
			
		||||
    <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 *ngIf="!(filteredEntityAliases | async)?.length" [value]="null" class="tb-not-found">
 | 
			
		||||
      <div class="tb-not-found-content" (click)="$event.stopPropagation()">
 | 
			
		||||
 | 
			
		||||
@ -51,9 +51,9 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="tb-form-row space-between">
 | 
			
		||||
      <div translate>widget-config.units-short</div>
 | 
			
		||||
      <tb-widget-units
 | 
			
		||||
      <tb-unit-input
 | 
			
		||||
        formControlName="units">
 | 
			
		||||
      </tb-widget-units>
 | 
			
		||||
      </tb-unit-input>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="tb-form-row space-between">
 | 
			
		||||
      <div translate>widget-config.decimals-short</div>
 | 
			
		||||
 | 
			
		||||
@ -148,9 +148,9 @@
 | 
			
		||||
    </tb-color-input>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div *ngIf="!hideUnits" class="tb-units-field">
 | 
			
		||||
    <tb-widget-units *ngIf="displayUnitsOrDigits"
 | 
			
		||||
    <tb-unit-input *ngIf="displayUnitsOrDigits"
 | 
			
		||||
      formControlName="units">
 | 
			
		||||
    </tb-widget-units>
 | 
			
		||||
    </tb-unit-input>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div *ngIf="!hideDecimals" class="tb-decimals-field">
 | 
			
		||||
    <mat-form-field *ngIf="displayUnitsOrDigits" appearance="outline" class="tb-inline-field number" subscriptSizing="dynamic">
 | 
			
		||||
 | 
			
		||||
@ -48,12 +48,19 @@
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-color-field, .tb-units-field, .tb-decimals-field {
 | 
			
		||||
    width: 60px;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    place-content: center;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-units-field {
 | 
			
		||||
    width: 80px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-color-field, .tb-decimals-field {
 | 
			
		||||
    width: 60px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tb-data-keys-table-row-buttons {
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,10 @@
 | 
			
		||||
      &.tb-source-header {
 | 
			
		||||
        width: 140px;
 | 
			
		||||
      }
 | 
			
		||||
      &.tb-color-header, &.tb-units-header, &.tb-decimals-header {
 | 
			
		||||
      &.tb-units-header {
 | 
			
		||||
        width: 80px;
 | 
			
		||||
      }
 | 
			
		||||
      &.tb-color-header, &.tb-decimals-header {
 | 
			
		||||
        width: 60px;
 | 
			
		||||
      }
 | 
			
		||||
      &.tb-actions-header {
 | 
			
		||||
 | 
			
		||||
@ -48,9 +48,9 @@
 | 
			
		||||
      <ng-container *ngIf="modelValue.type !== dataKeyTypes.alarm">
 | 
			
		||||
        <div class="tb-form-row space-between" *ngIf="!hideDataKeyUnits">
 | 
			
		||||
          <div translate>widget-config.units-short</div>
 | 
			
		||||
          <tb-widget-units
 | 
			
		||||
          <tb-unit-input
 | 
			
		||||
            formControlName="units">
 | 
			
		||||
          </tb-widget-units>
 | 
			
		||||
          </tb-unit-input>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="tb-form-row space-between" *ngIf="!hideDataKeyDecimals">
 | 
			
		||||
          <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 { WidgetSettingsComponent } from '@home/components/widget/config/widget-settings.component';
 | 
			
		||||
import { TimewindowConfigPanelComponent } from '@home/components/widget/config/timewindow-config-panel.component';
 | 
			
		||||
import { WidgetUnitsComponent } from '@home/components/widget/config/widget-units.component';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations:
 | 
			
		||||
@ -44,7 +43,6 @@ import { WidgetUnitsComponent } from '@home/components/widget/config/widget-unit
 | 
			
		||||
      EntityAliasSelectComponent,
 | 
			
		||||
      FilterSelectComponent,
 | 
			
		||||
      TimewindowConfigPanelComponent,
 | 
			
		||||
      WidgetUnitsComponent,
 | 
			
		||||
      WidgetSettingsComponent
 | 
			
		||||
    ],
 | 
			
		||||
  imports: [
 | 
			
		||||
@ -63,7 +61,6 @@ import { WidgetUnitsComponent } from '@home/components/widget/config/widget-unit
 | 
			
		||||
    EntityAliasSelectComponent,
 | 
			
		||||
    FilterSelectComponent,
 | 
			
		||||
    TimewindowConfigPanelComponent,
 | 
			
		||||
    WidgetUnitsComponent,
 | 
			
		||||
    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 *ngIf="displayUnitsConfig" class="tb-form-row space-between">
 | 
			
		||||
        <div translate>widget-config.units</div>
 | 
			
		||||
        <tb-widget-units
 | 
			
		||||
        <tb-unit-input
 | 
			
		||||
          formControlName="units">
 | 
			
		||||
        </tb-widget-units>
 | 
			
		||||
        </tb-unit-input>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div *ngIf="displayUnitsConfig" class="tb-form-row space-between">
 | 
			
		||||
        <div translate>widget-config.decimals</div>
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@
 | 
			
		||||
                    #entityAutocomplete="matAutocomplete"
 | 
			
		||||
                    [displayWith]="displayEntityFn">
 | 
			
		||||
    <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 *ngIf="!(filteredEntities | async)?.length" [value]="null">
 | 
			
		||||
      <span>
 | 
			
		||||
 | 
			
		||||
@ -25,3 +25,4 @@ export * from './notification/template-autocomplete.component';
 | 
			
		||||
export * from './resource/resource-autocomplete.component';
 | 
			
		||||
export * from './toggle-header.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' })
 | 
			
		||||
export class HighlightPipe implements PipeTransform {
 | 
			
		||||
  transform(text: string, search): string {
 | 
			
		||||
  transform(text: string, search: string, includes = false, flags = 'i'): string {
 | 
			
		||||
    const pattern = search
 | 
			
		||||
      .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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -194,6 +194,7 @@ import { ShortNumberPipe } from '@shared/pipe/short-number.pipe';
 | 
			
		||||
import { ToggleHeaderComponent, ToggleOption } from '@shared/components/toggle-header.component';
 | 
			
		||||
import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-chain-select.component';
 | 
			
		||||
import { ToggleSelectComponent } from '@shared/components/toggle-select.component';
 | 
			
		||||
import { UnitInputComponent } from '@shared/components/unit-input.component';
 | 
			
		||||
 | 
			
		||||
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
 | 
			
		||||
  return markedOptionsService;
 | 
			
		||||
@ -367,6 +368,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
 | 
			
		||||
    ToggleHeaderComponent,
 | 
			
		||||
    ToggleOption,
 | 
			
		||||
    ToggleSelectComponent,
 | 
			
		||||
    UnitInputComponent,
 | 
			
		||||
    RuleChainSelectComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
@ -597,6 +599,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
 | 
			
		||||
    ToggleHeaderComponent,
 | 
			
		||||
    ToggleOption,
 | 
			
		||||
    ToggleSelectComponent,
 | 
			
		||||
    UnitInputComponent,
 | 
			
		||||
    RuleChainSelectComponent
 | 
			
		||||
  ]
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -3860,6 +3860,15 @@
 | 
			
		||||
        "just-now": "Just now",
 | 
			
		||||
        "ago": "ago"
 | 
			
		||||
    },
 | 
			
		||||
    "unit": {
 | 
			
		||||
        "celsius": "Celsius",
 | 
			
		||||
        "kelvin": "Kelvin",
 | 
			
		||||
        "fahrenheit": "Fahrenheit",
 | 
			
		||||
        "percentage": "Percentage",
 | 
			
		||||
        "second": "Second",
 | 
			
		||||
        "minute": "Minute",
 | 
			
		||||
        "hour": "Hour"
 | 
			
		||||
    },
 | 
			
		||||
    "user": {
 | 
			
		||||
        "user": "User",
 | 
			
		||||
        "users": "Users",
 | 
			
		||||
 | 
			
		||||
@ -177,9 +177,15 @@
 | 
			
		||||
        opacity: 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    &:not(.mat-mdc-form-field-has-icon-suffix) {
 | 
			
		||||
      .mat-mdc-text-field-wrapper {
 | 
			
		||||
        &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
 | 
			
		||||
          padding-right: 12px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    .mat-mdc-text-field-wrapper {
 | 
			
		||||
      &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
 | 
			
		||||
        padding-right: 12px;
 | 
			
		||||
        padding-left: 12px;
 | 
			
		||||
        &:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) {
 | 
			
		||||
          .mdc-notched-outline__leading, .mdc-notched-outline__trailing {
 | 
			
		||||
@ -233,7 +239,9 @@
 | 
			
		||||
    }
 | 
			
		||||
    &.number {
 | 
			
		||||
      .mat-mdc-text-field-wrapper {
 | 
			
		||||
        padding-right: 4px;
 | 
			
		||||
        &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
 | 
			
		||||
          padding-right: 4px;
 | 
			
		||||
        }
 | 
			
		||||
        .mat-mdc-form-field-infix {
 | 
			
		||||
          input.mdc-text-field__input[type=number]::-webkit-inner-spin-button,
 | 
			
		||||
          input.mdc-text-field__input[type=number]::-webkit-outer-spin-button {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user