From 4fbe3b2e45aa352db4a69a24fd0df80ca9fdf43f Mon Sep 17 00:00:00 2001 From: Alexandre Caron Date: Fri, 17 Oct 2025 09:01:52 -0400 Subject: [PATCH 1/4] feat: migrate to signal and control-flow --- src/app/app.component.html | 13 +- .../expansion-panel-button.component.html | 4 +- .../expansion-panel-button.component.ts | 8 +- .../expansion-panel-header.component.html | 8 +- .../expansion-panel-header.component.ts | 12 +- .../expansion-panel.component.html | 9 +- .../expansion-panel.component.ts | 42 +--- src/app/pages/portal/portal.component.html | 135 +++++------- src/app/pages/portal/portal.component.ts | 72 +++---- .../portal/sidenav/sidenav.component.html | 14 +- .../pages/portal/sidenav/sidenav.component.ts | 46 ++-- .../toast-panel-for-expansion.component.html | 4 +- .../toast-panel-for-expansion.component.ts | 28 +-- .../toast-panel/toast-panel.component.html | 38 ++-- .../toast-panel/toast-panel.component.ts | 202 ++++++++---------- .../welcome-window.component.html | 2 +- 16 files changed, 243 insertions(+), 394 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index b9705716e..b1c1f3b86 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,15 +1,14 @@ - + @if (authConfig && authConfig.url) { - + } @if (hasHeader) { - + } - + /> @if (hasFooter) { - + } diff --git a/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.html b/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.html index 953d16a3a..23b5115c5 100644 --- a/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.html +++ b/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.html @@ -1,10 +1,10 @@ diff --git a/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.ts b/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.ts index 41baf34e8..51dd85492 100644 --- a/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.ts +++ b/src/app/pages/portal/expansion-panel/expansion-panel-button/expansion-panel-button.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, model } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; @@ -17,11 +17,9 @@ import { IgoLanguageModule } from '@igo2/core/language'; styleUrls: ['./expansion-panel-button.component.scss'] }) export class ExpansionPanelButtonComponent { - @Input() expanded: boolean; - - @Output() expand = new EventEmitter(); + readonly expanded = model(undefined); onToggleClick(): void { - this.expand.emit(!this.expanded); + this.expanded.update((expanded) => !expanded); } } diff --git a/src/app/pages/portal/expansion-panel/expansion-panel-header.component.html b/src/app/pages/portal/expansion-panel/expansion-panel-header.component.html index e82bd2ce5..8bfdff43a 100644 --- a/src/app/pages/portal/expansion-panel/expansion-panel-header.component.html +++ b/src/app/pages/portal/expansion-panel/expansion-panel-header.component.html @@ -1,9 +1,7 @@
- +
- +
diff --git a/src/app/pages/portal/expansion-panel/expansion-panel-header.component.ts b/src/app/pages/portal/expansion-panel/expansion-panel-header.component.ts index 1f65049d1..e2b6efbe1 100644 --- a/src/app/pages/portal/expansion-panel/expansion-panel-header.component.ts +++ b/src/app/pages/portal/expansion-panel/expansion-panel-header.component.ts @@ -1,10 +1,8 @@ import { ChangeDetectionStrategy, Component, - EventEmitter, HostBinding, - Input, - Output + model } from '@angular/core'; import { ExpansionPanelButtonComponent } from './expansion-panel-button/expansion-panel-button.component'; @@ -17,16 +15,14 @@ import { ExpansionPanelButtonComponent } from './expansion-panel-button/expansio imports: [ExpansionPanelButtonComponent] }) export class ExpansionPanelHeaderComponent { - @Input() expanded: boolean; - - @Output() expandedChange = new EventEmitter(); + readonly expanded = model(undefined); @HostBinding('class.app-expansion-panel-header-expanded') get hasExpandedClass() { - return this.expanded; + return this.expanded(); } handleClose(): void { - this.expandedChange.emit(false); + this.expanded.set(false); } } diff --git a/src/app/pages/portal/expansion-panel/expansion-panel.component.html b/src/app/pages/portal/expansion-panel/expansion-panel.component.html index 47849da73..79f6e628b 100644 --- a/src/app/pages/portal/expansion-panel/expansion-panel.component.html +++ b/src/app/pages/portal/expansion-panel/expansion-panel.component.html @@ -1,17 +1,16 @@
- - + - +
- +
diff --git a/src/app/pages/portal/expansion-panel/expansion-panel.component.ts b/src/app/pages/portal/expansion-panel/expansion-panel.component.ts index eec7c4cb1..f766c4e1e 100644 --- a/src/app/pages/portal/expansion-panel/expansion-panel.component.ts +++ b/src/app/pages/portal/expansion-panel/expansion-panel.component.ts @@ -1,10 +1,9 @@ import { ChangeDetectionStrategy, Component, - EventEmitter, HostBinding, - Input, - Output + input, + model } from '@angular/core'; import { BackdropComponent } from '@igo2/common/backdrop'; @@ -21,45 +20,22 @@ import { showContent } from './expansion-panel.animations'; imports: [BackdropComponent, ExpansionPanelHeaderComponent] }) export class ExpansionPanelComponent { - @Input() - get expanded(): boolean { - return this._expanded; - } - set expanded(value: boolean) { - if (value === this._expanded) { - return; - } - - this._expanded = value; - this.expandedChange.emit(this._expanded); - } - private _expanded: boolean; - - @Input() maximized = false; - - @Input() - get backdropShown(): boolean { - return this._backdropShown; - } - set backdropShown(value: boolean) { - this._backdropShown = value; - } - private _backdropShown: boolean; - - @Output() expandedChange = new EventEmitter(); + readonly expanded = model(false); + readonly maximized = input(false); + readonly backdropShown = model(false); @HostBinding('class.app-expansion-panel-expanded') get hasExpandedClass() { - return this.expanded; + return this.expanded(); } @HostBinding('class.app-expansion-panel-expanded-maximized') get hasExpandedFullClass() { - return this.expanded && this.maximized; + return this.expanded() && this.maximized(); } onBackdropClick() { - this.expanded = false; - this.backdropShown = false; + this.expanded.set(false); + this.backdropShown.set(false); } } diff --git a/src/app/pages/portal/portal.component.html b/src/app/pages/portal/portal.component.html index 817ed7b53..f4d05d9e2 100644 --- a/src/app/pages/portal/portal.component.html +++ b/src/app/pages/portal/portal.component.html @@ -1,9 +1,8 @@ - + /> - - + />
- + />
@if (appConfig.offlineButton) { - - + /> } @if (appConfig.homeExtentButton) { - - + /> } @if (appConfig.wakeLockApiButton) { - + } @if (hasGeolocateButton) { - - + /> } @if (!isTouchScreen) { - - + /> }
@if (authService.hasAuthService) { - - + /> }
- - + /> @if ( appConfig.hasExpansionPanel && (workspaceState.workspaceEnabled$ | async) && - !expansionPanelExpanded + !workspaceState.expanded() ) { - + }
- - + /> +
- - + />
-@if (expansionPanelExpanded) { +@if (workspaceState.expanded()) { - - + /> @if (selectedWorkspace$ | async; as workspace) {
@@ -285,8 +258,7 @@ } @if (workspace.actionStore) { - - + /> } @if (workspaceVisibility()) {
} @@ -342,15 +311,13 @@ workspace.meta.tableTemplate && workspaceVisibility() ) { - - + /> } {{ workspaceNotAvailableMessage | translate }} } @@ -372,17 +339,14 @@
@if (selectedWorkspace$ | async; as workspace) { - - + /> } @if ((queryStore.empty$ | async) !== true) { - - + /> } - - + /> diff --git a/src/app/pages/portal/portal.component.ts b/src/app/pages/portal/portal.component.ts index 1204cc55e..ba30dcd1c 100644 --- a/src/app/pages/portal/portal.component.ts +++ b/src/app/pages/portal/portal.component.ts @@ -1,12 +1,14 @@ import { AsyncPipe, NgClass } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild, + effect, inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; @@ -174,6 +176,7 @@ import { WelcomeWindowService } from './welcome-window/welcome-window.service'; selector: 'app-portal', templateUrl: './portal.component.html', styleUrls: ['./portal.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, animations: [ expansionPanelAnimation(), toastPanelAnimation(), @@ -371,18 +374,6 @@ export class PortalComponent implements OnInit, OnDestroy { ); } - get expansionPanelExpanded(): boolean { - return this.workspaceState.workspacePanelExpanded; - } - set expansionPanelExpanded(value: boolean) { - this.workspaceState.workspacePanelExpanded = value; - if (value === true) { - this.map.viewController.setPadding({ bottom: 280 }); - } else { - this.map.viewController.setPadding({ bottom: 0 }); - } - } - get contextUri(): string { return this.contextState.context$?.getValue() ? this.contextState.context$.getValue().uri @@ -390,7 +381,7 @@ export class PortalComponent implements OnInit, OnDestroy { } get expansionPanelBackdropShown(): boolean { - return this.expansionPanelExpanded && this.toastPanelForExpansionOpened; + return this.workspaceState.expanded() && this.toastPanelForExpansionOpened; } get actionbarMode(): ActionbarMode { @@ -434,6 +425,14 @@ export class PortalComponent implements OnInit, OnDestroy { } constructor() { + effect(() => { + const expanded = this.workspaceState.expanded(); + if (expanded === true) { + this.map.viewController.setPadding({ bottom: 280 }); + } else { + this.map.viewController.setPadding({ bottom: 0 }); + } + }); this.analyticsListenerService.listen(); this.handleAppConfigs(); this.storageService.set('version', getAppVersion(this.configService)); @@ -505,7 +504,7 @@ export class PortalComponent implements OnInit, OnDestroy { currentCnt !== prevCnt && this.isMobile() && this.appConfig.hasExpansionPanel && - this.expansionPanelExpanded && + this.workspaceState.expanded() && this.toastPanelOpened ) { this.toastPanelOpened = false; @@ -538,7 +537,7 @@ export class PortalComponent implements OnInit, OnDestroy { } this.workspaceState.workspaceEnabled$.next(workspaceEmpty ? false : true); if (workspaceEmpty) { - this.expansionPanelExpanded = false; + this.workspaceState.expanded.set(false); } this.updateMapBrowserClass(); }); @@ -553,7 +552,7 @@ export class PortalComponent implements OnInit, OnDestroy { (activeWks: WfsWorkspace | FeatureWorkspace | EditionWorkspace) => { if (activeWks) { this.selectedWorkspace$.next(activeWks); - this.expansionPanelExpanded = true; + this.workspaceState.expanded.set(true); if ( activeWks.layer.options.workspace?.pageSize && @@ -571,7 +570,7 @@ export class PortalComponent implements OnInit, OnDestroy { }; } } else { - this.expansionPanelExpanded = false; + this.workspaceState.expanded.set(false); } } ); @@ -989,17 +988,13 @@ export class PortalComponent implements OnInit, OnDestroy { if ( this.isMobile() && this.appConfig.hasExpansionPanel && - this.expansionPanelExpanded && + this.workspaceState.expanded() && this.toastPanelOpened ) { - this.expansionPanelExpanded = false; + this.workspaceState.expanded.set(false); } } - handleToggleExpansionPanel(isExpanded: boolean): void { - this.expansionPanelExpanded = isExpanded; - } - public onClearSearch() { this.map.searchResultsOverlay.clear(); this.searchStore.clear(); @@ -1075,7 +1070,7 @@ export class PortalComponent implements OnInit, OnDestroy { this.mapBrowser.nativeElement.classList.remove('has-expansion-panel'); } - if (this.appConfig.hasExpansionPanel && this.expansionPanelExpanded) { + if (this.appConfig.hasExpansionPanel && this.workspaceState.expanded()) { if (this.workspaceState.workspaceMaximize$.value) { this.mapBrowser.nativeElement.classList.add( 'expansion-offset-maximized' @@ -1111,7 +1106,7 @@ export class PortalComponent implements OnInit, OnDestroy { ); } - if (!this.toastPanelOpened && header && !this.expansionPanelExpanded) { + if (!this.toastPanelOpened && header && !this.workspaceState.expanded()) { this.mapBrowser.nativeElement.classList.add('toast-offset-scale-line'); } else { this.mapBrowser.nativeElement.classList.remove('toast-offset-scale-line'); @@ -1121,7 +1116,7 @@ export class PortalComponent implements OnInit, OnDestroy { !this.toastPanelOpened && header && (this.isMobile() || this.isTablet() || this.sidenavOpened) && - !this.expansionPanelExpanded + !this.workspaceState.expanded() ) { this.mapBrowser.nativeElement.classList.add('toast-offset-attribution'); } else { @@ -1159,20 +1154,21 @@ export class PortalComponent implements OnInit, OnDestroy { } getExpansionPanelStatus() { + const isExpanded = this.workspaceState.expanded(); if (this.sidenavOpened === false) { - if (this.expansionPanelExpanded === true) { + if (isExpanded === true) { return 'full'; } return 'notTriggered'; } if (this.sidenavOpened === true && this.isMobile() === false) { - if (this.expansionPanelExpanded === true) { + if (isExpanded === true) { return 'reduced'; } return 'reducedNotTriggered'; } if (this.sidenavOpened === true && this.isMobile() === true) { - if (this.expansionPanelExpanded === true) { + if (isExpanded === true) { return 'mobile'; } else { return 'notVisible'; @@ -1182,7 +1178,7 @@ export class PortalComponent implements OnInit, OnDestroy { getToastPanelOffsetY() { let status = 'noExpansion'; - if (this.expansionPanelExpanded) { + if (this.workspaceState.expanded()) { if (this.workspaceState.workspaceMaximize$.value) { if (this.toastPanelOpened) { status = 'expansionMaximizedAndToastOpened'; @@ -1203,9 +1199,10 @@ export class PortalComponent implements OnInit, OnDestroy { } getToastPanelStatus() { + const isExpanded = this.workspaceState.expanded(); if (this.isMobile() === true && this.toastPanelOpened === false) { if (this.sidenavOpened === false) { - if (this.expansionPanelExpanded === false) { + if (!isExpanded) { if (this.queryState.store.entities$.value.length > 0) { return 'low'; } @@ -1214,7 +1211,7 @@ export class PortalComponent implements OnInit, OnDestroy { } } getControlsOffsetY() { - return this.expansionPanelExpanded + return this.workspaceState.expanded() ? this.workspaceState.workspaceMaximize$.value ? 'firstRowFromBottom-expanded-maximized' : 'firstRowFromBottom-expanded' @@ -1222,10 +1219,11 @@ export class PortalComponent implements OnInit, OnDestroy { } getBaselayersSwitcherStatus() { + const isExpanded = this.workspaceState.expanded(); let status; if (this.isMobile()) { if (this.workspaceState.workspaceEnabled$.value) { - if (this.expansionPanelExpanded === false) { + if (!isExpanded) { if (this.queryState.store.entities$.value.length === 0) { status = 'secondRowFromBottom'; } else { @@ -1247,7 +1245,7 @@ export class PortalComponent implements OnInit, OnDestroy { } } else { if (this.workspaceState.workspaceEnabled$.value) { - if (this.expansionPanelExpanded) { + if (this.workspaceState.expanded()) { if (this.workspaceState.workspaceMaximize$.value) { status = 'firstRowFromBottom-expanded-maximized'; } else { @@ -1445,7 +1443,7 @@ export class PortalComponent implements OnInit, OnDestroy { ) .subscribe((c) => { if (c >= 1) { - this.directionState.zoomToActiveRoute$.next(); + this.directionState.zoomToActiveRoute.set(true); } }); // select the active route by url controls @@ -1462,7 +1460,7 @@ export class PortalComponent implements OnInit, OnDestroy { this.directionState.routesFeatureStore.entities$.value[ resultSelection ].properties.active = true; - this.directionState.zoomToActiveRoute$.next(); + this.directionState.zoomToActiveRoute.set(true); } }); } @@ -1561,7 +1559,7 @@ export class PortalComponent implements OnInit, OnDestroy { if ( activeWks.active && activeWks.inResolutionRange$.value && - this.workspaceState.workspacePanelExpanded + this.workspaceState.expanded ) { return true; } diff --git a/src/app/pages/portal/sidenav/sidenav.component.html b/src/app/pages/portal/sidenav/sidenav.component.html index 305f739ac..2e8904f94 100644 --- a/src/app/pages/portal/sidenav/sidenav.component.html +++ b/src/app/pages/portal/sidenav/sidenav.component.html @@ -11,8 +11,7 @@ [ngClass]="{ toolActivated: toolbox.activeTool$ | async }" > @if (toolbox.activeTool$ | async) { - - + } @@ -29,20 +28,17 @@ } - - + /> - + /> diff --git a/src/app/pages/portal/sidenav/sidenav.component.ts b/src/app/pages/portal/sidenav/sidenav.component.ts index 8efb69d4b..5fb5cd4a3 100644 --- a/src/app/pages/portal/sidenav/sidenav.component.ts +++ b/src/app/pages/portal/sidenav/sidenav.component.ts @@ -2,12 +2,12 @@ import { AsyncPipe, NgClass } from '@angular/common'; import { ChangeDetectionStrategy, Component, - EventEmitter, - Input, OnDestroy, OnInit, - Output, - inject + inject, + input, + model, + output } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -55,32 +55,12 @@ export class SidenavComponent implements OnInit, OnDestroy { private activeTool$$: Subscription; - @Input() - get map(): IgoMap { - return this._map; - } - set map(value: IgoMap) { - this._map = value; - } - private _map: IgoMap; - - @Input() - get opened(): boolean { - return this._opened; - } - set opened(value: boolean) { - if (value === this._opened) { - return; - } + readonly map = input(); + readonly opened = model(); - this._opened = value; - this.openedChange.emit(this._opened); - } - private _opened: boolean; - - @Output() openedChange = new EventEmitter(); - @Output() toolChange = new EventEmitter(); - @Output() widthChange = new EventEmitter(); + readonly openedChange = output(); + readonly toolChange = output(); + readonly widthChange = output(); get toolbox(): Toolbox { return this.toolState.toolbox; @@ -107,11 +87,9 @@ export class SidenavComponent implements OnInit, OnDestroy { tool.name === 'activeTimeFilter' || tool.name === 'activeOgcFilter' ) { - for (const layer of this.map.layerController.layersFlattened) { - if ( - isLayerItem(layer) && - this.map.layerController.isSelected(layer) - ) { + const layerController = this.map().layerController; + for (const layer of layerController.layersFlattened) { + if (isLayerItem(layer) && layerController.isSelected(layer)) { this.title$.next(layer.title); } } diff --git a/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.html b/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.html index 5c22c9778..6b7507741 100644 --- a/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.html +++ b/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.html @@ -1,5 +1,5 @@ - +
- +
diff --git a/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.ts b/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.ts index 025c8b14d..fd03402db 100644 --- a/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.ts +++ b/src/app/pages/portal/toast-panel-for-expansion/toast-panel-for-expansion.component.ts @@ -1,10 +1,8 @@ import { ChangeDetectionStrategy, Component, - EventEmitter, HostBinding, - Input, - Output + input } from '@angular/core'; import { PanelComponent } from '@igo2/common/panel'; @@ -20,32 +18,18 @@ import { showContent } from './toast-panel-for-expansion.animations'; imports: [PanelComponent] }) export class ToastPanelForExpansionComponent { - @Input() - set opened(value: boolean) { - if (value === this._opened) { - return; - } - this._opened = value; - this.openedChange.emit(this._opened); - } - get opened(): boolean { - return this._opened; - } - private _opened: boolean; - - @Input() title: string; - - @Input() withHeader: boolean; + readonly opened = input(false); + readonly title = input(undefined); - @Output() openedChange = new EventEmitter(); + readonly withHeader = input(undefined); @HostBinding('class.toast-panel-for-expansion-opened') get hasOpenedClass() { - return this.opened; + return this.opened(); } @HostBinding('style.visibility') get displayStyle() { - return this.withHeader || this.opened ? 'visible' : 'hidden'; + return this.withHeader() || this.opened() ? 'visible' : 'hidden'; } } diff --git a/src/app/pages/portal/toast-panel/toast-panel.component.html b/src/app/pages/portal/toast-panel/toast-panel.component.html index bd7d06b01..bf4b13ebe 100644 --- a/src/app/pages/portal/toast-panel/toast-panel.component.html +++ b/src/app/pages/portal/toast-panel/toast-panel.component.html @@ -12,8 +12,7 @@ tabindex="0" (keydown)="handleKeyboardEvent($event)" > - - + /> @if (resultSelected$.value) { } @if (workspace.actionStore) { - + /> } @if (workspaceVisibility()) { } - + />
- + /> diff --git a/src/app/pages/portal/toast-panel/toast-panel.component.html b/src/app/pages/portal/toast-panel/toast-panel.component.html index bf4b13ebe..985d71a32 100644 --- a/src/app/pages/portal/toast-panel/toast-panel.component.html +++ b/src/app/pages/portal/toast-panel/toast-panel.component.html @@ -12,7 +12,8 @@ tabindex="0" (keydown)="handleKeyboardEvent($event)" > - + /> @if (resultSelected$.value) {