Browse Source

UI: Implement SCADA layout.

pull/11391/head
Igor Kulikov 2 years ago
parent
commit
e84447d28e
  1. 4
      application/src/main/data/json/system/widget_types/power_button.json
  2. 92
      ui-ngx/src/app/core/services/dashboard-utils.service.ts
  3. 4
      ui-ngx/src/app/core/services/menu.service.ts
  4. 14
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  5. 283
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html
  6. 39
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts
  7. 10
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html
  8. 20
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.ts
  9. 1
      ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.scss
  10. 8
      ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts
  11. 4
      ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts
  12. 1
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.scss
  13. 8
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts
  14. 12
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.ts
  15. 1
      ui-ngx/src/app/shared/models/dashboard.models.ts
  16. 18
      ui-ngx/src/app/shared/models/widget-settings.models.ts
  17. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
application/src/main/data/json/system/widget_types/power_button.json

File diff suppressed because one or more lines are too long

92
ui-ngx/src/app/core/services/dashboard-utils.service.ts

@ -50,6 +50,7 @@ import { EntityId } from '@app/shared/models/id/entity-id';
import { initModelFromDefaultTimewindow } from '@shared/models/time/time.models'; import { initModelFromDefaultTimewindow } from '@shared/models/time/time.models';
import { AlarmSearchStatus } from '@shared/models/alarm.models'; import { AlarmSearchStatus } from '@shared/models/alarm.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { BackgroundType, colorBackground, isBackgroundSettings } from '@shared/models/widget-settings.models';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -332,6 +333,27 @@ export class DashboardUtilsService {
return widgetConfig; return widgetConfig;
} }
public prepareWidgetForScadaLayout(widget: Widget): Widget {
const config = widget.config;
config.showTitle = false;
config.dropShadow = false;
config.padding = '0';
config.margin = '0';
config.backgroundColor = 'rgba(0,0,0,0)';
const settings = config.settings || {};
settings.padding = '0';
const background = settings.background;
if (isBackgroundSettings(background)) {
background.type = BackgroundType.color;
background.color = 'rgba(0,0,0,0)';
background.overlay.enabled = false;
} else {
settings.background = colorBackground('rgba(0,0,0,0)');
}
config.settings = settings;
return widget;
}
public validateAndUpdateDatasources(datasources?: Datasource[]): Datasource[] { public validateAndUpdateDatasources(datasources?: Datasource[]): Datasource[] {
if (!datasources) { if (!datasources) {
datasources = []; datasources = [];
@ -588,15 +610,17 @@ export class DashboardUtilsService {
originalColumns = 24; originalColumns = 24;
} }
const gridSettings = layout.gridSettings; const gridSettings = layout.gridSettings;
let columns = 24; if (!gridSettings.isScada) {
if (gridSettings && gridSettings.columns) { let columns = 24;
columns = gridSettings.columns; if (gridSettings && gridSettings.columns) {
} columns = gridSettings.columns;
columns = columns * layoutCount; }
if (columns !== originalColumns) { columns = columns * layoutCount;
const ratio = columns / originalColumns; if (columns !== originalColumns) {
widgetLayout.sizeX *= ratio; const ratio = columns / originalColumns;
widgetLayout.sizeY *= ratio; widgetLayout.sizeX *= ratio;
widgetLayout.sizeY *= ratio;
}
} }
if (row > -1 && column > - 1) { if (row > -1 && column > - 1) {
@ -662,31 +686,33 @@ export class DashboardUtilsService {
const columns = gridSettings.columns || 24; const columns = gridSettings.columns || 24;
const ratio = columns / prevColumns; const ratio = columns / prevColumns;
layout.gridSettings = gridSettings; layout.gridSettings = gridSettings;
let maxRow = 0; if (!gridSettings.isScada) {
for (const w of Object.keys(layout.widgets)) { let maxRow = 0;
const widget = layout.widgets[w]; for (const w of Object.keys(layout.widgets)) {
if (!widget.sizeX) { const widget = layout.widgets[w];
widget.sizeX = 1; if (!widget.sizeX) {
} widget.sizeX = 1;
if (!widget.sizeY) { }
widget.sizeY = 1; if (!widget.sizeY) {
} widget.sizeY = 1;
maxRow = Math.max(maxRow, widget.row + widget.sizeY); }
} maxRow = Math.max(maxRow, widget.row + widget.sizeY);
const newMaxRow = Math.round(maxRow * ratio);
for (const w of Object.keys(layout.widgets)) {
const widget = layout.widgets[w];
if (widget.row + widget.sizeY === maxRow) {
widget.row = Math.round(widget.row * ratio);
widget.sizeY = newMaxRow - widget.row;
} else {
widget.row = Math.round(widget.row * ratio);
widget.sizeY = Math.round(widget.sizeY * ratio);
} }
widget.sizeX = Math.round(widget.sizeX * ratio); const newMaxRow = Math.round(maxRow * ratio);
widget.col = Math.round(widget.col * ratio); for (const w of Object.keys(layout.widgets)) {
if (widget.col + widget.sizeX > columns) { const widget = layout.widgets[w];
widget.sizeX = columns - widget.col; if (widget.row + widget.sizeY === maxRow) {
widget.row = Math.round(widget.row * ratio);
widget.sizeY = newMaxRow - widget.row;
} else {
widget.row = Math.round(widget.row * ratio);
widget.sizeY = Math.round(widget.sizeY * ratio);
}
widget.sizeX = Math.round(widget.sizeX * ratio);
widget.col = Math.round(widget.col * ratio);
if (widget.col + widget.sizeX > columns) {
widget.sizeX = columns - widget.col;
}
} }
} }
} }

4
ui-ngx/src/app/core/services/menu.service.ts

@ -155,7 +155,7 @@ export class MenuService {
name: 'scada.symbols', name: 'scada.symbols',
type: 'link', type: 'link',
path: '/resources/scada-symbols', path: '/resources/scada-symbols',
icon: 'precision_manufacturing' icon: 'view_in_ar'
}, },
{ {
id: 'resources_library', id: 'resources_library',
@ -561,7 +561,7 @@ export class MenuService {
name: 'scada.symbols', name: 'scada.symbols',
type: 'link', type: 'link',
path: '/resources/scada-symbols', path: '/resources/scada-symbols',
icon: 'precision_manufacturing' icon: 'view_in_ar'
}, },
{ {
id: 'resources_library', id: 'resources_library',

14
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -1163,6 +1163,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.runChangeDetection(); this.runChangeDetection();
} }
private isAddingToScadaLayout(): boolean {
const layouts = this.dashboardConfiguration.states[this.dashboardCtx.state].layouts;
let layoutIds: DashboardLayoutId[];
if (this.addingLayoutCtx?.id) {
layoutIds = [this.addingLayoutCtx?.id];
} else {
layoutIds = Object.keys(layouts) as DashboardLayoutId[];
}
return layoutIds.every(id => layouts[id].gridSettings.isScada);
}
private selectTargetLayout(): Observable<DashboardLayoutId> { private selectTargetLayout(): Observable<DashboardLayoutId> {
const layouts = this.dashboardConfiguration.states[this.dashboardCtx.state].layouts; const layouts = this.dashboardConfiguration.states[this.dashboardCtx.state].layouts;
const layoutIds = Object.keys(layouts); const layoutIds = Object.keys(layouts);
@ -1208,6 +1219,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
col: 0 col: 0
}; };
newWidget = this.dashboardUtils.validateAndUpdateWidget(newWidget); newWidget = this.dashboardUtils.validateAndUpdateWidget(newWidget);
if (this.isAddingToScadaLayout()) {
newWidget = this.dashboardUtils.prepareWidgetForScadaLayout(newWidget);
}
if (widgetTypeInfo.typeParameters.useCustomDatasources) { if (widgetTypeInfo.typeParameters.useCustomDatasources) {
this.addWidgetToDashboard(newWidget); this.addWidgetToDashboard(newWidget);
} else { } else {

283
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html

@ -27,129 +27,149 @@
</mat-toolbar> </mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar> </mat-progress-bar>
<div mat-dialog-content> <div mat-dialog-content class="tb-form-panel no-border no-padding">
<fieldset [disabled]="isLoading$ | async"> <div *ngIf="settings" class="tb-form-panel no-border no-padding" [formGroup]="settingsFormGroup">
<div *ngIf="settings" [formGroup]="settingsFormGroup" fxLayout="column"> <div class="tb-form-row space-between">
<mat-form-field class="mat-block"> <div>{{ 'dashboard.state-controller' | translate }}</div>
<mat-label translate>dashboard.state-controller</mat-label> <mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic">
<mat-select required formControlName="stateControllerId"> <mat-select required formControlName="stateControllerId">
<mat-option *ngFor="let stateControllerId of stateControllerIds" [value]="stateControllerId"> <mat-option *ngFor="let stateControllerId of stateControllerIds" [value]="stateControllerId">
{{ getStatesControllerTranslation(stateControllerId) }} {{ getStatesControllerTranslation(stateControllerId) }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<fieldset class="fields-group" fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px"> </div>
<legend class="group-title" translate>dashboard.title-settings</legend> <div class="tb-form-panel stroked">
<mat-slide-toggle fxFlex formControlName="showTitle"> <div class="tb-form-panel-title" translate>dashboard.title-settings</div>
{{ 'dashboard.display-title' | translate }} <div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="showTitle">
{{ 'dashboard.title' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<tb-color-input fxFlex <tb-color-input asBoxInput
label="{{'dashboard.title-color' | translate}}" colorClearButton
icon="format_color_fill"
openOnInput
formControlName="titleColor"> formControlName="titleColor">
</tb-color-input> </tb-color-input>
</fieldset> </div>
<fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px"> </div>
<legend class="group-title" translate>dashboard.dashboard-logo-settings</legend> <div class="tb-form-panel stroked">
<mat-slide-toggle formControlName="showDashboardLogo"> <div class="tb-form-panel-title" translate>dashboard.dashboard-logo-settings</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="showDashboardLogo">
{{ 'dashboard.display-dashboard-logo' | translate }} {{ 'dashboard.display-dashboard-logo' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<tb-gallery-image-input fxFlex </div>
label="{{'dashboard.dashboard-logo-image' | translate}}" <tb-gallery-image-input fxFlex
formControlName="dashboardLogoUrl"> label="{{'dashboard.dashboard-logo-image' | translate}}"
</tb-gallery-image-input> formControlName="dashboardLogoUrl">
</fieldset> </tb-gallery-image-input>
<fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px"> </div>
<legend class="group-title" translate>dashboard.toolbar-settings</legend> <div class="tb-form-panel stroked">
<mat-slide-toggle formControlName="hideToolbar"> <div class="tb-form-panel-title" translate>dashboard.toolbar-settings</div>
{{ 'dashboard.hide-toolbar' | translate }} <mat-slide-toggle class="mat-slide" formControlName="hideToolbar">
</mat-slide-toggle> {{ 'dashboard.hide-toolbar' | translate }}
<mat-slide-toggle formControlName="toolbarAlwaysOpen"> </mat-slide-toggle>
{{ 'dashboard.toolbar-always-open' | translate }} <mat-slide-toggle class="mat-slide" formControlName="toolbarAlwaysOpen">
</mat-slide-toggle> {{ 'dashboard.toolbar-always-open' | translate }}
<mat-slide-toggle formControlName="showDashboardsSelect"> </mat-slide-toggle>
{{ 'dashboard.display-dashboards-selection' | translate }} <mat-slide-toggle class="mat-slide" formControlName="showDashboardsSelect">
</mat-slide-toggle> {{ 'dashboard.display-dashboards-selection' | translate }}
<mat-slide-toggle formControlName="showEntitiesSelect"> </mat-slide-toggle>
{{ 'dashboard.display-entities-selection' | translate }} <mat-slide-toggle class="mat-slide" formControlName="showEntitiesSelect">
</mat-slide-toggle> {{ 'dashboard.display-entities-selection' | translate }}
<mat-slide-toggle formControlName="showFilters"> </mat-slide-toggle>
{{ 'dashboard.display-filters' | translate }} <mat-slide-toggle class="mat-slide" formControlName="showFilters">
</mat-slide-toggle> {{ 'dashboard.display-filters' | translate }}
<mat-slide-toggle formControlName="showDashboardTimewindow"> </mat-slide-toggle>
{{ 'dashboard.display-dashboard-timewindow' | translate }} <mat-slide-toggle class="mat-slide" formControlName="showDashboardTimewindow">
</mat-slide-toggle> {{ 'dashboard.display-dashboard-timewindow' | translate }}
<mat-slide-toggle formControlName="showDashboardExport"> </mat-slide-toggle>
{{ 'dashboard.display-dashboard-export' | translate }} <mat-slide-toggle class="mat-slide" formControlName="showDashboardExport">
</mat-slide-toggle> {{ 'dashboard.display-dashboard-export' | translate }}
<mat-slide-toggle formControlName="showUpdateDashboardImage"> </mat-slide-toggle>
{{ 'dashboard.display-update-dashboard-image' | translate }} <mat-slide-toggle class="mat-slide" formControlName="showUpdateDashboardImage">
</mat-slide-toggle> {{ 'dashboard.display-update-dashboard-image' | translate }}
</fieldset> </mat-slide-toggle>
<mat-expansion-panel class="tb-settings"> </div>
<mat-expansion-panel-header> <mat-expansion-panel class="tb-settings">
<mat-panel-description fxLayoutAlign="end" translate> <mat-expansion-panel-header>
dashboard.advanced-settings <mat-panel-description fxLayoutAlign="end" translate>
</mat-panel-description> dashboard.advanced-settings
</mat-expansion-panel-header> </mat-panel-description>
<ng-template matExpansionPanelContent> </mat-expansion-panel-header>
<tb-css <ng-template matExpansionPanelContent>
label="{{ 'dashboard.dashboard-css' | translate }}" <tb-css
formControlName="dashboardCss" label="{{ 'dashboard.dashboard-css' | translate }}"
></tb-css> formControlName="dashboardCss"
</ng-template> ></tb-css>
</mat-expansion-panel> </ng-template>
</mat-expansion-panel>
</div>
<div *ngIf="gridSettings" class="tb-form-panel no-border no-padding" [formGroup]="gridSettingsFormGroup">
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="isScada">
{{ 'dashboard.scada-layout' | translate }}
</mat-slide-toggle>
</div> </div>
<div *ngIf="gridSettings" [formGroup]="gridSettingsFormGroup" fxLayout="column"> <div class="tb-form-panel stroked">
<fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px"> <div class="tb-form-panel-title" translate>dashboard.layout-settings</div>
<legend class="group-title" translate>dashboard.layout-settings</legend> <div class="tb-form-row space-between">
<mat-form-field class="mat-block"> <div translate>dashboard.columns-count</div>
<mat-label translate>dashboard.columns-count</mat-label> <mat-form-field appearance="outline" class="number medium-width tb-suffix-absolute" subscriptSizing="dynamic">
<input matInput formControlName="columns" type="number" step="any" min="10" <input matInput formControlName="columns" type="number" min="10" max="1000" step="1"
max="1000" required> required
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('required')"> placeholder="{{ 'widget-config.set' | translate }}">
{{ 'dashboard.columns-count-required' | translate }} <mat-icon matSuffix
</mat-error> matTooltipPosition="above"
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('min')"> matTooltipClass="tb-error-tooltip"
{{ 'dashboard.min-columns-count-message' | translate }} [matTooltip]="(gridSettingsFormGroup.get('columns').hasError('required') ? 'dashboard.columns-count-required' :
</mat-error> (gridSettingsFormGroup.get('columns').hasError('min') ? 'dashboard.min-columns-count-message' : 'dashboard.max-columns-count-message')) | translate"
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('max')"> *ngIf="gridSettingsFormGroup.get('columns').invalid && gridSettingsFormGroup.get('columns').touched"
{{ 'dashboard.max-columns-count-message' | translate }} class="tb-error">
</mat-error> warning
</mat-icon>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex class="mat-block"> </div>
<mat-label translate>dashboard.widgets-margins</mat-label> <div *ngIf="!gridSettingsFormGroup.get('isScada').value" class="tb-form-row space-between">
<input matInput formControlName="margin" type="number" step="any" min="0" <div translate>dashboard.widgets-margins</div>
max="50" required> <mat-form-field appearance="outline" class="number medium-width tb-suffix-absolute" subscriptSizing="dynamic">
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('required')"> <input matInput formControlName="margin" type="number" min="0" max="50" step="any"
{{ 'dashboard.margin-required' | translate }} required
</mat-error> placeholder="{{ 'widget-config.set' | translate }}">
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('min')"> <mat-icon matSuffix
{{ 'dashboard.min-margin-message' | translate }} matTooltipPosition="above"
</mat-error> matTooltipClass="tb-error-tooltip"
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('max')"> [matTooltip]="(gridSettingsFormGroup.get('margin').hasError('required') ? 'dashboard.margin-required' :
{{ 'dashboard.max-margin-message' | translate }} (gridSettingsFormGroup.get('margin').hasError('min') ? 'dashboard.min-margin-message' : 'dashboard.max-margin-message')) | translate"
</mat-error> *ngIf="gridSettingsFormGroup.get('margin').invalid && gridSettingsFormGroup.get('margin').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field> </mat-form-field>
<mat-slide-toggle fxFlex formControlName="outerMargin" style="display: block; padding-bottom: 12px;"> </div>
<div *ngIf="!gridSettingsFormGroup.get('isScada').value" class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="outerMargin">
{{ 'dashboard.apply-outer-margin' | translate }} {{ 'dashboard.apply-outer-margin' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<mat-slide-toggle fxFlex formControlName="autoFillHeight" style="display: block; padding-bottom: 12px;"> </div>
<div *ngIf="!gridSettingsFormGroup.get('isScada').value" class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="autoFillHeight">
{{ 'dashboard.autofill-height' | translate }} {{ 'dashboard.autofill-height' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<tb-color-input fxFlex </div>
label="{{'dashboard.background-color' | translate}}" <div class="tb-form-row space-between">
icon="format_color_fill" <div translate>dashboard.background-color</div>
openOnInput <tb-color-input asBoxInput
colorClearButton
formControlName="backgroundColor"> formControlName="backgroundColor">
</tb-color-input> </tb-color-input>
<tb-gallery-image-input fxFlex </div>
label="{{'dashboard.background-image' | translate}}" <tb-gallery-image-input fxFlex
formControlName="backgroundImageUrl"> label="{{'dashboard.background-image' | translate}}"
</tb-gallery-image-input> formControlName="backgroundImageUrl">
<mat-form-field class="mat-block"> </tb-gallery-image-input>
<mat-label translate>dashboard.background-size-mode</mat-label> <div class="tb-form-row space-between">
<div>{{ 'dashboard.background-size-mode' | translate }}</div>
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="backgroundSizeMode"> <mat-select formControlName="backgroundSizeMode">
<mat-option value="100%">Fit width</mat-option> <mat-option value="100%">Fit width</mat-option>
<mat-option value="auto 100%">Fit height</mat-option> <mat-option value="auto 100%">Fit height</mat-option>
@ -158,34 +178,41 @@
<mat-option value="auto">Original size</mat-option> <mat-option value="auto">Original size</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</fieldset> </div>
<fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px"> </div>
<legend class="group-title" translate>dashboard.mobile-layout</legend> <div *ngIf="!gridSettingsFormGroup.get('isScada').value || isRightLayout" class="tb-form-panel stroked">
<mat-slide-toggle *ngIf="isRightLayout" fxFlex formControlName="mobileDisplayLayoutFirst" style="display: block;"> <div class="tb-form-panel-title" translate>dashboard.mobile-layout</div>
<div *ngIf="isRightLayout" class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="mobileDisplayLayoutFirst">
{{ 'dashboard.display-first-in-mobile-view' | translate }} {{ 'dashboard.display-first-in-mobile-view' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px" > </div>
<mat-slide-toggle fxFlex formControlName="mobileAutoFillHeight"> <div *ngIf="!gridSettingsFormGroup.get('isScada').value" class="tb-form-row">
{{ 'dashboard.autofill-height' | translate }} <mat-slide-toggle class="mat-slide" formControlName="mobileAutoFillHeight">
</mat-slide-toggle> {{ 'dashboard.autofill-height' | translate }}
<mat-form-field fxFlex class="mat-block"> </mat-slide-toggle>
<mat-label translate>dashboard.mobile-row-height</mat-label> </div>
<input matInput formControlName="mobileRowHeight" type="number" step="any" min="5" <div *ngIf="!gridSettingsFormGroup.get('isScada').value" class="tb-form-row space-between">
max="200" required> <div translate>dashboard.mobile-row-height</div>
<mat-error *ngIf="gridSettingsFormGroup.get('mobileRowHeight').hasError('required')"> <mat-form-field appearance="outline" class="number medium-width tb-suffix-absolute" subscriptSizing="dynamic">
{{ 'dashboard.mobile-row-height-required' | translate }} <input matInput formControlName="mobileRowHeight" type="number" min="5" max="200" step="any"
</mat-error> required
<mat-error *ngIf="gridSettingsFormGroup.get('mobileRowHeight').hasError('min')"> placeholder="{{ 'widget-config.set' | translate }}">
{{ 'dashboard.min-mobile-row-height-message' | translate }} <mat-icon matSuffix
</mat-error> matTooltipPosition="above"
<mat-error *ngIf="gridSettingsFormGroup.get('mobileRowHeight').hasError('max')"> matTooltipClass="tb-error-tooltip"
{{ 'dashboard.max-mobile-row-height-message' | translate }} [matTooltip]="(gridSettingsFormGroup.get('mobileRowHeight').hasError('required') ? 'dashboard.mobile-row-height-required' :
</mat-error> (gridSettingsFormGroup.get('mobileRowHeight').hasError('min') ?
</mat-form-field> 'dashboard.min-mobile-row-height-message' : 'dashboard.max-mobile-row-height-message')) | translate"
</div> *ngIf="gridSettingsFormGroup.get('mobileRowHeight').invalid && gridSettingsFormGroup.get('mobileRowHeight').touched"
</fieldset> class="tb-error">
warning
</mat-icon>
<span matSuffix>px</span>
</mat-form-field>
</div>
</div> </div>
</fieldset> </div>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-button color="primary" <button mat-button color="primary"

39
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts

@ -26,6 +26,7 @@ import { TranslateService } from '@ngx-translate/core';
import { DashboardSettings, GridSettings, StateControllerId } from '@app/shared/models/dashboard.models'; import { DashboardSettings, GridSettings, StateControllerId } from '@app/shared/models/dashboard.models';
import { isDefined, isUndefined } from '@core/utils'; import { isDefined, isUndefined } from '@core/utils';
import { StatesControllerService } from './states/states-controller.service'; import { StatesControllerService } from './states/states-controller.service';
import { merge } from 'rxjs';
export interface DashboardSettingsDialogData { export interface DashboardSettingsDialogData {
settings?: DashboardSettings; settings?: DashboardSettings;
@ -156,6 +157,7 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
if (this.gridSettings) { if (this.gridSettings) {
const mobileAutoFillHeight = isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight; const mobileAutoFillHeight = isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight;
this.gridSettingsFormGroup = this.fb.group({ this.gridSettingsFormGroup = this.fb.group({
isScada: [this.gridSettings.isScada, []],
columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]], columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]],
margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10, margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10,
[Validators.required, Validators.min(0), Validators.max(50)]], [Validators.required, Validators.min(0), Validators.max(50)]],
@ -169,18 +171,17 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
disabled: mobileAutoFillHeight}, [Validators.required, Validators.min(5), Validators.max(200)]] disabled: mobileAutoFillHeight}, [Validators.required, Validators.min(5), Validators.max(200)]]
}); });
if (this.isRightLayout) { if (this.isRightLayout) {
const mobileDisplayLayoutFirst = isUndefined(this.gridSettings.mobileDisplayLayoutFirst) ? false : this.gridSettings.mobileDisplayLayoutFirst; const mobileDisplayLayoutFirst =
isUndefined(this.gridSettings.mobileDisplayLayoutFirst) ? false : this.gridSettings.mobileDisplayLayoutFirst;
this.gridSettingsFormGroup.addControl('mobileDisplayLayoutFirst', this.fb.control(mobileDisplayLayoutFirst, [])); this.gridSettingsFormGroup.addControl('mobileDisplayLayoutFirst', this.fb.control(mobileDisplayLayoutFirst, []));
} }
this.gridSettingsFormGroup.get('mobileAutoFillHeight').valueChanges.subscribe( merge(this.gridSettingsFormGroup.get('isScada').valueChanges,
(mobileAutoFillHeightValue: boolean) => { this.gridSettingsFormGroup.get('mobileAutoFillHeight').valueChanges).subscribe(
if (mobileAutoFillHeightValue) { () => {
this.gridSettingsFormGroup.get('mobileRowHeight').disable(); this.updateGridSettingsFormState();
} else {
this.gridSettingsFormGroup.get('mobileRowHeight').enable();
}
} }
); );
this.updateGridSettingsFormState();
} else { } else {
this.gridSettingsFormGroup = this.fb.group({}); this.gridSettingsFormGroup = this.fb.group({});
} }
@ -218,4 +219,26 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
} }
return stateControllerId; return stateControllerId;
} }
private updateGridSettingsFormState() {
const isScada: boolean = this.gridSettingsFormGroup.get('isScada').value;
if (isScada) {
this.gridSettingsFormGroup.get('margin').disable();
this.gridSettingsFormGroup.get('outerMargin').disable();
this.gridSettingsFormGroup.get('autoFillHeight').disable();
this.gridSettingsFormGroup.get('mobileAutoFillHeight').disable({emitEvent: false});
this.gridSettingsFormGroup.get('mobileRowHeight').disable();
} else {
this.gridSettingsFormGroup.get('margin').enable();
this.gridSettingsFormGroup.get('outerMargin').enable();
this.gridSettingsFormGroup.get('autoFillHeight').enable();
this.gridSettingsFormGroup.get('mobileAutoFillHeight').enable({emitEvent: false});
const mobileAutoFillHeight: boolean = this.gridSettingsFormGroup.get('mobileAutoFillHeight').value;
if (mobileAutoFillHeight) {
this.gridSettingsFormGroup.get('mobileRowHeight').disable();
} else {
this.gridSettingsFormGroup.get('mobileRowHeight').enable();
}
}
}
} }

10
ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html

@ -42,17 +42,17 @@
[widgets]="layoutCtx.widgets" [widgets]="layoutCtx.widgets"
[widgetLayouts]="layoutCtx.widgetLayouts" [widgetLayouts]="layoutCtx.widgetLayouts"
[columns]="layoutCtx.gridSettings.columns" [columns]="layoutCtx.gridSettings.columns"
[outerMargin]="layoutCtx.gridSettings.outerMargin" [outerMargin]="outerMargin"
[margin]="layoutCtx.gridSettings.margin" [margin]="margin"
[aliasController]="dashboardCtx.aliasController" [aliasController]="dashboardCtx.aliasController"
[stateController]="dashboardCtx.stateController" [stateController]="dashboardCtx.stateController"
[dashboardTimewindow]="dashboardCtx.dashboardTimewindow" [dashboardTimewindow]="dashboardCtx.dashboardTimewindow"
[isEdit]="isEdit" [isEdit]="isEdit"
[autofillHeight]="layoutCtx.gridSettings.autoFillHeight && !isEdit" [autofillHeight]="autoFillHeight"
[mobileAutofillHeight]="layoutCtx.gridSettings.mobileAutoFillHeight && !isEdit" [mobileAutofillHeight]="mobileAutoFillHeight"
[mobileRowHeight]="layoutCtx.gridSettings.mobileRowHeight" [mobileRowHeight]="layoutCtx.gridSettings.mobileRowHeight"
[isMobile]="isMobile" [isMobile]="isMobile"
[isMobileDisabled]="widgetEditMode" [isMobileDisabled]="isMobileDisabled"
[disableWidgetInteraction]="isEdit" [disableWidgetInteraction]="isEdit"
[isEditActionEnabled]="isEdit" [isEditActionEnabled]="isEdit"
[isExportActionEnabled]="isEdit && !widgetEditMode" [isExportActionEnabled]="isEdit && !widgetEditMode"

20
ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.ts

@ -66,6 +66,26 @@ export class DashboardLayoutComponent extends PageComponent implements ILayoutCo
return this.layoutCtxValue; return this.layoutCtxValue;
} }
get outerMargin(): boolean {
return this.layoutCtx.gridSettings.isScada ? false : this.layoutCtx.gridSettings.outerMargin;
}
get margin(): number {
return this.layoutCtx.gridSettings.isScada ? 0 : this.layoutCtx.gridSettings.margin;
}
get autoFillHeight(): boolean {
return (this.isEdit || this.layoutCtx.gridSettings.isScada) ? false : this.layoutCtx.gridSettings.autoFillHeight;
}
get mobileAutoFillHeight(): boolean {
return (this.isEdit || this.layoutCtx.gridSettings.isScada) ? false : this.layoutCtx.gridSettings.mobileAutoFillHeight;
}
get isMobileDisabled(): boolean {
return this.widgetEditMode || this.layoutCtx.gridSettings.isScada;
}
@Input() @Input()
dashboardCtx: DashboardContext; dashboardCtx: DashboardContext;

1
ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.scss

@ -45,6 +45,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
fill: none;
} }
} }
} }

8
ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts

@ -482,7 +482,13 @@ export class ScadaSymbolObject {
this.svgShape = SVG().svg(doc.documentElement.innerHTML); this.svgShape = SVG().svg(doc.documentElement.innerHTML);
this.svgShape.node.style.overflow = 'visible'; this.svgShape.node.style.overflow = 'visible';
this.svgShape.node.style['user-select'] = 'none'; this.svgShape.node.style['user-select'] = 'none';
this.box = this.svgShape.bbox(); const origSvg = SVG(doc.documentElement.outerHTML);
if (origSvg.type === 'svg') {
this.box = (origSvg as Svg).viewbox();
} else {
this.box = this.svgShape.bbox();
}
origSvg.remove();
this.svgShape.size(this.box.width, this.box.height); this.svgShape.size(this.box.width, this.box.height);
this.svgShape.addTo(this.rootElement); this.svgShape.addTo(this.rootElement);
} }

4
ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts

@ -118,7 +118,7 @@ const routes: Routes = [
data: { data: {
breadcrumb: { breadcrumb: {
label: 'scada.symbols', label: 'scada.symbols',
icon: 'precision_manufacturing' icon: 'view_in_ar'
} }
}, },
children: [ children: [
@ -138,7 +138,7 @@ const routes: Routes = [
data: { data: {
breadcrumb: { breadcrumb: {
labelFunction: scadaSymbolBreadcumbLabelFunction, labelFunction: scadaSymbolBreadcumbLabelFunction,
icon: 'precision_manufacturing' icon: 'view_in_ar'
} as BreadCrumbConfig<ScadaSymbolComponent>, } as BreadCrumbConfig<ScadaSymbolComponent>,
auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN],
title: 'scada.symbol' title: 'scada.symbol'

1
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.scss

@ -19,6 +19,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
fill: none;
} }
.tb-scada-symbol-editor-tooltips { .tb-scada-symbol-editor-tooltips {
position: absolute; position: absolute;

8
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts

@ -100,7 +100,13 @@ export class ScadaSymbolEditObject {
this.svgShape = SVG().svg(contentData.innerSvg); this.svgShape = SVG().svg(contentData.innerSvg);
this.svgShape.node.style.overflow = 'visible'; this.svgShape.node.style.overflow = 'visible';
this.svgShape.node.style['user-select'] = 'none'; this.svgShape.node.style['user-select'] = 'none';
this.box = this.svgShape.bbox(); const origSvg = SVG(svgContent);
if (origSvg.type === 'svg') {
this.box = (origSvg as Svg).viewbox();
} else {
this.box = this.svgShape.bbox();
}
origSvg.remove();
this.svgShape.size(this.box.width, this.box.height); this.svgShape.size(this.box.width, this.box.height);
this.svgShape.viewbox(`0 0 ${this.box.width} ${this.box.height}`); this.svgShape.viewbox(`0 0 ${this.box.width} ${this.box.height}`);
this.svgShape.style().attr('tb:inner', true).rule('.tb-element', {cursor: 'pointer', transition: '0.2s filter ease-in-out'}); this.svgShape.style().attr('tb:inner', true).rule('.tb-element', {cursor: 'pointer', transition: '0.2s filter ease-in-out'});

12
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.ts

@ -71,7 +71,7 @@ import {
UploadImageDialogData, UploadImageDialogResult UploadImageDialogData, UploadImageDialogResult
} from '@shared/components/image/upload-image-dialog.component'; } from '@shared/components/image/upload-image-dialog.component';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { BackgroundType } from '@shared/models/widget-settings.models'; import { BackgroundType, colorBackground } from '@shared/models/widget-settings.models';
import { GridType } from 'angular-gridster2'; import { GridType } from 'angular-gridster2';
import { import {
SaveWidgetTypeAsDialogComponent, SaveWidgetTypeAsDialogData, SaveWidgetTypeAsDialogComponent, SaveWidgetTypeAsDialogData,
@ -259,15 +259,7 @@ export class ScadaSymbolComponent extends PageComponent
scadaSymbolContent: this.symbolData.scadaSymbolContent, scadaSymbolContent: this.symbolData.scadaSymbolContent,
scadaSymbolObjectSettings: this.previewScadaSymbolObjectSettings, scadaSymbolObjectSettings: this.previewScadaSymbolObjectSettings,
padding: '0', padding: '0',
background: { background: colorBackground('rgba(0,0,0,0)')
type: BackgroundType.color,
color: 'rgba(0,0,0,0)',
overlay: {
enabled: false,
color: 'rgba(255,255,255,0.72)',
blur: 3
}
}
} }
}; };
this.previewWidget = { this.previewWidget = {

1
ui-ngx/src/app/shared/models/dashboard.models.ts

@ -61,6 +61,7 @@ export interface GridSettings {
mobileRowHeight?: number; mobileRowHeight?: number;
mobileDisplayLayoutFirst?: boolean; mobileDisplayLayoutFirst?: boolean;
layoutDimension?: LayoutDimension; layoutDimension?: LayoutDimension;
isScada?: boolean;
[key: string]: any; [key: string]: any;
} }

18
ui-ngx/src/app/shared/models/widget-settings.models.ts

@ -935,6 +935,24 @@ export interface BackgroundSettings {
overlay: OverlaySettings; overlay: OverlaySettings;
} }
export const isBackgroundSettings = (background: any): background is BackgroundSettings => {
if (background && background.type && background.overlay && background.overlay.color) {
return true;
} else {
return false;
}
};
export const colorBackground = (color: string): BackgroundSettings => ({
type: BackgroundType.color,
color,
overlay: {
enabled: false,
color: 'rgba(255,255,255,0.72)',
blur: 3
}
});
export const iconStyle = (size: number | string, sizeUnit: cssUnit = 'px'): ComponentStyle => { export const iconStyle = (size: number | string, sizeUnit: cssUnit = 'px'): ComponentStyle => {
const iconSize = typeof size === 'number' ? size + sizeUnit : size; const iconSize = typeof size === 'number' ? size + sizeUnit : size;
return { return {

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

@ -1163,6 +1163,7 @@
"maximum-upload-file-size": "Maximum upload file size: {{ size }}", "maximum-upload-file-size": "Maximum upload file size: {{ size }}",
"cannot-upload-file": "Cannot upload file", "cannot-upload-file": "Cannot upload file",
"settings": "Settings", "settings": "Settings",
"scada-layout": "SCADA layout",
"layout-settings": "Layout settings", "layout-settings": "Layout settings",
"columns-count": "Columns count", "columns-count": "Columns count",
"columns-count-required": "Columns count is required.", "columns-count-required": "Columns count is required.",
@ -1183,7 +1184,7 @@
"apply-outer-margin": "Apply margin to the sides of the layout", "apply-outer-margin": "Apply margin to the sides of the layout",
"autofill-height": "Auto fill layout height", "autofill-height": "Auto fill layout height",
"mobile-layout": "Mobile layout settings", "mobile-layout": "Mobile layout settings",
"mobile-row-height": "Mobile row height, px", "mobile-row-height": "Mobile row height",
"mobile-row-height-required": "Mobile row height value is required.", "mobile-row-height-required": "Mobile row height value is required.",
"min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.",
"max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.",

Loading…
Cancel
Save