UI: Improve widget layout settings: Add preserve aspect ratio and resizable options.
This commit is contained in:
		
							parent
							
								
									bf6bd064b7
								
							
						
					
					
						commit
						1c2d1f8780
					
				@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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)) {
 | 
			
		||||
 | 
			
		||||
@ -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">
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,6 +43,8 @@ export interface WidgetLayout {
 | 
			
		||||
  mobileOrder?: number;
 | 
			
		||||
  col?: number;
 | 
			
		||||
  row?: number;
 | 
			
		||||
  resizable?: boolean;
 | 
			
		||||
  preserveAspectRatio?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface WidgetLayouts {
 | 
			
		||||
 | 
			
		||||
@ -761,6 +761,8 @@ export interface WidgetConfig {
 | 
			
		||||
  displayTimewindow?: boolean;
 | 
			
		||||
  timewindow?: Timewindow;
 | 
			
		||||
  timewindowStyle?: TimewindowStyle;
 | 
			
		||||
  resizable?: boolean;
 | 
			
		||||
  preserveAspectRatio?: boolean;
 | 
			
		||||
  desktopHide?: boolean;
 | 
			
		||||
  mobileHide?: boolean;
 | 
			
		||||
  mobileHeight?: number;
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user