UI: Value card basic config
This commit is contained in:
parent
9d8a9943bf
commit
c0309ac1aa
@ -55,5 +55,50 @@
|
|||||||
</tb-color-settings>
|
</tb-color-settings>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tb-form-row">
|
||||||
|
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showIcon">
|
||||||
|
{{ 'widgets.value-card.icon' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
|
||||||
|
<mat-form-field fxFlex appearance="outline" class="number" subscriptSizing="dynamic">
|
||||||
|
<input matInput type="number" min="0" formControlName="iconSize" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
</mat-form-field>
|
||||||
|
<tb-css-unit-select fxFlex formControlName="iconSizeUnit"></tb-css-unit-select>
|
||||||
|
<tb-material-icon-select asBoxInput
|
||||||
|
[color]="valueCardWidgetConfigForm.get('iconColor').value?.color"
|
||||||
|
formControlName="icon">
|
||||||
|
</tb-material-icon-select>
|
||||||
|
<tb-color-settings formControlName="iconColor">
|
||||||
|
</tb-color-settings>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tb-form-row">
|
||||||
|
<div class="fixed-title-width" translate>widgets.value-card.value</div>
|
||||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
|
||||||
|
<tb-unit-input fxFlex formControlName="units"></tb-unit-input>
|
||||||
|
<mat-form-field fxFlex appearance="outline" class="number" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
<div matSuffix translate>widget-config.decimals-suffix</div>
|
||||||
|
</mat-form-field>
|
||||||
|
<tb-font-settings formControlName="valueFont"
|
||||||
|
[previewText]="valuePreviewFn">
|
||||||
|
</tb-font-settings>
|
||||||
|
<tb-color-settings formControlName="valueColor">
|
||||||
|
</tb-color-settings>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tb-form-row">
|
||||||
|
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showDate">
|
||||||
|
{{ 'widgets.value-card.date' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
|
||||||
|
<tb-date-format-select fxFlex formControlName="dateFormat"></tb-date-format-select>
|
||||||
|
<tb-font-settings formControlName="dateFont"
|
||||||
|
[previewText]="datePreviewFn">
|
||||||
|
</tb-font-settings>
|
||||||
|
<tb-color-settings formControlName="dateColor">
|
||||||
|
</tb-color-settings>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
import { ChangeDetectorRef, Component, Injector } from '@angular/core';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
@ -28,8 +28,13 @@ import {
|
|||||||
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
|
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
|
||||||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
||||||
import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
|
import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
|
||||||
import { isDefinedAndNotNull, isUndefined } from '@core/utils';
|
import { formatValue, isDefinedAndNotNull, isUndefined } from '@core/utils';
|
||||||
import { getLabel, setLabel } from '@home/components/widget/config/widget-settings.models';
|
import {
|
||||||
|
DateFormatProcessor,
|
||||||
|
DateFormatSettings,
|
||||||
|
getLabel,
|
||||||
|
setLabel
|
||||||
|
} from '@home/components/widget/config/widget-settings.models';
|
||||||
import {
|
import {
|
||||||
valueCardDefaultSettings,
|
valueCardDefaultSettings,
|
||||||
ValueCardLayout,
|
ValueCardLayout,
|
||||||
@ -65,9 +70,14 @@ export class ValueCardBasicConfigComponent extends BasicWidgetConfigComponent {
|
|||||||
|
|
||||||
valueCardWidgetConfigForm: UntypedFormGroup;
|
valueCardWidgetConfigForm: UntypedFormGroup;
|
||||||
|
|
||||||
|
valuePreviewFn = this._valuePreviewFn.bind(this);
|
||||||
|
|
||||||
|
datePreviewFn = this._datePreviewFn.bind(this);
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
protected widgetConfigComponent: WidgetConfigComponent,
|
protected widgetConfigComponent: WidgetConfigComponent,
|
||||||
private cd: ChangeDetectorRef,
|
private cd: ChangeDetectorRef,
|
||||||
|
private $injector: Injector,
|
||||||
private fb: UntypedFormBuilder) {
|
private fb: UntypedFormBuilder) {
|
||||||
super(store, widgetConfigComponent);
|
super(store, widgetConfigComponent);
|
||||||
}
|
}
|
||||||
@ -251,4 +261,16 @@ export class ValueCardBasicConfigComponent extends BasicWidgetConfigComponent {
|
|||||||
config.enableFullscreen = buttons.includes('fullscreen');
|
config.enableFullscreen = buttons.includes('fullscreen');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _valuePreviewFn(): string {
|
||||||
|
const units: string = this.valueCardWidgetConfigForm.get('units').value;
|
||||||
|
const decimals: number = this.valueCardWidgetConfigForm.get('decimals').value;
|
||||||
|
return formatValue(22, decimals, units, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _datePreviewFn(): string {
|
||||||
|
const dateFormat: DateFormatSettings = this.valueCardWidgetConfigForm.get('dateFormat').value;
|
||||||
|
const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat);
|
||||||
|
processor.update(Date.now());
|
||||||
|
return processor.formatted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
import { isDefinedAndNotNull, isNumber, isNumeric, parseFunction } from '@core/utils';
|
import { isDefinedAndNotNull, isNumber, isNumeric, parseFunction } from '@core/utils';
|
||||||
import { DataKey, Datasource, DatasourceData } from '@shared/models/widget.models';
|
import { DataKey, Datasource, DatasourceData } from '@shared/models/widget.models';
|
||||||
|
import { Injector } from '@angular/core';
|
||||||
|
import { DatePipe, formatDate } from '@angular/common';
|
||||||
|
import { DateAgoPipe } from '@shared/pipe/date-ago.pipe';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
export type ComponentStyle = {[klass: string]: any};
|
export type ComponentStyle = {[klass: string]: any};
|
||||||
|
|
||||||
@ -168,6 +172,104 @@ class FunctionColorProcessor extends ColorProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DateFormatSettings {
|
||||||
|
format?: string;
|
||||||
|
lastUpdateAgo?: boolean;
|
||||||
|
custom?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const simpleDateFormat = (format: string): DateFormatSettings => ({
|
||||||
|
format,
|
||||||
|
lastUpdateAgo: false,
|
||||||
|
custom: false
|
||||||
|
});
|
||||||
|
|
||||||
|
export const lastUpdateAgoDateFormat = (): DateFormatSettings => ({
|
||||||
|
format: null,
|
||||||
|
lastUpdateAgo: true,
|
||||||
|
custom: false
|
||||||
|
});
|
||||||
|
|
||||||
|
export const customDateFormat = (format: string): DateFormatSettings => ({
|
||||||
|
format,
|
||||||
|
lastUpdateAgo: false,
|
||||||
|
custom: true
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dateFormats = ['MMM dd yyyy HH:mm', 'dd MMM yyyy HH:mm', 'yyyy MMM dd HH:mm',
|
||||||
|
'MM/dd/yyyy HH:mm', 'dd/MM/yyyy HH:mm', 'yyyy/MM/dd HH:mm:ss']
|
||||||
|
.map(f => simpleDateFormat(f)).concat([lastUpdateAgoDateFormat(), customDateFormat('EEE, MMMM dd, yyyy')]);
|
||||||
|
|
||||||
|
export const compareDateFormats = (df1: DateFormatSettings, df2: DateFormatSettings): boolean => {
|
||||||
|
if (df1 === df2) {
|
||||||
|
return true;
|
||||||
|
} else if (df1 && df2) {
|
||||||
|
if (df1.lastUpdateAgo && df2.lastUpdateAgo) {
|
||||||
|
return true;
|
||||||
|
} else if (df1.custom && df2.custom) {
|
||||||
|
return true;
|
||||||
|
} else if (!df1.lastUpdateAgo && !df2.lastUpdateAgo && !df1.custom && !df2.custom) {
|
||||||
|
return df1.format === df2.format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export abstract class DateFormatProcessor {
|
||||||
|
|
||||||
|
static fromSettings($injector: Injector, settings: DateFormatSettings): DateFormatProcessor {
|
||||||
|
if (settings.lastUpdateAgo) {
|
||||||
|
return new LastUpdateAgoDateFormatProcessor($injector, settings);
|
||||||
|
} else {
|
||||||
|
return new SimpleDateFormatProcessor($injector, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatted = '';
|
||||||
|
|
||||||
|
protected constructor(protected $injector: Injector,
|
||||||
|
protected settings: DateFormatSettings) {
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract update(ts: string | number | Date): void;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SimpleDateFormatProcessor extends DateFormatProcessor {
|
||||||
|
|
||||||
|
private datePipe: DatePipe;
|
||||||
|
|
||||||
|
constructor(protected $injector: Injector,
|
||||||
|
protected settings: DateFormatSettings) {
|
||||||
|
super($injector, settings);
|
||||||
|
this.datePipe = $injector.get(DatePipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(ts: string| number | Date): void {
|
||||||
|
this.formatted = this.datePipe.transform(ts, this.settings.format);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LastUpdateAgoDateFormatProcessor extends DateFormatProcessor {
|
||||||
|
|
||||||
|
private dateAgoPipe: DateAgoPipe;
|
||||||
|
private translate: TranslateService;
|
||||||
|
|
||||||
|
constructor(protected $injector: Injector,
|
||||||
|
protected settings: DateFormatSettings) {
|
||||||
|
super($injector, settings);
|
||||||
|
this.dateAgoPipe = $injector.get(DateAgoPipe);
|
||||||
|
this.translate = $injector.get(TranslateService);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(ts: string| number | Date): void {
|
||||||
|
this.formatted = this.translate.instant('date.last-update-n-ago-text',
|
||||||
|
{agoText: this.dateAgoPipe.transform(ts, {applyAgo: true, short: true, textPart: true})});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export enum BackgroundType {
|
export enum BackgroundType {
|
||||||
image = 'image',
|
image = 'image',
|
||||||
imageUrl = 'imageUrl',
|
imageUrl = 'imageUrl',
|
||||||
|
|||||||
@ -63,7 +63,7 @@
|
|||||||
<div *ngIf="showLabel" [style]="labelStyle" [style.color]="labelColor.color">{{ label }}</div>
|
<div *ngIf="showLabel" [style]="labelStyle" [style.color]="labelColor.color">{{ label }}</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #dateTpl>
|
<ng-template #dateTpl>
|
||||||
<div *ngIf="showDate" [style]="dateStyle" [style.color]="dateColor.color">{{ dateText }}</div>
|
<div *ngIf="showDate" [style]="dateStyle" [style.color]="dateColor.color">{{ dateFormat.formatted }}</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #valueTpl>
|
<ng-template #valueTpl>
|
||||||
<div class="tb-value-card-value" [style]="valueStyle" [style.color]="valueColor.color">{{ valueText }}</div>
|
<div class="tb-value-card-value" [style]="valueStyle" [style.color]="valueColor.color">{{ valueText }}</div>
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import { DatePipe } from '@angular/common';
|
|||||||
import {
|
import {
|
||||||
backgroundStyle,
|
backgroundStyle,
|
||||||
ColorProcessor,
|
ColorProcessor,
|
||||||
ComponentStyle,
|
ComponentStyle, DateFormatProcessor,
|
||||||
getDataKey,
|
getDataKey,
|
||||||
getLabel,
|
getLabel,
|
||||||
getSingleTsValue,
|
getSingleTsValue,
|
||||||
@ -62,7 +62,7 @@ export class ValueCardWidgetComponent implements OnInit {
|
|||||||
valueColor: ColorProcessor;
|
valueColor: ColorProcessor;
|
||||||
|
|
||||||
showDate = true;
|
showDate = true;
|
||||||
dateText = '';
|
dateFormat: DateFormatProcessor;
|
||||||
dateStyle: ComponentStyle = {};
|
dateStyle: ComponentStyle = {};
|
||||||
dateColor: ColorProcessor;
|
dateColor: ColorProcessor;
|
||||||
|
|
||||||
@ -70,7 +70,6 @@ export class ValueCardWidgetComponent implements OnInit {
|
|||||||
overlayStyle: ComponentStyle = {};
|
overlayStyle: ComponentStyle = {};
|
||||||
|
|
||||||
private horizontal = false;
|
private horizontal = false;
|
||||||
private dateFormat: string;
|
|
||||||
private decimals = 0;
|
private decimals = 0;
|
||||||
private units = '';
|
private units = '';
|
||||||
|
|
||||||
@ -110,7 +109,7 @@ export class ValueCardWidgetComponent implements OnInit {
|
|||||||
this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor);
|
this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor);
|
||||||
|
|
||||||
this.showDate = this.settings.showDate;
|
this.showDate = this.settings.showDate;
|
||||||
this.dateFormat = this.settings.dateFormat;
|
this.dateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.dateFormat);
|
||||||
this.dateStyle = textStyle(this.settings.dateFont, '1.33', '0.25px');
|
this.dateStyle = textStyle(this.settings.dateFont, '1.33', '0.25px');
|
||||||
this.dateColor = ColorProcessor.fromSettings(this.settings.dateColor);
|
this.dateColor = ColorProcessor.fromSettings(this.settings.dateColor);
|
||||||
|
|
||||||
@ -126,15 +125,16 @@ export class ValueCardWidgetComponent implements OnInit {
|
|||||||
|
|
||||||
public onDataUpdated() {
|
public onDataUpdated() {
|
||||||
const tsValue = getSingleTsValue(this.ctx.data);
|
const tsValue = getSingleTsValue(this.ctx.data);
|
||||||
|
let ts;
|
||||||
let value;
|
let value;
|
||||||
if (tsValue) {
|
if (tsValue) {
|
||||||
|
ts = tsValue[0];
|
||||||
value = tsValue[1];
|
value = tsValue[1];
|
||||||
this.valueText = formatValue(value, this.decimals, this.units, true);
|
this.valueText = formatValue(value, this.decimals, this.units, true);
|
||||||
this.dateText = this.date.transform(tsValue[0], this.dateFormat);
|
|
||||||
} else {
|
} else {
|
||||||
this.valueText = 'N/A';
|
this.valueText = 'N/A';
|
||||||
this.dateText = '';
|
|
||||||
}
|
}
|
||||||
|
this.dateFormat.update(ts);
|
||||||
this.iconColor.update(value);
|
this.iconColor.update(value);
|
||||||
this.labelColor.update(value);
|
this.labelColor.update(value);
|
||||||
this.valueColor.update(value);
|
this.valueColor.update(value);
|
||||||
|
|||||||
@ -19,8 +19,8 @@ import {
|
|||||||
BackgroundType,
|
BackgroundType,
|
||||||
ColorSettings,
|
ColorSettings,
|
||||||
constantColor,
|
constantColor,
|
||||||
cssUnit,
|
cssUnit, DateFormatSettings,
|
||||||
Font
|
Font, lastUpdateAgoDateFormat
|
||||||
} from '@home/components/widget/config/widget-settings.models';
|
} from '@home/components/widget/config/widget-settings.models';
|
||||||
|
|
||||||
export enum ValueCardLayout {
|
export enum ValueCardLayout {
|
||||||
@ -75,7 +75,7 @@ export interface ValueCardWidgetSettings {
|
|||||||
valueFont: Font;
|
valueFont: Font;
|
||||||
valueColor: ColorSettings;
|
valueColor: ColorSettings;
|
||||||
showDate: boolean;
|
showDate: boolean;
|
||||||
dateFormat: string;
|
dateFormat: DateFormatSettings;
|
||||||
dateFont: Font;
|
dateFont: Font;
|
||||||
dateColor: ColorSettings;
|
dateColor: ColorSettings;
|
||||||
background: BackgroundSettings;
|
background: BackgroundSettings;
|
||||||
@ -106,7 +106,7 @@ export const valueCardDefaultSettings = (horizontal: boolean): ValueCardWidgetSe
|
|||||||
},
|
},
|
||||||
valueColor: constantColor('rgba(0, 0, 0, 0.87)'),
|
valueColor: constantColor('rgba(0, 0, 0, 0.87)'),
|
||||||
showDate: true,
|
showDate: true,
|
||||||
dateFormat: 'yyyy-MM-dd HH:mm:ss',
|
dateFormat: lastUpdateAgoDateFormat(),
|
||||||
dateFont: {
|
dateFont: {
|
||||||
family: 'Roboto',
|
family: 'Roboto',
|
||||||
size: 12,
|
size: 12,
|
||||||
|
|||||||
@ -108,6 +108,7 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit
|
|||||||
|
|
||||||
removeRange(index: number) {
|
removeRange(index: number) {
|
||||||
this.rangeListFormArray.removeAt(index);
|
this.rangeListFormArray.removeAt(index);
|
||||||
|
this.colorSettingsFormGroup.markAsDirty();
|
||||||
setTimeout(() => {this.popover?.updatePosition();}, 0);
|
setTimeout(() => {this.popover?.updatePosition();}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +116,8 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit
|
|||||||
const newRange: ColorRange = {
|
const newRange: ColorRange = {
|
||||||
color: 'rgba(0,0,0,0.87)'
|
color: 'rgba(0,0,0,0.87)'
|
||||||
};
|
};
|
||||||
this.rangeListFormArray.push(this.colorRangeControl(newRange), {emitEvent: true});
|
this.rangeListFormArray.push(this.colorRangeControl(newRange));
|
||||||
|
this.colorSettingsFormGroup.markAsDirty();
|
||||||
setTimeout(() => {this.popover?.updatePosition();}, 0);
|
setTimeout(() => {this.popover?.updatePosition();}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<mat-form-field appearance="outline" subscriptSizing="dynamic" style="width: 100%;">
|
||||||
|
<mat-select [formControl]="cssUnitFormControl">
|
||||||
|
<mat-option *ngFor="let cssUnit of cssUnitsList" [value]="cssUnit">{{ cssUnit }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { Component, forwardRef, Input, OnInit } from '@angular/core';
|
||||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
|
||||||
|
import { cssUnit, cssUnits } from '@home/components/widget/config/widget-settings.models';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-css-unit-select',
|
||||||
|
templateUrl: './css-unit-select.component.html',
|
||||||
|
styleUrls: [],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => CssUnitSelectComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CssUnitSelectComponent implements OnInit, ControlValueAccessor {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
cssUnitsList = cssUnits;
|
||||||
|
|
||||||
|
cssUnitFormControl: UntypedFormControl;
|
||||||
|
|
||||||
|
modelValue: cssUnit;
|
||||||
|
|
||||||
|
private propagateChange = null;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.cssUnitFormControl = new UntypedFormControl();
|
||||||
|
this.cssUnitFormControl.valueChanges.subscribe((value: cssUnit) => {
|
||||||
|
this.updateModel(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
if (this.disabled) {
|
||||||
|
this.cssUnitFormControl.disable();
|
||||||
|
} else {
|
||||||
|
this.cssUnitFormControl.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: cssUnit): void {
|
||||||
|
this.modelValue = value;
|
||||||
|
this.cssUnitFormControl.patchValue(this.modelValue, {emitEvent: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModel(value: cssUnit): void {
|
||||||
|
if (this.modelValue !== value) {
|
||||||
|
this.modelValue = value;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<mat-form-field appearance="outline" subscriptSizing="dynamic" style="width: 100%;">
|
||||||
|
<mat-select [formControl]="dateFormatFormControl" [compareWith]="dateFormatsCompare" >
|
||||||
|
<mat-option *ngFor="let dateFormat of dateFormatList" [value]="dateFormat">{{ dateFormatDisplayValue(dateFormat) }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<button #customFormatButton
|
||||||
|
*ngIf="dateFormatFormControl.value?.custom" matSuffix mat-icon-button (click)="openDateFormatSettingsPopup($event, customFormatButton)">
|
||||||
|
<tb-icon>edit</tb-icon>
|
||||||
|
</button>
|
||||||
|
</mat-form-field>
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { Component, forwardRef, Input, OnInit, Renderer2, ViewChild, ViewContainerRef } from '@angular/core';
|
||||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms';
|
||||||
|
import {
|
||||||
|
compareDateFormats,
|
||||||
|
dateFormats,
|
||||||
|
DateFormatSettings
|
||||||
|
} from '@home/components/widget/config/widget-settings.models';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { DatePipe } from '@angular/common';
|
||||||
|
import { MatButton } from '@angular/material/button';
|
||||||
|
import { TbPopoverService } from '@shared/components/popover.service';
|
||||||
|
import { deepClone } from '@core/utils';
|
||||||
|
import {
|
||||||
|
DateFormatSettingsPanelComponent
|
||||||
|
} from '@home/components/widget/lib/settings/common/date-format-settings-panel.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-date-format-select',
|
||||||
|
templateUrl: './date-format-select.component.html',
|
||||||
|
styleUrls: [],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => DateFormatSelectComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class DateFormatSelectComponent implements OnInit, ControlValueAccessor {
|
||||||
|
|
||||||
|
@ViewChild('customFormatButton', {static: false})
|
||||||
|
customFormatButton: MatButton;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
dateFormatList = dateFormats;
|
||||||
|
|
||||||
|
dateFormatsCompare = compareDateFormats;
|
||||||
|
|
||||||
|
dateFormatFormControl: UntypedFormControl;
|
||||||
|
|
||||||
|
modelValue: DateFormatSettings;
|
||||||
|
|
||||||
|
private propagateChange = null;
|
||||||
|
|
||||||
|
private formatCache: {[format: string]: string} = {};
|
||||||
|
|
||||||
|
constructor(private translate: TranslateService,
|
||||||
|
private date: DatePipe,
|
||||||
|
private popoverService: TbPopoverService,
|
||||||
|
private renderer: Renderer2,
|
||||||
|
private viewContainerRef: ViewContainerRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.dateFormatFormControl = new UntypedFormControl();
|
||||||
|
this.dateFormatFormControl.valueChanges.subscribe((value: DateFormatSettings) => {
|
||||||
|
this.updateModel(value);
|
||||||
|
if (value?.custom) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.openDateFormatSettingsPopup(null, this.customFormatButton);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
if (this.disabled) {
|
||||||
|
this.dateFormatFormControl.disable();
|
||||||
|
} else {
|
||||||
|
this.dateFormatFormControl.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: DateFormatSettings): void {
|
||||||
|
this.modelValue = value;
|
||||||
|
this.dateFormatFormControl.patchValue(this.modelValue, {emitEvent: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModel(value: DateFormatSettings): void {
|
||||||
|
if (!compareDateFormats(this.modelValue, value)) {
|
||||||
|
this.modelValue = value;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dateFormatDisplayValue(value: DateFormatSettings): string {
|
||||||
|
if (value.custom) {
|
||||||
|
return this.translate.instant('date.custom-date');
|
||||||
|
} else if (value.lastUpdateAgo) {
|
||||||
|
return this.translate.instant('date.last-update-n-ago');
|
||||||
|
} else {
|
||||||
|
if (!this.formatCache[value.format]) {
|
||||||
|
this.formatCache[value.format] = this.date.transform(Date.now(), value.format);
|
||||||
|
}
|
||||||
|
return this.formatCache[value.format];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openDateFormatSettingsPopup($event: Event, matButton: MatButton) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
const trigger = matButton._elementRef.nativeElement;
|
||||||
|
if (this.popoverService.hasPopover(trigger)) {
|
||||||
|
this.popoverService.hidePopover(trigger);
|
||||||
|
} else {
|
||||||
|
const ctx: any = {
|
||||||
|
dateFormat: deepClone(this.modelValue)
|
||||||
|
};
|
||||||
|
const dateFormatSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
||||||
|
this.viewContainerRef, DateFormatSettingsPanelComponent, 'top', true, null,
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
{}, {}, true);
|
||||||
|
dateFormatSettingsPanelPopover.tbComponentRef.instance.popover = dateFormatSettingsPanelPopover;
|
||||||
|
dateFormatSettingsPanelPopover.tbComponentRef.instance.dateFormatApplied.subscribe((dateFormat) => {
|
||||||
|
dateFormatSettingsPanelPopover.hide();
|
||||||
|
this.modelValue = dateFormat;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<div class="tb-date-format-settings-panel">
|
||||||
|
<div class="tb-date-format-settings-title" translate>date.custom-date</div>
|
||||||
|
<div class="tb-form-row no-border no-padding">
|
||||||
|
<div class="fixed-title-width" translate>date.format</div>
|
||||||
|
<mat-form-field class="tb-date-format-input" required fxFlex appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<input matInput [formControl]="dateFormatFormControl" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
<div matSuffix tb-help-popup="date/date-format" [tb-help-popup-style]="{width: '800px'}"></div>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<div class="tb-form-row no-border no-padding date-format-preview">
|
||||||
|
<div class="fixed-title-width" translate>date.preview</div>
|
||||||
|
<div class="preview-text" fxFlex>{{ previewText }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="tb-date-format-settings-panel-buttons">
|
||||||
|
<button mat-button
|
||||||
|
color="primary"
|
||||||
|
type="button"
|
||||||
|
(click)="cancel()">
|
||||||
|
{{ 'action.cancel' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button
|
||||||
|
color="primary"
|
||||||
|
type="button"
|
||||||
|
(click)="applyDateFormat()"
|
||||||
|
[disabled]="dateFormatFormControl.invalid || !dateFormatFormControl.dirty">
|
||||||
|
{{ 'action.apply' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import '../../../../../../../../scss/constants';
|
||||||
|
|
||||||
|
.tb-date-format-settings-panel {
|
||||||
|
width: 500px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
@media #{$mat-xs} {
|
||||||
|
width: 90vw;
|
||||||
|
}
|
||||||
|
.tb-date-format-settings-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px;
|
||||||
|
letter-spacing: 0.25px;
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
.tb-form-row {
|
||||||
|
.fixed-title-width {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
&.date-format-preview {
|
||||||
|
align-items: flex-start;
|
||||||
|
.preview-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
color: rgba(0, 0, 0, 0.38);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mat-mdc-form-field.tb-date-format-input {
|
||||||
|
.mat-mdc-text-field-wrapper.mdc-text-field--outlined {
|
||||||
|
.mat-mdc-form-field-icon-suffix {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tb-date-format-settings-panel-buttons {
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 16px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { PageComponent } from '@shared/components/page.component';
|
||||||
|
import { DateFormatSettings } from '@home/components/widget/config/widget-settings.models';
|
||||||
|
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||||
|
import { UntypedFormControl, Validators } from '@angular/forms';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '@core/core.state';
|
||||||
|
import { DatePipe } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-date-format-settings-panel',
|
||||||
|
templateUrl: './date-format-settings-panel.component.html',
|
||||||
|
providers: [],
|
||||||
|
styleUrls: ['./date-format-settings-panel.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class DateFormatSettingsPanelComponent extends PageComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
dateFormat: DateFormatSettings;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
popover: TbPopoverComponent<DateFormatSettingsPanelComponent>;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
dateFormatApplied = new EventEmitter<DateFormatSettings>();
|
||||||
|
|
||||||
|
dateFormatFormControl: UntypedFormControl;
|
||||||
|
|
||||||
|
previewText = '';
|
||||||
|
|
||||||
|
constructor(private date: DatePipe,
|
||||||
|
protected store: Store<AppState>) {
|
||||||
|
super(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.dateFormatFormControl = new UntypedFormControl(this.dateFormat.format, [Validators.required]);
|
||||||
|
this.dateFormatFormControl.valueChanges.subscribe((value: string) => {
|
||||||
|
this.previewText = this.date.transform(Date.now(), value);
|
||||||
|
});
|
||||||
|
this.previewText = this.date.transform(Date.now(), this.dateFormat.format);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.popover?.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyDateFormat() {
|
||||||
|
this.dateFormat.format = this.dateFormatFormControl.value;
|
||||||
|
this.dateFormatApplied.emit(this.dateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -23,11 +23,7 @@
|
|||||||
<mat-form-field fxFlex appearance="outline" class="number" subscriptSizing="dynamic">
|
<mat-form-field fxFlex appearance="outline" class="number" subscriptSizing="dynamic">
|
||||||
<input matInput type="number" min="0" formControlName="size" placeholder="{{ 'widget-config.set' | translate }}">
|
<input matInput type="number" min="0" formControlName="size" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic">
|
<tb-css-unit-select fxFlex formControlName="sizeUnit"></tb-css-unit-select>
|
||||||
<mat-select formControlName="sizeUnit">
|
|
||||||
<mat-option *ngFor="let cssUnit of cssUnitsList" [value]="cssUnit">{{ cssUnit }}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-row no-border no-padding">
|
<div class="tb-form-row no-border no-padding">
|
||||||
|
|||||||
@ -28,7 +28,6 @@ import { PageComponent } from '@shared/components/page.component';
|
|||||||
import {
|
import {
|
||||||
commonFonts,
|
commonFonts,
|
||||||
ComponentStyle,
|
ComponentStyle,
|
||||||
cssUnits,
|
|
||||||
Font,
|
Font,
|
||||||
fontStyles,
|
fontStyles,
|
||||||
fontStyleTranslations,
|
fontStyleTranslations,
|
||||||
@ -66,8 +65,6 @@ export class FontSettingsPanelComponent extends PageComponent implements OnInit
|
|||||||
|
|
||||||
@ViewChild('familyInput', {static: true}) familyInput: ElementRef;
|
@ViewChild('familyInput', {static: true}) familyInput: ElementRef;
|
||||||
|
|
||||||
cssUnitsList = cssUnits;
|
|
||||||
|
|
||||||
fontWeightsList = fontWeights;
|
fontWeightsList = fontWeights;
|
||||||
|
|
||||||
fontWeightTranslationsMap = fontWeightTranslations;
|
fontWeightTranslationsMap = fontWeightTranslations;
|
||||||
|
|||||||
@ -276,6 +276,11 @@ import { ColorSettingsComponent } from '@home/components/widget/lib/settings/com
|
|||||||
import {
|
import {
|
||||||
ColorSettingsPanelComponent
|
ColorSettingsPanelComponent
|
||||||
} from '@home/components/widget/lib/settings/common/color-settings-panel.component';
|
} from '@home/components/widget/lib/settings/common/color-settings-panel.component';
|
||||||
|
import { CssUnitSelectComponent } from '@home/components/widget/lib/settings/common/css-unit-select.component';
|
||||||
|
import { DateFormatSelectComponent } from '@home/components/widget/lib/settings/common/date-format-select.component';
|
||||||
|
import {
|
||||||
|
DateFormatSettingsPanelComponent
|
||||||
|
} from '@home/components/widget/lib/settings/common/date-format-settings-panel.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -383,7 +388,10 @@ import {
|
|||||||
FontSettingsComponent,
|
FontSettingsComponent,
|
||||||
FontSettingsPanelComponent,
|
FontSettingsPanelComponent,
|
||||||
ColorSettingsComponent,
|
ColorSettingsComponent,
|
||||||
ColorSettingsPanelComponent
|
ColorSettingsPanelComponent,
|
||||||
|
CssUnitSelectComponent,
|
||||||
|
DateFormatSelectComponent,
|
||||||
|
DateFormatSettingsPanelComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -495,7 +503,10 @@ import {
|
|||||||
FontSettingsComponent,
|
FontSettingsComponent,
|
||||||
FontSettingsPanelComponent,
|
FontSettingsPanelComponent,
|
||||||
ColorSettingsComponent,
|
ColorSettingsComponent,
|
||||||
ColorSettingsPanelComponent
|
ColorSettingsPanelComponent,
|
||||||
|
CssUnitSelectComponent,
|
||||||
|
DateFormatSelectComponent,
|
||||||
|
DateFormatSettingsPanelComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class WidgetSettingsModule {
|
export class WidgetSettingsModule {
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import {
|
|||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { share } from 'rxjs/operators';
|
import { share } from 'rxjs/operators';
|
||||||
import { HelpService } from '@core/services/help.service';
|
import { HelpService } from '@core/services/help.service';
|
||||||
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-help-markdown',
|
selector: 'tb-help-markdown',
|
||||||
@ -36,7 +37,9 @@ export class HelpMarkdownComponent implements OnDestroy, OnInit, OnChanges {
|
|||||||
|
|
||||||
@Input() helpContent: string;
|
@Input() helpContent: string;
|
||||||
|
|
||||||
@Input() visible: boolean;
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
visible: boolean;
|
||||||
|
|
||||||
@Input() style: { [klass: string]: any } = {};
|
@Input() style: { [klass: string]: any } = {};
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<mat-form-field appearance="outline" class="tb-inline-field tb-suffix-show-on-hover" subscriptSizing="dynamic">
|
<mat-form-field appearance="outline" class="tb-inline-field tb-suffix-show-on-hover" subscriptSizing="dynamic" style="width: 100%;">
|
||||||
<input matInput #unitInput [formControl]="unitsFormControl"
|
<input matInput #unitInput [formControl]="unitsFormControl"
|
||||||
placeholder="{{ 'widget-config.set' | translate }}"
|
placeholder="{{ 'widget-config.set' | translate }}"
|
||||||
(focusin)="onFocus()"
|
(focusin)="onFocus()"
|
||||||
|
|||||||
@ -37,19 +37,21 @@ export class DateAgoPipe implements PipeTransform {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(value: number, args?: any): string {
|
transform(value: string| number | Date, args?: any): string {
|
||||||
if (value) {
|
if (value) {
|
||||||
const applyAgo = !!args?.applyAgo;
|
const applyAgo = !!args?.applyAgo;
|
||||||
|
const short = !!args?.short;
|
||||||
|
const textPart = !!args?.textPart;
|
||||||
const ms = Math.floor((+new Date() - +new Date(value)));
|
const ms = Math.floor((+new Date() - +new Date(value)));
|
||||||
if (ms < 29 * SECOND) { // less than 30 seconds ago will show as 'Just now'
|
if (ms < 29 * SECOND) { // less than 30 seconds ago will show as 'Just now'
|
||||||
return this.translate.instant('timewindow.just-now');
|
return this.translate.instant(textPart ? 'timewindow.just-now-lower' : 'timewindow.just-now');
|
||||||
}
|
}
|
||||||
let counter;
|
let counter;
|
||||||
// eslint-disable-next-line guard-for-in
|
// eslint-disable-next-line guard-for-in
|
||||||
for (const i in intervals) {
|
for (const i in intervals) {
|
||||||
counter = Math.floor(ms / intervals[i]);
|
counter = Math.floor(ms / intervals[i]);
|
||||||
if (counter > 0) {
|
if (counter > 0) {
|
||||||
let res = this.translate.instant(`timewindow.${i}`, {[i]: counter});
|
let res = this.translate.instant(`timewindow.${i+(short ? '-short' : '')}`, {[i]: counter});
|
||||||
if (applyAgo) {
|
if (applyAgo) {
|
||||||
res += ' ' + this.translate.instant('timewindow.ago');
|
res += ' ' + this.translate.instant('timewindow.ago');
|
||||||
}
|
}
|
||||||
|
|||||||
88
ui-ngx/src/assets/help/en_US/date/date-format.md
Normal file
88
ui-ngx/src/assets/help/en_US/date/date-format.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#### Pre-defined format options
|
||||||
|
|
||||||
|
| Option | Equivalent to | Examples (given in `en-US` locale) |
|
||||||
|
|------------|---------------------------------|-----------------------------------------------|
|
||||||
|
| short | M/d/yy, h:mm a | 6/15/15, 9:03 AM |
|
||||||
|
| medium | MMM d, y, h:mm:ss a | Jun 15, 2015, 9:03:01 AM |
|
||||||
|
| long | MMMM d, y, h:mm:ss a z | June 15, 2015 at 9:03:01 AM GMT+1 |
|
||||||
|
| full | EEEE, MMMM d, y, h:mm:ss a zzzz | Monday, June 15, 2015 at 9:03:01 AM GMT+01:00 |
|
||||||
|
| shortDate | M/d/yy | 6/15/15 |
|
||||||
|
| mediumDate | MMM d, y | Jun 15, 2015 |
|
||||||
|
| longDate | MMMM d, y | June 15, 2015 |
|
||||||
|
| fullDate | EEEE, MMMM d, y | Monday, June 15, 2015 |
|
||||||
|
| shortTime | h:mm a | 9:03 AM |
|
||||||
|
| mediumTime | h:mm:ss a | 9:03:01 AM |
|
||||||
|
| longTime | h:mm:ss a z | 9:03:01 AM GMT+1 |
|
||||||
|
| fullTime | h:mm:ss a zzzz | 9:03:01 AM GMT+01:00 |
|
||||||
|
|
||||||
|
#### Custom format options
|
||||||
|
|
||||||
|
You can construct a format string using symbols to specify the components
|
||||||
|
of a date-time value, as described in the following table.
|
||||||
|
Format details depend on the locale.
|
||||||
|
Fields marked with (*) are only available in the extra data set for the given locale.
|
||||||
|
|
||||||
|
| Field type | Format | Description | Example Value |
|
||||||
|
|---------------------|-------------|--------------------------------------------------------------|------------------------------------------------------------|
|
||||||
|
| Era | G, GG & GGG | Abbreviated | AD |
|
||||||
|
| | GGGG | Wide | Anno Domini |
|
||||||
|
| | GGGGG | Narrow | A |
|
||||||
|
| Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 |
|
||||||
|
| | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
||||||
|
| | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
||||||
|
| | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
||||||
|
| Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 |
|
||||||
|
| | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
||||||
|
| | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
||||||
|
| | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
||||||
|
| Month | M | Numeric: 1 digit | 9, 12 |
|
||||||
|
| | MM | Numeric: 2 digits + zero padded | 09, 12 |
|
||||||
|
| | MMM | Abbreviated | Sep |
|
||||||
|
| | MMMM | Wide | September |
|
||||||
|
| | MMMMM | Narrow | S |
|
||||||
|
| Month standalone | L | Numeric: 1 digit | 9, 12 |
|
||||||
|
| | LL | Numeric: 2 digits + zero padded | 09, 12 |
|
||||||
|
| | LLL | Abbreviated | Sep |
|
||||||
|
| | LLLL | Wide | September |
|
||||||
|
| | LLLLL | Narrow | S |
|
||||||
|
| Week of year | w | Numeric: minimum digits | 1... 53 |
|
||||||
|
| | ww | Numeric: 2 digits + zero padded | 01... 53 |
|
||||||
|
| Week of month | W | Numeric: 1 digit | 1... 5 |
|
||||||
|
| Day of month | d | Numeric: minimum digits | 1 |
|
||||||
|
| | dd | Numeric: 2 digits + zero padded | 01 |
|
||||||
|
| Week day | E, EE & EEE | Abbreviated | Tue |
|
||||||
|
| | EEEE | Wide | Tuesday |
|
||||||
|
| | EEEEE | Narrow | T |
|
||||||
|
| | EEEEEE | Short | Tu |
|
||||||
|
| Week day standalone | c, cc | Numeric: 1 digit | 2 |
|
||||||
|
| | ccc | Abbreviated | Tue |
|
||||||
|
| | cccc | Wide | Tuesday |
|
||||||
|
| | ccccc | Narrow | T |
|
||||||
|
| | cccccc | Short | Tu |
|
||||||
|
| Period | a, aa & aaa | Abbreviated | am/pm or AM/PM |
|
||||||
|
| | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem |
|
||||||
|
| | aaaaa | Narrow | a/p |
|
||||||
|
| Period* | B, BB & BBB | Abbreviated | mid. |
|
||||||
|
| | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night |
|
||||||
|
| | BBBBB | Narrow | md |
|
||||||
|
| Period standalone* | b, bb & bbb | Abbreviated | mid. |
|
||||||
|
| | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night |
|
||||||
|
| | bbbbb | Narrow | md |
|
||||||
|
| Hour 1-12 | h | Numeric: minimum digits | 1, 12 |
|
||||||
|
| | hh | Numeric: 2 digits + zero padded | 01, 12 |
|
||||||
|
| Hour 0-23 | H | Numeric: minimum digits | 0, 23 |
|
||||||
|
| | HH | Numeric: 2 digits + zero padded | 00, 23 |
|
||||||
|
| Minute | m | Numeric: minimum digits | 8, 59 |
|
||||||
|
| | mm | Numeric: 2 digits + zero padded | 08, 59 |
|
||||||
|
| Second | s | Numeric: minimum digits | 0... 59 |
|
||||||
|
| | ss | Numeric: 2 digits + zero padded | 00... 59 |
|
||||||
|
| Fractional seconds | S | Numeric: 1 digit | 0... 9 |
|
||||||
|
| | SS | Numeric: 2 digits + zero padded | 00... 99 |
|
||||||
|
| | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 |
|
||||||
|
| Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 |
|
||||||
|
| | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 |
|
||||||
|
| | Z, ZZ & ZZZ | ISO8601 basic format | -0800 |
|
||||||
|
| | ZZZZ | Long localized GMT format | GMT-8:00 |
|
||||||
|
| | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 |
|
||||||
|
| | O, OO & OOO | Short localized GMT format | GMT-8 |
|
||||||
|
| | OOOO | Long localized GMT format | GMT-08:00 |
|
||||||
@ -940,6 +940,13 @@
|
|||||||
"edges": "Customer edge instances",
|
"edges": "Customer edge instances",
|
||||||
"manage-edges": "Manage edges"
|
"manage-edges": "Manage edges"
|
||||||
},
|
},
|
||||||
|
"date": {
|
||||||
|
"last-update-n-ago": "Last update N ago",
|
||||||
|
"last-update-n-ago-text": "Last update {{ agoText }}",
|
||||||
|
"custom-date": "Custom date",
|
||||||
|
"format": "Format",
|
||||||
|
"preview": "Preview"
|
||||||
|
},
|
||||||
"datetime": {
|
"datetime": {
|
||||||
"date-from": "Date from",
|
"date-from": "Date from",
|
||||||
"time-from": "Time from",
|
"time-from": "Time from",
|
||||||
@ -3832,15 +3839,22 @@
|
|||||||
"timewindow": {
|
"timewindow": {
|
||||||
"timewindow": "Timewindow",
|
"timewindow": "Timewindow",
|
||||||
"years": "{ years, plural, =1 { year } other {# years } }",
|
"years": "{ years, plural, =1 { year } other {# years } }",
|
||||||
|
"years-short": "{{ years }}y",
|
||||||
"months": "{ months, plural, =1 { month } other {# months } }",
|
"months": "{ months, plural, =1 { month } other {# months } }",
|
||||||
|
"months-short": "{{ months }}M",
|
||||||
"weeks": "{ weeks, plural, =1 { week } other {# weeks } }",
|
"weeks": "{ weeks, plural, =1 { week } other {# weeks } }",
|
||||||
|
"weeks-short": "{{ weeks }}w",
|
||||||
"days": "{ days, plural, =1 { day } other {# days } }",
|
"days": "{ days, plural, =1 { day } other {# days } }",
|
||||||
|
"days-short": "{{ days }}d",
|
||||||
"hours": "{ hours, plural, =0 { hour } =1 {1 hour } other {# hours } }",
|
"hours": "{ hours, plural, =0 { hour } =1 {1 hour } other {# hours } }",
|
||||||
"hr": "{{ hr }} hr",
|
"hr": "{{ hr }} hr",
|
||||||
|
"hr-short": "{{ hr }}h",
|
||||||
"minutes": "{ minutes, plural, =0 { minute } =1 {1 minute } other {# minutes } }",
|
"minutes": "{ minutes, plural, =0 { minute } =1 {1 minute } other {# minutes } }",
|
||||||
"min": "{{ min }} min",
|
"min": "{{ min }} min",
|
||||||
|
"min-short": "{{ min }}m",
|
||||||
"seconds": "{ seconds, plural, =0 { second } =1 {1 second } other {# seconds } }",
|
"seconds": "{ seconds, plural, =0 { second } =1 {1 second } other {# seconds } }",
|
||||||
"sec": "{{ sec }} sec",
|
"sec": "{{ sec }} sec",
|
||||||
|
"sec-short": "{{ sec }}s",
|
||||||
"short": {
|
"short": {
|
||||||
"days": "{ days, plural, =1 {1 day } other {# days } }",
|
"days": "{ days, plural, =1 {1 day } other {# days } }",
|
||||||
"hours": "{ hours, plural, =1 {1 hour } other {# hours } }",
|
"hours": "{ hours, plural, =1 {1 hour } other {# hours } }",
|
||||||
@ -3859,6 +3873,7 @@
|
|||||||
"hide": "Hide",
|
"hide": "Hide",
|
||||||
"interval": "Interval",
|
"interval": "Interval",
|
||||||
"just-now": "Just now",
|
"just-now": "Just now",
|
||||||
|
"just-now-lower": "just now",
|
||||||
"ago": "ago"
|
"ago": "ago"
|
||||||
},
|
},
|
||||||
"unit": {
|
"unit": {
|
||||||
@ -4208,6 +4223,7 @@
|
|||||||
"decimals": "Number of digits after floating point",
|
"decimals": "Number of digits after floating point",
|
||||||
"units-short": "Units",
|
"units-short": "Units",
|
||||||
"decimals-short": "Decimals",
|
"decimals-short": "Decimals",
|
||||||
|
"decimals-suffix": "decimals",
|
||||||
"timewindow": "Timewindow",
|
"timewindow": "Timewindow",
|
||||||
"use-dashboard-timewindow": "Use dashboard timewindow",
|
"use-dashboard-timewindow": "Use dashboard timewindow",
|
||||||
"use-widget-timewindow": "Use widget timewindow",
|
"use-widget-timewindow": "Use widget timewindow",
|
||||||
@ -5235,7 +5251,10 @@
|
|||||||
"layout-simplified": "Simplified",
|
"layout-simplified": "Simplified",
|
||||||
"layout-horizontal": "Horizontal",
|
"layout-horizontal": "Horizontal",
|
||||||
"layout-horizontal-reversed": "Horizontal reversed",
|
"layout-horizontal-reversed": "Horizontal reversed",
|
||||||
"label": "Label"
|
"label": "Label",
|
||||||
|
"icon": "Icon",
|
||||||
|
"value": "Value",
|
||||||
|
"date": "Date"
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"common-table-settings": "Common Table Settings",
|
"common-table-settings": "Common Table Settings",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user