dark theme + percent-bar + Value.locale
This commit is contained in:
parent
f5cbf9cf43
commit
f495ad9af1
27
src/main/angular/config.less
Normal file
27
src/main/angular/config.less
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* bright theme */
|
||||||
|
|
||||||
|
@foreground: gray;
|
||||||
|
@background: white;
|
||||||
|
|
||||||
|
@consumption: orange;
|
||||||
|
@purchase: orangered;
|
||||||
|
@production: dodgerblue;
|
||||||
|
@self: forestgreen;
|
||||||
|
@delivery: magenta;
|
||||||
|
|
||||||
|
@consumptionBack: @consumption;
|
||||||
|
@purchaseBack: @purchase;
|
||||||
|
@productionBack: @production;
|
||||||
|
@selfBack: @self;
|
||||||
|
@deliveryBack: @delivery;
|
||||||
|
|
||||||
|
/* dark theme */
|
||||||
|
|
||||||
|
@foreground: gray;
|
||||||
|
@background: #11171b;
|
||||||
|
|
||||||
|
@consumptionBack: #856938;
|
||||||
|
@purchaseBack: #71361d;
|
||||||
|
@productionBack: #2d4255;
|
||||||
|
@selfBack: #2b4e2b;
|
||||||
|
@deliveryBack: #753475;
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="entry consumption" [class.zero]="powerConsumption?.zero">
|
<div class="entry consumption" [class.zero]="powerConsumption?.zero">
|
||||||
<div class="name">Verbrauch</div>
|
<div class="name">Bedarf</div>
|
||||||
<div class="percent"> </div>
|
<div class="percent"> </div>
|
||||||
<div class="value">{{ powerConsumption?.formatted }}</div>
|
<div class="value">{{ powerConsumption?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -15,13 +15,13 @@
|
|||||||
<div class="percent">{{ powerPurchasePercent?.formatted }}</div>
|
<div class="percent">{{ powerPurchasePercent?.formatted }}</div>
|
||||||
<div class="value">{{ powerPurchase?.formatted }}</div>
|
<div class="value">{{ powerPurchase?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="entry production" [class.zero]="powerProduced?.zero">
|
<div class="entry production" [class.zero]="powerProduction?.zero">
|
||||||
<div class="name">Produktion</div>
|
<div class="name">Produktion</div>
|
||||||
<div class="percent">{{ powerProducedPercent?.formatted }}</div>
|
<div class="percent">{{ powerProducedPercent?.formatted }}</div>
|
||||||
<div class="value">{{ powerProduced?.formatted }}</div>
|
<div class="value">{{ powerProduction?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="entry self" [class.zero]="powerSelf?.zero">
|
<div class="entry self" [class.zero]="powerSelf?.zero">
|
||||||
<div class="name">Eigenverbrauch</div>
|
<div class="name">Eigenbedarf</div>
|
||||||
<div class="percent">{{ powerSelfPercent?.formatted }}</div>
|
<div class="percent">{{ powerSelfPercent?.formatted }}</div>
|
||||||
<div class="value">{{ powerSelf?.formatted }}</div>
|
<div class="value">{{ powerSelf?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -32,14 +32,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<app-percent-bar [produktion]="powerProduction" [self]="powerSelf" [purchase]="powerPurchase" [delivery]="powerDelivery"></app-percent-bar>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
Energie
|
Energie
|
||||||
<span class="live" *ngIf="interval">Live</span>
|
|
||||||
<span class="archive" *ngIf="!interval">Archiv</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="option">
|
<div class="option">
|
||||||
@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="entry consumption" [class.zero]="aggregations.energyConsumed?.zero">
|
<div class="entry consumption" [class.zero]="aggregations.energyConsumed?.zero">
|
||||||
<div class="name">Verbrauch</div>
|
<div class="name">Bedarf</div>
|
||||||
<div class="percent"> </div>
|
<div class="percent"> </div>
|
||||||
<div class="value">{{ aggregations.energyConsumed?.formatted }}</div>
|
<div class="value">{{ aggregations.energyConsumed?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -71,17 +71,19 @@
|
|||||||
<div class="value">{{ aggregations.energyProduced?.delta?.formatted }}</div>
|
<div class="value">{{ aggregations.energyProduced?.delta?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="entry self" [class.zero]="aggregations.energySelf?.zero">
|
<div class="entry self" [class.zero]="aggregations.energySelf?.zero">
|
||||||
<div class="name">Eigenverbrauch</div>
|
<div class="name">Eigenbedarf</div>
|
||||||
<div class="percent">{{ aggregations.energySelfPercent?.formatted }}</div>
|
<div class="percent">{{ aggregations.energySelfPercent?.formatted }}</div>
|
||||||
<div class="value">{{ aggregations.energySelf?.formatted }}</div>
|
<div class="value">{{ aggregations.energySelf?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="entry delivery" [class.zero]="aggregations.energyDelivered?.delta?.zero">
|
<div class="entry delivery" [class.zero]="aggregations.energyDelivered?.delta?.zero">
|
||||||
<div class="name">Eingespeist</div>
|
<div class="name">Einspeisung</div>
|
||||||
<div class="percent">{{ aggregations.energyDeliveredPercent?.formatted }}</div>
|
<div class="percent">{{ aggregations.energyDeliveredPercent?.formatted }}</div>
|
||||||
<div class="value">{{ aggregations.energyDelivered?.delta?.formatted }}</div>
|
<div class="value">{{ aggregations.energyDelivered?.delta?.formatted }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<app-percent-bar [produktion]="aggregations.energyProduced?.delta" [self]="aggregations.energySelf" [purchase]="aggregations.energyPurchased?.delta" [delivery]="aggregations.energyDelivered?.delta"></app-percent-bar>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-outlet/>
|
<router-outlet/>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
@import "../../config.less";
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 2em;
|
||||||
|
|
||||||
.back {
|
.back {
|
||||||
float: left;
|
float: left;
|
||||||
@ -12,7 +14,6 @@
|
|||||||
.title {
|
.title {
|
||||||
clear: left;
|
clear: left;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 120%;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +29,6 @@
|
|||||||
|
|
||||||
.name {
|
.name {
|
||||||
float: left;
|
float: left;
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
@ -39,7 +39,7 @@
|
|||||||
float: right;
|
float: right;
|
||||||
padding-top: 0.5em;
|
padding-top: 0.5em;
|
||||||
font-size: 60%;
|
font-size: 60%;
|
||||||
width: 3.5em;
|
width: 4.5em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,25 +62,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.consumption {
|
.consumption {
|
||||||
color: orange;
|
color: @consumption;
|
||||||
}
|
}
|
||||||
|
|
||||||
.purchase {
|
.purchase {
|
||||||
color: orangered;
|
color: @purchase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.production {
|
.production {
|
||||||
color: dodgerblue;
|
color: @production;
|
||||||
}
|
}
|
||||||
|
|
||||||
.self {
|
.self {
|
||||||
color: forestgreen;
|
color: @self;
|
||||||
}
|
}
|
||||||
|
|
||||||
.delivery {
|
.delivery {
|
||||||
color: magenta;
|
color: @delivery;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zero {
|
.zero {
|
||||||
filter: opacity(30%);
|
filter: opacity(20%);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,11 @@ import {AggregationWrapperDto} from './series/AggregationWrapperDto';
|
|||||||
import {SeriesService} from './series/series.service';
|
import {SeriesService} from './series/series.service';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
import {Value} from './value/Value';
|
import {Value} from './value/Value';
|
||||||
import {NgIf} from '@angular/common';
|
import {PercentBarComponent} from './percent-bar/percent-bar.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet, NgIf],
|
imports: [RouterOutlet, PercentBarComponent],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.less'
|
styleUrl: './app.component.less'
|
||||||
})
|
})
|
||||||
@ -25,7 +25,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private readonly subs: Subscription[] = [];
|
private readonly subs: Subscription[] = [];
|
||||||
|
|
||||||
get powerProduced(): Value | undefined {
|
get powerProduction(): Value | undefined {
|
||||||
return this.seriesService.powerProduced.series?.lastValue;
|
return this.seriesService.powerProduced.series?.lastValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,19 +42,19 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get powerSelf(): Value | undefined {
|
get powerSelf(): Value | undefined {
|
||||||
return this.powerProduced?.minus(this.powerDelivery);
|
return this.powerProduction?.minus(this.powerDelivery);
|
||||||
}
|
}
|
||||||
|
|
||||||
get powerConsumption(): Value | undefined {
|
get powerConsumption(): Value | undefined {
|
||||||
return this.powerBalance?.plus(this.powerProduced);
|
return this.powerPurchase?.plus(this.powerProduction);
|
||||||
}
|
}
|
||||||
|
|
||||||
get powerProducedPercent(): Value | undefined {
|
get powerProducedPercent(): Value | undefined {
|
||||||
return this.powerProduced?.percent(this.powerConsumption);
|
return this.powerProduction?.percent(this.powerConsumption);
|
||||||
}
|
}
|
||||||
|
|
||||||
get powerSelfPercent(): Value | undefined {
|
get powerSelfPercent(): Value | undefined {
|
||||||
return this.powerSelf?.percent(this.powerProduced);
|
return this.powerSelf?.percent(this.powerProduction);
|
||||||
}
|
}
|
||||||
|
|
||||||
get powerPurchasePercent(): Value | undefined {
|
get powerPurchasePercent(): Value | undefined {
|
||||||
@ -62,7 +62,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get powerDeliveryPercent(): Value | undefined {
|
get powerDeliveryPercent(): Value | undefined {
|
||||||
return this.powerDelivery?.percent(this.powerProduced);
|
return this.powerDelivery?.percent(this.powerProduction);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
<div class="bar">
|
||||||
|
<div class="part purchase" *ngIf="barPurchasePercent" [style.width]="barPurchasePercent.value + '%'">
|
||||||
|
<div class="text">
|
||||||
|
{{ _purchase?.formatted }}<br>
|
||||||
|
{{ purchasePercent?.formatted }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="part self" *ngIf="barSelfPercent" [style.width]="barSelfPercent.value + '%'">
|
||||||
|
<div class="text">
|
||||||
|
{{ _self?.formatted }}<br>
|
||||||
|
{{ selfPercent?.formatted }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="part delivery" *ngIf="barDeliveryPercent" [style.width]="barDeliveryPercent.value + '%'">
|
||||||
|
<div class="text">
|
||||||
|
{{ _delivery?.formatted }}<br>
|
||||||
|
{{ deliveryPercent?.formatted }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
@import "../../../config.less";
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
position: relative;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
.part {
|
||||||
|
float: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 40%;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
padding-left: 0.25em;
|
||||||
|
}
|
||||||
|
border-radius: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.self {
|
||||||
|
background-color: @selfBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase {
|
||||||
|
background-color: @purchaseBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delivery {
|
||||||
|
background-color: @deliveryBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {Value} from '../value/Value';
|
||||||
|
import {NgIf} from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-percent-bar',
|
||||||
|
imports: [
|
||||||
|
NgIf
|
||||||
|
],
|
||||||
|
templateUrl: './percent-bar.component.html',
|
||||||
|
styleUrl: './percent-bar.component.less'
|
||||||
|
})
|
||||||
|
export class PercentBarComponent {
|
||||||
|
|
||||||
|
protected _produktion: Value | undefined;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set produktion(produktion: Value | undefined) {
|
||||||
|
this._produktion = produktion;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _self: Value | undefined;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set self(self: Value | undefined) {
|
||||||
|
this._self = self;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _purchase: Value | undefined;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set purchase(purchase: Value | undefined) {
|
||||||
|
this._purchase = purchase;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _delivery: Value | undefined;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set delivery(delivery: Value | undefined) {
|
||||||
|
this._delivery = delivery;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
percent: boolean = false;
|
||||||
|
|
||||||
|
protected consumption: Value | undefined;
|
||||||
|
|
||||||
|
protected barSum: Value | undefined;
|
||||||
|
|
||||||
|
protected selfPercent: Value | undefined;
|
||||||
|
|
||||||
|
protected purchasePercent: Value | undefined;
|
||||||
|
|
||||||
|
protected deliveryPercent: Value | undefined;
|
||||||
|
|
||||||
|
protected barSelfPercent: Value | undefined;
|
||||||
|
|
||||||
|
protected barPurchasePercent: Value | undefined;
|
||||||
|
|
||||||
|
protected barDeliveryPercent: Value | undefined;
|
||||||
|
|
||||||
|
private update() {
|
||||||
|
this.consumption = this._self?.plus(this._purchase);
|
||||||
|
this.selfPercent = this._self?.percent(this.consumption);
|
||||||
|
this.purchasePercent = this._purchase?.percent(this.consumption);
|
||||||
|
this.deliveryPercent = this._delivery?.percent(this._produktion);
|
||||||
|
|
||||||
|
this.barSum = this.consumption?.plus(this._delivery);
|
||||||
|
this.barSelfPercent = this._self?.percent(this.barSum);
|
||||||
|
this.barPurchasePercent = this._purchase?.percent(this.barSum);
|
||||||
|
this.barDeliveryPercent = this._delivery?.percent(this.barSum);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -48,11 +48,11 @@ export class AggregationWrapperDto {
|
|||||||
this.energySelf = this.energyProduced?.delta.minus(this.energyDelivered?.delta);
|
this.energySelf = this.energyProduced?.delta.minus(this.energyDelivered?.delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: any): AggregationWrapperDto {
|
static fromJson(json: any, locale: string): AggregationWrapperDto {
|
||||||
return new AggregationWrapperDto(
|
return new AggregationWrapperDto(
|
||||||
json['alignment'] as Alignment,
|
json['alignment'] as Alignment,
|
||||||
new Date(json['date']),
|
new Date(json['date']),
|
||||||
(json['aggregations'] as any[]).map(a => a.hasOwnProperty('delta') ? MeterAggregate.fromJson(a) : VaryingAggregate.fromJson(a)),
|
(json['aggregations'] as any[]).map(a => a.hasOwnProperty('delta') ? MeterAggregate.fromJson(a, locale) : VaryingAggregate.fromJson(a, locale)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export class Series {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: any): Series {
|
static fromJson(json: any, locale: string): Series {
|
||||||
const decimals = validateNumber(json['decimals']);
|
const decimals = validateNumber(json['decimals']);
|
||||||
const unit = Unit.fromJson(json['unit']);
|
const unit = Unit.fromJson(json['unit']);
|
||||||
return new Series(
|
return new Series(
|
||||||
@ -24,7 +24,7 @@ export class Series {
|
|||||||
validateString(json['title']),
|
validateString(json['title']),
|
||||||
decimals,
|
decimals,
|
||||||
unit,
|
unit,
|
||||||
Value.fromJson(json['lastValue'], json['unit'], unit, decimals),
|
Value.fromJson(json['lastValue'], unit, decimals, locale),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,11 +11,11 @@ export class MeterAggregate extends Aggregate {
|
|||||||
super(series);
|
super(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: any): MeterAggregate {
|
static fromJson(json: any, locale: string): MeterAggregate {
|
||||||
const series = Series.fromJson(json['series']);
|
const series = Series.fromJson(json['series'], locale);
|
||||||
return new MeterAggregate(
|
return new MeterAggregate(
|
||||||
series,
|
series,
|
||||||
Value.fromJson(json['delta'], series, series.unit, series.decimals),
|
Value.fromJson(json['delta'], series.unit, series.decimals, locale),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Inject, Injectable, LOCALE_ID} from '@angular/core';
|
||||||
import {ApiService} from '../core/api.service';
|
import {ApiService} from '../core/api.service';
|
||||||
import {Alignment} from './Alignment';
|
import {Alignment} from './Alignment';
|
||||||
import {AggregationWrapperDto} from './AggregationWrapperDto';
|
import {AggregationWrapperDto} from './AggregationWrapperDto';
|
||||||
@ -34,6 +34,7 @@ export class SeriesService {
|
|||||||
]
|
]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(LOCALE_ID) readonly locale: string,
|
||||||
protected readonly api: ApiService,
|
protected readonly api: ApiService,
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
@ -57,7 +58,7 @@ export class SeriesService {
|
|||||||
if (this.subs.length !== 0) {
|
if (this.subs.length !== 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.subs.push(this.api.subscribe(['Series'], Series.fromJson, series => this.update(series)));
|
this.subs.push(this.api.subscribe(['Series'], j => Series.fromJson(j, this.locale), series => this.update(series)));
|
||||||
this.subs.push(this.api.subscribeConnection(connected => {
|
this.subs.push(this.api.subscribeConnection(connected => {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
this.all();
|
this.all();
|
||||||
@ -97,7 +98,7 @@ export class SeriesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
all(next?: Next<Series[]>) {
|
all(next?: Next<Series[]>) {
|
||||||
this.api.getList(['Series', 'all'], Series.fromJson, list => {
|
this.api.getList(['Series', 'all'], j => Series.fromJson(j, this.locale), list => {
|
||||||
list.forEach(item => this.update(item));
|
list.forEach(item => this.update(item));
|
||||||
if (next) {
|
if (next) {
|
||||||
next(list);
|
next(list);
|
||||||
@ -106,7 +107,7 @@ export class SeriesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
aggregations(alignment: Alignment, offset: number, next: Next<AggregationWrapperDto>) {
|
aggregations(alignment: Alignment, offset: number, next: Next<AggregationWrapperDto>) {
|
||||||
this.api.getSingle(['Series', 'agg', 'all', alignment.name, 'offset', offset], AggregationWrapperDto.fromJson, next);
|
this.api.getSingle(['Series', 'agg', 'all', alignment.name, 'offset', offset], j => AggregationWrapperDto.fromJson(j, this.locale), next);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,9 +12,9 @@ export class VaryingAggregate extends Aggregate {
|
|||||||
super(series);
|
super(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: any): VaryingAggregate {
|
static fromJson(json: any, locale: string): VaryingAggregate {
|
||||||
return new VaryingAggregate(
|
return new VaryingAggregate(
|
||||||
Series.fromJson(json['series']),
|
Series.fromJson(json['series'], locale),
|
||||||
json['min'] as number,
|
json['min'] as number,
|
||||||
json['avg'] as number,
|
json['avg'] as number,
|
||||||
json['max'] as number,
|
json['max'] as number,
|
||||||
|
|||||||
@ -1,25 +1,18 @@
|
|||||||
import {Unit} from "./Unit";
|
import {Unit} from "./Unit";
|
||||||
import {validateNumber} from "../core/validators";
|
import {validateNumber} from "../core/validators";
|
||||||
import {Series} from "../series/Series";
|
|
||||||
|
|
||||||
export class Value {
|
export class Value {
|
||||||
|
|
||||||
static readonly EMPTY: Value = new Value(0, Unit._UNKNOWN_, 1);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly value: number,
|
readonly value: number,
|
||||||
readonly unit: Unit,
|
readonly unit: Unit,
|
||||||
readonly decimals: number,
|
readonly decimals: number,
|
||||||
) {
|
readonly locale: string) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(value: any, series: Series, unit: Unit, decimals: number): Value {
|
static fromJson(value: any, unit: Unit, decimals: number, locale: string): Value {
|
||||||
return new Value(
|
return new Value(validateNumber(value), unit, decimals, locale);
|
||||||
validateNumber(value),
|
|
||||||
unit,
|
|
||||||
decimals,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get zero(): boolean {
|
get zero(): boolean {
|
||||||
@ -27,25 +20,25 @@ export class Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get formatted(): string {
|
get formatted(): string {
|
||||||
return `${this.value.toFixed(this.decimals)} ${this.unit.unit}`;
|
return `${this.value.toLocaleString(this.locale, {maximumFractionDigits: this.decimals, minimumFractionDigits: this.decimals})} ${this.unit.unit}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
negate() {
|
negate() {
|
||||||
return new Value(-this.value, this.unit, this.decimals);
|
return new Value(-this.value, this.unit, this.decimals, this.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
plus(other: Value | undefined): Value | undefined {
|
plus(other: Value | undefined): Value | undefined {
|
||||||
if (!other) {
|
if (!other) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return new Value(this.value + other.value, this.unit, this.decimals);
|
return new Value(this.value + other.value, this.unit, this.decimals, this.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
minus(other: Value | undefined): Value | undefined {
|
minus(other: Value | undefined): Value | undefined {
|
||||||
if (!other) {
|
if (!other) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return new Value(this.value - other.value, this.unit, this.decimals);
|
return new Value(this.value - other.value, this.unit, this.decimals, this.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
notNegative(): Value {
|
notNegative(): Value {
|
||||||
@ -59,14 +52,14 @@ export class Value {
|
|||||||
if (this.value === 0) {
|
if (this.value === 0) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new Value(0, this.unit, this.decimals);
|
return new Value(0, this.unit, this.decimals, this.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
percent(other: Value | undefined): Value | undefined {
|
percent(other: Value | undefined): Value | undefined {
|
||||||
if (!other || other.value === 0) {
|
if (!other || other.value === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return new Value(this.value / other.value * 100, Unit.PERCENT, 0);
|
return new Value(this.value / other.value * 100, Unit.PERCENT, 0, this.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,22 @@
|
|||||||
|
@import "../config.less";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-size: 6vw;
|
font-size: 6vw;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
background-color: @background;
|
||||||
|
color: @foreground;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
button, input, select {
|
button {
|
||||||
|
all: unset;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
|
padding: 0 0.25em;
|
||||||
|
background-color: #002433;
|
||||||
|
color: #008fca;
|
||||||
|
border: unset;
|
||||||
|
border-radius: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user