diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 53d1df8219..2196969d01 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -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; diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts index 1d2fb7bfcf..7a2b550201 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts @@ -187,6 +187,8 @@ export class AddWidgetDialogComponent extends DialogComponent -
-
- - {{ 'widget-config.mobile-hide' | translate }} - +
+
+
widget-config.resize-options
+
+ + {{ 'widget-config.resizable' | translate }} + +
+
+ + {{ 'widget-config.preserve-aspect-ratio' | translate }} + +
-
- - {{ 'widget-config.desktop-hide' | translate }} - -
-
+
+
{{ (isDefaultBreakpoint ? 'widget-config.mobile' : 'widget-config.list-layout') | translate }}
+
+ + {{ 'widget-config.mobile-hide' | translate }} + +
+
+ + {{ 'widget-config.desktop-hide' | translate }} + +
widget-config.order
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index f554c4bf3f..643a5da990 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -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, diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index e7fdfa78e3..ed9ca5123f 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -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(); 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; } } diff --git a/ui-ngx/src/app/shared/models/dashboard.models.ts b/ui-ngx/src/app/shared/models/dashboard.models.ts index 2621175030..c4e767b314 100644 --- a/ui-ngx/src/app/shared/models/dashboard.models.ts +++ b/ui-ngx/src/app/shared/models/dashboard.models.ts @@ -43,6 +43,8 @@ export interface WidgetLayout { mobileOrder?: number; col?: number; row?: number; + resizable?: boolean; + preserveAspectRatio?: boolean; } export interface WidgetLayouts { diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 3b60cd727e..429bb79e35 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -761,6 +761,8 @@ export interface WidgetConfig { displayTimewindow?: boolean; timewindow?: Timewindow; timewindowStyle?: TimewindowStyle; + resizable?: boolean; + preserveAspectRatio?: boolean; desktopHide?: boolean; mobileHide?: boolean; mobileHeight?: number; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 4dd7125f72..aaa51ea3dd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -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",