UI: Refactoring after review
This commit is contained in:
parent
be4407c3ca
commit
8b14e72f8c
@ -33,7 +33,6 @@ import { svgIcons, svgIconsUrl } from '@shared/models/icon.models';
|
|||||||
import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions';
|
import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions';
|
||||||
import { SETTINGS_KEY } from '@core/settings/settings.effects';
|
import { SETTINGS_KEY } from '@core/settings/settings.effects';
|
||||||
import { initCustomJQueryEvents } from '@shared/models/jquery-event.models';
|
import { initCustomJQueryEvents } from '@shared/models/jquery-event.models';
|
||||||
import { UnitService } from '@core/services/unit.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-root',
|
selector: 'tb-root',
|
||||||
@ -47,8 +46,7 @@ export class AppComponent {
|
|||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private matIconRegistry: MatIconRegistry,
|
private matIconRegistry: MatIconRegistry,
|
||||||
private domSanitizer: DomSanitizer,
|
private domSanitizer: DomSanitizer,
|
||||||
private authService: AuthService,
|
private authService: AuthService) {
|
||||||
private unitService: UnitService) {
|
|
||||||
|
|
||||||
console.log(`ThingsBoard Version: ${env.tbVersion}`);
|
console.log(`ThingsBoard Version: ${env.tbVersion}`);
|
||||||
|
|
||||||
@ -96,14 +94,12 @@ export class AppComponent {
|
|||||||
this.store.select(selectUserReady).pipe(
|
this.store.select(selectUserReady).pipe(
|
||||||
filter((data) => data.isUserLoaded),
|
filter((data) => data.isUserLoaded),
|
||||||
tap((data) => {
|
tap((data) => {
|
||||||
const userDetails = getCurrentAuthState(this.store).userDetails;
|
let userLang = getCurrentAuthState(this.store).userDetails?.additionalInfo?.lang ?? null;
|
||||||
let userLang = userDetails?.additionalInfo?.lang ?? null;
|
|
||||||
if (!userLang && !data.isAuthenticated) {
|
if (!userLang && !data.isAuthenticated) {
|
||||||
const settings = this.storageService.getItem(SETTINGS_KEY);
|
const settings = this.storageService.getItem(SETTINGS_KEY);
|
||||||
userLang = settings?.userLang ?? null;
|
userLang = settings?.userLang ?? null;
|
||||||
}
|
}
|
||||||
this.notifyUserLang(userLang);
|
this.notifyUserLang(userLang);
|
||||||
this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem);
|
|
||||||
}),
|
}),
|
||||||
skip(1),
|
skip(1),
|
||||||
).subscribe((data) => {
|
).subscribe((data) => {
|
||||||
|
|||||||
@ -62,10 +62,11 @@ import { AlarmDataService } from '@core/api/alarm-data.service';
|
|||||||
import { IDashboardController } from '@home/components/dashboard-page/dashboard-page.models';
|
import { IDashboardController } from '@home/components/dashboard-page/dashboard-page.models';
|
||||||
import { PopoverPlacement } from '@shared/components/popover.models';
|
import { PopoverPlacement } from '@shared/components/popover.models';
|
||||||
import { PersistentRpc } from '@shared/models/rpc.models';
|
import { PersistentRpc } from '@shared/models/rpc.models';
|
||||||
import { EventEmitter, Injector } from '@angular/core';
|
import { EventEmitter } from '@angular/core';
|
||||||
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
|
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { TbUnit } from '@shared/models/unit.models';
|
import { TbUnit } from '@shared/models/unit.models';
|
||||||
|
import { UnitService } from '@core/services/unit.service';
|
||||||
|
|
||||||
export interface TimewindowFunctions {
|
export interface TimewindowFunctions {
|
||||||
onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void;
|
onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void;
|
||||||
@ -234,8 +235,8 @@ export class WidgetSubscriptionContext {
|
|||||||
utils: UtilsService;
|
utils: UtilsService;
|
||||||
dashboardUtils: DashboardUtilsService;
|
dashboardUtils: DashboardUtilsService;
|
||||||
raf: RafService;
|
raf: RafService;
|
||||||
|
unitService: UnitService;
|
||||||
widgetUtils: IWidgetUtils;
|
widgetUtils: IWidgetUtils;
|
||||||
$injector: Injector;
|
|
||||||
getServerTimeDiff: () => Observable<number>;
|
getServerTimeDiff: () => Observable<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1419,7 +1419,7 @@ export class WidgetSubscription implements IWidgetSubscription {
|
|||||||
if (this.displayLegend) {
|
if (this.displayLegend) {
|
||||||
const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals : this.decimals;
|
const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals : this.decimals;
|
||||||
const units = isNotEmptyTbUnits(dataKey.units) ? dataKey.units : this.units;
|
const units = isNotEmptyTbUnits(dataKey.units) ? dataKey.units : this.units;
|
||||||
const valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, {decimals, units})
|
const valueFormat = ValueFormatProcessor.fromSettings(this.ctx.unitService, {decimals, units})
|
||||||
const legendKey: LegendKey = {
|
const legendKey: LegendKey = {
|
||||||
dataKey,
|
dataKey,
|
||||||
dataIndex: dataKeyIndex,
|
dataIndex: dataKeyIndex,
|
||||||
|
|||||||
@ -32,6 +32,10 @@ import {
|
|||||||
import { isNotEmptyStr, isObject } from '@core/utils';
|
import { isNotEmptyStr, isObject } from '@core/utils';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '@core/core.state';
|
||||||
|
import { selectAuth, selectIsAuthenticated } from '@core/auth/auth.selectors';
|
||||||
|
import { filter, switchMap, take } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -41,12 +45,19 @@ export class UnitService {
|
|||||||
private currentUnitSystem: UnitSystem = UnitSystem.METRIC;
|
private currentUnitSystem: UnitSystem = UnitSystem.METRIC;
|
||||||
private converter: Converter;
|
private converter: Converter;
|
||||||
|
|
||||||
constructor(private translate: TranslateService) {
|
constructor(private translate: TranslateService,
|
||||||
|
private store: Store<AppState>) {
|
||||||
this.translate.onLangChange.pipe(
|
this.translate.onLangChange.pipe(
|
||||||
takeUntilDestroyed()
|
takeUntilDestroyed()
|
||||||
).subscribe(() => {
|
).subscribe(() => {
|
||||||
this.converter = getUnitConverter(this.translate);
|
this.converter = getUnitConverter(this.translate);
|
||||||
});
|
});
|
||||||
|
this.store.select(selectIsAuthenticated).pipe(
|
||||||
|
filter((data) => data),
|
||||||
|
switchMap(() => this.store.select(selectAuth).pipe(take(1)))
|
||||||
|
).subscribe((data) => {
|
||||||
|
this.setUnitSystem(data.userDetails?.additionalInfo?.unitSystem)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnitSystem(): UnitSystem {
|
getUnitSystem(): UnitSystem {
|
||||||
@ -65,8 +76,8 @@ export class UnitService {
|
|||||||
return this.converter?.listUnits(measure, unitSystem);
|
return this.converter?.listUnits(measure, unitSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure<AllMeasures> {
|
getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem, tagFilter?: string): UnitInfoGroupByMeasure<AllMeasures> {
|
||||||
return this.converter?.unitsGroupByMeasure(measure, unitSystem);
|
return this.converter?.unitsGroupByMeasure(measure, unitSystem, tagFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnitInfo(symbol: AllMeasuresUnits | string): UnitInfo {
|
getUnitInfo(symbol: AllMeasuresUnits | string): UnitInfo {
|
||||||
|
|||||||
@ -48,7 +48,7 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<div class="tb-units-field">
|
<div class="tb-units-field">
|
||||||
<tb-unit-input
|
<tb-unit-input
|
||||||
[supportsUnitConversion]="supportsUnitConversion"
|
supportsUnitConversion
|
||||||
formControlName="units">
|
formControlName="units">
|
||||||
</tb-unit-input>
|
</tb-unit-input>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -118,10 +118,6 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn
|
|||||||
return this.widgetConfigComponent.modelValue?.latestDataKeySettingsDirective;
|
return this.widgetConfigComponent.modelValue?.latestDataKeySettingsDirective;
|
||||||
}
|
}
|
||||||
|
|
||||||
get supportsUnitConversion(): boolean {
|
|
||||||
return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private propagateChange = (_val: any) => {};
|
private propagateChange = (_val: any) => {};
|
||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder,
|
constructor(private fb: UntypedFormBuilder,
|
||||||
@ -220,7 +216,7 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn
|
|||||||
hideDataKeyName: true,
|
hideDataKeyName: true,
|
||||||
hideDataKeyLabel: true,
|
hideDataKeyLabel: true,
|
||||||
hideDataKeyColor: true,
|
hideDataKeyColor: true,
|
||||||
supportsUnitConversion: this.supportsUnitConversion
|
supportsUnitConversion: true
|
||||||
}
|
}
|
||||||
}).afterClosed().subscribe((updatedDataKey) => {
|
}).afterClosed().subscribe((updatedDataKey) => {
|
||||||
if (updatedDataKey) {
|
if (updatedDataKey) {
|
||||||
|
|||||||
@ -100,7 +100,7 @@
|
|||||||
<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-unit-input
|
<tb-unit-input
|
||||||
supportsUnitConversion=""
|
supportsUnitConversion
|
||||||
formControlName="units">
|
formControlName="units">
|
||||||
</tb-unit-input>
|
</tb-unit-input>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -84,7 +84,7 @@
|
|||||||
<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-unit-input
|
<tb-unit-input
|
||||||
supportsUnitConversion=""
|
supportsUnitConversion
|
||||||
formControlName="units">
|
formControlName="units">
|
||||||
</tb-unit-input>
|
</tb-unit-input>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -105,6 +105,7 @@ import { ExceptionData } from '@shared/models/error.models';
|
|||||||
import { WidgetComponentService } from './widget-component.service';
|
import { WidgetComponentService } from './widget-component.service';
|
||||||
import { Timewindow } from '@shared/models/time/time.models';
|
import { Timewindow } from '@shared/models/time/time.models';
|
||||||
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
|
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
|
||||||
|
import { UnitService } from '@core/services/unit.service';
|
||||||
import { DashboardService } from '@core/http/dashboard.service';
|
import { DashboardService } from '@core/http/dashboard.service';
|
||||||
import { WidgetSubscription } from '@core/api/widget-subscription';
|
import { WidgetSubscription } from '@core/api/widget-subscription';
|
||||||
import { EntityService } from '@core/http/entity.service';
|
import { EntityService } from '@core/http/entity.service';
|
||||||
@ -216,6 +217,7 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
|
|||||||
private dashboardUtils: DashboardUtilsService,
|
private dashboardUtils: DashboardUtilsService,
|
||||||
private mobileService: MobileService,
|
private mobileService: MobileService,
|
||||||
private raf: RafService,
|
private raf: RafService,
|
||||||
|
private unitService: UnitService,
|
||||||
private ngZone: NgZone,
|
private ngZone: NgZone,
|
||||||
private cd: ChangeDetectorRef,
|
private cd: ChangeDetectorRef,
|
||||||
private http: HttpClient) {
|
private http: HttpClient) {
|
||||||
@ -341,8 +343,8 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
|
|||||||
this.subscriptionContext.utils = this.utils;
|
this.subscriptionContext.utils = this.utils;
|
||||||
this.subscriptionContext.dashboardUtils = this.dashboardUtils;
|
this.subscriptionContext.dashboardUtils = this.dashboardUtils;
|
||||||
this.subscriptionContext.raf = this.raf;
|
this.subscriptionContext.raf = this.raf;
|
||||||
|
this.subscriptionContext.unitService = this.unitService;
|
||||||
this.subscriptionContext.widgetUtils = this.widgetContext.utils;
|
this.subscriptionContext.widgetUtils = this.widgetContext.utils;
|
||||||
this.subscriptionContext.$injector = this.injector;
|
|
||||||
this.subscriptionContext.getServerTimeDiff = this.dashboardService.getServerTimeDiff.bind(this.dashboardService);
|
this.subscriptionContext.getServerTimeDiff = this.dashboardService.getServerTimeDiff.bind(this.dashboardService);
|
||||||
|
|
||||||
this.widgetComponentService.getWidgetInfo(this.widget.typeFullFqn).subscribe({
|
this.widgetComponentService.getWidgetInfo(this.widget.typeFullFqn).subscribe({
|
||||||
|
|||||||
@ -34,7 +34,9 @@ import { Observable, of, shareReplay } from 'rxjs';
|
|||||||
import {
|
import {
|
||||||
AllMeasures,
|
AllMeasures,
|
||||||
getSourceTbUnitSymbol,
|
getSourceTbUnitSymbol,
|
||||||
|
getTbUnitFromSearch,
|
||||||
isTbUnitMapping,
|
isTbUnitMapping,
|
||||||
|
searchUnit,
|
||||||
TbUnit,
|
TbUnit,
|
||||||
UnitInfo,
|
UnitInfo,
|
||||||
UnitSystem
|
UnitSystem
|
||||||
@ -43,7 +45,7 @@ import { map, mergeMap } from 'rxjs/operators';
|
|||||||
import { UnitService } from '@core/services/unit.service';
|
import { UnitService } from '@core/services/unit.service';
|
||||||
import { TbPopoverService } from '@shared/components/popover.service';
|
import { TbPopoverService } from '@shared/components/popover.service';
|
||||||
import { UnitSettingsPanelComponent } from '@shared/components/unit-settings-panel.component';
|
import { UnitSettingsPanelComponent } from '@shared/components/unit-settings-panel.component';
|
||||||
import { isDefinedAndNotNull, isEqual, isNotEmptyStr } from '@core/utils';
|
import { isDefinedAndNotNull, isEqual } from '@core/utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-unit-input',
|
selector: 'tb-unit-input',
|
||||||
@ -200,7 +202,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
|||||||
hostView: this.viewContainerRef,
|
hostView: this.viewContainerRef,
|
||||||
preferredPlacement: ['left', 'bottom', 'top'],
|
preferredPlacement: ['left', 'bottom', 'top'],
|
||||||
context: {
|
context: {
|
||||||
unit: this.extractTbUnit(this.unitsFormControl.value),
|
unit: getTbUnitFromSearch(this.unitsFormControl.value),
|
||||||
required: this.required,
|
required: this.required,
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
tagFilter: this.tagFilter,
|
tagFilter: this.tagFilter,
|
||||||
@ -217,7 +219,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateModel(value: UnitInfo | TbUnit ) {
|
private updateModel(value: UnitInfo | TbUnit ) {
|
||||||
let res = this.extractTbUnit(value);
|
let res = getTbUnitFromSearch(value);
|
||||||
if (this.onlySystemUnits && !isTbUnitMapping(res)) {
|
if (this.onlySystemUnits && !isTbUnitMapping(res)) {
|
||||||
const unitInfo = this.unitService.getUnitInfo(res as string);
|
const unitInfo = this.unitService.getUnitInfo(res as string);
|
||||||
if (unitInfo) {
|
if (unitInfo) {
|
||||||
@ -238,106 +240,17 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
|||||||
private fetchUnits(searchText?: string): Observable<Array<[AllMeasures, Array<UnitInfo>]>> {
|
private fetchUnits(searchText?: string): Observable<Array<[AllMeasures, Array<UnitInfo>]>> {
|
||||||
this.searchText = searchText;
|
this.searchText = searchText;
|
||||||
return this.getGroupedUnits().pipe(
|
return this.getGroupedUnits().pipe(
|
||||||
map(unit => this.searchUnit(unit, searchText))
|
map(unit => searchUnit(unit, searchText))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getGroupedUnits(): Observable<Array<[AllMeasures, Array<UnitInfo>]>> {
|
private getGroupedUnits(): Observable<Array<[AllMeasures, Array<UnitInfo>]>> {
|
||||||
if (this.fetchUnits$ === null) {
|
if (this.fetchUnits$ === null) {
|
||||||
this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem)).pipe(
|
this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem, this.tagFilter)).pipe(
|
||||||
map(data => {
|
map(data => Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>),
|
||||||
let objectData = Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>;
|
|
||||||
|
|
||||||
if (this.tagFilter) {
|
|
||||||
objectData = objectData
|
|
||||||
.map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitInfo[]])
|
|
||||||
.filter((measure) => measure[1].length > 0);
|
|
||||||
}
|
|
||||||
return objectData;
|
|
||||||
}),
|
|
||||||
shareReplay(1)
|
shareReplay(1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return this.fetchUnits$;
|
return this.fetchUnits$;
|
||||||
}
|
}
|
||||||
|
|
||||||
private searchUnit(units: Array<[AllMeasures, Array<UnitInfo>]>, searchText?: string): Array<[AllMeasures, Array<UnitInfo>]> {
|
|
||||||
if (isNotEmptyStr(searchText)) {
|
|
||||||
const filterValue = searchText.trim().toUpperCase();
|
|
||||||
|
|
||||||
const scoredGroups = units
|
|
||||||
.map(([measure, unitInfos]) => {
|
|
||||||
const scoredUnits = unitInfos
|
|
||||||
.map(unit => ({
|
|
||||||
unit,
|
|
||||||
score: this.calculateRelevanceScore(unit, filterValue)
|
|
||||||
}))
|
|
||||||
.filter(({ score }) => score > 0)
|
|
||||||
.sort((a, b) => b.score - a.score)
|
|
||||||
.map(({ unit }) => unit);
|
|
||||||
|
|
||||||
let groupScore = scoredUnits.length > 0
|
|
||||||
? Math.max(...scoredUnits.map(unit => this.calculateRelevanceScore(unit, filterValue)))
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
if (measure.toUpperCase() === filterValue) {
|
|
||||||
groupScore += 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { measure, units: scoredUnits, groupScore };
|
|
||||||
})
|
|
||||||
.filter(group => group.units.length > 0)
|
|
||||||
.sort((a, b) => {
|
|
||||||
if (b.groupScore !== a.groupScore) {
|
|
||||||
return b.groupScore - a.groupScore;
|
|
||||||
}
|
|
||||||
return b.units.length - a.units.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array<UnitInfo>]);
|
|
||||||
}
|
|
||||||
return units;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateRelevanceScore(unit: UnitInfo, filterValue: string): number {
|
|
||||||
const name = unit.name.toUpperCase();
|
|
||||||
const abbr = unit.abbr.toUpperCase();
|
|
||||||
const tags = unit.tags.map(tag => tag.toUpperCase());
|
|
||||||
|
|
||||||
let score = 0;
|
|
||||||
|
|
||||||
if (name === filterValue || abbr === filterValue) {
|
|
||||||
score += 100;
|
|
||||||
} else if (tags.includes(filterValue)) {
|
|
||||||
score += 80;
|
|
||||||
} else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) {
|
|
||||||
score += 60;
|
|
||||||
} else if (tags.some(tag => tag.startsWith(filterValue))) {
|
|
||||||
score += 50;
|
|
||||||
} else if (tags.some(tag => tag.includes(filterValue))) {
|
|
||||||
score += 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (score > 0) {
|
|
||||||
score += Math.max(0, 10 - (name.length + abbr.length) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractTbUnit(value: TbUnit | UnitInfo | null): TbUnit {
|
|
||||||
if (value === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (value === undefined) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if ('abbr' in value) {
|
|
||||||
return value.abbr;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ import voltage, { VoltageUnits } from '@shared/models/units/voltage';
|
|||||||
import volume, { VolumeUnits } from '@shared/models/units/volume';
|
import volume, { VolumeUnits } from '@shared/models/units/volume';
|
||||||
import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow';
|
import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { isNotEmptyStr } from '@core/utils';
|
import { deepClone, isNotEmptyStr } from '@core/utils';
|
||||||
|
|
||||||
export type AllMeasuresUnits =
|
export type AllMeasuresUnits =
|
||||||
| AbsorbedDoseRateUnits
|
| AbsorbedDoseRateUnits
|
||||||
@ -548,7 +548,7 @@ export class Converter {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure<AllMeasures> {
|
unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem, tagFilter?: string): UnitInfoGroupByMeasure<AllMeasures> {
|
||||||
const results: UnitInfoGroupByMeasure<AllMeasures> = {};
|
const results: UnitInfoGroupByMeasure<AllMeasures> = {};
|
||||||
|
|
||||||
const measures = measureName
|
const measures = measureName
|
||||||
@ -573,10 +573,16 @@ export class Converter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const abbr of Object.keys(units) as AllMeasuresUnits[]) {
|
for (const abbr of Object.keys(units) as AllMeasuresUnits[]) {
|
||||||
results[name].push(this.describe(abbr));
|
const unitInfo = this.describe(abbr);
|
||||||
|
if (!tagFilter || unitInfo.tags.includes(tagFilter)) {
|
||||||
|
results[name].push(unitInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!results[name].length) {
|
||||||
|
delete results[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,7 +647,7 @@ function buildUnitCache(measures: Record<AllMeasures, TbMeasure<AllMeasuresUnits
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getUnitConverter(translate: TranslateService): Converter {
|
export function getUnitConverter(translate: TranslateService): Converter {
|
||||||
const unitCache = buildUnitCache(allMeasures, translate);
|
const unitCache = buildUnitCache(deepClone(allMeasures), translate);
|
||||||
return new Converter(allMeasures, unitCache);
|
return new Converter(allMeasures, unitCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -669,3 +675,84 @@ export const isTbUnitMapping = (unit: any): boolean => {
|
|||||||
if (typeof unit !== 'object' || unit === null) return false;
|
if (typeof unit !== 'object' || unit === null) return false;
|
||||||
return isNotEmptyStr(unit.from);
|
return isNotEmptyStr(unit.from);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const searchUnit =
|
||||||
|
(units: Array<[AllMeasures, Array<UnitInfo>]>, searchText?: string): Array<[AllMeasures, Array<UnitInfo>]> => {
|
||||||
|
if (isNotEmptyStr(searchText)) {
|
||||||
|
const filterValue = searchText.trim().toUpperCase();
|
||||||
|
|
||||||
|
const scoredGroups = units
|
||||||
|
.map(([measure, unitInfos]) => {
|
||||||
|
const scoredUnits = unitInfos
|
||||||
|
.map(unit => ({
|
||||||
|
unit,
|
||||||
|
score: calculateRelevanceScore(unit, filterValue)
|
||||||
|
}))
|
||||||
|
.filter(({score}) => score > 0)
|
||||||
|
.sort((a, b) => b.score - a.score)
|
||||||
|
.map(({unit}) => unit);
|
||||||
|
|
||||||
|
let groupScore = scoredUnits.length > 0
|
||||||
|
? Math.max(...scoredUnits.map(unit => calculateRelevanceScore(unit, filterValue)))
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
if (measure.toUpperCase() === filterValue) {
|
||||||
|
groupScore += 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {measure, units: scoredUnits, groupScore};
|
||||||
|
})
|
||||||
|
.filter(group => group.units.length > 0)
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (b.groupScore !== a.groupScore) {
|
||||||
|
return b.groupScore - a.groupScore;
|
||||||
|
}
|
||||||
|
return b.units.length - a.units.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array<UnitInfo>]);
|
||||||
|
}
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateRelevanceScore (unit: UnitInfo, filterValue: string): number{
|
||||||
|
const name = unit.name.toUpperCase();
|
||||||
|
const abbr = unit.abbr.toUpperCase();
|
||||||
|
const tags = unit.tags.map(tag => tag.toUpperCase());
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
if (name === filterValue || abbr === filterValue) {
|
||||||
|
score += 100;
|
||||||
|
} else if (tags.includes(filterValue)) {
|
||||||
|
score += 80;
|
||||||
|
} else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) {
|
||||||
|
score += 60;
|
||||||
|
} else if (tags.some(tag => tag.startsWith(filterValue))) {
|
||||||
|
score += 50;
|
||||||
|
} else if (tags.some(tag => tag.includes(filterValue))) {
|
||||||
|
score += 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (score > 0) {
|
||||||
|
score += Math.max(0, 10 - (name.length + abbr.length) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTbUnitFromSearch = (value: TbUnit | UnitInfo | null): TbUnit => {
|
||||||
|
if (value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if ('abbr' in value) {
|
||||||
|
return value.abbr;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|||||||
@ -875,15 +875,16 @@ export abstract class ValueFormatProcessor {
|
|||||||
protected hideZeroDecimals: boolean;
|
protected hideZeroDecimals: boolean;
|
||||||
protected unitSymbol: string;
|
protected unitSymbol: string;
|
||||||
|
|
||||||
static fromSettings($injector: Injector, settings: ValueFormatSettings): ValueFormatProcessor {
|
static fromSettings($injector: Injector, settings: ValueFormatSettings): ValueFormatProcessor;
|
||||||
|
static fromSettings(unitService: UnitService, settings: ValueFormatSettings): ValueFormatProcessor;
|
||||||
|
static fromSettings(unitServiceOrInjector: Injector | UnitService, settings: ValueFormatSettings): ValueFormatProcessor {
|
||||||
if (settings.units !== null && typeof settings.units === 'object') {
|
if (settings.units !== null && typeof settings.units === 'object') {
|
||||||
return new UnitConverterValueFormatProcessor($injector, settings)
|
return new UnitConverterValueFormatProcessor(unitServiceOrInjector, settings)
|
||||||
}
|
}
|
||||||
return new SimpleValueFormatProcessor($injector, settings);
|
return new SimpleValueFormatProcessor(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected constructor(protected $injector: Injector,
|
protected constructor(protected settings: ValueFormatSettings) {
|
||||||
protected settings: ValueFormatSettings) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract format(value: any): string;
|
abstract format(value: any): string;
|
||||||
@ -908,9 +909,8 @@ export class SimpleValueFormatProcessor extends ValueFormatProcessor {
|
|||||||
|
|
||||||
private readonly isDefinedUnit: boolean;
|
private readonly isDefinedUnit: boolean;
|
||||||
|
|
||||||
constructor(protected $injector: Injector,
|
constructor(protected settings: ValueFormatSettings) {
|
||||||
protected settings: ValueFormatSettings) {
|
super(settings);
|
||||||
super($injector, settings);
|
|
||||||
this.unitSymbol = !settings.ignoreUnitSymbol && isNotEmptyStr(settings.units) ? (settings.units as string) : null;
|
this.unitSymbol = !settings.ignoreUnitSymbol && isNotEmptyStr(settings.units) ? (settings.units as string) : null;
|
||||||
this.isDefinedDecimals = isDefinedAndNotNull(settings.decimals);
|
this.isDefinedDecimals = isDefinedAndNotNull(settings.decimals);
|
||||||
this.hideZeroDecimals = !settings.showZeroDecimals;
|
this.hideZeroDecimals = !settings.showZeroDecimals;
|
||||||
@ -928,10 +928,10 @@ export class UnitConverterValueFormatProcessor extends ValueFormatProcessor {
|
|||||||
|
|
||||||
private readonly unitConverter: TbUnitConverter;
|
private readonly unitConverter: TbUnitConverter;
|
||||||
|
|
||||||
constructor(protected $injector: Injector,
|
constructor(protected unitServiceOrInjector: Injector | UnitService,
|
||||||
protected settings: ValueFormatSettings) {
|
protected settings: ValueFormatSettings) {
|
||||||
super($injector, settings);
|
super(settings);
|
||||||
const unitService = this.$injector.get(UnitService);
|
const unitService = this.unitServiceOrInjector instanceof UnitService ? this.unitServiceOrInjector : this.unitServiceOrInjector.get(UnitService);
|
||||||
const unit = settings.units;
|
const unit = settings.units;
|
||||||
this.unitSymbol = settings.ignoreUnitSymbol ? null : unitService.getTargetUnitSymbol(unit);
|
this.unitSymbol = settings.ignoreUnitSymbol ? null : unitService.getTargetUnitSymbol(unit);
|
||||||
this.unitConverter = unitService.geUnitConverter(unit);
|
this.unitConverter = unitService.geUnitConverter(unit);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user