Browse Source

UI: Add unit input group name

pull/13282/head
Vladyslav_Prykhodko 1 year ago
parent
commit
2d9d43f068
  1. 120
      ui-ngx/src/app/core/services/unit/converter-unit.ts
  2. 4
      ui-ngx/src/app/core/services/unit/definitions/all.ts
  3. 13
      ui-ngx/src/app/core/services/unit/unit.service.ts
  4. 23
      ui-ngx/src/app/shared/components/unit-input.component.html
  5. 8
      ui-ngx/src/app/shared/components/unit-input.component.scss
  6. 43
      ui-ngx/src/app/shared/components/unit-input.component.ts
  7. 7
      ui-ngx/src/app/shared/models/unit.models.ts
  8. 16
      ui-ngx/src/assets/locale/locale.constant-en_US.json

120
ui-ngx/src/app/core/services/unit/converter-unit.ts

@ -14,7 +14,14 @@
/// limitations under the License.
///
import { TbMeasure, TbUnitConvertor, Unit, UnitDescription, UnitSystem } from '@shared/models/unit.models';
import {
TbMeasure,
TbUnitConvertor,
Unit,
UnitDescription,
UnitDescriptionGroupByMeasure,
UnitSystem
} from '@shared/models/unit.models';
import { AllMeasures } from '@core/services/unit/definitions/all';
import { TranslateService } from '@ngx-translate/core';
import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils';
@ -340,6 +347,117 @@ export class Converter<
return list;
}
listGroupByMeasure(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure<TMeasures> | never {
const list: UnitDescriptionGroupByMeasure<TMeasures> = {};
if (isDefinedAndNotNull(measureName)) {
if (!this.isMeasure(measureName)) {
console.log(`Measure "${measureName}" not found.`);
return list;
}
const measure = this.measureData[measureName];
if (isDefinedAndNotNull(unitSystem)) {
let currentUnitSystem = unitSystem;
let units = measure[currentUnitSystem];
if (isUndefinedOrNull(units)) {
if (currentUnitSystem === UnitSystem.IMPERIAL) {
currentUnitSystem = UnitSystem.METRIC;
units = measure[currentUnitSystem];
}
if (!units) {
console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`);
return list;
}
}
list[measureName] = [];
const unitsDescription = list[measureName];
for (const [abbr, unit] of Object.entries(
units.units
)) {
unitsDescription.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: measureName as TMeasures,
system: currentUnitSystem,
unit: unit as Unit,
})
);
}
} else {
for (const [systemName, units] of Object.entries(
measure
) as Entries<TbMeasure<TUnits>, UnitSystem>[]) {
list[measureName] = [];
const unitsDescription = list[measureName];
for (const [abbr, unit] of Object.entries(
units.units as Partial<Record<TUnits, Unit>>
)) {
unitsDescription.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: measureName as TMeasures,
system: systemName,
unit: unit as Unit,
})
);
}
}
}
} else {
for (const [name, measure] of Object.entries(this.measureData)) {
if (isDefinedAndNotNull(unitSystem)) {
let currentUnitSystem = unitSystem;
let units = (measure as TbMeasure<TUnits>)[currentUnitSystem]?.units;
if (isUndefinedOrNull(units)) {
if (currentUnitSystem === UnitSystem.IMPERIAL) {
currentUnitSystem = UnitSystem.METRIC;
units = (measure as TbMeasure<TUnits>)[currentUnitSystem]?.units;
}
if (!units) {
console.log(`Measure "${name}" in ${currentUnitSystem} system is not found.`);
continue;
}
}
list[name] = [];
const unitsDescription = list[name];
for (const [abbr, unit] of Object.entries(
units as Partial<Record<TUnits, Unit>>
)) {
unitsDescription.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: name as TMeasures,
system: currentUnitSystem,
unit: unit as Unit,
})
);
}
} else {
list[name] = [];
const unitsDescription = list[name];
for (const [systemName, units] of Object.entries(
measure
) as Entries<TbMeasure<TUnits>, UnitSystem>[]) {
for (const [abbr, unit] of Object.entries(
units.units as Partial<Record<TUnits, Unit>>
)) {
unitsDescription.push(
this.describeUnit({
abbr: abbr as TUnits,
measure: name as TMeasures,
system: systemName,
unit: unit as Unit,
})
);
}
}
}
}
}
return list;
}
private isMeasure(measureName: string): measureName is TMeasures {
return measureName in this.measureData;
}

4
ui-ngx/src/app/core/services/unit/definitions/all.ts

@ -33,7 +33,7 @@ export type AllMeasuresUnits =
export type AllMeasures =
| 'acceleration'
| 'angle'
| 'angularAcceleration'
| 'angular-acceleration'
| 'area'
| 'temperature'
| 'time';
@ -44,7 +44,7 @@ const allMeasures: Record<
> = {
acceleration,
angle,
angularAcceleration,
'angular-acceleration': angularAcceleration,
area,
temperature,
time,

13
ui-ngx/src/app/core/services/unit/unit.service.ts

@ -16,7 +16,12 @@
import { Injectable } from '@angular/core';
import moment from 'moment-timezone';
import { TbUnitConvertor, UnitDescription, UnitSystem } from '@shared/models/unit.models';
import {
TbUnitConvertor,
UnitDescription,
UnitDescriptionGroupByMeasure,
UnitSystem
} from '@shared/models/unit.models';
import { isNotEmptyStr } from '@core/utils';
import { configureMeasurements, Converter } from '@core/services/unit/converter-unit';
import allMeasures, { AllMeasures, AllMeasuresUnits } from '@core/services/unit/definitions/all';
@ -60,7 +65,11 @@ export class UnitService {
}
getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescription[] {
return this.converter?.list(measure, unitSystem) ?? [];
return this.converter?.list(measure, unitSystem);
}
getUnitsGroupByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure<AllMeasures> {
return this.converter?.listGroupByMeasure(measure, unitSystem);
}
getUnitDescription(abbr: AllMeasuresUnits | string): UnitDescription {

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

@ -46,11 +46,24 @@
class="tb-autocomplete tb-unit-input-autocomplete"
panelWidth="fit-content"
[displayWith]="displayUnitFn.bind(this)">
@for(unit of filteredUnits | async; track unit.abbr) {
<mat-option [value]="unit">
<span class="tb-unit-name flex-1" [innerHTML]="unit.name | highlight:searchText:true:'ig'"></span>
<span class="tb-unit-symbol" [innerHTML]="unit.abbr | highlight:searchText:true:'ig'"></span>
</mat-option>
@for (group of filteredUnits | async; track group[0]) {
@if ((fetchUnits$ | async).length > 1) {
<mat-optgroup [label]="'unit.measures.' + group[0] | translate">
@for(unit of group[1]; track unit.abbr) {
<mat-option [value]="unit">
<span class="tb-unit-name flex-1" [innerHTML]="unit.name | highlight:searchText:true:'ig'"></span>
<span class="tb-unit-symbol" [innerHTML]="unit.abbr | highlight:searchText:true:'ig'"></span>
</mat-option>
}
</mat-optgroup>
} @else {
@for(unit of group[1]; track unit.abbr) {
<mat-option [value]="unit">
<span class="tb-unit-name flex-1" [innerHTML]="unit.name | highlight:searchText:true:'ig'"></span>
<span class="tb-unit-symbol" [innerHTML]="unit.abbr | highlight:searchText:true:'ig'"></span>
</mat-option>
}
}
}
</mat-autocomplete>
</mat-form-field>

8
ui-ngx/src/app/shared/components/unit-input.component.scss

@ -14,6 +14,14 @@
* limitations under the License.
*/
.tb-autocomplete.tb-unit-input-autocomplete {
.mat-mdc-optgroup-label {
.mdc-list-item__primary-text {
font-size: 14px;
font-weight: 500;
line-height: 20px;
letter-spacing: 0.2px;
}
}
.mat-mdc-option {
border-bottom: none;
.mdc-list-item__primary-text {

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

@ -37,6 +37,7 @@ import { AllMeasures } from '@core/services/unit/definitions/all';
import { UnitService } from '@core/services/unit/unit.service';
import { TbPopoverService } from '@shared/components/popover.service';
import { ConvertUnitSettingsPanelComponent } from '@shared/components/convert-unit-settings-panel.component';
import { isNotEmptyStr } from '@core/utils';
@Component({
selector: 'tb-unit-input',
@ -76,15 +77,17 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
@Input({transform: booleanAttribute})
allowConverted = false;
filteredUnits: Observable<Array<UnitDescription>>;
filteredUnits: Observable<Array<[AllMeasures, Array<UnitDescription>]>>;
searchText = '';
isGroupOption = false;
private dirty = false;
private modelValue: TbUnit | null;
private fetchUnits$: Observable<Array<UnitDescription>> = null;
fetchUnits$: Observable<Array<[AllMeasures, Array<UnitDescription>]>> = null;
private propagateChange = (_val: any) => {};
@ -106,6 +109,9 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
}),
mergeMap(symbol => this.fetchUnits(symbol))
);
if (!!this.measure || !!this.tagFilter) {
this.isGroupOption = true;
}
}
ngOnChanges(changes: SimpleChanges) {
@ -115,6 +121,11 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
if (propName === 'measure' || propName === 'unitSystem') {
this.fetchUnits$ = null;
this.dirty = true;
if (!!this.measure || !!this.tagFilter) {
this.isGroupOption = true;
} else {
this.isGroupOption = false;
}
}
}
}
@ -205,21 +216,25 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
}
}
private fetchUnits(searchText?: string): Observable<Array<UnitDescription>> {
private fetchUnits(searchText?: string): Observable<Array<[AllMeasures, Array<UnitDescription>]>> {
this.searchText = searchText;
return this.unitsConstant().pipe(
map(unit => searchUnits(unit, searchText))
map(unit => this.searchUnit(unit, searchText))
);
}
private unitsConstant(): Observable<Array<UnitDescription>> {
private unitsConstant(): Observable<Array<[AllMeasures, Array<UnitDescription>]>> {
if (this.fetchUnits$ === null) {
this.fetchUnits$ = of(this.unitService.getUnits(this.measure, this.unitSystem)).pipe(
map((units) => {
this.fetchUnits$ = of(this.unitService.getUnitsGroupByMeasure(this.measure, this.unitSystem)).pipe(
map(data => {
let objectData = Object.entries(data) as Array<[AllMeasures, UnitDescription[]]>;
if (this.tagFilter) {
units = units.filter(u => u.tags.includes(this.tagFilter));
objectData = objectData
.map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitDescription[]])
.filter((measure) => measure[1].length > 0);
}
return units;
return objectData;
}),
shareReplay(1)
);
@ -227,6 +242,16 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
return this.fetchUnits$;
}
private searchUnit(units: Array<[AllMeasures, Array<UnitDescription>]>, searchText?: string): Array<[AllMeasures, Array<UnitDescription>]> {
if (isNotEmptyStr(searchText)) {
const filterValue = searchText.trim().toUpperCase()
return units
.map(measure => [measure[0], searchUnits(measure[1], filterValue)] as [AllMeasures, UnitDescription[]])
.filter((measure) => measure[1].length > 0);
}
return units;
}
private getUnitSymbol(value: TbUnit | UnitDescription | null): string {
if (value === null) {
return '';

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

@ -24,6 +24,7 @@ export enum UnitsType {
}
export type TbUnitConvertor = (value: number) => number;
export type UnitDescriptionGroupByMeasure<TMeasure extends string> = Partial<Record<TMeasure, UnitDescription[]>>;
export interface UnitDescription {
abbr: string;
@ -66,11 +67,11 @@ export interface TbMeasureUnits<TUnits extends string> {
}
const searchUnitTags = (unit: UnitDescription, searchText: string): boolean =>
!!unit.tags.find(t => t.toUpperCase().includes(searchText.toUpperCase()));
!!unit.tags.find(t => t.toUpperCase().includes(searchText));
export const searchUnits = (_units: Array<UnitDescription>, searchText: string): Array<UnitDescription> => _units.filter(
u => u.abbr.toUpperCase().includes(searchText.toUpperCase()) ||
u.name.toUpperCase().includes(searchText.toUpperCase()) ||
u => u.abbr.toUpperCase().includes(searchText) ||
u.name.toUpperCase().includes(searchText) ||
searchUnitTags(u, searchText)
);

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

@ -5849,10 +5849,18 @@
"unit": {
"unit-system": "Unit system",
"unit-system-type": {
"AUTO": "Auto",
"METRIC": "Metric",
"IMPERIAL": "Imperial",
"HYBRID": "Hybrid"
"AUTO": "Auto",
"METRIC": "Metric",
"IMPERIAL": "Imperial",
"HYBRID": "Hybrid"
},
"measures": {
"acceleration": "Acceleration",
"angle": "Angle",
"angular-acceleration": "Angular acceleration",
"area": "Area",
"temperature": "Temperature",
"time": "Time"
},
"millimeter": "Millimeter",
"centimeter": "Centimeter",

Loading…
Cancel
Save