From 111ad28816b3b7e192b17f075b7d113fa7bc14f9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 13 May 2025 17:34:24 +0300 Subject: [PATCH] UI: Refactoring search in unit component --- ui-ngx/src/app/core/services/unit.service.ts | 3 - ...uid-level-card-basic-config.component.html | 8 +- ...iquid-level-card-basic-config.component.ts | 4 +- ...-level-card-widget-settings.component.html | 8 +- ...id-level-card-widget-settings.component.ts | 4 +- ...convert-unit-settings-panel.component.html | 82 ++++++----- ...convert-unit-settings-panel.component.scss | 5 +- .../convert-unit-settings-panel.component.ts | 4 +- .../components/unit-input.component.html | 2 +- .../shared/components/unit-input.component.ts | 69 +++++++-- ui-ngx/src/app/shared/models/unit.models.ts | 135 +++++++++--------- .../assets/locale/locale.constant-en_US.json | 22 +-- 12 files changed, 200 insertions(+), 146 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 86f74e78b5..5d60e80390 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -46,8 +46,6 @@ export class UnitService { takeUntilDestroyed() ).subscribe(() => { this.converter = getUnitConverter(this.translate); - console.warn(this.converter.listUnits()); - console.warn(this.converter.listUnits(null, UnitSystem.IMPERIAL)); }); } @@ -61,7 +59,6 @@ export class UnitService { } else { this.currentUnitSystem = this.getUnitSystemByTimezone(); } - console.warn('[Unit system] setUnitSystem', this.currentUnitSystem); } getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfo[] { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html index d2752aebab..c270b17551 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html @@ -125,7 +125,7 @@
widgets.liquid-level-card.datasource-units
@@ -143,7 +143,7 @@ @@ -199,7 +199,7 @@ @@ -270,7 +270,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts index 3a64cf6b1d..6cd016a85d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts @@ -56,7 +56,7 @@ import { ShapesTranslations, updatedFormSettingsValidators } from '@home/components/widget/lib/indicator/liquid-level-widget.models'; -import { getSourceTbUnitSymbol, UnitsType } from '@shared/models/unit.models'; +import { getSourceTbUnitSymbol } from '@shared/models/unit.models'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; import { map, share, tap } from 'rxjs/operators'; @@ -116,8 +116,6 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon shapesImageMap: Map = new Map(); ShapesTranslationMap = ShapesTranslations; - unitsType = UnitsType; - levelCardWidgetConfigForm: FormGroup; valuePreviewFn = this._valuePreviewFn.bind(this); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html index 88f62477dd..70eec82e2d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html @@ -83,7 +83,7 @@
widgets.liquid-level-card.datasource-units
@@ -100,7 +100,7 @@ @@ -156,7 +156,7 @@
@@ -239,7 +239,7 @@ {{ 'widgets.liquid-level-card.level' | translate }}
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts index 843c4470b5..386ada1cab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts @@ -44,7 +44,7 @@ import { ShapesTranslations, updatedFormSettingsValidators } from '@home/components/widget/lib/indicator/liquid-level-widget.models'; -import { getSourceTbUnitSymbol, UnitsType } from '@shared/models/unit.models'; +import { getSourceTbUnitSymbol } from '@shared/models/unit.models'; import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { Observable, of, ReplaySubject } from 'rxjs'; @@ -96,8 +96,6 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon shapesImageMap: Map = new Map(); ShapesTranslationMap = ShapesTranslations; - unitsType = UnitsType; - levelCardWidgetSettingsForm: FormGroup; valuePreviewFn = this._valuePreviewFn.bind(this); diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html index 7c935728a3..3f14ba290c 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html @@ -16,48 +16,60 @@ -->
-
unit.convert.units-conversion-settings
+
unit.conversion.unit-settings
-
unit.convert.convert-from
+
unit.conversion.source-unit
-
- -
- {{ 'unit.convert.convert-unit' | translate }} +
+ +
+ {{ 'unit.conversion.enable-unit-conversion' | translate }}
-
-
-
unit.convert.to-metric
- - -
-
-
unit.convert.to-imperial
- - -
-
-
unit.convert.to-imperial
- - +
+
unit.conversion.target-metric-unit
+ + +
+
+
unit.conversion.target-imperial-unit
+ + +
+
+
unit.conversion.target-hybrid-unit
+ + +
diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss index fb2bf1be09..24df54f839 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss @@ -16,13 +16,16 @@ @import '../scss/constants'; .tb-convert-settings-panel { - width: 320px; + width: 360px; display: flex; flex-direction: column; gap: 16px; max-height: calc(100vh - 24px); @media #{$mat-xs} { width: 90vw; + .tb-form-row tb-unit-input { + width: 90px; + } } .tb-convert-settings-title { font-size: 16px; diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts index 7d3b74e27e..92b8162576 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts @@ -15,7 +15,7 @@ /// import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -48,7 +48,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { unitSettingsApplied = new EventEmitter(); @Input() - tagFilter: UnitsType; + tagFilter: string; @Input() measure: AllMeasures; diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html index 33c77130b9..022c4f15e1 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.html +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -40,7 +40,7 @@ mdi:swap-vertical-circle-outline diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 510f05826d..1f263772b5 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -37,7 +37,6 @@ import { isTbUnitMapping, TbUnit, UnitInfo, - UnitsType, UnitSystem } from '@shared/models/unit.models'; import { map, mergeMap } from 'rxjs/operators'; @@ -73,7 +72,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang required = false; @Input() - tagFilter: UnitsType; + tagFilter: string; @Input() measure: AllMeasures; @@ -264,18 +263,68 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { if (isNotEmptyStr(searchText)) { - const filterValue = searchText.trim().toUpperCase() - return units.reduce((result: Array<[AllMeasures, Array]>, [measure, unitInfos]) => { - const filteredUnits = unitInfos.filter(unit => unit.searchText.toUpperCase().includes(filterValue)); - if (filteredUnits.length > 0) { - result.push([measure, filteredUnits]); - } - return result; - }, []); + 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]); } 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; diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index f388b6ab81..94a831afa0 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -204,6 +204,7 @@ export type AllMeasures = | 'angle' | 'angular-acceleration' | 'area' + | 'area-density' | 'capacitance' | 'catalytic-activity' | 'catalytic-concentration' @@ -287,28 +288,51 @@ const allMeasures: Record< AllMeasures, TbMeasure > = Object.freeze({ - 'absorbed-dose-rate': absorbedDoseRate, + temperature, + pressure, + voltage, + 'current-density': currentDensity, + 'electric-current': electricCurrent, + power, + energy, + speed, + length, + mass, + time, + area, + volume, + 'volume-flow': volumeFlow, + density, acceleration, - acidity, 'air-quality-index': airQualityIndex, - 'amount-of-substance': amountOfSubstance, - angle, - 'angular-acceleration': angularAcceleration, - area, - 'area-density': areaDensity, + illuminance, + 'signal-level': signalLevel, + 'fuel-efficiency': fuelEfficiency, + frequency, capacitance, + inductance, + resistance, + torque, + force, + 'magnetic-flux-density': magneticFluxDensity, + 'magnetic-flux': magneticFlux, + radioactivity, + 'radioactive-decay': radioactiveDecay, + 'specific-energy': specificEnergy, + 'specific-heat-capacity': specificHeatCapacity, + 'kinematic-viscosity': kinematicViscosity, + 'dynamic-viscosity': dynamicViscosity, + 'thermal-conductivity': thermalConductivity, + turbidity, + 'earthquake-magnitude': earthquakeMagnitude, + 'data-transfer-rate': dataTransferRate, + 'parts-per-million': partsPerMillion, + 'molar-concentration': molarConcentration, + 'number-concentration': numberConcentration, 'catalytic-activity': catalyticActivity, 'catalytic-concentration': catalyticConcentration, charge, - 'current-density': currentDensity, - 'data-transfer-rate': dataTransferRate, - density, - digital, - 'dimension-ratio': dimensionRatio, - 'dynamic-viscosity': dynamicViscosity, - 'earthquake-magnitude': earthquakeMagnitude, 'electric-charge-density': electricChargeDensity, - 'electric-current': electricCurrent, 'electric-dipole-moment': electricDipoleMoment, 'electric-field-strength': electricFieldStrength, 'electric-flux': electricFlux, @@ -316,70 +340,43 @@ const allMeasures: Record< 'electric-polarizability': electricPolarizability, 'electrical-conductance': electricalConductance, 'electrical-conductivity': electricalConductivity, - energy, - 'energy-density': energyDensity, - force, - frequency, - 'fuel-efficiency': fuelEfficiency, - 'heat-capacity': heatCapacity, - illuminance, - inductance, - 'kinematic-viscosity': kinematicViscosity, - length, - 'light-exposure': lightExposure, - 'linear-charge-density': linerChargeDensity, - 'logarithmic-ratio': logarithmicRatio, - 'luminous-efficacy': luminousEfficacy, - 'luminous-flux': luminousFlux, - 'luminous-intensity': luminousIntensity, - 'number-concentration': numberConcentration, 'magnetic-field-gradient': magneticFieldGradient, - 'magnetic-flux': magneticFlux, - 'magnetic-flux-density': magneticFluxDensity, 'magnetic-moment': magneticMoment, 'magnetic-permeability': magneticPermeability, - mass, - 'mass-fraction': massFraction, - 'molar-concentration': molarConcentration, - 'molar-energy': molarEnergy, - 'molar-heat-capacity': molarHeatCapacity, - 'molar-mass': molarMass, - 'parts-per-million': partsPerMillion, - power, - 'power-density': powerDensity, - pressure, radiance, 'radiant-intensity': radiantIntensity, 'radiation-dose': radiationDose, - 'radioactive-decay': radioactiveDecay, - radioactivity, 'radioactivity-concentration': radioactivityConcentration, 'reciprocal-length': reciprocalLength, - resistance, 'reynolds-number': reynoldsNumber, - 'signal-level': signalLevel, - 'solid-angle': solidAngle, - 'specific-energy': specificEnergy, - 'specific-heat-capacity': specificHeatCapacity, - 'specific-humidity': specificHumidity, - 'specific-volume': specificVolume, - speed, 'surface-charge-density': surfaceChargeDensity, 'surface-tension': surfaceTension, - temperature, - 'thermal-conductivity': thermalConductivity, - time, - torque, - turbidity, - voltage, - volume, - 'volume-flow': volumeFlow, + 'specific-volume': specificVolume, + 'specific-humidity': specificHumidity, + 'angular-acceleration': angularAcceleration, + angle, + 'solid-angle': solidAngle, + 'light-exposure': lightExposure, + 'luminous-intensity': luminousIntensity, + 'luminous-flux': luminousFlux, + 'luminous-efficacy': luminousEfficacy, + 'molar-energy': molarEnergy, + 'molar-heat-capacity': molarHeatCapacity, + 'molar-mass': molarMass, + 'mass-fraction': massFraction, + 'logarithmic-ratio': logarithmicRatio, + 'dimension-ratio': dimensionRatio, + 'absorbed-dose-rate': absorbedDoseRate, + acidity, + 'amount-of-substance': amountOfSubstance, + digital, + 'area-density': areaDensity, + 'energy-density': energyDensity, + 'heat-capacity': heatCapacity, + 'linear-charge-density': linerChargeDensity, + 'power-density': powerDensity, }); -export enum UnitsType { - capacity = 'capacity' -} - export type TbUnitConverter = (value: number) => number; export type UnitInfoGroupByMeasure = Partial>; @@ -389,7 +386,6 @@ export interface UnitInfo { system: UnitSystem; name: string; tags: string[]; - searchText: string; } export enum UnitSystem { @@ -430,7 +426,6 @@ export interface UnitCacheInfo { measure: AllMeasures; unit: Unit; abbr: AllMeasuresUnits; - searchText: string; } export type UnitCache = Map; @@ -591,8 +586,7 @@ export class Converter { measure: unit.measure, system: unit.system, name: unit.unit.name, - tags: unit.unit.tags, - searchText: unit.searchText + tags: unit.unit.tags }; } @@ -638,7 +632,6 @@ function buildUnitCache(measures: Record