UI: Refactoring unit-input and panel
This commit is contained in:
parent
f5f794191d
commit
caa643d157
@ -158,68 +158,31 @@ export class Converter<
|
||||
return result / destination.unit.to_anchor;
|
||||
}
|
||||
|
||||
// toBest(options?: {
|
||||
// exclude?: (TUnits | (string & {}))[];
|
||||
// cutOffNumber?: number;
|
||||
// system?: TSystems | (string & {});
|
||||
// }): BestResult<TUnits> | null {
|
||||
// if (this.origin == null)
|
||||
// throw new OperationOrderError('.toBest must be called after .from');
|
||||
//
|
||||
// const isNegative = this.val < 0;
|
||||
//
|
||||
// let exclude: (TUnits | (string & {}))[] = [];
|
||||
// let cutOffNumber = isNegative ? -1 : 1;
|
||||
// let system: TSystems | (string & {}) = this.origin.system;
|
||||
//
|
||||
// if (typeof options === 'object') {
|
||||
// exclude = options.exclude ?? [];
|
||||
// cutOffNumber = options.cutOffNumber ?? cutOffNumber;
|
||||
// system = options.system ?? this.origin.system;
|
||||
// }
|
||||
//
|
||||
// let best: BestResult<TUnits> | null = null;
|
||||
// /**
|
||||
// Looks through every possibility for the 'best' available unit.
|
||||
// i.e. Where the value has the fewest numbers before the decimal point,
|
||||
// but is still higher than 1.
|
||||
// */
|
||||
// for (const possibility of this.possibilities()) {
|
||||
// const unit = this.describe(possibility);
|
||||
// const isIncluded = exclude.indexOf(possibility) === -1;
|
||||
//
|
||||
// if (isIncluded && unit.system === system) {
|
||||
// const result = this.to(possibility);
|
||||
// if (isNegative ? result > cutOffNumber : result < cutOffNumber) {
|
||||
// continue;
|
||||
// }
|
||||
// if (
|
||||
// best === null ||
|
||||
// (isNegative
|
||||
// ? result <= cutOffNumber && result > best.val
|
||||
// : result >= cutOffNumber && result < best.val)
|
||||
// ) {
|
||||
// best = {
|
||||
// val: result,
|
||||
// unit: possibility,
|
||||
// name: unit.name,
|
||||
// tags: unit.tags
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (best == null) {
|
||||
// return {
|
||||
// val: this.val,
|
||||
// unit: this.origin.abbr,
|
||||
// name: this.origin.unit.name,
|
||||
// tags: this.origin.unit.tags
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// return best;
|
||||
// }
|
||||
getDefaultUnit(measureName: TMeasures | (string & {}), unitSystem: UnitSystem): TUnits {
|
||||
if (!this.isMeasure(measureName)) {
|
||||
return null;
|
||||
}
|
||||
const measure = this.measureData[measureName];
|
||||
let currentUnitSystem = unitSystem;
|
||||
let units = measure[currentUnitSystem].units;
|
||||
if (isUndefinedOrNull(units)) {
|
||||
if (currentUnitSystem === UnitSystem.IMPERIAL) {
|
||||
currentUnitSystem = UnitSystem.METRIC;
|
||||
units = measure[currentUnitSystem].units;
|
||||
}
|
||||
if (!units) {
|
||||
console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
for (const [abbr, unit] of Object.entries(
|
||||
units as Partial<Record<TUnits, Unit>>
|
||||
) as [TUnits, Unit][]) {
|
||||
if (unit.to_anchor === 1 && (isUndefinedOrNull(unit.anchor_shift) || unit.anchor_shift === 0)) {
|
||||
return abbr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getUnit(abbr: TUnits | (string & {})): Conversion<TMeasures, TUnits> | null {
|
||||
return this.unitCache.get(abbr) ?? null;
|
||||
|
||||
@ -76,6 +76,10 @@ export class UnitService {
|
||||
return this.converter.describe(abbr);
|
||||
}
|
||||
|
||||
getDefaultUnit(measure: AllMeasures, unitSystem: UnitSystem): AllMeasuresUnits {
|
||||
return this.converter.getDefaultUnit(measure, unitSystem);
|
||||
}
|
||||
|
||||
geUnitConvertor(from: string, to: string): TbUnitConvertor {
|
||||
return this.converter.convertor(from, to);
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import {
|
||||
ColorProcessor,
|
||||
ComponentStyle,
|
||||
DateFormatProcessor,
|
||||
FormatValueProcessor,
|
||||
getDataKey,
|
||||
getLabel,
|
||||
getSingleTsValue,
|
||||
@ -46,7 +47,6 @@ import { WidgetComponent } from '@home/components/widget/widget.component';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ImagePipe } from '@shared/pipe/image.pipe';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { FormatValueProcessor } from '@shared/models/unit.models';
|
||||
|
||||
const squareLayoutSize = 160;
|
||||
const horizontalLayoutHeight = 80;
|
||||
|
||||
@ -16,53 +16,54 @@
|
||||
|
||||
-->
|
||||
<div class="tb-convert-settings-panel">
|
||||
<div class="tb-convert-settings-title">Unit convertion settings</div>
|
||||
<div class="tb-convert-settings-title" translate>unit.convert.units-conversion-settings</div>
|
||||
<div class="tb-convert-settings-panel-content" [formGroup]="convertUnitForm">
|
||||
<div class="tb-form-row">
|
||||
<div class="min-w-25">From</div>
|
||||
<tb-unit-input class="flex-1" [required]="required" formControlName="from"></tb-unit-input>
|
||||
<div class="min-w-25" [class.tb-disabled-label]="convertUnitForm.get('from').disabled" translate>unit.convert.convert-from</div>
|
||||
<tb-unit-input #unitFrom class="flex-1" [required]="required" formControlName="from"></tb-unit-input>
|
||||
</div>
|
||||
<div class="tb-form-row">
|
||||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="convertUnit">
|
||||
Convert units
|
||||
<div tb-hint-tooltip-icon="{{ 'unit.convert.convert-unit-hint' | translate }}">
|
||||
{{ 'unit.convert.convert-unit' | translate }}
|
||||
</div>
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
@if(convertUnitForm.get('convertUnit').value) {
|
||||
<div class="tb-form-row">
|
||||
<div class="min-w-25">Metrical</div>
|
||||
<tb-unit-input class="flex-1"
|
||||
formControlName="METRIC"
|
||||
[unitSystem]="UnitSystem.METRIC"
|
||||
[measure]="measure">
|
||||
</tb-unit-input>
|
||||
</div>
|
||||
<div class="tb-form-row">
|
||||
<div class="min-w-25">Imperial</div>
|
||||
<tb-unit-input class="flex-1"
|
||||
formControlName="IMPERIAL"
|
||||
[unitSystem]="UnitSystem.IMPERIAL"
|
||||
[measure]="measure">
|
||||
</tb-unit-input>
|
||||
</div>
|
||||
<div class="tb-form-row">
|
||||
<div class="min-w-25">Hybrid</div>
|
||||
<tb-unit-input class="flex-1"
|
||||
formControlName="HYBRID"
|
||||
[measure]="measure">
|
||||
</tb-unit-input>
|
||||
</div>
|
||||
}
|
||||
<div class="tb-form-row">
|
||||
<div class="min-w-25" [class.tb-disabled-label]="convertUnitForm.get('METRIC').disabled" translate>unit.convert.to-metric</div>
|
||||
<tb-unit-input class="flex-1"
|
||||
formControlName="METRIC"
|
||||
[unitSystem]="UnitSystem.METRIC"
|
||||
[measure]="measure">
|
||||
</tb-unit-input>
|
||||
</div>
|
||||
<div class="tb-form-row">
|
||||
<div class="min-w-25" [class.tb-disabled-label]="convertUnitForm.get('IMPERIAL').disabled" translate>unit.convert.to-imperial</div>
|
||||
<tb-unit-input class="flex-1"
|
||||
formControlName="IMPERIAL"
|
||||
[unitSystem]="UnitSystem.IMPERIAL"
|
||||
[measure]="measure">
|
||||
</tb-unit-input>
|
||||
</div>
|
||||
<div class="tb-form-row">
|
||||
<div class="min-w-25" [class.tb-disabled-label]="convertUnitForm.get('HYBRID').disabled" translate>unit.convert.to-imperial</div>
|
||||
<tb-unit-input class="flex-1"
|
||||
formControlName="HYBRID"
|
||||
[measure]="measure">
|
||||
</tb-unit-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-convert-settings-panel-buttons">
|
||||
<button mat-button
|
||||
color="primary"
|
||||
type="button"
|
||||
(click)="cancel()">
|
||||
{{ 'action.cancel' | translate }}
|
||||
{{ (disabled ? 'action.close': 'action.cancel') | translate }}
|
||||
</button>
|
||||
<button mat-raised-button
|
||||
color="primary"
|
||||
type="button"
|
||||
*ngIf="!disabled"
|
||||
(click)="applyUnitSettings()"
|
||||
[disabled]="convertUnitForm.invalid || convertUnitForm.pristine">
|
||||
{{ 'action.apply' | translate }}
|
||||
|
||||
@ -14,14 +14,16 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { TbUnit, UnitDescription, 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';
|
||||
import { UnitService } from '@core/services/unit/unit.service';
|
||||
import { AllMeasures } from '@core/services/unit/definitions/all';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { debounceTime, first } from 'rxjs/operators';
|
||||
import { isEmptyStr } from '@core/utils';
|
||||
import type { UnitInputComponent } from '@shared/components/unit-input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-covert-unit-settings-panel',
|
||||
@ -32,12 +34,17 @@ import { debounceTime } from 'rxjs/operators';
|
||||
})
|
||||
export class ConvertUnitSettingsPanelComponent implements OnInit {
|
||||
|
||||
@ViewChild('unitFrom', {static: true}) unitFrom: UnitInputComponent;
|
||||
|
||||
@Input()
|
||||
unit: TbUnit;
|
||||
|
||||
@Input()
|
||||
required: boolean;
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
@Output()
|
||||
unitSettingsApplied = new EventEmitter<TbUnit>();
|
||||
|
||||
@ -67,15 +74,22 @@ export class ConvertUnitSettingsPanelComponent implements OnInit {
|
||||
this.convertUnitForm.get('convertUnit').enable({emitEvent: true});
|
||||
this.measure = unitDescription.measure;
|
||||
if (unitDescription.system === UnitSystem.IMPERIAL) {
|
||||
this.convertUnitForm.get('METRIC').setValue(this.unitService.getDefaultUnit(this.measure, UnitSystem.METRIC), {emitEvent: false});
|
||||
this.convertUnitForm.get('IMPERIAL').setValue(unit, {emitEvent: false});
|
||||
this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false});
|
||||
} else {
|
||||
this.convertUnitForm.get('METRIC').setValue(unit, {emitEvent: false});
|
||||
this.convertUnitForm.get('IMPERIAL').setValue(this.unitService.getDefaultUnit(this.measure, UnitSystem.IMPERIAL), {emitEvent: false});
|
||||
this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false});
|
||||
}
|
||||
} else {
|
||||
this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true});
|
||||
this.convertUnitForm.get('convertUnit').disable({emitEvent: false});
|
||||
this.convertUnitForm.patchValue({
|
||||
METRIC: '',
|
||||
IMPERIAL: '',
|
||||
HYBRID: ''
|
||||
}, {emitEvent: false});
|
||||
}
|
||||
})
|
||||
|
||||
@ -91,9 +105,6 @@ export class ConvertUnitSettingsPanelComponent implements OnInit {
|
||||
this.convertUnitForm.get('IMPERIAL').disable({emitEvent: false});
|
||||
this.convertUnitForm.get('HYBRID').disable({emitEvent: false});
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.popover.updatePosition();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
@ -120,6 +131,13 @@ export class ConvertUnitSettingsPanelComponent implements OnInit {
|
||||
} else {
|
||||
this.convertUnitForm.get('convertUnit').disable({emitEvent: false});
|
||||
}
|
||||
if (this.disabled) {
|
||||
this.convertUnitForm.disable({emitEvent: false});
|
||||
} else if (this.unit === null || isEmptyStr(this.unit)) {
|
||||
this.popover.tbAnimationDone.pipe(first()).subscribe(() => {
|
||||
this.unitFrom.unitInput.nativeElement.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
|
||||
@ -15,23 +15,20 @@
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<mat-form-field appearance="outline" class="tb-inline-field tb-suffix-show-on-hover w-full flex-1" subscriptSizing="dynamic">
|
||||
<mat-form-field appearance="outline" class="tb-inline-field tb-suffix-show-on-hover w-full flex-1" subscriptSizing="dynamic"
|
||||
(click)="openConvertSettingsPopup($event)">
|
||||
<input matInput #unitInput [formControl]="unitsFormControl"
|
||||
placeholder="{{ 'widget-config.set' | translate }}"
|
||||
[class.!pointer-events-none]="disabled"
|
||||
(focusin)="onFocus()"
|
||||
[matAutocomplete]="unitsAutocomplete">
|
||||
<button type="button"
|
||||
*ngIf="!disabled && allowConverted"
|
||||
class="tb-icon-24"
|
||||
[class.mr-2]="!unitsFormControl.value || disabled || unitsFormControl.invalid"
|
||||
matSuffix mat-icon-button (click)="openConvertSettingsPopup($event)">
|
||||
<tb-icon>mdi:tape-measure</tb-icon>
|
||||
</button>
|
||||
[matAutocomplete]="unitsAutocomplete"
|
||||
[matAutocompleteDisabled]="allowConverted">
|
||||
<button *ngIf="unitsFormControl.value && !disabled && unitsFormControl.valid"
|
||||
type="button"
|
||||
class="tb-icon-24 mr-2"
|
||||
[class.mr-2]="!allowConverted || !isUnitMapping"
|
||||
matSuffix mat-icon-button aria-label="Clear"
|
||||
(click)="clear()">
|
||||
(click)="clear($event)">
|
||||
<mat-icon class="material-icons">close</mat-icon>
|
||||
</button>
|
||||
<mat-icon matSuffix
|
||||
@ -41,29 +38,27 @@
|
||||
*ngIf="unitsFormControl.hasError('required')" class="material-icons tb-suffix-show-always tb-error">
|
||||
warning
|
||||
</mat-icon>
|
||||
<tb-icon matSuffix
|
||||
[color]="disabled ? null : 'primary'"
|
||||
matTooltipPosition="above"
|
||||
[matTooltip]="'unit.convert.set-units-conversion-settings' | translate"
|
||||
*ngIf="allowConverted && isUnitMapping" class="material-icons tb-icon-24 tb-suffix-show-always">
|
||||
mdi:swap-vertical-circle-outline
|
||||
</tb-icon>
|
||||
<mat-autocomplete
|
||||
#unitsAutocomplete="matAutocomplete"
|
||||
class="tb-autocomplete tb-unit-input-autocomplete"
|
||||
panelWidth="fit-content"
|
||||
[displayWith]="displayUnitFn.bind(this)">
|
||||
@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 {
|
||||
<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>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
.tb-autocomplete.tb-unit-input-autocomplete {
|
||||
.mat-mdc-optgroup-label {
|
||||
min-height: 36px;
|
||||
.mdc-list-item__primary-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
||||
@ -37,7 +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';
|
||||
import { isNotEmptyStr, isObject } from '@core/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-unit-input',
|
||||
@ -59,7 +59,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
||||
|
||||
unitsFormControl: FormControl<TbUnit | UnitDescription>;
|
||||
|
||||
@Input()
|
||||
@Input({transform: booleanAttribute})
|
||||
disabled: boolean;
|
||||
|
||||
@Input({transform: booleanAttribute})
|
||||
@ -81,13 +81,13 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
||||
|
||||
searchText = '';
|
||||
|
||||
isGroupOption = false;
|
||||
isUnitMapping = false;
|
||||
|
||||
private dirty = false;
|
||||
|
||||
private modelValue: TbUnit | null;
|
||||
|
||||
fetchUnits$: Observable<Array<[AllMeasures, Array<UnitDescription>]>> = null;
|
||||
private fetchUnits$: Observable<Array<[AllMeasures, Array<UnitDescription>]>> = null;
|
||||
|
||||
private propagateChange = (_val: any) => {};
|
||||
|
||||
@ -109,9 +109,6 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
||||
}),
|
||||
mergeMap(symbol => this.fetchUnits(symbol))
|
||||
);
|
||||
if (!!this.measure || !!this.tagFilter) {
|
||||
this.isGroupOption = true;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
@ -121,11 +118,6 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,8 +128,10 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
||||
this.modelValue = symbol;
|
||||
if (typeof symbol === 'string') {
|
||||
this.unitsFormControl.patchValue(this.unitService.getUnitDescription(symbol) ?? symbol, {emitEvent: false});
|
||||
this.isUnitMapping = false;
|
||||
} else {
|
||||
this.unitsFormControl.patchValue(symbol, {emitEvent: false});
|
||||
this.isUnitMapping = symbol !== null;
|
||||
}
|
||||
this.dirty = true;
|
||||
}
|
||||
@ -172,18 +166,25 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
clear($event: Event) {
|
||||
$event.stopPropagation();
|
||||
this.unitsFormControl.patchValue(null, {emitEvent: true});
|
||||
setTimeout(() => {
|
||||
this.unitInput.nativeElement.blur();
|
||||
this.unitInput.nativeElement.focus();
|
||||
}, 0);
|
||||
if (!this.allowConverted) {
|
||||
setTimeout(() => {
|
||||
this.unitInput.nativeElement.blur();
|
||||
this.unitInput.nativeElement.focus();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
openConvertSettingsPopup($event: Event) {
|
||||
if (!this.allowConverted) {
|
||||
return;
|
||||
}
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
this.unitInput.nativeElement.blur();
|
||||
const trigger = this.elementRef.nativeElement;
|
||||
if (this.popoverService.hasPopover(trigger)) {
|
||||
this.popoverService.hidePopover(trigger);
|
||||
@ -196,7 +197,8 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
||||
preferredPlacement: ['left', 'bottom', 'top'],
|
||||
context: {
|
||||
unit: this.getTbUnit(this.unitsFormControl.value),
|
||||
required: this.required
|
||||
required: this.required,
|
||||
disabled: this.disabled,
|
||||
},
|
||||
isModal: true
|
||||
});
|
||||
@ -212,6 +214,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang
|
||||
const res = this.getTbUnit(value);
|
||||
if (this.modelValue !== res) {
|
||||
this.modelValue = res;
|
||||
this.isUnitMapping = (res !== null && isObject(res));
|
||||
this.propagateChange(this.modelValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,6 @@
|
||||
///
|
||||
|
||||
import { AllMeasures } from '@core/services/unit/definitions/all';
|
||||
import { Injector } from '@angular/core';
|
||||
import { isDefinedAndNotNull, isNotEmptyStr, isNumeric } from '@core/utils';
|
||||
import { UnitService } from '@core/services/unit/unit.service';
|
||||
|
||||
export enum UnitsType {
|
||||
capacity = 'capacity'
|
||||
@ -74,104 +71,3 @@ export const searchUnits = (_units: Array<UnitDescription>, searchText: string):
|
||||
u.name.toUpperCase().includes(searchText) ||
|
||||
searchUnitTags(u, searchText)
|
||||
);
|
||||
|
||||
export interface FormatValueSettingProcessor {
|
||||
dec?: number;
|
||||
units?: TbUnit;
|
||||
showZeroDecimals?: boolean;
|
||||
}
|
||||
|
||||
export abstract class FormatValueProcessor {
|
||||
|
||||
static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor {
|
||||
if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) {
|
||||
return new ConvertUnitProcessor($injector, settings)
|
||||
} else {
|
||||
return new SimpleUnitProcessor($injector, settings);
|
||||
}
|
||||
}
|
||||
|
||||
protected constructor(protected $injector: Injector,
|
||||
protected settings: FormatValueSettingProcessor) {
|
||||
}
|
||||
|
||||
abstract format(value: any): string;
|
||||
}
|
||||
|
||||
export class SimpleUnitProcessor extends FormatValueProcessor {
|
||||
|
||||
private readonly isDefinedUnit: boolean;
|
||||
private readonly isDefinedDec: boolean;
|
||||
private readonly showZeroDecimals: boolean;
|
||||
|
||||
constructor(protected $injector: Injector,
|
||||
protected settings: FormatValueSettingProcessor) {
|
||||
super($injector, settings);
|
||||
this.isDefinedUnit = isNotEmptyStr(settings.units);
|
||||
this.isDefinedDec = isDefinedAndNotNull(settings.dec);
|
||||
this.showZeroDecimals = !!settings.showZeroDecimals;
|
||||
}
|
||||
|
||||
format(value: any): string {
|
||||
if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) {
|
||||
let formatted = value;
|
||||
if (this.isDefinedDec) {
|
||||
formatted = Number(formatted).toFixed(this.settings.dec);
|
||||
}
|
||||
if (!this.showZeroDecimals) {
|
||||
formatted = Number(formatted)
|
||||
}
|
||||
formatted = formatted.toString();
|
||||
if (this.isDefinedUnit) {
|
||||
formatted += ` ${this.settings.units}`;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
return value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
export class ConvertUnitProcessor extends FormatValueProcessor {
|
||||
|
||||
private readonly isDefinedDec: boolean;
|
||||
private readonly showZeroDecimals: boolean;
|
||||
private readonly unitConvertor: TbUnitConvertor;
|
||||
private readonly unitAbbr: string;
|
||||
|
||||
constructor(protected $injector: Injector,
|
||||
protected settings: FormatValueSettingProcessor) {
|
||||
super($injector, settings);
|
||||
const unitService = this.$injector.get(UnitService);
|
||||
const userUnitSystem = unitService.getUnitSystem();
|
||||
const unit = settings.units as TbUnitMapping;
|
||||
const fromUnit = unit.from;
|
||||
this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit;
|
||||
try {
|
||||
this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr);
|
||||
} catch (e) {/**/}
|
||||
|
||||
this.isDefinedDec = isDefinedAndNotNull(settings.dec);
|
||||
this.showZeroDecimals = !!settings.showZeroDecimals;
|
||||
}
|
||||
|
||||
format(value: any): string {
|
||||
if (isDefinedAndNotNull(value) && isNumeric(value)) {
|
||||
let formatted: number | string = Number(value);
|
||||
if (this.unitConvertor) {
|
||||
formatted = this.unitConvertor(value);
|
||||
}
|
||||
if (this.isDefinedDec) {
|
||||
formatted = Number(formatted).toFixed(this.settings.dec);
|
||||
}
|
||||
if (!this.showZeroDecimals) {
|
||||
formatted = Number(formatted)
|
||||
}
|
||||
formatted = formatted.toString();
|
||||
if (this.unitAbbr) {
|
||||
formatted += ` ${this.unitAbbr}`;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
return value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,15 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { isDefinedAndNotNull, isNumber, isNumeric, isUndefinedOrNull, mergeDeep, parseFunction } from '@core/utils';
|
||||
import {
|
||||
isDefinedAndNotNull,
|
||||
isNotEmptyStr,
|
||||
isNumber,
|
||||
isNumeric,
|
||||
isUndefinedOrNull,
|
||||
mergeDeep,
|
||||
parseFunction
|
||||
} from '@core/utils';
|
||||
import {
|
||||
DataEntry,
|
||||
DataKey,
|
||||
@ -45,6 +53,8 @@ import {
|
||||
WidgetSubscriptionCallbacks,
|
||||
WidgetSubscriptionOptions
|
||||
} from '@core/api/widget-api.models';
|
||||
import { UnitService } from '@core/services/unit/unit.service';
|
||||
import { TbUnit, TbUnitConvertor, TbUnitMapping } from '@shared/models/unit.models';
|
||||
|
||||
export type ComponentStyle = {[klass: string]: any};
|
||||
|
||||
@ -852,6 +862,107 @@ export class AutoDateFormatProcessor extends DateFormatProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
export interface FormatValueSettingProcessor {
|
||||
dec?: number;
|
||||
units?: TbUnit;
|
||||
showZeroDecimals?: boolean;
|
||||
}
|
||||
|
||||
export abstract class FormatValueProcessor {
|
||||
|
||||
static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor {
|
||||
if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) {
|
||||
return new ConvertUnitProcessor($injector, settings)
|
||||
} else {
|
||||
return new SimpleUnitProcessor($injector, settings);
|
||||
}
|
||||
}
|
||||
|
||||
protected constructor(protected $injector: Injector,
|
||||
protected settings: FormatValueSettingProcessor) {
|
||||
}
|
||||
|
||||
abstract format(value: any): string;
|
||||
}
|
||||
|
||||
export class SimpleUnitProcessor extends FormatValueProcessor {
|
||||
|
||||
private readonly isDefinedUnit: boolean;
|
||||
private readonly isDefinedDec: boolean;
|
||||
private readonly showZeroDecimals: boolean;
|
||||
|
||||
constructor(protected $injector: Injector,
|
||||
protected settings: FormatValueSettingProcessor) {
|
||||
super($injector, settings);
|
||||
this.isDefinedUnit = isNotEmptyStr(settings.units);
|
||||
this.isDefinedDec = isDefinedAndNotNull(settings.dec);
|
||||
this.showZeroDecimals = !!settings.showZeroDecimals;
|
||||
}
|
||||
|
||||
format(value: any): string {
|
||||
if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) {
|
||||
let formatted = value;
|
||||
if (this.isDefinedDec) {
|
||||
formatted = Number(formatted).toFixed(this.settings.dec);
|
||||
}
|
||||
if (!this.showZeroDecimals) {
|
||||
formatted = Number(formatted)
|
||||
}
|
||||
formatted = formatted.toString();
|
||||
if (this.isDefinedUnit) {
|
||||
formatted += ` ${this.settings.units}`;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
return value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
export class ConvertUnitProcessor extends FormatValueProcessor {
|
||||
|
||||
private readonly isDefinedDec: boolean;
|
||||
private readonly showZeroDecimals: boolean;
|
||||
private readonly unitConvertor: TbUnitConvertor;
|
||||
private readonly unitAbbr: string;
|
||||
|
||||
constructor(protected $injector: Injector,
|
||||
protected settings: FormatValueSettingProcessor) {
|
||||
super($injector, settings);
|
||||
const unitService = this.$injector.get(UnitService);
|
||||
const userUnitSystem = unitService.getUnitSystem();
|
||||
const unit = settings.units as TbUnitMapping;
|
||||
const fromUnit = unit.from;
|
||||
this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit;
|
||||
try {
|
||||
this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr);
|
||||
} catch (e) {/**/}
|
||||
|
||||
this.isDefinedDec = isDefinedAndNotNull(settings.dec);
|
||||
this.showZeroDecimals = !!settings.showZeroDecimals;
|
||||
}
|
||||
|
||||
format(value: any): string {
|
||||
if (isDefinedAndNotNull(value) && isNumeric(value)) {
|
||||
let formatted: number | string = Number(value);
|
||||
if (this.unitConvertor) {
|
||||
formatted = this.unitConvertor(value);
|
||||
}
|
||||
if (this.isDefinedDec) {
|
||||
formatted = Number(formatted).toFixed(this.settings.dec);
|
||||
}
|
||||
if (!this.showZeroDecimals) {
|
||||
formatted = Number(formatted)
|
||||
}
|
||||
formatted = formatted.toString();
|
||||
if (this.unitAbbr) {
|
||||
formatted += ` ${this.unitAbbr}`;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
return value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
const intervalToFormatTimeUnit = (interval: Interval): FormatTimeUnit => {
|
||||
const intervalValue = IntervalMath.numberValue(interval);
|
||||
if (intervalValue < SECOND) {
|
||||
|
||||
@ -5847,6 +5847,16 @@
|
||||
"background-blur": "Background blur"
|
||||
},
|
||||
"unit": {
|
||||
"convert": {
|
||||
"set-units-conversion-settings": "Set units conversion settings",
|
||||
"units-conversion-settings": "Units conversion settings",
|
||||
"convert-from": "Convert from",
|
||||
"to-metric": "To metric",
|
||||
"to-imperial": "To imperial",
|
||||
"to-hybrid": "To hybrid",
|
||||
"convert-unit": "Convert unit",
|
||||
"convert-unit-hint": "Work only with the default unit"
|
||||
},
|
||||
"unit-system": "Unit system",
|
||||
"unit-system-type": {
|
||||
"AUTO": "Auto",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user