From 80380f0fdfd8cc5b47ad952cc140d3b3f9983984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Thu, 16 Jul 2026 12:42:27 +0200 Subject: [PATCH 01/12] [NAE-2462] Builder component decomposition - add new case ref component view --- .../abstract-case-ref-process.component.ts | 28 ++++++ .../src/lib/data-fields/public-api.ts | 1 + .../src/lib/builder/builder.component.ts | 20 +++- .../src/lib/builder/builder.module.ts | 11 ++- .../case-ref-process.component.html | 3 + .../case-ref-process.component.scss | 4 + .../case-ref-process.component.spec.ts | 98 +++++++++++++++++++ .../case-ref-process.component.ts | 25 +++++ 8 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 projects/netgrif-components-core/src/lib/data-fields/case-ref-field/case-ref-process/abstract-case-ref-process.component.ts create mode 100644 projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.html create mode 100644 projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss create mode 100644 projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.spec.ts create mode 100644 projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts diff --git a/projects/netgrif-components-core/src/lib/data-fields/case-ref-field/case-ref-process/abstract-case-ref-process.component.ts b/projects/netgrif-components-core/src/lib/data-fields/case-ref-field/case-ref-process/abstract-case-ref-process.component.ts new file mode 100644 index 000000000..bbd2e9461 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/data-fields/case-ref-field/case-ref-process/abstract-case-ref-process.component.ts @@ -0,0 +1,28 @@ +import {Component, Inject, Injector, Optional, Type} from '@angular/core'; +import {CaseRefField} from '../model/case-ref-field'; +import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from '../../models/data-field-portal-data-injection-token'; +import {AbstractBaseDataFieldComponent} from '../../base-component/abstract-base-data-field.component'; +import {ComponentPortal} from '@angular/cdk/portal'; + +@Component({ + selector: 'ncc-abstract-case-ref-process', + template: '' +}) +export abstract class AbstractCaseRefProcessComponent extends AbstractBaseDataFieldComponent { + + public componentPortal: ComponentPortal; + + protected constructor(protected injector: Injector, + protected builderComponent: Type, + @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { + super(dataFieldPortalData); + } + + createPortal() { + const portalInjector = Injector.create({ + providers: [], + parent: this.injector + }); + this.componentPortal = new ComponentPortal(this.builderComponent, null, portalInjector); + } +} diff --git a/projects/netgrif-components-core/src/lib/data-fields/public-api.ts b/projects/netgrif-components-core/src/lib/data-fields/public-api.ts index 3d2286f30..88fc68e6c 100644 --- a/projects/netgrif-components-core/src/lib/data-fields/public-api.ts +++ b/projects/netgrif-components-core/src/lib/data-fields/public-api.ts @@ -67,6 +67,7 @@ export * from './task-ref-field/task-ref-dashboard-field/task-ref-dashboard-tile export * from './task-ref-field/task-ref-dashboard-field/abstract-task-ref-dashboard-field.component'; export * from './task-ref-field/task-ref-list-field/abstract-task-ref-list-field.component'; export * from './case-ref-field/case-ref-default/case-ref-default.component'; +export * from './case-ref-field/case-ref-process/abstract-case-ref-process.component'; export * from './case-ref-field/model/case-ref-injection-tokens'; export * from './string-collection-field/string-collection-default-field/abstract-string-collection-default-field.component'; diff --git a/projects/netgrif-components/src/lib/builder/builder.component.ts b/projects/netgrif-components/src/lib/builder/builder.component.ts index b0996398c..9b1799581 100644 --- a/projects/netgrif-components/src/lib/builder/builder.component.ts +++ b/projects/netgrif-components/src/lib/builder/builder.component.ts @@ -1,11 +1,11 @@ -import {ChangeDetectionStrategy, Component, Inject, Optional} from '@angular/core'; +import {Component, Inject, Optional} from '@angular/core'; import {BuilderMode, BuilderModeService} from "./services/builder-mode.service"; import {ModelService} from "./modeler/services/model/model.service"; import {ActivatedRoute, Router} from "@angular/router"; import {MatDialog} from "@angular/material/dialog"; import {HttpClient} from "@angular/common/http"; import {HistoryService} from "./modeler/services/history/history.service"; -import {CaseResourceService, LoadingEmitter, NAE_TAB_DATA, ImmediateData} from "@netgrif/components-core"; +import {CaseResourceService, LoadingEmitter, NAE_TAB_DATA, ImmediateData, DATA_FIELD_PORTAL_DATA, CaseRefField, DataFieldPortalData} from "@netgrif/components-core"; import {InjectedTabbedBuilderViewData} from "./injected-builder-data"; import {FieldListService} from './form-builder/field-list/field-list.service'; import {GridsterService} from './form-builder/gridster/gridster.service'; @@ -126,7 +126,8 @@ export class BuilderComponent { public builderModeService: BuilderModeService, protected _caseResourceService: CaseResourceService, protected _builderIntegrationService: BuilderIntegrationService, - @Optional() @Inject(NAE_TAB_DATA) injectedTabData: InjectedTabbedBuilderViewData) { + @Optional() @Inject(NAE_TAB_DATA) injectedTabData: InjectedTabbedBuilderViewData, + @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { this.loading = new LoadingEmitter(true); if (injectedTabData?.processCase) { this._builderIntegrationService.isIntegrated = true; @@ -139,6 +140,19 @@ export class BuilderComponent { this._builderIntegrationService.reloadModes = true; }) }); + } else if (dataFieldPortalData?.dataField?.value?.length > 0) { + this._builderIntegrationService.isIntegrated = true; + this._caseResourceService.getOneCase(dataFieldPortalData?.dataField?.value[0]).subscribe(processCase => { + this._builderIntegrationService.processCase = processCase; + this.resolveIntegratedMode(); + }) + this._builderIntegrationService.reloadCase.subscribe(() => { + this._caseResourceService.getOneCase(this._builderIntegrationService.processCase?.stringId).subscribe(processCase => { + this._builderIntegrationService.processCase = processCase; + this.resolveIntegratedMode() + this._builderIntegrationService.reloadModes = true; + }) + }); } else { this.loading.off(); this.resolveRouterModel(); diff --git a/projects/netgrif-components/src/lib/builder/builder.module.ts b/projects/netgrif-components/src/lib/builder/builder.module.ts index cdd1420fa..93bd342a3 100644 --- a/projects/netgrif-components/src/lib/builder/builder.module.ts +++ b/projects/netgrif-components/src/lib/builder/builder.module.ts @@ -1,7 +1,7 @@ -import {NgModule} from '@angular/core'; +import {Injector, NgModule} from '@angular/core'; import {CommonModule, NgOptimizedImage} from '@angular/common'; import {BuilderComponent} from './builder.component'; -import {MaterialModule} from '@netgrif/components-core'; +import {MaterialModule, ComponentRegistryService} from '@netgrif/components-core'; import {FormBuilderModule} from './form-builder/form-builder.module'; import {ModelerModule} from './modeler/modeler.module'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; @@ -12,11 +12,14 @@ import {TaskContentComponentModule} from '../task-content/task-content.module'; import {MaterialIconPickerComponent} from './modeler/components/material-icon-picker/material-icon-picker.component'; import {BuilderDialogsModule} from './dialogs/dialogs.module'; import {FlexLayoutModule} from "@ngbracket/ngx-layout"; +import {ComponentPortal} from '@angular/cdk/portal'; +import {CaseRefProcessComponent} from '../data-fields/case-ref-field/case-ref-process/case-ref-process.component'; @NgModule({ declarations: [ BuilderComponent, + CaseRefProcessComponent, ], imports: [ BuilderDialogsModule, @@ -36,4 +39,8 @@ import {FlexLayoutModule} from "@ngbracket/ngx-layout"; exports: [BuilderComponent] }) export class BuilderModule { + + constructor(registry: ComponentRegistryService) { + registry.register("case-ref-process", (injector: Injector) => new ComponentPortal(CaseRefProcessComponent, null, injector)); + } } diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.html b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.html new file mode 100644 index 000000000..c6b5db803 --- /dev/null +++ b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss new file mode 100644 index 000000000..77145e595 --- /dev/null +++ b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss @@ -0,0 +1,4 @@ +.builder-height { + height: 500px; + width: 100%; +} diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.spec.ts b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.spec.ts new file mode 100644 index 000000000..e003f66dc --- /dev/null +++ b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.spec.ts @@ -0,0 +1,98 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CaseRefProcessComponent } from './case-ref-process.component'; +import {NavigationComponentModule} from '../../../navigation/navigation.module'; +import { + CaseRefField, + FilterField, FilterType, + NAE_TAB_DATA, + NAE_VIEW_ID_SEGMENT, + OverflowService, + TestMockDependenciesModule, + GroupNavigationConstants, +} from '@netgrif/components-core'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {RouterTestingModule} from '@angular/router/testing'; +import {of} from 'rxjs'; +import { + DefaultTabbedTaskViewComponent +} from '../../../navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component'; +import {Component} from '@angular/core'; + +describe('CaseRefDefaultComponent', () => { + let component: CaseRefProcessComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + NavigationComponentModule, + TestMockDependenciesModule, + NoopAnimationsModule, + RouterTestingModule.withRoutes([]), + ], + providers: [ + { provide: NAE_VIEW_ID_SEGMENT, useValue: 'id'}, + OverflowService, + { + provide: NAE_TAB_DATA, + useValue: { + allowedNets: [], + tabUniqueId: '1', + tabSelected$: of(true), + tabClosed$: of(), + tabViewOrder: 1, + tabViewComponent: DefaultTabbedTaskViewComponent, + navigationItemTaskData: [{fields: []}, { + fields: [ + new FilterField( + `${GroupNavigationConstants.ITEM_FIELD_CASE_FILTER}`, + '', + '', + { + filterType: FilterType.CASE, + predicateMetadata: [], + searchCategories: [] + }, + [], + {visible: true}, + '', + '' + ) + ] + }] + } + } + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestWrapperComponent); + component = fixture.debugElement.children[0].componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); + +@Component({ + selector: 'nc-test-wrapper', + template: '' +}) +class TestWrapperComponent { + field = new CaseRefField('', '', ['633c6187bb12a90925e0a17e'], { + required: true, + optional: true, + visible: true, + editable: true, + hidden: true + }, undefined, undefined, undefined, []); +} diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts new file mode 100644 index 000000000..539a492d0 --- /dev/null +++ b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts @@ -0,0 +1,25 @@ +import {AfterViewInit, Component, Inject, Injector, Optional} from '@angular/core'; +import { + AbstractCaseRefProcessComponent, + DATA_FIELD_PORTAL_DATA, + DataFieldPortalData, + CaseRefField +} from '@netgrif/components-core'; +import {BuilderComponent} from '../../../builder/builder.component'; + +@Component({ + selector: 'nc-case-ref-process', + templateUrl: './case-ref-process.component.html', + styleUrls: ['./case-ref-process.component.scss'] +}) +export class CaseRefProcessComponent extends AbstractCaseRefProcessComponent implements AfterViewInit { + + constructor(injector: Injector, + @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { + super(injector, BuilderComponent, dataFieldPortalData) + } + + ngAfterViewInit() { + this.createPortal(); + } +} From 18b5d38962e4c5964293bda0b8cd8210899857e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Tue, 21 Jul 2026 14:07:43 +0200 Subject: [PATCH 02/12] [NAE-2462] Builder component decomposition - performance fixes for builder to be ready as datafield --- .../src/lib/builder/builder.component.ts | 13 +++++--- .../edit-mode/edit-mode.component.scss | 8 +++++ .../modeler/edit-mode/edit-mode.service.ts | 11 ++++--- .../services/modes/canvas-tool-context.ts | 4 ++- .../edit-mode/services/modes/canvas-tool.ts | 2 +- .../services/canvas/canvas-listener-tool.ts | 33 ++++++++++++++++--- .../services/canvas/canvas-mode-service.ts | 10 ++++++ .../simulation-mode.service.ts | 21 +++++++----- .../simulation-mode/tool/change-data-tool.ts | 7 ++-- .../tool/event-simulation.tool.ts | 7 ++-- .../modeler/simulation-mode/tool/grid-tool.ts | 7 ++-- .../tool/reset-position-and-zoom-tool.ts | 7 ++-- .../tool/reset-simulation.tool.ts | 7 ++-- .../simulation-mode/tool/simulation-tool.ts | 6 ++-- .../simulation-mode/tool/switch-label-tool.ts | 7 ++-- .../tool/task-simulation.tool.ts | 7 ++-- .../case-ref-process.component.scss | 10 ++++++ 17 files changed, 127 insertions(+), 40 deletions(-) diff --git a/projects/netgrif-components/src/lib/builder/builder.component.ts b/projects/netgrif-components/src/lib/builder/builder.component.ts index 9b1799581..bcd2b4c95 100644 --- a/projects/netgrif-components/src/lib/builder/builder.component.ts +++ b/projects/netgrif-components/src/lib/builder/builder.component.ts @@ -1,4 +1,4 @@ -import {Component, Inject, Optional} from '@angular/core'; +import {Component, Inject, Optional, Type} from '@angular/core'; import {BuilderMode, BuilderModeService} from "./services/builder-mode.service"; import {ModelService} from "./modeler/services/model/model.service"; import {ActivatedRoute, Router} from "@angular/router"; @@ -55,6 +55,7 @@ import {I18nControlService} from './modeler/i18n-mode/i18n-control.service'; import {BuilderIntegrationService} from "./services/builder-integration.service"; import {TaskModeService} from "./modeler/task-mode/task-mode.service"; import {LocalStorageService} from "./services/local-storage.service"; +import {PetriflowCanvasService} from '@netgrif/petriflow.svg'; @Component({ selector: 'nc-builder', @@ -110,7 +111,11 @@ import {LocalStorageService} from "./services/local-storage.service"; I18nControlService, BuilderIntegrationService, TaskModeService, - LocalStorageService + LocalStorageService, + // PetriflowCanvasService is `providedIn: 'root'` in @netgrif/petriflow.svg, which would make it a + // single instance shared by every open builder tab (wrong canvas/model rendered on tab switch). + // Re-provided here so each instance gets its own canvas/panzoom state. + {provide: PetriflowCanvasService, useClass: PetriflowCanvasService as unknown as Type} ] }) export class BuilderComponent { @@ -129,7 +134,7 @@ export class BuilderComponent { @Optional() @Inject(NAE_TAB_DATA) injectedTabData: InjectedTabbedBuilderViewData, @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { this.loading = new LoadingEmitter(true); - if (injectedTabData?.processCase) { + if (injectedTabData !== null && injectedTabData?.processCase) { this._builderIntegrationService.isIntegrated = true; this._builderIntegrationService.processCase = injectedTabData.processCase; this.resolveIntegratedMode(); @@ -140,7 +145,7 @@ export class BuilderComponent { this._builderIntegrationService.reloadModes = true; }) }); - } else if (dataFieldPortalData?.dataField?.value?.length > 0) { + } else if (dataFieldPortalData !== null && dataFieldPortalData?.dataField?.value?.length > 0) { this._builderIntegrationService.isIntegrated = true; this._caseResourceService.getOneCase(dataFieldPortalData?.dataField?.value[0]).subscribe(processCase => { this._builderIntegrationService.processCase = processCase; diff --git a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.scss b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.scss index 29b806a13..11eedefc6 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.scss +++ b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.scss @@ -2,7 +2,15 @@ display: flex; height: 100%; width: 100%; + overflow: hidden; border: 0; + + petriflow-svg-canvas { + display: block; + width: 100%; + min-width: 0; + overflow: hidden; + } } .nab-menu { diff --git a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts index 25ee2443e..55f757673 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts @@ -1,9 +1,10 @@ -import {Injectable, Injector} from '@angular/core'; +import {Injectable, Injector, NgZone, Optional, Inject} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {CanvasConfiguration} from '@netgrif/petri.svg'; import {Arc, ArcType, NodeElement, Place, Transition} from '@netgrif/petriflow'; import {PetriflowCanvasService, PetriflowNode} from '@netgrif/petriflow.svg'; +import {InjectedTabData, NAE_TAB_DATA} from '@netgrif/components-core'; import {PanzoomOptions} from '@panzoom/panzoom'; import {BehaviorSubject} from 'rxjs'; import {ChangedArc} from '../../dialogs/dialog-arc-edit/changed-arc'; @@ -76,9 +77,11 @@ export class EditModeService extends CanvasModeService { protected _actionMode: ActionsModeService, protected _actionsMasterDetail: ActionsMasterDetailService, protected _processActionsTool: ProcessActionsTool, - protected _builderIntegrationService: BuilderIntegrationService + protected _builderIntegrationService: BuilderIntegrationService, + private _ngZone: NgZone, + @Optional() @Inject(NAE_TAB_DATA) _tabData?: InjectedTabData ) { - super(_arcFactory, modelService, _canvasService); + super(_arcFactory, modelService, _canvasService, _tabData); this.mode = new Mode( 'modeler', new ControlPanelButton( @@ -90,7 +93,7 @@ export class EditModeService extends CanvasModeService { this._tutorialService.modeler, this._parentInjector ); - const context = new CanvasToolContext(modelService, dialog, this, router, transitionService, _actionMode, _actionsMasterDetail, _builderModeService, _processActionsTool, _builderIntegrationService); + const context = new CanvasToolContext(modelService, dialog, this, router, transitionService, _actionMode, _actionsMasterDetail, _builderModeService, _processActionsTool, _builderIntegrationService, this._ngZone); this.switchTools = new ToolGroup( new ClearModelTool(context), new ResetPositionAndZoomTool(context), diff --git a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool-context.ts b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool-context.ts index e0c847def..b13f04b6c 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool-context.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool-context.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {ActionsMasterDetailService} from '../../../actions-mode/actions-master-detail.service'; @@ -20,6 +21,7 @@ export class CanvasToolContext { public readonly actionsMasterDetail: ActionsMasterDetailService, public readonly builderModeService: BuilderModeService, public readonly processTool: ProcessActionsTool, - public readonly builderIntegrationService: BuilderIntegrationService + public readonly builderIntegrationService: BuilderIntegrationService, + public readonly ngZone: NgZone ) {} } diff --git a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool.ts b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool.ts index 4aa2b955b..0f780fae1 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/services/modes/canvas-tool.ts @@ -43,7 +43,7 @@ export abstract class CanvasTool extends CanvasListenerTool { button: ControlPanelButton, protected _context: CanvasToolContext, ) { - super(id, button, _context.modelService, _context.dialog, _context.router, _context.transitionService); + super(id, button, _context.modelService, _context.dialog, _context.router, _context.transitionService, _context.ngZone); this._editModeService = _context.editModeService; this.actionMode = _context.actionMode; this.actionsMasterDetail = _context.actionsMasterDetail; diff --git a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-listener-tool.ts b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-listener-tool.ts index 8b75929ef..1dfb58f54 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-listener-tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-listener-tool.ts @@ -1,4 +1,5 @@ import {ComponentType} from '@angular/cdk/overlay'; +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {PetriNet} from '@netgrif/petriflow'; @@ -39,7 +40,8 @@ export abstract class CanvasListenerTool extends Tool implements MouseListener, modelService: ModelService, dialog: MatDialog, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + private readonly _ngZone?: NgZone ) { super(id, button, ToolComponent); this._modelService = modelService; @@ -56,6 +58,19 @@ export abstract class CanvasListenerTool extends Tool implements MouseListener, this.hotkeys.push(new Hotkey('Home', false, false, false, this.move.bind(this, 0, 0, false))); } + /** + * onpointermove fires at high frequency during panning/dragging. Registering it while running + * outside the Angular zone keeps every mousemove (and the requestAnimationFrame panzoom schedules + * internally to apply the transform) from triggering a full change-detection pass. + */ + private runOutsideAngular(fn: () => void): void { + if (this._ngZone) { + this._ngZone.runOutsideAngular(fn); + } else { + fn(); + } + } + abstract get canvasService(): PetriflowCanvasService; abstract get elements(): CanvasElementCollection; @@ -116,7 +131,9 @@ export abstract class CanvasListenerTool extends Tool implements MouseListener, place.svgPlace.canvasElement.container.onpointerup = (e: PointerEvent) => this.handleEvent(this.onPlaceUp, e, place); place.svgPlace.canvasElement.container.onpointerenter = (e: PointerEvent) => this.handleEvent(this.onPlaceEnter, e, place); place.svgPlace.canvasElement.container.onpointerleave = (e: PointerEvent) => this.handleEvent(this.onPlaceLeave, e, place); - place.svgPlace.canvasElement.container.onpointermove = (e: PointerEvent) => this.handleEvent(this.onPlaceMove, e, place); + this.runOutsideAngular(() => { + place.svgPlace.canvasElement.container.onpointermove = (e: PointerEvent) => this.handleEvent(this.onPlaceMove, e, place); + }); } unbindPlaces(places: Array): void { @@ -140,7 +157,9 @@ export abstract class CanvasListenerTool extends Tool implements MouseListener, transition.svgTransition.canvasElement.container.onpointerup = (e: PointerEvent) => this.handleEvent(this.onTransitionUp, e, transition); transition.svgTransition.canvasElement.container.onpointerenter = (e: PointerEvent) => this.handleEvent(this.onTransitionEnter, e, transition); transition.svgTransition.canvasElement.container.onpointerleave = (e: PointerEvent) => this.handleEvent(this.onTransitionLeave, e, transition); - transition.svgTransition.canvasElement.container.onpointermove = (e: PointerEvent) => this.handleEvent(this.onTransitionMove, e, transition); + this.runOutsideAngular(() => { + transition.svgTransition.canvasElement.container.onpointermove = (e: PointerEvent) => this.handleEvent(this.onTransitionMove, e, transition); + }); } unbindTransitions(transitions: Array): void { @@ -164,7 +183,9 @@ export abstract class CanvasListenerTool extends Tool implements MouseListener, arc.svgArc.element.container.onpointerup = (e: PointerEvent) => this.handleEvent(this.onArcUp, e, arc); arc.svgArc.element.container.onpointerenter = (e: PointerEvent) => this.handleEvent(this.onArcEnter, e, arc); arc.svgArc.element.container.onpointerleave = (e: PointerEvent) => this.handleEvent(this.onArcLeave, e, arc); - arc.svgArc.element.container.onpointermove = (e: PointerEvent) => this.handleEvent(this.onArcMove, e, arc); + this.runOutsideAngular(() => { + arc.svgArc.element.container.onpointermove = (e: PointerEvent) => this.handleEvent(this.onArcMove, e, arc); + }); } unbindArcs(arcs: Array): void { @@ -187,7 +208,9 @@ export abstract class CanvasListenerTool extends Tool implements MouseListener, canvas.svg.onpointerup = (e: PointerEvent) => this.handleEvent(this.onMouseUp, e); canvas.svg.onpointerenter = (e: PointerEvent) => this.handleEvent(this.onMouseEnter, e); canvas.svg.onpointerleave = (e: PointerEvent) => this.handleEvent(this.onMouseLeave, e); - canvas.svg.onpointermove = (e: PointerEvent) => this.handleEvent(this.onMouseMove, e); + this.runOutsideAngular(() => { + canvas.svg.onpointermove = (e: PointerEvent) => this.handleEvent(this.onMouseMove, e); + }); canvas.svg.oncontextmenu = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); diff --git a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts index 71b00088e..73ccb6759 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts @@ -6,6 +6,7 @@ import { PetriflowPlace as PetriflowSvgPlace, PetriflowTransition as PetriflowSvgTransition, } from '@netgrif/petriflow.svg'; +import {InjectedTabData} from '@netgrif/components-core'; import {ModeService} from '../../control-panel/modes/mode-component/mode.service'; import {Tool} from '../../control-panel/tools/tool'; import {ArcFactory} from '../../edit-mode/domain/arc-builders/arc-factory.service'; @@ -24,8 +25,17 @@ export abstract class CanvasModeService extends ModeService { protected _arcFactory: ArcFactory, protected _modelService: ModelService, protected _canvasService: PetriflowCanvasService, + protected _tabData?: InjectedTabData, ) { super(); + // the tab's content (and this service) stays alive while the tab is open but hidden (see NAE-326); + // panzoom's initial view is computed from live element dimensions, which are wrong while hidden, + // so re-sync it once the tab becomes visible again. + this._tabData?.tabSelected$?.subscribe(selected => { + if (selected) { + this.panzoom?.reset({animate: false}); + } + }); this.labelText = (n: NodeElement) => n.label.value; this.multiplicityText = (a: CanvasArc) => { if (!!a.modelArc.reference) { diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts index 676ee76e4..55140bb3e 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts @@ -1,9 +1,10 @@ -import {Injectable, Injector} from '@angular/core'; +import {Injectable, Injector, NgZone, Optional, Inject} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {Arc, BasicSimulation, ImportUtils, PetriNet, Place, Transition} from '@netgrif/petriflow'; import {PetriflowCanvasService} from '@netgrif/petriflow.svg'; import {BehaviorSubject} from 'rxjs'; +import {InjectedTabData, NAE_TAB_DATA} from '@netgrif/components-core'; import {TutorialService} from '../../tutorial/tutorial-service'; import {ToolGroup} from '../control-panel/tools/tool-group'; import {ArcFactory} from '../edit-mode/domain/arc-builders/arc-factory.service'; @@ -42,8 +43,10 @@ export class SimulationModeService extends CanvasModeService { transitionService: SelectedTransitionService, private tutorialService: TutorialService, private parentInjector: Injector, + private _ngZone: NgZone, + @Optional() @Inject(NAE_TAB_DATA) _tabData?: InjectedTabData, ) { - super(_arcFactory, _modelService, _canvasService); + super(_arcFactory, _modelService, _canvasService, _tabData); this._data = new Map(); this.mode = new SimulationMode( this.tutorialService.simulator, @@ -66,19 +69,19 @@ export class SimulationModeService extends CanvasModeService { } return ''; }; - this.defaultTool = new TaskSimulationTool(this._modelService, dialog, this, router, transitionService); + this.defaultTool = new TaskSimulationTool(this._modelService, dialog, this, router, transitionService, this._ngZone); this.switchTools = new ToolGroup( - new ResetSimulationTool(this._modelService, dialog, this, router, transitionService), - new ChangeDataTool(this._modelService, dialog, this, router, transitionService), - new ResetPositionAndZoomTool(this._modelService, dialog, this, router, transitionService), - new GridTool(this._modelService, dialog, this, router, transitionService), - new SwitchLabelTool(this._modelService, dialog, this, router, transitionService), + new ResetSimulationTool(this._modelService, dialog, this, router, transitionService, this._ngZone), + new ChangeDataTool(this._modelService, dialog, this, router, transitionService, this._ngZone), + new ResetPositionAndZoomTool(this._modelService, dialog, this, router, transitionService, this._ngZone), + new GridTool(this._modelService, dialog, this, router, transitionService, this._ngZone), + new SwitchLabelTool(this._modelService, dialog, this, router, transitionService, this._ngZone), ); this.switchTools.tools.forEach(t => t.bind()); this.tools = [ new ToolGroup( this.defaultTool, - new EventSimulationTool(this._modelService, dialog, this, router, transitionService), + new EventSimulationTool(this._modelService, dialog, this, router, transitionService, this._ngZone), ), this.switchTools, ]; diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/change-data-tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/change-data-tool.ts index 9f2dcee93..266c41eb5 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/change-data-tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/change-data-tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import { @@ -19,7 +20,8 @@ export class ChangeDataTool extends SimulationTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { super( 'change_data', @@ -31,7 +33,8 @@ export class ChangeDataTool extends SimulationTool { dialog, simulationModeService, router, - transitionService + transitionService, + ngZone ); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/event-simulation.tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/event-simulation.tool.ts index 41dbc10f5..30ccc1ecb 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/event-simulation.tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/event-simulation.tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {ControlPanelButton} from '../../control-panel/control-panel-button'; @@ -15,7 +16,8 @@ export class EventSimulationTool extends SimulationTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { super( 'event_simulation', @@ -27,7 +29,8 @@ export class EventSimulationTool extends SimulationTool { dialog, simulationModeService, router, - transitionService + transitionService, + ngZone ); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/grid-tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/grid-tool.ts index a5e65ea37..137c0979e 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/grid-tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/grid-tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {ControlPanelButton} from '../../control-panel/control-panel-button'; @@ -20,7 +21,8 @@ export class GridTool extends SimulationTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { super( GridTool.ID, @@ -32,7 +34,8 @@ export class GridTool extends SimulationTool { dialog, simulationModeService, router, - transitionService + transitionService, + ngZone ); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-position-and-zoom-tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-position-and-zoom-tool.ts index 1164f586a..f4b1b0731 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-position-and-zoom-tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-position-and-zoom-tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {ControlPanelButton} from '../../control-panel/control-panel-button'; @@ -16,7 +17,8 @@ export class ResetPositionAndZoomTool extends SimulationTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { super( ResetPositionAndZoomTool.ID, @@ -28,7 +30,8 @@ export class ResetPositionAndZoomTool extends SimulationTool { dialog, simulationModeService, router, - transitionService + transitionService, + ngZone ); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-simulation.tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-simulation.tool.ts index 75fd97255..89c2bbef9 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-simulation.tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/reset-simulation.tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {ControlPanelButton} from '../../control-panel/control-panel-button'; @@ -14,7 +15,8 @@ export class ResetSimulationTool extends SimulationTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { super( 'reset_simulation', @@ -26,7 +28,8 @@ export class ResetSimulationTool extends SimulationTool { dialog, simulationModeService, router, - transitionService + transitionService, + ngZone ); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/simulation-tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/simulation-tool.ts index 1622caf18..bc3ef4634 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/simulation-tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/simulation-tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {BasicSimulation, Place} from '@netgrif/petriflow'; @@ -30,9 +31,10 @@ export abstract class SimulationTool extends CanvasListenerTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { - super(id, button, modelService, dialog, router, transitionService); + super(id, button, modelService, dialog, router, transitionService, ngZone); this._simulationModeService = simulationModeService; } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/switch-label-tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/switch-label-tool.ts index 9827fbf21..a395e518e 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/switch-label-tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/switch-label-tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {NodeElement} from '@netgrif/petriflow'; @@ -22,7 +23,8 @@ export class SwitchLabelTool extends SimulationTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { super( SwitchLabelTool.ID, @@ -34,7 +36,8 @@ export class SwitchLabelTool extends SimulationTool { dialog, simulationModeService, router, - transitionService + transitionService, + ngZone ); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/task-simulation.tool.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/task-simulation.tool.ts index 2daf2cf0c..a4db67cd8 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/task-simulation.tool.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/tool/task-simulation.tool.ts @@ -1,3 +1,4 @@ +import {NgZone} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {ControlPanelButton} from '../../control-panel/control-panel-button'; @@ -15,7 +16,8 @@ export class TaskSimulationTool extends SimulationTool { dialog: MatDialog, simulationModeService: SimulationModeService, router: Router, - transitionService: SelectedTransitionService + transitionService: SelectedTransitionService, + ngZone?: NgZone ) { super( 'simulation', @@ -27,7 +29,8 @@ export class TaskSimulationTool extends SimulationTool { dialog, simulationModeService, router, - transitionService + transitionService, + ngZone ); } diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss index 77145e595..74e8ac973 100644 --- a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss +++ b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss @@ -1,4 +1,14 @@ .builder-height { height: 500px; width: 100%; + max-width: 100%; + display: flex; + overflow: hidden; + contain: size; + border: 0; + + ::ng-deep > * { + min-width: 0; + min-height: 0; + } } From 8d08764fb3bd0adce96cc483bce61cc1c6b494af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Thu, 23 Jul 2026 13:56:07 +0200 Subject: [PATCH 03/12] [NAE-2462] Builder component decomposition - performance fixes for builder --- .../src/lib/user/services/user.service.ts | 4 ---- .../simulation-mode.component.html | 5 +++-- .../simulation-mode/simulation-mode.component.ts | 2 +- .../simulation-mode/simulation-mode.service.ts | 15 +++++++++++++++ .../src/lib/tabs/tab-view/tab-view.component.html | 2 +- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/user/services/user.service.ts b/projects/netgrif-components-core/src/lib/user/services/user.service.ts index e122a07d1..8c887f994 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user.service.ts @@ -66,10 +66,6 @@ export class UserService implements OnDestroy { return this._userChange$.asObservable(); } - get anonymousUser(): User { - return this.anonymousUser; - } - get anonymousUser$(): Observable { return this._anonymousUserChange$.asObservable(); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html index 4b2a8bb41..beba236f2 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html @@ -1,3 +1,4 @@ -
- +z
+
diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.ts index abbdeed6f..20e1bc57a 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.ts @@ -15,7 +15,7 @@ export class SimulationModeComponent implements AfterViewInit, OnDestroy { constructor( public canvasService: PetriflowCanvasService, - private simulationService: SimulationModeService, + public simulationService: SimulationModeService, private modelService: ModelService ) { } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts index 55140bb3e..0130813eb 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts @@ -3,6 +3,7 @@ import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {Arc, BasicSimulation, ImportUtils, PetriNet, Place, Transition} from '@netgrif/petriflow'; import {PetriflowCanvasService} from '@netgrif/petriflow.svg'; +import {PanzoomOptions} from '@panzoom/panzoom'; import {BehaviorSubject} from 'rxjs'; import {InjectedTabData, NAE_TAB_DATA} from '@netgrif/components-core'; import {TutorialService} from '../../tutorial/tutorial-service'; @@ -33,6 +34,20 @@ export class SimulationModeService extends CanvasModeService { // TODO: NAB-326 refactor public switchTools: ToolGroup; private _onTransitionDraw: (t: CanvasTransition) => void; + /** + * `noBind` is required here: pointer events are wired up manually through {@link CanvasListenerTool} + * (same as in edit mode). Without it, Panzoom binds its own raw `document` pointermove/pointerup + * listeners outside Angular's knowledge, which run on every mouse movement on the whole page. + */ + public panzoomConfiguration: PanzoomOptions = { + canvas: true, + contain: 'outside', + cursor: 'auto', + maxScale: 10, + minScale: 0.5, + step: 0.2, + noBind: true + }; constructor( protected _arcFactory: ArcFactory, diff --git a/projects/netgrif-components/src/lib/tabs/tab-view/tab-view.component.html b/projects/netgrif-components/src/lib/tabs/tab-view/tab-view.component.html index 55726d0df..2ca6fd918 100644 --- a/projects/netgrif-components/src/lib/tabs/tab-view/tab-view.component.html +++ b/projects/netgrif-components/src/lib/tabs/tab-view/tab-view.component.html @@ -24,7 +24,7 @@ - + From d980f0e6deabc899c6d464f0dbd1165a0623f3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Fri, 24 Jul 2026 08:56:43 +0200 Subject: [PATCH 04/12] [NAE-2462] Builder component decomposition - remove z in simulation mode - add fixes of toolbar for scrollbar on smaller devices or datafields --- .../control-panel/control-panel.component.scss | 13 +++++++++++++ .../modeler/edit-mode/edit-mode.component.ts | 2 +- .../builder/modeler/edit-mode/edit-mode.service.ts | 2 +- .../src/lib/builder/modeler/modeler.component.scss | 5 +++-- .../simulation-mode/simulation-mode.component.html | 2 +- .../modeler/task-mode/task-mode.component.ts | 10 ++++------ .../case-ref-process.component.scss | 1 + 7 files changed, 24 insertions(+), 11 deletions(-) diff --git a/projects/netgrif-components/src/lib/builder/modeler/control-panel/control-panel.component.scss b/projects/netgrif-components/src/lib/builder/modeler/control-panel/control-panel.component.scss index 5ee97703c..2949c0978 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/control-panel/control-panel.component.scss +++ b/projects/netgrif-components/src/lib/builder/modeler/control-panel/control-panel.component.scss @@ -4,3 +4,16 @@ mat-divider.style-fix { margin-left: 4px; margin-right: 4px; } + +:host { + display: block; + width: 100%; + min-width: 0; +} + +.control { + width: 100%; + min-width: 0; + overflow-x: auto; + overflow-y: hidden; +} diff --git a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.ts b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.ts index 6a31e3564..8ff2bf7f6 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.component.ts @@ -75,7 +75,7 @@ export class EditModeComponent implements AfterViewInit, OnDestroy { } onDropZone($event: NgxDropzoneChangeEvent) { - $event.addedFiles[0].text().then(originFile => { + $event.addedFiles[0]?.text()?.then(originFile => { this.importService.importFromXml(originFile); }); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts index 55f757673..7bd0f5acd 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts @@ -346,7 +346,7 @@ export class EditModeService extends CanvasModeService { const arcRatio = arcLengthOffset / arcLength; const finalX = intersect.x + xLineLength * arcRatio; const finalY = intersect.y + yLineLength * arcRatio; - arcLine.setAttributeNS(null, 'points', `${intersect.x},${intersect.y} ${finalX ?? 0},${finalY ?? 0}`); + arcLine.setAttributeNS(null, 'points', `${intersect.x},${intersect.y} ${isNaN(finalX) ? 0 : finalX},${isNaN(finalY) ? 0 : finalY}`); } // OTHER diff --git a/projects/netgrif-components/src/lib/builder/modeler/modeler.component.scss b/projects/netgrif-components/src/lib/builder/modeler/modeler.component.scss index a921b0833..3e2abca7b 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/modeler.component.scss +++ b/projects/netgrif-components/src/lib/builder/modeler/modeler.component.scss @@ -3,10 +3,11 @@ .modelerToolbar { padding-left: 4px; - height: 42px; + padding-right: 4px; + height: 48px; border-bottom: 1px solid #DDDDDD; } .height-modeler { - height: calc(100% - 42px); + height: calc(100% - 48px); } diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html index beba236f2..602b013ea 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.component.html @@ -1,4 +1,4 @@ -z
+
diff --git a/projects/netgrif-components/src/lib/builder/modeler/task-mode/task-mode.component.ts b/projects/netgrif-components/src/lib/builder/modeler/task-mode/task-mode.component.ts index 4f980ecdc..150424ceb 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/task-mode/task-mode.component.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/task-mode/task-mode.component.ts @@ -8,7 +8,6 @@ import { AbstractTaskViewComponent, AllowedNetsServiceFactory, ChangedFieldsService, - NAE_TAB_DATA, TaskViewService, SimpleFilter, CategoryFactory, @@ -19,12 +18,11 @@ import { TaskEventNotification, TaskEvent, NAE_DEFAULT_HEADERS -} from "@netgrif/components-core"; -import {InjectedTabbedBuilderViewData} from "../../injected-builder-data"; +} from '@netgrif/components-core'; import {BuilderIntegrationService} from "../../services/builder-integration.service"; -const baseFilterFactory = (injectedData: InjectedTabbedBuilderViewData) => { - const caseId = injectedData?.processCase?.stringId ?? "__EMPTY__" +const baseFilterFactory = (integrationService: BuilderIntegrationService) => { + const caseId = integrationService?.processCase?.stringId ?? "__EMPTY__" return {filter: SimpleFilter.fromTaskQuery({case: {id: caseId}, transitionId: ['deploy', 't10', 't11', 't4', 't6', 't9', 'view']})}; }; @@ -52,7 +50,7 @@ const localAllowedNetsFactory = (factory: AllowedNetsServiceFactory) => { { provide: NAE_BASE_FILTER, useFactory: baseFilterFactory, - deps: [NAE_TAB_DATA] + deps: [BuilderIntegrationService] }, { provide: AllowedNetsService, diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss index 74e8ac973..1ece6875d 100644 --- a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss +++ b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss @@ -10,5 +10,6 @@ ::ng-deep > * { min-width: 0; min-height: 0; + width: 100%; } } From b7ccfb819f5545ca695f581451beb7072d3aa320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Tue, 28 Jul 2026 10:12:26 +0200 Subject: [PATCH 05/12] [NAE-2462] Builder component decomposition - add processref as new datafield --- package.json | 2 +- .../src/lib/data-fields/models/component.ts | 1 + .../model/process-ref-field.ts | 18 ++++++++ ...abstract-process-ref-default.component.ts} | 8 ++-- .../src/lib/data-fields/public-api.ts | 3 +- .../task-content/model/field-type-resource.ts | 1 + .../services/field-converter.service.ts | 4 ++ .../src/lib/builder/builder.module.ts | 6 +-- .../field-list/field-list.component.ts | 4 +- .../field-list/field-list.service.ts | 9 +++- .../data-detail/data-detail.component.ts | 3 +- .../case-ref-process.component.ts | 25 ----------- .../process-ref-default.component.html} | 2 +- .../process-ref-default.component.scss} | 1 - .../process-ref-default.component.spec.ts} | 4 +- .../process-ref-default.component.ts | 43 +++++++++++++++++++ 16 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 projects/netgrif-components-core/src/lib/data-fields/process-ref-field/model/process-ref-field.ts rename projects/netgrif-components-core/src/lib/data-fields/{case-ref-field/case-ref-process/abstract-case-ref-process.component.ts => process-ref-field/process-ref-default/abstract-process-ref-default.component.ts} (75%) delete mode 100644 projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts rename projects/netgrif-components/src/lib/data-fields/{case-ref-field/case-ref-process/case-ref-process.component.html => process-ref/process-ref-default/process-ref-default.component.html} (57%) rename projects/netgrif-components/src/lib/data-fields/{case-ref-field/case-ref-process/case-ref-process.component.scss => process-ref/process-ref-default/process-ref-default.component.scss} (92%) rename projects/netgrif-components/src/lib/data-fields/{case-ref-field/case-ref-process/case-ref-process.component.spec.ts => process-ref/process-ref-default/process-ref-default.component.spec.ts} (96%) create mode 100644 projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.ts diff --git a/package.json b/package.json index ec9b8b1e6..b53d2e5df 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "@mdi/angular-material": "^7.2.96", "@mdi/font": "^7.4.47", "@netgrif/petri.svg": "1.1.1", - "@netgrif/petriflow": "2.2.3", + "@netgrif/petriflow": "2.2.4", "@netgrif/petriflow.svg": "1.1.1", "@ngbracket/ngx-layout": "^17.0.1", "@ngx-translate/core": "~15.0.0", diff --git a/projects/netgrif-components-core/src/lib/data-fields/models/component.ts b/projects/netgrif-components-core/src/lib/data-fields/models/component.ts index 87b798e7e..0dd9f9fa7 100644 --- a/projects/netgrif-components-core/src/lib/data-fields/models/component.ts +++ b/projects/netgrif-components-core/src/lib/data-fields/models/component.ts @@ -17,6 +17,7 @@ export enum ComponentPrefixes { NUMBER = 'number-', TASK_REF = 'task-ref-', CASE_REF = 'case-ref-', + PROCESS_REF = 'process-ref-', TEXT = 'text-', USER = 'user-', USER_LIST = 'user-list-', diff --git a/projects/netgrif-components-core/src/lib/data-fields/process-ref-field/model/process-ref-field.ts b/projects/netgrif-components-core/src/lib/data-fields/process-ref-field/model/process-ref-field.ts new file mode 100644 index 000000000..ac5ad5432 --- /dev/null +++ b/projects/netgrif-components-core/src/lib/data-fields/process-ref-field/model/process-ref-field.ts @@ -0,0 +1,18 @@ +import {DataField} from '../../models/abstract-data-field'; +import {Behavior} from '../../models/behavior'; +import {Layout} from '../../models/layout'; +import {Validation} from '../../models/validation'; +import {Component, ComponentPrefixes} from '../../models/component'; + +export class ProcessRefField extends DataField> { + + constructor(stringId: string, title: string, initialValue: Array, behavior: Behavior, + placeholder?: string, description?: string, layout?: Layout, validations?: Array, component?: Component, + parentTaskId?: string) { + super(stringId, title, initialValue, behavior, placeholder, description, layout, validations, component, parentTaskId); + } + + public getTypedComponentType(): string { + return ComponentPrefixes.PROCESS_REF + this.getComponentType(); + } +} diff --git a/projects/netgrif-components-core/src/lib/data-fields/case-ref-field/case-ref-process/abstract-case-ref-process.component.ts b/projects/netgrif-components-core/src/lib/data-fields/process-ref-field/process-ref-default/abstract-process-ref-default.component.ts similarity index 75% rename from projects/netgrif-components-core/src/lib/data-fields/case-ref-field/case-ref-process/abstract-case-ref-process.component.ts rename to projects/netgrif-components-core/src/lib/data-fields/process-ref-field/process-ref-default/abstract-process-ref-default.component.ts index bbd2e9461..8481925dc 100644 --- a/projects/netgrif-components-core/src/lib/data-fields/case-ref-field/case-ref-process/abstract-case-ref-process.component.ts +++ b/projects/netgrif-components-core/src/lib/data-fields/process-ref-field/process-ref-default/abstract-process-ref-default.component.ts @@ -1,20 +1,20 @@ import {Component, Inject, Injector, Optional, Type} from '@angular/core'; -import {CaseRefField} from '../model/case-ref-field'; import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from '../../models/data-field-portal-data-injection-token'; import {AbstractBaseDataFieldComponent} from '../../base-component/abstract-base-data-field.component'; import {ComponentPortal} from '@angular/cdk/portal'; +import {ProcessRefField} from '../model/process-ref-field'; @Component({ - selector: 'ncc-abstract-case-ref-process', + selector: 'ncc-abstract-process-ref-default', template: '' }) -export abstract class AbstractCaseRefProcessComponent extends AbstractBaseDataFieldComponent { +export abstract class AbstractProcessRefDefaultComponent extends AbstractBaseDataFieldComponent { public componentPortal: ComponentPortal; protected constructor(protected injector: Injector, protected builderComponent: Type, - @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { + @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { super(dataFieldPortalData); } diff --git a/projects/netgrif-components-core/src/lib/data-fields/public-api.ts b/projects/netgrif-components-core/src/lib/data-fields/public-api.ts index 88fc68e6c..dd26c1fb4 100644 --- a/projects/netgrif-components-core/src/lib/data-fields/public-api.ts +++ b/projects/netgrif-components-core/src/lib/data-fields/public-api.ts @@ -67,7 +67,8 @@ export * from './task-ref-field/task-ref-dashboard-field/task-ref-dashboard-tile export * from './task-ref-field/task-ref-dashboard-field/abstract-task-ref-dashboard-field.component'; export * from './task-ref-field/task-ref-list-field/abstract-task-ref-list-field.component'; export * from './case-ref-field/case-ref-default/case-ref-default.component'; -export * from './case-ref-field/case-ref-process/abstract-case-ref-process.component'; +export * from './process-ref-field/process-ref-default/abstract-process-ref-default.component'; +export * from './process-ref-field/model/process-ref-field'; export * from './case-ref-field/model/case-ref-injection-tokens'; export * from './string-collection-field/string-collection-default-field/abstract-string-collection-default-field.component'; diff --git a/projects/netgrif-components-core/src/lib/task-content/model/field-type-resource.ts b/projects/netgrif-components-core/src/lib/task-content/model/field-type-resource.ts index 5213f3799..2ed20950d 100644 --- a/projects/netgrif-components-core/src/lib/task-content/model/field-type-resource.ts +++ b/projects/netgrif-components-core/src/lib/task-content/model/field-type-resource.ts @@ -18,6 +18,7 @@ export enum FieldTypeResource { MULTICHOICE_MAP = 'multichoice_map', TASK_REF = 'taskRef', CASE_REF = 'caseRef', + PROCESS_REF = 'processRef', CASE_FILTER = 'caseFilter', TASK_FILTER = 'taskFilter', PROCESS_FILTER = 'processFilter', diff --git a/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts b/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts index 13eb0a56d..356e32116 100644 --- a/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts +++ b/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts @@ -26,6 +26,7 @@ import {UserListValue} from '../../data-fields/user-list-field/models/user-list- import {decodeBase64, encodeBase64} from "../../utility/base64"; import {CaseRefField} from '../../data-fields/case-ref-field/model/case-ref-field'; import {StringCollectionField} from '../../data-fields/string-collection-field/models/string-collection-field'; +import {ProcessRefField} from '../../data-fields/process-ref-field/model/process-ref-field'; @Injectable({ providedIn: 'root' @@ -108,6 +109,9 @@ export class FieldConverterService { case FieldTypeResource.CASE_REF: return new CaseRefField(item.stringId, item.name, item.value ? item.value : [], item.behavior, item.placeholder, item.description, item.layout, item.validations, item.component, item.parentTaskId); + case FieldTypeResource.PROCESS_REF: + return new ProcessRefField(item.stringId, item.name, item.value ? item.value : [], item.behavior, + item.placeholder, item.description, item.layout, item.validations, item.component, item.parentTaskId); case FieldTypeResource.CASE_FILTER: case FieldTypeResource.TASK_FILTER: case FieldTypeResource.PROCESS_FILTER: diff --git a/projects/netgrif-components/src/lib/builder/builder.module.ts b/projects/netgrif-components/src/lib/builder/builder.module.ts index 93bd342a3..ba351c5bd 100644 --- a/projects/netgrif-components/src/lib/builder/builder.module.ts +++ b/projects/netgrif-components/src/lib/builder/builder.module.ts @@ -13,13 +13,13 @@ import {MaterialIconPickerComponent} from './modeler/components/material-icon-pi import {BuilderDialogsModule} from './dialogs/dialogs.module'; import {FlexLayoutModule} from "@ngbracket/ngx-layout"; import {ComponentPortal} from '@angular/cdk/portal'; -import {CaseRefProcessComponent} from '../data-fields/case-ref-field/case-ref-process/case-ref-process.component'; +import {ProcessRefDefaultComponent} from '../data-fields/process-ref/process-ref-default/process-ref-default.component'; @NgModule({ declarations: [ BuilderComponent, - CaseRefProcessComponent, + ProcessRefDefaultComponent, ], imports: [ BuilderDialogsModule, @@ -41,6 +41,6 @@ import {CaseRefProcessComponent} from '../data-fields/case-ref-field/case-ref-pr export class BuilderModule { constructor(registry: ComponentRegistryService) { - registry.register("case-ref-process", (injector: Injector) => new ComponentPortal(CaseRefProcessComponent, null, injector)); + registry.register("process-ref-default", (injector: Injector) => new ComponentPortal(ProcessRefDefaultComponent, null, injector)); } } diff --git a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts index 6925b7d95..21576d8e6 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts +++ b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts @@ -141,8 +141,8 @@ export class FieldListComponent implements OnInit, AfterViewInit { } typeTitle(type: DataType): string { - const name = (type as string).replace('_', ' ').replace(/([A-Z])/g, ' $1').toLowerCase(); - return name.charAt(0).toUpperCase() + name.slice(1); + const name = (type as string)?.replace('_', ' ')?.replace(/([A-Z])/g, ' $1')?.toLowerCase(); + return name?.charAt(0)?.toUpperCase() + name?.slice(1); } addNewField(type: DataType, meta: any) { diff --git a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.service.ts b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.service.ts index f1ea6836a..2bc87a135 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.service.ts +++ b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.service.ts @@ -373,6 +373,13 @@ export class FieldListService { {title: 'Dashboard', name: 'dashboard', cols: 4} ] }, + { + type: DataType.PROCESS_REF, + showPlaceholder: true, + components: [ + {title: 'Simple', cols: 4}, + ] + }, { type: DataType.CASE_REF, showPlaceholder: true, @@ -403,7 +410,7 @@ export class FieldListService { ] }, { - type: 'stringCollection' as DataType, + type: DataType.STRING_COLLECTION, components: [ {title: 'Simple'} ], diff --git a/projects/netgrif-components/src/lib/builder/modeler/data-mode/data-detail/data-detail.component.ts b/projects/netgrif-components/src/lib/builder/modeler/data-mode/data-detail/data-detail.component.ts index 493fef0aa..ab2a500c7 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/data-mode/data-detail/data-detail.component.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/data-mode/data-detail/data-detail.component.ts @@ -60,6 +60,7 @@ export class DataDetailComponent implements OnDestroy { {viewValue: 'Boolean', value: DataType.BOOLEAN}, {viewValue: 'Button', value: DataType.BUTTON}, {viewValue: 'Case Ref', value: DataType.CASE_REF}, + {viewValue: 'Process Ref', value: DataType.PROCESS_REF}, {viewValue: 'Date', value: DataType.DATE}, {viewValue: 'Datetime', value: DataType.DATETIME}, {viewValue: 'Enumeration', value: DataType.ENUMERATION}, @@ -76,7 +77,7 @@ export class DataDetailComponent implements OnDestroy { {viewValue: 'Number', value: DataType.NUMBER}, {viewValue: 'User', value: DataType.USER}, {viewValue: 'User List', value: DataType.USER_LIST}, - {viewValue: 'String Collection', value: 'stringCollection'}, + {viewValue: 'String Collection', value: DataType.STRING_COLLECTION}, {viewValue: 'Task Ref', value: DataType.TASK_REF}, {viewValue: 'Text', value: DataType.TEXT} ]; diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts b/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts deleted file mode 100644 index 539a492d0..000000000 --- a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {AfterViewInit, Component, Inject, Injector, Optional} from '@angular/core'; -import { - AbstractCaseRefProcessComponent, - DATA_FIELD_PORTAL_DATA, - DataFieldPortalData, - CaseRefField -} from '@netgrif/components-core'; -import {BuilderComponent} from '../../../builder/builder.component'; - -@Component({ - selector: 'nc-case-ref-process', - templateUrl: './case-ref-process.component.html', - styleUrls: ['./case-ref-process.component.scss'] -}) -export class CaseRefProcessComponent extends AbstractCaseRefProcessComponent implements AfterViewInit { - - constructor(injector: Injector, - @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { - super(injector, BuilderComponent, dataFieldPortalData) - } - - ngAfterViewInit() { - this.createPortal(); - } -} diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.html b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.html similarity index 57% rename from projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.html rename to projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.html index c6b5db803..c4fff2508 100644 --- a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.html +++ b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.scss similarity index 92% rename from projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss rename to projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.scss index 1ece6875d..1365d8028 100644 --- a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.scss +++ b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.scss @@ -1,5 +1,4 @@ .builder-height { - height: 500px; width: 100%; max-width: 100%; display: flex; diff --git a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.spec.ts b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts similarity index 96% rename from projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.spec.ts rename to projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts index e003f66dc..241f79948 100644 --- a/projects/netgrif-components/src/lib/data-fields/case-ref-field/case-ref-process/case-ref-process.component.spec.ts +++ b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CaseRefProcessComponent } from './case-ref-process.component'; +import { ProcessRefDefaultComponent } from './process-ref-default.component'; import {NavigationComponentModule} from '../../../navigation/navigation.module'; import { CaseRefField, @@ -20,7 +20,7 @@ import { import {Component} from '@angular/core'; describe('CaseRefDefaultComponent', () => { - let component: CaseRefProcessComponent; + let component: ProcessRefDefaultComponent; let fixture: ComponentFixture; beforeEach(async () => { diff --git a/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.ts b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.ts new file mode 100644 index 000000000..1f36014ac --- /dev/null +++ b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.ts @@ -0,0 +1,43 @@ +import {AfterViewInit, Component, Inject, Injector, OnDestroy, OnInit, Optional} from '@angular/core'; +import { + AbstractProcessRefDefaultComponent, + DATA_FIELD_PORTAL_DATA, + DataFieldPortalData, + ProcessRefField +} from '@netgrif/components-core'; +import {BuilderComponent} from '../../../builder/builder.component'; +import {Subscription} from 'rxjs'; + +@Component({ + selector: 'nc-process-ref-default', + templateUrl: './process-ref-default.component.html', + styleUrls: ['./process-ref-default.component.scss'] +}) +export class ProcessRefDefaultComponent extends AbstractProcessRefDefaultComponent implements OnInit, AfterViewInit, OnDestroy { + + public height: number; + protected subComp: Subscription; + + constructor(injector: Injector, + @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { + super(injector, BuilderComponent, dataFieldPortalData) + } + + ngOnInit() { + this.checkProperties(); + this.subComp = this.dataField.componentChange$().subscribe(() => this.checkProperties()); + } + + protected checkProperties() { + this.height = this.dataField.component?.properties?.height ? parseInt(this.dataField.component?.properties?.height) : 500; + } + + ngOnDestroy() { + super.ngOnDestroy(); + this.subComp.unsubscribe(); + } + + ngAfterViewInit() { + this.createPortal(); + } +} From b0c856771ed64e0c25fa217614a568c67dbab5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Thu, 30 Jul 2026 16:01:36 +0200 Subject: [PATCH 06/12] [NAE-2462] Builder component decomposition - rework of form builder - rework of info label - remove paper component --- .../src/lib/builder/builder.theme.scss | 16 ++++++ .../field-list/field-list.component.html | 2 +- .../form-builder/form-builder.component.html | 28 +++++------ .../form-builder/form-builder.component.scss | 49 ++++++++++++++---- .../form-builder/form-builder.component.ts | 24 ++++++--- .../form-builder/form-builder.module.ts | 2 - .../info-label/info-label.component.html | 45 +++++++++++------ .../info-label/info-label.component.scss | 50 +++++++++++++++---- .../info-label/info-label.component.ts | 4 +- .../form-builder/paper/paper.component.html | 5 -- .../form-builder/paper/paper.component.scss | 21 -------- .../paper/paper.component.spec.ts | 19 ------- .../form-builder/paper/paper.component.ts | 10 ---- 13 files changed, 155 insertions(+), 120 deletions(-) delete mode 100644 projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.html delete mode 100644 projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.scss delete mode 100644 projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.spec.ts delete mode 100644 projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.ts diff --git a/projects/netgrif-components/src/lib/builder/builder.theme.scss b/projects/netgrif-components/src/lib/builder/builder.theme.scss index 640a09fb0..81008946e 100644 --- a/projects/netgrif-components/src/lib/builder/builder.theme.scss +++ b/projects/netgrif-components/src/lib/builder/builder.theme.scss @@ -6,6 +6,18 @@ $warn: map-get($theme, warn); // BUILDER + + .single-task-fix { + .fix-datafield-expansion-panel { + .mat-expansion-panel:not(.mat-expanded) > .mat-expansion-panel-content { + height: 0 !important; + } + + .mat-expansion-panel.mat-expanded > .mat-expansion-panel-content { + height: 100% !important; + } + } + } .svg-active-stroke { stroke: mat.get-color-from-palette($accent, 500) !important; } @@ -15,6 +27,10 @@ } .builder-fix { + .info-label-icon { + color: mat.get-color-from-palette($primary, 500); + } + .app-mat-viewnav { background-color: mat.get-color-from-palette($primary, 600) !important; diff --git a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.html b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.html index 9311ad556..571e09c52 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.html +++ b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.html @@ -1,4 +1,4 @@ - +

Data fields

diff --git a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.html b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.html index 7ecad4aac..b014ee33a 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.html +++ b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.html @@ -1,21 +1,19 @@ - +
- +
- - - +
+ + +
- -
+
+
- +
diff --git a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss index a7b32570d..ee99237dc 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss +++ b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss @@ -1,23 +1,50 @@ -.app-mat-sidenav-content { - background: white; +:host { + display: flex; + flex-direction: row; + height: 100%; + overflow: hidden; } -.app-mat-sidenav { - width: 280px; - box-shadow: 6px 0 6px rgba(0, 0, 0, .1); - z-index: 2; +.form-builder-left-panel { + flex: 0 0 280px; + width: 280px; + height: 100%; + overflow-y: auto; + background: white; + box-shadow: 0 2px 4px #0d2d3c29, 0 4px 8px #546e7a29; + z-index: 1; } -.app-mat-sidenav-right { - box-shadow: -6px 0 6px rgba(0, 0, 0, 0.1); +.form-builder-right-panel { + flex: 0 0 auto; + max-width: 450px; + height: 100%; + overflow-y: auto; + background: white; + box-shadow: 0 2px 4px #0d2d3c29, 0 4px 8px #546e7a29; +} + +.form-builder-right-panel-row { + height: 100%; +} + +.form-builder-canvas { + flex: 1 1 auto; + width: 100%; + height: 100%; + background: lightgray; + padding: 12px; } .handle { width: 5px; - height: 100vh; + height: 100%; cursor: col-resize; } -.integrated-top { - top: 49px !important; +@media print { + body, .page { + margin: 0; + box-shadow: none; + } } diff --git a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.ts b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.ts index 467c27293..780a8107e 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.ts +++ b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component} from '@angular/core'; +import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core'; import {Router} from '@angular/router'; import {DataGroup, LayoutType} from '@netgrif/petriflow'; import {ModelerConfig} from '../modeler/modeler-config'; @@ -6,22 +6,25 @@ import {ModelerUtils} from '../modeler/modeler-utils'; import {SelectedTransitionService} from '../modeler/selected-transition.service'; import {ModelService} from '../modeler/services/model/model.service'; import {BuilderMode, BuilderModeService} from "../services/builder-mode.service"; -import {BuilderIntegrationService} from "../services/builder-integration.service"; +import {GridsterService} from './gridster/gridster.service'; @Component({ selector: 'nc-builder-form-builder', templateUrl: './form-builder.component.html', styleUrls: ['./form-builder.component.scss'] }) -export class FormBuilderComponent implements AfterViewInit { +export class FormBuilderComponent implements AfterViewInit, OnDestroy { title = 'form-builder'; width: number; + @ViewChild('rightPanel') rightPanel: ElementRef; + private rightPanelResizeObserver: ResizeObserver; + constructor(private router: Router, private modelService: ModelService, private transitionService: SelectedTransitionService, private _builderModeService: BuilderModeService, - protected _builderInterationService: BuilderIntegrationService) { + private _gridsterService: GridsterService) { if (!this.modelService.model) { this._builderModeService.mode = BuilderMode.MODELER; } @@ -35,6 +38,14 @@ export class FormBuilderComponent implements AfterViewInit { ngAfterViewInit(): void { ModelerUtils.clearSelection(); + this.rightPanelResizeObserver = new ResizeObserver(() => { + this._gridsterService.options?.api?.optionsChanged(); + }); + this.rightPanelResizeObserver.observe(this.rightPanel.nativeElement); + } + + ngOnDestroy(): void { + this.rightPanelResizeObserver?.disconnect(); } onResizeEvent(event: any): void { @@ -45,9 +56,6 @@ export class FormBuilderComponent implements AfterViewInit { } else { this.width = event.rectangle.width; } - } - - isIntegrated(): boolean { - return this._builderInterationService.isIntegrated; + this._gridsterService.options?.api?.optionsChanged(); } } diff --git a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.module.ts b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.module.ts index 990435d1a..4c7362513 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.module.ts +++ b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.module.ts @@ -13,7 +13,6 @@ import {FormBuilderComponent} from './form-builder.component'; import {GridsterDataFieldComponent} from './gridster/gridster-datafield/gridster-data-field.component'; import {GridsterComponent} from './gridster/gridster.component'; import {InfoLabelComponent} from './info-label/info-label.component'; -import {PaperComponent} from './paper/paper.component'; import {MaterialModule} from '@netgrif/components-core'; import {NgxMatDatetimePickerModule} from "@angular-material-components/datetime-picker"; import {NgxMatMomentModule} from "@angular-material-components/moment-adapter"; @@ -28,7 +27,6 @@ import { DataFieldsComponentModule } from "../../data-fields/data-fields.module" GridsterComponent, GridsterDataFieldComponent, InfoLabelComponent, - PaperComponent, ], exports: [ FormBuilderComponent diff --git a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.html b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.html index 0443dcdfa..138b8a0ac 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.html +++ b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.html @@ -1,19 +1,32 @@ - - - {{ selectedTransition.icon }} - - ID: {{ selectedTransition.id }} - - Label: {{ selectedTransition.label?.value }} - - - - + +
+
+ Process +
+
+ Task +
+
+ ID +
+
+
+
+ {{ model.icon }} + {{ model?.title?.value }} +
+
+ {{ selectedTransition.label?.value }} +
+
+ {{ selectedTransition.id }} +
+
+
+
You have no selected transition! -
+ - - +
diff --git a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss index 80bcdd742..25e038791 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss +++ b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss @@ -1,18 +1,48 @@ -mat-toolbar { - width: 43%; - align-content: center; - margin: auto; +.info-label-header { + display: flex; + align-items: center; + min-height: 40px; + padding: 10px 16px; + border-radius: 8px 8px 0 0; + background: white; + box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 2px 0px rgba(0, 0, 0, 0.12); } -.icon { - margin-right: 16px; +.info-label-panel { + display: flex; + align-items: center; + min-height: 53px; + padding: 0 24px; + border-radius: 0 0 8px 8px; + background: white; + box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 2px 0px rgba(0, 0, 0, 0.12); + margin-bottom: 8px; } -.margins-sides { - padding: 0 48px; +.info-label-group { + display: flex; + align-items: center; + flex: 1 1 0%; } -.action-button { - background: white; +.info-label-icon { margin-right: 8px; } + +.info-label-header-text { + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.1px; + color: #64748B; +} + +.info-label-title { + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.1px; +} + diff --git a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.ts b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.ts index c8a65c257..e5ecbc901 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.ts +++ b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.ts @@ -11,8 +11,7 @@ import {BuilderModeService, BuilderMode} from "../../services/builder-mode.servi }) export class InfoLabelComponent { - models: Array; - transitions: Array; + model: PetriNet; selectedTransition: Transition; constructor( @@ -20,6 +19,7 @@ export class InfoLabelComponent { private builderModeService: BuilderModeService, private transitionService: SelectedTransitionService, ) { + this.model = this.modelService.model; const id = this.transitionService.id; if (id) { this.selectedTransition = this.modelService.model.getTransition(id); diff --git a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.html b/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.html deleted file mode 100644 index 2336f64c9..000000000 --- a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.html +++ /dev/null @@ -1,5 +0,0 @@ -
- - - -
diff --git a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.scss b/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.scss deleted file mode 100644 index 711822bff..000000000 --- a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.scss +++ /dev/null @@ -1,21 +0,0 @@ -.page { - background: white; - display: block; - margin: 0 auto; - margin-bottom: 5px; - margin-top: 10px; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); - padding: 5px; -} - -.page[data-size="A4"] { - width: 21cm; - height: 29.7cm; -} - -@media print { - body, .page { - margin: 0; - box-shadow: none; - } -} diff --git a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.spec.ts b/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.spec.ts deleted file mode 100644 index 6b0196543..000000000 --- a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {NO_ERRORS_SCHEMA} from '@angular/core'; -import {TestBed} from '@angular/core/testing'; -import {PaperComponent} from './paper.component'; - -describe('PaperComponent', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [PaperComponent], - schemas: [NO_ERRORS_SCHEMA], - }); - }); - - afterEach(() => TestBed.resetTestingModule()); - - it('should create', () => { - const fixture = TestBed.createComponent(PaperComponent); - expect(fixture.componentInstance).toBeTruthy(); - }); -}); diff --git a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.ts b/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.ts deleted file mode 100644 index 647c49e05..000000000 --- a/projects/netgrif-components/src/lib/builder/form-builder/paper/paper.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Component} from '@angular/core'; - -@Component({ - selector: 'nc-builder-paper-component', - templateUrl: './paper.component.html', - styleUrls: ['./paper.component.scss'], -}) - -export class PaperComponent { -} From 6b663987e8504b3b448e04ad889c9e33fb77f0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Mon, 3 Aug 2026 14:42:21 +0200 Subject: [PATCH 07/12] [NAE-2462] Builder component decomposition - pr fixes --- .../services/field-converter.service.ts | 2 ++ .../field-list/field-list.component.ts | 2 +- .../form-builder/form-builder.component.scss | 6 ---- .../info-label/info-label.component.scss | 2 ++ .../modeler/edit-mode/edit-mode.service.ts | 29 ++++++++++++++----- .../services/canvas/canvas-mode-service.ts | 10 +++++-- .../simulation-mode.service.ts | 10 +++++-- .../process-ref-default.component.spec.ts | 2 +- 8 files changed, 43 insertions(+), 20 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts b/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts index 356e32116..35fc609fb 100644 --- a/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts +++ b/projects/netgrif-components-core/src/lib/task-content/services/field-converter.service.ts @@ -157,6 +157,8 @@ export class FieldConverterService { return FieldTypeResource.I18N; } else if (item instanceof CaseRefField) { return FieldTypeResource.CASE_REF; + } else if (item instanceof ProcessRefField) { + return FieldTypeResource.PROCESS_REF; } else if (item instanceof StringCollectionField) { return FieldTypeResource.STRING_COLLECTION; } diff --git a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts index 21576d8e6..9af259f6d 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts +++ b/projects/netgrif-components/src/lib/builder/form-builder/field-list/field-list.component.ts @@ -142,7 +142,7 @@ export class FieldListComponent implements OnInit, AfterViewInit { typeTitle(type: DataType): string { const name = (type as string)?.replace('_', ' ')?.replace(/([A-Z])/g, ' $1')?.toLowerCase(); - return name?.charAt(0)?.toUpperCase() + name?.slice(1); + return name ? name.charAt(0).toUpperCase() + name.slice(1) : ''; } addNewField(type: DataType, meta: any) { diff --git a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss index ee99237dc..edddde51f 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss +++ b/projects/netgrif-components/src/lib/builder/form-builder/form-builder.component.scss @@ -42,9 +42,3 @@ cursor: col-resize; } -@media print { - body, .page { - margin: 0; - box-shadow: none; - } -} diff --git a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss index 25e038791..79b3b4a22 100644 --- a/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss +++ b/projects/netgrif-components/src/lib/builder/form-builder/info-label/info-label.component.scss @@ -23,6 +23,7 @@ display: flex; align-items: center; flex: 1 1 0%; + min-width: 0; } .info-label-icon { @@ -39,6 +40,7 @@ } .info-label-title { + overflow-wrap: anywhere; font-style: normal; font-weight: 500; font-size: 14px; diff --git a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts index 7edc2b69d..f9b378d73 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/edit-mode/edit-mode.service.ts @@ -1,4 +1,4 @@ -import {Injectable, Injector, NgZone, Optional, Inject} from '@angular/core'; +import {Injectable, Injector, NgZone, Optional, Inject, OnDestroy} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {CanvasConfiguration} from '@netgrif/petri.svg'; @@ -6,7 +6,7 @@ import {Arc, ArcType, NodeElement, Place, Transition} from '@netgrif/petriflow'; import {PetriflowCanvasService, PetriflowNode} from '@netgrif/petriflow.svg'; import {InjectedTabData, NAE_TAB_DATA} from '@netgrif/components-core'; import {PanzoomOptions} from '@panzoom/panzoom'; -import {BehaviorSubject} from 'rxjs'; +import {BehaviorSubject, Subscription} from 'rxjs'; import {ChangedArc} from '../../dialogs/dialog-arc-edit/changed-arc'; import {ChangedTransition} from '../../dialogs/dialog-transition-edit/changed-transition'; import {TutorialService} from '../../tutorial/tutorial-service'; @@ -49,7 +49,7 @@ import {BuilderIntegrationService} from "../../services/builder-integration.serv import {LocalStorageService} from '../../services/local-storage.service'; @Injectable() -export class EditModeService extends CanvasModeService { +export class EditModeService extends CanvasModeService implements OnDestroy { public contextMenuItems: BehaviorSubject; // TODO: NAB-326 refactor @@ -64,6 +64,11 @@ export class EditModeService extends CanvasModeService { noBind: true }; + private _modelSubscription: Subscription; + private _placeSubscription: Subscription; + private _transitionSubscription: Subscription; + private _arcSubscription: Subscription; + constructor( _arcFactory: ArcFactory, modelService: ModelService, @@ -119,16 +124,24 @@ export class EditModeService extends CanvasModeService { ), this.switchTools ]; - this.modelService.model$().subscribe(_ => this.renderModel()); - this.modelService.placeChange.subscribe(value => this.updatePlace(value)); - this.modelService.transitionChange.subscribe(value => this.updateTransition(value)); - this.modelService.arcChange.subscribe(arc => this.updateArc(arc)); + this._modelSubscription = this.modelService.model$().subscribe(_ => this.renderModel()); + this._placeSubscription = this.modelService.placeChange.subscribe(value => this.updatePlace(value)); + this._transitionSubscription = this.modelService.transitionChange.subscribe(value => this.updateTransition(value)); + this._arcSubscription = this.modelService.arcChange.subscribe(arc => this.updateArc(arc)); this.canvasService.gridConfiguration.size = ModelerConfig.SIZE; CanvasConfiguration.SIZE = ModelerConfig.SIZE; this.contextMenuItems = new BehaviorSubject(undefined); this.switchTools.tools.forEach(t => t.bind()); } + ngOnDestroy(): void { + super.ngOnDestroy(); + this._modelSubscription?.unsubscribe(); + this._placeSubscription?.unsubscribe(); + this._transitionSubscription?.unsubscribe(); + this._arcSubscription?.unsubscribe(); + } + activate(tool?: CanvasTool) { if (tool === undefined) { tool = this.defaultTool; @@ -348,7 +361,7 @@ export class EditModeService extends CanvasModeService { const arcRatio = arcLengthOffset / arcLength; const finalX = intersect.x + xLineLength * arcRatio; const finalY = intersect.y + yLineLength * arcRatio; - arcLine.setAttributeNS(null, 'points', `${intersect.x},${intersect.y} ${isNaN(finalX) ? 0 : finalX},${isNaN(finalY) ? 0 : finalY}`); + arcLine.setAttributeNS(null, 'points', `${intersect.x},${intersect.y} ${isNaN(finalX) ? intersect.x : finalX},${isNaN(finalY) ? intersect.y : finalY}`); } // OTHER diff --git a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts index 73ccb6759..ba92d273b 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts @@ -15,11 +15,13 @@ import {CanvasElementCollection} from '../../edit-mode/domain/canvas-element-col import {CanvasPlace} from '../../edit-mode/domain/canvas-place'; import {CanvasTransition} from '../../edit-mode/domain/canvas-transition'; import {ModelService} from '../model/model.service'; +import {OnDestroy} from '@angular/core'; -export abstract class CanvasModeService extends ModeService { +export abstract class CanvasModeService extends ModeService implements OnDestroy { protected readonly _elements: CanvasElementCollection; protected _labelText: (n: NodeElement) => string; protected _multiplicityText: (a: CanvasArc) => string; + protected _tabSubscription: Subscription; protected constructor( protected _arcFactory: ArcFactory, @@ -31,7 +33,7 @@ export abstract class CanvasModeService extends ModeService { // the tab's content (and this service) stays alive while the tab is open but hidden (see NAE-326); // panzoom's initial view is computed from live element dimensions, which are wrong while hidden, // so re-sync it once the tab becomes visible again. - this._tabData?.tabSelected$?.subscribe(selected => { + this._tabSubscription = this._tabData?.tabSelected$?.subscribe(selected => { if (selected) { this.panzoom?.reset({animate: false}); } @@ -49,6 +51,10 @@ export abstract class CanvasModeService extends ModeService { this._elements = new CanvasElementCollection(); } + ngOnDestroy(): void { + this._tabSubscription?.unsubscribe(); + } + public renderModel(model: PetriNet = this.model): void { if (model === undefined) { return; diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts index 0130813eb..03cb7398e 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts @@ -26,7 +26,7 @@ import {SwitchLabelTool} from './tool/switch-label-tool'; import {TaskSimulationTool} from './tool/task-simulation.tool'; @Injectable() -export class SimulationModeService extends CanvasModeService { +export class SimulationModeService extends CanvasModeService implements OnDestroy { private _simulation: BasicSimulation; private _data: Map; @@ -48,6 +48,7 @@ export class SimulationModeService extends CanvasModeService { step: 0.2, noBind: true }; + private _modelSubscription: Subscription; constructor( protected _arcFactory: ArcFactory, @@ -101,7 +102,7 @@ export class SimulationModeService extends CanvasModeService { this.switchTools, ]; this.originalModel = new BehaviorSubject(this._modelService.model.clone()); - this.originalModel.subscribe(model => { + this._modelSubscription = this.originalModel.subscribe(model => { this.data = new Map(model.getArcs().filter(a => !!a.reference && !!model.getData(a.reference)) .map(a => { const data = model.getData(a.reference); @@ -115,6 +116,11 @@ export class SimulationModeService extends CanvasModeService { }); } + ngOnDestroy(): void { + super.ngOnDestroy(); + this._modelSubscription?.unsubscribe(); + } + renderModel(model: PetriNet = this.originalModel.value) { super.renderModel(model); } diff --git a/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts index 241f79948..ae14c1640 100644 --- a/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts +++ b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts @@ -19,7 +19,7 @@ import { } from '../../../navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-task-view/default-tabbed-task-view.component'; import {Component} from '@angular/core'; -describe('CaseRefDefaultComponent', () => { +describe('ProcessRefDefaultComponent', () => { let component: ProcessRefDefaultComponent; let fixture: ComponentFixture; From fe17c875ba86dcc8a0a8dd8fbf935a762ac8f250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Mon, 3 Aug 2026 14:58:45 +0200 Subject: [PATCH 08/12] [NAE-2462] Builder component decomposition - pr fixes and fix tests --- .../netgrif-components/src/lib/builder/builder.component.ts | 6 ++++-- .../builder/modeler/services/canvas/canvas-mode-service.ts | 1 + .../modeler/simulation-mode/simulation-mode.service.ts | 4 ++-- .../process-ref-default.component.spec.ts | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/projects/netgrif-components/src/lib/builder/builder.component.ts b/projects/netgrif-components/src/lib/builder/builder.component.ts index bcd2b4c95..8f403c3b6 100644 --- a/projects/netgrif-components/src/lib/builder/builder.component.ts +++ b/projects/netgrif-components/src/lib/builder/builder.component.ts @@ -147,10 +147,12 @@ export class BuilderComponent { }); } else if (dataFieldPortalData !== null && dataFieldPortalData?.dataField?.value?.length > 0) { this._builderIntegrationService.isIntegrated = true; - this._caseResourceService.getOneCase(dataFieldPortalData?.dataField?.value[0]).subscribe(processCase => { + this._caseResourceService.getOneCase(dataFieldPortalData?.dataField?.value[0]).subscribe({next: processCase => { this._builderIntegrationService.processCase = processCase; this.resolveIntegratedMode(); - }) + }, error: () => { + this.loading.off(); + }}) this._builderIntegrationService.reloadCase.subscribe(() => { this._caseResourceService.getOneCase(this._builderIntegrationService.processCase?.stringId).subscribe(processCase => { this._builderIntegrationService.processCase = processCase; diff --git a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts index ba92d273b..2781ba188 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts @@ -16,6 +16,7 @@ import {CanvasPlace} from '../../edit-mode/domain/canvas-place'; import {CanvasTransition} from '../../edit-mode/domain/canvas-transition'; import {ModelService} from '../model/model.service'; import {OnDestroy} from '@angular/core'; +import {Subscription} from 'rxjs'; export abstract class CanvasModeService extends ModeService implements OnDestroy { protected readonly _elements: CanvasElementCollection; diff --git a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts index 03cb7398e..829d812c0 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/simulation-mode/simulation-mode.service.ts @@ -1,10 +1,10 @@ -import {Injectable, Injector, NgZone, Optional, Inject} from '@angular/core'; +import {Injectable, Injector, NgZone, Optional, Inject, OnDestroy} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {Router} from '@angular/router'; import {Arc, BasicSimulation, ImportUtils, PetriNet, Place, Transition} from '@netgrif/petriflow'; import {PetriflowCanvasService} from '@netgrif/petriflow.svg'; import {PanzoomOptions} from '@panzoom/panzoom'; -import {BehaviorSubject} from 'rxjs'; +import {BehaviorSubject, Subscription} from 'rxjs'; import {InjectedTabData, NAE_TAB_DATA} from '@netgrif/components-core'; import {TutorialService} from '../../tutorial/tutorial-service'; import {ToolGroup} from '../control-panel/tools/tool-group'; diff --git a/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts index ae14c1640..db8e89bc4 100644 --- a/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts +++ b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ProcessRefDefaultComponent } from './process-ref-default.component'; import {NavigationComponentModule} from '../../../navigation/navigation.module'; import { - CaseRefField, + ProcessRefField, FilterField, FilterType, NAE_TAB_DATA, NAE_VIEW_ID_SEGMENT, @@ -85,10 +85,10 @@ describe('ProcessRefDefaultComponent', () => { @Component({ selector: 'nc-test-wrapper', - template: '' + template: '' }) class TestWrapperComponent { - field = new CaseRefField('', '', ['633c6187bb12a90925e0a17e'], { + field = new ProcessRefField('', '', ['633c6187bb12a90925e0a17e'], { required: true, optional: true, visible: true, From fb821f3279964caeee3f992a22bed74f51755c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Thu, 10 Sep 2026 14:07:27 +0200 Subject: [PATCH 09/12] [NAE-2462] Builder component decomposition - pr fixes and fix tests --- .../src/lib/builder/builder.component.ts | 14 ++++++++++---- .../modeler/services/canvas/canvas-mode-service.ts | 3 ++- .../services/builder-integration.service.ts | 13 +++++++++---- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/projects/netgrif-components/src/lib/builder/builder.component.ts b/projects/netgrif-components/src/lib/builder/builder.component.ts index 8f403c3b6..5fc7e9871 100644 --- a/projects/netgrif-components/src/lib/builder/builder.component.ts +++ b/projects/netgrif-components/src/lib/builder/builder.component.ts @@ -1,4 +1,4 @@ -import {Component, Inject, Optional, Type} from '@angular/core'; +import {Component, Inject, Optional, Type, OnDestroy} from '@angular/core'; import {BuilderMode, BuilderModeService} from "./services/builder-mode.service"; import {ModelService} from "./modeler/services/model/model.service"; import {ActivatedRoute, Router} from "@angular/router"; @@ -56,6 +56,7 @@ import {BuilderIntegrationService} from "./services/builder-integration.service" import {TaskModeService} from "./modeler/task-mode/task-mode.service"; import {LocalStorageService} from "./services/local-storage.service"; import {PetriflowCanvasService} from '@netgrif/petriflow.svg'; +import {Subscription} from "rxjs"; @Component({ selector: 'nc-builder', @@ -118,9 +119,10 @@ import {PetriflowCanvasService} from '@netgrif/petriflow.svg'; {provide: PetriflowCanvasService, useClass: PetriflowCanvasService as unknown as Type} ] }) -export class BuilderComponent { +export class BuilderComponent implements OnDestroy { public typeMode = BuilderMode; public loading: LoadingEmitter; + protected _reloadSub: Subscription; constructor(private modelService: ModelService, private router: Router, public dialog: MatDialog, @@ -138,7 +140,7 @@ export class BuilderComponent { this._builderIntegrationService.isIntegrated = true; this._builderIntegrationService.processCase = injectedTabData.processCase; this.resolveIntegratedMode(); - this._builderIntegrationService.reloadCase.subscribe(() => { + this._reloadSub = this._builderIntegrationService.reloadCase.subscribe(() => { this._caseResourceService.getOneCase(this._builderIntegrationService.processCase.stringId).subscribe(processCase => { this._builderIntegrationService.processCase = processCase; this.resolveIntegratedMode() @@ -153,7 +155,7 @@ export class BuilderComponent { }, error: () => { this.loading.off(); }}) - this._builderIntegrationService.reloadCase.subscribe(() => { + this._reloadSub = this._builderIntegrationService.reloadCase.subscribe(() => { this._caseResourceService.getOneCase(this._builderIntegrationService.processCase?.stringId).subscribe(processCase => { this._builderIntegrationService.processCase = processCase; this.resolveIntegratedMode() @@ -170,6 +172,10 @@ export class BuilderComponent { } } + ngOnDestroy(): void { + this._reloadSub?.unsubscribe(); + } + public onlyTaskMode() { return this._builderIntegrationService.isIntegrated && this._builderIntegrationService.onlyTaskView; } diff --git a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts index 2781ba188..430570b1f 100644 --- a/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts +++ b/projects/netgrif-components/src/lib/builder/modeler/services/canvas/canvas-mode-service.ts @@ -15,9 +15,10 @@ import {CanvasElementCollection} from '../../edit-mode/domain/canvas-element-col import {CanvasPlace} from '../../edit-mode/domain/canvas-place'; import {CanvasTransition} from '../../edit-mode/domain/canvas-transition'; import {ModelService} from '../model/model.service'; -import {OnDestroy} from '@angular/core'; +import {Injectable, OnDestroy} from '@angular/core'; import {Subscription} from 'rxjs'; +@Injectable() export abstract class CanvasModeService extends ModeService implements OnDestroy { protected readonly _elements: CanvasElementCollection; protected _labelText: (n: NodeElement) => string; diff --git a/projects/netgrif-components/src/lib/builder/services/builder-integration.service.ts b/projects/netgrif-components/src/lib/builder/services/builder-integration.service.ts index 49e906096..9e68cb5a7 100644 --- a/projects/netgrif-components/src/lib/builder/services/builder-integration.service.ts +++ b/projects/netgrif-components/src/lib/builder/services/builder-integration.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from "@angular/core"; +import {Injectable, OnDestroy} from "@angular/core"; import {HistoryService} from "../modeler/services/history/history.service"; import { Case, @@ -10,10 +10,10 @@ import { } from "@netgrif/components-core"; import {ExportService, PetriNet} from "@netgrif/petriflow"; import {HistoryChange} from "../modeler/services/history/history-change"; -import {Observable, of, Subject} from "rxjs"; +import {Observable, of, Subject, Subscription} from 'rxjs'; @Injectable() -export class BuilderIntegrationService { +export class BuilderIntegrationService implements OnDestroy { protected _isIntegrated: boolean; protected _processCase: Case; protected _editTaskId: string; @@ -23,6 +23,7 @@ export class BuilderIntegrationService { protected _reloadCase: Subject; protected _reloadModes: Subject; protected _loading: LoadingEmitter; + protected _historySub: Subscription; constructor(protected _historyService: HistoryService, protected _taskResourceService: TaskResourceService, @@ -32,7 +33,7 @@ export class BuilderIntegrationService { this._reloadCase = new Subject(); this._reloadModes = new Subject(); this._loading = new LoadingEmitter(); - this._historyService.historyChange.subscribe(history => { + this._historySub = this._historyService.historyChange.subscribe(history => { if (this._isIntegrated && this._editTaskId) { if (!this._isAssigned) { this._taskResourceService.assignTask(this._editTaskId).subscribe(result => { @@ -48,6 +49,10 @@ export class BuilderIntegrationService { }); } + ngOnDestroy(): any { + this._historySub?.unsubscribe(); + } + get isIntegrated(): boolean { return this._isIntegrated; } From b6eafa25308b3f63d0b9412de674f487b2e8ee75 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 22 Sep 2026 09:53:35 +0200 Subject: [PATCH 10/12] [NAE-2462] Builder component decomposition - fix tests --- .../abstract-filter-field-content.component.spec.ts | 3 +-- .../lib/search/search-service/search.service.spec.ts | 8 +++----- .../process-ref-default.component.spec.ts | 10 +++------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/projects/netgrif-components-core/src/lib/data-fields/filter-field/abstract-filter-field-content.component.spec.ts b/projects/netgrif-components-core/src/lib/data-fields/filter-field/abstract-filter-field-content.component.spec.ts index 3be135f9e..977855ffc 100644 --- a/projects/netgrif-components-core/src/lib/data-fields/filter-field/abstract-filter-field-content.component.spec.ts +++ b/projects/netgrif-components-core/src/lib/data-fields/filter-field/abstract-filter-field-content.component.spec.ts @@ -8,8 +8,7 @@ import {Subject} from 'rxjs'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {HttpClientTestingModule} from '@angular/common/http/testing'; import {FieldTypeResource} from "../../task-content/model/field-type-resource"; -import {DATA_FIELD_PORTAL_DATA} from "@netgrif/components-core"; -import {DataFieldPortalData} from "../models/data-field-portal-data-injection-token"; +import {DATA_FIELD_PORTAL_DATA, DataFieldPortalData} from "../models/data-field-portal-data-injection-token"; import {FormControl} from "@angular/forms"; describe('AbstractFilterFieldContentComponent', () => { diff --git a/projects/netgrif-components-core/src/lib/search/search-service/search.service.spec.ts b/projects/netgrif-components-core/src/lib/search/search-service/search.service.spec.ts index b95a5d331..0cf3bfeaf 100644 --- a/projects/netgrif-components-core/src/lib/search/search-service/search.service.spec.ts +++ b/projects/netgrif-components-core/src/lib/search/search-service/search.service.spec.ts @@ -20,16 +20,14 @@ import {of} from 'rxjs'; import {SimpleFilter} from '../../filter/models/simple-filter'; import {PetriNetRequestBody} from '../../resources/interface/petri-net-request-body'; import {TaskSearchRequestBody} from "../../filter/models/task-search-request-body"; -import { - MockUserService, - User, - UserService -} from "@netgrif/components-core"; import {Injectable} from "@angular/core"; import {PfqlVisitor} from "../../pfql/pfql-visitor"; import {AuthenticationModule} from "../../authentication/authentication.module"; import {NAE_IGNORE_NETS_ON_AUTOCOMPLETE_CATEGORY} from "../category-factory/search-categories-injection-token"; import {RouterTestingModule} from "@angular/router/testing"; +import {UserService} from "../../user/services/user.service"; +import {MockUserService} from "../../utility/tests/mocks/mock-user.service"; +import {User} from "../../user/models/user"; describe('SearchService', () => { let service: SearchService; diff --git a/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts index db8e89bc4..ac8e9e39f 100644 --- a/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts +++ b/projects/netgrif-components/src/lib/data-fields/process-ref/process-ref-default/process-ref-default.component.spec.ts @@ -4,12 +4,12 @@ import { ProcessRefDefaultComponent } from './process-ref-default.component'; import {NavigationComponentModule} from '../../../navigation/navigation.module'; import { ProcessRefField, - FilterField, FilterType, + FilterField, NAE_TAB_DATA, NAE_VIEW_ID_SEGMENT, OverflowService, TestMockDependenciesModule, - GroupNavigationConstants, + GroupNavigationConstants, FieldTypeResource, } from '@netgrif/components-core'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {RouterTestingModule} from '@angular/router/testing'; @@ -49,11 +49,7 @@ describe('ProcessRefDefaultComponent', () => { `${GroupNavigationConstants.ITEM_FIELD_CASE_FILTER}`, '', '', - { - filterType: FilterType.CASE, - predicateMetadata: [], - searchCategories: [] - }, + FieldTypeResource.CASE_FILTER, [], {visible: true}, '', From 05a41b0866b8e34876a0f3c3f95e08670f6a12f6 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 22 Sep 2026 10:22:07 +0200 Subject: [PATCH 11/12] [NAE-2462] Builder component decomposition - add error handling when fetching process case --- .../src/lib/builder/builder.component.ts | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/projects/netgrif-components/src/lib/builder/builder.component.ts b/projects/netgrif-components/src/lib/builder/builder.component.ts index 5fc7e9871..711362731 100644 --- a/projects/netgrif-components/src/lib/builder/builder.component.ts +++ b/projects/netgrif-components/src/lib/builder/builder.component.ts @@ -5,7 +5,16 @@ import {ActivatedRoute, Router} from "@angular/router"; import {MatDialog} from "@angular/material/dialog"; import {HttpClient} from "@angular/common/http"; import {HistoryService} from "./modeler/services/history/history.service"; -import {CaseResourceService, LoadingEmitter, NAE_TAB_DATA, ImmediateData, DATA_FIELD_PORTAL_DATA, CaseRefField, DataFieldPortalData} from "@netgrif/components-core"; +import { + CaseResourceService, + LoadingEmitter, + NAE_TAB_DATA, + ImmediateData, + DATA_FIELD_PORTAL_DATA, + CaseRefField, + DataFieldPortalData, + LoggerService +} from "@netgrif/components-core"; import {InjectedTabbedBuilderViewData} from "./injected-builder-data"; import {FieldListService} from './form-builder/field-list/field-list.service'; import {GridsterService} from './form-builder/gridster/gridster.service'; @@ -133,6 +142,7 @@ export class BuilderComponent implements OnDestroy { public builderModeService: BuilderModeService, protected _caseResourceService: CaseResourceService, protected _builderIntegrationService: BuilderIntegrationService, + protected _logger: LoggerService, @Optional() @Inject(NAE_TAB_DATA) injectedTabData: InjectedTabbedBuilderViewData, @Optional() @Inject(DATA_FIELD_PORTAL_DATA) dataFieldPortalData: DataFieldPortalData) { this.loading = new LoadingEmitter(true); @@ -141,10 +151,15 @@ export class BuilderComponent implements OnDestroy { this._builderIntegrationService.processCase = injectedTabData.processCase; this.resolveIntegratedMode(); this._reloadSub = this._builderIntegrationService.reloadCase.subscribe(() => { - this._caseResourceService.getOneCase(this._builderIntegrationService.processCase.stringId).subscribe(processCase => { - this._builderIntegrationService.processCase = processCase; - this.resolveIntegratedMode() - this._builderIntegrationService.reloadModes = true; + this._caseResourceService.getOneCase(this._builderIntegrationService.processCase.stringId).subscribe({ + next: processCase => { + this._builderIntegrationService.processCase = processCase; + this.resolveIntegratedMode() + this._builderIntegrationService.reloadModes = true; + }, + error: error => { + this._logger.error('Process case reload failed', error.message); + } }) }); } else if (dataFieldPortalData !== null && dataFieldPortalData?.dataField?.value?.length > 0) { From 72975467c48eb633f3521c8e6043ff194a743dcb Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 22 Sep 2026 10:51:23 +0200 Subject: [PATCH 12/12] [NAE-2462] Builder component decomposition - fix ncc build order --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e836edb26..091300928 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "lint": "ng lint", "e2e": "ng e2e", "ncc:clean": "node projects/netgrif-components-core/src/scripts/delete-local-build-link.js", - "ncc:build": "npm run ncc:clean && ng build netgrif-components-core --configuration production && npm run ncc:build-schematics && npm run pfql:build-grammar", + "ncc:build": "npm run ncc:clean && npm run pfql:build-grammar && ng build netgrif-components-core --configuration production && npm run ncc:build-schematics", "ncc:local-build": "npm run ncc:build && npm i ./dist/netgrif-components-core --save-optional --legacy-peer-deps", "ncc:build-schematics": "node projects/netgrif-components-core/src/scripts/build-schematics.js", "ncc:lint": "ng lint netgrif-components-core",