UI: Improve dashboard layout settings.
This commit is contained in:
parent
9bd923a878
commit
8fad8245df
44
ui-ngx/patches/angular-gridster2+15.0.4.patch
Normal file
44
ui-ngx/patches/angular-gridster2+15.0.4.patch
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
diff --git a/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs b/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs
|
||||||
|
index cf4e220..4275d11 100644
|
||||||
|
--- a/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs
|
||||||
|
+++ b/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs
|
||||||
|
@@ -208,6 +208,7 @@ const GridsterConfigService = {
|
||||||
|
useTransformPositioning: true,
|
||||||
|
scrollSensitivity: 10,
|
||||||
|
scrollSpeed: 20,
|
||||||
|
+ colWidthUpdateCallback: undefined,
|
||||||
|
initCallback: undefined,
|
||||||
|
destroyCallback: undefined,
|
||||||
|
gridSizeChangedCallback: undefined,
|
||||||
|
@@ -1243,6 +1244,9 @@ class GridsterComponent {
|
||||||
|
this.renderer.setStyle(this.el, 'padding-right', this.$options.margin + 'px');
|
||||||
|
}
|
||||||
|
this.curColWidth = (this.curWidth - marginWidth) / this.columns;
|
||||||
|
+ if (this.options.colWidthUpdateCallback) {
|
||||||
|
+ this.curColWidth = this.options.colWidthUpdateCallback(this.curColWidth);
|
||||||
|
+ }
|
||||||
|
let marginHeight = -this.$options.margin;
|
||||||
|
if (this.$options.outerMarginTop !== null) {
|
||||||
|
marginHeight += this.$options.outerMarginTop;
|
||||||
|
@@ -1266,6 +1270,9 @@ class GridsterComponent {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.curColWidth = (this.curWidth + this.$options.margin) / this.columns;
|
||||||
|
+ if (this.options.colWidthUpdateCallback) {
|
||||||
|
+ this.curColWidth = this.options.colWidthUpdateCallback(this.curColWidth);
|
||||||
|
+ }
|
||||||
|
this.curRowHeight =
|
||||||
|
((this.curHeight + this.$options.margin) / this.rows) *
|
||||||
|
this.$options.rowHeightRatio;
|
||||||
|
diff --git a/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts b/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts
|
||||||
|
index 1d7cdf0..a712b35 100644
|
||||||
|
--- a/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts
|
||||||
|
+++ b/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts
|
||||||
|
@@ -73,6 +73,7 @@ export interface GridsterConfig {
|
||||||
|
useTransformPositioning?: boolean;
|
||||||
|
scrollSensitivity?: number | null;
|
||||||
|
scrollSpeed?: number;
|
||||||
|
+ colWidthUpdateCallback?: (colWidth: number) => number;
|
||||||
|
initCallback?: (gridster: GridsterComponentInterface) => void;
|
||||||
|
destroyCallback?: (gridster: GridsterComponentInterface) => void;
|
||||||
|
gridSizeChangedCallback?: (gridster: GridsterComponentInterface) => void;
|
||||||
@ -610,7 +610,6 @@ export class DashboardUtilsService {
|
|||||||
originalColumns = 24;
|
originalColumns = 24;
|
||||||
}
|
}
|
||||||
const gridSettings = layout.gridSettings;
|
const gridSettings = layout.gridSettings;
|
||||||
if (!gridSettings.isScada) {
|
|
||||||
let columns = 24;
|
let columns = 24;
|
||||||
if (gridSettings && gridSettings.columns) {
|
if (gridSettings && gridSettings.columns) {
|
||||||
columns = gridSettings.columns;
|
columns = gridSettings.columns;
|
||||||
@ -621,7 +620,6 @@ export class DashboardUtilsService {
|
|||||||
widgetLayout.sizeX *= ratio;
|
widgetLayout.sizeX *= ratio;
|
||||||
widgetLayout.sizeY *= ratio;
|
widgetLayout.sizeY *= ratio;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (row > -1 && column > - 1) {
|
if (row > -1 && column > - 1) {
|
||||||
widgetLayout.row = row;
|
widgetLayout.row = row;
|
||||||
@ -686,8 +684,6 @@ 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;
|
||||||
if (!gridSettings.isScada) {
|
|
||||||
let maxRow = 0;
|
|
||||||
for (const w of Object.keys(layout.widgets)) {
|
for (const w of Object.keys(layout.widgets)) {
|
||||||
const widget = layout.widgets[w];
|
const widget = layout.widgets[w];
|
||||||
if (!widget.sizeX) {
|
if (!widget.sizeX) {
|
||||||
@ -696,24 +692,23 @@ export class DashboardUtilsService {
|
|||||||
if (!widget.sizeY) {
|
if (!widget.sizeY) {
|
||||||
widget.sizeY = 1;
|
widget.sizeY = 1;
|
||||||
}
|
}
|
||||||
maxRow = Math.max(maxRow, widget.row + widget.sizeY);
|
|
||||||
}
|
}
|
||||||
const newMaxRow = Math.round(maxRow * ratio);
|
|
||||||
for (const w of Object.keys(layout.widgets)) {
|
for (const w of Object.keys(layout.widgets)) {
|
||||||
const widget = layout.widgets[w];
|
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.row = Math.round(widget.row * ratio);
|
||||||
|
widget.col = Math.round(widget.col * ratio);
|
||||||
|
widget.sizeX = Math.round(widget.sizeX * ratio);
|
||||||
widget.sizeY = Math.round(widget.sizeY * 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public moveWidgets(layout: DashboardLayout, cols: number, rows: number) {
|
||||||
|
cols = isDefinedAndNotNull(cols) ? Math.round(cols) : 0;
|
||||||
|
rows = isDefinedAndNotNull(rows) ? Math.round(rows) : 0;
|
||||||
|
for (const w of Object.keys(layout.widgets)) {
|
||||||
|
const widget = layout.widgets[w];
|
||||||
|
widget.col = Math.max(0, widget.col + cols);
|
||||||
|
widget.row = Math.max(0, widget.row + rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -150,6 +150,10 @@ import { LayoutFixedSize, LayoutWidthType } from '@home/components/dashboard-pag
|
|||||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||||
import { ResizeObserver } from '@juggle/resize-observer';
|
import { ResizeObserver } from '@juggle/resize-observer';
|
||||||
import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard';
|
import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard';
|
||||||
|
import {
|
||||||
|
MoveWidgetsDialogComponent,
|
||||||
|
MoveWidgetsDialogResult
|
||||||
|
} from '@home/components/dashboard-page/layout/move-widgets-dialog.component';
|
||||||
|
|
||||||
// @dynamic
|
// @dynamic
|
||||||
@Component({
|
@Component({
|
||||||
@ -285,7 +289,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
gridSettings: {},
|
gridSettings: {},
|
||||||
ignoreLoading: true,
|
ignoreLoading: true,
|
||||||
ctrl: null,
|
ctrl: null,
|
||||||
dashboardCtrl: this
|
dashboardCtrl: this,
|
||||||
|
displayGrid: 'onDrag&Resize'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
right: {
|
right: {
|
||||||
@ -297,7 +302,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
gridSettings: {},
|
gridSettings: {},
|
||||||
ignoreLoading: true,
|
ignoreLoading: true,
|
||||||
ctrl: null,
|
ctrl: null,
|
||||||
dashboardCtrl: this
|
dashboardCtrl: this,
|
||||||
|
displayGrid: 'onDrag&Resize'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -913,6 +919,28 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private moveWidgets($event: Event, layoutId: DashboardLayoutId) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
this.layouts[layoutId].layoutCtx.displayGrid = 'always';
|
||||||
|
this.cd.markForCheck();
|
||||||
|
this.dialog.open<MoveWidgetsDialogComponent, any,
|
||||||
|
MoveWidgetsDialogResult>(MoveWidgetsDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog']
|
||||||
|
}).afterClosed().subscribe((result) => {
|
||||||
|
this.layouts[layoutId].layoutCtx.displayGrid = 'onDrag&Resize';
|
||||||
|
if (result) {
|
||||||
|
const targetLayout = this.dashboardConfiguration.states[this.dashboardCtx.state].layouts[layoutId];
|
||||||
|
this.dashboardUtils.moveWidgets(targetLayout, result.cols, result.rows);
|
||||||
|
this.updateLayouts();
|
||||||
|
} else {
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private updateDashboardLayouts(newLayouts: DashboardStateLayouts) {
|
private updateDashboardLayouts(newLayouts: DashboardStateLayouts) {
|
||||||
this.dashboardUtils.setLayouts(this.dashboard, this.dashboardCtx.state, newLayouts);
|
this.dashboardUtils.setLayouts(this.dashboard, this.dashboardCtx.state, newLayouts);
|
||||||
this.updateLayouts();
|
this.updateLayouts();
|
||||||
@ -1422,6 +1450,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
|||||||
shortcut: 'M-I'
|
shortcut: 'M-I'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
dashboardContextActions.push(
|
||||||
|
{
|
||||||
|
action: ($event) => {
|
||||||
|
this.moveWidgets($event, layoutCtx.id);
|
||||||
|
},
|
||||||
|
enabled: true,
|
||||||
|
value: 'dashboard.move-all-widgets',
|
||||||
|
icon: 'open_with'
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return dashboardContextActions;
|
return dashboardContextActions;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { IAliasController, IStateController } from '@core/api/widget-api.models'
|
|||||||
import { ILayoutController } from './layout/layout.models';
|
import { ILayoutController } from './layout/layout.models';
|
||||||
import { DashboardContextMenuItem, WidgetContextMenuItem } from '@home/models/dashboard-component.models';
|
import { DashboardContextMenuItem, WidgetContextMenuItem } from '@home/models/dashboard-component.models';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { displayGrids } from 'angular-gridster2/lib/gridsterConfig.interface';
|
||||||
|
|
||||||
export declare type DashboardPageScope = 'tenant' | 'customer';
|
export declare type DashboardPageScope = 'tenant' | 'customer';
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ export interface DashboardPageLayoutContext {
|
|||||||
ctrl: ILayoutController;
|
ctrl: ILayoutController;
|
||||||
dashboardCtrl: IDashboardController;
|
dashboardCtrl: IDashboardController;
|
||||||
ignoreLoading: boolean;
|
ignoreLoading: boolean;
|
||||||
|
displayGrid: displayGrids;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DashboardPageLayout {
|
export interface DashboardPageLayout {
|
||||||
|
|||||||
@ -114,8 +114,9 @@
|
|||||||
<div class="tb-form-panel-title" translate>dashboard.layout-settings</div>
|
<div class="tb-form-panel-title" translate>dashboard.layout-settings</div>
|
||||||
<div class="tb-form-row space-between">
|
<div class="tb-form-row space-between">
|
||||||
<div translate>dashboard.columns-count</div>
|
<div translate>dashboard.columns-count</div>
|
||||||
<mat-form-field appearance="outline" class="number medium-width tb-suffix-absolute" subscriptSizing="dynamic">
|
<mat-form-field *ngIf="!gridSettingsFormGroup.get('isScada').value"
|
||||||
<input matInput formControlName="columns" type="number" min="10" max="1000" step="1"
|
appearance="outline" class="number medium-width tb-suffix-absolute" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="columns" type="number" min="10" max="1008" step="1"
|
||||||
required
|
required
|
||||||
placeholder="{{ 'widget-config.set' | translate }}">
|
placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
<mat-icon matSuffix
|
<mat-icon matSuffix
|
||||||
@ -128,6 +129,32 @@
|
|||||||
warning
|
warning
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<mat-form-field *ngIf="gridSettingsFormGroup.get('isScada').value"
|
||||||
|
class="medium-width" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<mat-select required formControlName="columns">
|
||||||
|
<mat-option *ngFor="let columns of scadaColumns" [value]="columns">
|
||||||
|
{{ columns }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="tb-form-row space-between">
|
||||||
|
<div translate>dashboard.min-layout-width</div>
|
||||||
|
<mat-form-field appearance="outline" class="number medium-width tb-suffix-absolute" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="minColumns" type="number" min="10" max="1008" step="1"
|
||||||
|
required
|
||||||
|
placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
<mat-icon matSuffix
|
||||||
|
matTooltipPosition="above"
|
||||||
|
matTooltipClass="tb-error-tooltip"
|
||||||
|
[matTooltip]="(gridSettingsFormGroup.get('columns').hasError('required') ? 'dashboard.columns-count-required' :
|
||||||
|
(gridSettingsFormGroup.get('columns').hasError('min') ? 'dashboard.min-columns-count-message' : 'dashboard.max-columns-count-message')) | translate"
|
||||||
|
*ngIf="gridSettingsFormGroup.get('minColumns').invalid && gridSettingsFormGroup.get('minColumns').touched"
|
||||||
|
class="tb-error">
|
||||||
|
warning
|
||||||
|
</mat-icon>
|
||||||
|
<span translate matSuffix>dashboard.columns-suffix</span>
|
||||||
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!gridSettingsFormGroup.get('isScada').value" class="tb-form-row space-between">
|
<div *ngIf="!gridSettingsFormGroup.get('isScada').value" class="tb-form-row space-between">
|
||||||
<div translate>dashboard.widgets-margins</div>
|
<div translate>dashboard.widgets-margins</div>
|
||||||
|
|||||||
@ -54,6 +54,8 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
|
|||||||
|
|
||||||
submitted = false;
|
submitted = false;
|
||||||
|
|
||||||
|
scadaColumns: number[] = [];
|
||||||
|
|
||||||
private stateControllerTranslationMap = new Map<string, string>([
|
private stateControllerTranslationMap = new Map<string, string>([
|
||||||
['default', 'dashboard.state-controller-default'],
|
['default', 'dashboard.state-controller-default'],
|
||||||
]);
|
]);
|
||||||
@ -155,10 +157,17 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.gridSettings) {
|
if (this.gridSettings) {
|
||||||
|
let columns = 24;
|
||||||
|
while (columns <= 1008) {
|
||||||
|
this.scadaColumns.push(columns);
|
||||||
|
columns += 24;
|
||||||
|
}
|
||||||
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, []],
|
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(1008)]],
|
||||||
|
minColumns: [this.gridSettings.minColumns || this.gridSettings.columns || 24,
|
||||||
|
[Validators.required, Validators.min(10), Validators.max(1008)]],
|
||||||
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)]],
|
||||||
outerMargin: [isUndefined(this.gridSettings.outerMargin) ? true : this.gridSettings.outerMargin, []],
|
outerMargin: [isUndefined(this.gridSettings.outerMargin) ? true : this.gridSettings.outerMargin, []],
|
||||||
@ -228,6 +237,11 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
|
|||||||
this.gridSettingsFormGroup.get('autoFillHeight').disable();
|
this.gridSettingsFormGroup.get('autoFillHeight').disable();
|
||||||
this.gridSettingsFormGroup.get('mobileAutoFillHeight').disable({emitEvent: false});
|
this.gridSettingsFormGroup.get('mobileAutoFillHeight').disable({emitEvent: false});
|
||||||
this.gridSettingsFormGroup.get('mobileRowHeight').disable();
|
this.gridSettingsFormGroup.get('mobileRowHeight').disable();
|
||||||
|
const columns: number = this.gridSettingsFormGroup.get('columns').value;
|
||||||
|
if (columns % 24 !== 0) {
|
||||||
|
const newColumns = Math.min(1008, 24 * Math.ceil(columns / 24));
|
||||||
|
this.gridSettingsFormGroup.get('columns').patchValue(newColumns);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.gridSettingsFormGroup.get('margin').enable();
|
this.gridSettingsFormGroup.get('margin').enable();
|
||||||
this.gridSettingsFormGroup.get('outerMargin').enable();
|
this.gridSettingsFormGroup.get('outerMargin').enable();
|
||||||
|
|||||||
@ -41,7 +41,9 @@
|
|||||||
[backgroundImage]="backgroundImage$ | async"
|
[backgroundImage]="backgroundImage$ | async"
|
||||||
[widgets]="layoutCtx.widgets"
|
[widgets]="layoutCtx.widgets"
|
||||||
[widgetLayouts]="layoutCtx.widgetLayouts"
|
[widgetLayouts]="layoutCtx.widgetLayouts"
|
||||||
[columns]="layoutCtx.gridSettings.columns"
|
[columns]="columns"
|
||||||
|
[displayGrid]="displayGrid"
|
||||||
|
[colWidthInteger]="colWidthInteger"
|
||||||
[outerMargin]="outerMargin"
|
[outerMargin]="outerMargin"
|
||||||
[margin]="margin"
|
[margin]="margin"
|
||||||
[aliasController]="dashboardCtx.aliasController"
|
[aliasController]="dashboardCtx.aliasController"
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import { TbCheatSheetComponent } from '@shared/components/cheatsheet.component';
|
|||||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||||
import { ImagePipe } from '@shared/pipe/image.pipe';
|
import { ImagePipe } from '@shared/pipe/image.pipe';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
import { displayGrids } from 'angular-gridster2/lib/gridsterConfig.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-dashboard-layout',
|
selector: 'tb-dashboard-layout',
|
||||||
@ -86,6 +87,18 @@ export class DashboardLayoutComponent extends PageComponent implements ILayoutCo
|
|||||||
return this.widgetEditMode || this.layoutCtx.gridSettings.isScada;
|
return this.widgetEditMode || this.layoutCtx.gridSettings.isScada;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get colWidthInteger(): boolean {
|
||||||
|
return this.layoutCtx.gridSettings.isScada;
|
||||||
|
}
|
||||||
|
|
||||||
|
get columns(): number {
|
||||||
|
return this.layoutCtx.gridSettings.minColumns || this.layoutCtx.gridSettings.columns || 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
get displayGrid(): displayGrids {
|
||||||
|
return this.layoutCtx.displayGrid || 'onDrag&Resize';
|
||||||
|
}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
dashboardCtx: DashboardContext;
|
dashboardCtx: DashboardContext;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,60 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2024 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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<form (ngSubmit)="move()">
|
||||||
|
<mat-toolbar color="primary">
|
||||||
|
<h2 translate>dashboard.move-all-widgets</h2>
|
||||||
|
<span fxFlex></span>
|
||||||
|
<button mat-icon-button
|
||||||
|
(click)="cancel()"
|
||||||
|
type="button">
|
||||||
|
<mat-icon class="material-icons">close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-toolbar>
|
||||||
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||||
|
</mat-progress-bar>
|
||||||
|
<div mat-dialog-content class="tb-form-panel no-border no-padding" [formGroup]="moveWidgetsFormGroup">
|
||||||
|
<div class="tb-form-row space-between column-xs">
|
||||||
|
<div translate>dashboard.move-by</div>
|
||||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
|
||||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
|
||||||
|
<input required matInput formControlName="cols"
|
||||||
|
type="number" step="1" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
<span translate matSuffix>dashboard.cols</span>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
|
||||||
|
<input required matInput formControlName="rows"
|
||||||
|
type="number" step="1" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
<span translate matSuffix>dashboard.rows</span>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions fxLayoutAlign="end center">
|
||||||
|
<button mat-button color="primary"
|
||||||
|
type="button"
|
||||||
|
[disabled]="(isLoading$ | async)"
|
||||||
|
(click)="cancel()" cdkFocusInitial>
|
||||||
|
{{ 'action.cancel' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button color="primary"
|
||||||
|
type="submit"
|
||||||
|
[disabled]="(isLoading$ | async) || moveWidgetsFormGroup.invalid || !moveWidgetsFormGroup.dirty">
|
||||||
|
{{ 'action.move' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 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 } from '@angular/core';
|
||||||
|
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '@core/core.state';
|
||||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { DialogComponent } from '@app/shared/components/dialog.component';
|
||||||
|
|
||||||
|
export interface MoveWidgetsDialogResult {
|
||||||
|
cols: number;
|
||||||
|
rows: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-move-widgets-dialog',
|
||||||
|
templateUrl: './move-widgets-dialog.component.html',
|
||||||
|
providers: [],
|
||||||
|
styleUrls: []
|
||||||
|
})
|
||||||
|
export class MoveWidgetsDialogComponent extends DialogComponent<MoveWidgetsDialogComponent, MoveWidgetsDialogResult> {
|
||||||
|
|
||||||
|
moveWidgetsFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
|
constructor(protected store: Store<AppState>,
|
||||||
|
protected router: Router,
|
||||||
|
protected dialogRef: MatDialogRef<MoveWidgetsDialogComponent, MoveWidgetsDialogResult>,
|
||||||
|
private fb: UntypedFormBuilder,
|
||||||
|
private dialog: MatDialog) {
|
||||||
|
super(store, router, dialogRef);
|
||||||
|
|
||||||
|
this.moveWidgetsFormGroup = this.fb.group({
|
||||||
|
cols: [0, [Validators.required]],
|
||||||
|
rows: [0, [Validators.required]]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(): void {
|
||||||
|
this.dialogRef.close(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
move(): void {
|
||||||
|
const result: MoveWidgetsDialogResult = this.moveWidgetsFormGroup.value;
|
||||||
|
this.dialogRef.close(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -90,6 +90,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
|
|||||||
@Input()
|
@Input()
|
||||||
columns: number;
|
columns: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
colWidthInteger = false;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
setGridSize = false;
|
setGridSize = false;
|
||||||
@ -256,15 +260,22 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
|
|||||||
itemChangeCallback: item => this.dashboardWidgets.sortWidgets(),
|
itemChangeCallback: item => this.dashboardWidgets.sortWidgets(),
|
||||||
itemInitCallback: (item, itemComponent) => {
|
itemInitCallback: (item, itemComponent) => {
|
||||||
(itemComponent.item as DashboardWidget).gridsterItemComponent = itemComponent;
|
(itemComponent.item as DashboardWidget).gridsterItemComponent = itemComponent;
|
||||||
|
},
|
||||||
|
colWidthUpdateCallback: (colWidth) => {
|
||||||
|
if (this.colWidthInteger) {
|
||||||
|
return Math.floor(colWidth);
|
||||||
|
} else {
|
||||||
|
return colWidth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateMobileOpts();
|
this.updateGridOpts();
|
||||||
|
|
||||||
this.breakpointObserverSubscription = this.breakpointObserver
|
this.breakpointObserverSubscription = this.breakpointObserver
|
||||||
.observe(MediaBreakpoints['gt-sm']).subscribe(
|
.observe(MediaBreakpoints['gt-sm']).subscribe(
|
||||||
() => {
|
() => {
|
||||||
this.updateMobileOpts();
|
this.updateGridOpts();
|
||||||
this.notifyGridsterOptionsChanged();
|
this.notifyGridsterOptionsChanged();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -291,20 +302,24 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
let updateMobileOpts = false;
|
let updateGridOpts = false;
|
||||||
let updateLayoutOpts = false;
|
let updateColumnOpts = false;
|
||||||
let updateEditingOpts = false;
|
let updateEditingOpts = false;
|
||||||
|
let updateDisplayGridOpts = false;
|
||||||
let updateWidgets = false;
|
let updateWidgets = false;
|
||||||
let updateDashboardTimewindow = false;
|
let updateDashboardTimewindow = false;
|
||||||
for (const propName of Object.keys(changes)) {
|
for (const propName of Object.keys(changes)) {
|
||||||
const change = changes[propName];
|
const change = changes[propName];
|
||||||
if (!change.firstChange && change.currentValue !== change.previousValue) {
|
if (!change.firstChange && change.currentValue !== change.previousValue) {
|
||||||
if (['isMobile', 'isMobileDisabled', 'autofillHeight', 'mobileAutofillHeight', 'mobileRowHeight'].includes(propName)) {
|
if (['isMobile', 'isMobileDisabled', 'autofillHeight', 'mobileAutofillHeight',
|
||||||
updateMobileOpts = true;
|
'mobileRowHeight', 'colWidthInteger'].includes(propName)) {
|
||||||
|
updateGridOpts = true;
|
||||||
} else if (['outerMargin', 'margin', 'columns'].includes(propName)) {
|
} else if (['outerMargin', 'margin', 'columns'].includes(propName)) {
|
||||||
updateLayoutOpts = true;
|
updateColumnOpts = true;
|
||||||
} else if (['isEdit', 'isEditingWidget'].includes(propName)) {
|
} else if (['isEdit', 'isEditingWidget'].includes(propName)) {
|
||||||
updateEditingOpts = true;
|
updateEditingOpts = true;
|
||||||
|
} else if (propName === 'displayGrid') {
|
||||||
|
updateDisplayGridOpts = true;
|
||||||
} else if (['widgets', 'widgetLayouts'].includes(propName)) {
|
} else if (['widgets', 'widgetLayouts'].includes(propName)) {
|
||||||
updateWidgets = true;
|
updateWidgets = true;
|
||||||
} else if (propName === 'dashboardTimewindow') {
|
} else if (propName === 'dashboardTimewindow') {
|
||||||
@ -318,16 +333,19 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
|
|||||||
this.dashboardTimewindowChangedSubject.next(this.dashboardTimewindow);
|
this.dashboardTimewindowChangedSubject.next(this.dashboardTimewindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateLayoutOpts) {
|
if (updateColumnOpts) {
|
||||||
this.updateLayoutOpts();
|
this.updateColumnOpts();
|
||||||
}
|
}
|
||||||
if (updateMobileOpts) {
|
if (updateGridOpts) {
|
||||||
this.updateMobileOpts();
|
this.updateGridOpts();
|
||||||
}
|
}
|
||||||
if (updateEditingOpts) {
|
if (updateEditingOpts) {
|
||||||
this.updateEditingOpts();
|
this.updateEditingOpts();
|
||||||
}
|
}
|
||||||
if (updateMobileOpts || updateLayoutOpts || updateEditingOpts) {
|
if (updateDisplayGridOpts) {
|
||||||
|
this.updateDisplayGridOpts();
|
||||||
|
}
|
||||||
|
if (updateGridOpts || updateColumnOpts || updateEditingOpts || updateDisplayGridOpts) {
|
||||||
this.notifyGridsterOptionsChanged();
|
this.notifyGridsterOptionsChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,7 +562,15 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateMobileOpts(parentHeight?: number) {
|
private onGridsterParentResize() {
|
||||||
|
const parentHeight = this.gridster.el.offsetHeight;
|
||||||
|
if (this.isMobileSize && this.mobileAutofillHeight && parentHeight) {
|
||||||
|
this.updateGridOpts(parentHeight);
|
||||||
|
}
|
||||||
|
this.notifyGridsterOptionsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateGridOpts(parentHeight?: number) {
|
||||||
let updateWidgetRowsAndSort = false;
|
let updateWidgetRowsAndSort = false;
|
||||||
const isMobileSize = this.checkIsMobileSize();
|
const isMobileSize = this.checkIsMobileSize();
|
||||||
if (this.isMobileSize !== isMobileSize) {
|
if (this.isMobileSize !== isMobileSize) {
|
||||||
@ -568,15 +594,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onGridsterParentResize() {
|
private updateColumnOpts() {
|
||||||
const parentHeight = this.gridster.el.offsetHeight;
|
|
||||||
if (this.isMobileSize && this.mobileAutofillHeight && parentHeight) {
|
|
||||||
this.updateMobileOpts(parentHeight);
|
|
||||||
}
|
|
||||||
this.notifyGridsterOptionsChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateLayoutOpts() {
|
|
||||||
this.gridsterOpts.minCols = this.columns ? this.columns : 24;
|
this.gridsterOpts.minCols = this.columns ? this.columns : 24;
|
||||||
this.gridsterOpts.outerMargin = isDefined(this.outerMargin) ? this.outerMargin : true;
|
this.gridsterOpts.outerMargin = isDefined(this.outerMargin) ? this.outerMargin : true;
|
||||||
this.gridsterOpts.margin = isDefined(this.margin) ? this.margin : 10;
|
this.gridsterOpts.margin = isDefined(this.margin) ? this.margin : 10;
|
||||||
@ -587,6 +605,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
|
|||||||
this.gridsterOpts.draggable.enabled = this.isEdit && !this.isEditingWidget;
|
this.gridsterOpts.draggable.enabled = this.isEdit && !this.isEditingWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateDisplayGridOpts() {
|
||||||
|
this.gridsterOpts.displayGrid = this.displayGrid;
|
||||||
|
}
|
||||||
|
|
||||||
public notifyGridsterOptionsChanged() {
|
public notifyGridsterOptionsChanged() {
|
||||||
if (!this.optionsChangeNotificationsPaused) {
|
if (!this.optionsChangeNotificationsPaused) {
|
||||||
if (this.gridster && this.gridster.options) {
|
if (this.gridster && this.gridster.options) {
|
||||||
|
|||||||
@ -174,6 +174,7 @@ import {
|
|||||||
import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module';
|
import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module';
|
||||||
import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/basic-widget-config.module';
|
import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/basic-widget-config.module';
|
||||||
import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component';
|
import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component';
|
||||||
|
import { MoveWidgetsDialogComponent } from '@home/components/dashboard-page/layout/move-widgets-dialog.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations:
|
declarations:
|
||||||
@ -287,6 +288,7 @@ import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delet
|
|||||||
EditWidgetComponent,
|
EditWidgetComponent,
|
||||||
DashboardWidgetSelectComponent,
|
DashboardWidgetSelectComponent,
|
||||||
AddWidgetDialogComponent,
|
AddWidgetDialogComponent,
|
||||||
|
MoveWidgetsDialogComponent,
|
||||||
ManageDashboardLayoutsDialogComponent,
|
ManageDashboardLayoutsDialogComponent,
|
||||||
DashboardSettingsDialogComponent,
|
DashboardSettingsDialogComponent,
|
||||||
ManageDashboardStatesDialogComponent,
|
ManageDashboardStatesDialogComponent,
|
||||||
@ -419,6 +421,7 @@ import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delet
|
|||||||
EditWidgetComponent,
|
EditWidgetComponent,
|
||||||
DashboardWidgetSelectComponent,
|
DashboardWidgetSelectComponent,
|
||||||
AddWidgetDialogComponent,
|
AddWidgetDialogComponent,
|
||||||
|
MoveWidgetsDialogComponent,
|
||||||
ManageDashboardLayoutsDialogComponent,
|
ManageDashboardLayoutsDialogComponent,
|
||||||
DashboardSettingsDialogComponent,
|
DashboardSettingsDialogComponent,
|
||||||
ManageDashboardStatesDialogComponent,
|
ManageDashboardStatesDialogComponent,
|
||||||
|
|||||||
@ -52,6 +52,7 @@ export interface WidgetLayouts {
|
|||||||
export interface GridSettings {
|
export interface GridSettings {
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
columns?: number;
|
columns?: number;
|
||||||
|
minColumns?: number;
|
||||||
margin?: number;
|
margin?: number;
|
||||||
outerMargin?: boolean;
|
outerMargin?: boolean;
|
||||||
backgroundSizeMode?: string;
|
backgroundSizeMode?: string;
|
||||||
|
|||||||
@ -1163,12 +1163,18 @@
|
|||||||
"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",
|
||||||
|
"move-all-widgets": "Move all widgets",
|
||||||
|
"move-by": "Move by",
|
||||||
|
"cols": "cols",
|
||||||
|
"rows": "rows",
|
||||||
"scada-layout": "SCADA layout",
|
"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.",
|
||||||
"min-columns-count-message": "Only 10 minimum column count is allowed.",
|
"min-columns-count-message": "Only 10 minimum column count is allowed.",
|
||||||
"max-columns-count-message": "Only 1000 maximum column count is allowed.",
|
"max-columns-count-message": "Only 1000 maximum column count is allowed.",
|
||||||
|
"min-layout-width": "Minimum layout width",
|
||||||
|
"columns-suffix": "columns",
|
||||||
"widgets-margins": "Margin between widgets",
|
"widgets-margins": "Margin between widgets",
|
||||||
"margin-required": "Margin value is required.",
|
"margin-required": "Margin value is required.",
|
||||||
"min-margin-message": "Only 0 is allowed as minimum margin value.",
|
"min-margin-message": "Only 0 is allowed as minimum margin value.",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user