UI: Implement basic config form for Entities table widget.

This commit is contained in:
Igor Kulikov 2023-06-06 17:20:35 +03:00
parent c71d29c507
commit d992355a3c
28 changed files with 461 additions and 235 deletions

View File

@ -104,7 +104,7 @@
"settingsDirective": "tb-simple-card-widget-settings",
"hasBasicMode": true,
"basicModeDirective": "tb-simple-card-basic-config",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\"}"
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true}"
}
},
{
@ -143,7 +143,9 @@
"dataKeySettingsSchema": "",
"settingsDirective": "tb-entities-table-widget-settings",
"dataKeySettingsDirective": "tb-entities-table-key-settings",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"entitiesTitle\":\"\",\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":true,\"entityNameColumnTitle\":\"\",\"displayEntityLabel\":false,\"displayEntityType\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"useRowStyleFunction\":false},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"useCellContentFunction\":false,\"defaultColumnVisibility\":\"visible\",\"columnSelectionToDisplay\":\"enabled\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}]}"
"hasBasicMode": true,
"basicModeDirective": "tb-entities-table-basic-config",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":false,\"displayEntityLabel\":false,\"displayEntityType\":false,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"name\",\"useRowStyleFunction\":false,\"entitiesTitle\":\"Entities\"},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity name\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return 'Simulated';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity type\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.782057645776538,\"funcBody\":\"return 'Device';\",\"decimals\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.904797781901171,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.1961430898042078,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7678057538205878,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"decimals\":2}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"displayTimewindow\":false,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"list\",\"iconColor\":null}"
}
},
{

View File

@ -816,7 +816,8 @@ export class EntityService {
);
}
public getEntityKeysByEntityFilter(filter: EntityFilter, types: DataKeyType[], config?: RequestConfig): Observable<Array<DataKey>> {
public getEntityKeysByEntityFilter(filter: EntityFilter, types: DataKeyType[],
entityTypes?: EntityType[], config?: RequestConfig): Observable<Array<DataKey>> {
if (!types.length) {
return of([]);
}
@ -832,7 +833,7 @@ export class EntityService {
entitiesKeysByQuery$ = of({
attribute: [],
timeseries: [],
entityTypes: [],
entityTypes: entityTypes || [],
});
}
return entitiesKeysByQuery$.pipe(

View File

@ -366,25 +366,18 @@ export class DashboardUtilsService {
}
const dataKeys = newDatasource.dataKeys;
newDatasource.dataKeys = [];
dataKeys.forEach(dataKey => {
newDatasource.dataKeys.push(this.convertDataKeyFromWidgetType(widgetTypeDescriptor, config, dataKey));
});
if (widgetTypeDescriptor.type === widgetType.alarm) {
dataKeys.forEach(dataKey => {
const newDataKey = deepClone(dataKey);
newDataKey.funcBody = null;
newDataKey.type = DataKeyType.alarm;
newDatasource.dataKeys.push(newDataKey);
});
}
}
return newDatasource;
}
private convertDataKeyFromWidgetType(widgetTypeDescriptor: WidgetTypeDescriptor, config: WidgetConfig, dataKey: DataKey): DataKey {
const newDataKey = deepClone(dataKey);
newDataKey.funcBody = null;
if (widgetTypeDescriptor.type === widgetType.alarm) {
newDataKey.type = DataKeyType.alarm;
} else {
newDataKey.type = DataKeyType.timeseries;
newDataKey.name = newDataKey.label;
}
return newDataKey;
}
private validateAndUpdateState(state: DashboardState) {
if (!state.layouts) {
state.layouts = this.createDefaultLayouts();

View File

@ -51,6 +51,7 @@
[widget]="widget"
[widgetConfigMode]="widgetConfigMode"
[hideHeader]="widgetConfigMode === widgetConfigModes.basic"
isAdd
formControlName="widgetConfig">
</tb-widget-config>
<tb-widget-preview *ngIf="previewMode" class="tb-absolute-fill"

View File

@ -30,6 +30,7 @@
addKeyTitle="{{ 'widgets.table.add-column' | translate }}"
removeKeyTitle="{{ 'widgets.table.remove-column' | translate }}"
noKeysText="{{ 'widgets.table.no-columns' | translate }}"
hideDataKeyColor
[datasourceType]="datasource?.type"
[deviceId]="datasource?.deviceId"
[entityAliasId]="datasource?.entityAliasId"
@ -37,6 +38,29 @@
</tb-data-keys-panel>
<div class="tb-widget-config-panel">
<div class="tb-widget-config-panel-title" translate>widget-config.appearance</div>
<div class="tb-widget-config-row">
<mat-slide-toggle class="mat-slide" formControlName="showTitle">
{{ 'widget-config.card-title' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
<div class="tb-widget-config-row space-between same-padding">
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
{{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<tb-material-icon-select asBoxInput
[color]="entitiesTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="iconColor">
</tb-color-input>
</div>
</div>
<div class="tb-widget-config-row space-between same-padding">
<div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">

View File

@ -26,11 +26,13 @@ import {
datasourcesHasAggregation,
datasourcesHasOnlyComparisonAggregation
} from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
@Component({
selector: 'tb-entities-table-basic-config',
templateUrl: './entities-table-basic-config.component.html',
styleUrls: ['../basic-config.scss', '../../widget-config.scss']
styleUrls: ['../basic-config.scss']
})
export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponent {
@ -56,14 +58,19 @@ export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponen
entitiesTableWidgetConfigForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent,
private fb: UntypedFormBuilder) {
super(store);
super(store, widgetConfigComponent);
}
protected configForm(): UntypedFormGroup {
return this.entitiesTableWidgetConfigForm;
}
protected setupDefaults(configData: WidgetConfigComponentData) {
this.setupDefaultDatasource(configData, [{ name: 'name', type: DataKeyType.entityField }]);
}
protected onConfigSet(configData: WidgetConfigComponentData) {
this.entitiesTableWidgetConfigForm = this.fb.group({
timewindowConfig: [{
@ -73,6 +80,11 @@ export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponen
}, []],
datasources: [configData.config.datasources, []],
columns: [this.getColumns(configData.config.datasources), []],
showTitle: [configData.config.showTitle, []],
title: [configData.config.settings?.entitiesTitle, []],
showTitleIcon: [configData.config.showTitleIcon, []],
titleIcon: [configData.config.titleIcon, []],
iconColor: [configData.config.iconColor, []],
color: [configData.config.color, []],
backgroundColor: [configData.config.backgroundColor, []],
actions: [configData.config.actions || {}, []]
@ -86,11 +98,46 @@ export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponen
this.widgetConfig.config.datasources = config.datasources;
this.setColumns(config.columns, this.widgetConfig.config.datasources);
this.widgetConfig.config.actions = config.actions;
this.widgetConfig.config.showTitle = config.showTitle;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
this.widgetConfig.config.settings.entitiesTitle = config.title;
this.widgetConfig.config.showTitleIcon = config.showTitleIcon;
this.widgetConfig.config.titleIcon = config.titleIcon;
this.widgetConfig.config.iconColor = config.iconColor;
this.widgetConfig.config.color = config.color;
this.widgetConfig.config.backgroundColor = config.backgroundColor;
return this.widgetConfig;
}
protected validatorTriggers(): string[] {
return ['showTitle', 'showTitleIcon'];
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
const showTitle: boolean = this.entitiesTableWidgetConfigForm.get('showTitle').value;
const showTitleIcon: boolean = this.entitiesTableWidgetConfigForm.get('showTitleIcon').value;
if (showTitle) {
this.entitiesTableWidgetConfigForm.get('title').enable();
this.entitiesTableWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false});
if (showTitleIcon) {
this.entitiesTableWidgetConfigForm.get('titleIcon').enable();
this.entitiesTableWidgetConfigForm.get('iconColor').enable();
} else {
this.entitiesTableWidgetConfigForm.get('titleIcon').disable();
this.entitiesTableWidgetConfigForm.get('iconColor').disable();
}
} else {
this.entitiesTableWidgetConfigForm.get('title').disable();
this.entitiesTableWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false});
this.entitiesTableWidgetConfigForm.get('titleIcon').disable();
this.entitiesTableWidgetConfigForm.get('iconColor').disable();
}
this.entitiesTableWidgetConfigForm.get('title').updateValueAndValidity({emitEvent});
this.entitiesTableWidgetConfigForm.get('showTitleIcon').updateValueAndValidity({emitEvent: false});
this.entitiesTableWidgetConfigForm.get('titleIcon').updateValueAndValidity({emitEvent});
this.entitiesTableWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent});
}
private getColumns(datasources?: Datasource[]): DataKey[] {
if (datasources && datasources.length) {
return datasources[0].dataKeys || [];

View File

@ -23,13 +23,15 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models'
import {
Datasource,
datasourcesHasAggregation,
datasourcesHasOnlyComparisonAggregation
datasourcesHasOnlyComparisonAggregation,
} from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
@Component({
selector: 'tb-simple-card-basic-config',
templateUrl: './simple-card-basic-config.component.html',
styleUrls: ['../basic-config.scss', '../../widget-config.scss']
styleUrls: ['../basic-config.scss']
})
export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
@ -46,14 +48,19 @@ export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
simpleCardWidgetConfigForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent,
private fb: UntypedFormBuilder) {
super(store);
super(store, widgetConfigComponent);
}
protected configForm(): UntypedFormGroup {
return this.simpleCardWidgetConfigForm;
}
protected setupDefaults(configData: WidgetConfigComponentData) {
this.setupDefaultDatasource(configData, [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries }]);
}
protected onConfigSet(configData: WidgetConfigComponentData) {
this.simpleCardWidgetConfigForm = this.fb.group({
timewindowConfig: [{

View File

@ -136,6 +136,21 @@
<mat-form-field fxFlex class="tb-inline-field" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="label" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<div *ngIf="!hideDataKeyColor" class="tb-color-field">
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
<div class="tb-units-field">
<tb-widget-units *ngIf="displayUnitsOrDigits"
formControlName="units">
</tb-widget-units>
</div>
<div class="tb-decimals-field">
<mat-form-field *ngIf="displayUnitsOrDigits" appearance="outline" class="tb-inline-field center number" subscriptSizing="dynamic">
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
<ng-template #keyName>
<ng-container *ngIf="dataKeyHasPostprocessing(); else keyName">

View File

@ -42,4 +42,12 @@
}
}
}
.tb-color-field, .tb-units-field, .tb-decimals-field {
width: 60px;
display: flex;
flex-direction: row;
place-content: center;
align-items: center;
}
}

View File

@ -37,7 +37,7 @@ import {
} from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKey, DatasourceType, JsonSettingsSchema, widgetType } from '@shared/models/widget.models';
import { DataKey, DatasourceType, JsonSettingsSchema, Widget, widgetType } from '@shared/models/widget.models';
import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { AggregationType } from '@shared/models/time/time.models';
@ -49,6 +49,13 @@ import { Observable, of } from 'rxjs';
import { filter, map, mergeMap, publishReplay, refCount, share, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import {
DataKeyConfigDialogComponent,
DataKeyConfigDialogData
} from '@home/components/widget/config/data-key-config-dialog.component';
import { deepClone } from '@core/utils';
import { Dashboard } from '@shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models';
export const dataKeyRowValidator = (control: AbstractControl): ValidationErrors | null => {
const dataKey: DataKey = control.value;
@ -63,7 +70,7 @@ export const dataKeyRowValidator = (control: AbstractControl): ValidationErrors
@Component({
selector: 'tb-data-key-row',
templateUrl: './data-key-row.component.html',
styleUrls: ['./data-key-row.component.scss', '../../data-keys.component.scss', '../../widget-config.scss'],
styleUrls: ['./data-key-row.component.scss', '../../data-keys.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -122,6 +129,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
return this.dataKeysPanelComponent.functionTypeKeys;
}
get hideDataKeyColor(): boolean {
return this.dataKeysPanelComponent.hideDataKeyColor;
}
get widgetType(): widgetType {
return this.widgetConfigComponent.widgetType;
}
@ -130,14 +141,34 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
return this.widgetConfigComponent.widgetConfigCallbacks;
}
get widget(): Widget {
return this.widgetConfigComponent.widget;
}
get dashboard(): Dashboard {
return this.widgetConfigComponent.dashboard;
}
get aliasController(): IAliasController {
return this.widgetConfigComponent.aliasController;
}
get datakeySettingsSchema(): JsonSettingsSchema {
return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema;
}
get dataKeySettingsDirective(): string {
return this.widgetConfigComponent.modelValue?.dataKeySettingsDirective;
}
get isEntityDatasource(): boolean {
return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType);
}
get displayUnitsOrDigits() {
return this.modelValue.type && ![ DataKeyType.alarm, DataKeyType.entityField, DataKeyType.count ].includes(this.modelValue.type);
}
private propagateChange = (_val: any) => {};
constructor(private fb: UntypedFormBuilder,
@ -261,7 +292,37 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
}
editKey() {
this.dialog.open<DataKeyConfigDialogComponent, DataKeyConfigDialogData, DataKey>(DataKeyConfigDialogComponent,
{
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
dataKey: deepClone(this.modelValue),
dataKeySettingsSchema: this.datakeySettingsSchema,
dataKeySettingsDirective: this.dataKeySettingsDirective,
dashboard: this.dashboard,
aliasController: this.aliasController,
widget: this.widget,
widgetType: this.widgetType,
deviceId: this.deviceId,
entityAliasId: this.entityAliasId,
showPostProcessing: this.widgetType !== widgetType.alarm,
callbacks: this.callbacks,
hideDataKeyLabel: false,
hideDataKeyColor: this.hideDataKeyColor,
hideDataKeyUnits: !this.displayUnitsOrDigits,
hideDataKeyDecimals: !this.displayUnitsOrDigits
}
}).afterClosed().subscribe((updatedDataKey) => {
if (updatedDataKey) {
this.modelValue = updatedDataKey;
this.keyRowFormGroup.get('label').patchValue(this.modelValue.label, {emitEvent: false});
this.keyRowFormGroup.get('color').patchValue(this.modelValue.color, {emitEvent: false});
this.keyRowFormGroup.get('units').patchValue(this.modelValue.units, {emitEvent: false});
this.keyRowFormGroup.get('decimals').patchValue(this.modelValue.decimals, {emitEvent: false});
this.updateModel();
}
});
}
removeKey() {

View File

@ -21,9 +21,10 @@
<div class="tb-data-keys-header">
<div class="tb-data-keys-header-cell" fxFlex translate>datakey.key</div>
<div class="tb-data-keys-header-cell" fxFlex translate>datakey.label</div>
<div class="tb-data-keys-header-cell" translate>datakey.color</div>
<div class="tb-data-keys-header-cell" translate>widget-config.units-short</div>
<div class="tb-data-keys-header-cell" translate>widget-config.decimals-short</div>
<div *ngIf="!hideDataKeyColor" class="tb-data-keys-header-cell tb-color-header" translate>datakey.color</div>
<div class="tb-data-keys-header-cell tb-units-header" translate>widget-config.units-short</div>
<div class="tb-data-keys-header-cell tb-decimals-header" translate>widget-config.decimals-short</div>
<div class="tb-data-keys-header-cell tb-actions-header"></div>
</div>
<div *ngIf="keysFormArray().controls.length; else noKeys" class="tb-data-keys-body tb-drop-list" cdkDropList cdkDropListOrientation="vertical"
(cdkDropListDropped)="keyDrop($event)">

View File

@ -29,13 +29,18 @@
align-items: center;
gap: 12px;
padding-left: 12px;
padding-right: 12px;
.tb-data-keys-header-cell {
font-weight: 400;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.54);
&.tb-color-header, &.tb-units-header, &.tb-decimals-header {
width: 60px;
}
&.tb-actions-header {
width: 76px;
}
}
}
.tb-data-keys-body {

View File

@ -44,11 +44,12 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { alarmFields } from '@shared/models/alarm.models';
import { UtilsService } from '@core/services/utils.service';
import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
selector: 'tb-data-keys-panel',
templateUrl: './data-keys-panel.component.html',
styleUrls: ['./data-keys-panel.component.scss', '../../widget-config.scss'],
styleUrls: ['./data-keys-panel.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -89,6 +90,10 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
@Input()
deviceId: string;
@Input()
@coerceBoolean()
hideDataKeyColor = false;
dataKeyType: DataKeyType;
alarmKeys: Array<DataKey>;
functionTypeKeys: Array<DataKey>;
@ -212,13 +217,11 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
addKey() {
const dataKey = this.callbacks.generateDataKey('', null, this.datakeySettingsSchema);
dataKey.label = '';
dataKey.decimals = 0;
const keysArray = this.keysListFormGroup.get('keys') as UntypedFormArray;
const keyControl = this.fb.control(dataKey, [dataKeyRowValidator]);
keysArray.push(keyControl);
this.keysListFormGroup.updateValueAndValidity();
if (!this.keysListFormGroup.valid) {
this.propagateChange(this.keysListFormGroup.get('keys').value);
}
}
private prepareKeysFormArray(keys: DataKey[] | undefined): UntypedFormArray {

View File

@ -29,7 +29,7 @@ import { MatDialog } from '@angular/material/dialog';
@Component({
selector: 'tb-widget-actions-panel',
templateUrl: './widget-actions-panel.component.html',
styleUrls: ['../../widget-config.scss'],
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,

View File

@ -155,6 +155,7 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
private dataKeySettingsData: JsonFormComponentData;
private alarmKeys: Array<DataKey>;
private functionTypeKeys: Array<DataKey>;
filteredKeys: Observable<Array<string>>;
private latestKeySearchResult: Array<string> = null;
@ -183,6 +184,13 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
type: DataKeyType.alarm
});
}
this.functionTypeKeys = [];
for (const type of this.utils.getPredefinedFunctionsList()) {
this.functionTypeKeys.push({
name: type,
type: DataKeyType.function
});
}
if (this.dataKeySettingsSchema && this.dataKeySettingsSchema.schema ||
this.dataKeySettingsDirective && this.dataKeySettingsDirective.length) {
this.displayAdvanced = true;
@ -396,6 +404,8 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
let fetchObservable: Observable<Array<DataKey>>;
if (this.modelValue.type === DataKeyType.alarm) {
fetchObservable = of(this.alarmKeys);
} else if (this.modelValue.type === DataKeyType.function) {
fetchObservable = of(this.functionTypeKeys);
} else {
if (this.deviceId || this.entityAliasId) {
const dataKeyTypes = [this.modelValue.type];

View File

@ -46,7 +46,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
selector: 'tb-datasources',
templateUrl: './datasources.component.html',
styleUrls: ['./datasources.component.scss', './widget-config.scss'],
styleUrls: ['./datasources.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,

View File

@ -31,7 +31,7 @@ export interface TimewindowConfigData {
@Component({
selector: 'tb-timewindow-config-panel',
templateUrl: './timewindow-config-panel.component.html',
styleUrls: ['./widget-config.scss'],
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,

View File

@ -23,12 +23,15 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
import { WidgetConfigMode } from '@shared/models/widget.models';
import { DataKey, DatasourceType, KeyInfo, WidgetConfigMode } from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { isDefinedAndNotNull } from '@core/utils';
export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks;
export interface IBasicWidgetConfigComponent {
isAdd: boolean;
widgetConfig: WidgetConfigComponentData;
widgetConfigChanged: Observable<WidgetConfigComponentData>;
validateConfig(): boolean;
@ -40,6 +43,8 @@ export interface IBasicWidgetConfigComponent {
export abstract class BasicWidgetConfigComponent extends PageComponent implements
IBasicWidgetConfigComponent, OnInit, AfterViewInit {
isAdd = false;
basicMode = WidgetConfigMode.basic;
widgetConfigValue: WidgetConfigComponentData;
@ -56,7 +61,8 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
widgetConfigChangedEmitter = new EventEmitter<WidgetConfigComponentData>();
widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable();
protected constructor(@Inject(Store) protected store: Store<AppState>) {
protected constructor(@Inject(Store) protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent) {
super(store);
}
@ -65,12 +71,15 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
ngAfterViewInit(): void {
setTimeout(() => {
if (!this.validateConfig()) {
this.onConfigChanged(this.prepareOutputConfig(this.configForm().value));
this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue()));
}
}, 0);
}
protected setupConfig(widgetConfig: WidgetConfigComponentData) {
if (this.isAdd) {
this.setupDefaults(widgetConfig);
}
this.onConfigSet(widgetConfig);
this.updateValidators(false);
for (const trigger of this.validatorTriggers()) {
@ -83,11 +92,13 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
this.updateValidators(true, trigger);
});
}
this.configForm().valueChanges.subscribe((updated: any) => {
this.onConfigChanged(this.prepareOutputConfig(updated));
this.configForm().valueChanges.subscribe(() => {
this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue()));
});
}
protected setupDefaults(configData: WidgetConfigComponentData) {}
protected updateValidators(emitEvent: boolean, trigger?: string) {
}
@ -108,6 +119,41 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
return this.configForm().valid;
}
protected setupDefaultDatasource(configData: WidgetConfigComponentData, keys?: DataKey[]) {
let datasources = configData.config.datasources;
if (!datasources || !datasources.length) {
datasources = [
{
type: DatasourceType.device,
dataKeys: []
}
];
configData.config.datasources = datasources;
}
let dataKeys = datasources[0].dataKeys;
if (!dataKeys) {
dataKeys = [];
datasources[0].dataKeys = dataKeys;
}
if (keys && keys.length) {
dataKeys.length = 0;
keys.forEach(key => {
const dataKey =
this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, configData.dataKeySettingsSchema);
if (key.label) {
dataKey.label = key.label;
}
if (key.units) {
dataKey.units = key.units;
}
if (isDefinedAndNotNull(key.decimals)) {
dataKey.decimals = key.decimals;
}
dataKeys.push(dataKey);
});
}
}
protected abstract configForm(): UntypedFormGroup;
protected abstract onConfigSet(configData: WidgetConfigComponentData);

View File

@ -1,175 +0,0 @@
/**
* 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.
*/
.tb-widget-config-panel {
box-shadow: 0 0 10px 6px rgba(11, 17, 51, 0.04);
border-radius: 4px;
padding: 16px;
gap: 16px;
display: flex;
flex-direction: column;
color: rgba(0, 0, 0, 0.87);
letter-spacing: 0.15px;
position: relative;
&.no-padding-bottom {
padding-bottom: 0;
}
&.stroked {
box-shadow: none;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px;
}
}
.tb-widget-config-panel-title {
font-weight: 500;
font-size: 16px;
}
.tb-widget-config-panel-hint {
font-size: 12px;
color: #808080;
}
.tb-widget-config-row {
height: 56px;
display: flex;
flex-direction: row;
align-items: center;
gap: 16px;
padding-left: 16px;
padding-right: 12px;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px;
&.same-padding {
padding-right: 16px;
}
&.space-between {
justify-content: space-between;
}
.mat-divider-vertical {
height: 56px;
}
}
.tb-widget-config-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field {
&.mat-form-field-appearance-fill {
.mdc-text-field--filled:not(.mdc-text-field--disabled):before {
opacity: 0;
}
.mat-mdc-form-field-focus-overlay {
opacity: 0;
}
}
.mat-mdc-text-field-wrapper {
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
padding-right: 12px;
padding-left: 12px;
&:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) {
.mdc-notched-outline__leading, .mdc-notched-outline__trailing {
border-color: rgba(0, 0, 0, 0.12);
}
}
.mat-mdc-form-field-infix {
padding-top: 7px;
padding-bottom: 7px;
min-height: 38px;
width: 72px;
}
}
}
&.center {
.mat-mdc-text-field-wrapper {
.mat-mdc-form-field-infix {
.mdc-text-field__input {
text-align: center;
}
}
}
}
&.number {
.mat-mdc-text-field-wrapper {
padding-right: 4px;
.mat-mdc-form-field-infix {
width: 80px;
input.mdc-text-field__input[type=number]::-webkit-inner-spin-button,
input.mdc-text-field__input[type=number]::-webkit-outer-spin-button {
opacity: 1;
}
}
}
}
}
:host ::ng-deep {
.mat-slide {
margin: 8px 0;
.mdc-form-field>label {
font-weight: 400;
font-size: 16px;
line-height: 24px;
margin-left: 12px;
}
}
.slide-block {
display: block;
&:not(:last-child) {
margin-bottom: 8px;
}
}
.tb-widget-config-panel {
.mat-expansion-panel {
&.tb-settings {
box-shadow: none;
.mat-content {
overflow: visible;
}
.mat-expansion-panel-header {
font-weight: 500;
font-size: 16px;
line-height: 24px;
letter-spacing: 0.25px;
padding: 0;
.mat-content {
flex: 0;
white-space: nowrap;
}
&:hover {
background: none;
}
.mat-expansion-indicator {
height: 32px;
padding: 2px;
}
}
.mat-expansion-panel-header-description {
align-items: center;
}
> .mat-expansion-panel-content {
> .mat-expansion-panel-body {
padding: 0;
}
}
.tb-json-object-panel, .tb-css-content-panel {
margin: 0 0 8px;
}
}
.mat-expansion-panel-content {
font: inherit;
}
}
}
}

View File

@ -15,6 +15,6 @@
limitations under the License.
-->
<mat-form-field appearance="outline" class="center" subscriptSizing="dynamic">
<mat-form-field appearance="outline" class="tb-inline-field center" subscriptSizing="dynamic">
<input matInput [formControl]="unitsFormControl" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>

View File

@ -20,7 +20,7 @@ import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, UntypedFormBuilde
@Component({
selector: 'tb-widget-units',
templateUrl: './widget-units.component.html',
styleUrls: ['./widget-config.scss'],
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -43,6 +43,7 @@ export class WidgetUnitsComponent implements ControlValueAccessor, OnInit {
ngOnInit() {
this.unitsFormControl = this.fb.control('', []);
this.unitsFormControl.valueChanges.subscribe(val => this.propagateChange(val));
}
writeValue(units?: string): void {

View File

@ -23,7 +23,7 @@ import { AppState } from '@core/core.state';
@Component({
selector: 'tb-simple-card-widget-settings',
templateUrl: './simple-card-widget-settings.component.html',
styleUrls: ['../../../config/widget-config.scss']
styleUrls: []
})
export class SimpleCardWidgetSettingsComponent extends WidgetSettingsComponent {

View File

@ -215,7 +215,7 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr
mergeMap((aliasInfo) => {
return this.entityService.getEntityKeysByEntityFilter(
aliasInfo.entityFilter,
dataKeyTypes,
dataKeyTypes, [],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))

View File

@ -15,7 +15,13 @@
///
import { Component, ElementRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
UntypedFormBuilder,
UntypedFormGroup,
Validators
} from '@angular/forms';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@ -27,6 +33,7 @@ import { catchError, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/op
import { DataKey } from '@shared/models/widget.models';
import { EntityService } from '@core/http/entity.service';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { EntityType } from '@shared/models/entity-type.models';
@Component({
selector: 'tb-device-key-autocomplete',
@ -199,7 +206,7 @@ export class DeviceKeyAutocompleteComponent extends PageComponent implements OnI
mergeMap((aliasInfo) => {
return this.entityService.getEntityKeysByEntityFilter(
aliasInfo.entityFilter,
dataKeyTypes,
dataKeyTypes, [EntityType.DEVICE],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))

View File

@ -236,7 +236,7 @@ export class ImageMapProviderSettingsComponent extends PageComponent implements
mergeMap((aliasInfo) => {
return this.entityService.getEntityKeysByEntityFilter(
aliasInfo.entityFilter,
dataKeyTypes,
dataKeyTypes, [],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))

View File

@ -81,8 +81,8 @@ import { FilterDialogComponent, FilterDialogData } from '@home/components/filter
import { ToggleHeaderOption } from '@shared/components/toggle-header.component';
import { coerceBoolean } from '@shared/decorators/coercion';
import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module';
import Timeout = NodeJS.Timeout;
import { TimewindowConfigData } from '@home/components/widget/config/timewindow-config-panel.component';
import Timeout = NodeJS.Timeout;
const emptySettingsSchema: JsonSchema = {
type: 'object',
@ -96,7 +96,7 @@ const defaultSettingsForm = [
@Component({
selector: 'tb-widget-config',
templateUrl: './widget-config.component.html',
styleUrls: ['./widget-config.component.scss', './config/widget-config.scss'],
styleUrls: ['./widget-config.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -143,6 +143,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
@coerceBoolean()
hideToggleHeader = false;
@Input()
@coerceBoolean()
isAdd = false;
@Input() disabled: boolean;
@Input()
@ -399,22 +403,22 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
writeValue(value: WidgetConfigComponentData): void {
this.modelValue = value;
this.widgetType = this.modelValue?.widgetType;
this.setupConfig();
this.setupConfig(this.isAdd);
}
private setupConfig() {
private setupConfig(isAdd = false) {
if (this.modelValue) {
this.destroyBasicModeComponent();
this.removeChangeSubscriptions();
if (this.hasBasicModeDirective && this.widgetConfigMode === WidgetConfigMode.basic) {
this.setupBasicModeConfig();
this.setupBasicModeConfig(isAdd);
} else {
this.setupDefaultConfig();
}
}
}
private setupBasicModeConfig() {
private setupBasicModeConfig(isAdd = false) {
const componentType = basicWidgetConfigComponentsMap[this.modelValue.basicModeDirective];
if (!componentType) {
this.basicModeDirectiveError = this.translate.instant('widget-config.settings-component-not-found',
@ -425,6 +429,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
this.createBasicModeComponentTimeout = null;
this.basicModeComponentRef = this.basicModeContainer.createComponent(factory);
this.basicModeComponent = this.basicModeComponentRef.instance;
this.basicModeComponent.isAdd = isAdd;
this.basicModeComponent.widgetConfig = this.modelValue;
this.basicModeComponentChangeSubscription = this.basicModeComponent.widgetConfigChanged.subscribe((data) => {
this.modelValue = data;
@ -826,7 +831,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
const entityFilter = singleEntityFilterFromDeviceId(deviceId);
return this.entityService.getEntityKeysByEntityFilter(
entityFilter,
dataKeyTypes,
dataKeyTypes, [EntityType.DEVICE],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))
@ -837,7 +842,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
return this.aliasController.getAliasInfo(entityAliasId).pipe(
mergeMap((aliasInfo) => this.entityService.getEntityKeysByEntityFilter(
aliasInfo.entityFilter,
dataKeyTypes,
dataKeyTypes, [],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))

View File

@ -4125,6 +4125,7 @@
"title-tooltip": "Title Tooltip",
"general-settings": "General settings",
"display-title": "Display widget title",
"card-title": "Card title",
"drop-shadow": "Drop shadow",
"enable-fullscreen": "Enable fullscreen",
"background-color": "Background color",
@ -4179,6 +4180,7 @@
"delete-action-text": "Are you sure you want delete widget action with name '{{actionName}}'?",
"title-icon": "Title icon",
"display-icon": "Display title icon",
"card-icon": "Card icon",
"icon-color": "Icon color",
"icon-size": "Icon size",
"advanced-settings": "Advanced settings",

View File

@ -1183,4 +1183,166 @@ mat-label {
.mat-expansion-panel {
color: inherit;
}
// Widget config
.tb-widget-config-panel {
box-shadow: 0 0 10px 6px rgba(11, 17, 51, 0.04);
border-radius: 4px;
padding: 16px;
gap: 16px;
display: flex;
flex-direction: column;
color: rgba(0, 0, 0, 0.87);
letter-spacing: 0.15px;
position: relative;
&.no-padding-bottom {
padding-bottom: 0;
}
&.stroked {
box-shadow: none;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px;
}
.mat-expansion-panel {
&.tb-settings {
box-shadow: none;
.mat-content {
overflow: visible;
}
.mat-expansion-panel-header {
font-weight: 500;
font-size: 16px;
line-height: 24px;
letter-spacing: 0.25px;
padding: 0;
.mat-content {
flex: 0;
white-space: nowrap;
}
&:hover {
background: none;
}
.mat-expansion-indicator {
height: 32px;
padding: 2px;
}
}
.mat-expansion-panel-header-description {
align-items: center;
}
> .mat-expansion-panel-content {
> .mat-expansion-panel-body {
padding: 0;
}
}
.tb-json-object-panel, .tb-css-content-panel {
margin: 0 0 8px;
}
}
.mat-expansion-panel-content {
font: inherit;
}
}
.mat-slide {
margin: 8px 0;
.mdc-form-field>label {
font-weight: 400;
font-size: 16px;
line-height: 24px;
margin-left: 12px;
}
}
}
.tb-widget-config-panel-title {
font-weight: 500;
font-size: 16px;
}
.tb-widget-config-panel-hint {
font-size: 12px;
color: #808080;
}
.tb-widget-config-row {
height: 56px;
display: flex;
flex-direction: row;
align-items: center;
gap: 16px;
padding-left: 16px;
padding-right: 12px;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px;
&.same-padding {
padding-right: 16px;
}
&.space-between {
justify-content: space-between;
}
.mat-divider-vertical {
height: 56px;
}
.mat-mdc-form-field {
width: 80px;
}
}
.tb-widget-config-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field {
&.mat-form-field-appearance-fill {
.mdc-text-field--filled:not(.mdc-text-field--disabled) {
&:before {
opacity: 0;
}
.mdc-line-ripple::before {
border-bottom-color: rgba(0, 0, 0, 0.12);
}
}
.mat-mdc-form-field-focus-overlay {
opacity: 0;
}
}
.mat-mdc-text-field-wrapper {
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
padding-right: 12px;
padding-left: 12px;
&:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) {
.mdc-notched-outline__leading, .mdc-notched-outline__trailing {
border-color: rgba(0, 0, 0, 0.12);
}
}
.mat-mdc-form-field-infix {
padding-top: 7px;
padding-bottom: 7px;
min-height: 38px;
width: auto;
.mdc-text-field__input, .mat-mdc-select {
font-weight: 400;
font-size: 14px;
line-height: 20px;
}
}
}
}
&.center {
.mat-mdc-text-field-wrapper {
.mat-mdc-form-field-infix {
.mdc-text-field__input {
text-align: center;
}
}
}
}
&.number {
.mat-mdc-text-field-wrapper {
padding-right: 4px;
.mat-mdc-form-field-infix {
input.mdc-text-field__input[type=number]::-webkit-inner-spin-button,
input.mdc-text-field__input[type=number]::-webkit-outer-spin-button {
opacity: 1;
}
}
}
}
}
}