@arsedizioni/ars-utils 21.2.220 → 21.2.221

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"arsedizioni-ars-utils-ui.oauth.mjs","sources":["../../../projects/ars-utils/ui.oauth/ui/definitions.ts","../../../projects/ars-utils/ui.oauth/ui/ui.module.ts","../../../projects/ars-utils/ui.oauth/ui/components/login/login-oauth.component.ts","../../../projects/ars-utils/ui.oauth/ui/components/login/login-oauth.component.html","../../../projects/ars-utils/ui.oauth/ui/components/login-ok/ms/login-oauth-ok-ms.component.ts","../../../projects/ars-utils/ui.oauth/ui/components/login-ok/ms/login-oauth-ok-ms.component.html","../../../projects/ars-utils/ui.oauth/public_api.ts","../../../projects/ars-utils/ui.oauth/arsedizioni-ars-utils-ui.oauth.ts"],"sourcesContent":["export enum LoginOAuthType {\r\n None = 0,\r\n Microsoft = 1,\r\n Google = 2\r\n}\r\n\r\nexport interface LoginOAuthResult {\r\n type: number,\r\n token: string\r\n}","import { NgModule } from \"@angular/core\";\r\n\r\n\r\n@NgModule()\r\nexport class ArsUIOAuthModule { }\r\n\r\nexport * from \"./definitions\";\r\n\r\n\r\n","import { ChangeDetectionStrategy, Component, DestroyRef, NgZone, inject, input, output, signal } from '@angular/core';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\nimport { MatButtonModule } from '@angular/material/button';\r\nimport { EnvironmentService } from '@arsedizioni/ars-utils/core';\r\nimport { DialogService } from '@arsedizioni/ars-utils/ui';\r\nimport { MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalBroadcastService, MsalGuardConfiguration, MsalInterceptorConfiguration, MsalService } from '@azure/msal-angular';\r\nimport { AuthenticationResult, BrowserCacheLocation, EventMessage, EventType, IPublicClientApplication, InteractionType, PublicClientApplication } from '@azure/msal-browser';\r\nimport { filter } from 'rxjs';\r\nimport { LoginOAuthResult } from '../../definitions';\r\n\r\nexport function MSALInstanceFactory(environment: EnvironmentService): IPublicClientApplication {\r\n return new PublicClientApplication({\r\n auth: {\r\n clientId: \"6b2c080f-6ab0-4511-a9df-0bce69db5833\",\r\n authority: \"https://login.microsoftonline.com/common\",\r\n redirectUri: environment.appLoginRedirectUri,\r\n },\r\n cache: {\r\n cacheLocation: BrowserCacheLocation.LocalStorage\r\n }\r\n });\r\n}\r\n\r\nexport function MSALInterceptorConfigFactory(environment: EnvironmentService): MsalInterceptorConfiguration {\r\n\r\n return {\r\n interactionType: InteractionType.Popup,\r\n protectedResourceMap: new Map([\r\n [environment.appServiceLoginUri, [\"api://6b2c080f-6ab0-4511-a9df-0bce69db5833/AngularSPAAuthScope\"]],\r\n [\"https://graph.microsoft.com/v2.0/me\", [\"user.read\"]],\r\n ])\r\n };\r\n}\r\n\r\nexport function MSALGuardConfigFactory(): MsalGuardConfiguration {\r\n return {\r\n interactionType: InteractionType.Popup,\r\n authRequest: {\r\n scopes: [\"user.read\"]\r\n }\r\n };\r\n}\r\n\r\n@Component({\r\n selector: 'login-oauth',\r\n templateUrl: './login-oauth.component.html',\r\n styleUrls: ['./login-oauth.component.scss'],\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n providers: [\r\n {\r\n provide: MSAL_INSTANCE,\r\n deps: [EnvironmentService],\r\n useFactory: MSALInstanceFactory\r\n },\r\n {\r\n provide: MSAL_GUARD_CONFIG,\r\n useFactory: MSALGuardConfigFactory\r\n },\r\n {\r\n provide: MSAL_INTERCEPTOR_CONFIG,\r\n deps: [EnvironmentService],\r\n useFactory: MSALInterceptorConfigFactory\r\n },\r\n MsalService,\r\n MsalBroadcastService],\r\n imports: [\r\n MatButtonModule\r\n ]\r\n})\r\nexport class LoginOAuthComponent {\r\n private readonly destroyRef = inject(DestroyRef);\r\n private readonly msalBroadcastService = inject(MsalBroadcastService);\r\n private readonly msaLService = inject(MsalService);\r\n private readonly dialogService = inject(DialogService);\r\n private readonly ngZone = inject(NgZone);\r\n /** Emitted when OAuth authentication succeeds. */\r\n readonly success = output<LoginOAuthResult>();\r\n /** Whether to show the introductory info panel. */\r\n readonly showInfo = input<boolean>(true);\r\n /** Application name displayed in the info panel. */\r\n readonly applicationName = input<string>();\r\n /** Whether to trigger `initialize()` automatically on mount. */\r\n readonly autoInitialize = input<boolean>(false);\r\n /** Whether the Microsoft login button is shown. */\r\n readonly allowMicrosoft = input<boolean>(true);\r\n /** Whether the Google login button is shown. */\r\n readonly allowGoogle = input<boolean>(true);\r\n protected readonly oauthReady = signal<boolean>(false);\r\n protected readonly oauthMicrosoftReady = signal<boolean>(false);\r\n protected readonly oauthGoogleReady = signal<boolean>(false);\r\n\r\n constructor() {\r\n if (this.autoInitialize()) {\r\n this.initialize();\r\n }\r\n this.msalBroadcastService.msalSubject$\r\n .pipe(\r\n filter((msg: EventMessage) =>\r\n msg.eventType === EventType.LOGOUT_FAILURE ||\r\n msg.eventType === EventType.ACQUIRE_TOKEN_FAILURE),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe((r: EventMessage) => {\r\n if (r?.payload) {\r\n const error = r.payload as any;\r\n this.dialogService.error(error?.message ?? 'Errore di autenticazione Microsoft');\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Initialize OAuth providers (Microsoft MSAL and Google GSI).\r\n * Safe to call multiple times — already-initialized providers are skipped.\r\n * Must be public so the host component can trigger it manually when `autoInitialize` is false.\r\n */\r\n initialize() {\r\n if (this.oauthMicrosoftReady() && this.oauthGoogleReady()) {\r\n return; // Both already loaded\r\n }\r\n this.oauthReady.set(true);\r\n\r\n // Load MS stack only if not already initialized\r\n if (!this.oauthMicrosoftReady()) {\r\n this.msaLService.initialize()\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe(() => {\r\n this.oauthMicrosoftReady.set(true);\r\n });\r\n }\r\n\r\n // Load Google script only if not already initialized\r\n if (!this.oauthGoogleReady()) {\r\n const DSLScript = document.createElement('script');\r\n DSLScript.src = 'https://accounts.google.com/gsi/client';\r\n DSLScript.type = 'text/javascript';\r\n document.body.appendChild(DSLScript);\r\n document.body.removeChild(DSLScript);\r\n\r\n // @ts-ignore\r\n window.onGoogleLibraryLoad = () => {\r\n // initialize google account\r\n // @ts-ignore\r\n google.accounts.id.initialize({\r\n client_id: '71204983077-ag31h3bgt2udcbfr9nn84r9rni2k3snp.apps.googleusercontent.com',\r\n cancel_on_tap_outside: true,\r\n auto_select: false,\r\n callback: (r: any) => {\r\n this.ngZone.run(() => {\r\n this.success.emit({ type: 2, token: r.credential });\r\n });\r\n }\r\n });\r\n\r\n googleButtonWrapper = createFakeGoogleWrapper();\r\n\r\n this.oauthGoogleReady.set(true);\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Trigger a Microsoft (MSAL) login popup and acquire an access token on success.\r\n */\r\n protected loginMSAL() {\r\n // Clear any in-progress interaction flag that may have been left by a previous incomplete popup\r\n sessionStorage.removeItem('msal.interaction.status');\r\n\r\n this.msaLService.loginPopup()\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe({\r\n next: (r: AuthenticationResult) => {\r\n try {\r\n this.msaLService.instance.setActiveAccount(r.account);\r\n this.msaLService.acquireTokenSilent({\r\n scopes: [\r\n \"api://6b2c080f-6ab0-4511-a9df-0bce69db5833/AngularSPAAuthScope\"\r\n ],\r\n account: r.account\r\n }).subscribe({\r\n next: (tokenResult: AuthenticationResult) => {\r\n console.log(\"Token acquired!\");\r\n this.success.emit({ type: 1, token: tokenResult.accessToken });\r\n },\r\n error: (error: any) => {\r\n this.dialogService.error(error.message);\r\n }\r\n });\r\n } catch (error: any) {\r\n this.dialogService.error(error.message);\r\n }\r\n },\r\n error: (error: any) => {\r\n this.dialogService.error(error.message);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Trigger the hidden Google GSI button to open the Google sign-in flow.\r\n */\r\n protected loginGoogle() {\r\n // Use wrapper click to prevent Illegal invocation exception\r\n googleButtonWrapper.click();\r\n }\r\n}\r\n\r\nconst createFakeGoogleWrapper = () => {\r\n const googleLoginWrapper = document.createElement(\"div\");\r\n // Or you can simple hide it in CSS rule for custom-google-button\r\n googleLoginWrapper.style.display = \"none\";\r\n googleLoginWrapper.classList.add(\"custom-google-button\");\r\n\r\n // Add the wrapper to body\r\n document.body.appendChild(googleLoginWrapper);\r\n\r\n // Use GSI javascript api to render the button inside our wrapper\r\n // You can ignore the properties because this button will not appear\r\n // @ts-ignore\r\n window.google.accounts.id.renderButton(googleLoginWrapper, {\r\n type: \"icon\",\r\n width: \"200\",\r\n });\r\n\r\n const googleLoginWrapperButton =\r\n googleLoginWrapper.querySelector(\"div[role=button]\");\r\n\r\n return {\r\n click: () => {\r\n // @ts-ignore\r\n googleLoginWrapperButton.click();\r\n },\r\n };\r\n};\r\nvar googleButtonWrapper: any;\r\n\r\n","<div class=\"login-oauth\">\r\n @if(showInfo()) {\r\n <div class=\"message\">\r\n <p>Questa sezione consente di accedere al servizio utilizzando un provider OAuth2.</p>\r\n @if(applicationName()) {\r\n <p>La email utilizzata deve essere collegata ad un utente valido di {{applicationName()}} e dei servizi ad esso\r\n connessi.</p>\r\n }\r\n </div>\r\n }\r\n @if (allowMicrosoft()) {\r\n <div class=\"button\">\r\n <button mat-flat-button (click)=\"loginMSAL()\" aria-label=\"Effettua accesso\"\r\n [disabled]=\"!oauthMicrosoftReady()\" style=\"width: 100%;\">\r\n Accedi con Microsoft</button>\r\n </div>\r\n }\r\n @if (allowGoogle()) {\r\n <div class=\"button\">\r\n <button mat-flat-button (click)=\"loginGoogle()\" style=\"width: 100%;\"\r\n aria-label=\"Effettua accesso\" [disabled]=\"!oauthGoogleReady()\">\r\n Accedi con Google</button>\r\n </div>\r\n }\r\n</div>\r\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\r\nimport { broadcastResponseToMainFrame } from '@azure/msal-browser/redirect-bridge';\r\nimport { FlexModule } from '@ngbracket/ngx-layout/flex';\r\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\r\n\r\n\r\n@Component({\r\n selector: 'app-login-oauth-ok-ms',\r\n styleUrls: ['./login-oauth-ok-ms.component.scss'],\r\n templateUrl: './login-oauth-ok-ms.component.html',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [FlexModule, MatProgressBarModule]\r\n})\r\nexport class LoginOAuthOkMSComponent {\r\n\r\n /** Message displayed while the popup is completing authentication. */\r\n readonly message = input<string>('Accesso in corso...');\r\n\r\n constructor() {\r\n // MSAL v5 popup flow: parse the auth response from the URL and send\r\n // it back to the opener window via BroadcastChannel, then close the popup.\r\n broadcastResponseToMainFrame();\r\n }\r\n}\r\n","<div fxLayout=\"column\" fxLayoutAlign=\"center center\" fxFill>\r\n <div class=\"login-auto fade-in\">\r\n <div class=\"message\">\r\n Autenticazione in corso...\r\n </div>\r\n <mat-progress-bar mode=\"indeterminate\" color=\"primary\"></mat-progress-bar>\r\n </div>\r\n</div>\r\n","/*\r\n * Public API Surface of ars-utils\r\n */\r\nexport * from './ui/ui.module';\r\nexport * from './ui/components/login/login-oauth.component';\r\nexport * from './ui/components/login-ok/ms/login-oauth-ok-ms.component';\r\n\r\n\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"arsedizioni-ars-utils-ui.oauth.mjs","sources":["../../../projects/ars-utils/ui.oauth/ui/definitions.ts","../../../projects/ars-utils/ui.oauth/ui/ui.module.ts","../../../projects/ars-utils/ui.oauth/ui/components/login/login-oauth.component.ts","../../../projects/ars-utils/ui.oauth/ui/components/login/login-oauth.component.html","../../../projects/ars-utils/ui.oauth/ui/components/login-ok/ms/login-oauth-ok-ms.component.ts","../../../projects/ars-utils/ui.oauth/ui/components/login-ok/ms/login-oauth-ok-ms.component.html","../../../projects/ars-utils/ui.oauth/public_api.ts","../../../projects/ars-utils/ui.oauth/arsedizioni-ars-utils-ui.oauth.ts"],"sourcesContent":["export enum LoginOAuthType {\r\n None = 0,\r\n Microsoft = 1,\r\n Google = 2\r\n}\r\n\r\nexport interface LoginOAuthResult {\r\n type: number,\r\n token: string\r\n}","import { NgModule } from \"@angular/core\";\r\n\r\n\r\n@NgModule()\r\nexport class ArsUIOAuthModule { }\r\n\r\nexport * from \"./definitions\";\r\n\r\n\r\n","import { ChangeDetectionStrategy, Component, DestroyRef, NgZone, inject, input, output, signal } from '@angular/core';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\nimport { MatButtonModule } from '@angular/material/button';\r\nimport { EnvironmentService } from '@arsedizioni/ars-utils/core';\r\nimport { DialogService } from '@arsedizioni/ars-utils/ui';\r\nimport { MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalBroadcastService, MsalGuardConfiguration, MsalInterceptorConfiguration, MsalService } from '@azure/msal-angular';\r\nimport { AuthenticationResult, BrowserCacheLocation, EventMessage, EventType, IPublicClientApplication, InteractionType, PublicClientApplication } from '@azure/msal-browser';\r\nimport { filter } from 'rxjs';\r\nimport { LoginOAuthResult } from '../../definitions';\r\n\r\nexport function MSALInstanceFactory(environment: EnvironmentService): IPublicClientApplication {\r\n return new PublicClientApplication({\r\n auth: {\r\n clientId: \"6b2c080f-6ab0-4511-a9df-0bce69db5833\",\r\n authority: \"https://login.microsoftonline.com/common\",\r\n redirectUri: environment.appLoginRedirectUri,\r\n },\r\n cache: {\r\n cacheLocation: BrowserCacheLocation.LocalStorage\r\n }\r\n });\r\n}\r\n\r\nexport function MSALInterceptorConfigFactory(environment: EnvironmentService): MsalInterceptorConfiguration {\r\n\r\n return {\r\n interactionType: InteractionType.Popup,\r\n protectedResourceMap: new Map([\r\n [environment.appServiceLoginUri, [\"api://6b2c080f-6ab0-4511-a9df-0bce69db5833/AngularSPAAuthScope\"]],\r\n [\"https://graph.microsoft.com/v2.0/me\", [\"user.read\"]],\r\n ])\r\n };\r\n}\r\n\r\nexport function MSALGuardConfigFactory(): MsalGuardConfiguration {\r\n return {\r\n interactionType: InteractionType.Popup,\r\n authRequest: {\r\n scopes: [\"user.read\"]\r\n }\r\n };\r\n}\r\n\r\n@Component({\r\n selector: 'login-oauth',\r\n templateUrl: './login-oauth.component.html',\r\n styleUrls: ['./login-oauth.component.scss'],\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n providers: [\r\n {\r\n provide: MSAL_INSTANCE,\r\n deps: [EnvironmentService],\r\n useFactory: MSALInstanceFactory\r\n },\r\n {\r\n provide: MSAL_GUARD_CONFIG,\r\n useFactory: MSALGuardConfigFactory\r\n },\r\n {\r\n provide: MSAL_INTERCEPTOR_CONFIG,\r\n deps: [EnvironmentService],\r\n useFactory: MSALInterceptorConfigFactory\r\n },\r\n MsalService,\r\n MsalBroadcastService],\r\n imports: [\r\n MatButtonModule\r\n ]\r\n})\r\nexport class LoginOAuthComponent {\r\n private readonly destroyRef = inject(DestroyRef);\r\n private readonly msalBroadcastService = inject(MsalBroadcastService);\r\n private readonly msaLService = inject(MsalService);\r\n private readonly dialogService = inject(DialogService);\r\n private readonly ngZone = inject(NgZone);\r\n /** Emitted when OAuth authentication succeeds. */\r\n readonly success = output<LoginOAuthResult>();\r\n /** Whether to show the introductory info panel. */\r\n readonly showInfo = input<boolean>(true);\r\n /** Application name displayed in the info panel. */\r\n readonly applicationName = input<string>();\r\n /** Whether to trigger `initialize()` automatically on mount. */\r\n readonly autoInitialize = input<boolean>(false);\r\n /** Whether the Microsoft login button is shown. */\r\n readonly allowMicrosoft = input<boolean>(true);\r\n /** Whether the Google login button is shown. */\r\n readonly allowGoogle = input<boolean>(true);\r\n protected readonly oauthReady = signal<boolean>(false);\r\n protected readonly oauthMicrosoftReady = signal<boolean>(false);\r\n protected readonly oauthGoogleReady = signal<boolean>(false);\r\n\r\n constructor() {\r\n if (this.autoInitialize()) {\r\n this.initialize();\r\n }\r\n this.msalBroadcastService.msalSubject$\r\n .pipe(\r\n filter((msg: EventMessage) =>\r\n msg.eventType === EventType.LOGOUT_FAILURE ||\r\n msg.eventType === EventType.ACQUIRE_TOKEN_FAILURE),\r\n takeUntilDestroyed(this.destroyRef),\r\n )\r\n .subscribe((r: EventMessage) => {\r\n if (r?.payload) {\r\n const error = r.payload as any;\r\n this.dialogService.error(error?.message ?? 'Errore di autenticazione Microsoft');\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Initialize OAuth providers (Microsoft MSAL and Google GSI).\r\n * Safe to call multiple times — already-initialized providers are skipped.\r\n * Must be public so the host component can trigger it manually when `autoInitialize` is false.\r\n */\r\n initialize() {\r\n if (this.oauthMicrosoftReady() && this.oauthGoogleReady()) {\r\n return; // Both already loaded\r\n }\r\n this.oauthReady.set(true);\r\n\r\n // Load MS stack only if not already initialized\r\n if (!this.oauthMicrosoftReady()) {\r\n this.msaLService.initialize()\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe(() => {\r\n this.oauthMicrosoftReady.set(true);\r\n });\r\n }\r\n\r\n // Load Google script only if not already initialized\r\n if (!this.oauthGoogleReady()) {\r\n const DSLScript = document.createElement('script');\r\n DSLScript.src = 'https://accounts.google.com/gsi/client';\r\n DSLScript.type = 'text/javascript';\r\n document.body.appendChild(DSLScript);\r\n document.body.removeChild(DSLScript);\r\n\r\n // @ts-ignore\r\n window.onGoogleLibraryLoad = () => {\r\n // initialize google account\r\n // @ts-ignore\r\n google.accounts.id.initialize({\r\n client_id: '71204983077-ag31h3bgt2udcbfr9nn84r9rni2k3snp.apps.googleusercontent.com',\r\n cancel_on_tap_outside: true,\r\n auto_select: false,\r\n callback: (r: any) => {\r\n this.ngZone.run(() => {\r\n this.success.emit({ type: 2, token: r.credential });\r\n });\r\n }\r\n });\r\n\r\n googleButtonWrapper = createFakeGoogleWrapper();\r\n\r\n this.oauthGoogleReady.set(true);\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Trigger a Microsoft (MSAL) login popup and acquire an access token on success.\r\n */\r\n protected loginMSAL() {\r\n // Clear any in-progress interaction flag that may have been left by a previous incomplete popup\r\n sessionStorage.removeItem('msal.interaction.status');\r\n\r\n this.msaLService.loginPopup()\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe({\r\n next: (r: AuthenticationResult) => {\r\n try {\r\n this.msaLService.instance.setActiveAccount(r.account);\r\n this.msaLService.acquireTokenSilent({\r\n scopes: [\r\n \"api://6b2c080f-6ab0-4511-a9df-0bce69db5833/AngularSPAAuthScope\"\r\n ],\r\n account: r.account\r\n }).subscribe({\r\n next: (tokenResult: AuthenticationResult) => {\r\n console.log(\"Token acquired!\");\r\n this.success.emit({ type: 1, token: tokenResult.accessToken });\r\n },\r\n error: (error: any) => {\r\n this.dialogService.error(error.message);\r\n }\r\n });\r\n } catch (error: any) {\r\n this.dialogService.error(error.message);\r\n }\r\n },\r\n error: (error: any) => {\r\n this.dialogService.error(error.message);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Trigger the hidden Google GSI button to open the Google sign-in flow.\r\n */\r\n protected loginGoogle() {\r\n // Use wrapper click to prevent Illegal invocation exception\r\n googleButtonWrapper.click();\r\n }\r\n}\r\n\r\nconst createFakeGoogleWrapper = () => {\r\n const googleLoginWrapper = document.createElement(\"div\");\r\n // Or you can simple hide it in CSS rule for custom-google-button\r\n googleLoginWrapper.style.display = \"none\";\r\n googleLoginWrapper.classList.add(\"custom-google-button\");\r\n\r\n // Add the wrapper to body\r\n document.body.appendChild(googleLoginWrapper);\r\n\r\n // Use GSI javascript api to render the button inside our wrapper\r\n // You can ignore the properties because this button will not appear\r\n // @ts-ignore\r\n window.google.accounts.id.renderButton(googleLoginWrapper, {\r\n type: \"icon\",\r\n width: \"200\",\r\n });\r\n\r\n const googleLoginWrapperButton =\r\n googleLoginWrapper.querySelector(\"div[role=button]\");\r\n\r\n return {\r\n click: () => {\r\n // @ts-ignore\r\n googleLoginWrapperButton.click();\r\n },\r\n };\r\n};\r\nvar googleButtonWrapper: any;\r\n\r\n","<div class=\"login-oauth\">\r\n @if(showInfo()) {\r\n <div class=\"message\">\r\n <p>Questa sezione consente di accedere al servizio utilizzando un provider OAuth2.</p>\r\n @if(applicationName()) {\r\n <p>La email utilizzata deve essere collegata ad un utente valido di {{applicationName()}} e dei servizi ad esso\r\n connessi.</p>\r\n }\r\n </div>\r\n }\r\n @if (allowMicrosoft()) {\r\n <div class=\"button\">\r\n <button mat-flat-button (click)=\"loginMSAL()\" aria-label=\"Effettua accesso\"\r\n [disabled]=\"!oauthMicrosoftReady()\" style=\"width: 100%;\">\r\n Accedi con Microsoft</button>\r\n </div>\r\n }\r\n @if (allowGoogle()) {\r\n <div class=\"button\">\r\n <button mat-flat-button (click)=\"loginGoogle()\" style=\"width: 100%;\"\r\n aria-label=\"Effettua accesso\" [disabled]=\"!oauthGoogleReady()\">\r\n Accedi con Google</button>\r\n </div>\r\n }\r\n</div>\r\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\r\nimport { broadcastResponseToMainFrame } from '@azure/msal-browser/redirect-bridge';\r\nimport { FlexModule } from '@ngbracket/ngx-layout/flex';\r\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\r\n\r\n\r\n@Component({\r\n selector: 'app-login-oauth-ok-ms',\r\n styleUrls: ['./login-oauth-ok-ms.component.scss'],\r\n templateUrl: './login-oauth-ok-ms.component.html',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [FlexModule, MatProgressBarModule]\r\n})\r\nexport class LoginOAuthOkMSComponent {\r\n\r\n /** Message displayed while the popup is completing authentication. */\r\n readonly message = input<string>('Accesso in corso...');\r\n\r\n constructor() {\r\n // MSAL v5 popup flow: parse the auth response from the URL and send\r\n // it back to the opener window via BroadcastChannel, then close the popup.\r\n broadcastResponseToMainFrame();\r\n }\r\n}\r\n","<div fxLayout=\"column\" fxLayoutAlign=\"center center\" fxFill>\r\n <div class=\"login-auto fade-in\">\r\n <div class=\"message\">\r\n Autenticazione in corso...\r\n </div>\r\n <mat-progress-bar mode=\"indeterminate\" color=\"primary\"></mat-progress-bar>\r\n </div>\r\n</div>\r\n","/*\r\n * Public API Surface of ars-utils\r\n */\r\nexport * from './ui/ui.module';\r\nexport * from './ui/components/index';\r\n\r\n\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arsedizioni/ars-utils",
3
- "version": "21.2.220",
3
+ "version": "21.2.221",
4
4
  "author": {
5
5
  "email": "software@arsedizioni.it",
6
6
  "name": "Fabio Buscaroli, Alberto Doria"
@@ -1,19 +1,19 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { DestroyRef, ChangeDetectorRef, Injector } from '@angular/core';
3
+ import { ScrollDispatcher } from '@angular/cdk/scrolling';
4
+ import { MatExpansionPanel } from '@angular/material/expansion';
5
+ import { MatInput } from '@angular/material/input';
3
6
  import { MatPaginator, PageEvent } from '@angular/material/paginator';
4
7
  import { MatDrawer } from '@angular/material/sidenav';
5
- import { ClipperSearchFacetsSnapshot, ClipperSearchFacet, ClipperSearchFacetGroup, ClipperService, ClipperDocumentInfo, ClipperSearchParams, ClipperQueryReferencesMode, ClipperModule, ClipperModel, ClipperSearchCalendarSnapshotMonth, ClipperSearchCalendarSnapshotResult, ClipperSelectionMode, ClipperSearchResult, ClipperSort, ClipperUserInfo, ClipperUserSearch, SectorInfo, ClipperDocumentRelevants, ClipperDocumentAnchorInfo, ClipperDocumentProperty, EditDialogData, EditDialogCompleted, ClipperContactInfo } from '@arsedizioni/ars-utils/clipper.common';
6
- import { BreakpointObserver } from '@angular/cdk/layout';
8
+ import { ClipperSearchFacetsSnapshot, ClipperSearchFacet, ClipperSearchFacetGroup, ClipperService, ClipperDocumentInfo, ClipperSearchParams, ClipperQueryReferencesMode, ClipperModule, ClipperModel, ClipperSearchCalendarSnapshotMonth, ClipperSearchCalendarSnapshotResult, ClipperSelectionMode, ClipperSearchResult, ClipperSort, ClipperUserSearch, SectorInfo, ClipperUserInfo, EditDialogCompleted, ClipperContactInfo, ClipperDocumentRelevants, ClipperDocumentAnchorInfo, ClipperDocumentProperty, EditDialogData } from '@arsedizioni/ars-utils/clipper.common';
7
9
  import { BroadcastService, DateInterval, ThemeService, ScreenService, SelectableModel, NameValueItem, INode, ThemeType } from '@arsedizioni/ars-utils/core';
8
- import { Clipboard } from '@angular/cdk/clipboard';
9
10
  import { SendToPopulateData, ApplicationDialogService, CalendarEmptyHeader, ButtonSelectorComponent, ChipsSelectorComponent } from '@arsedizioni/ars-utils/ui.application';
11
+ import { BreakpointObserver } from '@angular/cdk/layout';
12
+ import { Clipboard } from '@angular/cdk/clipboard';
10
13
  import { MatCalendar, MatCalendarCellClassFunction } from '@angular/material/datepicker';
11
- import { ScrollDispatcher } from '@angular/cdk/scrolling';
12
- import { MatExpansionPanel } from '@angular/material/expansion';
13
- import { MatInput } from '@angular/material/input';
14
- import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
15
- import { MatSelectionList } from '@angular/material/list';
16
14
  import { NgForm } from '@angular/forms';
15
+ import { MatSelectionList } from '@angular/material/list';
16
+ import { MatMenuTrigger, MatMenu } from '@angular/material/menu';
17
17
  import { MatDialogRef } from '@angular/material/dialog';
18
18
 
19
19
  declare class ClipperSearchFacetsComponent {
@@ -449,99 +449,6 @@ declare class ClipperSearchResultManager extends ClipperDocumentManager {
449
449
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperSearchResultManager, "ng-component", never, {}, {}, never, never, true, never>;
450
450
  }
451
451
 
452
- interface ClipperReferencesDialogData {
453
- mode?: ClipperQueryReferencesMode;
454
- anchor?: string;
455
- anchorTitle?: string;
456
- documentId: string;
457
- model: number;
458
- canUseArchive?: boolean;
459
- canHandleTooManyResults?: boolean;
460
- }
461
- interface ClipperReferenceDialogResult {
462
- documentId: string;
463
- }
464
- declare class ClipperReferencesComponent extends ClipperSearchResultManager {
465
- readonly filterPane: _angular_core.Signal<MatDrawer>;
466
- private readonly dialogRef;
467
- protected readonly dialogData: ClipperReferencesDialogData;
468
- protected title?: string;
469
- protected item?: ClipperDocumentInfo;
470
- protected readonly user: ClipperUserInfo | undefined;
471
- constructor();
472
- /**
473
- * Toggles the filter pane open/closed state.
474
- */
475
- protected toggleFilterPane(): void;
476
- /**
477
- * Adjusts the filter pane mode (side vs. overlay) and backdrop based on the current breakpoint.
478
- */
479
- private handleFilterPaneVisibility;
480
- /**
481
- * Closes the references dialog without returning a result.
482
- */
483
- close(): void;
484
- /**
485
- * Sends document links by email for the given item or, when omitted, for all selected items.
486
- * @param item - The specific document to send, or `undefined` to use the current selection.
487
- */
488
- sendTo(item?: ClipperDocumentInfo): void;
489
- /**
490
- * Builds the search parameters for the next fetch call.
491
- * @param newSearch - When `true`, resets the pagination offset to 0.
492
- * @returns A cloned and enriched `ClipperSearchParams` ready to be sent to the API.
493
- */
494
- private prepareFetch;
495
- /**
496
- * Maps a generic `ClipperSearchParams` to the references-specific API parameter shape.
497
- * @param params - The base search parameters to convert.
498
- * @returns A `ClipperReferencesSearchParams` object ready for the references endpoint.
499
- */
500
- private toReferencesSearchParams;
501
- /**
502
- * Executes the references search and updates the result snapshot.
503
- * @param newSearch - When `true`, resets pagination and reloads facets. Defaults to `false`.
504
- */
505
- fetch(newSearch?: boolean): void;
506
- /**
507
- * Navigates to the next result page.
508
- * @param e - The `PageEvent` emitted by the paginator with the new page index and size.
509
- */
510
- fetchMore(e: PageEvent): void;
511
- /**
512
- * Loads available facets for the current search parameters and updates the filter pane.
513
- * @param params - The search parameters used to request facets from the API.
514
- */
515
- fetchFacets(params: ClipperSearchParams): void;
516
- /**
517
- * Hides the facet filter pane and resets all applied facet filters.
518
- */
519
- protected hideFacets(): void;
520
- /**
521
- * Restores previously saved facets from the snapshot and adjusts the filter pane accordingly.
522
- */
523
- restoreFacets(): void;
524
- /**
525
- * Closes the dialog and navigates to the selected document.
526
- * @param documentId - The ID of the document to open.
527
- */
528
- open(documentId?: string): void;
529
- /**
530
- * Returns the CSS class reflecting the item's validity state.
531
- * @param item - The document to evaluate.
532
- * @returns A CSS class name string, or `undefined` when no special state applies.
533
- */
534
- getItemStateCssClass(item: ClipperDocumentInfo): string | undefined;
535
- /**
536
- * Returns the tooltip text explaining the item's current validity state.
537
- * @param item - The document to evaluate.
538
- * @returns A human-readable state description, or an empty string when no special state applies.
539
- */
540
- getItemStateTooltip(item: ClipperDocumentInfo): string;
541
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperReferencesComponent, never>;
542
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperReferencesComponent, "ng-component", never, {}, {}, never, never, true, never>;
543
- }
544
-
545
452
  declare class ClipperBrowserComponent extends ClipperSearchResultManager {
546
453
  readonly filterByNumber: _angular_core.Signal<MatInput>;
547
454
  readonly filterByText: _angular_core.Signal<MatInput>;
@@ -833,77 +740,54 @@ declare class ClipperBrowserDialogComponent {
833
740
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperBrowserDialogComponent, "ng-component", never, {}, {}, never, never, true, never>;
834
741
  }
835
742
 
836
- declare class ClipperSearchFreeTextHelpComponent {
837
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperSearchFreeTextHelpComponent, never>;
838
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperSearchFreeTextHelpComponent, "ng-component", never, {}, {}, never, never, true, never>;
839
- }
840
-
841
- interface SearchFreeTextQueryParams {
842
- query: string;
843
- }
844
- declare class ClipperSearchFreeTextQueryBuilderComponent {
845
- readonly done: _angular_core.OutputEmitterRef<SearchFreeTextQueryParams>;
846
- private readonly dialogService;
847
- protected queryAnd: string;
848
- protected queryOr: string;
849
- protected queryNot: string;
850
- protected queryPhrase: string;
851
- protected queryStart: string;
743
+ declare class ClipperContactEditComponent {
744
+ private clipperService;
745
+ private dialogService;
746
+ private dialogRef;
747
+ private dialogData;
748
+ protected readonly f: _angular_core.Signal<NgForm>;
749
+ readonly done: _angular_core.OutputEmitterRef<EditDialogCompleted<ClipperContactInfo>>;
750
+ protected readonly dialogTitle: string;
751
+ protected readonly isNew: _angular_core.WritableSignal<boolean>;
752
+ protected item: ClipperContactInfo;
753
+ protected readonly availableTeams: _angular_core.WritableSignal<NameValueItem<string>[]>;
754
+ protected selectedTeam: NameValueItem<string>;
755
+ constructor();
852
756
  /**
853
- * Resets all query fields to empty strings.
757
+ * Loads the list of available teams from the server and populates the team selector.
758
+ * Pre-selects the team that matches the current contact's team id, falling back to 'Personale'.
854
759
  */
855
- protected clear(): void;
760
+ private loadTeams;
856
761
  /**
857
- * Builds the Lucene-style free-text query from the current field values and emits it via `done`.
858
- * Shows an error dialog when only the NOT operator has been filled in, since it requires
859
- * at least one positive term to produce a valid query.
762
+ * Persists the current contact to the server.
763
+ * Closes the dialog without saving when editing an existing contact whose form is unchanged.
860
764
  */
861
- protected ok(): void;
862
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperSearchFreeTextQueryBuilderComponent, never>;
863
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperSearchFreeTextQueryBuilderComponent, "ng-component", never, {}, { "done": "done"; }, never, never, true, never>;
765
+ protected save(): void;
766
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperContactEditComponent, never>;
767
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperContactEditComponent, "ng-component", never, {}, { "done": "done"; }, never, never, true, never>;
864
768
  }
865
769
 
866
- declare enum ClipperSearchResultItemDisplayMode {
867
- List = 1,
868
- Tile = 2,
869
- Stripe = 3
870
- }
871
- declare class ClipperSearchResultItemComponent {
872
- private readonly destroyRef;
873
- protected readonly screenService: ScreenService;
874
- private readonly broadcastService;
875
- readonly parent: _angular_core.InputSignal<ClipperSearchResultManager>;
876
- readonly item: _angular_core.ModelSignal<ClipperDocumentInfo>;
877
- readonly actions: _angular_core.InputSignal<MatMenu>;
878
- readonly tileNoPictureUrl: _angular_core.InputSignal<string>;
879
- readonly tilePictureUrl: _angular_core.InputSignal<string>;
880
- readonly isSelectable: _angular_core.InputSignal<boolean>;
881
- readonly isReadable: _angular_core.InputSignal<boolean>;
882
- readonly isReadableModel: _angular_core.InputSignal<ClipperModel>;
883
- readonly displayModifiedTitle: _angular_core.InputSignal<boolean>;
884
- readonly displayModelName: _angular_core.InputSignal<boolean>;
885
- readonly displayDate: _angular_core.InputSignal<boolean>;
886
- readonly displayMode: _angular_core.InputSignal<ClipperSearchResultItemDisplayMode>;
887
- protected readonly displayModesEnum: typeof ClipperSearchResultItemDisplayMode;
888
- protected readonly modelsEnum: typeof ClipperModel;
889
- /** Incremented whenever the parent's SelectionModel changes, invalidating `isSelected`. */
890
- private readonly selectionChangeTick;
770
+ declare class ClipperContactsSelector {
891
771
  /**
892
- * Whether this item is currently selected in the parent's selection model.
893
- * Recomputed whenever `selectionChangeTick` is updated by the selection-changed subscription.
772
+ * Builds an HTML string representing a list of email addresses separated by line breaks.
773
+ * @param emails - A semicolon-separated string of email addresses, or undefined.
774
+ * @returns An HTML string with each address wrapped in a `<span>` tag.
894
775
  */
895
- protected readonly isSelected: _angular_core.Signal<boolean>;
776
+ private static buildEmailsHtml;
896
777
  /**
897
- * Whether this item has already been read by the user.
778
+ * Builds the HTML row template used to display a contact inside the selection dialog.
779
+ * @param item - The contact data to render.
780
+ * @returns An HTML string representing the contact row.
898
781
  */
899
- protected readonly isRead: _angular_core.Signal<boolean>;
782
+ private static buildContactTemplate;
900
783
  /**
901
- * Whether this item is readable but not yet read (i.e. the read-flag action should be shown).
784
+ * Opens the contacts selection/management dialog and wires up all interaction events.
785
+ * @param injector - The Angular injector used to resolve services within the dialog context.
786
+ * @param canAppend - When true, the dialog allows adding new contacts.
787
+ * @param canEdit - When true, the dialog allows editing and deleting contacts.
788
+ * @param onSelect - Optional callback invoked with the selection result when the user confirms.
902
789
  */
903
- protected readonly canBeRead: _angular_core.Signal<boolean>;
904
- constructor();
905
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperSearchResultItemComponent, never>;
906
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperSearchResultItemComponent, "clipper-search-result-item", never, { "parent": { "alias": "parent"; "required": true; "isSignal": true; }; "item": { "alias": "item"; "required": true; "isSignal": true; }; "actions": { "alias": "actions"; "required": true; "isSignal": true; }; "tileNoPictureUrl": { "alias": "tileNoPictureUrl"; "required": false; "isSignal": true; }; "tilePictureUrl": { "alias": "tilePictureUrl"; "required": false; "isSignal": true; }; "isSelectable": { "alias": "isSelectable"; "required": false; "isSignal": true; }; "isReadable": { "alias": "isReadable"; "required": false; "isSignal": true; }; "isReadableModel": { "alias": "isReadableModel"; "required": false; "isSignal": true; }; "displayModifiedTitle": { "alias": "displayModifiedTitle"; "required": false; "isSignal": true; }; "displayModelName": { "alias": "displayModelName"; "required": false; "isSignal": true; }; "displayDate": { "alias": "displayDate"; "required": false; "isSignal": true; }; "displayMode": { "alias": "displayMode"; "required": false; "isSignal": true; }; }, { "item": "itemChange"; }, never, never, true, never>;
790
+ static Show(injector: Injector, canAppend?: boolean, canEdit?: boolean, onSelect?: (result: unknown) => void): void;
907
791
  }
908
792
 
909
793
  interface ClipperDocumentDialogData {
@@ -1133,7 +1017,7 @@ declare class ClipperDocumentMenuComponent {
1133
1017
  /** Internal counter incremented on each external selection-model change to drive signal re-evaluation. */
1134
1018
  private readonly selectionChangeTick;
1135
1019
  readonly useSelections: _angular_core.InputSignal<boolean>;
1136
- readonly selectionSource: _angular_core.InputSignal<"none" | "selection" | "bag">;
1020
+ readonly selectionSource: _angular_core.InputSignal<"bag" | "selection" | "none">;
1137
1021
  /**
1138
1022
  * Computed signal that returns the current effective document selection.
1139
1023
  * Re-evaluates when any input signal or the underlying selection model changes.
@@ -1341,54 +1225,170 @@ declare class ClipperPropertyEditComponent {
1341
1225
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperPropertyEditComponent, "ng-component", never, {}, { "done": "done"; }, never, never, true, never>;
1342
1226
  }
1343
1227
 
1344
- declare class ClipperContactEditComponent {
1345
- private clipperService;
1346
- private dialogService;
1347
- private dialogRef;
1348
- private dialogData;
1349
- protected readonly f: _angular_core.Signal<NgForm>;
1350
- readonly done: _angular_core.OutputEmitterRef<EditDialogCompleted<ClipperContactInfo>>;
1351
- protected readonly dialogTitle: string;
1352
- protected readonly isNew: _angular_core.WritableSignal<boolean>;
1353
- protected item: ClipperContactInfo;
1354
- protected readonly availableTeams: _angular_core.WritableSignal<NameValueItem<string>[]>;
1355
- protected selectedTeam: NameValueItem<string>;
1228
+ interface ClipperReferencesDialogData {
1229
+ mode?: ClipperQueryReferencesMode;
1230
+ anchor?: string;
1231
+ anchorTitle?: string;
1232
+ documentId: string;
1233
+ model: number;
1234
+ canUseArchive?: boolean;
1235
+ canHandleTooManyResults?: boolean;
1236
+ }
1237
+ interface ClipperReferenceDialogResult {
1238
+ documentId: string;
1239
+ }
1240
+ declare class ClipperReferencesComponent extends ClipperSearchResultManager {
1241
+ readonly filterPane: _angular_core.Signal<MatDrawer>;
1242
+ private readonly dialogRef;
1243
+ protected readonly dialogData: ClipperReferencesDialogData;
1244
+ protected title?: string;
1245
+ protected item?: ClipperDocumentInfo;
1246
+ protected readonly user: ClipperUserInfo | undefined;
1356
1247
  constructor();
1357
1248
  /**
1358
- * Loads the list of available teams from the server and populates the team selector.
1359
- * Pre-selects the team that matches the current contact's team id, falling back to 'Personale'.
1249
+ * Toggles the filter pane open/closed state.
1360
1250
  */
1361
- private loadTeams;
1251
+ protected toggleFilterPane(): void;
1362
1252
  /**
1363
- * Persists the current contact to the server.
1364
- * Closes the dialog without saving when editing an existing contact whose form is unchanged.
1253
+ * Adjusts the filter pane mode (side vs. overlay) and backdrop based on the current breakpoint.
1365
1254
  */
1366
- protected save(): void;
1367
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperContactEditComponent, never>;
1368
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperContactEditComponent, "ng-component", never, {}, { "done": "done"; }, never, never, true, never>;
1255
+ private handleFilterPaneVisibility;
1256
+ /**
1257
+ * Closes the references dialog without returning a result.
1258
+ */
1259
+ close(): void;
1260
+ /**
1261
+ * Sends document links by email for the given item or, when omitted, for all selected items.
1262
+ * @param item - The specific document to send, or `undefined` to use the current selection.
1263
+ */
1264
+ sendTo(item?: ClipperDocumentInfo): void;
1265
+ /**
1266
+ * Builds the search parameters for the next fetch call.
1267
+ * @param newSearch - When `true`, resets the pagination offset to 0.
1268
+ * @returns A cloned and enriched `ClipperSearchParams` ready to be sent to the API.
1269
+ */
1270
+ private prepareFetch;
1271
+ /**
1272
+ * Maps a generic `ClipperSearchParams` to the references-specific API parameter shape.
1273
+ * @param params - The base search parameters to convert.
1274
+ * @returns A `ClipperReferencesSearchParams` object ready for the references endpoint.
1275
+ */
1276
+ private toReferencesSearchParams;
1277
+ /**
1278
+ * Executes the references search and updates the result snapshot.
1279
+ * @param newSearch - When `true`, resets pagination and reloads facets. Defaults to `false`.
1280
+ */
1281
+ fetch(newSearch?: boolean): void;
1282
+ /**
1283
+ * Navigates to the next result page.
1284
+ * @param e - The `PageEvent` emitted by the paginator with the new page index and size.
1285
+ */
1286
+ fetchMore(e: PageEvent): void;
1287
+ /**
1288
+ * Loads available facets for the current search parameters and updates the filter pane.
1289
+ * @param params - The search parameters used to request facets from the API.
1290
+ */
1291
+ fetchFacets(params: ClipperSearchParams): void;
1292
+ /**
1293
+ * Hides the facet filter pane and resets all applied facet filters.
1294
+ */
1295
+ protected hideFacets(): void;
1296
+ /**
1297
+ * Restores previously saved facets from the snapshot and adjusts the filter pane accordingly.
1298
+ */
1299
+ restoreFacets(): void;
1300
+ /**
1301
+ * Closes the dialog and navigates to the selected document.
1302
+ * @param documentId - The ID of the document to open.
1303
+ */
1304
+ open(documentId?: string): void;
1305
+ /**
1306
+ * Returns the CSS class reflecting the item's validity state.
1307
+ * @param item - The document to evaluate.
1308
+ * @returns A CSS class name string, or `undefined` when no special state applies.
1309
+ */
1310
+ getItemStateCssClass(item: ClipperDocumentInfo): string | undefined;
1311
+ /**
1312
+ * Returns the tooltip text explaining the item's current validity state.
1313
+ * @param item - The document to evaluate.
1314
+ * @returns A human-readable state description, or an empty string when no special state applies.
1315
+ */
1316
+ getItemStateTooltip(item: ClipperDocumentInfo): string;
1317
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperReferencesComponent, never>;
1318
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperReferencesComponent, "ng-component", never, {}, {}, never, never, true, never>;
1369
1319
  }
1370
1320
 
1371
- declare class ClipperContactsSelector {
1321
+ declare class ClipperSearchFreeTextHelpComponent {
1322
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperSearchFreeTextHelpComponent, never>;
1323
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperSearchFreeTextHelpComponent, "ng-component", never, {}, {}, never, never, true, never>;
1324
+ }
1325
+
1326
+ interface SearchFreeTextQueryParams {
1327
+ query: string;
1328
+ }
1329
+ declare class ClipperSearchFreeTextQueryBuilderComponent {
1330
+ readonly done: _angular_core.OutputEmitterRef<SearchFreeTextQueryParams>;
1331
+ private readonly dialogService;
1332
+ protected queryAnd: string;
1333
+ protected queryOr: string;
1334
+ protected queryNot: string;
1335
+ protected queryPhrase: string;
1336
+ protected queryStart: string;
1372
1337
  /**
1373
- * Builds an HTML string representing a list of email addresses separated by line breaks.
1374
- * @param emails - A semicolon-separated string of email addresses, or undefined.
1375
- * @returns An HTML string with each address wrapped in a `<span>` tag.
1338
+ * Resets all query fields to empty strings.
1376
1339
  */
1377
- private static buildEmailsHtml;
1340
+ protected clear(): void;
1378
1341
  /**
1379
- * Builds the HTML row template used to display a contact inside the selection dialog.
1380
- * @param item - The contact data to render.
1381
- * @returns An HTML string representing the contact row.
1342
+ * Builds the Lucene-style free-text query from the current field values and emits it via `done`.
1343
+ * Shows an error dialog when only the NOT operator has been filled in, since it requires
1344
+ * at least one positive term to produce a valid query.
1382
1345
  */
1383
- private static buildContactTemplate;
1346
+ protected ok(): void;
1347
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperSearchFreeTextQueryBuilderComponent, never>;
1348
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperSearchFreeTextQueryBuilderComponent, "ng-component", never, {}, { "done": "done"; }, never, never, true, never>;
1349
+ }
1350
+
1351
+ declare enum ClipperSearchResultItemDisplayMode {
1352
+ List = 1,
1353
+ Tile = 2,
1354
+ Stripe = 3
1355
+ }
1356
+ declare class ClipperSearchResultItemComponent {
1357
+ private readonly destroyRef;
1358
+ protected readonly screenService: ScreenService;
1359
+ private readonly broadcastService;
1360
+ readonly parent: _angular_core.InputSignal<ClipperSearchResultManager>;
1361
+ readonly item: _angular_core.ModelSignal<ClipperDocumentInfo>;
1362
+ readonly actions: _angular_core.InputSignal<MatMenu>;
1363
+ readonly tileNoPictureUrl: _angular_core.InputSignal<string>;
1364
+ readonly tilePictureUrl: _angular_core.InputSignal<string>;
1365
+ readonly isSelectable: _angular_core.InputSignal<boolean>;
1366
+ readonly isReadable: _angular_core.InputSignal<boolean>;
1367
+ readonly isReadableModel: _angular_core.InputSignal<ClipperModel>;
1368
+ readonly displayModifiedTitle: _angular_core.InputSignal<boolean>;
1369
+ readonly displayModelName: _angular_core.InputSignal<boolean>;
1370
+ readonly displayDate: _angular_core.InputSignal<boolean>;
1371
+ readonly displayMode: _angular_core.InputSignal<ClipperSearchResultItemDisplayMode>;
1372
+ protected readonly displayModesEnum: typeof ClipperSearchResultItemDisplayMode;
1373
+ protected readonly modelsEnum: typeof ClipperModel;
1374
+ /** Incremented whenever the parent's SelectionModel changes, invalidating `isSelected`. */
1375
+ private readonly selectionChangeTick;
1384
1376
  /**
1385
- * Opens the contacts selection/management dialog and wires up all interaction events.
1386
- * @param injector - The Angular injector used to resolve services within the dialog context.
1387
- * @param canAppend - When true, the dialog allows adding new contacts.
1388
- * @param canEdit - When true, the dialog allows editing and deleting contacts.
1389
- * @param onSelect - Optional callback invoked with the selection result when the user confirms.
1377
+ * Whether this item is currently selected in the parent's selection model.
1378
+ * Recomputed whenever `selectionChangeTick` is updated by the selection-changed subscription.
1390
1379
  */
1391
- static Show(injector: Injector, canAppend?: boolean, canEdit?: boolean, onSelect?: (result: unknown) => void): void;
1380
+ protected readonly isSelected: _angular_core.Signal<boolean>;
1381
+ /**
1382
+ * Whether this item has already been read by the user.
1383
+ */
1384
+ protected readonly isRead: _angular_core.Signal<boolean>;
1385
+ /**
1386
+ * Whether this item is readable but not yet read (i.e. the read-flag action should be shown).
1387
+ */
1388
+ protected readonly canBeRead: _angular_core.Signal<boolean>;
1389
+ constructor();
1390
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ClipperSearchResultItemComponent, never>;
1391
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ClipperSearchResultItemComponent, "clipper-search-result-item", never, { "parent": { "alias": "parent"; "required": true; "isSignal": true; }; "item": { "alias": "item"; "required": true; "isSignal": true; }; "actions": { "alias": "actions"; "required": true; "isSignal": true; }; "tileNoPictureUrl": { "alias": "tileNoPictureUrl"; "required": false; "isSignal": true; }; "tilePictureUrl": { "alias": "tilePictureUrl"; "required": false; "isSignal": true; }; "isSelectable": { "alias": "isSelectable"; "required": false; "isSignal": true; }; "isReadable": { "alias": "isReadable"; "required": false; "isSignal": true; }; "isReadableModel": { "alias": "isReadableModel"; "required": false; "isSignal": true; }; "displayModifiedTitle": { "alias": "displayModifiedTitle"; "required": false; "isSignal": true; }; "displayModelName": { "alias": "displayModelName"; "required": false; "isSignal": true; }; "displayDate": { "alias": "displayDate"; "required": false; "isSignal": true; }; "displayMode": { "alias": "displayMode"; "required": false; "isSignal": true; }; }, { "item": "itemChange"; }, never, never, true, never>;
1392
1392
  }
1393
1393
 
1394
1394
  declare class ClipperDocumentsUtils {
@@ -1434,22 +1434,6 @@ declare class ArsDateFnsModule {
1434
1434
  static ɵinj: i0.ɵɵInjectorDeclaration<ArsDateFnsModule>;
1435
1435
  }
1436
1436
 
1437
- /**
1438
- * Directive that removes focus from the host element after it is clicked,
1439
- * preventing the browser from keeping a visible focus ring post-interaction.
1440
- * Apply `removeFocus` to any focusable element (e.g. a button).
1441
- */
1442
- declare class RemoveFocusDirective {
1443
- private readonly elementRef;
1444
- /**
1445
- * Handles click events on the host element and blurs it on the next event-loop tick
1446
- * so that the click action completes before focus is removed.
1447
- */
1448
- onClick(): void;
1449
- static ɵfac: i0.ɵɵFactoryDeclaration<RemoveFocusDirective, never>;
1450
- static ɵdir: i0.ɵɵDirectiveDeclaration<RemoveFocusDirective, "[removeFocus]", never, {}, {}, never, never, true, never>;
1451
- }
1452
-
1453
1437
  /**
1454
1438
  * Directive that validates that the host control's value is different from another control's value.
1455
1439
  * Bind `[notEqual]="otherControl"`.
@@ -1468,6 +1452,22 @@ declare class NotEqualValidatorDirective implements Validator {
1468
1452
  static ɵdir: i0.ɵɵDirectiveDeclaration<NotEqualValidatorDirective, "[notEqual]", never, { "notEqual": { "alias": "notEqual"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1469
1453
  }
1470
1454
 
1455
+ /**
1456
+ * Directive that removes focus from the host element after it is clicked,
1457
+ * preventing the browser from keeping a visible focus ring post-interaction.
1458
+ * Apply `removeFocus` to any focusable element (e.g. a button).
1459
+ */
1460
+ declare class RemoveFocusDirective {
1461
+ private readonly elementRef;
1462
+ /**
1463
+ * Handles click events on the host element and blurs it on the next event-loop tick
1464
+ * so that the click action completes before focus is removed.
1465
+ */
1466
+ onClick(): void;
1467
+ static ɵfac: i0.ɵɵFactoryDeclaration<RemoveFocusDirective, never>;
1468
+ static ɵdir: i0.ɵɵDirectiveDeclaration<RemoveFocusDirective, "[removeFocus]", never, {}, {}, never, never, true, never>;
1469
+ }
1470
+
1471
1471
  /**
1472
1472
  * Directive that validates a time string against optional allowed time slot ranges.
1473
1473
  * Bind `[time]` and optionally `[slots]="'08:00-12:00|14:00-18:00'"` (pipe-separated ranges).