@yuuvis/client-core 2.3.0 → 2.3.2

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.
@@ -3,15 +3,16 @@ import { TranslateService, TranslateLoader, MissingTranslationHandler, Translate
3
3
  export { TranslateDirective, TranslateLoader, TranslateModule, TranslatePipe, TranslateService } from '@ngx-translate/core';
4
4
  import { HttpErrorResponse, HttpClient, HttpHeaders, HttpParams, HttpRequest, HttpResponse, HttpEventType, provideHttpClient, withInterceptors } from '@angular/common/http';
5
5
  import * as i0 from '@angular/core';
6
- import { inject, Injectable, InjectionToken, Inject, Pipe, importProvidersFrom, provideAppInitializer } from '@angular/core';
6
+ import { inject, Injectable, InjectionToken, Inject, Directive, Pipe, importProvidersFrom, provideAppInitializer } from '@angular/core';
7
7
  import { tap, finalize, shareReplay, catchError, map, switchMap, first, filter, scan, delay } from 'rxjs/operators';
8
- import { EMPTY, of, forkJoin, Observable, ReplaySubject, Subject, BehaviorSubject, tap as tap$1, map as map$1, merge, fromEvent, filter as filter$1, debounceTime, throwError, switchMap as switchMap$1 } from 'rxjs';
8
+ import { EMPTY, of, forkJoin, Observable, ReplaySubject, Subject, fromEvent, BehaviorSubject, tap as tap$1, map as map$1, merge, filter as filter$1, debounceTime, throwError, isObservable, switchMap as switchMap$1 } from 'rxjs';
9
9
  import { StorageMap } from '@ngx-pwa/local-storage';
10
+ import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
10
11
  import { __decorate, __param, __metadata } from 'tslib';
11
12
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
12
- import { toSignal } from '@angular/core/rxjs-interop';
13
13
  import { DeviceDetectorService } from 'ngx-device-detector';
14
14
  import { DOCUMENT, DecimalPipe, PercentPipe, CurrencyPipe, registerLocaleData } from '@angular/common';
15
+ import { MatTabGroup } from '@angular/material/tabs';
15
16
  import { FormGroup, FormControl } from '@angular/forms';
16
17
  import * as i1$1 from '@angular/platform-browser';
17
18
  import localeAr from '@angular/common/locales/ar';
@@ -2190,21 +2191,71 @@ var Direction;
2190
2191
  Direction["RTL"] = "rtl";
2191
2192
  })(Direction || (Direction = {}));
2192
2193
 
2194
+ /**
2195
+ * Mandatory Custom event prefix for all custom YUV events
2196
+ */
2197
+ const CUSTOM_EVENT_PREFIX = 'yuv.';
2198
+
2193
2199
  /**
2194
2200
  * Service for providing triggered events
2195
2201
  */
2196
2202
  class EventService {
2203
+ #eventSource;
2204
+ #allowedOrigins;
2197
2205
  constructor() {
2198
2206
  this.#eventSource = new Subject();
2199
2207
  this.event$ = this.#eventSource.asObservable();
2208
+ this.#allowedOrigins = [window.location.origin];
2209
+ this.#listenToWindowEvents();
2210
+ }
2211
+ #isYuvMessage(data) {
2212
+ return data && typeof data.type === 'string' && data.type.startsWith(CUSTOM_EVENT_PREFIX);
2213
+ }
2214
+ #isValidExternalMessage(event) {
2215
+ // Skip Angular DevTools and other framework messages
2216
+ if (event.data?.source?.startsWith('angular-devtools'))
2217
+ return false;
2218
+ if (event.data?.source?.startsWith('react-devtools'))
2219
+ return false;
2220
+ if (event.data?.source?.startsWith('webpack'))
2221
+ return false;
2222
+ if (event.data?.type?.startsWith('webpackOk'))
2223
+ return false;
2224
+ // Accept messages from trusted origins
2225
+ const trustedOrigins = [window.location.origin];
2226
+ const isFromTrustedOrigin = trustedOrigins.includes(event.origin);
2227
+ const hasValidStructure = event.data && (event.data.type || event.data.eventType) && typeof event.data === 'object';
2228
+ return isFromTrustedOrigin && hasValidStructure && this.#isYuvMessage(event.data);
2229
+ }
2230
+ #listenToWindowEvents() {
2231
+ fromEvent(window, 'message')
2232
+ .pipe(
2233
+ // TODO: Prevent message spam (necessary?)
2234
+ // debounceTime(500),
2235
+ filter((event) => this.#isValidExternalMessage(event)), takeUntilDestroyed())
2236
+ .subscribe((event) => this.trigger(event.data.type, event.data.data));
2237
+ }
2238
+ /**
2239
+ * Triggers a postMessage event that will be sent to the yuuvis global event Trigger
2240
+ * @param type Type/key of the event
2241
+ * @param data Data to be sent along with the event
2242
+ */
2243
+ triggerPostMessageEvent(type, data) {
2244
+ const targetOrigin = window.location.origin;
2245
+ const payload = {
2246
+ type,
2247
+ data,
2248
+ timestamp: Date.now()
2249
+ };
2250
+ window.postMessage(payload, targetOrigin);
2200
2251
  }
2201
- #eventSource;
2202
2252
  /**
2203
2253
  * Trigger an global event
2204
2254
  * @param type Type/key of the event
2205
2255
  * @param data Data to be send along with the event
2206
2256
  */
2207
2257
  trigger(type, data) {
2258
+ console.log(`EventService: trigger event ${type}`, data);
2208
2259
  this.#eventSource.next({ type, data });
2209
2260
  }
2210
2261
  /**
@@ -2222,7 +2273,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
2222
2273
  args: [{
2223
2274
  providedIn: 'root'
2224
2275
  }]
2225
- }] });
2276
+ }], ctorParameters: () => [] });
2226
2277
 
2227
2278
  /**
2228
2279
  * Events emitted by parts of the application
@@ -4477,6 +4528,185 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
4477
4528
  }]
4478
4529
  }], ctorParameters: () => [{ type: PendingChangesService }] });
4479
4530
 
4531
+ /**
4532
+ * Service that provides guards for Material Dialog close operations.
4533
+ *
4534
+ * This service allows you to intercept and control dialog close events,
4535
+ * enabling confirmation dialogs or preventing accidental closure when
4536
+ * there are unsaved changes or pending operations.
4537
+ *
4538
+ * @example
4539
+ * ```typescript
4540
+ * readonly #dialogCloseGuard = inject(DialogCloseGuard);
4541
+ *
4542
+ * openDialog() {
4543
+ * const dialogRef = this.dialog.open(MyComponent);
4544
+ *
4545
+ * // Prevent dialog from closing if there are unsaved changes
4546
+ * this.#dialogCloseGuard.dialogCanClose(dialogRef, () => {
4547
+ * return this.hasUnsavedChanges() ? this.confirmClose() : true;
4548
+ * }).subscribe();
4549
+ * }
4550
+ * ```
4551
+ */
4552
+ class DialogCloseGuard {
4553
+ #pendingChangesService = inject(PendingChangesService);
4554
+ /**
4555
+ * Converts a synchronous or asynchronous check function result to an Observable.
4556
+ *
4557
+ * This private method normalizes the return value of check functions,
4558
+ * ensuring they are always handled as Observables for consistent processing.
4559
+ *
4560
+ * @private
4561
+ * @param checkFunction - Function that returns either a boolean or Observable<boolean>
4562
+ * @returns Observable<boolean> - Always returns an Observable, regardless of input type
4563
+ */
4564
+ #canClose(checkFunction) {
4565
+ const result = checkFunction();
4566
+ return isObservable(result) ? result : of(result);
4567
+ }
4568
+ /**
4569
+ * Guards against dialog closure when the backdrop is clicked.
4570
+ *
4571
+ * This method disables the default close behavior and only allows the dialog
4572
+ * to close when the provided check function returns true. The check is triggered
4573
+ * specifically when the user clicks on the dialog backdrop.
4574
+ *
4575
+ * @param dialogRef - Reference to the Material Dialog instance
4576
+ * @param checkFunction - Function that determines if the dialog can be closed.
4577
+ * Can return a boolean directly or an Observable<boolean>
4578
+ * @returns Observable<boolean> - Emits true when the dialog can be closed via backdrop click
4579
+ *
4580
+ * @example
4581
+ * ```typescript
4582
+ * this.dialogCloseGuard.checkBackdropCanClose(dialogRef, () => {
4583
+ * return this.formIsDirty ? this.showConfirmDialog() : true;
4584
+ * }).subscribe(canClose => {
4585
+ * if (canClose) {
4586
+ * dialogRef.close();
4587
+ * }
4588
+ * });
4589
+ * ```
4590
+ */
4591
+ checkBackdropCanClose(dialogRef, checkFunction) {
4592
+ dialogRef.disableClose = true;
4593
+ return dialogRef.backdropClick().pipe(switchMap$1(() => this.#canClose(checkFunction)));
4594
+ }
4595
+ /**
4596
+ * Guards against dialog closure when the Escape key is pressed.
4597
+ *
4598
+ * This method disables the default close behavior and only allows the dialog
4599
+ * to close when the provided check function returns true. The check is triggered
4600
+ * specifically when the user presses the Escape key.
4601
+ *
4602
+ * @param dialogRef - Reference to the Material Dialog instance
4603
+ * @param checkFunction - Function that determines if the dialog can be closed.
4604
+ * Can return a boolean directly or an Observable<boolean>
4605
+ * @returns Observable<boolean> - Emits true when the dialog can be closed via Escape key
4606
+ *
4607
+ * @example
4608
+ * ```typescript
4609
+ * this.dialogCloseGuard.checkKeydownEventsCanClose(dialogRef, () => {
4610
+ * return this.hasUnsavedChanges ? this.confirmDiscardChanges() : true;
4611
+ * }).subscribe(canClose => {
4612
+ * if (canClose) {
4613
+ * dialogRef.close();
4614
+ * }
4615
+ * });
4616
+ * ```
4617
+ */
4618
+ checkKeydownEventsCanClose(dialogRef, checkFunction) {
4619
+ dialogRef.disableClose = true;
4620
+ return dialogRef.keydownEvents().pipe(filter$1((event) => event.key === 'Escape'), switchMap$1(() => this.#canClose(checkFunction)));
4621
+ }
4622
+ /**
4623
+ * Comprehensive dialog close guard that handles both backdrop clicks and Escape key presses.
4624
+ *
4625
+ * This method provides a unified approach to guard dialog closure by monitoring both
4626
+ * backdrop clicks and Escape key events. When either event occurs, it runs the provided
4627
+ * check function and automatically closes the dialog if the check passes.
4628
+ *
4629
+ * This is the most convenient method when you want to guard against all common
4630
+ * dialog close scenarios with a single setup.
4631
+ *
4632
+ * @template T - The type of data that the dialog returns
4633
+ * @param dialogRef - Reference to the Material Dialog instance
4634
+ * @param checkFunction - Function that determines if the dialog can be closed.
4635
+ * Can return a boolean directly or an Observable<boolean>
4636
+ * @returns Observable<false | void> - Emits false if the dialog cannot be closed,
4637
+ * or void when the dialog is successfully closed
4638
+ *
4639
+ * @example
4640
+ * ```typescript
4641
+ * // Simple boolean check
4642
+ * this.dialogCloseGuard.dialogCanClose(dialogRef, () => !this.hasUnsavedChanges).subscribe();
4643
+ *
4644
+ * // Async check with confirmation dialog
4645
+ * this.dialogCloseGuard.dialogCanClose(dialogRef, () => {
4646
+ * return this.hasUnsavedChanges
4647
+ * ? this.confirmationService.confirm('Discard changes?')
4648
+ * : of(true);
4649
+ * }).subscribe();
4650
+ * ```
4651
+ */
4652
+ preventCloseUntil(dialogRef, checkFunction) {
4653
+ dialogRef.disableClose = true;
4654
+ return merge(dialogRef.backdropClick(), dialogRef.keydownEvents().pipe(filter$1((event) => event.key === 'Escape'))).pipe(switchMap$1(() => this.#canClose(checkFunction)), map$1((canClose) => canClose && dialogRef.close()));
4655
+ }
4656
+ /**
4657
+ * Specialized dialog close guard that automatically checks for pending changes.
4658
+ *
4659
+ * This convenience method provides automatic pending changes detection without
4660
+ * requiring a custom check function. It integrates with the PendingChangesService
4661
+ * to determine if there are any unsaved changes, and handles both backdrop clicks
4662
+ * and Escape key presses automatically.
4663
+ *
4664
+ * This method is ideal when you want to prevent dialog closure if there are
4665
+ * pending changes anywhere in the application, without having to implement
4666
+ * custom change detection logic.
4667
+ *
4668
+ * @template T - The type of data that the dialog returns
4669
+ * @param dialogRef - Reference to the Material Dialog instance
4670
+ * @returns Observable<false | void> - Emits false if there are pending changes
4671
+ * preventing closure, or void when the dialog
4672
+ * is successfully closed
4673
+ *
4674
+ */
4675
+ pendingChangesDialogCanClose(dialogRef) {
4676
+ dialogRef.disableClose = true;
4677
+ return merge(dialogRef.backdropClick(), dialogRef.keydownEvents().pipe(filter$1((event) => event.key === 'Escape'))).pipe(switchMap$1(() => of(this.#pendingChangesService.check())), map$1((canClose) => !canClose && dialogRef.close()));
4678
+ }
4679
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DialogCloseGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4680
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DialogCloseGuard }); }
4681
+ }
4682
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DialogCloseGuard, decorators: [{
4683
+ type: Injectable
4684
+ }] });
4685
+
4686
+ class TabGuardDirective {
4687
+ #tabGroup = inject(MatTabGroup);
4688
+ #pending = inject(PendingChangesService);
4689
+ constructor() {
4690
+ this.#tabGroup.selectedIndexChange.pipe(takeUntilDestroyed()).subscribe(() => this.#updateTabs());
4691
+ }
4692
+ ngDoCheck() {
4693
+ this.#updateTabs();
4694
+ }
4695
+ #updateTabs() {
4696
+ const activeIndex = this.#tabGroup.selectedIndex ?? 0;
4697
+ const hasPending = this.#pending.hasPendingTask();
4698
+ this.#tabGroup._tabs.forEach((tab, i) => (tab.disabled = hasPending && i !== activeIndex));
4699
+ }
4700
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabGuardDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
4701
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: TabGuardDirective, isStandalone: true, selector: "mat-tab-group[yuvTabGuardDisable]", ngImport: i0 }); }
4702
+ }
4703
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabGuardDirective, decorators: [{
4704
+ type: Directive,
4705
+ args: [{
4706
+ selector: 'mat-tab-group[yuvTabGuardDisable]'
4707
+ }]
4708
+ }], ctorParameters: () => [] });
4709
+
4480
4710
  class PredictionService {
4481
4711
  #backend = inject(BackendService);
4482
4712
  classify(objectIDs, schemaObjectTypeId = 'CLASSIFICATION') {
@@ -5245,5 +5475,5 @@ const provideYuvClientCore = (options = { translations: [] }) => {
5245
5475
  * Generated bundle index. Do not edit.
5246
5476
  */
5247
5477
 
5248
- export { AFO_STATE, AdministrationRoles, ApiBase, AppCacheService, AuditField, AuditService, AuthService, BackendService, BaseObjectTypeField, BpmService, CORE_CONFIG, CUSTOM_CONFIG, CatalogService, Classification, ClassificationPrefix, ClientDefaultsObjectTypeField, ClipboardService, ColumnConfigSkipFields, ConfigService, ConnectionService, ContentStreamAllowed, ContentStreamField, CoreConfig, DeviceScreenOrientation, DeviceService, Direction, DmsObject, DmsService, EventService, FileSizePipe, IdmService, InternalFieldType, KeysPipe, LocaleCurrencyPipe, LocaleDatePipe, LocaleDecimalPipe, LocaleNumberPipe, LocalePercentPipe, Logger, LoginStateName, NativeNotificationService, NotificationService, ObjectConfigService, ObjectFormControl, ObjectFormControlWrapper, ObjectFormGroup, ObjectTag, ObjectTypeClassification, ObjectTypePropertyClassification, Operator, OperatorLabel, ParentField, PendingChangesGuard, PendingChangesService, PredictionService, ProcessAction, RelationshipTypeField, RetentionField, RetentionService, SafeHtmlPipe, SafeUrlPipe, SearchService, SearchUtils, SecondaryObjectTypeClassification, SessionStorageService, Situation, Sort, SystemResult, SystemSOT, SystemService, SystemType, TENANT_HEADER, ToastService, UploadService, UserRoles, UserService, UserStorageService, Utils, YUV_USER, YuvError, YuvEventType, YuvUser, init_moduleFnc, provideUser, provideYuvClientCore };
5478
+ export { AFO_STATE, AdministrationRoles, ApiBase, AppCacheService, AuditField, AuditService, AuthService, BackendService, BaseObjectTypeField, BpmService, CORE_CONFIG, CUSTOM_CONFIG, CUSTOM_EVENT_PREFIX, CatalogService, Classification, ClassificationPrefix, ClientDefaultsObjectTypeField, ClipboardService, ColumnConfigSkipFields, ConfigService, ConnectionService, ContentStreamAllowed, ContentStreamField, CoreConfig, DeviceScreenOrientation, DeviceService, DialogCloseGuard, Direction, DmsObject, DmsService, EventService, FileSizePipe, IdmService, InternalFieldType, KeysPipe, LocaleCurrencyPipe, LocaleDatePipe, LocaleDecimalPipe, LocaleNumberPipe, LocalePercentPipe, Logger, LoginStateName, NativeNotificationService, NotificationService, ObjectConfigService, ObjectFormControl, ObjectFormControlWrapper, ObjectFormGroup, ObjectTag, ObjectTypeClassification, ObjectTypePropertyClassification, Operator, OperatorLabel, ParentField, PendingChangesGuard, PendingChangesService, PredictionService, ProcessAction, RelationshipTypeField, RetentionField, RetentionService, SafeHtmlPipe, SafeUrlPipe, SearchService, SearchUtils, SecondaryObjectTypeClassification, SessionStorageService, Situation, Sort, SystemResult, SystemSOT, SystemService, SystemType, TENANT_HEADER, TabGuardDirective, ToastService, UploadService, UserRoles, UserService, UserStorageService, Utils, YUV_USER, YuvError, YuvEventType, YuvUser, init_moduleFnc, provideUser, provideYuvClientCore };
5249
5479
  //# sourceMappingURL=yuuvis-client-core.mjs.map