@ -51,7 +51,6 @@ import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA , MatDialog , MatDialogRef } from '@angular/material/dialog' ;
import { MAT_DIALOG_DATA , MatDialog , MatDialogRef } from '@angular/material/dialog' ;
import { MatExpansionPanel } from '@angular/material/expansion' ;
import { MatExpansionPanel } from '@angular/material/expansion' ;
import { DialogService } from '@core/services/dialog.service' ;
import { DialogService } from '@core/services/dialog.service' ;
import { AuthService } from '@core/auth/auth.service' ;
import { ActivatedRoute , Router } from '@angular/router' ;
import { ActivatedRoute , Router } from '@angular/router' ;
import {
import {
inputNodeComponent ,
inputNodeComponent ,
@ -63,9 +62,12 @@ import {
} from '@shared/models/rule-chain.models' ;
} from '@shared/models/rule-chain.models' ;
import { FcItemInfo , FlowchartConstants , NgxFlowchartComponent , UserCallbacks } from 'ngx-flowchart' ;
import { FcItemInfo , FlowchartConstants , NgxFlowchartComponent , UserCallbacks } from 'ngx-flowchart' ;
import {
import {
FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE ,
FC_RULE_NOTE_DEFAULT_BACKGROUND_COLOR ,
FcRuleEdge ,
FcRuleEdge ,
FcRuleNode ,
FcRuleNode ,
FcRuleNodeType ,
FcRuleNodeType ,
FcRuleNote ,
getRuleNodeHelpLink ,
getRuleNodeHelpLink ,
LinkLabel ,
LinkLabel ,
outputNodeClazz ,
outputNodeClazz ,
@ -81,14 +83,15 @@ import { RuleChainService } from '@core/http/rule-chain.service';
import { NEVER , Observable , of , ReplaySubject , skip , startWith , Subject , throwError } from 'rxjs' ;
import { NEVER , Observable , of , ReplaySubject , skip , startWith , Subject , throwError } from 'rxjs' ;
import { catchError , debounceTime , distinctUntilChanged , mergeMap , takeUntil , tap } from 'rxjs/operators' ;
import { catchError , debounceTime , distinctUntilChanged , mergeMap , takeUntil , tap } from 'rxjs/operators' ;
import { ISearchableComponent } from '../../models/searchable-component.models' ;
import { ISearchableComponent } from '../../models/searchable-component.models' ;
import { deepClone , isDefinedAndNotNull } from '@core/utils' ;
import { deepClone , guid , isDefinedAndNotNull } from '@core/utils' ;
import { RuleNodeDetailsComponent } from '@home/pages/rulechain/rule-node-details.component' ;
import { RuleNodeDetailsComponent } from '@home/pages/rulechain/rule-node-details.component' ;
import { RuleNodeLinkComponent } from './rule-node-link.component' ;
import { RuleNodeLinkComponent } from './rule-node-link.component' ;
import { RuleNoteEditorComponent } from './rule-note-editor.component' ;
import { DialogComponent } from '@shared/components/dialog.component' ;
import { DialogComponent } from '@shared/components/dialog.component' ;
import { MatMenuTrigger } from '@angular/material/menu' ;
import { MatMenuTrigger } from '@angular/material/menu' ;
import { ItemBufferService , RuleNodeConnection } from '@core/services/item-buffer.service' ;
import { ItemBufferService , RuleNodeConnection } from '@core/services/item-buffer.service' ;
import { Hotkey } from 'angular2-hotkeys' ;
import { Hotkey } from 'angular2-hotkeys' ;
import { DebugEventType , DebugRuleNodeEventBody , EventType } from '@shared/models/event.models' ;
import { DebugEventType , DebugRuleNodeEventBody } from '@shared/models/event.models' ;
import { MatMiniFabButton } from '@angular/material/button' ;
import { MatMiniFabButton } from '@angular/material/button' ;
import { TbPopoverService } from '@shared/components/popover.service' ;
import { TbPopoverService } from '@shared/components/popover.service' ;
import { VersionControlComponent } from '@home/components/vc/version-control.component' ;
import { VersionControlComponent } from '@home/components/vc/version-control.component' ;
@ -97,10 +100,10 @@ import { MatDrawer } from '@angular/material/sidenav';
import { HttpStatusCode } from '@angular/common/http' ;
import { HttpStatusCode } from '@angular/common/http' ;
import { TbContextMenuEvent } from '@shared/models/jquery-event.models' ;
import { TbContextMenuEvent } from '@shared/models/jquery-event.models' ;
import { EntityDebugSettings } from '@shared/models/entity.models' ;
import { EntityDebugSettings } from '@shared/models/entity.models' ;
import Timeout = NodeJS . Timeout ;
import { DomSanitizer } from '@angular/platform-browser' ;
import { DomSanitizer } from '@angular/platform-browser' ;
import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model' ;
import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model' ;
import { EventsDialogComponent } from '@home/dialogs/events-dialog.component' ;
import { EventsDialogComponent } from '@home/dialogs/events-dialog.component' ;
import Timeout = NodeJS . Timeout ;
@Component ( {
@Component ( {
selector : 'tb-rulechain-page' ,
selector : 'tb-rulechain-page' ,
@ -132,8 +135,6 @@ export class RuleChainPageComponent extends PageComponent
@ViewChild ( 'drawer' ) drawer : MatDrawer ;
@ViewChild ( 'drawer' ) drawer : MatDrawer ;
eventTypes = EventType ;
debugEventTypes = DebugEventType ;
debugEventTypes = DebugEventType ;
ruleChainMenuPosition = { x : '0px' , y : '0px' } ;
ruleChainMenuPosition = { x : '0px' , y : '0px' } ;
@ -163,11 +164,16 @@ export class RuleChainPageComponent extends PageComponent
@ViewChild ( 'tbRuleNode' ) ruleNodeComponent : RuleNodeDetailsComponent ;
@ViewChild ( 'tbRuleNode' ) ruleNodeComponent : RuleNodeDetailsComponent ;
@ViewChild ( 'tbRuleNodeLink' ) ruleNodeLinkComponent : RuleNodeLinkComponent ;
@ViewChild ( 'tbRuleNodeLink' ) ruleNodeLinkComponent : RuleNodeLinkComponent ;
@ViewChild ( 'tbRuleNote' ) ruleNoteComponent : RuleNoteEditorComponent ;
editingRuleNodeLink : FcRuleEdge = null ;
editingRuleNodeLink : FcRuleEdge = null ;
isEditingRuleNodeLink = false ;
isEditingRuleNodeLink = false ;
editingRuleNodeLinkIndex = - 1 ;
editingRuleNodeLinkIndex = - 1 ;
editingNote : FcRuleNote = null ;
isEditingNote = false ;
editingNoteIndex = - 1 ;
hotKeys : Hotkey [ ] = [ ] ;
hotKeys : Hotkey [ ] = [ ] ;
enableHotKeys = true ;
enableHotKeys = true ;
@ -180,22 +186,23 @@ export class RuleChainPageComponent extends PageComponent
ruleChainModel : FcRuleNodeModel = {
ruleChainModel : FcRuleNodeModel = {
nodes : [ ] ,
nodes : [ ] ,
edges : [ ]
edges : [ ] ,
notes : [ ]
} ;
} ;
selectedObjects = [ ] ;
selectedObjects = [ ] ;
editCallbacks : UserCallbacks = {
editCallbacks : UserCallbacks = {
edgeDoubleClick : ( event , edge ) = > {
edgeDoubleClick : ( _ event, edge ) = > {
this . openLinkDetails ( edge ) ;
this . openLinkDetails ( edge ) ;
} ,
} ,
edgeEdit : ( event , edge ) = > {
edgeEdit : ( _ event, edge ) = > {
this . openLinkDetails ( edge ) ;
this . openLinkDetails ( edge ) ;
} ,
} ,
nodeCallbacks : {
nodeCallbacks : {
doubleClick : ( event , node : FcRuleNode ) = > {
doubleClick : ( _ event, node : FcRuleNode ) = > {
this . openNodeDetails ( node ) ;
this . openNodeDetails ( node ) ;
} ,
} ,
nodeEdit : ( event , node : FcRuleNode ) = > {
nodeEdit : ( _ event, node : FcRuleNode ) = > {
this . openNodeDetails ( node ) ;
this . openNodeDetails ( node ) ;
} ,
} ,
mouseEnter : this.displayNodeDescriptionTooltip.bind ( this ) ,
mouseEnter : this.displayNodeDescriptionTooltip.bind ( this ) ,
@ -204,7 +211,7 @@ export class RuleChainPageComponent extends PageComponent
} ,
} ,
isValidEdge : ( source , destination ) = >
isValidEdge : ( source , destination ) = >
source . type === FlowchartConstants . rightConnectorType && destination . type === FlowchartConstants . leftConnectorType ,
source . type === FlowchartConstants . rightConnectorType && destination . type === FlowchartConstants . leftConnectorType ,
createEdge : ( event , edge : FcRuleEdge ) = > {
createEdge : ( _ event, edge : FcRuleEdge ) = > {
const sourceNode = this . ruleChainCanvas . modelService . nodes . getNodeByConnectorId ( edge . source ) as FcRuleNode ;
const sourceNode = this . ruleChainCanvas . modelService . nodes . getNodeByConnectorId ( edge . source ) as FcRuleNode ;
if ( sourceNode . component . type === RuleNodeType . INPUT ) {
if ( sourceNode . component . type === RuleNodeType . INPUT ) {
const found = this . ruleChainModel . edges . find ( theEdge = > theEdge . source === ( this . inputConnectorId + '' ) ) ;
const found = this . ruleChainModel . edges . find ( theEdge = > theEdge . source === ( this . inputConnectorId + '' ) ) ;
@ -238,8 +245,19 @@ export class RuleChainPageComponent extends PageComponent
}
}
}
}
} ,
} ,
dropNode : ( event , node : FcRuleNode ) = > {
dropNode : ( _ event, node : FcRuleNode ) = > {
this . addRuleNode ( node ) ;
this . addRuleNode ( node ) ;
} ,
noteCallbacks : {
noteEdit : ( _event , note ) = > {
this . openNoteDetails ( note ) ;
} ,
doubleClick : ( _event , note ) = > {
this . openNoteDetails ( note ) ;
}
} ,
noteRemoved : ( _note ) = > {
this . isDirty = true ;
}
}
} ;
} ;
@ -267,11 +285,9 @@ export class RuleChainPageComponent extends PageComponent
private tooltipTimeout : Timeout ;
private tooltipTimeout : Timeout ;
constructor ( protected store : Store < AppState > ,
constructor ( private route : ActivatedRoute ,
private route : ActivatedRoute ,
private router : Router ,
private router : Router ,
private ruleChainService : RuleChainService ,
private ruleChainService : RuleChainService ,
private authService : AuthService ,
private translate : TranslateService ,
private translate : TranslateService ,
private itembuffer : ItemBufferService ,
private itembuffer : ItemBufferService ,
private popoverService : TbPopoverService ,
private popoverService : TbPopoverService ,
@ -282,7 +298,7 @@ export class RuleChainPageComponent extends PageComponent
public dialog : MatDialog ,
public dialog : MatDialog ,
public dialogService : DialogService ,
public dialogService : DialogService ,
public fb : FormBuilder ) {
public fb : FormBuilder ) {
super ( store ) ;
super ( ) ;
this . route . data . pipe (
this . route . data . pipe (
takeUntil ( this . destroy $ )
takeUntil ( this . destroy $ )
) . subscribe (
) . subscribe (
@ -363,12 +379,15 @@ export class RuleChainPageComponent extends PageComponent
this . selectedObjects = [ ] ;
this . selectedObjects = [ ] ;
this . ruleChainModel . nodes = [ ] ;
this . ruleChainModel . nodes = [ ] ;
this . ruleChainModel . edges = [ ] ;
this . ruleChainModel . edges = [ ] ;
this . ruleChainModel . notes = [ ] ;
this . ruleNodeTypesModel = { } ;
this . ruleNodeTypesModel = { } ;
if ( this . ruleChainCanvas ) {
if ( this . ruleChainCanvas ) {
this . ruleChainCanvas . adjustCanvasSize ( true ) ;
this . ruleChainCanvas . adjustCanvasSize ( true ) ;
}
}
this . isEditingRuleNode = false ;
this . isEditingRuleNode = false ;
this . isEditingRuleNodeLink = false ;
this . isEditingRuleNodeLink = false ;
this . isEditingNote = false ;
this . editingNote = null ;
this . updateRuleNodesHighlight ( ) ;
this . updateRuleNodesHighlight ( ) ;
}
}
@ -389,7 +408,7 @@ export class RuleChainPageComponent extends PageComponent
new Hotkey ( [ 'ctrl+c' , 'meta+c' ] , ( event : KeyboardEvent ) = > {
new Hotkey ( [ 'ctrl+c' , 'meta+c' ] , ( event : KeyboardEvent ) = > {
if ( this . enableHotKeys ) {
if ( this . enableHotKeys ) {
event . preventDefault ( ) ;
event . preventDefault ( ) ;
this . copyRuleNode s ( ) ;
this . copyRuleChainObject s ( ) ;
return false ;
return false ;
}
}
return true ;
return true ;
@ -400,8 +419,8 @@ export class RuleChainPageComponent extends PageComponent
new Hotkey ( [ 'ctrl+v' , 'meta+v' ] , ( event : KeyboardEvent ) = > {
new Hotkey ( [ 'ctrl+v' , 'meta+v' ] , ( event : KeyboardEvent ) = > {
if ( this . enableHotKeys ) {
if ( this . enableHotKeys ) {
event . preventDefault ( ) ;
event . preventDefault ( ) ;
if ( this . itembuffer . hasRuleNode s ( ) ) {
if ( this . itembuffer . hasRuleChainObject s ( ) ) {
this . pasteRuleNode s ( ) ;
this . pasteRuleChainObject s ( ) ;
}
}
return false ;
return false ;
}
}
@ -465,6 +484,17 @@ export class RuleChainPageComponent extends PageComponent
} , [ 'INPUT' , 'SELECT' , 'TEXTAREA' ] ,
} , [ 'INPUT' , 'SELECT' , 'TEXTAREA' ] ,
this . translate . instant ( 'rulenode.create-nested-rulechain' ) )
this . translate . instant ( 'rulenode.create-nested-rulechain' ) )
) ;
) ;
this . hotKeys . push (
new Hotkey ( 'alt+n' , ( event : KeyboardEvent ) = > {
if ( this . enableHotKeys ) {
event . preventDefault ( ) ;
this . addNote ( ) ;
return false ;
}
return true ;
} , [ 'INPUT' , 'SELECT' , 'TEXTAREA' ] ,
this . translate . instant ( 'rulechain.add-note' ) )
) ;
}
}
}
}
@ -523,7 +553,7 @@ export class RuleChainPageComponent extends PageComponent
} ) ;
} ) ;
if ( this . expansionPanels ) {
if ( this . expansionPanels ) {
for ( let i = 0 ; i < ruleNodeTypesLibrary . length ; i ++ ) {
for ( let i = 0 ; i < ruleNodeTypesLibrary . length ; i ++ ) {
const panel = this . expansionPanels . find ( ( item , index ) = > index === i ) ;
const panel = this . expansionPanels . find ( ( _ item, index ) = > index === i ) ;
if ( panel ) {
if ( panel ) {
const type = ruleNodeTypesLibrary [ i ] ;
const type = ruleNodeTypesLibrary [ i ] ;
if ( ! this . ruleNodeTypesModel [ type ] . model . nodes . length ) {
if ( ! this . ruleNodeTypesModel [ type ] . model . nodes . length ) {
@ -657,6 +687,7 @@ export class RuleChainPageComponent extends PageComponent
}
}
} ) ;
} ) ;
}
}
this . ruleChainModel . notes = deepClone ( this . ruleChainMetaData . notes ) || [ ] ;
if ( this . ruleChainCanvas ) {
if ( this . ruleChainCanvas ) {
this . ruleChainCanvas . adjustCanvasSize ( true ) ;
this . ruleChainCanvas . adjustCanvasSize ( true ) ;
}
}
@ -688,10 +719,12 @@ export class RuleChainPageComponent extends PageComponent
}
}
private prepareContextMenu ( item : FcItemInfo ) : RuleChainMenuContextInfo {
private prepareContextMenu ( item : FcItemInfo ) : RuleChainMenuContextInfo {
if ( this . objectsSelected ( ) || ( ! item . node && ! item . edge ) ) {
if ( this . objectsSelected ( ) || ( ! item . node && ! item . edge && ! item . note ) ) {
return this . prepareRuleChainContextMenu ( ) ;
return this . prepareRuleChainContextMenu ( ) ;
} else if ( item . node ) {
} else if ( item . node ) {
return this . prepareRuleNodeContextMenu ( item . node ) ;
return this . prepareRuleNodeContextMenu ( item . node ) ;
} else if ( item . note ) {
return this . prepareNoteContextMenu ( item . note ) ;
} else if ( item . edge ) {
} else if ( item . edge ) {
return this . prepareEdgeContextMenu ( item . edge ) ;
return this . prepareEdgeContextMenu ( item . edge ) ;
}
}
@ -705,11 +738,12 @@ export class RuleChainPageComponent extends PageComponent
subtitle : this.translate.instant ( 'rulechain.rulechain' ) ,
subtitle : this.translate.instant ( 'rulechain.rulechain' ) ,
menuItems : [ ]
menuItems : [ ]
} ;
} ;
if ( this . ruleChainCanvas . modelService . nodes . getSelectedNodes ( ) . length ) {
if ( this . ruleChainCanvas . modelService . nodes . getSelectedNodes ( ) . length ||
this . ruleChainCanvas . modelService . notes . getSelectedNotes ( ) . length ) {
contextInfo . menuItems . push (
contextInfo . menuItems . push (
{
{
action : ( ) = > {
action : ( ) = > {
this . copyRuleNode s ( ) ;
this . copyRuleChainObject s ( ) ;
} ,
} ,
enabled : true ,
enabled : true ,
value : 'rulenode.copy-selected' ,
value : 'rulenode.copy-selected' ,
@ -721,14 +755,25 @@ export class RuleChainPageComponent extends PageComponent
contextInfo . menuItems . push (
contextInfo . menuItems . push (
{
{
action : ( $event ) = > {
action : ( $event ) = > {
this . pasteRuleNode s ( $event ) ;
this . pasteRuleChainObject s ( $event ) ;
} ,
} ,
enabled : this.itembuffer.hasRuleNode s ( ) ,
enabled : this.itembuffer.hasRuleChainObject s ( ) ,
value : 'action.paste' ,
value : 'action.paste' ,
icon : 'content_paste' ,
icon : 'content_paste' ,
shortcut : 'M-V'
shortcut : 'M-V'
}
}
) ;
) ;
contextInfo . menuItems . push (
{
action : ( $event ) = > {
this . addNote ( $event ) ;
} ,
enabled : true ,
value : 'rulechain.add-note' ,
icon : 'sticky_note_2' ,
shortcut : 'A-N'
}
) ;
contextInfo . menuItems . push (
contextInfo . menuItems . push (
{
{
divider : true
divider : true
@ -907,7 +952,7 @@ export class RuleChainPageComponent extends PageComponent
toIndexSet . add ( toIndex ) ;
toIndexSet . add ( toIndex ) ;
}
}
} ) ;
} ) ;
const noInputNodes = selectedNodes . filter ( ( node , index ) = > ! toIndexSet . has ( index ) ) ;
const noInputNodes = selectedNodes . filter ( ( _ node, index ) = > ! toIndexSet . has ( index ) ) ;
return noInputNodes . filter ( ( node : FcRuleNode ) = > node . component . configurationDescriptor . nodeDefinition . inEnabled ) . length <= 1 ;
return noInputNodes . filter ( ( node : FcRuleNode ) = > node . component . configurationDescriptor . nodeDefinition . inEnabled ) . length <= 1 ;
}
}
return false ;
return false ;
@ -925,11 +970,13 @@ export class RuleChainPageComponent extends PageComponent
}
}
} ) . afterClosed ( ) . subscribe ( ( ruleChain ) = > {
} ) . afterClosed ( ) . subscribe ( ( ruleChain ) = > {
if ( ruleChain ) {
if ( ruleChain ) {
const selectedNotes : FcRuleNote [ ] = this . ruleChainCanvas . modelService . notes . getSelectedNotes ( ) ;
this . ruleChainCanvas . modelService . deselectAll ( ) ;
this . ruleChainCanvas . modelService . deselectAll ( ) ;
const ruleChainMetaData : RuleChainMetaData = {
const ruleChainMetaData : RuleChainMetaData = {
ruleChainId : ruleChain.id ,
ruleChainId : ruleChain.id ,
nodes : [ ] ,
nodes : [ ] ,
connections : [ ]
connections : [ ] ,
notes : deepClone ( selectedNotes )
} ;
} ;
let outputEdges : FcRuleEdge [ ] = [ ] ;
let outputEdges : FcRuleEdge [ ] = [ ] ;
let minX : number = null ;
let minX : number = null ;
@ -993,7 +1040,7 @@ export class RuleChainPageComponent extends PageComponent
}
}
}
}
} ) ;
} ) ;
const noInputNodes = selectedNodes . filter ( ( node , index ) = > ! toIndexSet . has ( index ) ) ;
const noInputNodes = selectedNodes . filter ( ( _ node, index ) = > ! toIndexSet . has ( index ) ) ;
const possibleInputNodes = noInputNodes . filter ( ( node : FcRuleNode ) = >
const possibleInputNodes = noInputNodes . filter ( ( node : FcRuleNode ) = >
node . component . configurationDescriptor . nodeDefinition . inEnabled ) ;
node . component . configurationDescriptor . nodeDefinition . inEnabled ) ;
let inputEdges : FcRuleEdge [ ] = [ ] ;
let inputEdges : FcRuleEdge [ ] = [ ] ;
@ -1039,6 +1086,10 @@ export class RuleChainPageComponent extends PageComponent
node . additionalInfo . layoutX -= deltaX ;
node . additionalInfo . layoutX -= deltaX ;
node . additionalInfo . layoutY -= deltaY ;
node . additionalInfo . layoutY -= deltaY ;
} ) ;
} ) ;
ruleChainMetaData . notes ? . forEach ( ( note ) = > {
note . x -= deltaX ;
note . y -= deltaY ;
} ) ;
this . ruleChainService . saveRuleChainMetadata ( ruleChainMetaData ) . subscribe ( ( ) = > {
this . ruleChainService . saveRuleChainMetadata ( ruleChainMetaData ) . subscribe ( ( ) = > {
const component = this . ruleChainService . getRuleNodeComponentByClazz ( this . ruleChainType , ruleChainNodeClazz ) ;
const component = this . ruleChainService . getRuleNodeComponentByClazz ( this . ruleChainType , ruleChainNodeClazz ) ;
const descriptor = ruleNodeTypeDescriptors . get ( component . type ) ;
const descriptor = ruleNodeTypeDescriptors . get ( component . type ) ;
@ -1090,6 +1141,9 @@ export class RuleChainPageComponent extends PageComponent
selectedNodes . forEach ( ( node ) = > {
selectedNodes . forEach ( ( node ) = > {
this . ruleChainCanvas . modelService . nodes . delete ( node ) ;
this . ruleChainCanvas . modelService . nodes . delete ( node ) ;
} ) ;
} ) ;
selectedNotes . forEach ( ( note ) = > {
this . ruleChainCanvas . modelService . notes . delete ( note ) ;
} ) ;
this . onModelChanged ( ) ;
this . onModelChanged ( ) ;
this . updateRuleNodesHighlight ( ) ;
this . updateRuleNodesHighlight ( ) ;
} ) ;
} ) ;
@ -1146,12 +1200,15 @@ export class RuleChainPageComponent extends PageComponent
}
}
private copyNode ( node : FcRuleNode ) {
private copyNode ( node : FcRuleNode ) {
this . itembuffer . copyRuleNode s ( [ node ] , [ ] ) ;
this . itembuffer . copyRuleChainObject s ( [ node ] , [ ] , [ ] ) ;
}
}
private copyRuleNode s() {
private copyRuleChainObject s() {
const nodes : FcRuleNode [ ] = this . ruleChainCanvas . modelService . nodes . getSelectedNodes ( ) ;
const nodes : FcRuleNode [ ] = this . ruleChainCanvas . modelService . nodes . getSelectedNodes ( ) ;
const edges : FcRuleEdge [ ] = this . ruleChainCanvas . modelService . edges . getSelectedEdges ( ) ;
const edges : FcRuleEdge [ ] = this . ruleChainCanvas . modelService . edges . getSelectedEdges ( ) ;
const notes : FcRuleNote [ ] = ( this . ruleChainModel . notes || [ ] ) . filter (
note = > this . ruleChainCanvas . modelService . notes . isSelected ( note )
) ;
const connections : RuleNodeConnection [ ] = [ ] ;
const connections : RuleNodeConnection [ ] = [ ] ;
edges . forEach ( ( edge ) = > {
edges . forEach ( ( edge ) = > {
const sourceNode = this . ruleChainCanvas . modelService . nodes . getNodeByConnectorId ( edge . source ) ;
const sourceNode = this . ruleChainCanvas . modelService . nodes . getNodeByConnectorId ( edge . source ) ;
@ -1170,10 +1227,10 @@ export class RuleChainPageComponent extends PageComponent
connections . push ( connection ) ;
connections . push ( connection ) ;
}
}
} ) ;
} ) ;
this . itembuffer . copyRuleNode s ( nodes , connections ) ;
this . itembuffer . copyRuleChainObject s ( nodes , connections , note s ) ;
}
}
private pasteRuleNode s ( event? : MouseEvent ) {
private pasteRuleChainObject s ( event? : MouseEvent ) {
const canvas = $ ( this . ruleChainCanvas . modelService . canvasHtmlElement ) ;
const canvas = $ ( this . ruleChainCanvas . modelService . canvasHtmlElement ) ;
let x : number ;
let x : number ;
let y : number ;
let y : number ;
@ -1188,11 +1245,11 @@ export class RuleChainPageComponent extends PageComponent
x = scrollLeft + scrollParent . width ( ) / 2 ;
x = scrollLeft + scrollParent . width ( ) / 2 ;
y = scrollTop + scrollParent . height ( ) / 2 ;
y = scrollTop + scrollParent . height ( ) / 2 ;
}
}
const ruleNode s = this . itembuffer . pasteRuleNode s ( x , y ) ;
const ruleChainObject s = this . itembuffer . pasteRuleChainObject s ( x , y ) ;
if ( ruleNode s ) {
if ( ruleChainObject s ) {
this . ruleChainCanvas . modelService . deselectAll ( ) ;
this . ruleChainCanvas . modelService . deselectAll ( ) ;
const nodes : FcRuleNode [ ] = [ ] ;
const nodes : FcRuleNode [ ] = [ ] ;
ruleNode s . nodes . forEach ( ( node ) = > {
ruleChainObject s . nodes . forEach ( ( node ) = > {
node . id = 'rule-chain-node-' + this . nextNodeID ++ ;
node . id = 'rule-chain-node-' + this . nextNodeID ++ ;
const component = node . component ;
const component = node . component ;
if ( component . configurationDescriptor . nodeDefinition . inEnabled ) {
if ( component . configurationDescriptor . nodeDefinition . inEnabled ) {
@ -1215,7 +1272,7 @@ export class RuleChainPageComponent extends PageComponent
this . ruleChainModel . nodes . push ( node ) ;
this . ruleChainModel . nodes . push ( node ) ;
this . ruleChainCanvas . modelService . nodes . select ( node ) ;
this . ruleChainCanvas . modelService . nodes . select ( node ) ;
} ) ;
} ) ;
ruleNode s . connections . forEach ( ( connection ) = > {
ruleChainObject s . connections . forEach ( ( connection ) = > {
const sourceNode = nodes [ connection . fromIndex ] ;
const sourceNode = nodes [ connection . fromIndex ] ;
const destNode = nodes [ connection . toIndex ] ;
const destNode = nodes [ connection . toIndex ] ;
if ( ( connection . isInputSource || sourceNode ) && destNode ) {
if ( ( connection . isInputSource || sourceNode ) && destNode ) {
@ -1251,15 +1308,142 @@ export class RuleChainPageComponent extends PageComponent
}
}
}
}
} ) ;
} ) ;
if ( ruleChainObjects . notes ? . length ) {
if ( ! this . ruleChainModel . notes ) {
this . ruleChainModel . notes = [ ] ;
}
ruleChainObjects . notes . forEach ( ( note ) = > {
note . id = guid ( ) ;
this . ruleChainModel . notes . push ( note ) ;
this . ruleChainCanvas . modelService . notes . select ( note ) ;
} ) ;
}
this . updateRuleNodesHighlight ( ) ;
this . updateRuleNodesHighlight ( ) ;
this . validate ( ) ;
this . validate ( ) ;
this . onModelChanged ( ) ;
this . onModelChanged ( ) ;
}
}
}
}
addNote ( event? : MouseEvent ) : void {
this . dialog . open < AddNoteDialogComponent , FcRuleNote , Partial < FcRuleNote > > ( AddNoteDialogComponent , {
disableClose : true ,
panelClass : [ 'tb-dialog' , 'tb-fullscreen-dialog' ] ,
data : {
id : '' , x : 0 , y : 0 , width : 200 , height : 120 ,
content : '' ,
backgroundColor : FC_RULE_NOTE_DEFAULT_BACKGROUND_COLOR ,
applyDefaultMarkdownStyle : FC_RULE_NOTE_DEFAULT_APPLY_MARKDOWN_STYLE ,
markdownCss : ''
}
} ) . afterClosed ( ) . subscribe ( ( noteData ) = > {
if ( noteData !== undefined ) {
const canvas = $ ( this . ruleChainCanvas . modelService . canvasHtmlElement ) ;
let x : number ;
let y : number ;
if ( event ) {
const offset = canvas . offset ( ) ;
x = Math . round ( event . clientX - offset . left ) ;
y = Math . round ( event . clientY - offset . top ) ;
} else {
const scrollParent = canvas . parent ( ) ;
x = scrollParent . scrollLeft ( ) + scrollParent . width ( ) / 2 ;
y = scrollParent . scrollTop ( ) + scrollParent . height ( ) / 2 ;
}
const note : FcRuleNote = {
id : guid ( ) ,
x ,
y ,
width : 200 ,
height : 120 ,
. . . noteData
} ;
if ( ! this . ruleChainModel . notes ) {
this . ruleChainModel . notes = [ ] ;
}
this . ruleChainModel . notes . push ( note ) ;
this . isDirty = true ;
}
} ) ;
}
private prepareNoteContextMenu ( note : FcRuleNote ) : RuleChainMenuContextInfo {
const contextInfo : RuleChainMenuContextInfo = {
headerClass : 'tb-rulechain-header' ,
icon : 'sticky_note_2' ,
title : this.translate.instant ( 'rulechain.note' ) ,
menuItems : [ ]
} ;
if ( ! note . readonly ) {
contextInfo . menuItems . push (
{
action : ( ) = > {
this . openNoteDetails ( note ) ;
} ,
enabled : true ,
value : 'action.edit' ,
icon : 'edit'
}
) ;
contextInfo . menuItems . push (
{
action : ( ) = > {
this . itembuffer . copyRuleChainObjects ( [ ] , [ ] , [ note ] ) ;
} ,
enabled : true ,
value : 'action.copy' ,
icon : 'content_copy'
}
) ;
contextInfo . menuItems . push (
{
action : ( ) = > {
this . ruleChainCanvas . modelService . notes . delete ( note ) ;
} ,
enabled : true ,
value : 'action.delete' ,
icon : 'clear' ,
shortcut : 'Del'
}
) ;
}
return contextInfo ;
}
private openNoteDetails ( note : FcRuleNote ) : void {
this . enableHotKeys = false ;
this . updateErrorTooltips ( true ) ;
this . isEditingRuleNode = false ;
this . editingRuleNode = null ;
this . isEditingRuleNodeLink = false ;
this . editingRuleNodeLink = null ;
this . isEditingNote = true ;
this . editingNoteIndex = this . ruleChainModel . notes . indexOf ( note ) ;
this . editingNote = deepClone ( note ) ;
}
saveNote ( ) : void {
this . ruleNoteComponent . noteForm . markAsPristine ( ) ;
Object . assign ( this . editingNote , this . ruleNoteComponent . noteForm . value ) ;
this . ruleChainModel . notes [ this . editingNoteIndex ] = this . editingNote ;
this . editingNote = deepClone ( this . editingNote ) ;
this . onModelChanged ( ) ;
}
onRevertNoteEdit ( ) : void {
this . ruleNoteComponent . noteForm . markAsPristine ( ) ;
const note = this . ruleChainModel . notes [ this . editingNoteIndex ] ;
this . editingNote = deepClone ( note ) ;
}
onEditNoteClosed ( ) : void {
this . editingNote = null ;
this . isEditingNote = false ;
}
onDetailsDrawerClosed() {
onDetailsDrawerClosed() {
this . onEditRuleNodeClosed ( ) ;
this . onEditRuleNodeClosed ( ) ;
this . onEditRuleNodeLinkClosed ( ) ;
this . onEditRuleNodeLinkClosed ( ) ;
this . onEditNoteClosed ( ) ;
this . enableHotKeys = true ;
this . enableHotKeys = true ;
this . updateErrorTooltips ( false ) ;
this . updateErrorTooltips ( false ) ;
}
}
@ -1419,7 +1603,8 @@ export class RuleChainPageComponent extends PageComponent
objectsSelected ( ) : boolean {
objectsSelected ( ) : boolean {
return this . ruleChainCanvas . modelService . nodes . getSelectedNodes ( ) . length > 0 ||
return this . ruleChainCanvas . modelService . nodes . getSelectedNodes ( ) . length > 0 ||
this . ruleChainCanvas . modelService . edges . getSelectedEdges ( ) . length > 0 ;
this . ruleChainCanvas . modelService . edges . getSelectedEdges ( ) . length > 0 ||
this . ruleChainCanvas . modelService . notes . getSelectedNotes ( ) . length > 0 ;
}
}
deleteSelected() {
deleteSelected() {
@ -1477,6 +1662,7 @@ export class RuleChainPageComponent extends PageComponent
ruleChainId : this.ruleChain.id ,
ruleChainId : this.ruleChain.id ,
nodes : [ ] ,
nodes : [ ] ,
connections : [ ] ,
connections : [ ] ,
notes : deepClone ( this . ruleChainModel . notes ) || [ ] ,
version : ruleChain.version
version : ruleChain.version
} ;
} ;
const nodes : FcRuleNode [ ] = [ ] ;
const nodes : FcRuleNode [ ] = [ ] ;
@ -1535,6 +1721,9 @@ export class RuleChainPageComponent extends PageComponent
. subscribe ( ( savedRuleChainMetaData ) = > {
. subscribe ( ( savedRuleChainMetaData ) = > {
this . ruleChain . version = savedRuleChainMetaData . version ;
this . ruleChain . version = savedRuleChainMetaData . version ;
this . ruleChainMetaData = savedRuleChainMetaData ;
this . ruleChainMetaData = savedRuleChainMetaData ;
if ( ! this . ruleChainMetaData . notes ) {
this . ruleChainMetaData . notes = deepClone ( this . ruleChainModel . notes ) || [ ] ;
}
if ( this . isImport ) {
if ( this . isImport ) {
this . isDirtyValue = false ;
this . isDirtyValue = false ;
this . isImport = false ;
this . isImport = false ;
@ -1796,7 +1985,7 @@ export interface AddRuleNodeLinkDialogData {
standalone : false
standalone : false
} )
} )
export class AddRuleNodeLinkDialogComponent extends DialogComponent < AddRuleNodeLinkDialogComponent , FcRuleEdge >
export class AddRuleNodeLinkDialogComponent extends DialogComponent < AddRuleNodeLinkDialogComponent , FcRuleEdge >
implements OnInit , ErrorStateMatcher {
implements ErrorStateMatcher {
ruleNodeLinkFormGroup : UntypedFormGroup ;
ruleNodeLinkFormGroup : UntypedFormGroup ;
@ -1826,9 +2015,6 @@ export class AddRuleNodeLinkDialogComponent extends DialogComponent<AddRuleNodeL
) ;
) ;
}
}
ngOnInit ( ) : void {
}
isErrorState ( control : UntypedFormControl | null , form : FormGroupDirective | NgForm | null ) : boolean {
isErrorState ( control : UntypedFormControl | null , form : FormGroupDirective | NgForm | null ) : boolean {
const originalErrorState = this . errorStateMatcher . isErrorState ( control , form ) ;
const originalErrorState = this . errorStateMatcher . isErrorState ( control , form ) ;
const customErrorState = ! ! ( control && control . invalid && this . submitted ) ;
const customErrorState = ! ! ( control && control . invalid && this . submitted ) ;
@ -1861,7 +2047,7 @@ export interface AddRuleNodeDialogData {
standalone : false
standalone : false
} )
} )
export class AddRuleNodeDialogComponent extends DialogComponent < AddRuleNodeDialogComponent , FcRuleNode >
export class AddRuleNodeDialogComponent extends DialogComponent < AddRuleNodeDialogComponent , FcRuleNode >
implements OnInit , ErrorStateMatcher {
implements ErrorStateMatcher {
@ViewChild ( 'tbRuleNode' , { static : true } ) ruleNodeDetailsComponent : RuleNodeDetailsComponent ;
@ViewChild ( 'tbRuleNode' , { static : true } ) ruleNodeDetailsComponent : RuleNodeDetailsComponent ;
@ -1883,9 +2069,6 @@ export class AddRuleNodeDialogComponent extends DialogComponent<AddRuleNodeDialo
this . ruleChainType = this . data . ruleChainType ;
this . ruleChainType = this . data . ruleChainType ;
}
}
ngOnInit ( ) : void {
}
isErrorState ( control : UntypedFormControl | null , form : FormGroupDirective | NgForm | null ) : boolean {
isErrorState ( control : UntypedFormControl | null , form : FormGroupDirective | NgForm | null ) : boolean {
const originalErrorState = this . errorStateMatcher . isErrorState ( control , form ) ;
const originalErrorState = this . errorStateMatcher . isErrorState ( control , form ) ;
const customErrorState = ! ! ( control && control . invalid && this . submitted ) ;
const customErrorState = ! ! ( control && control . invalid && this . submitted ) ;
@ -1980,3 +2163,30 @@ export class CreateNestedRuleChainDialogComponent extends DialogComponent<Create
}
}
}
}
@Component ( {
selector : 'tb-add-note-dialog' ,
templateUrl : './add-note-dialog.component.html' ,
styleUrls : [ './add-note-dialog.component.scss' ] ,
standalone : false
} )
export class AddNoteDialogComponent extends DialogComponent < AddNoteDialogComponent , Partial < FcRuleNote > > {
@ViewChild ( 'tbRuleNote' ) ruleNoteComponent : RuleNoteEditorComponent ;
constructor ( protected store : Store < AppState > ,
protected router : Router ,
@Inject ( MAT_DIALOG_DATA ) public data : FcRuleNote ,
public dialogRef : MatDialogRef < AddNoteDialogComponent , Partial < FcRuleNote > > ) {
super ( store , router , dialogRef ) ;
}
cancel ( ) : void {
this . dialogRef . close ( undefined ) ;
}
save ( ) : void {
if ( this . ruleNoteComponent . noteForm . valid ) {
this . dialogRef . close ( this . ruleNoteComponent . noteForm . value ) ;
}
}
}