@ -40,8 +40,9 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state' ;
import { ActivatedRoute , Router } from '@angular/router' ;
import { UtilsService } from '@core/services/utils.service' ;
import { AuthService } from '@core/auth/auth.service' ;
import {
BreakpointId ,
BreakpointInfo ,
Dashboard ,
DashboardConfiguration ,
DashboardLayoutId ,
@ -52,6 +53,7 @@ import {
GridSettings ,
LayoutDimension ,
LayoutType ,
ViewFormatType ,
WidgetLayout
} from '@app/shared/models/dashboard.models' ;
import { WINDOW } from '@core/services/window.service' ;
@ -67,7 +69,7 @@ import {
IDashboardController ,
LayoutWidgetsArray
} from './dashboard-page.models' ;
import { BreakpointObserver , BreakpointState } from '@angular/cdk/layout' ;
import { BreakpointObserver } from '@angular/cdk/layout' ;
import { MediaBreakpoints } from '@shared/models/constants' ;
import { AuthUser } from '@shared/models/user.model' ;
import { getCurrentAuthState } from '@core/auth/auth.selectors' ;
@ -84,7 +86,7 @@ import { Authority } from '@shared/models/authority.enum';
import { DialogService } from '@core/services/dialog.service' ;
import { EntityService } from '@core/http/entity.service' ;
import { AliasController } from '@core/api/alias-controller' ;
import { Observable , of , Subscription } from 'rxjs' ;
import { BehaviorSubject , Observable , of , Subject , Subscription } from 'rxjs' ;
import { DashboardUtilsService } from '@core/services/dashboard-utils.service' ;
import { DashboardService } from '@core/http/dashboard.service' ;
import {
@ -93,7 +95,6 @@ import {
WidgetContextMenuItem
} from '../../models/dashboard-component.models' ;
import { WidgetComponentService } from '../../components/widget/widget-component.service' ;
import { UntypedFormBuilder } from '@angular/forms' ;
import { ItemBufferService } from '@core/services/item-buffer.service' ;
import { MatDialog } from '@angular/material/dialog' ;
import {
@ -139,14 +140,14 @@ import {
DashboardImageDialogData ,
DashboardImageDialogResult
} from '@home/components/dashboard-page/dashboard-image-dialog.component' ;
import { DomSanitizer , SafeUrl } from '@angular/platform-browser' ;
import { SafeUrl } from '@angular/platform-browser' ;
import cssjs from '@core/css/css' ;
import { DOCUMENT } from '@angular/common' ;
import { IAliasController } from '@core/api/widget-api.models' ;
import { MatButton } from '@angular/material/button' ;
import { VersionControlComponent } from '@home/components/vc/version-control.component' ;
import { TbPopoverService } from '@shared/components/popover.service' ;
import { map , tap } from 'rxjs/operators' ;
import { distinctUntilChanged , map , ski p , tap } from 'rxjs/operators' ;
import { LayoutFixedSize , LayoutWidthType } from '@home/components/dashboard-page/layout/layout.models' ;
import { TbPopoverComponent } from '@shared/components/popover.component' ;
import { ResizeObserver } from '@juggle/resize-observer' ;
@ -262,7 +263,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
addingLayoutCtx : DashboardPageLayoutContext ;
mainLayoutSize : { width : string ; height : string } = { width : '100%' , height : '100%' } ;
mainLayoutSize : { width : string ; height : string ; maxWidth : string ; minWidth : string } =
{ width : '100%' , height : '100%' , maxWidth : '100%' , minWidth : '100%' } ;
rightLayoutSize : { width : string ; height : string } = { width : '100%' , height : '100%' } ;
private dashboardLogoCache : SafeUrl ;
@ -275,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
getDashboard : ( ) = > this . dashboard ,
dashboardTimewindow : null ,
state : null ,
breakpoint : null ,
stateController : null ,
stateChanged : null ,
stateId : null ,
@ -287,26 +290,32 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
show : false ,
layoutCtx : {
id : 'main' ,
breakpoint : 'default' ,
widgets : null ,
widgetLayouts : { } ,
gridSettings : { } ,
ignoreLoading : true ,
ctrl : null ,
dashboardCtrl : this ,
displayGrid : 'onDrag&Resize'
displayGrid : 'onDrag&Resize' ,
layoutData : null ,
layoutDataChanged : new BehaviorSubject ( null ) ,
}
} ,
right : {
show : false ,
layoutCtx : {
id : 'right' ,
breakpoint : 'default' ,
widgets : null ,
widgetLayouts : { } ,
gridSettings : { } ,
ignoreLoading : true ,
ctrl : null ,
dashboardCtrl : this ,
displayGrid : 'onDrag&Resize'
displayGrid : 'onDrag&Resize' ,
layoutData : null ,
layoutDataChanged : new BehaviorSubject ( null ) ,
}
}
} ;
@ -340,6 +349,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
@ViewChild ( 'dashboardWidgetSelect' ) dashboardWidgetSelectComponent : DashboardWidgetSelectComponent ;
private changeMobileSize = new Subject < boolean > ( ) ;
constructor ( protected store : Store < AppState > ,
@Inject ( WINDOW ) private window : Window ,
@Inject ( DOCUMENT ) private document : Document ,
@ -348,7 +359,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
private router : Router ,
private utils : UtilsService ,
private dashboardUtils : DashboardUtilsService ,
private authService : AuthService ,
private entityService : EntityService ,
private dialogService : DialogService ,
private widgetComponentService : WidgetComponentService ,
@ -356,7 +366,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
private itembuffer : ItemBufferService ,
private importExport : ImportExportService ,
private mobileService : MobileService ,
private fb : UntypedFormBuilder ,
private dialog : MatDialog ,
public translate : TranslateService ,
private popoverService : TbPopoverService ,
@ -366,12 +375,11 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
private overlay : Overlay ,
private viewContainerRef : ViewContainerRef ,
private cd : ChangeDetectorRef ,
private sanitizer : DomSanitizer ,
public elRef : ElementRef ,
private injector : Injector ) {
super ( store ) ;
if ( isDefinedAndNotNull ( embeddedValue ) ) {
this . embedded = embeddedValue ;
if ( isDefinedAndNotNull ( this . embeddedValue ) ) {
this . embedded = this . embeddedValue ;
}
}
@ -411,13 +419,42 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
) ) ;
}
this . rxSubscriptions . push ( this . breakpointObserver
. observe ( MediaBreakpoints [ 'gt-sm' ] )
. subscribe ( ( state : BreakpointState ) = > {
this . isMobile = ! state . matches ;
this . rxSubscriptions . push (
this . changeMobileSize . pipe (
distinctUntilChanged ( ) ,
) . subscribe ( ( state ) = > {
this . isMobile = state ;
this . updateLayoutSizes ( ) ;
} )
) ;
this . rxSubscriptions . push (
this . breakpointObserver . observe (
this . dashboardUtils . getBreakpoints ( )
) . pipe (
map ( value = > this . parseBreakpointsResponse ( value . breakpoints ) ) ,
tap ( ( value ) = > {
this . dashboardCtx . breakpoint = value . id ;
this . changeMobileSize . next ( this . isMobileSize ( value ) ) ;
} ) ,
distinctUntilChanged ( ( _ , next ) = > {
if ( this . layouts . right . show || this . isEdit ) {
return true ;
}
let nextBreakpointConfiguration : BreakpointId = 'default' ;
if ( ! ! this . layouts . main . layoutCtx . layoutData ? . [ next . id ] ) {
nextBreakpointConfiguration = next . id ;
}
return this . layouts . main . layoutCtx . breakpoint === nextBreakpointConfiguration ;
} ) ,
skip ( 1 )
) . subscribe ( ( ) = > {
this . layouts . main . layoutCtx . ctrl . updatedCurrentBreakpoint ( ) ;
this . updateLayoutSizes ( ) ;
}
) ) ;
)
) ;
if ( this . isMobileApp && this . syncStateWithQueryParam ) {
this . mobileService . registerToggleLayoutFunction ( ( ) = > {
setTimeout ( ( ) = > {
@ -565,6 +602,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
if ( this . dashboardResize $ ) {
this . dashboardResize $ . disconnect ( ) ;
}
this . layouts . main . layoutCtx . layoutDataChanged . unsubscribe ( ) ;
this . layouts . right . layoutCtx . layoutDataChanged . unsubscribe ( ) ;
}
public runChangeDetection() {
@ -588,7 +627,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
public hideFullscreenButton ( ) : boolean {
return ( this . widgetEditMode || this . iframeMode || this . forceFullscreen || this . singlePageMode || this . isEdit ) ;
return ( this . widgetEditMode || this . iframeMode || this . forceFullscreen || this . singlePageMode ) ;
}
public toolbarAlwaysOpen ( ) : boolean {
@ -703,7 +742,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this . mobileService . onDashboardRightLayoutChanged ( this . isRightLayoutOpened ) ;
}
private updateLayoutSizes() {
public updateLayoutSizes() {
let changeMainLayoutSize = false ;
let changeRightLayoutSize = false ;
if ( this . dashboardCtx . state ) {
@ -718,6 +757,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
private updateMainLayoutSize ( ) : boolean {
const prevMainLayoutWidth = this . mainLayoutSize . width ;
const prevMainLayoutHeight = this . mainLayoutSize . height ;
const prevMainLayoutMaxWidth = this . mainLayoutSize . maxWidth ;
if ( this . isEditingWidget && this . editingLayoutCtx . id === 'main' ) {
this . mainLayoutSize . width = '100%' ;
} else {
@ -728,7 +768,31 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
} else {
this . mainLayoutSize . height = '0px' ;
}
return prevMainLayoutWidth !== this . mainLayoutSize . width || prevMainLayoutHeight !== this . mainLayoutSize . height ;
if ( this . isEdit && ! this . isEditingWidget ) {
const xOffset = this . dashboardContainer . nativeElement . getBoundingClientRect ( ) . x ;
const breakpoint = this . dashboardUtils . getBreakpointInfoById ( this . layouts . main . layoutCtx . breakpoint ) ;
let maxWidth : string ;
if ( breakpoint ? . maxWidth ) {
if ( this . isMobileSize ( breakpoint ) ) {
maxWidth = ` ${ breakpoint . maxWidth } px ` ;
} else {
maxWidth = ` ${ breakpoint . maxWidth - xOffset } px ` ;
}
} else {
maxWidth = '100%' ;
}
const minWidth = breakpoint ? . minWidth ? ` ${ breakpoint . minWidth } px ` : undefined ;
this . mainLayoutSize . maxWidth = maxWidth ;
this . mainLayoutSize . minWidth = minWidth ;
} else {
this . mainLayoutSize . maxWidth = '100%' ;
this . mainLayoutSize . minWidth = undefined ;
}
return prevMainLayoutWidth !== this . mainLayoutSize . width || prevMainLayoutHeight !== this . mainLayoutSize . height ||
prevMainLayoutMaxWidth !== this . mainLayoutSize . maxWidth ;
}
private updateRightLayoutSize ( ) : boolean {
@ -864,6 +928,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
data : {
settings : deepClone ( this . dashboard . configuration . settings ) ,
gridSettings ,
breakpointId : this.layouts.main.layoutCtx.breakpoint
}
} ) . afterClosed ( ) . subscribe ( ( data ) = > {
if ( data ) {
@ -1056,21 +1121,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this . updateLayout ( layout , layoutInfo ) ;
} else {
layout . show = false ;
this . updateLayout ( layout , { widgetIds : [ ] , widgetLayouts : { } , gridSettings : null } ) ;
this . updateLayout ( layout , { default : { widgetIds : [ ] , widgetLayouts : { } , gridSettings : null } } ) ;
}
}
}
private updateLayout ( layout : DashboardPageLayout , layoutInfo : DashboardLayoutInfo ) {
if ( layoutInfo . gridSettings ) {
layout . layoutCtx . gridSettings = layoutInfo . gridSettings ;
}
layout . layoutCtx . widgets . setWidgetIds ( layoutInfo . widgetIds ) ;
layout . layoutCtx . widgetLayouts = layoutInfo . widgetLayouts ;
if ( layout . show && layout . layoutCtx . ctrl ) {
layout . layoutCtx . ctrl . reload ( ) ;
}
layout . layoutCtx . ignoreLoading = true ;
layout . layoutCtx . layoutData = layoutInfo ;
layout . layoutCtx . layoutDataChanged . next ( ) ;
layout . layoutCtx . ctrl ? . updatedCurrentBreakpoint ( this . isEdit ? layout.layoutCtx.breakpoint : null , layout . show ) ;
this . updateLayoutSizes ( ) ;
}
@ -1189,8 +1248,10 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
private addWidgetToLayout ( widget : Widget , layoutId : DashboardLayoutId ) {
this . dashboardUtils . addWidgetToLayout ( this . dashboard , this . dashboardCtx . state , layoutId , widget ) ;
this . layouts [ layoutId ] . layoutCtx . widgets . addWidgetId ( widget . id ) ;
const layoutCtx = this . layouts [ layoutId ] . layoutCtx ;
this . dashboardUtils . addWidgetToLayout ( this . dashboard , this . dashboardCtx . state , layoutId , widget , undefined ,
undefined , - 1 , - 1 , layoutCtx . breakpoint ) ;
layoutCtx . widgets . addWidgetId ( widget . id ) ;
this . runChangeDetection ( ) ;
}
@ -1250,10 +1311,20 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
col : 0
} ;
newWidget = this . dashboardUtils . validateAndUpdateWidget ( newWidget ) ;
let isDefaultBreakpoint = true ;
if ( this . addingLayoutCtx ? . breakpoint ) {
isDefaultBreakpoint = this . addingLayoutCtx . breakpoint === 'default' ;
} else if ( ! this . layouts . right . show ) {
isDefaultBreakpoint = this . layouts . main . layoutCtx . breakpoint === 'default' ;
}
const scada = this . isAddingToScadaLayout ( ) ;
if ( scada ) {
newWidget = this . dashboardUtils . prepareWidgetForScadaLayout ( newWidget ) ;
}
let showLayoutConfig = true ;
if ( scada || this . layouts . right . show || ! this . showLayoutConfigInEdit ( this . layouts . main . layoutCtx ) ) {
showLayoutConfig = false ;
}
if ( widgetTypeInfo . typeParameters . useCustomDatasources ) {
this . addWidgetToDashboard ( newWidget ) ;
} else {
@ -1269,7 +1340,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
stateController : this.dashboardCtx.stateController ,
widget : newWidget ,
widgetInfo : widgetTypeInfo ,
scada
showLayoutConfig ,
isDefaultBreakpoint
}
} ) . afterClosed ( ) . subscribe ( ( addedWidget ) = > {
if ( addedWidget ) {
@ -1348,18 +1420,38 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
}
showLayoutConfigInEdit ( layoutCtx : DashboardPageLayoutContext ) : boolean {
return layoutCtx ? . gridSettings ? . layoutType === LayoutType . divider ||
layoutCtx ? . gridSettings ? . layoutType === LayoutType . default &&
( layoutCtx ? . breakpoint === 'default' ||
layoutCtx ? . breakpoint !== 'default' && layoutCtx ? . gridSettings ? . viewFormat === ViewFormatType . list ) ;
}
replaceReferenceWithWidgetCopy ( $event : Event , layoutCtx : DashboardPageLayoutContext , widget : Widget ) {
$event . stopPropagation ( ) ;
const isRemove = layoutCtx . widgets . removeWidgetId ( widget . id ) ;
const widgetCopy = this . dashboardUtils . replaceReferenceWithWidgetCopy ( widget , this . dashboard , this . dashboardCtx . state ,
layoutCtx . id , layoutCtx . breakpoint , isRemove ) ;
layoutCtx . widgets . addWidgetId ( widgetCopy . id ) ;
this . runChangeDetection ( ) ;
}
copyWidget ( $event : Event , layoutCtx : DashboardPageLayoutContext , widget : Widget ) {
this . itembuffer . copyWidget ( this . dashboard ,
this . dashboardCtx . state , layoutCtx . id , widget ) ;
this . dashboardCtx . state , layoutCtx . id , widget , layoutCtx . breakpoint ) ;
}
copyWidgetReference ( $event : Event , layoutCtx : DashboardPageLayoutContext , widget : Widget ) {
this . itembuffer . copyWidgetReference ( this . dashboard ,
this . dashboardCtx . state , layoutCtx . id , widget ) ;
this . dashboardCtx . state , layoutCtx . id , widget , layoutCtx . breakpoint ) ;
}
pasteWidget ( $event : Event , layoutCtx : DashboardPageLayoutContext , pos : WidgetPosition ) {
this . itembuffer . pasteWidget ( this . dashboard , this . dashboardCtx . state , layoutCtx . id ,
this . itembuffer . pasteWidget ( this . dashboard , this . dashboardCtx . state , layoutCtx . id , layoutCtx . breakpoint ,
pos , this . entityAliasesUpdated . bind ( this ) , this . filtersUpdated . bind ( this ) ) . subscribe (
( widget ) = > {
layoutCtx . widgets . addWidgetId ( widget . id ) ;
@ -1368,7 +1460,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
pasteWidgetReference ( $event : Event , layoutCtx : DashboardPageLayoutContext , pos : WidgetPosition ) {
this . itembuffer . pasteWidgetReference ( this . dashboard , this . dashboardCtx . state , layoutCtx . id ,
this . itembuffer . pasteWidgetReference ( this . dashboard , this . dashboardCtx . state , layoutCtx . id , layoutCtx . breakpoint ,
pos ) . subscribe (
( widget ) = > {
layoutCtx . widgets . addWidgetId ( widget . id ) ;
@ -1390,7 +1482,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
) . subscribe ( ( res ) = > {
if ( res ) {
if ( layoutCtx . widgets . removeWidgetId ( widget . id ) ) {
this . dashboardUtils . removeWidgetFromLayout ( this . dashboard , this . dashboardCtx . state , layoutCtx . id , widget . id ) ;
this . dashboardUtils . removeWidgetFromLayout ( this . dashboard , this . dashboardCtx . state , layoutCtx . id ,
widget . id , layoutCtx . breakpoint ) ;
this . runChangeDetection ( ) ;
}
}
@ -1399,7 +1492,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
exportWidget ( $event : Event , layoutCtx : DashboardPageLayoutContext , widget : Widget , widgetTitle : string ) {
$event . stopPropagation ( ) ;
this . importExport . exportWidget ( this . dashboard , this . dashboardCtx . state , layoutCtx . id , widget , widgetTitle ) ;
this . importExport . exportWidget ( this . dashboard , this . dashboardCtx . state , layoutCtx . id , widget , widgetTitle , layoutCtx . breakpoint ) ;
}
dashboardMouseDown ( $event : Event , layoutCtx : DashboardPageLayoutContext ) {
@ -1455,7 +1548,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
action : ( $event ) = > {
layoutCtx . ctrl . pasteWidgetReference ( $event ) ;
} ,
enabled : this.itembuffer.canPasteWidgetReference ( this . dashboard , this . dashboardCtx . state , layoutCtx . id ) ,
enabled : this.itembuffer.canPasteWidgetReference ( this . dashboard , this . dashboardCtx . state , layoutCtx . id , layoutCtx . breakpoint ) ,
value : 'action.paste-reference' ,
icon : 'content_paste' ,
shortcut : 'M-I'
@ -1475,7 +1568,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
return dashboardContextActions ;
}
prepareWidgetContextMenu ( layoutCtx : DashboardPageLayoutContext , widget : Widget ) : Array < WidgetContextMenuItem > {
prepareWidgetContextMenu ( layoutCtx : DashboardPageLayoutContext , widget : Widget , isReference : boolean ) : Array < WidgetContextMenuItem > {
const widgetContextActions : Array < WidgetContextMenuItem > = [ ] ;
if ( this . isEdit && ! this . isEditingWidget ) {
widgetContextActions . push (
@ -1488,6 +1581,18 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
icon : 'edit'
}
) ;
if ( isReference ) {
widgetContextActions . push (
{
action : ( event , currentWidget ) = > {
this . replaceReferenceWithWidgetCopy ( event , layoutCtx , currentWidget ) ;
} ,
enabled : true ,
value : 'widget.replace-reference-with-widget-copy' ,
icon : 'mdi:file-replace-outline'
}
) ;
}
if ( ! this . widgetEditMode ) {
widgetContextActions . push (
{
@ -1646,4 +1751,31 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
} ) ;
}
}
get showMainLayoutFiller ( ) : boolean {
const layoutMaxWidth = this . dashboardUtils . getBreakpointInfoById ( this . layouts . main . layoutCtx . breakpoint ) ? . maxWidth || Infinity ;
const dashboardMaxWidth = this . dashboardUtils . getBreakpointInfoById ( this . dashboardCtx . breakpoint ) ? . maxWidth || Infinity ;
return ! this . layouts . right . show && layoutMaxWidth < dashboardMaxWidth && ! this . isEditingWidget ;
}
get currentBreakpointValue ( ) : string {
return this . dashboardUtils . getBreakpointSizeDescription ( this . layouts . main . layoutCtx . breakpoint ) ;
}
private parseBreakpointsResponse ( breakpoints : { [ key : string ] : boolean } ) : BreakpointInfo {
const activeBreakpoints : BreakpointInfo [ ] = [ ] ;
Object . keys ( breakpoints ) . map ( ( key ) = > {
if ( breakpoints [ key ] ) {
activeBreakpoints . push ( this . dashboardUtils . getBreakpointInfoByValue ( key ) ) ;
}
} ) ;
return activeBreakpoints . pop ( ) ;
}
private isMobileSize ( breakpoint : BreakpointInfo ) : boolean {
if ( breakpoint ? . maxWidth ) {
return breakpoint . maxWidth < 960 ;
}
return false ;
}
}