@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.
- package/fesm2022/yuuvis-client-core.mjs +236 -6
- package/fesm2022/yuuvis-client-core.mjs.map +1 -1
- package/index.d.ts +1 -2
- package/lib/service/event/event.interface.d.ts +13 -0
- package/lib/service/event/event.service.d.ts +7 -0
- package/lib/service/pending-changes/index.d.ts +4 -0
- package/lib/service/pending-changes/pending-changes-dialog.guard.service.d.ts +128 -0
- package/lib/service/pending-changes/pending-changes-tab.guard.directive.d.ts +9 -0
- package/package.json +1 -1
|
@@ -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,
|
|
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
|