UI: Improve widget layout settings: Add preserve aspect ratio and resizable options.

This commit is contained in:
Igor Kulikov 2024-09-05 10:19:05 +03:00
parent bf6bd064b7
commit 1c2d1f8780
9 changed files with 206 additions and 49 deletions

View File

@ -345,10 +345,11 @@ export class DashboardUtilsService {
return widgetConfig;
}
public prepareWidgetForScadaLayout(widget: Widget): Widget {
public prepareWidgetForScadaLayout(widget: Widget, isScada: boolean): Widget {
const config = widget.config;
config.showTitle = false;
config.dropShadow = false;
config.preserveAspectRatio = isScada;
config.padding = '0';
config.margin = '0';
config.backgroundColor = 'rgba(0,0,0,0)';
@ -654,7 +655,9 @@ export class DashboardUtilsService {
mobileOrder: widget.config.mobileOrder,
mobileHeight: widget.config.mobileHeight,
mobileHide: widget.config.mobileHide,
desktopHide: widget.config.desktopHide
desktopHide: widget.config.desktopHide,
preserveAspectRatio: widget.config.preserveAspectRatio,
resizable: widget.config.resizable
};
if (isUndefined(originalColumns)) {
originalColumns = 24;

View File

@ -187,6 +187,8 @@ export class AddWidgetDialogComponent extends DialogComponent<AddWidgetDialogCom
this.widget.config.mobileHeight = widgetConfig.layout.mobileHeight;
this.widget.config.mobileHide = widgetConfig.layout.mobileHide;
this.widget.config.desktopHide = widgetConfig.layout.desktopHide;
this.widget.config.preserveAspectRatio = widgetConfig.layout.preserveAspectRatio;
this.widget.config.resizable = widgetConfig.layout.resizable;
this.dialogRef.close(this.widget);
}
}

View File

@ -1351,7 +1351,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
const scada = this.isAddingToScadaLayout();
if (scada) {
newWidget = this.dashboardUtils.prepareWidgetForScadaLayout(newWidget);
newWidget = this.dashboardUtils.prepareWidgetForScadaLayout(newWidget, widgetTypeInfo.scada);
}
let showLayoutConfig = true;
if (scada || this.layouts.right.show || !this.showLayoutConfigInEdit(this.layouts.main.layoutCtx)) {

View File

@ -168,18 +168,32 @@
</tb-manage-widget-actions>
</div>
</div>
<div [fxShow]="selectedOption === 'mobile'" [formGroup]="layoutSettings" class="mat-content">
<div class="tb-form-panel" *ngIf="isDefaultBreakpoint">
<mat-slide-toggle class="mat-slide" formControlName="mobileHide">
{{ 'widget-config.mobile-hide' | translate }}
</mat-slide-toggle>
<div [fxShow]="selectedOption === 'layout'" [formGroup]="layoutSettings" class="mat-content">
<div class="tb-form-panel" *ngIf="!showLayoutConfig || isDefaultBreakpoint">
<div class="tb-form-panel-title" translate>widget-config.resize-options</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="resizable">
{{ 'widget-config.resizable' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="preserveAspectRatio">
{{ 'widget-config.preserve-aspect-ratio' | translate }}
</mat-slide-toggle>
</div>
</div>
<div class="tb-form-panel" *ngIf="isDefaultBreakpoint">
<mat-slide-toggle class="mat-slide" formControlName="desktopHide">
{{ 'widget-config.desktop-hide' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel" *ngIf="showLayoutConfig">
<div class="tb-form-panel-title">{{ (isDefaultBreakpoint ? 'widget-config.mobile' : 'widget-config.list-layout') | translate }}</div>
<div class="tb-form-row" *ngIf="isDefaultBreakpoint">
<mat-slide-toggle class="mat-slide" formControlName="mobileHide">
{{ 'widget-config.mobile-hide' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row" *ngIf="isDefaultBreakpoint">
<mat-slide-toggle class="mat-slide" formControlName="desktopHide">
{{ 'widget-config.desktop-hide' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between">
<div translate>widget-config.order</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">

View File

@ -256,6 +256,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
});
this.layoutSettings = this.fb.group({
resizable: [true],
preserveAspectRatio: [false],
mobileOrder: [null, [Validators.pattern(/^-?[0-9]+$/)]],
mobileHeight: [null, [Validators.min(1), Validators.pattern(/^\d*$/)]],
mobileHide: [false],
@ -349,16 +351,12 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
value: 'actions'
}
);
if (this.showLayoutConfig) {
this.headerOptions.push(
{
name: this.isDefaultBreakpoint
? this.translate.instant('widget-config.mobile')
: this.translate.instant('widget-config.list-layout'),
value: 'mobile'
}
);
}
this.headerOptions.push(
{
name: this.translate.instant('widget-config.layout'),
value: 'layout'
}
);
if (!this.selectedOption || !this.headerOptions.find(o => o.value === this.selectedOption)) {
this.selectedOption = this.headerOptions[0].value;
}
@ -569,6 +567,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
if (layout) {
this.layoutSettings.patchValue(
{
resizable: isDefined(layout.resizable) ? layout.resizable : true,
preserveAspectRatio: layout.preserveAspectRatio,
mobileOrder: layout.mobileOrder,
mobileHeight: layout.mobileHeight,
mobileHide: layout.mobileHide,
@ -579,6 +579,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
} else {
this.layoutSettings.patchValue(
{
resizable: true,
preserveAspectRatio: false,
mobileOrder: null,
mobileHeight: null,
mobileHide: false,

View File

@ -343,6 +343,10 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
private selectedValue = false;
private selectedCallback: (selected: boolean) => void = () => {};
resizableHandles = {} as any;
resizeEnabled = true;
isFullscreen = false;
isReference = false;
@ -387,6 +391,15 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
private gridsterItemComponentSubject = new Subject<GridsterItemComponentInterface>();
private gridsterItemComponentValue: GridsterItemComponentInterface;
private readonly aspectRatio: number;
private heightValue: number;
private widthValue: number;
private rowsValue: number;
private colsValue: number;
get mobileHide(): boolean {
return this.widgetLayout ? this.widgetLayout.mobileHide === true : false;
}
@ -397,10 +410,96 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
set gridsterItemComponent(item: GridsterItemComponentInterface) {
this.gridsterItemComponentValue = item;
if (this.widgetLayout?.preserveAspectRatio) {
this.applyPreserveAspectRatio(item);
}
this.gridsterItemComponentSubject.next(this.gridsterItemComponentValue);
this.gridsterItemComponentSubject.complete();
}
private applyPreserveAspectRatio(item: GridsterItemComponentInterface) {
this.resizableHandles.ne = false;
this.resizableHandles.sw = false;
this.resizableHandles.nw = false;
const $item = item.$item;
this.rowsValue = $item.rows;
this.colsValue = $item.cols;
Object.defineProperty($item, 'rows', {
get: () => this.rowsValue,
set: v => {
if (this.rowsValue !== v) {
if (this.preserveAspectRatio) {
this.colsValue = v * this.aspectRatio;
}
this.rowsValue = v;
}
}
});
Object.defineProperty($item, 'cols', {
get: () => this.colsValue,
set: v => {
if (this.colsValue !== v) {
if (this.preserveAspectRatio) {
this.rowsValue = v / this.aspectRatio;
}
this.colsValue = v;
}
}
});
const resizable = item.resize;
this.heightValue = resizable.height;
this.widthValue = resizable.width;
const setItemHeight = resizable.setItemHeight.bind(resizable);
const setItemWidth = resizable.setItemWidth.bind(resizable);
resizable.setItemHeight = (height) => {
setItemHeight(height);
this.heightValue = height;
if (this.preserveAspectRatio) {
setItemWidth(height * this.aspectRatio);
}
};
resizable.setItemWidth = (width) => {
setItemWidth(width);
this.widthValue = width;
if (this.preserveAspectRatio) {
setItemHeight(width / this.aspectRatio);
}
};
Object.defineProperty(resizable, 'height', {
get: () => this.heightValue,
set: v => {
if (this.heightValue !== v) {
if (this.preserveAspectRatio) {
this.widthValue = v * this.aspectRatio;
}
this.heightValue = v;
}
}
});
Object.defineProperty(resizable, 'width', {
get: () => this.widthValue,
set: v => {
if (this.widthValue !== v) {
if (this.preserveAspectRatio) {
this.heightValue = v / this.aspectRatio;
}
this.widthValue = v;
}
}
});
}
get highlighted() {
return this.highlightedValue;
}
@ -434,6 +533,13 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
public widgetLayout?: WidgetLayout,
private parentDashboard?: IDashboardComponent,
private popoverComponent?: TbPopoverComponent) {
if (isDefined(widgetLayout?.resizable)) {
this.resizeEnabled = widgetLayout.resizable;
}
if (widgetLayout?.preserveAspectRatio) {
this.aspectRatio = this.widgetLayout.sizeX / this.widgetLayout.sizeY;
}
if (!widget.id) {
widget.id = guid();
}
@ -603,30 +709,28 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
}
}
get preserveAspectRatio(): boolean {
if (!this.dashboard.isMobileSize && this.widgetLayout) {
return this.widgetLayout.preserveAspectRatio;
} else {
return false;
}
}
@enumerable(true)
get cols(): number {
let res;
if (this.widgetLayout) {
res = this.widgetLayout.sizeX;
} else {
res = this.widget.sizeX;
}
return Math.floor(res);
return Math.floor(this.sizeX);
}
set cols(cols: number) {
if (!this.dashboard.isMobileSize) {
if (this.widgetLayout) {
this.widgetLayout.sizeX = cols;
} else {
this.widget.sizeX = cols;
}
this.sizeX = cols;
}
}
@enumerable(true)
get rows(): number {
let res;
let res: number;
if (this.dashboard.isMobileSize) {
let mobileHeight;
if (this.widgetLayout) {
@ -638,26 +742,50 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
if (mobileHeight) {
res = mobileHeight;
} else {
const sizeY = this.widgetLayout ? this.widgetLayout.sizeY : this.widget.sizeY;
const sizeY = this.sizeY;
res = sizeY * 24 / this.dashboard.gridsterOpts.minCols;
}
} else {
if (this.widgetLayout) {
res = this.widgetLayout.sizeY;
} else {
res = this.widget.sizeY;
}
res = this.sizeY;
}
return Math.floor(res);
}
set rows(rows: number) {
if (!this.dashboard.isMobileSize && !this.dashboard.autofillHeight) {
if (this.widgetLayout) {
this.widgetLayout.sizeY = rows;
} else {
this.widget.sizeY = rows;
}
this.sizeY = rows;
}
}
get sizeX(): number {
if (this.widgetLayout) {
return this.widgetLayout.sizeX;
} else {
return this.widget.sizeX;
}
}
set sizeX(sizeX: number) {
if (this.widgetLayout) {
this.widgetLayout.sizeX = sizeX;
} else {
this.widget.sizeX = sizeX;
}
}
get sizeY(): number {
if (this.widgetLayout) {
return this.widgetLayout.sizeY;
} else {
return this.widget.sizeY;
}
}
set sizeY(sizeY: number) {
if (this.widgetLayout) {
this.widgetLayout.sizeY = sizeY;
} else {
this.widget.sizeY = sizeY;
}
}

View File

@ -43,6 +43,8 @@ export interface WidgetLayout {
mobileOrder?: number;
col?: number;
row?: number;
resizable?: boolean;
preserveAspectRatio?: boolean;
}
export interface WidgetLayouts {

View File

@ -761,6 +761,8 @@ export interface WidgetConfig {
displayTimewindow?: boolean;
timewindow?: Timewindow;
timewindowStyle?: TimewindowStyle;
resizable?: boolean;
preserveAspectRatio?: boolean;
desktopHide?: boolean;
mobileHide?: boolean;
mobileHeight?: number;

View File

@ -6045,7 +6045,11 @@
"color": "Color",
"tooltip": "Tooltip",
"units-required": "Unit is required.",
"list-layout": "List layout"
"list-layout": "List layout",
"layout": "Layout",
"resize-options": "Resize options",
"resizable": "Resizable",
"preserve-aspect-ratio": "Preserve aspect ratio"
},
"widget-type": {
"import": "Import widget type",